From 182016d7c0059e9cfd74124c3d732060d31f3cec Mon Sep 17 00:00:00 2001 From: Daniel Bertalan Date: Tue, 11 Jan 2022 16:51:34 +0100 Subject: [PATCH] Kernel+LibC+LibCore+UE: Implement `fchmodat(2)` This function is an extended version of `chmod(2)` that lets one control whether to dereference symlinks, and specify a file descriptor to a directory that will be used as the base for relative paths. --- Kernel/API/Syscall.h | 7 +++++++ Kernel/FileSystem/VirtualFileSystem.cpp | 4 ++-- Kernel/FileSystem/VirtualFileSystem.h | 2 +- Kernel/Process.h | 2 +- Kernel/Syscalls/chmod.cpp | 19 +++++++++++++++--- .../DevTools/UserspaceEmulator/Emulator.h | 2 +- .../UserspaceEmulator/Emulator_syscalls.cpp | 13 ++++++++---- Userland/Libraries/LibC/stat.cpp | 20 ++++++++++++++++++- Userland/Libraries/LibC/sys/stat.h | 1 + Userland/Libraries/LibCore/System.cpp | 8 +++++++- 10 files changed, 64 insertions(+), 14 deletions(-) diff --git a/Kernel/API/Syscall.h b/Kernel/API/Syscall.h index d02184fdfd..1fbea5601a 100644 --- a/Kernel/API/Syscall.h +++ b/Kernel/API/Syscall.h @@ -478,6 +478,13 @@ struct SC_statvfs_params { struct statvfs* buf; }; +struct SC_chmod_params { + int dirfd; + StringArgument path; + u16 mode; + int follow_symlinks; +}; + void initialize(); int sync(); diff --git a/Kernel/FileSystem/VirtualFileSystem.cpp b/Kernel/FileSystem/VirtualFileSystem.cpp index 2bcf342329..873d71535e 100644 --- a/Kernel/FileSystem/VirtualFileSystem.cpp +++ b/Kernel/FileSystem/VirtualFileSystem.cpp @@ -434,9 +434,9 @@ ErrorOr VirtualFileSystem::chmod(Custody& custody, mode_t mode) return inode.chmod(mode); } -ErrorOr VirtualFileSystem::chmod(StringView path, mode_t mode, Custody& base) +ErrorOr VirtualFileSystem::chmod(StringView path, mode_t mode, Custody& base, int options) { - auto custody = TRY(resolve_path(path, base)); + auto custody = TRY(resolve_path(path, base, nullptr, options)); return chmod(custody, mode); } diff --git a/Kernel/FileSystem/VirtualFileSystem.h b/Kernel/FileSystem/VirtualFileSystem.h index a1386dbb14..9fbde822ca 100644 --- a/Kernel/FileSystem/VirtualFileSystem.h +++ b/Kernel/FileSystem/VirtualFileSystem.h @@ -57,7 +57,7 @@ public: ErrorOr unlink(StringView path, Custody& base); ErrorOr symlink(StringView target, StringView linkpath, Custody& base); ErrorOr rmdir(StringView path, Custody& base); - ErrorOr chmod(StringView path, mode_t, Custody& base); + ErrorOr chmod(StringView path, mode_t, Custody& base, int options = 0); ErrorOr chmod(Custody&, mode_t); ErrorOr chown(StringView path, UserID, GroupID, Custody& base, int options); ErrorOr chown(Custody&, UserID, GroupID); diff --git a/Kernel/Process.h b/Kernel/Process.h index 75f7fc2f6f..3874774186 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -359,7 +359,7 @@ public: ErrorOr sys$rmdir(Userspace pathname, size_t path_length); ErrorOr sys$mount(Userspace); ErrorOr sys$umount(Userspace mountpoint, size_t mountpoint_length); - ErrorOr sys$chmod(Userspace pathname, size_t path_length, mode_t); + ErrorOr sys$chmod(Userspace); ErrorOr sys$fchmod(int fd, mode_t); ErrorOr sys$chown(Userspace); ErrorOr sys$fchown(int fd, UserID, GroupID); diff --git a/Kernel/Syscalls/chmod.cpp b/Kernel/Syscalls/chmod.cpp index 34af280f07..9b79682afd 100644 --- a/Kernel/Syscalls/chmod.cpp +++ b/Kernel/Syscalls/chmod.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2022, Daniel Bertalan * * SPDX-License-Identifier: BSD-2-Clause */ @@ -10,12 +11,24 @@ namespace Kernel { -ErrorOr Process::sys$chmod(Userspace user_path, size_t path_length, mode_t mode) +ErrorOr Process::sys$chmod(Userspace user_params) { VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); TRY(require_promise(Pledge::fattr)); - auto path = TRY(get_syscall_path_argument(user_path, path_length)); - TRY(VirtualFileSystem::the().chmod(path->view(), mode, current_directory())); + auto params = TRY(copy_typed_from_user(user_params)); + auto path = TRY(get_syscall_path_argument(params.path)); + + RefPtr base; + if (params.dirfd == AT_FDCWD) { + base = current_directory(); + } else { + auto base_description = TRY(fds().open_file_description(params.dirfd)); + if (!base_description->custody()) + return EINVAL; + base = base_description->custody(); + } + + TRY(VirtualFileSystem::the().chmod(path->view(), params.mode, *base, params.follow_symlinks ? 0 : O_NOFOLLOW_NOERROR)); return 0; } diff --git a/Userland/DevTools/UserspaceEmulator/Emulator.h b/Userland/DevTools/UserspaceEmulator/Emulator.h index 90c4d8644f..b62f66fdd3 100644 --- a/Userland/DevTools/UserspaceEmulator/Emulator.h +++ b/Userland/DevTools/UserspaceEmulator/Emulator.h @@ -136,7 +136,7 @@ private: int virt$beep(); int virt$bind(int sockfd, FlatPtr address, socklen_t address_length); int virt$chdir(FlatPtr, size_t); - int virt$chmod(FlatPtr, size_t, mode_t); + int virt$chmod(FlatPtr); int virt$chown(FlatPtr); int virt$clock_gettime(int, FlatPtr); int virt$clock_nanosleep(FlatPtr); diff --git a/Userland/DevTools/UserspaceEmulator/Emulator_syscalls.cpp b/Userland/DevTools/UserspaceEmulator/Emulator_syscalls.cpp index adba1a8faf..ed29cbd10d 100644 --- a/Userland/DevTools/UserspaceEmulator/Emulator_syscalls.cpp +++ b/Userland/DevTools/UserspaceEmulator/Emulator_syscalls.cpp @@ -52,7 +52,7 @@ u32 Emulator::virt_syscall(u32 function, u32 arg1, u32 arg2, u32 arg3) case SC_chdir: return virt$chdir(arg1, arg2); case SC_chmod: - return virt$chmod(arg1, arg2, arg3); + return virt$chmod(arg1); case SC_chown: return virt$chown(arg1); case SC_clock_gettime: @@ -418,10 +418,15 @@ int Emulator::virt$dbgputstr(FlatPtr characters, int length) return 0; } -int Emulator::virt$chmod(FlatPtr path_addr, size_t path_length, mode_t mode) +int Emulator::virt$chmod(FlatPtr params_addr) { - auto path = mmu().copy_buffer_from_vm(path_addr, path_length); - return syscall(SC_chmod, path.data(), path.size(), mode); + Syscall::SC_chmod_params params; + mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); + + auto path = mmu().copy_buffer_from_vm((FlatPtr)params.path.characters, params.path.length); + params.path.characters = (char const*)path.data(); + params.path.length = path.size(); + return syscall(SC_chmod, ¶ms); } int Emulator::virt$chown(FlatPtr params_addr) diff --git a/Userland/Libraries/LibC/stat.cpp b/Userland/Libraries/LibC/stat.cpp index b5f12904a3..3516de8df3 100644 --- a/Userland/Libraries/LibC/stat.cpp +++ b/Userland/Libraries/LibC/stat.cpp @@ -34,12 +34,30 @@ int mkdir(const char* pathname, mode_t mode) // https://pubs.opengroup.org/onlinepubs/9699919799/functions/chmod.html int chmod(const char* pathname, mode_t mode) +{ + return fchmodat(AT_FDCWD, pathname, mode, 0); +} + +// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html +int fchmodat(int dirfd, char const* pathname, mode_t mode, int flags) { if (!pathname) { errno = EFAULT; return -1; } - int rc = syscall(SC_chmod, pathname, strlen(pathname), mode); + + if (flags & ~AT_SYMLINK_NOFOLLOW) { + errno = EINVAL; + return -1; + } + + Syscall::SC_chmod_params params { + dirfd, + { pathname, strlen(pathname) }, + mode, + !(flags & AT_SYMLINK_NOFOLLOW) + }; + int rc = syscall(SC_chmod, ¶ms); __RETURN_WITH_ERRNO(rc, rc, -1); } diff --git a/Userland/Libraries/LibC/sys/stat.h b/Userland/Libraries/LibC/sys/stat.h index b24e807f87..9aeb90611f 100644 --- a/Userland/Libraries/LibC/sys/stat.h +++ b/Userland/Libraries/LibC/sys/stat.h @@ -15,6 +15,7 @@ __BEGIN_DECLS mode_t umask(mode_t); int chmod(const char* pathname, mode_t); +int fchmodat(int fd, char const* path, mode_t mode, int flag); int fchmod(int fd, mode_t); int mkdir(const char* pathname, mode_t); int mkfifo(const char* pathname, mode_t); diff --git a/Userland/Libraries/LibCore/System.cpp b/Userland/Libraries/LibCore/System.cpp index b01e412d62..ac83adbda2 100644 --- a/Userland/Libraries/LibCore/System.cpp +++ b/Userland/Libraries/LibCore/System.cpp @@ -386,7 +386,13 @@ ErrorOr chmod(StringView pathname, mode_t mode) return Error::from_syscall("chmod"sv, -EFAULT); #ifdef __serenity__ - int rc = syscall(SC_chmod, pathname.characters_without_null_termination(), pathname.length(), mode); + Syscall::SC_chmod_params params { + AT_FDCWD, + { pathname.characters_without_null_termination(), pathname.length() }, + mode, + true + }; + int rc = syscall(SC_chmod, ¶ms); HANDLE_SYSCALL_RETURN_VALUE("chmod"sv, rc, {}); #else String path = pathname;