From cbf78975f14ac60dfaa7f712fb35f8651567f39e Mon Sep 17 00:00:00 2001 From: Liav A Date: Sat, 8 Apr 2023 11:37:15 +0300 Subject: [PATCH] Kernel: Add the futimens syscall We have a problem with the original utimensat syscall because when we do call LibC futimens function, internally we provide an empty path, and the Kernel get_syscall_path_argument method will detect this as an invalid path. This happens to spit an error for example in the touch utility, so if a user is running "touch non_existing_file", it will create that file, but the user will still see an error coming from LibC futimens function. This new syscall gets an open file description and it provides the same functionality as utimensat, on the specified open file description. The new syscall will be used later by LibC to properly implement LibC futimens function so the situation described with relation to the "touch" utility could be fixed. --- Kernel/API/Syscall.h | 6 +++++ Kernel/FileSystem/VirtualFileSystem.cpp | 9 +++++-- Kernel/FileSystem/VirtualFileSystem.h | 1 + Kernel/Process.h | 1 + Kernel/Syscalls/utimensat.cpp | 34 +++++++++++++++++++++++++ 5 files changed, 49 insertions(+), 2 deletions(-) diff --git a/Kernel/API/Syscall.h b/Kernel/API/Syscall.h index 1ab7d2d41c..19b7074197 100644 --- a/Kernel/API/Syscall.h +++ b/Kernel/API/Syscall.h @@ -84,6 +84,7 @@ enum class NeedsBigProcessLock { S(fsync, NeedsBigProcessLock::No) \ S(ftruncate, NeedsBigProcessLock::No) \ S(futex, NeedsBigProcessLock::Yes) \ + S(futimens, NeedsBigProcessLock::No) \ S(get_dir_entries, NeedsBigProcessLock::Yes) \ S(get_root_session_id, NeedsBigProcessLock::No) \ S(get_stack_bounds, NeedsBigProcessLock::No) \ @@ -457,6 +458,11 @@ struct SC_utimensat_params { int flag; }; +struct SC_futimens_params { + int fd; + struct timespec const* times; +}; + struct SC_waitid_params { int idtype; int id; diff --git a/Kernel/FileSystem/VirtualFileSystem.cpp b/Kernel/FileSystem/VirtualFileSystem.cpp index 1f9a85ae60..4b66e5f3c3 100644 --- a/Kernel/FileSystem/VirtualFileSystem.cpp +++ b/Kernel/FileSystem/VirtualFileSystem.cpp @@ -313,10 +313,15 @@ ErrorOr VirtualFileSystem::utime(Credentials const& credentials, StringVie ErrorOr VirtualFileSystem::utimensat(Credentials const& credentials, StringView path, Custody& base, timespec const& atime, timespec const& mtime, int options) { auto custody = TRY(resolve_path(credentials, path, base, nullptr, options)); - auto& inode = custody->inode(); + return do_utimens(credentials, custody, atime, mtime); +} + +ErrorOr VirtualFileSystem::do_utimens(Credentials const& credentials, Custody& custody, timespec const& atime, timespec const& mtime) +{ + auto& inode = custody.inode(); if (!credentials.is_superuser() && inode.metadata().uid != credentials.euid()) return EACCES; - if (custody->is_readonly()) + if (custody.is_readonly()) return EROFS; // NOTE: A standard ext2 inode cannot store nanosecond timestamps. diff --git a/Kernel/FileSystem/VirtualFileSystem.h b/Kernel/FileSystem/VirtualFileSystem.h index 361f7809cf..f23d5088bf 100644 --- a/Kernel/FileSystem/VirtualFileSystem.h +++ b/Kernel/FileSystem/VirtualFileSystem.h @@ -75,6 +75,7 @@ public: ErrorOr lookup_metadata(Credentials const&, StringView path, Custody& base, int options = 0); ErrorOr utime(Credentials const&, StringView path, Custody& base, time_t atime, time_t mtime); ErrorOr utimensat(Credentials const&, StringView path, Custody& base, timespec const& atime, timespec const& mtime, int options = 0); + ErrorOr do_utimens(Credentials const& credentials, Custody& custody, timespec const& atime, timespec const& mtime); ErrorOr rename(Credentials const&, Custody& old_base, StringView oldpath, Custody& new_base, StringView newpath); ErrorOr mknod(Credentials const&, StringView path, mode_t, dev_t, Custody& base); ErrorOr> open_directory(Credentials const&, StringView path, Custody& base); diff --git a/Kernel/Process.h b/Kernel/Process.h index 09798c2da6..7251aa5be2 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -334,6 +334,7 @@ public: ErrorOr sys$annotate_mapping(Userspace, int flags); ErrorOr sys$lseek(int fd, Userspace, int whence); ErrorOr sys$ftruncate(int fd, Userspace); + ErrorOr sys$futimens(Userspace); ErrorOr sys$posix_fallocate(int fd, Userspace, Userspace); ErrorOr sys$kill(pid_t pid_or_pgid, int sig); [[noreturn]] void sys$exit(int status); diff --git a/Kernel/Syscalls/utimensat.cpp b/Kernel/Syscalls/utimensat.cpp index 14319ab84d..aa48aaf92e 100644 --- a/Kernel/Syscalls/utimensat.cpp +++ b/Kernel/Syscalls/utimensat.cpp @@ -12,6 +12,40 @@ namespace Kernel { +ErrorOr Process::sys$futimens(Userspace user_params) +{ + VERIFY_NO_PROCESS_BIG_LOCK(this); + TRY(require_promise(Pledge::fattr)); + + auto params = TRY(copy_typed_from_user(user_params)); + auto now = kgettimeofday().to_timespec(); + + timespec times[2]; + if (params.times) { + TRY(copy_from_user(times, params.times, sizeof(times))); + if (times[0].tv_nsec == UTIME_NOW) + times[0] = now; + if (times[1].tv_nsec == UTIME_NOW) + times[1] = now; + } else { + // According to POSIX, both access and modification times are set to + // the current time given a nullptr. + times[0] = now; + times[1] = now; + } + + auto description = TRY(open_file_description(params.fd)); + if (!description->inode()) + return EBADF; + if (!description->custody()) + return EBADF; + + auto& atime = times[0]; + auto& mtime = times[1]; + TRY(VirtualFileSystem::the().do_utimens(credentials(), *description->custody(), atime, mtime)); + return 0; +} + ErrorOr Process::sys$utimensat(Userspace user_params) { VERIFY_NO_PROCESS_BIG_LOCK(this);