Skip to content

Commit

Permalink
Kernel+LibC+VFS: Implement utimensat(3)
Browse files Browse the repository at this point in the history
Create POSIX utimensat() library call and corresponding system call to
update file access and modification times.
  • Loading branch information
arieldon authored and awesomekling committed May 21, 2022
1 parent 7550017 commit 9a6bd85
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 0 deletions.
3 changes: 3 additions & 0 deletions Kernel/API/POSIX/sys/stat.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
8 changes: 8 additions & 0 deletions Kernel/API/Syscall.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions Kernel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions Kernel/FileSystem/Ext2FileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {};
Expand Down
19 changes: 19 additions & 0 deletions Kernel/FileSystem/VirtualFileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
1 change: 1 addition & 0 deletions Kernel/FileSystem/VirtualFileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class VirtualFileSystem {
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);
Expand Down
1 change: 1 addition & 0 deletions Kernel/Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ class Process final
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*>);
Expand Down
61 changes: 61 additions & 0 deletions Kernel/Syscalls/utimensat.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (c) 2022, Ariel Don <[email protected]>
*
* 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_ ? O__NOERROR : 0;
TRY(VirtualFileSystem::the().utimensat(path->view(), *base, atime, mtime, follow_symlink));
return 0;
}

}
47 changes: 47 additions & 0 deletions Userland/Libraries/LibC/fcntl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <stdarg.h>
#include <string.h>
#include <syscall.h>
#include <time.h>

extern "C" {

Expand Down Expand Up @@ -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_ flag or no flags.
if (flag & ~AT_SYMLINK_) {
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);
}
}
3 changes: 3 additions & 0 deletions Userland/Libraries/LibC/fcntl.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#pragma once

#include <Kernel/API/POSIX/fcntl.h>
#include <Kernel/API/POSIX/sys/stat.h>

__BEGIN_DECLS

Expand All @@ -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

0 comments on commit 9a6bd85

Please sign in to comment.