ladybird/Kernel/Syscalls/utimensat.cpp
Liav A cbf78975f1 Kernel: Add the futimens syscall
We have a problem with the original utimensat syscall because when we
do call LibC futimens function, internally we provide an empty path,
and the Kernel get_syscall_path_argument method will detect this as an
invalid path.

This happens to spit an error for example in the touch utility, so if a
user is running "touch non_existing_file", it will create that file, but
the user will still see an error coming from LibC futimens function.

This new syscall gets an open file description and it provides the same
functionality as utimensat, on the specified open file description.
The new syscall will be used later by LibC to properly implement LibC
futimens function so the situation described with relation to the
"touch" utility could be fixed.
2023-04-10 10:21:28 +02:00

81 lines
2.4 KiB
C++

/*
* 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$futimens(Userspace<Syscall::SC_futimens_params const*> user_params)
{
VERIFY_NO_PROCESS_BIG_LOCK(this);
TRY(require_promise(Pledge::fattr));
auto params = TRY(copy_typed_from_user(user_params));
auto now = kgettimeofday().to_timespec();
timespec times[2];
if (params.times) {
TRY(copy_from_user(times, params.times, sizeof(times)));
if (times[0].tv_nsec == UTIME_NOW)
times[0] = now;
if (times[1].tv_nsec == UTIME_NOW)
times[1] = now;
} else {
// According to POSIX, both access and modification times are set to
// the current time given a nullptr.
times[0] = now;
times[1] = now;
}
auto description = TRY(open_file_description(params.fd));
if (!description->inode())
return EBADF;
if (!description->custody())
return EBADF;
auto& atime = times[0];
auto& mtime = times[1];
TRY(VirtualFileSystem::the().do_utimens(credentials(), *description->custody(), atime, mtime));
return 0;
}
ErrorOr<FlatPtr> Process::sys$utimensat(Userspace<Syscall::SC_utimensat_params const*> user_params)
{
VERIFY_NO_PROCESS_BIG_LOCK(this);
TRY(require_promise(Pledge::fattr));
auto params = TRY(copy_typed_from_user(user_params));
auto now = kgettimeofday().to_timespec();
int follow_symlink = params.flag & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW_NOERROR : 0;
timespec times[2];
if (params.times) {
TRY(copy_from_user(times, params.times, sizeof(times)));
if (times[0].tv_nsec == UTIME_NOW)
times[0] = now;
if (times[1].tv_nsec == UTIME_NOW)
times[1] = now;
} else {
// According to POSIX, both access and modification times are set to
// the current time given a nullptr.
times[0] = now;
times[1] = now;
}
auto path = TRY(get_syscall_path_argument(params.path));
auto base = TRY(custody_for_dirfd(params.dirfd));
auto& atime = times[0];
auto& mtime = times[1];
TRY(VirtualFileSystem::the().utimensat(credentials(), path->view(), *base, atime, mtime, follow_symlink));
return 0;
}
}