Kernel/LibC: Implement utime* family functions

This patch adds *working*
 - utime
 - utimes
 - utimensat
 - futimens
This commit is contained in:
Bananymous 2025-05-29 05:01:26 +03:00
parent 8392472bac
commit 1bd454b8fd
13 changed files with 150 additions and 20 deletions

View File

@ -46,6 +46,7 @@ namespace Kernel
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
virtual BAN::ErrorOr<void> truncate_impl(size_t) override;
virtual BAN::ErrorOr<void> chmod_impl(mode_t) override;
virtual BAN::ErrorOr<void> utimens_impl(const timespec[2]) override;
virtual BAN::ErrorOr<void> fsync_impl() override;
virtual bool can_read_impl() const override { return true; }

View File

@ -44,6 +44,7 @@ namespace Kernel
//virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
//virtual BAN::ErrorOr<void> truncate_impl(size_t) override;
//virtual BAN::ErrorOr<void> chmod_impl(mode_t) override;
//virtual BAN::ErrorOr<void> utimens_impl(const timespec[2]) override;
virtual BAN::ErrorOr<void> fsync_impl() override { return {}; }
virtual bool can_read_impl() const override { return true; }

View File

@ -119,6 +119,7 @@ namespace Kernel
BAN::ErrorOr<void> truncate(size_t);
BAN::ErrorOr<void> chmod(mode_t);
BAN::ErrorOr<void> chown(uid_t, gid_t);
BAN::ErrorOr<void> utimens(const timespec[2]);
BAN::ErrorOr<void> fsync();
// Select/Non blocking API
@ -165,6 +166,7 @@ namespace Kernel
virtual BAN::ErrorOr<void> truncate_impl(size_t) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> chmod_impl(mode_t) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> chown_impl(uid_t, gid_t) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> utimens_impl(const timespec[2]) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> fsync_impl() = 0;
// Select/Non blocking API

View File

@ -47,6 +47,8 @@ namespace Kernel
protected:
TmpInode(TmpFileSystem&, ino_t, const TmpInodeInfo&);
virtual BAN::ErrorOr<void> chmod_impl(mode_t) override;
virtual BAN::ErrorOr<void> utimens_impl(const timespec[2]) override;
virtual BAN::ErrorOr<void> fsync_impl() override { return {}; }
void sync();
@ -75,7 +77,6 @@ namespace Kernel
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
virtual BAN::ErrorOr<void> truncate_impl(size_t) override;
virtual BAN::ErrorOr<void> 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<size_t> read_impl(off_t, BAN::ByteSpan) override { return BAN::Error::from_errno(ENODEV); }
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override { return BAN::Error::from_errno(ENODEV); }
virtual BAN::ErrorOr<void> truncate_impl(size_t) override { return BAN::Error::from_errno(ENODEV); }
virtual BAN::ErrorOr<void> chmod_impl(mode_t) override;
virtual bool can_read_impl() const override { return false; }
virtual bool can_write_impl() const override { return false; }

View File

@ -116,6 +116,7 @@ namespace Kernel
BAN::ErrorOr<long> sys_fchmodat(int fd, const char* path, mode_t mode, int flag);
BAN::ErrorOr<long> sys_fchownat(int fd, const char* path, uid_t uid, gid_t gid, int flag);
BAN::ErrorOr<long> sys_utimensat(int fd, const char* path, const struct timespec times[2], int flag);
BAN::ErrorOr<long> sys_socket(int domain, int type, int protocol);
BAN::ErrorOr<long> sys_socketpair(int domain, int type, int protocol, int socket_vector[2]);

View File

@ -4,6 +4,8 @@
#include <kernel/FS/Ext2/Inode.h>
#include <kernel/Timer/Timer.h>
#include <sys/stat.h>
namespace Kernel
{
@ -287,6 +289,28 @@ namespace Kernel
return {};
}
BAN::ErrorOr<void> 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<void> Ext2Inode::fsync_impl()
{
for (size_t i = 0; i < max_used_data_block_count(); i++)

View File

@ -231,6 +231,12 @@ namespace Kernel
return chown_impl(uid, gid);
}
BAN::ErrorOr<void> Inode::utimens(const timespec times[2])
{
LockGuard _(m_mutex);
return utimens_impl(times);
}
BAN::ErrorOr<void> Inode::fsync()
{
LockGuard _(m_mutex);

View File

@ -2,6 +2,8 @@
#include <kernel/FS/TmpFS/Inode.h>
#include <kernel/Timer/Timer.h>
#include <sys/stat.h>
namespace Kernel
{
@ -90,6 +92,23 @@ namespace Kernel
m_fs.delete_inode(ino());
}
BAN::ErrorOr<void> 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<void> 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<void> TmpFileInode::chmod_impl(mode_t new_mode)
{
m_inode_info.mode = new_mode;
return {};
}
/* SOCKET INODE */
BAN::ErrorOr<BAN::RefPtr<TmpSocketInode>> TmpSocketInode::create_new(TmpFileSystem& fs, mode_t mode, uid_t uid, gid_t gid)
{
@ -248,12 +261,6 @@ namespace Kernel
{
}
BAN::ErrorOr<void> TmpSocketInode::chmod_impl(mode_t new_mode)
{
m_inode_info.mode = new_mode;
return {};
}
/* SYMLINK INODE */
BAN::ErrorOr<BAN::RefPtr<TmpSymlinkInode>> TmpSymlinkInode::create_new(TmpFileSystem& fs, mode_t mode, uid_t uid, gid_t gid, BAN::StringView target)

View File

@ -1262,6 +1262,56 @@ namespace Kernel
return 0;
}
BAN::ErrorOr<long> 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<time_t>(current_ns / 1'000'000),
.tv_nsec = static_cast<long>(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<long> Process::sys_socket(int domain, int type, int protocol)
{
LockGuard _(m_process_lock);

View File

@ -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) \

View File

@ -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);
}

View File

@ -1,3 +1,4 @@
#include <fcntl.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <time.h>
@ -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);
}

View File

@ -1,11 +1,20 @@
#include <utime.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <utime.h>
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);
}