Kernel/FileSystem: Introduce the VFSRootContext class

The VFSRootContext class, as its name suggests, holds a context for a
root directory with its mount table and the root custody/inode in the
same class.

The idea is derived from the Linux mount namespace mechanism.
It mimicks the concept of the ProcessList object, but it is adjusted for
a root directory tree context.
In contrast to the ProcessList concept, processes that share the default
VFSRootContext can't see other VFSRootContext related properties such as
as the mount table and root custody/inode.

To accommodate to this change progressively, we internally create 2 main
VFS root contexts for now - one for kernel processes (as they don't need
to care about VFS root contexts for the most part), and another for all
userspace programs.
This separation allows us to continue pretending for userspace that
everything is "normal" as it is used to be, until we introduce proper
interfaces in the mount-related syscalls as well as in the SysFS.

We make VFSRootContext objects being listed, as another preparation
before we could expose interfaces to userspace.
As a result, the PowerStateSwitchTask now iterates on all contexts
and tear them down one by one.
This commit is contained in:
Liav A. 2024-01-26 11:10:06 +02:00 committed by Tim Schumacher
parent 02157e3eaf
commit 01e1af732b
44 changed files with 714 additions and 380 deletions

View file

@ -310,6 +310,10 @@ extern "C" [[noreturn]] UNMAP_AFTER_INIT NO_SANITIZE_COVERAGE void init([[maybe_
__stack_chk_guard = get_fast_random<uintptr_t>();
// NOTE: Initialize the empty VFS root context just before we need to create
// kernel processes.
VFSRootContext::initialize_empty_ramfs_root_context_for_kernel_processes();
Process::initialize();
Scheduler::initialize();
@ -451,10 +455,14 @@ void init_stage2(void*)
dbgln_if(STORAGE_DEVICE_DEBUG, "Boot device {} not found, sleeping 2 seconds", kernel_command_line().root_device());
(void)Thread::current()->sleep(Duration::from_seconds(2));
}
if (VirtualFileSystem::the().mount_root(StorageManagement::the().root_filesystem()).is_error()) {
PANIC("VirtualFileSystem::mount_root failed");
auto first_process_vfs_context_or_error = StorageManagement::the().create_first_vfs_root_context();
if (first_process_vfs_context_or_error.is_error()) {
PANIC("StorageManagement::create_first_vfs_root_context failed");
}
auto first_process_vfs_context = first_process_vfs_context_or_error.release_value();
// Switch out of early boot mode.
g_not_in_early_boot.set();
@ -472,7 +480,7 @@ void init_stage2(void*)
dmesgln("Running first user process: {}", userspace_init);
dmesgln("Init (first) process args: {}", init_args);
auto init_or_error = Process::create_user_process(userspace_init, UserID(0), GroupID(0), move(init_args), {}, tty0);
auto init_or_error = Process::create_user_process(userspace_init, UserID(0), GroupID(0), move(init_args), {}, move(first_process_vfs_context), tty0);
if (init_or_error.is_error())
PANIC("init_stage2: Error spawning init process: {}", init_or_error.error());

View file

@ -226,6 +226,7 @@ set(KERNEL_SOURCES
FileSystem/SysFS/Subsystems/Kernel/Configuration/DumpKmallocStack.cpp
FileSystem/SysFS/Subsystems/Kernel/Configuration/StringVariable.cpp
FileSystem/SysFS/Subsystems/Kernel/Configuration/UBSANDeadly.cpp
FileSystem/VFSRootContext.cpp
FileSystem/VirtualFileSystem.cpp
Firmware/ACPI/Initialize.cpp
Firmware/ACPI/Parser.cpp

View file

@ -26,6 +26,7 @@
#include <Kernel/Devices/Storage/StorageManagement.h>
#include <Kernel/Devices/Storage/VirtIO/VirtIOBlockController.h>
#include <Kernel/FileSystem/Ext2FS/FileSystem.h>
#include <Kernel/FileSystem/MountFile.h>
#include <Kernel/FileSystem/VirtualFileSystem.h>
#include <Kernel/Library/Panic.h>
#include <LibPartition/EBRPartitionTable.h>
@ -43,6 +44,8 @@ static Atomic<u32> s_relative_ahci_controller_id;
static Atomic<u32> s_relative_nvme_controller_id;
static Atomic<u32> s_relative_sd_controller_id;
static constexpr int root_mount_flags = 0;
static constexpr StringView partition_uuid_prefix = "PARTUUID:"sv;
static constexpr StringView partition_number_prefix = "part"sv;
@ -446,25 +449,40 @@ u32 StorageManagement::generate_controller_id()
return s_controller_id.fetch_add(1);
}
NonnullRefPtr<FileSystem> StorageManagement::root_filesystem() const
ErrorOr<NonnullRefPtr<VFSRootContext>> StorageManagement::create_first_vfs_root_context() const
{
auto vfs_root_context = TRY(VFSRootContext::create_with_empty_ramfs());
auto const* fs_type_initializer = TRY(VirtualFileSystem::find_filesystem_type_initializer("ext2"sv));
VERIFY(fs_type_initializer);
auto mount_file = TRY(MountFile::create(*fs_type_initializer, root_mount_flags));
auto boot_device_description = boot_block_device();
if (!boot_device_description) {
dump_storage_devices_and_partitions();
PANIC("StorageManagement: Couldn't find a suitable device to boot from");
}
auto description_or_error = OpenFileDescription::try_create(boot_device_description.release_nonnull());
VERIFY(!description_or_error.is_error());
auto description = TRY(OpenFileDescription::try_create(boot_device_description.release_nonnull()));
Array<u8, PAGE_SIZE> mount_specific_data;
mount_specific_data.fill(0);
auto file_system = Ext2FS::try_create(description_or_error.release_value(), mount_specific_data.span()).release_value();
RefPtr<FileSystem> fs;
TRY(VirtualFileSystem::the().m_file_backed_file_systems_list.with_exclusive([&](auto& list) -> ErrorOr<void> {
fs = TRY(VirtualFileSystem::create_and_initialize_filesystem_from_mount_file_and_description(list, mount_file, description));
// NOTE: Fake a mounted count of 1 so the called VirtualFileSystem function in the
// next pivot_root logic block thinks everything is OK.
fs->mounted_count(Badge<StorageManagement> {}).with([](auto& mounted_count) {
mounted_count++;
});
list.append(static_cast<FileBackedFileSystem&>(*fs));
return {};
}));
VERIFY(fs);
if (auto result = file_system->initialize(); result.is_error()) {
dump_storage_devices_and_partitions();
PANIC("StorageManagement: Couldn't open root filesystem: {}", result.error());
}
return file_system;
TRY(VirtualFileSystem::the().pivot_root_by_copying_mounted_fs_instance(vfs_root_context, *fs, root_mount_flags));
// NOTE: Return the mounted count to normal now we have it really mounted.
fs->mounted_count(Badge<StorageManagement> {}).with([](auto& mounted_count) {
mounted_count--;
});
return vfs_root_context;
}
UNMAP_AFTER_INIT void StorageManagement::initialize(bool poll)
@ -491,5 +509,4 @@ StorageManagement& StorageManagement::the()
{
return *s_the;
}
}

View file

@ -29,7 +29,7 @@ public:
static StorageManagement& the();
bool determine_boot_device(StringView boot_argument);
NonnullRefPtr<FileSystem> root_filesystem() const;
ErrorOr<NonnullRefPtr<VFSRootContext>> create_first_vfs_root_context() const;
static MinorNumber generate_storage_minor_number();

View file

@ -15,8 +15,11 @@ ErrorOr<NonnullRefPtr<Custody>> CustodyBase::resolve() const
{
if (m_base)
return *m_base;
if (KLexicalPath::is_absolute(m_path))
return VirtualFileSystem::the().root_custody();
if (KLexicalPath::is_absolute(m_path)) {
return Process::current().vfs_root_context()->root_custody().with([](auto& custody) -> NonnullRefPtr<Custody> {
return custody;
});
}
return Process::current().custody_for_dirfd({}, m_dirfd);
}

View file

@ -67,6 +67,8 @@ public:
virtual u8 internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const { return entry.file_type; }
SpinlockProtected<size_t, LockRank::FileSystem>& mounted_count(Badge<VirtualFileSystem>) { return m_attach_count; }
SpinlockProtected<size_t, LockRank::FileSystem>& mounted_count(Badge<StorageManagement>) { return m_attach_count; }
SpinlockProtected<size_t, LockRank::FileSystem>& mounted_count(Badge<VFSRootContext>) { return m_attach_count; }
protected:
FileSystem();

View file

@ -13,6 +13,7 @@
#include <Kernel/FileSystem/Inode.h>
#include <Kernel/FileSystem/InodeWatcher.h>
#include <Kernel/FileSystem/OpenFileDescription.h>
#include <Kernel/FileSystem/VFSRootContext.h>
#include <Kernel/FileSystem/VirtualFileSystem.h>
#include <Kernel/Library/KBufferBuilder.h>
#include <Kernel/Memory/SharedInodeVMObject.h>
@ -52,7 +53,7 @@ void Inode::sync()
}
}
ErrorOr<NonnullRefPtr<Custody>> Inode::resolve_as_link(Credentials const& credentials, CustodyBase const& base, RefPtr<Custody>* out_parent, int options, int symlink_recursion_level) const
ErrorOr<NonnullRefPtr<Custody>> Inode::resolve_as_link(VFSRootContext const& vfs_root_context, Credentials const& credentials, CustodyBase const& base, RefPtr<Custody>* out_parent, int options, int symlink_recursion_level) const
{
// The default implementation simply treats the stored
// contents as a path and resolves that. That is, it
@ -64,7 +65,7 @@ ErrorOr<NonnullRefPtr<Custody>> Inode::resolve_as_link(Credentials const& creden
Array<u8, MAXPATHLEN> contents;
auto read_bytes = TRY(read_until_filled_or_end(0, contents.size(), UserOrKernelBuffer::for_kernel_buffer(contents.data()), nullptr));
return VirtualFileSystem::the().resolve_path(credentials, StringView { contents.span().trim(read_bytes) }, base, out_parent, options, symlink_recursion_level);
return VirtualFileSystem::the().resolve_path(vfs_root_context, credentials, StringView { contents.span().trim(read_bytes) }, base, out_parent, options, symlink_recursion_level);
}
Inode::Inode(FileSystem& fs, InodeIndex index)

View file

@ -73,7 +73,7 @@ public:
virtual ErrorOr<void> chmod(mode_t) = 0;
virtual ErrorOr<void> chown(UserID, GroupID) = 0;
ErrorOr<NonnullRefPtr<Custody>> resolve_as_link(Credentials const&, CustodyBase const& base, RefPtr<Custody>* out_parent, int options, int symlink_recursion_level) const;
ErrorOr<NonnullRefPtr<Custody>> resolve_as_link(VFSRootContext const&, Credentials const&, CustodyBase const& base, RefPtr<Custody>* out_parent, int options, int symlink_recursion_level) const;
virtual ErrorOr<int> get_block_address(int) { return ENOTSUP; }

View file

@ -10,6 +10,7 @@
#include <Kernel/FileSystem/Inode.h>
#include <Kernel/FileSystem/InodeFile.h>
#include <Kernel/FileSystem/OpenFileDescription.h>
#include <Kernel/FileSystem/VFSRootContext.h>
#include <Kernel/FileSystem/VirtualFileSystem.h>
#include <Kernel/Memory/PrivateInodeVMObject.h>
#include <Kernel/Memory/SharedInodeVMObject.h>

View file

@ -11,10 +11,9 @@
namespace Kernel {
Mount::Mount(NonnullRefPtr<FileSystem> guest_fs, RefPtr<Custody> host_custody, int flags)
: m_guest_fs(move(guest_fs))
, m_guest(m_guest_fs->root_inode())
, m_host_custody(move(host_custody))
Mount::Mount(NonnullRefPtr<Inode> source, int flags)
: m_guest_fs(source->fs())
, m_guest(move(source))
, m_flags(flags)
{
}

View file

@ -20,9 +20,12 @@ class Mount {
AK_MAKE_NONCOPYABLE(Mount);
AK_MAKE_NONMOVABLE(Mount);
friend class VirtualFileSystem;
friend class VFSRootContext;
public:
Mount(NonnullRefPtr<FileSystem>, RefPtr<Custody> host_custody, int flags);
// NOTE: This constructor is valid for VFSRootContext root inodes (as for the "/" directory)
Mount(NonnullRefPtr<Inode> source, int flags);
Mount(NonnullRefPtr<Inode> source, NonnullRefPtr<Custody> host_custody, int flags);
RefPtr<Inode const> host() const;

View file

@ -11,6 +11,7 @@
#include <Kernel/FileSystem/SysFS/Subsystems/Kernel/DiskUsage.h>
#include <Kernel/FileSystem/VirtualFileSystem.h>
#include <Kernel/Sections.h>
#include <Kernel/Tasks/Process.h>
namespace Kernel {
@ -27,7 +28,7 @@ UNMAP_AFTER_INIT SysFSDiskUsage::SysFSDiskUsage(SysFSDirectory const& parent_dir
ErrorOr<void> SysFSDiskUsage::try_generate(KBufferBuilder& builder)
{
auto array = TRY(JsonArraySerializer<>::try_create(builder));
TRY(VirtualFileSystem::the().for_each_mount([&array](auto& mount) -> ErrorOr<void> {
TRY(VirtualFileSystem::the().for_each_mount(Process::current().vfs_root_context(), [&array](auto& mount) -> ErrorOr<void> {
auto& fs = mount.guest_fs();
auto fs_object = TRY(array.add_object());
TRY(fs_object.add("class_name"sv, fs.class_name()));

View file

@ -0,0 +1,162 @@
/*
* Copyright (c) 2024, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/FileSystem/RAMFS/FileSystem.h>
#include <Kernel/FileSystem/VFSRootContext.h>
#include <Kernel/FileSystem/VirtualFileSystem.h>
namespace Kernel {
static RawPtr<VFSRootContext> s_empty_context;
static RawPtr<Custody> s_empty_context_custody;
static Atomic<u64> s_vfs_root_context_id = 0;
UNMAP_AFTER_INIT void VFSRootContext::initialize_empty_ramfs_root_context_for_kernel_processes()
{
s_empty_context = &MUST(VFSRootContext::create_with_empty_ramfs()).leak_ref();
// NOTE: This custody is immutable, so we expose it also outside of the SpinlockProtected
// template so it could be accessed immediately.
s_empty_context_custody = &s_empty_context->root_custody().with([](auto& custody) -> NonnullRefPtr<Custody> { return *custody; }).leak_ref();
// NOTE: We remove the context from the vfs root contexts list because
// we leaked a ref, and this context is artificially created only for
// kernel processes.
dbgln("VFSRootContext({}): Context is artificially made, detach from global list", s_empty_context->id());
VirtualFileSystem::the().all_root_contexts_list(Badge<VFSRootContext> {}).with([&](auto& list) {
list.remove(*s_empty_context);
});
}
VFSRootContext::VFSRootContext(NonnullRefPtr<Custody> custody)
: m_root_custody(custody)
, m_id(s_vfs_root_context_id.fetch_add(1))
{
}
VFSRootContext const& VFSRootContext::empty_context_for_kernel_processes()
{
VERIFY(s_empty_context);
return *s_empty_context;
}
Custody const& VFSRootContext::empty_context_custody_for_kernel_processes()
{
VERIFY(s_empty_context_custody);
return *s_empty_context_custody;
}
void VFSRootContext::add_to_mounts_list_and_increment_fs_mounted_count(DoBindMount do_bind_mount, IntrusiveList<&Mount::m_vfs_list_node>& mounts_list, NonnullOwnPtr<Mount> new_mount)
{
new_mount->guest_fs().mounted_count(Badge<VFSRootContext> {}).with([&](auto& mounted_count) {
// NOTE: We increment the mounted counter for the given filesystem regardless of the mount type,
// as a bind mount also counts as a normal mount from the perspective of unmount(),
// so we need to keep track of it in order for prepare_to_clear_last_mount() to work properly.
mounted_count++;
// NOTE: Add the filesystem to the file systems list if it's not a bind mount (this
// condition is VERIFYed within the if-case) and this is the first time this FileSystem is mounted.
// This is symmetric with VirtualFileSystem::unmount()'s `remove()` calls (which remove
// the FileSystem once it is no longer mounted).
if (mounted_count == 1) {
// NOTE: If the mounted_count is 1, and we try to do a bind-mount on an inode
// from this filesystem this means we have a bug because it's expected that
// we will always have an already-mounted filesystem when creating a new bind-mount.
//
// Even in the odd case of mounting a new filesystem, creating a new bind mount
// from a source Inode within the same filesystem and then removing the original mountpoint
// we should still maintain a mounted_count > 1 if somehow new bind mounts from the filesystem inodes
// appear.
VERIFY(do_bind_mount != DoBindMount::Yes);
VirtualFileSystem::the().all_file_systems_list({}).with([&](auto& fs_list) {
fs_list.append(new_mount->guest_fs());
});
}
});
// NOTE: Leak the mount pointer so it can be added to the mount list, but it won't be
// deleted after being added.
mounts_list.append(*new_mount.leak_ptr());
}
ErrorOr<NonnullRefPtr<VFSRootContext>> VFSRootContext::create_with_empty_ramfs()
{
auto fs = TRY(RAMFS::try_create({}));
TRY(fs->initialize());
auto root_custody = TRY(Custody::try_create(nullptr, ""sv, fs->root_inode(), 0));
auto context = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) VFSRootContext(root_custody)));
auto new_mount = TRY(adopt_nonnull_own_or_enomem(new (nothrow) Mount(fs->root_inode(), 0)));
TRY(context->mounts().with([&](auto& mounts) -> ErrorOr<void> {
dbgln("VFSRootContext({}): Root (\"/\") FileSystemID {}, Mounting {} at inode {} with flags {}",
context->id(),
fs->fsid(),
fs->class_name(),
root_custody->inode().identifier(),
0);
add_to_mounts_list_and_increment_fs_mounted_count(DoBindMount::No, mounts, move(new_mount));
return {};
}));
// Finally, add the context to the global list so it can be used.
VirtualFileSystem::the().all_root_contexts_list(Badge<VFSRootContext> {}).with([&](auto& list) {
list.append(*context);
});
return context;
}
void VFSRootContext::set_attached(Badge<Process>)
{
m_attributes.with([](auto& attributes) {
attributes.attached_by_process.set();
});
}
bool VFSRootContext::mount_point_exists_at_custody(Custody& mount_point)
{
return m_mounts.with([&](auto& mounts) -> bool {
return any_of(mounts, [&mount_point](auto const& existing_mount) {
return existing_mount.host_custody() && VirtualFileSystem::check_matching_absolute_path_hierarchy(*existing_mount.host_custody(), mount_point);
});
});
}
ErrorOr<void> VFSRootContext::add_new_mount(DoBindMount do_bind_mount, Inode& source, Custody& mount_point, int flags)
{
auto new_mount = TRY(adopt_nonnull_own_or_enomem(new (nothrow) Mount(source, mount_point, flags)));
return m_mounts.with([&](auto& mounts) -> ErrorOr<void> {
// NOTE: The VFSRootContext should be attached to the list if there's
// at least one mount in the mount table.
// We also should have at least one mount in the table because
// this method shouldn't be called for new contexts when adding
// their root mounts.
VERIFY(!mounts.is_empty());
VirtualFileSystem::the().all_root_contexts_list(Badge<VFSRootContext> {}).with([&](auto& list) {
VERIFY(list.contains(*this));
});
VERIFY(&new_mount->guest_fs() == &source.fs());
if (do_bind_mount == DoBindMount::No) {
VERIFY(&source == &source.fs().root_inode());
dbgln("VFSRootContext({}): FileSystemID {}, Mounting {} at inode {} with flags {}",
id(),
source.fs().fsid(),
source.fs().class_name(),
mount_point.inode().identifier(),
flags);
} else {
dbgln("VFSRootContext({}): Bind-mounting inode {} at inode {}", id(), source.identifier(), mount_point.inode().identifier());
}
if (mount_point_exists_at_custody(mount_point)) {
dbgln("VFSRootContext({}): Mounting unsuccessful - inode {} is already a mount-point.", id(), mount_point.inode().identifier());
return EBUSY;
}
add_to_mounts_list_and_increment_fs_mounted_count(do_bind_mount, mounts, move(new_mount));
return {};
});
}
}

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2024, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/IntrusiveList.h>
#include <AK/Noncopyable.h>
#include <AK/RefPtr.h>
#include <AK/SetOnce.h>
#include <Kernel/FileSystem/Custody.h>
#include <Kernel/FileSystem/FileSystem.h>
#include <Kernel/FileSystem/Mount.h>
#include <Kernel/Locking/SpinlockProtected.h>
namespace Kernel {
class VFSRootContext : public AtomicRefCounted<VFSRootContext> {
AK_MAKE_NONCOPYABLE(VFSRootContext);
AK_MAKE_NONMOVABLE(VFSRootContext);
friend class VirtualFileSystem;
public:
AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, IndexID);
static VFSRootContext const& empty_context_for_kernel_processes();
static Custody const& empty_context_custody_for_kernel_processes();
static void initialize_empty_ramfs_root_context_for_kernel_processes();
static ErrorOr<NonnullRefPtr<VFSRootContext>> create_with_empty_ramfs();
static ErrorOr<NonnullRefPtr<VFSRootContext>> create_empty();
SpinlockProtected<NonnullRefPtr<Custody>, LockRank::None>& root_custody() { return m_root_custody; }
SpinlockProtected<NonnullRefPtr<Custody>, LockRank::None> const& root_custody() const { return m_root_custody; }
SpinlockProtected<IntrusiveList<&Mount::m_vfs_list_node>, LockRank::None>& mounts() { return m_mounts; }
SpinlockProtected<IntrusiveList<&Mount::m_vfs_list_node>, LockRank::None> const& mounts() const { return m_mounts; }
struct Attributes {
SetOnce attached_by_process;
SetOnce immutable;
};
SpinlockProtected<Attributes, LockRank::None>& attributes() { return m_attributes; }
bool mount_point_exists_at_custody(Custody& mount_point);
enum class DoBindMount {
Yes,
No,
};
ErrorOr<void> add_new_mount(DoBindMount, Inode& source, Custody& mount_point, int flags);
IndexID id() const { return m_id; }
void set_attached(Badge<Process>);
private:
VFSRootContext(NonnullRefPtr<Custody> custody);
static void add_to_mounts_list_and_increment_fs_mounted_count(DoBindMount do_bind_mount, IntrusiveList<&Mount::m_vfs_list_node>&, NonnullOwnPtr<Mount>);
SpinlockProtected<Attributes, LockRank::None> m_attributes {};
SpinlockProtected<NonnullRefPtr<Custody>, LockRank::None> m_root_custody;
SpinlockProtected<IntrusiveList<&Mount::m_vfs_list_node>, LockRank::None> m_mounts {};
IntrusiveListNode<VFSRootContext, NonnullRefPtr<VFSRootContext>> m_list_node;
IndexID m_id;
};
}

View file

@ -1,6 +1,6 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2022-2023, Liav A. <liavalb@hotmail.co.il>
* Copyright (c) 2022-2024, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -40,7 +40,6 @@
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)
{
@ -120,71 +119,26 @@ bool VirtualFileSystem::check_matching_absolute_path_hierarchy(Custody const& fi
return true;
}
bool VirtualFileSystem::mount_point_exists_at_custody(Custody& mount_point)
{
return m_mounts.with([&](auto& mounts) -> bool {
return any_of(mounts, [&mount_point](auto const& existing_mount) {
return existing_mount.host_custody() && check_matching_absolute_path_hierarchy(*existing_mount.host_custody(), mount_point);
});
});
}
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(file_system, &mount_point, flags)));
return m_mounts.with([&](auto& mounts) -> ErrorOr<void> {
auto& mount_point_inode = mount_point.inode();
dbgln("VirtualFileSystem: FileSystemID {}, Mounting {} at inode {} with flags {}",
file_system.fsid(),
file_system.class_name(),
mount_point_inode.identifier(),
flags);
if (mount_point_exists_at_custody(mount_point)) {
dbgln("VirtualFileSystem: Mounting unsuccessful - inode {} is already a mount-point.", mount_point_inode.identifier());
return EBUSY;
}
// Note: Actually add a mount for the filesystem and increment the filesystem mounted count
new_mount->guest_fs().mounted_count({}).with([&](auto& mounted_count) {
mounted_count++;
// When this is the first time this FileSystem is mounted,
// begin managing the FileSystem by adding it to the list of
// managed file systems. This is symmetric with
// VirtualFileSystem::unmount()'s `remove()` calls (which remove
// the FileSystem once it is no longer mounted).
if (mounted_count == 1) {
m_file_systems_list.with([&](auto& fs_list) {
fs_list.append(file_system);
});
}
});
// NOTE: Leak the mount pointer so it can be added to the mount list, but it won't be
// deleted after being added.
mounts.append(*new_mount.leak_ptr());
return {};
});
}
ErrorOr<void> VirtualFileSystem::mount(MountFile& mount_file, OpenFileDescription* source_description, Custody& mount_point, int flags)
ErrorOr<NonnullRefPtr<FileSystem>> VirtualFileSystem::create_and_initialize_filesystem_from_mount_file(MountFile& mount_file)
{
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));
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());
return fs.release_nonnull();
}
ErrorOr<void> VirtualFileSystem::verify_mount_file_and_description_requirements(MountFile& mount_file, OpenFileDescription& source_description)
{
auto const& file_system_initializer = mount_file.file_system_initializer();
// 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,
@ -193,71 +147,71 @@ ErrorOr<void> VirtualFileSystem::mount(MountFile& mount_file, OpenFileDescriptio
if (!file_system_initializer.requires_open_file_description)
return ENOTSUP;
if (file_system_initializer.requires_block_device && !source_description->file().is_block_device())
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()) {
if (file_system_initializer.requires_seekable_file && !source_description.file().is_seekable()) {
dbgln("mount: this is not a seekable file");
return ENODEV;
}
return {};
}
ErrorOr<NonnullRefPtr<FileSystem>> VirtualFileSystem::create_and_initialize_filesystem_from_mount_file_and_description(IntrusiveList<&FileBackedFileSystem::m_file_backed_file_system_node>& file_backed_fs_list, MountFile& mount_file, OpenFileDescription& source_description)
{
// 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.
auto const& file_system_initializer = mount_file.file_system_initializer();
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());
}
if (source_description->file().is_loop_device()) {
auto& device = static_cast<LoopDevice&>(source_description->file());
auto path = TRY(device.custody().try_serialize_absolute_path());
dbgln("VirtualFileSystem: mounting from loop device {}, originated from {}", device.index(), path->view());
RefPtr<FileSystem> fs;
for (auto& node : file_backed_fs_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());
}
if (source_description.file().is_loop_device()) {
auto& device = static_cast<LoopDevice&>(source_description.file());
auto path = TRY(device.custody().try_serialize_absolute_path());
dbgln("VirtualFileSystem: mounting from loop device {}, originated from {}", device.index(), path->view());
}
return fs.release_nonnull();
}
TRY(add_file_system_to_mount_table(*fs, mount_point, flags));
ErrorOr<void> VirtualFileSystem::mount(VFSRootContext& context, MountFile& mount_file, OpenFileDescription* source_description, Custody& mount_point, int flags)
{
if (!source_description) {
auto fs = TRY(create_and_initialize_filesystem_from_mount_file(mount_file));
TRY(context.add_new_mount(VFSRootContext::DoBindMount::No, fs->root_inode(), mount_point, flags));
return {};
}
TRY(verify_mount_file_and_description_requirements(mount_file, *source_description));
return m_file_backed_file_systems_list.with_exclusive([&](auto& list) -> ErrorOr<void> {
auto fs = TRY(create_and_initialize_filesystem_from_mount_file_and_description(list, mount_file, *source_description));
TRY(context.add_new_mount(VFSRootContext::DoBindMount::No, fs->root_inode(), mount_point, flags));
list.append(static_cast<FileBackedFileSystem&>(*fs));
return {};
});
}
ErrorOr<void> VirtualFileSystem::bind_mount(Custody& source, Custody& mount_point, int flags)
ErrorOr<void> VirtualFileSystem::bind_mount(VFSRootContext& context, Custody& source, Custody& mount_point, int flags)
{
auto new_mount = TRY(adopt_nonnull_own_or_enomem(new (nothrow) Mount(source.inode(), mount_point, flags)));
return m_mounts.with([&](auto& mounts) -> ErrorOr<void> {
auto& inode = mount_point.inode();
dbgln("VirtualFileSystem: Bind-mounting inode {} at inode {}", source.inode().identifier(), inode.identifier());
if (mount_point_exists_at_custody(mount_point)) {
dbgln("VirtualFileSystem: Bind-mounting unsuccessful - inode {} is already a mount-point.",
mount_point.inode().identifier());
return EBUSY;
}
// A bind mount also counts as a normal mount from the perspective of unmount(),
// so we need to keep track of it in order for prepare_to_clear_last_mount() to work properly.
new_mount->guest_fs().mounted_count({}).with([&](auto& count) { count++; });
// NOTE: Leak the mount pointer so it can be added to the mount list, but it won't be
// deleted after being added.
mounts.append(*new_mount.leak_ptr());
return {};
});
return context.add_new_mount(VFSRootContext::DoBindMount::Yes, source.inode(), mount_point, flags);
}
ErrorOr<void> VirtualFileSystem::remount(Custody& mount_point, int new_flags)
ErrorOr<void> VirtualFileSystem::remount(VFSRootContext& context, Custody& mount_point, int new_flags)
{
dbgln("VirtualFileSystem: Remounting inode {}", mount_point.inode().identifier());
TRY(apply_to_mount_for_host_custody(mount_point, [new_flags](auto& mount) {
TRY(apply_to_mount_for_host_custody(context, mount_point, [new_flags](auto& mount) {
mount.set_flags(new_flags);
}));
return {};
@ -291,104 +245,139 @@ void VirtualFileSystem::lock_all_filesystems()
fs->m_lock.lock();
}
ErrorOr<void> VirtualFileSystem::unmount(Custody& mountpoint_custody)
ErrorOr<void> VirtualFileSystem::unmount(VFSRootContext& context, Custody& mountpoint_custody)
{
auto& guest_inode = mountpoint_custody.inode();
auto custody_path = TRY(mountpoint_custody.try_serialize_absolute_path());
return unmount(guest_inode, custody_path->view());
return unmount(context, guest_inode, custody_path->view());
}
ErrorOr<void> VirtualFileSystem::unmount(Inode& guest_inode, StringView custody_path)
void VirtualFileSystem::delete_mount_from_list(Mount& mount)
{
dbgln("VirtualFileSystem: Unmounting file system {}...", mount.guest_fs().fsid());
VERIFY(mount.m_vfs_list_node.is_in_list());
mount.m_vfs_list_node.remove();
delete &mount;
}
ErrorOr<void> VirtualFileSystem::remove_mount(Mount& mount, IntrusiveList<&FileBackedFileSystem::m_file_backed_file_system_node>& file_backed_fs_list)
{
NonnullRefPtr<FileSystem> fs = mount.guest_fs();
TRY(fs->prepare_to_unmount(mount.guest()));
fs->mounted_count(Badge<VirtualFileSystem> {}).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([&fs](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--;
}
});
delete_mount_from_list(mount);
return {};
}
ErrorOr<void> VirtualFileSystem::pivot_root_by_copying_mounted_fs_instance(VFSRootContext& context, FileSystem& fs, int root_mount_flags)
{
auto root_mount_point = TRY(Custody::try_create(nullptr, ""sv, fs.root_inode(), root_mount_flags));
auto new_mount = TRY(adopt_nonnull_own_or_enomem(new (nothrow) Mount(fs.root_inode(), root_mount_flags)));
return m_file_backed_file_systems_list.with_exclusive([&](auto& file_backed_file_systems_list) -> ErrorOr<void> {
return context.mounts().with([&](auto& mounts) -> ErrorOr<void> {
return fs.mounted_count(Badge<VirtualFileSystem> {}).with([&](auto& mounted_count) -> ErrorOr<void> {
// NOTE: If the mounted count is 0, then this filesystem is about to be
// deleted, so this must be a kernel bug as we don't include such filesystem
// in the m_file_backed_file_systems_list list anymore.
VERIFY(mounted_count > 0);
// NOTE: The mounts table should not be empty as it always need
// to have at least one mount!
VERIFY(!mounts.is_empty());
// NOTE: If we have many mounts in the table, then simply don't allow
// userspace to override them but instead require to unmount everything except
// the root mount first.
if (mounts.size_slow() != 1)
return EPERM;
auto& mount = *mounts.first();
TRY(remove_mount(mount, file_backed_file_systems_list));
VERIFY(mounts.is_empty());
dbgln("VFSRootContext({}): Root mount set to FileSystemID {}, Mounting {} at inode {} with flags {}",
context.id(),
new_mount->guest_fs().fsid(),
new_mount->guest_fs().class_name(),
root_mount_point->inode().identifier(),
root_mount_flags);
// NOTE: Leak the mount pointer so it can be added to the mount list, but it won't be
// deleted after being added.
mounts.append(*new_mount.leak_ptr());
// NOTE: We essentially do the same thing like VFSRootContext::add_to_mounts_list_and_increment_fs_mounted_count function
// but because we already locked the spinlock of the attach count, we can't call that function here.
mounted_count++;
// NOTE: Now fill the root custody with a valid custody for the new root mount.
context.root_custody().with([&root_mount_point](auto& custody) {
custody = root_mount_point;
});
return {};
});
});
});
}
ErrorOr<void> VirtualFileSystem::unmount(VFSRootContext& context, Inode& guest_inode, StringView custody_path)
{
return m_file_backed_file_systems_list.with_exclusive([&](auto& file_backed_fs_list) -> ErrorOr<void> {
TRY(m_mounts.with([&](auto& mounts) -> ErrorOr<void> {
TRY(context.mounts().with([&](auto& mounts) -> ErrorOr<void> {
bool did_unmount = false;
for (auto& mount : mounts) {
if (&mount.guest() != &guest_inode)
continue;
auto mountpoint_path = TRY(mount.absolute_path());
if (custody_path != mountpoint_path->view())
continue;
NonnullRefPtr<FileSystem> fs = mount.guest_fs();
TRY(fs->prepare_to_unmount(mount.guest()));
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--;
}
});
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("VFSRootContext({}): Unmounting {}...", context.id(), custody_path);
TRY(remove_mount(mount, file_backed_fs_list));
did_unmount = true;
break;
}
dbgln("VirtualFileSystem: Nothing mounted on inode {}", guest_inode.identifier());
return ENODEV;
if (!did_unmount) {
dbgln("VirtualFileSystem: Nothing mounted on inode {}", guest_inode.identifier());
return ENODEV;
}
// NOTE: The VFSRootContext mount table is not empty and we
// successfully deleted the desired mount from it, so return
// a success now.
if (!mounts.is_empty())
return {};
// NOTE: If the mount table is empty, then the VFSRootContext
// is no longer in valid state (each VFSRootContext at least should
// have a root mount), so remove it now.
m_root_contexts.with([&context](auto& list) {
dbgln("VirtualFileSystem: Nothing mounted in VFSRootContext({}), removing it", context.id());
list.remove(context);
});
return {};
}));
return {};
});
}
ErrorOr<void> VirtualFileSystem::mount_root(FileSystem& fs)
ErrorOr<void> VirtualFileSystem::apply_to_mount_for_host_custody(VFSRootContext& context, Custody const& current_custody, Function<void(Mount&)> callback)
{
if (m_root_inode) {
dmesgln("VirtualFileSystem: mount_root can't mount another root");
return EEXIST;
}
auto new_mount = TRY(adopt_nonnull_own_or_enomem(new (nothrow) Mount(fs, nullptr, root_mount_flags)));
auto& root_inode = fs.root_inode();
if (!root_inode.is_directory()) {
dmesgln("VirtualFileSystem: root inode ({}) for / is not a directory :(", root_inode.identifier());
return ENOTDIR;
}
m_root_inode = root_inode;
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_exclusive([&](auto& list) {
list.append(static_cast<FileBackedFileSystem&>(fs));
});
} else {
dmesgln("VirtualFileSystem: mounted root({}) from {}", fs.fsid(), fs.class_name());
}
m_file_systems_list.with([&](auto& fs_list) {
fs_list.append(fs);
});
fs.mounted_count({}).with([&](auto& mounted_count) {
mounted_count++;
});
// Note: Actually add a mount for the filesystem and increment the filesystem mounted count
m_mounts.with([&](auto& mounts) {
// NOTE: Leak the mount pointer so it can be added to the mount list, but it won't be
// deleted after being added.
mounts.append(*new_mount.leak_ptr());
});
RefPtr<Custody> new_root_custody = TRY(Custody::try_create(nullptr, ""sv, *m_root_inode, root_mount_flags));
m_root_custody.with([&](auto& root_custody) {
swap(root_custody, new_root_custody);
});
return {};
}
ErrorOr<void> VirtualFileSystem::apply_to_mount_for_host_custody(Custody const& current_custody, Function<void(Mount&)> callback)
{
return m_mounts.with([&](auto& mounts) -> ErrorOr<void> {
return context.mounts().with([&](auto& mounts) -> ErrorOr<void> {
// NOTE: We either search for the root mount or for a mount that has a parent custody!
if (!current_custody.parent()) {
for (auto& mount : mounts) {
@ -419,9 +408,9 @@ ErrorOr<void> VirtualFileSystem::traverse_directory_inode(Inode& dir_inode, Func
});
}
ErrorOr<void> VirtualFileSystem::utime(Credentials const& credentials, StringView path, CustodyBase const& base, time_t atime, time_t mtime)
ErrorOr<void> VirtualFileSystem::utime(VFSRootContext const& vfs_root_context, Credentials const& credentials, StringView path, CustodyBase const& base, time_t atime, time_t mtime)
{
auto custody = TRY(resolve_path(credentials, path, base));
auto custody = TRY(resolve_path(vfs_root_context, credentials, path, base));
auto& inode = custody->inode();
if (!credentials.is_superuser() && inode.metadata().uid != credentials.euid())
return EACCES;
@ -432,9 +421,9 @@ ErrorOr<void> VirtualFileSystem::utime(Credentials const& credentials, StringVie
return {};
}
ErrorOr<void> VirtualFileSystem::utimensat(Credentials const& credentials, StringView path, CustodyBase const& base, timespec const& atime, timespec const& mtime, int options)
ErrorOr<void> VirtualFileSystem::utimensat(VFSRootContext const& vfs_root_context, Credentials const& credentials, StringView path, CustodyBase const& base, timespec const& atime, timespec const& mtime, int options)
{
auto custody = TRY(resolve_path(credentials, path, base, nullptr, options));
auto custody = TRY(resolve_path(vfs_root_context, credentials, path, base, nullptr, options));
return do_utimens(credentials, custody, atime, mtime);
}
@ -455,24 +444,24 @@ ErrorOr<void> VirtualFileSystem::do_utimens(Credentials const& credentials, Cust
return {};
}
ErrorOr<InodeMetadata> VirtualFileSystem::lookup_metadata(Credentials const& credentials, StringView path, CustodyBase const& base, int options)
ErrorOr<InodeMetadata> VirtualFileSystem::lookup_metadata(VFSRootContext const& vfs_root_context, Credentials const& credentials, StringView path, CustodyBase const& base, int options)
{
auto custody = TRY(resolve_path(credentials, path, base, nullptr, options));
auto custody = TRY(resolve_path(vfs_root_context, credentials, path, base, nullptr, options));
return custody->inode().metadata();
}
ErrorOr<NonnullRefPtr<OpenFileDescription>> VirtualFileSystem::open(Credentials const& credentials, StringView path, int options, mode_t mode, CustodyBase const& base, Optional<UidAndGid> owner)
ErrorOr<NonnullRefPtr<OpenFileDescription>> VirtualFileSystem::open(VFSRootContext const& vfs_root_context, Credentials const& credentials, StringView path, int options, mode_t mode, CustodyBase const& base, Optional<UidAndGid> owner)
{
return open(Process::current(), credentials, path, options, mode, base, owner);
return open(Process::current(), vfs_root_context, credentials, path, options, mode, base, owner);
}
ErrorOr<NonnullRefPtr<OpenFileDescription>> VirtualFileSystem::open(Process const& process, Credentials const& credentials, StringView path, int options, mode_t mode, CustodyBase const& base, Optional<UidAndGid> owner)
ErrorOr<NonnullRefPtr<OpenFileDescription>> VirtualFileSystem::open(Process const& process, VFSRootContext const& vfs_root_context, Credentials const& credentials, StringView path, int options, mode_t mode, CustodyBase const& base, Optional<UidAndGid> owner)
{
if ((options & O_CREAT) && (options & O_DIRECTORY))
return EINVAL;
RefPtr<Custody> parent_custody;
auto custody_or_error = resolve_path(process, credentials, path, base, &parent_custody, options);
auto custody_or_error = resolve_path(process, vfs_root_context, credentials, path, base, &parent_custody, options);
if (custody_or_error.is_error()) {
// NOTE: ENOENT with a non-null parent custody signals us that the immediate parent
// of the file exists, but the file itself does not.
@ -557,13 +546,13 @@ ErrorOr<NonnullRefPtr<OpenFileDescription>> VirtualFileSystem::open(Process cons
return description;
}
ErrorOr<void> VirtualFileSystem::mknod(Credentials const& credentials, StringView path, mode_t mode, dev_t dev, CustodyBase const& base)
ErrorOr<void> VirtualFileSystem::mknod(VFSRootContext const& vfs_root_context, Credentials const& credentials, StringView path, mode_t mode, dev_t dev, CustodyBase const& base)
{
if (!is_regular_file(mode) && !is_block_device(mode) && !is_character_device(mode) && !is_fifo(mode) && !is_socket(mode))
return EINVAL;
RefPtr<Custody> parent_custody;
auto existing_file_or_error = resolve_path(credentials, path, base, &parent_custody);
auto existing_file_or_error = resolve_path(vfs_root_context, credentials, path, base, &parent_custody);
if (!existing_file_or_error.is_error())
return EEXIST;
if (!parent_custody)
@ -620,7 +609,7 @@ ErrorOr<NonnullRefPtr<OpenFileDescription>> VirtualFileSystem::create(Process co
return description;
}
ErrorOr<void> VirtualFileSystem::mkdir(Credentials const& credentials, StringView path, mode_t mode, CustodyBase const& base)
ErrorOr<void> VirtualFileSystem::mkdir(VFSRootContext const& vfs_root_context, Credentials const& credentials, StringView path, mode_t mode, CustodyBase const& base)
{
// Unlike in basically every other case, where it's only the last
// path component (the one being created) that is allowed not to
@ -636,7 +625,7 @@ ErrorOr<void> VirtualFileSystem::mkdir(Credentials const& credentials, StringVie
// FIXME: The errors returned by resolve_path_without_veil can leak information about paths that are not unveiled,
// e.g. when the error is EACCESS or similar.
auto base_custody = TRY(base.resolve());
auto result = resolve_path_without_veil(credentials, path, base_custody, &parent_custody);
auto result = resolve_path_without_veil(vfs_root_context, credentials, path, base_custody, &parent_custody);
if (!result.is_error())
return EEXIST;
else if (!parent_custody)
@ -657,10 +646,10 @@ ErrorOr<void> VirtualFileSystem::mkdir(Credentials const& credentials, StringVie
return {};
}
ErrorOr<void> VirtualFileSystem::access(Credentials const& credentials, StringView path, int mode, CustodyBase const& base, AccessFlags access_flags)
ErrorOr<void> VirtualFileSystem::access(VFSRootContext const& vfs_root_context, Credentials const& credentials, StringView path, int mode, CustodyBase const& base, AccessFlags access_flags)
{
auto should_follow_symlinks = !has_flag(access_flags, AccessFlags::DoNotFollowSymlinks);
auto custody = TRY(resolve_path(credentials, path, base, nullptr, should_follow_symlinks ? 0 : O_NOFOLLOW_NOERROR));
auto custody = TRY(resolve_path(vfs_root_context, credentials, path, base, nullptr, should_follow_symlinks ? 0 : O_NOFOLLOW_NOERROR));
auto& inode = custody->inode();
auto metadata = inode.metadata();
@ -682,9 +671,9 @@ ErrorOr<void> VirtualFileSystem::access(Credentials const& credentials, StringVi
return {};
}
ErrorOr<NonnullRefPtr<Custody>> VirtualFileSystem::open_directory(Credentials const& credentials, StringView path, CustodyBase const& base)
ErrorOr<NonnullRefPtr<Custody>> VirtualFileSystem::open_directory(VFSRootContext const& vfs_root_context, Credentials const& credentials, StringView path, CustodyBase const& base)
{
auto custody = TRY(resolve_path(credentials, path, base));
auto custody = TRY(resolve_path(vfs_root_context, credentials, path, base));
auto& inode = custody->inode();
if (!inode.is_directory())
return ENOTDIR;
@ -707,20 +696,20 @@ ErrorOr<void> VirtualFileSystem::chmod(Credentials const& credentials, Custody&
return inode.chmod(mode);
}
ErrorOr<void> VirtualFileSystem::chmod(Credentials const& credentials, StringView path, mode_t mode, CustodyBase const& base, int options)
ErrorOr<void> VirtualFileSystem::chmod(VFSRootContext const& vfs_root_context, Credentials const& credentials, StringView path, mode_t mode, CustodyBase const& base, int options)
{
auto custody = TRY(resolve_path(credentials, path, base, nullptr, options));
auto custody = TRY(resolve_path(vfs_root_context, credentials, path, base, nullptr, options));
return chmod(credentials, custody, mode);
}
ErrorOr<void> VirtualFileSystem::rename(Credentials const& credentials, CustodyBase const& old_base, StringView old_path, CustodyBase const& new_base, StringView new_path)
ErrorOr<void> VirtualFileSystem::rename(VFSRootContext const& vfs_root_context, Credentials const& credentials, CustodyBase const& old_base, StringView old_path, CustodyBase const& new_base, StringView new_path)
{
RefPtr<Custody> old_parent_custody;
auto old_custody = TRY(resolve_path(credentials, old_path, old_base, &old_parent_custody, O_NOFOLLOW_NOERROR));
auto old_custody = TRY(resolve_path(vfs_root_context, credentials, old_path, old_base, &old_parent_custody, O_NOFOLLOW_NOERROR));
auto& old_inode = old_custody->inode();
RefPtr<Custody> new_parent_custody;
auto new_custody_or_error = resolve_path(credentials, new_path, new_base, &new_parent_custody);
auto new_custody_or_error = resolve_path(vfs_root_context, credentials, new_path, new_base, &new_parent_custody);
if (new_custody_or_error.is_error()) {
if (new_custody_or_error.error().code() != ENOENT || !new_parent_custody)
return new_custody_or_error.release_error();
@ -847,9 +836,9 @@ ErrorOr<void> VirtualFileSystem::chown(Credentials const& credentials, Custody&
return inode.chown(new_uid, new_gid);
}
ErrorOr<void> VirtualFileSystem::chown(Credentials const& credentials, StringView path, UserID a_uid, GroupID a_gid, CustodyBase const& base, int options)
ErrorOr<void> VirtualFileSystem::chown(VFSRootContext const& vfs_root_context, Credentials const& credentials, StringView path, UserID a_uid, GroupID a_gid, CustodyBase const& base, int options)
{
auto custody = TRY(resolve_path(credentials, path, base, nullptr, options));
auto custody = TRY(resolve_path(vfs_root_context, credentials, path, base, nullptr, options));
return chown(credentials, custody, a_uid, a_gid);
}
@ -870,15 +859,15 @@ static bool hard_link_allowed(Credentials const& credentials, Inode const& inode
return false;
}
ErrorOr<void> VirtualFileSystem::link(Credentials const& credentials, StringView old_path, StringView new_path, CustodyBase const& base)
ErrorOr<void> VirtualFileSystem::link(VFSRootContext const& vfs_root_context, Credentials const& credentials, StringView old_path, StringView new_path, CustodyBase const& base)
{
// NOTE: To prevent unveil bypass by creating an hardlink after unveiling a path as read-only,
// check that if write permission is allowed by the veil info on the old_path.
auto old_custody = TRY(resolve_path(credentials, old_path, base, nullptr, O_RDWR));
auto old_custody = TRY(resolve_path(vfs_root_context, credentials, old_path, base, nullptr, O_RDWR));
auto& old_inode = old_custody->inode();
RefPtr<Custody> parent_custody;
auto new_custody_or_error = resolve_path(credentials, new_path, base, &parent_custody);
auto new_custody_or_error = resolve_path(vfs_root_context, credentials, new_path, base, &parent_custody);
if (!new_custody_or_error.is_error())
return EEXIST;
@ -905,10 +894,10 @@ ErrorOr<void> VirtualFileSystem::link(Credentials const& credentials, StringView
return parent_inode.add_child(old_inode, KLexicalPath::basename(new_path), old_inode.mode());
}
ErrorOr<void> VirtualFileSystem::unlink(Credentials const& credentials, StringView path, CustodyBase const& base)
ErrorOr<void> VirtualFileSystem::unlink(VFSRootContext const& vfs_root_context, Credentials const& credentials, StringView path, CustodyBase const& base)
{
RefPtr<Custody> parent_custody;
auto custody = TRY(resolve_path(credentials, path, base, &parent_custody, O_WRONLY | O_NOFOLLOW_NOERROR | O_UNLINK_INTERNAL));
auto custody = TRY(resolve_path(vfs_root_context, credentials, path, base, &parent_custody, O_WRONLY | O_NOFOLLOW_NOERROR | O_UNLINK_INTERNAL));
auto& inode = custody->inode();
if (inode.is_directory())
@ -934,17 +923,17 @@ ErrorOr<void> VirtualFileSystem::unlink(Credentials const& credentials, StringVi
return parent_inode.remove_child(KLexicalPath::basename(path));
}
ErrorOr<void> VirtualFileSystem::symlink(Credentials const& credentials, StringView target, StringView linkpath, CustodyBase const& base)
ErrorOr<void> VirtualFileSystem::symlink(VFSRootContext const& vfs_root_context, Credentials const& credentials, StringView target, StringView linkpath, CustodyBase const& base)
{
auto base_custody = TRY(base.resolve());
// NOTE: Check that the actual target (if it exists right now) is unveiled and prevent creating symlinks on veiled paths!
if (auto target_custody_or_error = resolve_path_without_veil(credentials, target, base_custody, nullptr, O_RDWR, 0); !target_custody_or_error.is_error()) {
if (auto target_custody_or_error = resolve_path_without_veil(vfs_root_context, credentials, target, base_custody, nullptr, O_RDWR, 0); !target_custody_or_error.is_error()) {
auto target_custody = target_custody_or_error.release_value();
TRY(validate_path_against_process_veil(*target_custody, O_RDWR));
}
RefPtr<Custody> parent_custody;
auto existing_custody_or_error = resolve_path(credentials, linkpath, base, &parent_custody, O_RDWR);
auto existing_custody_or_error = resolve_path(vfs_root_context, credentials, linkpath, base, &parent_custody, O_RDWR);
if (!existing_custody_or_error.is_error())
return EEXIST;
if (!parent_custody)
@ -975,10 +964,10 @@ ErrorOr<void> VirtualFileSystem::symlink(Credentials const& credentials, StringV
}
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html
ErrorOr<void> VirtualFileSystem::rmdir(Credentials const& credentials, StringView path, CustodyBase const& base)
ErrorOr<void> VirtualFileSystem::rmdir(VFSRootContext const& vfs_root_context, Credentials const& credentials, StringView path, CustodyBase const& base)
{
RefPtr<Custody> parent_custody;
auto custody = TRY(resolve_path(credentials, path, base, &parent_custody, O_CREAT));
auto custody = TRY(resolve_path(vfs_root_context, credentials, path, base, &parent_custody, O_CREAT));
auto& inode = custody->inode();
auto last_component = KLexicalPath::basename(path);
@ -1037,9 +1026,9 @@ ErrorOr<void> VirtualFileSystem::rmdir(Credentials const& credentials, StringVie
return parent_inode.remove_child(KLexicalPath::basename(path));
}
ErrorOr<void> VirtualFileSystem::for_each_mount(Function<ErrorOr<void>(Mount const&)> callback) const
ErrorOr<void> VirtualFileSystem::for_each_mount(VFSRootContext& context, Function<ErrorOr<void>(Mount const&)> callback) const
{
return m_mounts.with([&](auto& mounts) -> ErrorOr<void> {
return context.mounts().with([&](auto& mounts) -> ErrorOr<void> {
for (auto& mount : mounts)
TRY(callback(mount));
return {};
@ -1051,11 +1040,6 @@ void VirtualFileSystem::sync()
FileSystem::sync();
}
NonnullRefPtr<Custody> VirtualFileSystem::root_custody()
{
return m_root_custody.with([](auto& root_custody) -> NonnullRefPtr<Custody> { return *root_custody; });
}
UnveilNode const& VirtualFileSystem::find_matching_unveiled_path(Process const& process, StringView path)
{
VERIFY(process.veil_state() != VeilState::None);
@ -1155,17 +1139,17 @@ ErrorOr<void> VirtualFileSystem::validate_path_against_process_veil(StringView p
return validate_path_against_process_veil(Process::current(), path, options);
}
ErrorOr<NonnullRefPtr<Custody>> VirtualFileSystem::resolve_path(Credentials const& credentials, StringView path, CustodyBase const& base, RefPtr<Custody>* out_parent, int options, int symlink_recursion_level)
ErrorOr<NonnullRefPtr<Custody>> VirtualFileSystem::resolve_path(VFSRootContext const& vfs_root_context, Credentials const& credentials, StringView path, CustodyBase const& base, RefPtr<Custody>* out_parent, int options, int symlink_recursion_level)
{
return resolve_path(Process::current(), credentials, path, base, out_parent, options, symlink_recursion_level);
return resolve_path(Process::current(), vfs_root_context, credentials, path, base, out_parent, options, symlink_recursion_level);
}
ErrorOr<NonnullRefPtr<Custody>> VirtualFileSystem::resolve_path(Process const& process, Credentials const& credentials, StringView path, CustodyBase const& base, RefPtr<Custody>* out_parent, int options, int symlink_recursion_level)
ErrorOr<NonnullRefPtr<Custody>> VirtualFileSystem::resolve_path(Process const& process, VFSRootContext const& vfs_root_context, Credentials const& credentials, StringView path, CustodyBase const& base, RefPtr<Custody>* out_parent, int options, int symlink_recursion_level)
{
auto base_custody = TRY(base.resolve());
// FIXME: The errors returned by resolve_path_without_veil can leak information about paths that are not unveiled,
// e.g. when the error is EACCESS or similar.
auto custody = TRY(resolve_path_without_veil(credentials, path, base_custody, out_parent, options, symlink_recursion_level));
auto custody = TRY(resolve_path_without_veil(vfs_root_context, credentials, path, base_custody, out_parent, options, symlink_recursion_level));
if (auto result = validate_path_against_process_veil(process, *custody, options); result.is_error()) {
if (out_parent)
out_parent->clear();
@ -1189,7 +1173,7 @@ static bool safe_to_follow_symlink(Credentials const& credentials, Inode const&
return false;
}
ErrorOr<NonnullRefPtr<Custody>> VirtualFileSystem::resolve_path_without_veil(Credentials const& credentials, StringView path, NonnullRefPtr<Custody> base, RefPtr<Custody>* out_parent, int options, int symlink_recursion_level)
ErrorOr<NonnullRefPtr<Custody>> VirtualFileSystem::resolve_path_without_veil(VFSRootContext const& vfs_root_context, Credentials const& credentials, StringView path, NonnullRefPtr<Custody> base, RefPtr<Custody>* out_parent, int options, int symlink_recursion_level)
{
if (symlink_recursion_level >= symlink_recursion_limit)
return ELOOP;
@ -1199,7 +1183,11 @@ ErrorOr<NonnullRefPtr<Custody>> VirtualFileSystem::resolve_path_without_veil(Cre
GenericLexer path_lexer(path);
NonnullRefPtr<Custody> custody = path[0] == '/' ? root_custody() : base;
auto vfs_root_context_custody = vfs_root_context.root_custody().with([](auto& custody) -> NonnullRefPtr<Custody> {
return custody;
});
NonnullRefPtr<Custody> custody = path[0] == '/' ? vfs_root_context_custody : base;
bool extra_iteration = path[path.length() - 1] == '/';
while (!path_lexer.is_eof() || extra_iteration) {
@ -1246,7 +1234,7 @@ ErrorOr<NonnullRefPtr<Custody>> VirtualFileSystem::resolve_path_without_veil(Cre
// See if there's something mounted on the child; in that case
// we would need to return the guest inode, not the host inode.
auto found_mount_or_error = apply_to_mount_for_host_custody(current_custody, [&child_inode, &mount_flags_for_child](auto& mount) {
auto found_mount_or_error = apply_to_mount_for_host_custody(const_cast<VFSRootContext&>(vfs_root_context), current_custody, [&child_inode, &mount_flags_for_child](auto& mount) {
child_inode = mount.guest();
mount_flags_for_child = mount.flags();
});
@ -1269,7 +1257,7 @@ ErrorOr<NonnullRefPtr<Custody>> VirtualFileSystem::resolve_path_without_veil(Cre
TRY(validate_path_against_process_veil(*custody, options));
auto symlink_target = TRY(child_inode->resolve_as_link(credentials, parent, out_parent, options, symlink_recursion_level + 1));
auto symlink_target = TRY(child_inode->resolve_as_link(vfs_root_context, credentials, parent, out_parent, options, symlink_recursion_level + 1));
if (!have_more_parts)
return symlink_target;
@ -1280,7 +1268,7 @@ ErrorOr<NonnullRefPtr<Custody>> VirtualFileSystem::resolve_path_without_veil(Cre
TRY(remaining_path.try_append('.'));
TRY(remaining_path.try_append(path.substring_view_starting_after_substring(part)));
return resolve_path_without_veil(credentials, remaining_path.string_view(), symlink_target, out_parent, options, symlink_recursion_level + 1);
return resolve_path_without_veil(vfs_root_context, credentials, remaining_path.string_view(), symlink_target, out_parent, options, symlink_recursion_level + 1);
}
}

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2024, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -21,6 +22,7 @@
#include <Kernel/FileSystem/Mount.h>
#include <Kernel/FileSystem/MountFile.h>
#include <Kernel/FileSystem/UnveilNode.h>
#include <Kernel/FileSystem/VFSRootContext.h>
#include <Kernel/Forward.h>
#include <Kernel/Locking/MutexProtected.h>
#include <Kernel/Locking/SpinlockProtected.h>
@ -45,6 +47,8 @@ enum class AccessFlags {
AK_ENUM_BITWISE_OPERATORS(AccessFlags);
class VirtualFileSystem {
friend class StorageManagement;
public:
// Required to be at least 8 by POSIX
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html
@ -53,51 +57,57 @@ public:
static void initialize();
static VirtualFileSystem& the();
static bool check_matching_absolute_path_hierarchy(Custody const& first_custody, Custody const& second_custody);
static ErrorOr<FileSystemInitializer const*> find_filesystem_type_initializer(StringView fs_type);
VirtualFileSystem();
~VirtualFileSystem();
ErrorOr<void> mount_root(FileSystem&);
ErrorOr<void> mount(MountFile&, OpenFileDescription*, Custody& mount_point, int flags);
ErrorOr<void> bind_mount(Custody& source, Custody& mount_point, int flags);
ErrorOr<void> remount(Custody& mount_point, int new_flags);
ErrorOr<void> unmount(Custody& mount_point);
ErrorOr<void> unmount(Inode& guest_inode, StringView custody_path);
SpinlockProtected<IntrusiveList<&FileSystem::m_file_system_node>, LockRank::FileSystem>& all_file_systems_list(Badge<VFSRootContext>) { return m_file_systems_list; }
SpinlockProtected<IntrusiveList<&VFSRootContext::m_list_node>, LockRank::FileSystem>& all_root_contexts_list(Badge<PowerStateSwitchTask>) { return m_root_contexts; }
SpinlockProtected<IntrusiveList<&VFSRootContext::m_list_node>, LockRank::FileSystem>& all_root_contexts_list(Badge<VFSRootContext>) { return m_root_contexts; }
ErrorOr<NonnullRefPtr<OpenFileDescription>> open(Credentials const&, StringView path, int options, mode_t mode, CustodyBase const& base, Optional<UidAndGid> = {});
ErrorOr<NonnullRefPtr<OpenFileDescription>> open(Process const&, Credentials const&, StringView path, int options, mode_t mode, CustodyBase const& base, Optional<UidAndGid> = {});
ErrorOr<void> mount(VFSRootContext&, MountFile&, OpenFileDescription*, Custody& mount_point, int flags);
ErrorOr<void> pivot_root_by_copying_mounted_fs_instance(VFSRootContext&, FileSystem& fs, int root_mount_flags);
ErrorOr<void> bind_mount(VFSRootContext&, Custody& source, Custody& mount_point, int flags);
ErrorOr<void> remount(VFSRootContext&, Custody& mount_point, int new_flags);
ErrorOr<void> unmount(VFSRootContext&, Custody& mount_point);
ErrorOr<void> unmount(VFSRootContext&, Inode& guest_inode, StringView custody_path);
ErrorOr<NonnullRefPtr<OpenFileDescription>> open(VFSRootContext const&, Credentials const&, StringView path, int options, mode_t mode, CustodyBase const& base, Optional<UidAndGid> = {});
ErrorOr<NonnullRefPtr<OpenFileDescription>> open(Process const&, VFSRootContext const&, Credentials const&, StringView path, int options, mode_t mode, CustodyBase const& base, Optional<UidAndGid> = {});
ErrorOr<NonnullRefPtr<OpenFileDescription>> create(Credentials const&, StringView path, int options, mode_t mode, Custody& parent_custody, Optional<UidAndGid> = {});
ErrorOr<NonnullRefPtr<OpenFileDescription>> create(Process const&, Credentials const&, StringView path, int options, mode_t mode, Custody& parent_custody, Optional<UidAndGid> = {});
ErrorOr<void> mkdir(Credentials const&, StringView path, mode_t mode, CustodyBase const& base);
ErrorOr<void> link(Credentials const&, StringView old_path, StringView new_path, CustodyBase const& base);
ErrorOr<void> unlink(Credentials const&, StringView path, CustodyBase const& base);
ErrorOr<void> symlink(Credentials const&, StringView target, StringView linkpath, CustodyBase const& base);
ErrorOr<void> rmdir(Credentials const&, StringView path, CustodyBase const& base);
ErrorOr<void> chmod(Credentials const&, StringView path, mode_t, CustodyBase const& base, int options = 0);
ErrorOr<void> mkdir(VFSRootContext const&, Credentials const&, StringView path, mode_t mode, CustodyBase const& base);
ErrorOr<void> link(VFSRootContext const&, Credentials const&, StringView old_path, StringView new_path, CustodyBase const& base);
ErrorOr<void> unlink(VFSRootContext const&, Credentials const&, StringView path, CustodyBase const& base);
ErrorOr<void> symlink(VFSRootContext const&, Credentials const&, StringView target, StringView linkpath, CustodyBase const& base);
ErrorOr<void> rmdir(VFSRootContext const&, Credentials const&, StringView path, CustodyBase const& base);
ErrorOr<void> chmod(VFSRootContext const&, Credentials const&, StringView path, mode_t, CustodyBase const& base, int options = 0);
ErrorOr<void> chmod(Credentials const&, Custody&, mode_t);
ErrorOr<void> chown(Credentials const&, StringView path, UserID, GroupID, CustodyBase const& base, int options);
ErrorOr<void> chown(VFSRootContext const&, Credentials const&, StringView path, UserID, GroupID, CustodyBase const& base, int options);
ErrorOr<void> chown(Credentials const&, Custody&, UserID, GroupID);
ErrorOr<void> access(Credentials const&, StringView path, int mode, CustodyBase const& base, AccessFlags);
ErrorOr<InodeMetadata> lookup_metadata(Credentials const&, StringView path, CustodyBase const& base, int options = 0);
ErrorOr<void> utime(Credentials const&, StringView path, CustodyBase const& base, time_t atime, time_t mtime);
ErrorOr<void> utimensat(Credentials const&, StringView path, CustodyBase const& base, timespec const& atime, timespec const& mtime, int options = 0);
ErrorOr<void> do_utimens(Credentials const& credentials, Custody& custody, timespec const& atime, timespec const& mtime);
ErrorOr<void> rename(Credentials const&, CustodyBase const& old_base, StringView oldpath, CustodyBase const& new_base, StringView newpath);
ErrorOr<void> mknod(Credentials const&, StringView path, mode_t, dev_t, CustodyBase const& base);
ErrorOr<NonnullRefPtr<Custody>> open_directory(Credentials const&, StringView path, CustodyBase const& base);
ErrorOr<void> access(VFSRootContext const&, Credentials const&, StringView path, int mode, CustodyBase const& base, AccessFlags);
ErrorOr<InodeMetadata> lookup_metadata(VFSRootContext const&, Credentials const&, StringView path, CustodyBase const& base, int options = 0);
ErrorOr<void> utime(VFSRootContext const&, Credentials const&, StringView path, CustodyBase const& base, time_t atime, time_t mtime);
ErrorOr<void> utimensat(VFSRootContext const&, Credentials const&, StringView path, CustodyBase const& base, timespec const& atime, timespec const& mtime, int options = 0);
ErrorOr<void> do_utimens(Credentials const&, Custody& custody, timespec const& atime, timespec const& mtime);
ErrorOr<void> rename(VFSRootContext const&, Credentials const&, CustodyBase const& old_base, StringView oldpath, CustodyBase const& new_base, StringView newpath);
ErrorOr<void> mknod(VFSRootContext const&, Credentials const&, StringView path, mode_t, dev_t, CustodyBase const& base);
ErrorOr<NonnullRefPtr<Custody>> open_directory(VFSRootContext const&, Credentials const&, StringView path, CustodyBase const& base);
ErrorOr<void> for_each_mount(Function<ErrorOr<void>(Mount const&)>) const;
ErrorOr<void> for_each_mount(VFSRootContext& context, Function<ErrorOr<void>(Mount const&)>) const;
void sync_filesystems();
void lock_all_filesystems();
static void sync();
NonnullRefPtr<Custody> root_custody();
ErrorOr<NonnullRefPtr<Custody>> resolve_path(Credentials const&, StringView path, CustodyBase const& base, RefPtr<Custody>* out_parent = nullptr, int options = 0, int symlink_recursion_level = 0);
ErrorOr<NonnullRefPtr<Custody>> resolve_path(Process const&, Credentials const&, StringView path, CustodyBase const& base, RefPtr<Custody>* out_parent = nullptr, int options = 0, int symlink_recursion_level = 0);
ErrorOr<NonnullRefPtr<Custody>> resolve_path_without_veil(Credentials const&, StringView path, NonnullRefPtr<Custody> base, RefPtr<Custody>* out_parent = nullptr, int options = 0, int symlink_recursion_level = 0);
ErrorOr<NonnullRefPtr<Custody>> resolve_path(VFSRootContext const&, Credentials const&, StringView path, CustodyBase const& base, RefPtr<Custody>* out_parent = nullptr, int options = 0, int symlink_recursion_level = 0);
ErrorOr<NonnullRefPtr<Custody>> resolve_path(Process const&, VFSRootContext const&, Credentials const&, StringView path, CustodyBase const& base, RefPtr<Custody>* out_parent = nullptr, int options = 0, int symlink_recursion_level = 0);
ErrorOr<NonnullRefPtr<Custody>> resolve_path_without_veil(VFSRootContext const&, Credentials const&, StringView path, NonnullRefPtr<Custody> base, RefPtr<Custody>* out_parent = nullptr, int options = 0, int symlink_recursion_level = 0);
private:
friend class OpenFileDescription;
@ -108,20 +118,16 @@ private:
ErrorOr<void> validate_path_against_process_veil(Custody const& path, int options);
ErrorOr<void> validate_path_against_process_veil(StringView path, int options);
ErrorOr<void> add_file_system_to_mount_table(FileSystem& file_system, Custody& mount_point, int flags);
static void delete_mount_from_list(Mount& mount);
ErrorOr<void> remove_mount(Mount& mount, IntrusiveList<&FileBackedFileSystem::m_file_backed_file_system_node>& file_backed_fs_list);
static ErrorOr<NonnullRefPtr<FileSystem>> create_and_initialize_filesystem_from_mount_file(MountFile& mount_file);
static ErrorOr<NonnullRefPtr<FileSystem>> create_and_initialize_filesystem_from_mount_file_and_description(IntrusiveList<&FileBackedFileSystem::m_file_backed_file_system_node>& file_backed_fs_list, MountFile& mount_file, OpenFileDescription& source_description);
static ErrorOr<void> verify_mount_file_and_description_requirements(MountFile& mount_file, OpenFileDescription& source_description);
ErrorOr<void> traverse_directory_inode(Inode&, Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)>);
static bool check_matching_absolute_path_hierarchy(Custody const& first_custody, Custody const& second_custody);
bool mount_point_exists_at_custody(Custody& mount_point);
ErrorOr<void> apply_to_mount_for_host_custody(Custody const& current_custody, Function<void(Mount&)>);
RefPtr<Inode> m_root_inode;
SpinlockProtected<RefPtr<Custody>, LockRank::None> m_root_custody {};
SpinlockProtected<IntrusiveList<&Mount::m_vfs_list_node>, LockRank::None> m_mounts {};
ErrorOr<void> apply_to_mount_for_host_custody(VFSRootContext&, Custody const& current_custody, Function<void(Mount&)>);
// NOTE: The FileBackedFileSystem list is protected by a mutex because we need to scan it
// to search for existing filesystems for already used block devices and therefore when doing
@ -131,6 +137,8 @@ private:
MutexProtected<IntrusiveList<&FileBackedFileSystem::m_file_backed_file_system_node>> m_file_backed_file_systems_list {};
SpinlockProtected<IntrusiveList<&FileSystem::m_file_system_node>, LockRank::FileSystem> m_file_systems_list {};
SpinlockProtected<IntrusiveList<&VFSRootContext::m_list_node>, LockRank::FileSystem> m_root_contexts {};
};
}

View file

@ -43,6 +43,7 @@ class Mutex;
class MasterPTY;
class Mount;
class PerformanceEventBuffer;
class PowerStateSwitchTask;
class ProcFS;
class ProcFSInode;
class Process;
@ -52,6 +53,7 @@ template<LockRank Rank>
class RecursiveSpinlock;
class Scheduler;
class Socket;
class StorageManagement;
class SysFS;
class SysFSDirectory;
class SysFSRootDirectory;
@ -66,6 +68,7 @@ class ThreadTracer;
class RAMFSInode;
class UDPSocket;
class UserOrKernelBuffer;
class VFSRootContext;
class VirtualFileSystem;
class WaitQueue;
class WorkQueue;

View file

@ -139,7 +139,7 @@ ErrorOr<void> LocalSocket::bind(Credentials const& credentials, Userspace<sockad
mode_t mode = S_IFSOCK | (m_prebind_mode & 0777);
UidAndGid owner { m_prebind_uid, m_prebind_gid };
auto result = VirtualFileSystem::the().open(credentials, path->view(), O_CREAT | O_EXCL | O_NOFOLLOW_NOERROR, mode, Process::current().current_directory(), owner);
auto result = VirtualFileSystem::the().open(Process::current().vfs_root_context(), credentials, path->view(), O_CREAT | O_EXCL | O_NOFOLLOW_NOERROR, mode, Process::current().current_directory(), owner);
if (result.is_error()) {
if (result.error().code() == EEXIST)
return set_so_error(EADDRINUSE);
@ -180,7 +180,7 @@ ErrorOr<void> LocalSocket::connect(Credentials const& credentials, OpenFileDescr
auto path = SOCKET_TRY(KString::try_create(StringView { address.sun_path, strnlen(address.sun_path, sizeof(address.sun_path)) }));
dbgln_if(LOCAL_SOCKET_DEBUG, "LocalSocket({}) connect({})", this, *path);
auto file = SOCKET_TRY(VirtualFileSystem::the().open(credentials, path->view(), O_RDWR, 0, Process::current().current_directory()));
auto file = SOCKET_TRY(VirtualFileSystem::the().open(Process::current().vfs_root_context(), credentials, path->view(), O_RDWR, 0, Process::current().current_directory()));
auto inode = file->inode();
m_inode = inode;

View file

@ -17,7 +17,7 @@ ErrorOr<FlatPtr> Process::sys$chdir(Userspace<char const*> user_path, size_t pat
TRY(require_promise(Pledge::rpath));
auto path = TRY(get_syscall_path_argument(user_path, path_length));
RefPtr<Custody> new_directory = TRY(VirtualFileSystem::the().open_directory(credentials(), path->view(), current_directory()));
RefPtr<Custody> new_directory = TRY(VirtualFileSystem::the().open_directory(vfs_root_context(), credentials(), path->view(), current_directory()));
m_current_directory.with([&](auto& current_directory) {
// NOTE: We use swap() here to avoid manipulating the ref counts while holding the lock.
swap(current_directory, new_directory);

View file

@ -18,7 +18,7 @@ ErrorOr<FlatPtr> Process::sys$chmod(Userspace<Syscall::SC_chmod_params const*> u
auto params = TRY(copy_typed_from_user(user_params));
auto path = TRY(get_syscall_path_argument(params.path));
CustodyBase base(params.dirfd, path->view());
TRY(VirtualFileSystem::the().chmod(credentials(), path->view(), params.mode, base, params.follow_symlinks ? 0 : O_NOFOLLOW_NOERROR));
TRY(VirtualFileSystem::the().chmod(vfs_root_context(), credentials(), path->view(), params.mode, base, params.follow_symlinks ? 0 : O_NOFOLLOW_NOERROR));
return 0;
}

View file

@ -28,7 +28,7 @@ ErrorOr<FlatPtr> Process::sys$chown(Userspace<Syscall::SC_chown_params const*> u
auto params = TRY(copy_typed_from_user(user_params));
auto path = TRY(get_syscall_path_argument(params.path));
CustodyBase base(params.dirfd, path->view());
TRY(VirtualFileSystem::the().chown(credentials(), path->view(), params.uid, params.gid, base, params.follow_symlinks ? 0 : O_NOFOLLOW_NOERROR));
TRY(VirtualFileSystem::the().chown(vfs_root_context(), credentials(), path->view(), params.uid, params.gid, base, params.follow_symlinks ? 0 : O_NOFOLLOW_NOERROR));
return 0;
}

View file

@ -760,7 +760,7 @@ ErrorOr<RefPtr<OpenFileDescription>> Process::find_elf_interpreter_for_executabl
if (!interpreter_path.is_empty()) {
dbgln_if(EXEC_DEBUG, "exec({}): Using program interpreter {}", path, interpreter_path);
auto interpreter_description = TRY(VirtualFileSystem::the().open(credentials(), interpreter_path, O_EXEC, 0, current_directory()));
auto interpreter_description = TRY(VirtualFileSystem::the().open(vfs_root_context(), credentials(), interpreter_path, O_EXEC, 0, current_directory()));
auto interp_metadata = interpreter_description->metadata();
VERIFY(interpreter_description->inode());
@ -830,7 +830,7 @@ ErrorOr<void> Process::exec(NonnullOwnPtr<KString> path, Vector<NonnullOwnPtr<KS
// * ET_EXEC binary that just gets loaded
// * ET_DYN binary that requires a program interpreter
//
auto description = TRY(VirtualFileSystem::the().open(credentials(), path->view(), O_EXEC, 0, current_directory()));
auto description = TRY(VirtualFileSystem::the().open(vfs_root_context(), credentials(), path->view(), O_EXEC, 0, current_directory()));
auto metadata = description->metadata();
if (!metadata.is_regular_file())

View file

@ -27,7 +27,7 @@ ErrorOr<FlatPtr> Process::sys$faccessat(Userspace<Syscall::SC_faccessat_params c
flags |= AccessFlags::EffectiveAccess;
CustodyBase base(params.dirfd, pathname->view());
TRY(VirtualFileSystem::the().access(credentials(), pathname->view(), params.mode, base, flags));
TRY(VirtualFileSystem::the().access(vfs_root_context(), credentials(), pathname->view(), params.mode, base, flags));
return 0;
}

View file

@ -21,7 +21,7 @@ ErrorOr<FlatPtr> Process::sys$fork(RegisterState& regs)
TRY(require_promise(Pledge::proc));
auto credentials = this->credentials();
auto child_and_first_thread = TRY(Process::create_with_forked_name(credentials->uid(), credentials->gid(), pid(), m_is_kernel_process, current_directory(), executable(), tty(), this));
auto child_and_first_thread = TRY(Process::create_with_forked_name(credentials->uid(), credentials->gid(), pid(), m_is_kernel_process, vfs_root_context(), current_directory(), executable(), tty(), this));
auto& child = child_and_first_thread.process;
auto& child_first_thread = child_and_first_thread.first_thread;

View file

@ -48,7 +48,7 @@ ErrorOr<FlatPtr> Process::sys$inode_watcher_add_watch(Userspace<Syscall::SC_inod
return EBADF;
auto* inode_watcher = description->inode_watcher();
auto path = TRY(get_syscall_path_argument(params.user_path));
auto custody = TRY(VirtualFileSystem::the().resolve_path(credentials(), path->view(), current_directory()));
auto custody = TRY(VirtualFileSystem::the().resolve_path(vfs_root_context(), credentials(), path->view(), current_directory()));
if (!custody->inode().fs().supports_watchers())
return ENOTSUP;

View file

@ -18,7 +18,7 @@ ErrorOr<FlatPtr> Process::sys$link(Userspace<Syscall::SC_link_params const*> use
auto old_path = TRY(try_copy_kstring_from_user(params.old_path));
auto new_path = TRY(try_copy_kstring_from_user(params.new_path));
TRY(VirtualFileSystem::the().link(credentials(), old_path->view(), new_path->view(), current_directory()));
TRY(VirtualFileSystem::the().link(vfs_root_context(), credentials(), old_path->view(), new_path->view(), current_directory()));
return 0;
}
@ -31,7 +31,7 @@ ErrorOr<FlatPtr> Process::sys$symlink(Userspace<Syscall::SC_symlink_params const
auto target = TRY(get_syscall_path_argument(params.target));
auto linkpath = TRY(get_syscall_path_argument(params.linkpath));
CustodyBase base(params.dirfd, target->view());
TRY(VirtualFileSystem::the().symlink(credentials(), target->view(), linkpath->view(), base));
TRY(VirtualFileSystem::the().symlink(vfs_root_context(), credentials(), target->view(), linkpath->view(), base));
return 0;
}

View file

@ -17,7 +17,7 @@ ErrorOr<FlatPtr> Process::sys$mkdir(int dirfd, Userspace<char const*> user_path,
auto path = TRY(get_syscall_path_argument(user_path, path_length));
CustodyBase base(dirfd, path->view());
TRY(VirtualFileSystem::the().mkdir(credentials(), path->view(), mode & ~umask(), base));
TRY(VirtualFileSystem::the().mkdir(vfs_root_context(), credentials(), path->view(), mode & ~umask(), base));
return 0;
}
}

View file

@ -22,7 +22,7 @@ ErrorOr<FlatPtr> Process::sys$mknod(Userspace<Syscall::SC_mknod_params const*> u
auto path = TRY(get_syscall_path_argument(params.path));
CustodyBase base(params.dirfd, path->view());
TRY(VirtualFileSystem::the().mknod(credentials, path->view(), params.mode & ~umask(), params.dev, base));
TRY(VirtualFileSystem::the().mknod(vfs_root_context(), credentials, path->view(), params.mode & ~umask(), params.dev, base));
return 0;
}

View file

@ -55,9 +55,10 @@ ErrorOr<FlatPtr> Process::sys$fsmount(Userspace<Syscall::SC_fsmount_params const
RefPtr<OpenFileDescription> source_description = TRY(open_file_description_ignoring_negative(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 current_vfs_root_context = Process::current().vfs_root_context();
auto target_custody = TRY(VirtualFileSystem::the().resolve_path(current_vfs_root_context, credentials, target->view(), current_directory()));
auto flags = mount_description->mount_file()->mount_flags();
TRY(VirtualFileSystem::the().mount(*mount_description->mount_file(), source_description.ptr(), target_custody, flags));
TRY(VirtualFileSystem::the().mount(current_vfs_root_context, *mount_description->mount_file(), source_description.ptr(), target_custody, flags));
return 0;
}
@ -76,8 +77,9 @@ ErrorOr<FlatPtr> Process::sys$remount(Userspace<Syscall::SC_remount_params const
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));
auto current_vfs_root_context = Process::current().vfs_root_context();
auto target_custody = TRY(VirtualFileSystem::the().resolve_path(current_vfs_root_context, credentials, target->view(), current_directory()));
TRY(VirtualFileSystem::the().remount(current_vfs_root_context, target_custody, params.flags));
return 0;
}
@ -97,7 +99,8 @@ ErrorOr<FlatPtr> Process::sys$bindmount(Userspace<Syscall::SC_bindmount_params c
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 current_vfs_root_context = Process::current().vfs_root_context();
auto target_custody = TRY(VirtualFileSystem::the().resolve_path(current_vfs_root_context, credentials, target->view(), current_directory()));
auto description = TRY(open_file_description(source_fd));
if (!description->custody()) {
@ -105,7 +108,7 @@ ErrorOr<FlatPtr> Process::sys$bindmount(Userspace<Syscall::SC_bindmount_params c
return ENODEV;
}
TRY(VirtualFileSystem::the().bind_mount(*description->custody(), target_custody, params.flags));
TRY(VirtualFileSystem::the().bind_mount(current_vfs_root_context, *description->custody(), target_custody, params.flags));
return 0;
}
@ -119,8 +122,9 @@ ErrorOr<FlatPtr> Process::sys$umount(Userspace<char const*> user_mountpoint, siz
TRY(require_promise(Pledge::mount));
auto mountpoint = TRY(get_syscall_path_argument(user_mountpoint, mountpoint_length));
auto custody = TRY(VirtualFileSystem::the().resolve_path(credentials, mountpoint->view(), current_directory()));
TRY(VirtualFileSystem::the().unmount(*custody));
auto current_vfs_root_context = Process::current().vfs_root_context();
auto custody = TRY(VirtualFileSystem::the().resolve_path(current_vfs_root_context, credentials, mountpoint->view(), current_directory()));
TRY(VirtualFileSystem::the().unmount(current_vfs_root_context, custody));
return 0;
}

View file

@ -56,7 +56,7 @@ ErrorOr<FlatPtr> Process::open_impl(Userspace<Syscall::SC_open_params const*> us
auto fd_allocation = TRY(allocate_fd());
CustodyBase base(dirfd, path->view());
auto description = TRY(VirtualFileSystem::the().open(credentials(), path->view(), options, mode & ~umask(), base));
auto description = TRY(VirtualFileSystem::the().open(vfs_root_context(), credentials(), path->view(), options, mode & ~umask(), base));
if (description->inode() && description->inode()->bound_socket())
return ENXIO;

View file

@ -17,7 +17,7 @@ ErrorOr<FlatPtr> Process::sys$readlink(Userspace<Syscall::SC_readlink_params con
auto params = TRY(copy_typed_from_user(user_params));
auto path = TRY(get_syscall_path_argument(params.path));
auto description = TRY(VirtualFileSystem::the().open(credentials(), path->view(), O_RDONLY | O_NOFOLLOW_NOERROR, 0, TRY(custody_for_dirfd(params.dirfd))));
auto description = TRY(VirtualFileSystem::the().open(vfs_root_context(), credentials(), path->view(), O_RDONLY | O_NOFOLLOW_NOERROR, 0, TRY(custody_for_dirfd(params.dirfd))));
if (!description->metadata().is_symlink())
return EINVAL;

View file

@ -18,7 +18,7 @@ ErrorOr<FlatPtr> Process::sys$realpath(Userspace<Syscall::SC_realpath_params con
auto params = TRY(copy_typed_from_user(user_params));
auto path = TRY(get_syscall_path_argument(params.path));
auto custody = TRY(VirtualFileSystem::the().resolve_path(credentials(), path->view(), current_directory()));
auto custody = TRY(VirtualFileSystem::the().resolve_path(vfs_root_context(), credentials(), path->view(), current_directory()));
auto absolute_path = TRY(custody->try_serialize_absolute_path());
size_t ideal_size = absolute_path->length() + 1;

View file

@ -19,7 +19,7 @@ ErrorOr<FlatPtr> Process::sys$rename(Userspace<Syscall::SC_rename_params const*>
auto new_path = TRY(get_syscall_path_argument(params.new_path));
CustodyBase old_base(params.olddirfd, old_path->view());
CustodyBase new_base(params.newdirfd, new_path->view());
TRY(VirtualFileSystem::the().rename(credentials(), old_base, old_path->view(), new_base, new_path->view()));
TRY(VirtualFileSystem::the().rename(vfs_root_context(), credentials(), old_base, old_path->view(), new_base, new_path->view()));
return 0;
}

View file

@ -15,7 +15,7 @@ ErrorOr<FlatPtr> Process::sys$rmdir(Userspace<char const*> user_path, size_t pat
VERIFY_NO_PROCESS_BIG_LOCK(this);
TRY(require_promise(Pledge::cpath));
auto path = TRY(get_syscall_path_argument(user_path, path_length));
TRY(VirtualFileSystem::the().rmdir(credentials(), path->view(), current_directory()));
TRY(VirtualFileSystem::the().rmdir(vfs_root_context(), credentials(), path->view(), current_directory()));
return 0;
}

View file

@ -30,7 +30,7 @@ ErrorOr<FlatPtr> Process::sys$stat(Userspace<Syscall::SC_stat_params const*> use
auto path = TRY(get_syscall_path_argument(params.path));
CustodyBase base(params.dirfd, path->view());
auto metadata = TRY(VirtualFileSystem::the().lookup_metadata(credentials(), path->view(), base, params.follow_symlinks ? 0 : O_NOFOLLOW_NOERROR));
auto metadata = TRY(VirtualFileSystem::the().lookup_metadata(vfs_root_context(), credentials(), path->view(), base, params.follow_symlinks ? 0 : O_NOFOLLOW_NOERROR));
auto statbuf = TRY(metadata.stat());
TRY(copy_to_user(params.statbuf, &statbuf));
return 0;

View file

@ -47,7 +47,7 @@ ErrorOr<FlatPtr> Process::sys$statvfs(Userspace<Syscall::SC_statvfs_params const
auto path = TRY(get_syscall_path_argument(params.path));
auto custody = TRY(VirtualFileSystem::the().resolve_path(credentials(), path->view(), current_directory(), nullptr, 0));
auto custody = TRY(VirtualFileSystem::the().resolve_path(vfs_root_context(), credentials(), path->view(), current_directory(), nullptr, 0));
auto& inode = custody->inode();
auto const& fs = inode.fs();

View file

@ -22,9 +22,9 @@ ErrorOr<FlatPtr> Process::sys$unlink(int dirfd, Userspace<char const*> user_path
CustodyBase base(dirfd, path->view());
if (flags & AT_REMOVEDIR)
TRY(VirtualFileSystem::the().rmdir(credentials(), path->view(), base));
TRY(VirtualFileSystem::the().rmdir(vfs_root_context(), credentials(), path->view(), base));
else
TRY(VirtualFileSystem::the().unlink(credentials(), path->view(), base));
TRY(VirtualFileSystem::the().unlink(vfs_root_context(), credentials(), path->view(), base));
return 0;
}

View file

@ -139,7 +139,10 @@ ErrorOr<FlatPtr> Process::sys$unveil(Userspace<Syscall::SC_unveil_params const*>
// If this case is encountered, the parent node of the path is returned and the custody of that inode is used instead.
RefPtr<Custody> parent_custody; // Parent inode in case of ENOENT
OwnPtr<KString> new_unveiled_path;
auto custody_or_error = VirtualFileSystem::the().resolve_path_without_veil(credentials(), path->view(), VirtualFileSystem::the().root_custody(), &parent_custody);
auto vfs_root_context_root_custody = Process::current().vfs_root_context()->root_custody().with([](auto& custody) -> NonnullRefPtr<Custody> {
return custody;
});
auto custody_or_error = VirtualFileSystem::the().resolve_path_without_veil(vfs_root_context(), credentials(), path->view(), vfs_root_context_root_custody, &parent_custody);
if (!custody_or_error.is_error()) {
new_unveiled_path = TRY(custody_or_error.value()->try_serialize_absolute_path());
} else if (custody_or_error.error().code() == ENOENT && parent_custody && (new_permissions & UnveilAccess::CreateOrRemove)) {

View file

@ -23,7 +23,7 @@ ErrorOr<FlatPtr> Process::sys$utime(Userspace<char const*> user_path, size_t pat
// Not a bug!
buf = { now, now };
}
TRY(VirtualFileSystem::the().utime(credentials(), path->view(), current_directory(), buf.actime, buf.modtime));
TRY(VirtualFileSystem::the().utime(vfs_root_context(), credentials(), path->view(), current_directory(), buf.actime, buf.modtime));
return 0;
}

View file

@ -74,7 +74,7 @@ ErrorOr<FlatPtr> Process::sys$utimensat(Userspace<Syscall::SC_utimensat_params c
auto& mtime = times[1];
CustodyBase base(params.dirfd, path->view());
TRY(VirtualFileSystem::the().utimensat(credentials(), path->view(), base, atime, mtime, follow_symlink));
TRY(VirtualFileSystem::the().utimensat(vfs_root_context(), credentials(), path->view(), base, atime, mtime, follow_symlink));
return 0;
}

View file

@ -96,7 +96,11 @@ Coredump::Coredump(NonnullRefPtr<Process> process, NonnullRefPtr<OpenFileDescrip
ErrorOr<NonnullRefPtr<OpenFileDescription>> Coredump::try_create_target_file(Process const& process, StringView output_path)
{
auto output_directory = KLexicalPath::dirname(output_path);
auto dump_directory = TRY(VirtualFileSystem::the().open_directory(Process::current().credentials(), output_directory, VirtualFileSystem::the().root_custody()));
auto current_vfs_root_context = Process::current().vfs_root_context();
auto current_vfs_root_context_root_custody = current_vfs_root_context->root_custody().with([](auto& custody) -> NonnullRefPtr<Custody> {
return custody;
});
auto dump_directory = TRY(VirtualFileSystem::the().open_directory(current_vfs_root_context, Process::current().credentials(), output_directory, *current_vfs_root_context_root_custody));
auto dump_directory_metadata = dump_directory->inode().metadata();
if (dump_directory_metadata.uid != 0 || dump_directory_metadata.gid != 0 || dump_directory_metadata.mode != 040777) {
dbgln("Refusing to put coredump in sketchy directory '{}'", output_directory);
@ -105,6 +109,7 @@ ErrorOr<NonnullRefPtr<OpenFileDescription>> Coredump::try_create_target_file(Pro
auto process_credentials = process.credentials();
return TRY(VirtualFileSystem::the().open(
current_vfs_root_context,
Process::current().credentials(),
KLexicalPath::basename(output_path),
O_CREAT | O_WRONLY | O_EXCL,

View file

@ -57,6 +57,50 @@ void PowerStateSwitchTask::spawn(PowerStateCommand command)
g_power_state_switch_task = move(power_state_switch_task_thread);
}
static ErrorOr<void> unmount_mounts_on_vfs_root_context(VFSRootContext& vfs_root_context)
{
// NOTE: We are going to tear down the entire VFS root context from its mounts.
// To do this properly, we swap out the original root custody with the empty
// root custody for vfs root context of kernel processes.
vfs_root_context.root_custody().with([](auto& custody) {
custody = VFSRootContext::empty_context_custody_for_kernel_processes();
});
auto unmount_was_successful = true;
while (unmount_was_successful) {
unmount_was_successful = false;
Vector<Mount&, 16> mounts;
TRY(vfs_root_context.mounts().with([&mounts](auto& list) -> ErrorOr<void> {
for (auto& mount : list) {
TRY(mounts.try_append(const_cast<Mount&>(mount)));
}
return {};
}));
if (mounts.is_empty())
break;
auto const remaining_mounts = mounts.size();
while (!mounts.is_empty()) {
auto& mount = mounts.take_last();
TRY(mount.guest_fs().flush_writes());
auto mount_path = TRY(mount.absolute_path());
auto& mount_inode = mount.guest();
auto const result = VirtualFileSystem::the().unmount(vfs_root_context, mount_inode, mount_path->view());
if (result.is_error()) {
dbgln("Error during unmount of {}: {}", mount_path, result.error());
// FIXME: For unknown reasons the root FS stays busy even after everything else has shut down and was unmounted.
// Until we find the underlying issue, allow an unclean shutdown here.
if (remaining_mounts <= 1)
dbgln("BUG! One mount remaining; the root file system may not be unmountable at all. Shutting down anyways.");
} else {
unmount_was_successful = true;
}
}
}
return {};
}
ErrorOr<void> PowerStateSwitchTask::perform_shutdown(PowerStateSwitchTask::DoReboot do_reboot)
{
// We assume that by this point userland has tried as much as possible to shut down everything in an orderly fashion.
@ -92,36 +136,20 @@ ErrorOr<void> PowerStateSwitchTask::perform_shutdown(PowerStateSwitchTask::DoReb
dbgln("Unmounting all file systems...");
auto unmount_was_successful = true;
while (unmount_was_successful) {
unmount_was_successful = false;
Vector<Mount&, 16> mounts;
TRY(VirtualFileSystem::the().for_each_mount([&](auto const& mount) -> ErrorOr<void> {
TRY(mounts.try_append(const_cast<Mount&>(mount)));
return {};
}));
if (mounts.is_empty())
break;
auto const remaining_mounts = mounts.size();
while (!mounts.is_empty()) {
auto& mount = mounts.take_last();
TRY(mount.guest_fs().flush_writes());
auto mount_path = TRY(mount.absolute_path());
auto& mount_inode = mount.guest();
auto const result = VirtualFileSystem::the().unmount(mount_inode, mount_path->view());
if (result.is_error()) {
dbgln("Error during unmount of {}: {}", mount_path, result.error());
// FIXME: For unknown reasons the root FS stays busy even after everything else has shut down and was unmounted.
// Until we find the underlying issue, allow an unclean shutdown here.
if (remaining_mounts <= 1)
dbgln("BUG! One mount remaining; the root file system may not be unmountable at all. Shutting down anyways.");
} else {
unmount_was_successful = true;
size_t collected_contexts_count = 0;
do {
Array<RefPtr<VFSRootContext>, 16> contexts;
VirtualFileSystem::the().all_root_contexts_list(Badge<PowerStateSwitchTask> {}).with([&collected_contexts_count, &contexts](auto& list) {
size_t iteration_collect_count = min(contexts.size(), list.size_slow());
for (collected_contexts_count = 0; collected_contexts_count < iteration_collect_count; collected_contexts_count++) {
contexts[collected_contexts_count] = list.take_first();
}
});
for (size_t index = 0; index < collected_contexts_count; index++) {
VERIFY(contexts[index]);
TRY(unmount_mounts_on_vfs_root_context(*contexts[index]));
}
}
} while (collected_contexts_count > 0);
// NOTE: We don't really need to kill kernel processes, because in contrast
// to user processes, kernel processes will simply not make syscalls

View file

@ -212,7 +212,7 @@ void Process::register_new(Process& process)
});
}
ErrorOr<Process::ProcessAndFirstThread> Process::create_user_process(StringView path, UserID uid, GroupID gid, Vector<NonnullOwnPtr<KString>> arguments, Vector<NonnullOwnPtr<KString>> environment, RefPtr<TTY> tty)
ErrorOr<Process::ProcessAndFirstThread> Process::create_user_process(StringView path, UserID uid, GroupID gid, Vector<NonnullOwnPtr<KString>> arguments, Vector<NonnullOwnPtr<KString>> environment, NonnullRefPtr<VFSRootContext> vfs_root_context, RefPtr<TTY> tty)
{
auto parts = path.split_view('/');
if (arguments.is_empty()) {
@ -221,7 +221,11 @@ ErrorOr<Process::ProcessAndFirstThread> Process::create_user_process(StringView
}
auto path_string = TRY(KString::try_create(path));
auto [process, first_thread] = TRY(Process::create(parts.last(), uid, gid, ProcessID(0), false, VirtualFileSystem::the().root_custody(), nullptr, tty));
auto vfs_root_context_root_custody = vfs_root_context->root_custody().with([](auto& custody) -> NonnullRefPtr<Custody> {
return custody;
});
auto [process, first_thread] = TRY(Process::create(parts.last(), uid, gid, ProcessID(0), false, vfs_root_context, vfs_root_context_root_custody, nullptr, tty));
TRY(process->m_fds.with_exclusive([&](auto& fds) -> ErrorOr<void> {
TRY(fds.try_resize(Process::OpenFileDescriptions::max_open()));
@ -258,7 +262,7 @@ ErrorOr<Process::ProcessAndFirstThread> Process::create_user_process(StringView
ErrorOr<Process::ProcessAndFirstThread> Process::create_kernel_process(StringView name, void (*entry)(void*), void* entry_data, u32 affinity, RegisterProcess do_register)
{
auto process_and_first_thread = TRY(Process::create(name, UserID(0), GroupID(0), ProcessID(0), true));
auto process_and_first_thread = TRY(Process::create(name, UserID(0), GroupID(0), ProcessID(0), true, VFSRootContext::empty_context_for_kernel_processes()));
auto& process = *process_and_first_thread.process;
auto& thread = *process_and_first_thread.first_thread;
@ -287,22 +291,22 @@ void Process::unprotect_data()
});
}
ErrorOr<Process::ProcessAndFirstThread> Process::create_with_forked_name(UserID uid, GroupID gid, ProcessID ppid, bool is_kernel_process, RefPtr<Custody> current_directory, RefPtr<Custody> executable, RefPtr<TTY> tty, Process* fork_parent)
ErrorOr<Process::ProcessAndFirstThread> Process::create_with_forked_name(UserID uid, GroupID gid, ProcessID ppid, bool is_kernel_process, NonnullRefPtr<VFSRootContext> vfs_root_context, RefPtr<Custody> current_directory, RefPtr<Custody> executable, RefPtr<TTY> tty, Process* fork_parent)
{
Process::Name name {};
Process::current().name().with([&name](auto& process_name) {
name.store_characters(process_name.representable_view());
});
return TRY(Process::create(name.representable_view(), uid, gid, ppid, is_kernel_process, current_directory, executable, tty, fork_parent));
return TRY(Process::create(name.representable_view(), uid, gid, ppid, is_kernel_process, move(vfs_root_context), current_directory, executable, tty, fork_parent));
}
ErrorOr<Process::ProcessAndFirstThread> Process::create(StringView name, UserID uid, GroupID gid, ProcessID ppid, bool is_kernel_process, RefPtr<Custody> current_directory, RefPtr<Custody> executable, RefPtr<TTY> tty, Process* fork_parent)
ErrorOr<Process::ProcessAndFirstThread> Process::create(StringView name, UserID uid, GroupID gid, ProcessID ppid, bool is_kernel_process, NonnullRefPtr<VFSRootContext> vfs_root_context, RefPtr<Custody> current_directory, RefPtr<Custody> executable, RefPtr<TTY> tty, Process* fork_parent)
{
auto unveil_tree = UnveilNode { TRY(KString::try_create("/"sv)), UnveilMetadata(TRY(KString::try_create("/"sv))) };
auto exec_unveil_tree = UnveilNode { TRY(KString::try_create("/"sv)), UnveilMetadata(TRY(KString::try_create("/"sv))) };
auto credentials = TRY(Credentials::create(uid, gid, uid, gid, uid, gid, {}, fork_parent ? fork_parent->sid() : 0, fork_parent ? fork_parent->pgid() : 0));
auto process = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Process(name, move(credentials), ppid, is_kernel_process, move(current_directory), move(executable), tty, move(unveil_tree), move(exec_unveil_tree), kgettimeofday())));
auto process = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Process(name, move(credentials), ppid, is_kernel_process, move(vfs_root_context), move(current_directory), move(executable), tty, move(unveil_tree), move(exec_unveil_tree), kgettimeofday())));
OwnPtr<Memory::AddressSpace> new_address_space;
if (fork_parent) {
@ -319,11 +323,12 @@ ErrorOr<Process::ProcessAndFirstThread> Process::create(StringView name, UserID
return ProcessAndFirstThread { move(process), move(first_thread) };
}
Process::Process(StringView name, NonnullRefPtr<Credentials> credentials, ProcessID ppid, bool is_kernel_process, RefPtr<Custody> current_directory, RefPtr<Custody> executable, RefPtr<TTY> tty, UnveilNode unveil_tree, UnveilNode exec_unveil_tree, UnixDateTime creation_time)
Process::Process(StringView name, NonnullRefPtr<Credentials> credentials, ProcessID ppid, bool is_kernel_process, NonnullRefPtr<VFSRootContext> vfs_root_context, RefPtr<Custody> current_directory, RefPtr<Custody> executable, RefPtr<TTY> tty, UnveilNode unveil_tree, UnveilNode exec_unveil_tree, UnixDateTime creation_time)
: m_is_kernel_process(is_kernel_process)
, m_executable(move(executable))
, m_current_directory(move(current_directory))
, m_creation_time(creation_time)
, m_attached_vfs_root_context(move(vfs_root_context))
, m_unveil_data(move(unveil_tree))
, m_exec_unveil_data(move(exec_unveil_tree))
, m_wait_blocker_set(*this)
@ -342,6 +347,10 @@ Process::Process(StringView name, NonnullRefPtr<Credentials> credentials, Proces
dbgln("Created new process {}({})", process_name.representable_view(), this->pid().value());
});
}
m_attached_vfs_root_context.with([](auto& context) {
context->set_attached({});
});
}
ErrorOr<NonnullRefPtr<Thread>> Process::attach_resources(NonnullOwnPtr<Memory::AddressSpace>&& preallocated_space, Process* fork_parent)
@ -737,9 +746,13 @@ siginfo_t Process::wait_info() const
NonnullRefPtr<Custody> Process::current_directory()
{
return m_current_directory.with([&](auto& current_directory) -> NonnullRefPtr<Custody> {
if (!current_directory)
current_directory = VirtualFileSystem::the().root_custody();
return *current_directory;
return m_attached_vfs_root_context.with([&](auto& context) -> NonnullRefPtr<Custody> {
return context->root_custody().with([&](auto& custody) -> NonnullRefPtr<Custody> {
if (!current_directory)
current_directory = custody;
return *current_directory;
});
});
});
}
@ -793,7 +806,7 @@ ErrorOr<void> Process::dump_perfcore()
RefPtr<OpenFileDescription> description;
auto credentials = this->credentials();
for (size_t attempt = 1; attempt <= 10; ++attempt) {
auto description_or_error = VirtualFileSystem::the().open(*this, credentials, perfcore_filename->view(), O_CREAT | O_EXCL, 0400, current_directory(), UidAndGid { 0, 0 });
auto description_or_error = VirtualFileSystem::the().open(*this, vfs_root_context(), credentials, perfcore_filename->view(), O_CREAT | O_EXCL, 0400, current_directory(), UidAndGid { 0, 0 });
if (!description_or_error.is_error()) {
description = description_or_error.release_value();
break;

View file

@ -24,6 +24,8 @@
#include <Kernel/FileSystem/InodeMetadata.h>
#include <Kernel/FileSystem/OpenFileDescription.h>
#include <Kernel/FileSystem/UnveilNode.h>
#include <Kernel/FileSystem/VFSRootContext.h>
#include <Kernel/FileSystem/VirtualFileSystem.h>
#include <Kernel/Forward.h>
#include <Kernel/Library/Assertions.h>
#include <Kernel/Library/LockWeakPtr.h>
@ -214,7 +216,7 @@ public:
}
static ErrorOr<ProcessAndFirstThread> create_kernel_process(StringView name, void (*entry)(void*), void* entry_data = nullptr, u32 affinity = THREAD_AFFINITY_DEFAULT, RegisterProcess do_register = RegisterProcess::Yes);
static ErrorOr<ProcessAndFirstThread> create_user_process(StringView path, UserID, GroupID, Vector<NonnullOwnPtr<KString>> arguments, Vector<NonnullOwnPtr<KString>> environment, RefPtr<TTY>);
static ErrorOr<ProcessAndFirstThread> create_user_process(StringView path, UserID, GroupID, Vector<NonnullOwnPtr<KString>> arguments, Vector<NonnullOwnPtr<KString>> environment, NonnullRefPtr<VFSRootContext>, RefPtr<TTY>);
static void register_new(Process&);
~Process();
@ -274,6 +276,13 @@ public:
return with_protected_data([](auto& protected_data) { return protected_data.ppid; });
}
NonnullRefPtr<VFSRootContext> vfs_root_context() const
{
return m_attached_vfs_root_context.with([](auto& context) -> NonnullRefPtr<VFSRootContext> {
return *context;
});
}
SpinlockProtected<RefPtr<Jail>, LockRank::Process> const& jail() { return m_attached_jail; }
bool is_currently_in_jail() const
@ -674,9 +683,9 @@ private:
bool add_thread(Thread&);
bool remove_thread(Thread&);
Process(StringView name, NonnullRefPtr<Credentials>, ProcessID ppid, bool is_kernel_process, RefPtr<Custody> current_directory, RefPtr<Custody> executable, RefPtr<TTY> tty, UnveilNode unveil_tree, UnveilNode exec_unveil_tree, UnixDateTime creation_time);
static ErrorOr<ProcessAndFirstThread> create_with_forked_name(UserID, GroupID, ProcessID ppid, bool is_kernel_process, RefPtr<Custody> current_directory = nullptr, RefPtr<Custody> executable = nullptr, RefPtr<TTY> = nullptr, Process* fork_parent = nullptr);
static ErrorOr<ProcessAndFirstThread> create(StringView name, UserID, GroupID, ProcessID ppid, bool is_kernel_process, RefPtr<Custody> current_directory = nullptr, RefPtr<Custody> executable = nullptr, RefPtr<TTY> = nullptr, Process* fork_parent = nullptr);
Process(StringView name, NonnullRefPtr<Credentials>, ProcessID ppid, bool is_kernel_process, NonnullRefPtr<VFSRootContext>, RefPtr<Custody> current_directory, RefPtr<Custody> executable, RefPtr<TTY> tty, UnveilNode unveil_tree, UnveilNode exec_unveil_tree, UnixDateTime creation_time);
static ErrorOr<ProcessAndFirstThread> create_with_forked_name(UserID, GroupID, ProcessID ppid, bool is_kernel_process, NonnullRefPtr<VFSRootContext> vfs_root_context, RefPtr<Custody> current_directory = nullptr, RefPtr<Custody> executable = nullptr, RefPtr<TTY> = nullptr, Process* fork_parent = nullptr);
static ErrorOr<ProcessAndFirstThread> create(StringView name, UserID, GroupID, ProcessID ppid, bool is_kernel_process, NonnullRefPtr<VFSRootContext> vfs_root_context, RefPtr<Custody> current_directory = nullptr, RefPtr<Custody> executable = nullptr, RefPtr<TTY> = nullptr, Process* fork_parent = nullptr);
ErrorOr<NonnullRefPtr<Thread>> attach_resources(NonnullOwnPtr<Memory::AddressSpace>&&, Process* fork_parent);
static ProcessID allocate_pid();
@ -964,6 +973,8 @@ private:
SpinlockProtected<RefPtr<ProcessList>, LockRank::None> m_jail_process_list;
SpinlockProtected<RefPtr<Jail>, LockRank::Process> m_attached_jail {};
SpinlockProtected<RefPtr<VFSRootContext>, LockRank::Process> m_attached_vfs_root_context;
Mutex m_big_lock { "Process"sv, Mutex::MutexBehavior::BigLock };
Mutex m_ptrace_lock { "ptrace"sv };