From 0d2e4a7e6714ede09b543a42a3b40ab82e747424 Mon Sep 17 00:00:00 2001 From: Liav A Date: Fri, 9 Feb 2024 15:30:09 +0200 Subject: [PATCH] Kernel/FileSystem: Add the DevLoopFS filesystem Similarly to DevPtsFS, this filesystem is about exposing loop device nodes easily in /dev/loop, so userspace doesn't need to do anything in order to use new devices immediately. --- Kernel/CMakeLists.txt | 2 + Kernel/FileSystem/DevLoopFS/FileSystem.cpp | 76 +++++++++++++ Kernel/FileSystem/DevLoopFS/FileSystem.h | 39 +++++++ Kernel/FileSystem/DevLoopFS/Inode.cpp | 124 +++++++++++++++++++++ Kernel/FileSystem/DevLoopFS/Inode.h | 49 ++++++++ Kernel/FileSystem/VirtualFileSystem.cpp | 2 + 6 files changed, 292 insertions(+) create mode 100644 Kernel/FileSystem/DevLoopFS/FileSystem.cpp create mode 100644 Kernel/FileSystem/DevLoopFS/FileSystem.h create mode 100644 Kernel/FileSystem/DevLoopFS/Inode.cpp create mode 100644 Kernel/FileSystem/DevLoopFS/Inode.h diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index ca67fdb7cdf..b0b3981fae5 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -140,6 +140,8 @@ set(KERNEL_SOURCES FileSystem/AnonymousFile.cpp FileSystem/BlockBasedFileSystem.cpp FileSystem/Custody.cpp + FileSystem/DevLoopFS/FileSystem.cpp + FileSystem/DevLoopFS/Inode.cpp FileSystem/DevPtsFS/FileSystem.cpp FileSystem/DevPtsFS/Inode.cpp FileSystem/Ext2FS/FileSystem.cpp diff --git a/Kernel/FileSystem/DevLoopFS/FileSystem.cpp b/Kernel/FileSystem/DevLoopFS/FileSystem.cpp new file mode 100644 index 00000000000..5f2557c3dec --- /dev/null +++ b/Kernel/FileSystem/DevLoopFS/FileSystem.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2024, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +ErrorOr> DevLoopFS::try_create(ReadonlyBytes) +{ + return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) DevLoopFS)); +} + +DevLoopFS::DevLoopFS() = default; +DevLoopFS::~DevLoopFS() = default; + +u8 DevLoopFS::internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const +{ + return ram_backed_file_type_to_directory_entry_type(entry); +} + +ErrorOr DevLoopFS::initialize() +{ + m_root_inode = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) DevLoopFSInode(*this))); + m_root_inode->m_metadata.inode = { fsid(), 1 }; + m_root_inode->m_metadata.mode = S_IFDIR + | S_IROTH | S_IRGRP | S_IRUSR + | S_IXUSR | S_IXGRP | S_IXOTH; + m_root_inode->m_metadata.uid = 0; + m_root_inode->m_metadata.gid = 0; + m_root_inode->m_metadata.size = 0; + m_root_inode->m_metadata.mtime = TimeManagement::boot_time(); + return {}; +} + +static unsigned inode_index_to_loop_index(InodeIndex inode_index) +{ + VERIFY(inode_index > 1); + return inode_index.value() - 2; +} + +Inode& DevLoopFS::root_inode() +{ + return *m_root_inode; +} + +ErrorOr> DevLoopFS::get_inode(InodeIdentifier inode_id) const +{ + if (inode_id.index() == 1) + return *m_root_inode; + + unsigned loop_index = inode_index_to_loop_index(inode_id.index()); + auto device = DeviceManagement::the().get_device(20, loop_index); + VERIFY(device); + + auto& loop_device = static_cast(*device); + auto inode = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) DevLoopFSInode(const_cast(*this), inode_id.index(), loop_device))); + inode->m_metadata.inode = inode_id; + inode->m_metadata.size = 0; + inode->m_metadata.uid = 0; + inode->m_metadata.gid = 0; + inode->m_metadata.mode = S_IFCHR | S_IRUSR | S_IWUSR; + inode->m_metadata.major_device = device->major(); + inode->m_metadata.minor_device = device->minor(); + inode->m_metadata.mtime = TimeManagement::boot_time(); + return inode; +} + +} diff --git a/Kernel/FileSystem/DevLoopFS/FileSystem.h b/Kernel/FileSystem/DevLoopFS/FileSystem.h new file mode 100644 index 00000000000..76bca0c39f8 --- /dev/null +++ b/Kernel/FileSystem/DevLoopFS/FileSystem.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Kernel { + +class LoopDevice; +class DevLoopFSInode; + +class DevLoopFS final : public FileSystem { + friend class DevLoopFSInode; + +public: + virtual ~DevLoopFS() override; + static ErrorOr> try_create(ReadonlyBytes); + + virtual ErrorOr initialize() override; + virtual StringView class_name() const override { return "DevLoopFS"sv; } + + virtual Inode& root_inode() override; + +private: + virtual u8 internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const override; + + DevLoopFS(); + ErrorOr> get_inode(InodeIdentifier) const; + + RefPtr m_root_inode; +}; + +} diff --git a/Kernel/FileSystem/DevLoopFS/Inode.cpp b/Kernel/FileSystem/DevLoopFS/Inode.cpp new file mode 100644 index 00000000000..e5bbae783d0 --- /dev/null +++ b/Kernel/FileSystem/DevLoopFS/Inode.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2024, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include + +namespace Kernel { + +static InodeIndex loop_index_to_inode_index(unsigned loop_index) +{ + return loop_index + 2; +} + +DevLoopFSInode::DevLoopFSInode(DevLoopFS& fs, InodeIndex index, LoopDevice& loop_device) + : Inode(fs, index) + , m_loop_device(loop_device) +{ +} + +// NOTE: This constructor is used for the root inode only. +DevLoopFSInode::DevLoopFSInode(DevLoopFS& fs) + : Inode(fs, 1) +{ +} + +DevLoopFSInode::~DevLoopFSInode() = default; + +ErrorOr DevLoopFSInode::read_bytes_locked(off_t, size_t, UserOrKernelBuffer&, OpenFileDescription*) const +{ + VERIFY_NOT_REACHED(); +} + +ErrorOr DevLoopFSInode::write_bytes_locked(off_t, size_t, UserOrKernelBuffer const&, OpenFileDescription*) +{ + VERIFY_NOT_REACHED(); +} + +InodeMetadata DevLoopFSInode::metadata() const +{ + return m_metadata; +} + +ErrorOr DevLoopFSInode::traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)> callback) const +{ + if (identifier().index() > 1) + return ENOTDIR; + + TRY(callback({ "."sv, identifier(), to_underlying(RAMBackedFileType::Directory) })); + TRY(callback({ ".."sv, identifier(), to_underlying(RAMBackedFileType::Directory) })); + + return LoopDevice::all_instances().with([&](auto& list) -> ErrorOr { + StringBuilder builder; + for (LoopDevice& loop_device : list) { + builder.clear(); + TRY(builder.try_appendff("{}", loop_device.index())); + TRY(callback({ builder.string_view(), { fsid(), loop_index_to_inode_index(loop_device.index()) }, to_underlying(RAMBackedFileType::Block) })); + } + return {}; + }); +} + +ErrorOr> DevLoopFSInode::lookup(StringView name) +{ + VERIFY(identifier().index() == 1); + + if (name == "." || name == "..") + return *this; + + auto loop_index = name.to_number(); + if (!loop_index.has_value()) + return ENOENT; + + return LoopDevice::all_instances().with([&](auto& list) -> ErrorOr> { + for (LoopDevice& loop_device : list) { + if (loop_device.index() != loop_index.value()) + continue; + return fs().get_inode({ fsid(), loop_index_to_inode_index(loop_index.value()) }); + } + return ENOENT; + }); +} + +ErrorOr DevLoopFSInode::flush_metadata() +{ + return {}; +} + +ErrorOr DevLoopFSInode::add_child(Inode&, StringView, mode_t) +{ + return EROFS; +} + +ErrorOr> DevLoopFSInode::create_child(StringView, mode_t, dev_t, UserID, GroupID) +{ + return EROFS; +} + +ErrorOr DevLoopFSInode::remove_child(StringView) +{ + return EROFS; +} + +ErrorOr DevLoopFSInode::replace_child(StringView, Inode&) +{ + return EROFS; +} + +ErrorOr DevLoopFSInode::chmod(mode_t) +{ + return EROFS; +} + +ErrorOr DevLoopFSInode::chown(UserID, GroupID) +{ + return EROFS; +} + +} diff --git a/Kernel/FileSystem/DevLoopFS/Inode.h b/Kernel/FileSystem/DevLoopFS/Inode.h new file mode 100644 index 00000000000..30864c38103 --- /dev/null +++ b/Kernel/FileSystem/DevLoopFS/Inode.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel { + +class DevLoopFSInode final : public Inode { + friend class DevLoopFS; + +public: + virtual ~DevLoopFSInode() override; + + DevLoopFS& fs() { return static_cast(Inode::fs()); } + DevLoopFS const& fs() const { return static_cast(Inode::fs()); } + +private: + DevLoopFSInode(DevLoopFS&, InodeIndex, LoopDevice&); + + // NOTE: This constructor is used for the root inode only. + DevLoopFSInode(DevLoopFS&); + + // ^Inode + virtual ErrorOr read_bytes_locked(off_t, size_t, UserOrKernelBuffer& buffer, OpenFileDescription*) const override; + virtual InodeMetadata metadata() const override; + virtual ErrorOr traverse_as_directory(Function(FileSystem::DirectoryEntryView const&)>) const override; + virtual ErrorOr> lookup(StringView name) override; + virtual ErrorOr flush_metadata() override; + virtual ErrorOr write_bytes_locked(off_t, size_t, UserOrKernelBuffer const& buffer, OpenFileDescription*) override; + virtual ErrorOr> create_child(StringView name, mode_t, dev_t, UserID, GroupID) override; + virtual ErrorOr add_child(Inode&, StringView name, mode_t) override; + virtual ErrorOr remove_child(StringView name) override; + virtual ErrorOr replace_child(StringView name, Inode& child) override; + virtual ErrorOr chmod(mode_t) override; + virtual ErrorOr chown(UserID, GroupID) override; + + LockWeakPtr m_loop_device; + InodeMetadata m_metadata; +}; + +} diff --git a/Kernel/FileSystem/VirtualFileSystem.cpp b/Kernel/FileSystem/VirtualFileSystem.cpp index 59cd24c2a98..c3c670663a0 100644 --- a/Kernel/FileSystem/VirtualFileSystem.cpp +++ b/Kernel/FileSystem/VirtualFileSystem.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -68,6 +69,7 @@ static constexpr FileSystemInitializer s_initializers[] = { { "9p"sv, "Plan9FS"sv, true, true, true, Plan9FS::try_create, {}, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid }, { "iso9660"sv, "ISO9660FS"sv, true, true, true, ISO9660FS::try_create, {}, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid }, { "fat"sv, "FATFS"sv, true, true, true, FATFS::try_create, {}, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid }, + { "devloop"sv, "DevLoopFS"sv, false, false, false, {}, DevLoopFS::try_create, handle_mount_boolean_flag_as_invalid, handle_mount_unsigned_integer_flag_as_invalid, handle_mount_signed_integer_flag_as_invalid, handle_mount_ascii_string_flag_as_invalid }, }; ErrorOr VirtualFileSystem::find_filesystem_type_initializer(StringView fs_type)