summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorCameron Youell <cameronyouell@gmail.com>2023-03-20 18:16:44 +1100
committerLinus Groh <mail@linusgroh.de>2023-03-21 19:03:21 +0000
commite78be03b4942b844c17896f4a4b9046dc09f0fc8 (patch)
tree9aea8fe12d4a2e9cf999fbafcf94e8a0e15da205 /Userland
parentb5f0f94757c6adfe45ff94b884b8c5f461a8b4af (diff)
downloadserenity-e78be03b4942b844c17896f4a4b9046dc09f0fc8.zip
LibCore: Add ErrorOr wrapper for `utimensat`
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibCore/System.cpp50
-rw-r--r--Userland/Libraries/LibCore/System.h1
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, &params);
+ 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