diff options
author | Cameron Youell <cameronyouell@gmail.com> | 2023-03-20 18:16:44 +1100 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2023-03-21 19:03:21 +0000 |
commit | e78be03b4942b844c17896f4a4b9046dc09f0fc8 (patch) | |
tree | 9aea8fe12d4a2e9cf999fbafcf94e8a0e15da205 /Userland | |
parent | b5f0f94757c6adfe45ff94b884b8c5f461a8b4af (diff) | |
download | serenity-e78be03b4942b844c17896f4a4b9046dc09f0fc8.zip |
LibCore: Add ErrorOr wrapper for `utimensat`
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Libraries/LibCore/System.cpp | 50 | ||||
-rw-r--r-- | Userland/Libraries/LibCore/System.h | 1 |
2 files changed, 51 insertions, 0 deletions
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<void> utime(StringView path, Optional<struct utimbuf> maybe_buf) #endif } +ErrorOr<void> 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<struct utsname> 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<void> fchown(int fd, uid_t, gid_t); ErrorOr<void> rename(StringView old_path, StringView new_path); ErrorOr<void> unlink(StringView path); ErrorOr<void> utime(StringView path, Optional<struct utimbuf>); +ErrorOr<void> utimensat(int fd, StringView path, struct timespec const times[2], int flag); ErrorOr<struct utsname> uname(); ErrorOr<Array<int, 2>> pipe2(int flags); #ifndef AK_OS_ANDROID |