Newer
Older
minerva / Kernel / Syscalls / mount.cpp
@minerva minerva on 13 Jul 6 KB Initial commit
/*
 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
 * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <AK/FixedStringBuffer.h>
#include <Kernel/FileSystem/Custody.h>
#include <Kernel/FileSystem/MountFile.h>
#include <Kernel/FileSystem/VirtualFileSystem.h>
#include <Kernel/Tasks/Process.h>

namespace Kernel {

ErrorOr<FlatPtr> Process::sys$copy_mount(Userspace<Syscall::SC_copy_mount_params const*> user_params)
{
    VERIFY_NO_PROCESS_BIG_LOCK(this);
    TRY(require_promise(Pledge::mount));
    auto credentials = this->credentials();
    if (!credentials->is_superuser())
        return EPERM;

    auto params = TRY(copy_typed_from_user(user_params));

    // NOTE: If some userspace program uses MS_REMOUNT, return EINVAL to indicate that we never want this
    // flag to appear in the mount table...
    if (params.flags & MS_REMOUNT || params.flags & MS_BIND)
        return Error::from_errno(EINVAL);

    auto original_path = TRY(try_copy_kstring_from_user(params.original_path));
    auto target_path = TRY(try_copy_kstring_from_user(params.target_path));

    auto mount_original_context = TRY(context_for_mount_operation(params.original_vfs_root_context_id, original_path->view()));
    auto mount_target_context = TRY(context_for_mount_operation(params.target_vfs_root_context_id, target_path->view()));

    TRY(VirtualFileSystem::copy_mount(
        mount_original_context.custody,
        mount_target_context.vfs_root_context,
        mount_target_context.custody,
        params.flags));
    return 0;
}

ErrorOr<FlatPtr> Process::sys$fsopen(Userspace<Syscall::SC_fsopen_params const*> user_params)
{
    VERIFY_NO_PROCESS_BIG_LOCK(this);
    TRY(require_promise(Pledge::mount));
    auto credentials = this->credentials();
    if (!credentials->is_superuser())
        return Error::from_errno(EPERM);
    auto params = TRY(copy_typed_from_user(user_params));
    // NOTE: 16 characters should be enough for any fstype today and in the future.
    auto fs_type_string = TRY(get_syscall_name_string_fixed_buffer<16>(params.fs_type));

    // NOTE: If some userspace program uses MS_REMOUNT, return EINVAL to indicate that we never want this
    // flag to appear in the mount table...
    if (params.flags & MS_REMOUNT || params.flags & MS_BIND)
        return Error::from_errno(EINVAL);

    auto const* fs_type_initializer = TRY(VirtualFileSystem::find_filesystem_type_initializer(fs_type_string.representable_view()));
    VERIFY(fs_type_initializer);
    auto mount_file = TRY(MountFile::create(*fs_type_initializer, params.flags));
    auto description = TRY(OpenFileDescription::try_create(move(mount_file)));
    return m_fds.with_exclusive([&](auto& fds) -> ErrorOr<FlatPtr> {
        auto new_fd = TRY(fds.allocate());
        fds[new_fd.fd].set(move(description), FD_CLOEXEC);
        return new_fd.fd;
    });
}

ErrorOr<FlatPtr> Process::sys$fsmount(Userspace<Syscall::SC_fsmount_params const*> user_params)
{
    VERIFY_NO_PROCESS_BIG_LOCK(this);
    TRY(require_promise(Pledge::mount));
    auto credentials = this->credentials();
    if (!credentials->is_superuser())
        return Error::from_errno(EPERM);

    auto params = TRY(copy_typed_from_user(user_params));
    auto mount_description = TRY(open_file_description(params.mount_fd));
    if (!mount_description->is_mount_file())
        return Error::from_errno(EINVAL);

    RefPtr<OpenFileDescription> source_description = TRY(open_file_description_ignoring_negative(params.source_fd));
    auto target = TRY(try_copy_kstring_from_user(params.target));
    auto mount_target_context = TRY(context_for_mount_operation(params.vfs_root_context_id, target->view()));
    auto flags = mount_description->mount_file()->mount_flags();
    TRY(VirtualFileSystem::mount(mount_target_context.vfs_root_context, *mount_description->mount_file(), source_description.ptr(), mount_target_context.custody, flags));
    return 0;
}

ErrorOr<FlatPtr> Process::sys$remount(Userspace<Syscall::SC_remount_params const*> user_params)
{
    VERIFY_NO_PROCESS_BIG_LOCK(this);
    TRY(require_promise(Pledge::mount));
    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 current_vfs_root_context = Process::current().vfs_root_context();
    auto mount_target_context = TRY(context_for_mount_operation(params.vfs_root_context_id, target->view()));
    TRY(VirtualFileSystem::remount(mount_target_context.vfs_root_context, mount_target_context.custody, params.flags));
    return 0;
}

ErrorOr<FlatPtr> Process::sys$bindmount(Userspace<Syscall::SC_bindmount_params const*> user_params)
{
    VERIFY_NO_PROCESS_BIG_LOCK(this);
    TRY(require_promise(Pledge::mount));
    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 mount_target_context = TRY(context_for_mount_operation(params.vfs_root_context_id, target->view()));

    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::bind_mount(mount_target_context.vfs_root_context, *description->custody(), mount_target_context.custody, params.flags));
    return 0;
}

ErrorOr<FlatPtr> Process::sys$umount(Userspace<Syscall::SC_umount_params const*> user_params)
{
    VERIFY_NO_PROCESS_BIG_LOCK(this);
    auto credentials = this->credentials();
    if (!credentials->is_superuser())
        return EPERM;

    TRY(require_promise(Pledge::mount));

    auto params = TRY(copy_typed_from_user(user_params));
    auto target = TRY(try_copy_kstring_from_user(params.target));
    auto mount_target_context = TRY(context_for_mount_operation(params.vfs_root_context_id, target->view()));
    TRY(VirtualFileSystem::unmount(mount_target_context.vfs_root_context, mount_target_context.custody));
    return 0;
}

}