From 8a854ba309a591254935559e72c44dca98c9a05e Mon Sep 17 00:00:00 2001 From: Ariel Don Date: Tue, 3 May 2022 14:52:08 -0500 Subject: [PATCH] Kernel+LibC: Implement futimens(3) Implement futimes() in terms of utimensat(). Now, utimensat() strays from POSIX compliance because it also accepts a combination of a file descriptor of a regular file and an empty path. utimensat() then uses this file descriptor instead of the path to update the last access and/or modification time of a file. That being said, its prior behavior remains intact. With the new behavior of utimensat(), `path` must point to a valid string; given a null pointer instead of an empty string, utimensat() sets `errno` to `EFAULT` and returns a failure. --- Kernel/Syscalls/utimensat.cpp | 41 +++++++++++++++++++++--------- Userland/Libraries/LibC/stat.cpp | 6 +++++ Userland/Libraries/LibC/sys/stat.h | 1 + 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/Kernel/Syscalls/utimensat.cpp b/Kernel/Syscalls/utimensat.cpp index 4a6ca189a3..1cd3760bb0 100644 --- a/Kernel/Syscalls/utimensat.cpp +++ b/Kernel/Syscalls/utimensat.cpp @@ -20,6 +20,9 @@ ErrorOr Process::sys$utimensat(Userspace Process::sys$utimensat(Userspace path; + RefPtr description; RefPtr base; - if (dirfd == AT_FDCWD) { - base = current_directory(); + + auto path_or_error = get_syscall_path_argument(params.path); + if (path_or_error.is_error()) { + // If the path is empty ("") but not a nullptr, attempt to get a path + // from the file descriptor. This allows futimens() to be implemented + // in terms of utimensat(). + if (params.path.characters && params.path.length == 0) { + description = TRY(open_file_description(dirfd)); + path = TRY(description->original_absolute_path()); + base = description->custody(); + } else { + return path_or_error.release_error(); + } } else { - auto base_description = TRY(open_file_description(dirfd)); - if (!KLexicalPath::is_absolute(path->view()) && !base_description->is_directory()) - return ENOTDIR; - if (!base_description->custody()) - return EINVAL; - base = base_description->custody(); + path = path_or_error.release_value(); + if (dirfd == AT_FDCWD) { + base = current_directory(); + } else { + description = TRY(open_file_description(dirfd)); + if (!KLexicalPath::is_absolute(path->view()) && !description->is_directory()) + return ENOTDIR; + if (!description->custody()) + return EINVAL; + base = description->custody(); + } } auto& atime = times[0]; auto& mtime = times[1]; - int follow_symlink = params.flag & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW_NOERROR : 0; TRY(VirtualFileSystem::the().utimensat(path->view(), *base, atime, mtime, follow_symlink)); return 0; } diff --git a/Userland/Libraries/LibC/stat.cpp b/Userland/Libraries/LibC/stat.cpp index bcb52fd898..d1ce13ea90 100644 --- a/Userland/Libraries/LibC/stat.cpp +++ b/Userland/Libraries/LibC/stat.cpp @@ -109,4 +109,10 @@ int fstatat(int fd, char const* path, struct stat* statbuf, int flags) { return do_stat(fd, path, statbuf, !(flags & AT_SYMLINK_NOFOLLOW)); } + +// https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html +int futimens(int fd, struct timespec const times[2]) +{ + return utimensat(fd, "", times, 0); +} } diff --git a/Userland/Libraries/LibC/sys/stat.h b/Userland/Libraries/LibC/sys/stat.h index 4ada805708..16b8a2c2e8 100644 --- a/Userland/Libraries/LibC/sys/stat.h +++ b/Userland/Libraries/LibC/sys/stat.h @@ -23,5 +23,6 @@ int fstat(int fd, struct stat* statbuf); int lstat(char const* path, struct stat* statbuf); int stat(char const* path, struct stat* statbuf); int fstatat(int fd, char const* path, struct stat* statbuf, int flags); +int futimens(int fd, struct timespec const times[2]); __END_DECLS