From a92077d311fb50a536c752896711fb04abfb5787 Mon Sep 17 00:00:00 2001 From: apio Date: Tue, 8 Aug 2023 14:14:35 +0200 Subject: [PATCH] kernel+libc: Add all variants of utime --- kernel/src/sys/file.cpp | 51 +++++++++++++++++++++++++++++++ libc/CMakeLists.txt | 1 + libc/include/bits/utime.h | 9 ++++++ libc/include/sys/stat.h | 8 +++++ libc/include/sys/time.h | 9 ++++++ libc/include/utime.h | 26 ++++++++++++++++ libc/src/sys/stat.cpp | 11 +++++++ libc/src/utime.cpp | 55 ++++++++++++++++++++++++++++++++++ libluna/include/luna/Syscall.h | 2 +- 9 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 libc/include/bits/utime.h create mode 100644 libc/include/utime.h create mode 100644 libc/src/utime.cpp diff --git a/kernel/src/sys/file.cpp b/kernel/src/sys/file.cpp index b47dd6b7..a1e511b3 100644 --- a/kernel/src/sys/file.cpp +++ b/kernel/src/sys/file.cpp @@ -4,9 +4,11 @@ #include "memory/MemoryManager.h" #include "sys/Syscall.h" #include "thread/Scheduler.h" +#include #include #include #include +#include #include #include @@ -262,3 +264,52 @@ Result sys_ftruncate(Registers*, SyscallArgs args) return 0; } + +Result sys_utimensat(Registers*, SyscallArgs args) +{ + int dirfd = (int)args[0]; + auto path = TRY(MemoryManager::strdup_from_user(args[1])); + const auto* times = (const struct timespec*)args[2]; + int flags = (int)args[3]; + + auto* current = Scheduler::current(); + auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW))); + + struct timespec ktimes[2]; + ktimes[0].tv_sec = ktimes[1].tv_sec = 0; + ktimes[0].tv_nsec = ktimes[1].tv_nsec = UTIME_NOW; + + if (times && !MemoryManager::copy_from_user(times, ktimes, sizeof(ktimes))) return err(EFAULT); + + // No permission checks are performed, since no actual modification is done, but the above checks are still + // performed. + if (ktimes[0].tv_nsec == UTIME_OMIT && ktimes[1].tv_nsec == UTIME_OMIT) return 0; + + bool allow_write_access = ktimes[0].tv_nsec == UTIME_NOW && ktimes[1].tv_nsec == UTIME_NOW; + + if (allow_write_access) + { + if (!VFS::can_write(inode, current->auth) && current->auth.euid != inode->metadata().uid && + current->auth.euid != 0) + return err(EACCES); + } + else if (current->auth.euid != inode->metadata().uid && current->auth.euid != 0) + return err(EPERM); + + auto metadata = inode->metadata(); + if (ktimes[0].tv_nsec != UTIME_OMIT) + { + if (ktimes[0].tv_nsec == UTIME_NOW) metadata.atime = *Timer::realtime_clock(); + if (ktimes[0].tv_nsec < 0 || ktimes[0].tv_nsec > 999'999'999) return err(EINVAL); + metadata.atime = ktimes[0]; + } + if (ktimes[1].tv_nsec != UTIME_OMIT) + { + if (ktimes[1].tv_nsec == UTIME_NOW) metadata.mtime = *Timer::realtime_clock(); + if (ktimes[1].tv_nsec < 0 || ktimes[1].tv_nsec > 999'999'999) return err(EINVAL); + metadata.mtime = ktimes[1]; + } + TRY(inode->set_metadata(metadata)); + + return 0; +} diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt index bb4a0d7a..0dd645e4 100644 --- a/libc/CMakeLists.txt +++ b/libc/CMakeLists.txt @@ -23,6 +23,7 @@ set(SOURCES src/scanf.cpp src/signal.cpp src/termios.cpp + src/utime.cpp src/sys/stat.cpp src/sys/mman.cpp src/sys/wait.cpp diff --git a/libc/include/bits/utime.h b/libc/include/bits/utime.h new file mode 100644 index 00000000..83b6edb8 --- /dev/null +++ b/libc/include/bits/utime.h @@ -0,0 +1,9 @@ +/* bits/utime.h: Definitions for UTIME_NOW and UTIME_OMIT. */ + +#ifndef _BITS_UTIME_H +#define _BITS_UTIME_H + +#define UTIME_NOW -1 +#define UTIME_OMIT -2 + +#endif diff --git a/libc/include/sys/stat.h b/libc/include/sys/stat.h index cc603372..830990d3 100644 --- a/libc/include/sys/stat.h +++ b/libc/include/sys/stat.h @@ -5,6 +5,8 @@ #include #include +#include +#include #include #ifdef __cplusplus @@ -41,6 +43,12 @@ extern "C" /* Change the process's file creation mask. */ mode_t umask(mode_t mask); + /* Change a file's access and modification timestamps, with nanosecond precision. */ + int utimensat(int dirfd, const char* path, const struct timespec times[2], int flags); + + /* Change a file's access and modification timestamps, with nanosecond precision. */ + int futimens(int fd, const struct timespec times[2], int flags); + #ifdef __cplusplus } #endif diff --git a/libc/include/sys/time.h b/libc/include/sys/time.h index 4b9b9c47..e29720b8 100644 --- a/libc/include/sys/time.h +++ b/libc/include/sys/time.h @@ -16,6 +16,15 @@ extern "C" /* Get the current time of day. */ __deprecated int gettimeofday(struct timeval* tp, void* timezone); + /* Change a file's access and modification timestamps, with microsecond precision. */ + int utimes(const char* path, const struct timeval buf[2]); + + /* Change a file descriptor's access and modification timestamps, with microsecond precision. */ + int futimes(int fd, const struct timeval buf[2]); + + /* Change a symlink's access and modification timestamps, with microsecond precision. */ + int lutimes(const char* path, const struct timeval buf[2]); + #ifdef __cplusplus } #endif diff --git a/libc/include/utime.h b/libc/include/utime.h new file mode 100644 index 00000000..5f090904 --- /dev/null +++ b/libc/include/utime.h @@ -0,0 +1,26 @@ +/* utime.h: The utime function. */ + +#ifndef _UTIME_H +#define _UTIME_H + +#include + +struct utimbuf +{ + time_t actime; + time_t modtime; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* Change a file's access and modification timestamps. */ + int utime(const char* path, const struct utimbuf* buf); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libc/src/sys/stat.cpp b/libc/src/sys/stat.cpp index 0a2a312e..99443e35 100644 --- a/libc/src/sys/stat.cpp +++ b/libc/src/sys/stat.cpp @@ -52,4 +52,15 @@ extern "C" { return (mode_t)syscall(SYS_umask, mask); } + + int utimensat(int dirfd, const char* path, const struct timespec* times, int flags) + { + long rc = syscall(SYS_utimensat, dirfd, path, times, flags); + __errno_return(rc, int); + } + + int futimens(int fd, const struct timespec* times, int flags) + { + return utimensat(fd, "", times, flags | AT_EMPTY_PATH); + } } diff --git a/libc/src/utime.cpp b/libc/src/utime.cpp new file mode 100644 index 00000000..db2d6cc2 --- /dev/null +++ b/libc/src/utime.cpp @@ -0,0 +1,55 @@ +#include +#include +#include +#include + +extern "C" +{ + int utime(const char* path, const struct utimbuf* buf) + { + if (!buf) return utimensat(AT_FDCWD, path, nullptr, 0); + + struct timespec times[2] = { + { .tv_sec = buf->actime, .tv_nsec = 0 }, + { .tv_sec = buf->modtime, .tv_nsec = 0 }, + }; + + return utimensat(AT_FDCWD, path, times, 0); + } + + int utimes(const char* path, const struct timeval* buf) + { + if (!buf) return utimensat(AT_FDCWD, path, nullptr, 0); + + struct timespec times[2] = { + { .tv_sec = buf[0].tv_sec, .tv_nsec = buf[0].tv_usec * 1000 }, + { .tv_sec = buf[1].tv_sec, .tv_nsec = buf[1].tv_usec * 1000 }, + }; + + return utimensat(AT_FDCWD, path, times, 0); + } + + int futimes(int fd, const struct timeval* buf) + { + if (!buf) return utimensat(fd, "", nullptr, AT_EMPTY_PATH); + + struct timespec times[2] = { + { .tv_sec = buf[0].tv_sec, .tv_nsec = buf[0].tv_usec * 1000 }, + { .tv_sec = buf[1].tv_sec, .tv_nsec = buf[1].tv_usec * 1000 }, + }; + + return utimensat(fd, "", times, AT_EMPTY_PATH); + } + + int lutimes(const char* path, const struct timeval* buf) + { + if (!buf) return utimensat(AT_FDCWD, path, nullptr, AT_SYMLINK_NOFOLLOW); + + struct timespec times[2] = { + { .tv_sec = buf[0].tv_sec, .tv_nsec = buf[0].tv_usec * 1000 }, + { .tv_sec = buf[1].tv_sec, .tv_nsec = buf[1].tv_usec * 1000 }, + }; + + return utimensat(AT_FDCWD, path, times, AT_SYMLINK_NOFOLLOW); + } +} diff --git a/libluna/include/luna/Syscall.h b/libluna/include/luna/Syscall.h index 62bb044d..f501222b 100644 --- a/libluna/include/luna/Syscall.h +++ b/libluna/include/luna/Syscall.h @@ -8,7 +8,7 @@ _e(umount) _e(pstat) _e(getrusage) _e(symlinkat) _e(readlinkat) _e(umask) _e(linkat) _e(faccessat) \ _e(pivot_root) _e(sigreturn) _e(sigaction) _e(kill) _e(sigprocmask) _e(setpgid) _e(isatty) \ _e(getpgid) _e(socket) _e(bind) _e(connect) _e(listen) _e(accept) _e(poll) _e(msync) \ - _e(truncate) _e(ftruncate) + _e(truncate) _e(ftruncate) _e(utimensat) enum Syscalls {