From 1bd454b8fd9c7337da7153e4c8e2ac8d9ff788fb Mon Sep 17 00:00:00 2001 From: Bananymous Date: Thu, 29 May 2025 05:01:26 +0300 Subject: [PATCH] Kernel/LibC: Implement utime* family functions This patch adds *working* - utime - utimes - utimensat - futimens --- kernel/include/kernel/FS/Ext2/Inode.h | 1 + kernel/include/kernel/FS/FAT/Inode.h | 1 + kernel/include/kernel/FS/Inode.h | 2 + kernel/include/kernel/FS/TmpFS/Inode.h | 4 +- kernel/include/kernel/Process.h | 1 + kernel/kernel/FS/Ext2/Inode.cpp | 24 +++++++++ kernel/kernel/FS/Inode.cpp | 6 +++ kernel/kernel/FS/TmpFS/Inode.cpp | 31 +++++++----- kernel/kernel/Process.cpp | 50 +++++++++++++++++++ .../libraries/LibC/include/sys/syscall.h | 1 + userspace/libraries/LibC/sys/stat.cpp | 10 ++++ userspace/libraries/LibC/sys/time.cpp | 18 +++++++ userspace/libraries/LibC/utime.cpp | 21 +++++--- 13 files changed, 150 insertions(+), 20 deletions(-) diff --git a/kernel/include/kernel/FS/Ext2/Inode.h b/kernel/include/kernel/FS/Ext2/Inode.h index 90ff7baa..d84851bd 100644 --- a/kernel/include/kernel/FS/Ext2/Inode.h +++ b/kernel/include/kernel/FS/Ext2/Inode.h @@ -46,6 +46,7 @@ namespace Kernel virtual BAN::ErrorOr write_impl(off_t, BAN::ConstByteSpan) override; virtual BAN::ErrorOr truncate_impl(size_t) override; virtual BAN::ErrorOr chmod_impl(mode_t) override; + virtual BAN::ErrorOr utimens_impl(const timespec[2]) override; virtual BAN::ErrorOr fsync_impl() override; virtual bool can_read_impl() const override { return true; } diff --git a/kernel/include/kernel/FS/FAT/Inode.h b/kernel/include/kernel/FS/FAT/Inode.h index 615669d7..6753a7fd 100644 --- a/kernel/include/kernel/FS/FAT/Inode.h +++ b/kernel/include/kernel/FS/FAT/Inode.h @@ -44,6 +44,7 @@ namespace Kernel //virtual BAN::ErrorOr write_impl(off_t, BAN::ConstByteSpan) override; //virtual BAN::ErrorOr truncate_impl(size_t) override; //virtual BAN::ErrorOr chmod_impl(mode_t) override; + //virtual BAN::ErrorOr utimens_impl(const timespec[2]) override; virtual BAN::ErrorOr fsync_impl() override { return {}; } virtual bool can_read_impl() const override { return true; } diff --git a/kernel/include/kernel/FS/Inode.h b/kernel/include/kernel/FS/Inode.h index 7d58c9ef..833d0342 100644 --- a/kernel/include/kernel/FS/Inode.h +++ b/kernel/include/kernel/FS/Inode.h @@ -119,6 +119,7 @@ namespace Kernel BAN::ErrorOr truncate(size_t); BAN::ErrorOr chmod(mode_t); BAN::ErrorOr chown(uid_t, gid_t); + BAN::ErrorOr utimens(const timespec[2]); BAN::ErrorOr fsync(); // Select/Non blocking API @@ -165,6 +166,7 @@ namespace Kernel virtual BAN::ErrorOr truncate_impl(size_t) { return BAN::Error::from_errno(ENOTSUP); } virtual BAN::ErrorOr chmod_impl(mode_t) { return BAN::Error::from_errno(ENOTSUP); } virtual BAN::ErrorOr chown_impl(uid_t, gid_t) { return BAN::Error::from_errno(ENOTSUP); } + virtual BAN::ErrorOr utimens_impl(const timespec[2]) { return BAN::Error::from_errno(ENOTSUP); } virtual BAN::ErrorOr fsync_impl() = 0; // Select/Non blocking API diff --git a/kernel/include/kernel/FS/TmpFS/Inode.h b/kernel/include/kernel/FS/TmpFS/Inode.h index 12acf687..eaffad03 100644 --- a/kernel/include/kernel/FS/TmpFS/Inode.h +++ b/kernel/include/kernel/FS/TmpFS/Inode.h @@ -47,6 +47,8 @@ namespace Kernel protected: TmpInode(TmpFileSystem&, ino_t, const TmpInodeInfo&); + virtual BAN::ErrorOr chmod_impl(mode_t) override; + virtual BAN::ErrorOr utimens_impl(const timespec[2]) override; virtual BAN::ErrorOr fsync_impl() override { return {}; } void sync(); @@ -75,7 +77,6 @@ namespace Kernel virtual BAN::ErrorOr read_impl(off_t, BAN::ByteSpan) override; virtual BAN::ErrorOr write_impl(off_t, BAN::ConstByteSpan) override; virtual BAN::ErrorOr truncate_impl(size_t) override; - virtual BAN::ErrorOr chmod_impl(mode_t) override; virtual bool can_read_impl() const override { return true; } virtual bool can_write_impl() const override { return true; } @@ -98,7 +99,6 @@ namespace Kernel virtual BAN::ErrorOr read_impl(off_t, BAN::ByteSpan) override { return BAN::Error::from_errno(ENODEV); } virtual BAN::ErrorOr write_impl(off_t, BAN::ConstByteSpan) override { return BAN::Error::from_errno(ENODEV); } virtual BAN::ErrorOr truncate_impl(size_t) override { return BAN::Error::from_errno(ENODEV); } - virtual BAN::ErrorOr chmod_impl(mode_t) override; virtual bool can_read_impl() const override { return false; } virtual bool can_write_impl() const override { return false; } diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index 4540c554..c6baf7cd 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -116,6 +116,7 @@ namespace Kernel BAN::ErrorOr sys_fchmodat(int fd, const char* path, mode_t mode, int flag); BAN::ErrorOr sys_fchownat(int fd, const char* path, uid_t uid, gid_t gid, int flag); + BAN::ErrorOr sys_utimensat(int fd, const char* path, const struct timespec times[2], int flag); BAN::ErrorOr sys_socket(int domain, int type, int protocol); BAN::ErrorOr sys_socketpair(int domain, int type, int protocol, int socket_vector[2]); diff --git a/kernel/kernel/FS/Ext2/Inode.cpp b/kernel/kernel/FS/Ext2/Inode.cpp index d676ef6c..c7560433 100644 --- a/kernel/kernel/FS/Ext2/Inode.cpp +++ b/kernel/kernel/FS/Ext2/Inode.cpp @@ -4,6 +4,8 @@ #include #include +#include + namespace Kernel { @@ -287,6 +289,28 @@ namespace Kernel return {}; } + BAN::ErrorOr Ext2Inode::utimens_impl(const timespec times[2]) + { + const uint32_t old_times[2] { + m_inode.atime, + m_inode.mtime, + }; + + if (times[0].tv_nsec != UTIME_OMIT) + m_inode.atime = times[0].tv_sec; + if (times[1].tv_nsec != UTIME_OMIT) + m_inode.mtime = times[1].tv_sec; + + if (auto ret = sync(); ret.is_error()) + { + m_inode.atime = old_times[0]; + m_inode.mtime = old_times[1]; + return ret.release_error(); + } + + return {}; + } + BAN::ErrorOr Ext2Inode::fsync_impl() { for (size_t i = 0; i < max_used_data_block_count(); i++) diff --git a/kernel/kernel/FS/Inode.cpp b/kernel/kernel/FS/Inode.cpp index aeddd8a5..c26be539 100644 --- a/kernel/kernel/FS/Inode.cpp +++ b/kernel/kernel/FS/Inode.cpp @@ -231,6 +231,12 @@ namespace Kernel return chown_impl(uid, gid); } + BAN::ErrorOr Inode::utimens(const timespec times[2]) + { + LockGuard _(m_mutex); + return utimens_impl(times); + } + BAN::ErrorOr Inode::fsync() { LockGuard _(m_mutex); diff --git a/kernel/kernel/FS/TmpFS/Inode.cpp b/kernel/kernel/FS/TmpFS/Inode.cpp index 4e3eb048..78e46cb8 100644 --- a/kernel/kernel/FS/TmpFS/Inode.cpp +++ b/kernel/kernel/FS/TmpFS/Inode.cpp @@ -2,6 +2,8 @@ #include #include +#include + namespace Kernel { @@ -90,6 +92,23 @@ namespace Kernel m_fs.delete_inode(ino()); } + BAN::ErrorOr TmpInode::chmod_impl(mode_t new_mode) + { + ASSERT(!(new_mode & Mode::TYPE_MASK)); + m_inode_info.mode &= ~Mode::TYPE_MASK; + m_inode_info.mode |= new_mode; + return {}; + } + + BAN::ErrorOr TmpInode::utimens_impl(const timespec times[2]) + { + if (times[0].tv_nsec != UTIME_OMIT) + m_inode_info.atime = times[0]; + if (times[1].tv_nsec != UTIME_OMIT) + m_inode_info.atime = times[1]; + return {}; + } + void TmpInode::sync() { m_fs.write_inode(m_ino, m_inode_info); @@ -219,12 +238,6 @@ namespace Kernel return {}; } - BAN::ErrorOr TmpFileInode::chmod_impl(mode_t new_mode) - { - m_inode_info.mode = new_mode; - return {}; - } - /* SOCKET INODE */ BAN::ErrorOr> TmpSocketInode::create_new(TmpFileSystem& fs, mode_t mode, uid_t uid, gid_t gid) { @@ -248,12 +261,6 @@ namespace Kernel { } - BAN::ErrorOr TmpSocketInode::chmod_impl(mode_t new_mode) - { - m_inode_info.mode = new_mode; - return {}; - } - /* SYMLINK INODE */ BAN::ErrorOr> TmpSymlinkInode::create_new(TmpFileSystem& fs, mode_t mode, uid_t uid, gid_t gid, BAN::StringView target) diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index f9b2d1c2..7c2f0f9f 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -1262,6 +1262,56 @@ namespace Kernel return 0; } + BAN::ErrorOr Process::sys_utimensat(int fd, const char* path, const struct timespec _times[2], int flag) + { + if (flag & ~AT_SYMLINK_NOFOLLOW) + return BAN::Error::from_errno(EINVAL); + if (flag == AT_SYMLINK_NOFOLLOW) + flag = O_NOFOLLOW; + + timespec times[2]; + const uint64_t current_ns = SystemTimer::get().ns_since_boot(); + times[0] = times[1] = timespec { + .tv_sec = static_cast(current_ns / 1'000'000), + .tv_nsec = static_cast(current_ns % 1'000'000), + }; + + if (_times) + { + LockGuard _(m_process_lock); + TRY(validate_pointer_access(_times, sizeof(timespec) * 2, false)); + if (_times[0].tv_nsec != UTIME_NOW) + { + times[0] = _times[0]; + if (auto ns = times[0].tv_nsec; ns != UTIME_OMIT && (ns < 0 || ns >= 1'000'000'000)) + return BAN::Error::from_errno(EINVAL); + } + if (_times[1].tv_nsec != UTIME_NOW) + { + times[1] = _times[1]; + if (auto ns = times[1].tv_nsec; ns != UTIME_OMIT && (ns < 0 || ns >= 1'000'000'000)) + return BAN::Error::from_errno(EINVAL); + } + } + + if (times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT) + return 0; + + LockGuard _(m_process_lock); + + auto inode = TRY(find_file(fd, path, flag)).inode; + + if (!m_credentials.is_superuser() && inode->uid() != m_credentials.euid()) + { + dwarnln("cannot chmod uid {} vs {}", inode->uid(), m_credentials.euid()); + return BAN::Error::from_errno(EPERM); + } + + TRY(inode->utimens(times)); + + return 0; + } + BAN::ErrorOr Process::sys_socket(int domain, int type, int protocol) { LockGuard _(m_process_lock); diff --git a/userspace/libraries/LibC/include/sys/syscall.h b/userspace/libraries/LibC/include/sys/syscall.h index e298c887..df7ad078 100644 --- a/userspace/libraries/LibC/include/sys/syscall.h +++ b/userspace/libraries/LibC/include/sys/syscall.h @@ -94,6 +94,7 @@ __BEGIN_DECLS O(SYS_FSYNC, fsync) \ O(SYS_SYMLINKAT, symlinkat) \ O(SYS_HARDLINKAT, hardlinkat) \ + O(SYS_UTIMENSAT, utimensat) \ O(SYS_YIELD, yield) \ O(SYS_SET_TLS, set_tls) \ O(SYS_GET_TLS, get_tls) \ diff --git a/userspace/libraries/LibC/sys/stat.cpp b/userspace/libraries/LibC/sys/stat.cpp index d1ffb259..ef82df56 100644 --- a/userspace/libraries/LibC/sys/stat.cpp +++ b/userspace/libraries/LibC/sys/stat.cpp @@ -53,3 +53,13 @@ int mkdir(const char* path, mode_t mode) { return syscall(SYS_CREATE_DIR, path, __UMASKED_MODE(mode)); } + +int futimens(int fd, const struct timespec times[2]) +{ + return utimensat(fd, nullptr, times, 0); +} + +int utimensat(int fd, const char* path, const struct timespec times[2], int flag) +{ + return syscall(SYS_UTIMENSAT, fd, path, times, flag); +} diff --git a/userspace/libraries/LibC/sys/time.cpp b/userspace/libraries/LibC/sys/time.cpp index 9667cede..41e470bd 100644 --- a/userspace/libraries/LibC/sys/time.cpp +++ b/userspace/libraries/LibC/sys/time.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -26,3 +27,20 @@ int setitimer(int which, const struct itimerval* __restrict value, struct itimer { return syscall(SYS_SETITIMER, which, value, ovalue); } + +int utimes(const char* path, const struct timeval times[2]) +{ + if (times == nullptr) + return utimensat(AT_FDCWD, path, nullptr, 0); + const timespec times_ts[2] { + timespec { + .tv_sec = times[0].tv_sec, + .tv_nsec = times[0].tv_usec * 1000, + }, + timespec { + .tv_sec = times[1].tv_sec, + .tv_nsec = times[1].tv_usec * 1000, + }, + }; + return utimensat(AT_FDCWD, path, times_ts, 0); +} diff --git a/userspace/libraries/LibC/utime.cpp b/userspace/libraries/LibC/utime.cpp index 727dd942..db9a03b0 100644 --- a/userspace/libraries/LibC/utime.cpp +++ b/userspace/libraries/LibC/utime.cpp @@ -1,11 +1,20 @@ -#include +#include #include -#include +#include int utime(const char* filename, const struct utimbuf* times) { - fprintf(stddbg, "TODO: utime(\"%s\", %p)\n", filename, times); - - struct stat st; - return stat(filename, &st); + if (times == nullptr) + return utimensat(AT_FDCWD, filename, nullptr, 0); + const timespec times_ts[2] { + timespec { + .tv_sec = times->actime, + .tv_nsec = 0, + }, + timespec { + .tv_sec = times->modtime, + .tv_nsec = 0, + }, + }; + return utimensat(AT_FDCWD, filename, times_ts, 0); }