From e78be03b4942b844c17896f4a4b9046dc09f0fc8 Mon Sep 17 00:00:00 2001 From: Cameron Youell Date: Mon, 20 Mar 2023 18:16:44 +1100 Subject: [PATCH] LibCore: Add ErrorOr wrapper for `utimensat` --- Userland/Libraries/LibCore/System.cpp | 50 +++++++++++++++++++++++++++ Userland/Libraries/LibCore/System.h | 1 + 2 files changed, 51 insertions(+) diff --git a/Userland/Libraries/LibCore/System.cpp b/Userland/Libraries/LibCore/System.cpp index 70f3a01cfa..04624f7a37 100644 --- a/Userland/Libraries/LibCore/System.cpp +++ b/Userland/Libraries/LibCore/System.cpp @@ -1066,6 +1066,56 @@ ErrorOr utime(StringView path, Optional maybe_buf) #endif } +ErrorOr utimensat(int fd, StringView path, struct timespec const times[2], int flag) +{ + if (path.is_null()) + return Error::from_errno(EFAULT); + +#ifdef AK_OS_SERENITY + // POSIX allows AT_SYMLINK_NOFOLLOW flag or no flags. + if (flag & ~AT_SYMLINK_NOFOLLOW) + return Error::from_errno(EINVAL); + + // Return early without error since both changes are to be omitted. + if (times && times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT) + return {}; + + // According to POSIX, when times is a nullptr, it's equivalent to setting + // both last access time and last modification time to the current time. + // Setting the times argument to nullptr if it matches this case prevents + // the need to copy it in the kernel. + if (times && times[0].tv_nsec == UTIME_NOW && times[1].tv_nsec == UTIME_NOW) + times = nullptr; + + if (times) { + for (int i = 0; i < 2; ++i) { + if ((times[i].tv_nsec != UTIME_NOW && times[i].tv_nsec != UTIME_OMIT) + && (times[i].tv_nsec < 0 || times[i].tv_nsec >= 1'000'000'000L)) { + return Error::from_errno(EINVAL); + } + } + } + + Syscall::SC_utimensat_params params { + .dirfd = fd, + .path = { path.characters_without_null_termination(), path.length() }, + .times = times, + .flag = flag, + }; + int rc = syscall(SC_utimensat, ¶ms); + HANDLE_SYSCALL_RETURN_VALUE("utimensat", rc, {}); +#else + auto builder = TRY(StringBuilder::create()); + TRY(builder.try_append(path)); + TRY(builder.try_append('\0')); + + // Note the explicit null terminators above. + if (::utimensat(fd, builder.string_view().characters_without_null_termination(), times, flag) < 0) + return Error::from_syscall("utimensat"sv, -errno); + return {}; +#endif +} + ErrorOr uname() { struct utsname uts; diff --git a/Userland/Libraries/LibCore/System.h b/Userland/Libraries/LibCore/System.h index 2c0f73b1e9..664db9c63a 100644 --- a/Userland/Libraries/LibCore/System.h +++ b/Userland/Libraries/LibCore/System.h @@ -172,6 +172,7 @@ ErrorOr fchown(int fd, uid_t, gid_t); ErrorOr rename(StringView old_path, StringView new_path); ErrorOr unlink(StringView path); ErrorOr utime(StringView path, Optional); +ErrorOr utimensat(int fd, StringView path, struct timespec const times[2], int flag); ErrorOr uname(); ErrorOr> pipe2(int flags); #ifndef AK_OS_ANDROID