diff --git a/Base/usr/share/man/man2/bindmount.md b/Base/usr/share/man/man2/bindmount.md new file mode 100644 index 0000000000..17e9aa1a4f --- /dev/null +++ b/Base/usr/share/man/man2/bindmount.md @@ -0,0 +1,39 @@ +## Name + +bindmount - create a bindmount from `source_fd` to a target path. + +## Synopsis + +```**c++ +#include + +ErrorOr bindmount(int source_fd, StringView target, int flags); +``` + +## Description + +`bindmount()` create a bindmount from `source_fd` to a target path `target`, with mount flags of `flags`. + +The following `flags` are supported: + +* `MS_NODEV`: Disallow opening any devices from this file system. +* `MS_NOEXEC`: Disallow executing any executables from this file system. +* `MS_NOSUID`: Ignore set-user-id bits on executables from this file system. +* `MS_RDONLY`: Mount the filesystem read-only. +* `MS_WXALLOWED`: Allow W^X protection circumvention for executables on this file system. +* `MS_AXALLOWED`: Allow anonymous executable mappings for executables on this file system. +* `MS_NOREGULAR`: Disallow opening any regular files from this file system. + +These flags can be used as a security measure to limit the possible abuses of the mounted file system. + +## Errors + +* `EINVAL`: The `flags` value contains deprecated flags such as `MS_REMOUNT` or `MS_BIND`. +* `EPERM`: The current process does not have superuser privileges. +* `ENODEV`: The `source_fd` is not an open file descriptor to a valid filesystem inode. + +All of the usual path resolution errors may also occur. + +## See also + +* [`mount`(2)](help://man/2/mount) diff --git a/Base/usr/share/man/man2/mount.md b/Base/usr/share/man/man2/mount.md index ccbb66c520..b15b4fc870 100644 --- a/Base/usr/share/man/man2/mount.md +++ b/Base/usr/share/man/man2/mount.md @@ -34,9 +34,7 @@ The following `flags` are supported: * `MS_NODEV`: Disallow opening any devices from this file system. * `MS_NOEXEC`: Disallow executing any executables from this file system. * `MS_NOSUID`: Ignore set-user-id bits on executables from this file system. -* `MS_BIND`: Perform a bind-mount (see below). * `MS_RDONLY`: Mount the filesystem read-only. -* `MS_REMOUNT`: Remount an already mounted filesystem (see below). * `MS_WXALLOWED`: Allow W^X protection circumvention for executables on this file system. * `MS_AXALLOWED`: Allow anonymous executable mappings for executables on this file system. * `MS_NOREGULAR`: Disallow opening any regular files from this file system. @@ -57,11 +55,6 @@ itself, which may be useful for changing mount flags for a part of a filesystem. ### Remounting -If `MS_REMOUNT` is specified in `flags`, `source_fd` and `fs_type` are ignored, -and a remount is performed instead. `target` must point to an existing mount -point. The mount flags for that mount point are reset to `flags` (except the -`MS_REMOUNT` flag itself, which is stripped from the value). - Note that remounting a file system will only affect future operations with the file system, not any already opened files. For example, if you open a directory on a filesystem that's mounted with `MS_NODEV`, then remount the filesystem to @@ -74,14 +67,9 @@ in mount flags of the underlying file system. To "refresh" the working directory to use the new mount flags after remounting a filesystem, a process can call `chdir()` with the path to the same directory. -Similarly, to change the mount flags used by the root directory, a process can -remount the root filesystem using `MS_REMOUNT`. -However, it only have a noticeable effect if -the kernel was to launch more userspace processes directly, the way it does -launch the initial userspace process. - ## Errors +* `EINVAL`: The `flags` value contains deprecated flags such as `MS_REMOUNT` or `MS_BIND`. * `EFAULT`: The `fs_type` or `target` are invalid strings. * `EPERM`: The current process does not have superuser privileges. * `ENODEV`: The `fs_type` is unrecognized, or the file descriptor to source is @@ -99,3 +87,5 @@ All of the usual path resolution errors may also occur. ## See also * [`mount`(8)](help://man/8/mount) +* [`remount`(2)](help://man/2/remount) +* [`bindmount`(2)](help://man/2/bindmount) diff --git a/Base/usr/share/man/man2/remount.md b/Base/usr/share/man/man2/remount.md new file mode 100644 index 0000000000..d4384caaf3 --- /dev/null +++ b/Base/usr/share/man/man2/remount.md @@ -0,0 +1,39 @@ +## Name + +remount - remount a filesystem with new mount flags + +## Synopsis + +```**c++ +#include + +ErrorOr remount(StringView target, int flags); +``` + +## Description + +`remount()` mounts a filesystem that is mounted at `target` with new mount flags of `flags`. + +The following `flags` are supported: + +* `MS_NODEV`: Disallow opening any devices from this file system. +* `MS_NOEXEC`: Disallow executing any executables from this file system. +* `MS_NOSUID`: Ignore set-user-id bits on executables from this file system. +* `MS_RDONLY`: Mount the filesystem read-only. +* `MS_WXALLOWED`: Allow W^X protection circumvention for executables on this file system. +* `MS_AXALLOWED`: Allow anonymous executable mappings for executables on this file system. +* `MS_NOREGULAR`: Disallow opening any regular files from this file system. + +These flags can be used as a security measure to limit the possible abuses of the mounted file system. + +## Errors + +* `EINVAL`: The `flags` value contains deprecated flags such as `MS_REMOUNT` or `MS_BIND`. +* `EPERM`: The current process does not have superuser privileges. +* `ENODEV`: No mount point was found for `target` path target. + +All of the usual path resolution errors may also occur. + +## See also + +* [`mount`(2)](help://man/2/mount) diff --git a/Kernel/API/Syscall.h b/Kernel/API/Syscall.h index 122eb2d826..ccf039da33 100644 --- a/Kernel/API/Syscall.h +++ b/Kernel/API/Syscall.h @@ -53,6 +53,7 @@ enum class NeedsBigProcessLock { S(annotate_mapping, NeedsBigProcessLock::No) \ S(beep, NeedsBigProcessLock::No) \ S(bind, NeedsBigProcessLock::No) \ + S(bindmount, NeedsBigProcessLock::No) \ S(chdir, NeedsBigProcessLock::No) \ S(chmod, NeedsBigProcessLock::No) \ S(chown, NeedsBigProcessLock::No) \ @@ -153,6 +154,7 @@ enum class NeedsBigProcessLock { S(recvfd, NeedsBigProcessLock::No) \ S(recvmsg, NeedsBigProcessLock::Yes) \ S(rename, NeedsBigProcessLock::No) \ + S(remount, NeedsBigProcessLock::No) \ S(rmdir, NeedsBigProcessLock::No) \ S(scheduler_get_parameters, NeedsBigProcessLock::No) \ S(scheduler_set_parameters, NeedsBigProcessLock::No) \ @@ -437,6 +439,17 @@ struct SC_mount_params { int flags; }; +struct SC_remount_params { + StringArgument target; + int flags; +}; + +struct SC_bindmount_params { + StringArgument target; + int source_fd; + int flags; +}; + struct SC_pledge_params { StringArgument promises; StringArgument execpromises; diff --git a/Kernel/Process.h b/Kernel/Process.h index 7251aa5be2..14b92e5c49 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -454,6 +454,8 @@ public: ErrorOr sys$jail_create(Userspace user_params); ErrorOr sys$jail_attach(Userspace user_params); ErrorOr sys$get_root_session_id(pid_t force_sid); + ErrorOr sys$remount(Userspace user_params); + ErrorOr sys$bindmount(Userspace user_params); enum SockOrPeerName { SockName, diff --git a/Kernel/Syscalls/mount.cpp b/Kernel/Syscalls/mount.cpp index 1c80abf617..c8387b4d84 100644 --- a/Kernel/Syscalls/mount.cpp +++ b/Kernel/Syscalls/mount.cpp @@ -77,6 +77,10 @@ ErrorOr Process::sys$mount(Userspace u return EPERM; auto params = TRY(copy_typed_from_user(user_params)); + if (params.flags & MS_REMOUNT) + return EINVAL; + if (params.flags & MS_BIND) + return EINVAL; auto source_fd = params.source_fd; auto target = TRY(try_copy_kstring_from_user(params.target)); @@ -91,25 +95,6 @@ ErrorOr Process::sys$mount(Userspace u auto target_custody = TRY(VirtualFileSystem::the().resolve_path(credentials, target->view(), current_directory())); - if (params.flags & MS_REMOUNT) { - // We're not creating a new mount, we're updating an existing one! - TRY(VirtualFileSystem::the().remount(target_custody, params.flags & ~MS_REMOUNT)); - return 0; - } - - if (params.flags & MS_BIND) { - // We're doing a bind mount. - if (description_or_error.is_error()) - return description_or_error.release_error(); - auto description = description_or_error.release_value(); - if (!description->custody()) { - // We only support bind-mounting inodes, not arbitrary files. - return ENODEV; - } - TRY(VirtualFileSystem::the().bind_mount(*description->custody(), target_custody, params.flags)); - return 0; - } - RefPtr fs; if (!description_or_error.is_error()) { @@ -126,6 +111,54 @@ ErrorOr Process::sys$mount(Userspace u return 0; } +ErrorOr Process::sys$remount(Userspace user_params) +{ + VERIFY_NO_PROCESS_BIG_LOCK(this); + TRY(require_no_promises()); + auto credentials = this->credentials(); + if (!credentials->is_superuser()) + return EPERM; + + auto params = TRY(copy_typed_from_user(user_params)); + if (params.flags & MS_REMOUNT) + return EINVAL; + if (params.flags & MS_BIND) + return EINVAL; + + auto target = TRY(try_copy_kstring_from_user(params.target)); + auto target_custody = TRY(VirtualFileSystem::the().resolve_path(credentials, target->view(), current_directory())); + TRY(VirtualFileSystem::the().remount(target_custody, params.flags)); + return 0; +} + +ErrorOr Process::sys$bindmount(Userspace user_params) +{ + VERIFY_NO_PROCESS_BIG_LOCK(this); + TRY(require_no_promises()); + auto credentials = this->credentials(); + if (!credentials->is_superuser()) + return EPERM; + + auto params = TRY(copy_typed_from_user(user_params)); + if (params.flags & MS_REMOUNT) + return EINVAL; + if (params.flags & MS_BIND) + return EINVAL; + + auto source_fd = params.source_fd; + auto target = TRY(try_copy_kstring_from_user(params.target)); + auto target_custody = TRY(VirtualFileSystem::the().resolve_path(credentials, target->view(), current_directory())); + + auto description = TRY(open_file_description(source_fd)); + if (!description->custody()) { + // NOTE: We only support bind-mounting inodes, not arbitrary files. + return ENODEV; + } + + TRY(VirtualFileSystem::the().bind_mount(*description->custody(), target_custody, params.flags)); + return 0; +} + ErrorOr Process::sys$umount(Userspace user_mountpoint, size_t mountpoint_length) { VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); diff --git a/Userland/Libraries/LibCore/System.cpp b/Userland/Libraries/LibCore/System.cpp index a9ae27414e..965f17a7a8 100644 --- a/Userland/Libraries/LibCore/System.cpp +++ b/Userland/Libraries/LibCore/System.cpp @@ -232,6 +232,33 @@ ErrorOr ptrace_peekbuf(pid_t tid, void const* tracee_addr, Bytes destinati HANDLE_SYSCALL_RETURN_VALUE("ptrace_peekbuf", rc, {}); } +ErrorOr bindmount(int source_fd, StringView target, int flags) +{ + if (target.is_null()) + return Error::from_errno(EFAULT); + + Syscall::SC_bindmount_params params { + { target.characters_without_null_termination(), target.length() }, + source_fd, + flags, + }; + int rc = syscall(SC_bindmount, ¶ms); + HANDLE_SYSCALL_RETURN_VALUE("bindmount", rc, {}); +} + +ErrorOr remount(StringView target, int flags) +{ + if (target.is_null()) + return Error::from_errno(EFAULT); + + Syscall::SC_remount_params params { + { target.characters_without_null_termination(), target.length() }, + flags + }; + int rc = syscall(SC_remount, ¶ms); + HANDLE_SYSCALL_RETURN_VALUE("remount", rc, {}); +} + ErrorOr mount(int source_fd, StringView target, StringView fs_type, int flags) { if (target.is_null() || fs_type.is_null()) diff --git a/Userland/Libraries/LibCore/System.h b/Userland/Libraries/LibCore/System.h index 7da769eb96..444f37a540 100644 --- a/Userland/Libraries/LibCore/System.h +++ b/Userland/Libraries/LibCore/System.h @@ -59,6 +59,8 @@ ErrorOr sendfd(int sockfd, int fd); ErrorOr recvfd(int sockfd, int options); ErrorOr ptrace_peekbuf(pid_t tid, void const* tracee_addr, Bytes destination_buf); ErrorOr mount(int source_fd, StringView target, StringView fs_type, int flags); +ErrorOr bindmount(int source_fd, StringView target, int flags); +ErrorOr remount(StringView target, int flags); ErrorOr umount(StringView mount_point); ErrorOr ptrace(int request, pid_t tid, void* address, void* data); ErrorOr disown(pid_t pid); diff --git a/Userland/Services/SystemServer/main.cpp b/Userland/Services/SystemServer/main.cpp index 90099d6855..dc16075949 100644 --- a/Userland/Services/SystemServer/main.cpp +++ b/Userland/Services/SystemServer/main.cpp @@ -401,10 +401,7 @@ static ErrorOr populate_devtmpfs() static ErrorOr prepare_synthetic_filesystems() { - // FIXME: Don't hardcode the fs type as the ext2 filesystem and once there's - // more than this filesystem implementation (which is suitable for usage on - // physical storage), find a way to detect it. - TRY(Core::System::mount(-1, "/"sv, "ext2"sv, MS_REMOUNT | MS_NODEV | MS_NOSUID | MS_RDONLY)); + TRY(Core::System::remount("/"sv, MS_NODEV | MS_NOSUID | MS_RDONLY)); // FIXME: Find a better way to all of this stuff, without hardcoding all of this! TRY(Core::System::mount(-1, "/proc"sv, "proc"sv, MS_NOSUID)); TRY(Core::System::mount(-1, "/sys"sv, "sys"sv, 0)); diff --git a/Userland/Utilities/mount.cpp b/Userland/Utilities/mount.cpp index c5bc783531..28f7752077 100644 --- a/Userland/Utilities/mount.cpp +++ b/Userland/Utilities/mount.cpp @@ -95,7 +95,15 @@ static bool mount_by_line(DeprecatedString const& line) dbgln("Mounting {} ({}) on {}", filename, fstype, mountpoint); - auto error_or_void = Core::System::mount(fd, mountpoint, fstype, flags); + ErrorOr error_or_void; + + if (flags & MS_BIND) + error_or_void = Core::System::bindmount(fd, mountpoint, flags & ~MS_BIND); + else if (flags & MS_REMOUNT) + error_or_void = Core::System::remount(mountpoint, flags & ~MS_REMOUNT); + else + error_or_void = Core::System::mount(fd, mountpoint, fstype, flags); + if (error_or_void.is_error()) { warnln("Failed to mount {} (FD: {}) ({}) on {}: {}", filename, fd, fstype, mountpoint, error_or_void.error()); return false; @@ -215,15 +223,27 @@ ErrorOr serenity_main(Main::Arguments arguments) return 0; } - if (!source.is_empty() && !mountpoint.is_empty()) { - if (fs_type.is_empty()) - fs_type = "ext2"sv; + if (source.is_empty() && !mountpoint.is_empty()) { int flags = !options.is_empty() ? parse_options(options) : 0; + if (!(flags & MS_REMOUNT)) + return Error::from_string_literal("Expected valid source."); + TRY(Core::System::remount(mountpoint, flags & ~MS_REMOUNT)); + return 0; + } + if (!source.is_empty() && !mountpoint.is_empty()) { + int flags = !options.is_empty() ? parse_options(options) : 0; int const fd = TRY(get_source_fd(source)); - TRY(Core::System::mount(fd, mountpoint, fs_type, flags)); - + if (flags & MS_BIND) { + TRY(Core::System::bindmount(fd, mountpoint, flags & ~MS_BIND)); + } else if (flags & MS_REMOUNT) { + TRY(Core::System::remount(mountpoint, flags & ~MS_REMOUNT)); + } else { + if (fs_type.is_empty()) + fs_type = "ext2"sv; + TRY(Core::System::mount(fd, mountpoint, fs_type, flags)); + } return 0; }