mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-23 01:41:59 -05:00
Kernel+LibC+VFS: Implement utimensat(3)
Create POSIX utimensat() library call and corresponding system call to update file access and modification times.
This commit is contained in:
parent
7550017f97
commit
9a6bd85924
10 changed files with 146 additions and 0 deletions
|
@ -70,6 +70,9 @@ struct stat {
|
|||
#define st_mtime st_mtim.tv_sec
|
||||
#define st_ctime st_ctim.tv_sec
|
||||
|
||||
#define UTIME_OMIT -1
|
||||
#define UTIME_NOW -2
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -185,6 +185,7 @@ enum class NeedsBigProcessLock {
|
|||
S(unlink, NeedsBigProcessLock::No) \
|
||||
S(unveil, NeedsBigProcessLock::Yes) \
|
||||
S(utime, NeedsBigProcessLock::Yes) \
|
||||
S(utimensat, NeedsBigProcessLock::Yes) \
|
||||
S(waitid, NeedsBigProcessLock::Yes) \
|
||||
S(write, NeedsBigProcessLock::Yes) \
|
||||
S(writev, NeedsBigProcessLock::Yes) \
|
||||
|
@ -416,6 +417,13 @@ struct SC_unveil_params {
|
|||
StringArgument permissions;
|
||||
};
|
||||
|
||||
struct SC_utimensat_params {
|
||||
int dirfd;
|
||||
StringArgument path;
|
||||
struct timespec const* times;
|
||||
int flag;
|
||||
};
|
||||
|
||||
struct SC_waitid_params {
|
||||
int idtype;
|
||||
int id;
|
||||
|
|
|
@ -281,6 +281,7 @@ set(KERNEL_SOURCES
|
|||
Syscalls/unlink.cpp
|
||||
Syscalls/unveil.cpp
|
||||
Syscalls/utime.cpp
|
||||
Syscalls/utimensat.cpp
|
||||
Syscalls/waitid.cpp
|
||||
Syscalls/inode_watcher.cpp
|
||||
Syscalls/write.cpp
|
||||
|
|
|
@ -1545,6 +1545,8 @@ ErrorOr<void> Ext2FSInode::set_atime(time_t t)
|
|||
MutexLocker locker(m_inode_lock);
|
||||
if (fs().is_readonly())
|
||||
return EROFS;
|
||||
if (t > INT32_MAX)
|
||||
return EINVAL;
|
||||
m_raw_inode.i_atime = t;
|
||||
set_metadata_dirty(true);
|
||||
return {};
|
||||
|
|
|
@ -196,6 +196,25 @@ ErrorOr<void> VirtualFileSystem::utime(StringView path, Custody& base, time_t at
|
|||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> VirtualFileSystem::utimensat(StringView path, Custody& base, timespec const& atime, timespec const& mtime, int options)
|
||||
{
|
||||
auto custody = TRY(resolve_path(path, base, nullptr, options));
|
||||
auto& inode = custody->inode();
|
||||
auto& current_process = Process::current();
|
||||
if (!current_process.is_superuser() && inode.metadata().uid != current_process.euid())
|
||||
return EACCES;
|
||||
if (custody->is_readonly())
|
||||
return EROFS;
|
||||
|
||||
// NOTE: A standard ext2 inode cannot store nanosecond timestamps.
|
||||
if (atime.tv_nsec != UTIME_OMIT)
|
||||
TRY(inode.set_atime(atime.tv_sec));
|
||||
if (mtime.tv_nsec != UTIME_OMIT)
|
||||
TRY(inode.set_mtime(mtime.tv_sec));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<InodeMetadata> VirtualFileSystem::lookup_metadata(StringView path, Custody& base, int options)
|
||||
{
|
||||
auto custody = TRY(resolve_path(path, base, nullptr, options));
|
||||
|
|
|
@ -64,6 +64,7 @@ public:
|
|||
ErrorOr<void> access(StringView path, int mode, Custody& base);
|
||||
ErrorOr<InodeMetadata> lookup_metadata(StringView path, Custody& base, int options = 0);
|
||||
ErrorOr<void> utime(StringView path, Custody& base, time_t atime, time_t mtime);
|
||||
ErrorOr<void> utimensat(StringView path, Custody& base, timespec const& atime, timespec const& mtime, int options = 0);
|
||||
ErrorOr<void> rename(StringView oldpath, StringView newpath, Custody& base);
|
||||
ErrorOr<void> mknod(StringView path, mode_t, dev_t, Custody& base);
|
||||
ErrorOr<NonnullRefPtr<Custody>> open_directory(StringView path, Custody& base);
|
||||
|
|
|
@ -353,6 +353,7 @@ public:
|
|||
ErrorOr<FlatPtr> sys$mkdir(Userspace<char const*> pathname, size_t path_length, mode_t mode);
|
||||
ErrorOr<FlatPtr> sys$times(Userspace<tms*>);
|
||||
ErrorOr<FlatPtr> sys$utime(Userspace<char const*> pathname, size_t path_length, Userspace<const struct utimbuf*>);
|
||||
ErrorOr<FlatPtr> sys$utimensat(Userspace<Syscall::SC_utimensat_params const*>);
|
||||
ErrorOr<FlatPtr> sys$link(Userspace<Syscall::SC_link_params const*>);
|
||||
ErrorOr<FlatPtr> sys$unlink(int dirfd, Userspace<char const*> pathname, size_t path_length, int flags);
|
||||
ErrorOr<FlatPtr> sys$symlink(Userspace<Syscall::SC_symlink_params const*>);
|
||||
|
|
61
Kernel/Syscalls/utimensat.cpp
Normal file
61
Kernel/Syscalls/utimensat.cpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Ariel Don <ariel@arieldon.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <Kernel/FileSystem/VirtualFileSystem.h>
|
||||
#include <Kernel/KLexicalPath.h>
|
||||
#include <Kernel/Process.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ErrorOr<FlatPtr> Process::sys$utimensat(Userspace<Syscall::SC_utimensat_params const*> user_params)
|
||||
{
|
||||
VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
|
||||
TRY(require_promise(Pledge::fattr));
|
||||
|
||||
auto params = TRY(copy_typed_from_user(user_params));
|
||||
auto now = kgettimeofday().to_truncated_seconds();
|
||||
|
||||
timespec times[2];
|
||||
if (params.times) {
|
||||
TRY(copy_from_user(times, params.times, sizeof(times)));
|
||||
if (times[0].tv_nsec == UTIME_NOW)
|
||||
times[0].tv_sec = now;
|
||||
if (times[1].tv_nsec == UTIME_NOW)
|
||||
times[1].tv_sec = now;
|
||||
} else {
|
||||
// According to POSIX, both access and modification times are set to
|
||||
// the current time given a nullptr.
|
||||
times[0].tv_sec = now;
|
||||
times[0].tv_nsec = UTIME_NOW;
|
||||
times[1].tv_sec = now;
|
||||
times[1].tv_nsec = UTIME_NOW;
|
||||
}
|
||||
|
||||
int dirfd = params.dirfd;
|
||||
auto path = TRY(get_syscall_path_argument(params.path));
|
||||
|
||||
RefPtr<Custody> base;
|
||||
if (dirfd == AT_FDCWD) {
|
||||
base = current_directory();
|
||||
} else {
|
||||
auto base_description = TRY(open_file_description(dirfd));
|
||||
if (!KLexicalPath::is_absolute(path->view()) && !base_description->is_directory())
|
||||
return ENOTDIR;
|
||||
if (!base_description->custody())
|
||||
return EINVAL;
|
||||
base = base_description->custody();
|
||||
}
|
||||
|
||||
auto& atime = times[0];
|
||||
auto& mtime = times[1];
|
||||
int follow_symlink = params.flag & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW_NOERROR : 0;
|
||||
TRY(VirtualFileSystem::the().utimensat(path->view(), *base, atime, mtime, follow_symlink));
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue