1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-18 21:15:09 +00:00

Kernel+LibCore+LibC: Split the mount syscall into multiple syscalls

This is a preparation before we can create a usable mechanism to use
filesystem-specific mount flags.
To keep some compatibility with userland code, LibC and LibCore mount
functions are kept being usable, but now instead of doing an "atomic"
syscall, they do multiple syscalls to perform the complete procedure of
mounting a filesystem.

The FileBackedFileSystem IntrusiveList in the VFS code is now changed to
be protected by a Mutex, because when we mount a new filesystem, we need
to check if a filesystem is already created for a given source_fd so we
do a scan for that OpenFileDescription in that list. If we fail to find
an already-created filesystem we create a new one and register it in the
list if we successfully mounted it. We use a Mutex because we might need
to initiate disk access during the filesystem creation, which will take
other mutexes in other parts of the kernel, therefore making it not
possible to take a spinlock while doing this.
This commit is contained in:
Liav A 2022-12-15 11:42:40 +02:00 committed by Jelle Raaijmakers
parent 63fc36d00d
commit 23a7ccf607
39 changed files with 651 additions and 214 deletions

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2022-2023, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -23,11 +24,60 @@
#include <Kernel/Sections.h>
#include <Kernel/Tasks/Process.h>
#include <Kernel/FileSystem/DevPtsFS/FileSystem.h>
#include <Kernel/FileSystem/Ext2FS/FileSystem.h>
#include <Kernel/FileSystem/FATFS/FileSystem.h>
#include <Kernel/FileSystem/ISO9660FS/FileSystem.h>
#include <Kernel/FileSystem/Plan9FS/FileSystem.h>
#include <Kernel/FileSystem/ProcFS/FileSystem.h>
#include <Kernel/FileSystem/RAMFS/FileSystem.h>
#include <Kernel/FileSystem/SysFS/FileSystem.h>
namespace Kernel {
static Singleton<VirtualFileSystem> s_the;
static constexpr int root_mount_flags = 0;
static ErrorOr<void> handle_mount_boolean_flag_as_invalid(Span<u8>, StringView, bool)
{
return EINVAL;
}
static ErrorOr<void> handle_mount_unsigned_integer_flag_as_invalid(Span<u8>, StringView, u64)
{
return EINVAL;
}
static ErrorOr<void> handle_mount_signed_integer_flag_as_invalid(Span<u8>, StringView, i64)
{
return EINVAL;
}
static ErrorOr<void> handle_mount_ascii_string_flag_as_invalid(Span<u8>, StringView, StringView)
{
return EINVAL;
}
static constexpr FileSystemInitializer s_initializers[] = {
{ "proc"sv, "ProcFS"sv, false, false, false, {}, ProcFS::try_create, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid },
{ "devpts"sv, "DevPtsFS"sv, false, false, false, {}, DevPtsFS::try_create, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid },
{ "sys"sv, "SysFS"sv, false, false, false, {}, SysFS::try_create, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid },
{ "ram"sv, "RAMFS"sv, false, false, false, {}, RAMFS::try_create, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid },
{ "ext2"sv, "Ext2FS"sv, true, true, true, Ext2FS::try_create, {}, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid },
{ "9p"sv, "Plan9FS"sv, true, true, true, Plan9FS::try_create, {}, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid },
{ "iso9660"sv, "ISO9660FS"sv, true, true, true, ISO9660FS::try_create, {}, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid },
{ "fat"sv, "FATFS"sv, true, true, true, FATFS::try_create, {}, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid },
};
ErrorOr<FileSystemInitializer const*> VirtualFileSystem::find_filesystem_type_initializer(StringView fs_type)
{
for (auto& initializer_entry : s_initializers) {
if (fs_type == initializer_entry.short_name || fs_type == initializer_entry.name)
return &initializer_entry;
}
return ENODEV;
}
UNMAP_AFTER_INIT void VirtualFileSystem::initialize()
{
s_the.ensure_instance();
@ -59,14 +109,14 @@ bool VirtualFileSystem::mount_point_exists_at_inode(InodeIdentifier inode_identi
});
}
ErrorOr<void> VirtualFileSystem::mount(FileSystem& fs, Custody& mount_point, int flags)
ErrorOr<void> VirtualFileSystem::add_file_system_to_mount_table(FileSystem& file_system, Custody& mount_point, int flags)
{
auto new_mount = TRY(adopt_nonnull_own_or_enomem(new (nothrow) Mount(fs, &mount_point, flags)));
auto new_mount = TRY(adopt_nonnull_own_or_enomem(new (nothrow) Mount(file_system, &mount_point, flags)));
return m_mounts.with([&](auto& mounts) -> ErrorOr<void> {
auto& inode = mount_point.inode();
dbgln("VirtualFileSystem: FileSystemID {}, Mounting {} at inode {} with flags {}",
fs.fsid(),
fs.class_name(),
dbgln("VirtualFileSystem: FileSystemID {} (non file-backed), Mounting {} at inode {} with flags {}",
file_system.fsid(),
file_system.class_name(),
inode.identifier(),
flags);
if (mount_point_exists_at_inode(inode.identifier())) {
@ -84,14 +134,8 @@ ErrorOr<void> VirtualFileSystem::mount(FileSystem& fs, Custody& mount_point, int
// the FileSystem once it is no longer mounted).
if (mounted_count == 1) {
m_file_systems_list.with([&](auto& fs_list) {
fs_list.append(fs);
fs_list.append(file_system);
});
if (fs.is_file_backed()) {
auto& file_backed_fs = static_cast<FileBackedFileSystem&>(fs);
m_file_backed_file_systems_list.with([&](auto& fs_list) {
fs_list.append(file_backed_fs);
});
}
}
});
@ -102,6 +146,65 @@ ErrorOr<void> VirtualFileSystem::mount(FileSystem& fs, Custody& mount_point, int
});
}
ErrorOr<void> VirtualFileSystem::mount(MountFile& mount_file, OpenFileDescription* source_description, Custody& mount_point, int flags)
{
auto const& file_system_initializer = mount_file.file_system_initializer();
if (!source_description) {
if (file_system_initializer.requires_open_file_description)
return ENOTSUP;
if (!file_system_initializer.create)
return ENOTSUP;
RefPtr<FileSystem> fs;
TRY(mount_file.mount_file_system_specific_data().with_exclusive([&](auto& mount_specific_data) -> ErrorOr<void> {
fs = TRY(file_system_initializer.create(mount_specific_data->bytes()));
return {};
}));
VERIFY(fs);
TRY(fs->initialize());
TRY(add_file_system_to_mount_table(*fs, mount_point, flags));
return {};
}
// NOTE: Although it might be OK to support creating filesystems
// without providing an actual file descriptor to their create() method
// because the caller of this function actually supplied a valid file descriptor,
// this will only make things complicated in the future, so we should block
// this kind of behavior.
if (!file_system_initializer.requires_open_file_description)
return ENOTSUP;
if (file_system_initializer.requires_block_device && !source_description->file().is_block_device())
return ENOTBLK;
if (file_system_initializer.requires_seekable_file && !source_description->file().is_seekable()) {
dbgln("mount: this is not a seekable file");
return ENODEV;
}
// NOTE: If there's an associated file description with the filesystem, we could
// try to first find it from the VirtualFileSystem filesystem list and if it was not found,
// then create it and add it.
VERIFY(file_system_initializer.create_with_fd);
return m_file_backed_file_systems_list.with_exclusive([&](auto& list) -> ErrorOr<void> {
RefPtr<FileSystem> fs;
for (auto& node : list) {
if ((&node.file_description() == source_description) || (&node.file() == &source_description->file())) {
fs = node;
break;
}
}
if (!fs) {
TRY(mount_file.mount_file_system_specific_data().with_exclusive([&](auto& mount_specific_data) -> ErrorOr<void> {
fs = TRY(file_system_initializer.create_with_fd(*source_description, mount_specific_data->bytes()));
return {};
}));
TRY(fs->initialize());
}
TRY(add_file_system_to_mount_table(*fs, mount_point, flags));
list.append(static_cast<FileBackedFileSystem&>(*fs));
return {};
});
}
ErrorOr<void> VirtualFileSystem::bind_mount(Custody& source, Custody& mount_point, int flags)
{
auto new_mount = TRY(adopt_nonnull_own_or_enomem(new (nothrow) Mount(source.inode(), mount_point, flags)));
@ -163,41 +266,42 @@ ErrorOr<void> VirtualFileSystem::unmount(Custody& mountpoint_custody)
auto custody_path = TRY(mountpoint_custody.try_serialize_absolute_path());
dbgln("VirtualFileSystem: unmount called with inode {} on mountpoint {}", guest_inode.identifier(), custody_path->view());
return m_mounts.with([&](auto& mounts) -> ErrorOr<void> {
for (auto& mount : mounts) {
if (&mount.guest() != &guest_inode)
continue;
auto mountpoint_path = TRY(mount.absolute_path());
if (custody_path->view() != mountpoint_path->view())
continue;
NonnullRefPtr<FileSystem> fs = mount.guest_fs();
TRY(fs->prepare_to_unmount());
fs->mounted_count({}).with([&](auto& mounted_count) {
VERIFY(mounted_count > 0);
if (mounted_count == 1) {
dbgln("VirtualFileSystem: Unmounting file system {} for the last time...", fs->fsid());
m_file_systems_list.with([&](auto& list) {
list.remove(*fs);
});
if (fs->is_file_backed()) {
dbgln("VirtualFileSystem: Unmounting file backed file system {} for the last time...", fs->fsid());
auto& file_backed_fs = static_cast<FileBackedFileSystem&>(*fs);
m_file_backed_file_systems_list.with([&](auto& list) {
list.remove(file_backed_fs);
return m_file_backed_file_systems_list.with_exclusive([&](auto& file_backed_fs_list) -> ErrorOr<void> {
TRY(m_mounts.with([&](auto& mounts) -> ErrorOr<void> {
for (auto& mount : mounts) {
if (&mount.guest() != &guest_inode)
continue;
auto mountpoint_path = TRY(mount.absolute_path());
if (custody_path->view() != mountpoint_path->view())
continue;
NonnullRefPtr<FileSystem> fs = mount.guest_fs();
TRY(fs->prepare_to_unmount());
fs->mounted_count({}).with([&](auto& mounted_count) {
VERIFY(mounted_count > 0);
if (mounted_count == 1) {
dbgln("VirtualFileSystem: Unmounting file system {} for the last time...", fs->fsid());
m_file_systems_list.with([&](auto& list) {
list.remove(*fs);
});
if (fs->is_file_backed()) {
dbgln("VirtualFileSystem: Unmounting file backed file system {} for the last time...", fs->fsid());
auto& file_backed_fs = static_cast<FileBackedFileSystem&>(*fs);
file_backed_fs_list.remove(file_backed_fs);
}
} else {
mounted_count--;
}
} else {
mounted_count--;
}
});
dbgln("VirtualFileSystem: Unmounting file system {}...", fs->fsid());
mount.m_vfs_list_node.remove();
// Note: This is balanced by a `new` statement that is happening in various places before inserting the Mount object to the list.
delete &mount;
return {};
}
dbgln("VirtualFileSystem: Nothing mounted on inode {}", guest_inode.identifier());
return ENODEV;
});
dbgln("VirtualFileSystem: Unmounting file system {}...", fs->fsid());
mount.m_vfs_list_node.remove();
// NOTE: This is balanced by a `new` statement that is happening in various places before inserting the Mount object to the list.
delete &mount;
return {};
}
dbgln("VirtualFileSystem: Nothing mounted on inode {}", guest_inode.identifier());
return ENODEV;
}));
return {};
});
}
@ -219,7 +323,7 @@ ErrorOr<void> VirtualFileSystem::mount_root(FileSystem& fs)
if (fs.is_file_backed()) {
auto pseudo_path = TRY(static_cast<FileBackedFileSystem&>(fs).file_description().pseudo_path());
dmesgln("VirtualFileSystem: mounted root({}) from {} ({})", fs.fsid(), fs.class_name(), pseudo_path);
m_file_backed_file_systems_list.with([&](auto& list) {
m_file_backed_file_systems_list.with_exclusive([&](auto& list) {
list.append(static_cast<FileBackedFileSystem&>(fs));
});
} else {
@ -339,28 +443,6 @@ ErrorOr<InodeMetadata> VirtualFileSystem::lookup_metadata(Credentials const& cre
return custody->inode().metadata();
}
ErrorOr<NonnullRefPtr<FileBackedFileSystem>> VirtualFileSystem::find_already_existing_or_create_file_backed_file_system(OpenFileDescription& description, Function<ErrorOr<NonnullRefPtr<FileSystem>>(OpenFileDescription&)> callback)
{
return TRY(m_file_backed_file_systems_list.with([&](auto& list) -> ErrorOr<NonnullRefPtr<FileBackedFileSystem>> {
for (auto& node : list) {
if (&node.file_description() == &description) {
return node;
}
if (&node.file() == &description.file()) {
return node;
}
}
auto fs = TRY(callback(description));
// The created FileSystem is only added to the file_systems_lists
// when the FS has been successfully initialized and mounted
// (in VirtualFileSystem::mount()). This prevents file systems which
// fail to initialize or mount from existing in the list when the
// FileSystem is destroyed after failure.
return static_ptr_cast<FileBackedFileSystem>(fs);
}));
}
ErrorOr<NonnullRefPtr<OpenFileDescription>> VirtualFileSystem::open(Credentials const& credentials, StringView path, int options, mode_t mode, Custody& base, Optional<UidAndGid> owner)
{
return open(Process::current(), credentials, path, options, mode, base, owner);