summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibC
diff options
context:
space:
mode:
authorAriel Don <ariel@arieldon.com>2022-05-02 15:26:10 -0500
committerAndreas Kling <kling@serenityos.org>2022-05-21 18:15:00 +0200
commit9a6bd859243df604c1b1f1762bb701165917f367 (patch)
tree45e15f48dcc9c2b996e02ac925da99b278226d0c /Userland/Libraries/LibC
parent7550017f97d7892f8b7d26074046a87144f491dd (diff)
downloadserenity-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.cpp47
-rw-r--r--Userland/Libraries/LibC/fcntl.h3
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, &params);
+ __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