diff options
author | Ariel Don <ariel@arieldon.com> | 2022-05-02 15:26:10 -0500 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-05-21 18:15:00 +0200 |
commit | 9a6bd859243df604c1b1f1762bb701165917f367 (patch) | |
tree | 45e15f48dcc9c2b996e02ac925da99b278226d0c /Userland/Libraries/LibC | |
parent | 7550017f97d7892f8b7d26074046a87144f491dd (diff) | |
download | serenity-9a6bd859243df604c1b1f1762bb701165917f367.zip |
Kernel+LibC+VFS: Implement utimensat(3)
Create POSIX utimensat() library call and corresponding system call to
update file access and modification times.
Diffstat (limited to 'Userland/Libraries/LibC')
-rw-r--r-- | Userland/Libraries/LibC/fcntl.cpp | 47 | ||||
-rw-r--r-- | Userland/Libraries/LibC/fcntl.h | 3 |
2 files changed, 50 insertions, 0 deletions
diff --git a/Userland/Libraries/LibC/fcntl.cpp b/Userland/Libraries/LibC/fcntl.cpp index 9ce5039f18..4a421178d0 100644 --- a/Userland/Libraries/LibC/fcntl.cpp +++ b/Userland/Libraries/LibC/fcntl.cpp @@ -11,6 +11,7 @@ #include <stdarg.h> #include <string.h> #include <syscall.h> +#include <time.h> extern "C" { @@ -102,4 +103,50 @@ int posix_fadvise(int fd, off_t offset, off_t len, int advice) (void)advice; return 0; } + +// https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimensat.html +int utimensat(int dirfd, char const* path, struct timespec const times[2], int flag) +{ + if (!path) { + errno = EFAULT; + return -1; + } + + size_t path_length = strlen(path); + if (path_length > INT32_MAX) { + errno = EINVAL; + return -1; + } + + // POSIX allows AT_SYMLINK_NOFOLLOW flag or no flags. + if (flag & ~AT_SYMLINK_NOFOLLOW) { + errno = EINVAL; + return -1; + } + + // 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 0; + + // 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)) { + errno = EINVAL; + return -1; + } + } + } + + Syscall::SC_utimensat_params params { dirfd, { path, path_length }, times, flag }; + int rc = syscall(SC_utimensat, ¶ms); + __RETURN_WITH_ERRNO(rc, rc, -1); +} } diff --git a/Userland/Libraries/LibC/fcntl.h b/Userland/Libraries/LibC/fcntl.h index 8681676945..3704d6a7a7 100644 --- a/Userland/Libraries/LibC/fcntl.h +++ b/Userland/Libraries/LibC/fcntl.h @@ -8,6 +8,7 @@ #pragma once #include <Kernel/API/POSIX/fcntl.h> +#include <Kernel/API/POSIX/sys/stat.h> __BEGIN_DECLS @@ -29,4 +30,6 @@ int inode_watcher_remove_watch(int fd, int wd); int posix_fadvise(int fd, off_t offset, off_t len, int advice); +int utimensat(int dirfd, char const* path, struct timespec const times[2], int flag); + __END_DECLS |