diff --git a/Kernel/FileSystem/BlockBasedFileSystem.cpp b/Kernel/FileSystem/BlockBasedFileSystem.cpp index ab2cd21689..b343022ecf 100644 --- a/Kernel/FileSystem/BlockBasedFileSystem.cpp +++ b/Kernel/FileSystem/BlockBasedFileSystem.cpp @@ -116,8 +116,10 @@ BlockBasedFileSystem::BlockBasedFileSystem(OpenFileDescription& file_description BlockBasedFileSystem::~BlockBasedFileSystem() = default; -ErrorOr BlockBasedFileSystem::initialize() +ErrorOr BlockBasedFileSystem::initialize_while_locked() { + VERIFY(m_lock.is_locked()); + VERIFY(!is_initialized_while_locked()); VERIFY(block_size() != 0); auto cached_block_data = TRY(KBuffer::try_create_with_size("BlockBasedFS: Cache blocks"sv, DiskCache::EntryCount * block_size())); auto entries_data = TRY(KBuffer::try_create_with_size("BlockBasedFS: Cache entries"sv, DiskCache::EntryCount * sizeof(CacheEntry))); diff --git a/Kernel/FileSystem/BlockBasedFileSystem.h b/Kernel/FileSystem/BlockBasedFileSystem.h index 47a25ec8f2..817747a482 100644 --- a/Kernel/FileSystem/BlockBasedFileSystem.h +++ b/Kernel/FileSystem/BlockBasedFileSystem.h @@ -16,7 +16,6 @@ public: AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, BlockIndex); virtual ~BlockBasedFileSystem() override; - virtual ErrorOr initialize() override; u64 logical_block_size() const { return m_logical_block_size; }; @@ -26,6 +25,8 @@ public: protected: explicit BlockBasedFileSystem(OpenFileDescription&); + virtual ErrorOr initialize_while_locked() override; + ErrorOr read_block(BlockIndex, UserOrKernelBuffer*, size_t count, u64 offset = 0, bool allow_cache = true) const; ErrorOr read_blocks(BlockIndex, unsigned count, UserOrKernelBuffer&, bool allow_cache = true) const; diff --git a/Kernel/FileSystem/Ext2FileSystem.cpp b/Kernel/FileSystem/Ext2FileSystem.cpp index fadf67888f..e36f63e5f5 100644 --- a/Kernel/FileSystem/Ext2FileSystem.cpp +++ b/Kernel/FileSystem/Ext2FileSystem.cpp @@ -77,9 +77,16 @@ ext2_group_desc const& Ext2FS::group_descriptor(GroupIndex group_index) const return block_group_descriptors()[group_index.value() - 1]; } -ErrorOr Ext2FS::initialize() +bool Ext2FS::is_initialized_while_locked() { - MutexLocker locker(m_lock); + VERIFY(m_lock.is_locked()); + return !m_root_inode.is_null(); +} + +ErrorOr Ext2FS::initialize_while_locked() +{ + VERIFY(m_lock.is_locked()); + VERIFY(!is_initialized_while_locked()); VERIFY((sizeof(ext2_super_block) % logical_block_size()) == 0); auto super_block_buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)&m_super_block); @@ -109,7 +116,7 @@ ErrorOr Ext2FS::initialize() set_fragment_size(EXT2_FRAG_SIZE(&super_block)); // Note: This depends on the block size being available. - TRY(BlockBasedFileSystem::initialize()); + TRY(BlockBasedFileSystem::initialize_while_locked()); VERIFY(block_size() <= (int)max_block_size); @@ -1680,7 +1687,7 @@ unsigned Ext2FS::free_inode_count() const return super_block().s_free_inodes_count; } -ErrorOr Ext2FS::prepare_to_unmount() +ErrorOr Ext2FS::prepare_to_clear_last_mount() { MutexLocker locker(m_lock); diff --git a/Kernel/FileSystem/Ext2FileSystem.h b/Kernel/FileSystem/Ext2FileSystem.h index 9c65ee68f4..9d22d04a42 100644 --- a/Kernel/FileSystem/Ext2FileSystem.h +++ b/Kernel/FileSystem/Ext2FileSystem.h @@ -91,15 +91,12 @@ public: static ErrorOr> try_create(OpenFileDescription&); virtual ~Ext2FS() override; - virtual ErrorOr initialize() override; virtual unsigned total_block_count() const override; virtual unsigned free_block_count() const override; virtual unsigned total_inode_count() const override; virtual unsigned free_inode_count() const override; - virtual ErrorOr prepare_to_unmount() override; - virtual bool supports_watchers() const override { return true; } virtual u8 internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const override; @@ -126,6 +123,10 @@ private: ErrorOr flush_super_block(); + virtual ErrorOr initialize_while_locked() override; + virtual bool is_initialized_while_locked() override; + + virtual ErrorOr prepare_to_clear_last_mount() override; virtual StringView class_name() const override { return "Ext2FS"sv; } virtual Ext2FSInode& root_inode() override; ErrorOr> get_inode(InodeIdentifier) const; diff --git a/Kernel/FileSystem/FATFileSystem.cpp b/Kernel/FileSystem/FATFileSystem.cpp index ee06488b86..431c95ff8f 100644 --- a/Kernel/FileSystem/FATFileSystem.cpp +++ b/Kernel/FileSystem/FATFileSystem.cpp @@ -22,9 +22,16 @@ FATFS::FATFS(OpenFileDescription& file_description) { } -ErrorOr FATFS::initialize() +bool FATFS::is_initialized_while_locked() { - MutexLocker locker(m_lock); + VERIFY(m_lock.is_locked()); + return !m_root_inode.is_null(); +} + +ErrorOr FATFS::initialize_while_locked() +{ + VERIFY(m_lock.is_locked()); + VERIFY(!is_initialized_while_locked()); m_boot_record = TRY(KBuffer::try_create_with_size("FATFS: Boot Record"sv, m_logical_block_size)); auto boot_record_buffer = UserOrKernelBuffer::for_kernel_buffer(m_boot_record->data()); @@ -63,7 +70,7 @@ ErrorOr FATFS::initialize() u32 root_directory_sectors = ((boot_record()->root_directory_entry_count * sizeof(FATEntry)) + (m_logical_block_size - 1)) / m_logical_block_size; m_first_data_sector = boot_record()->reserved_sector_count + (boot_record()->fat_count * boot_record()->sectors_per_fat) + root_directory_sectors; - TRY(BlockBasedFileSystem::initialize()); + TRY(BlockBasedFileSystem::initialize_while_locked()); FATEntry root_entry {}; diff --git a/Kernel/FileSystem/FATFileSystem.h b/Kernel/FileSystem/FATFileSystem.h index 807191aaa3..61cc30d437 100644 --- a/Kernel/FileSystem/FATFileSystem.h +++ b/Kernel/FileSystem/FATFileSystem.h @@ -116,11 +116,16 @@ public: static ErrorOr> try_create(OpenFileDescription&); virtual ~FATFS() override = default; - virtual ErrorOr initialize() override; virtual StringView class_name() const override { return "FATFS"sv; } virtual Inode& root_inode() override; private: + virtual ErrorOr initialize_while_locked() override; + virtual bool is_initialized_while_locked() override; + // FIXME: This is not a proper way to clear last mount of a FAT filesystem, + // but for now we simply have no other way to properly do it. + virtual ErrorOr prepare_to_clear_last_mount() override { return {}; } + FATFS(OpenFileDescription&); static constexpr u8 signature_1 = 0x28; diff --git a/Kernel/FileSystem/FileBackedFileSystem.cpp b/Kernel/FileSystem/FileBackedFileSystem.cpp index ab0c1e97e4..f67a75f48b 100644 --- a/Kernel/FileSystem/FileBackedFileSystem.cpp +++ b/Kernel/FileSystem/FileBackedFileSystem.cpp @@ -15,4 +15,12 @@ FileBackedFileSystem::FileBackedFileSystem(OpenFileDescription& file_description FileBackedFileSystem::~FileBackedFileSystem() = default; +ErrorOr FileBackedFileSystem::initialize() +{ + MutexLocker locker(m_lock); + if (is_initialized_while_locked()) + return {}; + return initialize_while_locked(); +} + } diff --git a/Kernel/FileSystem/FileBackedFileSystem.h b/Kernel/FileSystem/FileBackedFileSystem.h index e959bb729c..8bfb1a1b6f 100644 --- a/Kernel/FileSystem/FileBackedFileSystem.h +++ b/Kernel/FileSystem/FileBackedFileSystem.h @@ -12,6 +12,8 @@ namespace Kernel { class FileBackedFileSystem : public FileSystem { + friend class VirtualFileSystem; + public: virtual ~FileBackedFileSystem() override; @@ -23,10 +25,19 @@ public: protected: explicit FileBackedFileSystem(OpenFileDescription&); + // Note: We require all FileBackedFileSystem to implement something that actually + // takes into account the fact that we will clean the last mount of the filesystem, + // therefore, removing the file system with it from the Kernel memory. + virtual ErrorOr prepare_to_clear_last_mount() override = 0; + + virtual ErrorOr initialize_while_locked() = 0; + virtual bool is_initialized_while_locked() = 0; + private: + virtual ErrorOr initialize() override final; virtual bool is_file_backed() const override { return true; } + IntrusiveListNode m_file_backed_file_system_node; mutable NonnullLockRefPtr m_file_description; }; - } diff --git a/Kernel/FileSystem/FileSystem.cpp b/Kernel/FileSystem/FileSystem.cpp index 4c3ea5959f..09f3bfc143 100644 --- a/Kernel/FileSystem/FileSystem.cpp +++ b/Kernel/FileSystem/FileSystem.cpp @@ -42,6 +42,15 @@ FileSystem* FileSystem::from_fsid(FileSystemID id) return nullptr; } +ErrorOr FileSystem::prepare_to_unmount() +{ + return m_attach_count.with([&](auto& attach_count) -> ErrorOr { + if (attach_count == 1) + return prepare_to_clear_last_mount(); + return {}; + }); +} + FileSystem::DirectoryEntryView::DirectoryEntryView(StringView n, InodeIdentifier i, u8 ft) : name(n) , inode(i) diff --git a/Kernel/FileSystem/FileSystem.h b/Kernel/FileSystem/FileSystem.h index 7618cd5edf..4098884062 100644 --- a/Kernel/FileSystem/FileSystem.h +++ b/Kernel/FileSystem/FileSystem.h @@ -41,7 +41,7 @@ public: virtual unsigned total_inode_count() const { return 0; } virtual unsigned free_inode_count() const { return 0; } - virtual ErrorOr prepare_to_unmount() { return {}; } + ErrorOr prepare_to_unmount(); struct DirectoryEntryView { DirectoryEntryView(StringView name, InodeIdentifier, u8 file_type); @@ -61,12 +61,16 @@ public: // Converts file types that are used internally by the filesystem to DT_* types virtual u8 internal_file_type_to_directory_entry_type(DirectoryEntryView const& entry) const { return entry.file_type; } + SpinlockProtected& mounted_count(Badge) { return m_attach_count; } + protected: FileSystem(); void set_block_size(u64 size) { m_block_size = size; } void set_fragment_size(size_t size) { m_fragment_size = size; } + virtual ErrorOr prepare_to_clear_last_mount() { return {}; } + mutable Mutex m_lock { "FS"sv }; private: @@ -74,6 +78,8 @@ private: u64 m_block_size { 0 }; size_t m_fragment_size { 0 }; bool m_readonly { false }; + + SpinlockProtected m_attach_count { LockRank::FileSystem, 0 }; }; inline FileSystem* InodeIdentifier::fs() // NOLINT(readability-make-member-function-const) const InodeIdentifiers should not be able to modify the FileSystem diff --git a/Kernel/FileSystem/ISO9660FileSystem.cpp b/Kernel/FileSystem/ISO9660FileSystem.cpp index c7fbf99f09..3061f868a8 100644 --- a/Kernel/FileSystem/ISO9660FileSystem.cpp +++ b/Kernel/FileSystem/ISO9660FileSystem.cpp @@ -182,9 +182,18 @@ ISO9660FS::ISO9660FS(OpenFileDescription& description) ISO9660FS::~ISO9660FS() = default; -ErrorOr ISO9660FS::initialize() +bool ISO9660FS::is_initialized_while_locked() { - TRY(BlockBasedFileSystem::initialize()); + VERIFY(m_lock.is_locked()); + return !m_root_inode.is_null(); +} + +ErrorOr ISO9660FS::initialize_while_locked() +{ + VERIFY(m_lock.is_locked()); + VERIFY(!is_initialized_while_locked()); + + TRY(BlockBasedFileSystem::initialize_while_locked()); TRY(parse_volume_set()); TRY(create_root_inode()); return {}; @@ -223,6 +232,12 @@ u8 ISO9660FS::internal_file_type_to_directory_entry_type(DirectoryEntryView cons return DT_REG; } +ErrorOr ISO9660FS::prepare_to_clear_last_mount() +{ + // FIXME: Do proper cleaning here. + return {}; +} + ErrorOr ISO9660FS::parse_volume_set() { VERIFY(!m_primary_volume); diff --git a/Kernel/FileSystem/ISO9660FileSystem.h b/Kernel/FileSystem/ISO9660FileSystem.h index db97de8480..dd44683766 100644 --- a/Kernel/FileSystem/ISO9660FileSystem.h +++ b/Kernel/FileSystem/ISO9660FileSystem.h @@ -308,7 +308,6 @@ public: static ErrorOr> try_create(OpenFileDescription&); virtual ~ISO9660FS() override; - virtual ErrorOr initialize() override; virtual StringView class_name() const override { return "ISO9660FS"sv; } virtual Inode& root_inode() override; @@ -322,6 +321,11 @@ public: private: ISO9660FS(OpenFileDescription&); + virtual ErrorOr prepare_to_clear_last_mount() override; + + virtual bool is_initialized_while_locked() override; + virtual ErrorOr initialize_while_locked() override; + ErrorOr parse_volume_set(); ErrorOr create_root_inode(); ErrorOr calculate_inode_count() const; diff --git a/Kernel/FileSystem/Plan9FileSystem.cpp b/Kernel/FileSystem/Plan9FileSystem.cpp index 882faaf35b..be2f4f18fb 100644 --- a/Kernel/FileSystem/Plan9FileSystem.cpp +++ b/Kernel/FileSystem/Plan9FileSystem.cpp @@ -20,6 +20,12 @@ Plan9FS::Plan9FS(OpenFileDescription& file_description) { } +ErrorOr Plan9FS::prepare_to_clear_last_mount() +{ + // FIXME: Do proper cleaning here. + return {}; +} + Plan9FS::~Plan9FS() { // Make sure to destroy the root inode before the FS gets destroyed. @@ -196,8 +202,17 @@ private: bool m_have_been_built { false }; }; -ErrorOr Plan9FS::initialize() +bool Plan9FS::is_initialized_while_locked() { + VERIFY(m_lock.is_locked()); + return !m_root_inode.is_null(); +} + +ErrorOr Plan9FS::initialize_while_locked() +{ + VERIFY(m_lock.is_locked()); + VERIFY(!is_initialized_while_locked()); + ensure_thread(); Message version_message { *this, Message::Type::Tversion }; diff --git a/Kernel/FileSystem/Plan9FileSystem.h b/Kernel/FileSystem/Plan9FileSystem.h index e5c59b90ce..ac3c6ad126 100644 --- a/Kernel/FileSystem/Plan9FileSystem.h +++ b/Kernel/FileSystem/Plan9FileSystem.h @@ -22,8 +22,6 @@ public: virtual ~Plan9FS() override; static ErrorOr> try_create(OpenFileDescription&); - virtual ErrorOr initialize() override; - virtual bool supports_watchers() const override { return false; } virtual Inode& root_inode() override; @@ -48,6 +46,11 @@ public: private: Plan9FS(OpenFileDescription&); + virtual ErrorOr prepare_to_clear_last_mount() override; + + virtual bool is_initialized_while_locked() override; + virtual ErrorOr initialize_while_locked() override; + class Blocker; class Plan9FSBlockerSet final : public Thread::BlockerSet { diff --git a/Kernel/FileSystem/VirtualFileSystem.cpp b/Kernel/FileSystem/VirtualFileSystem.cpp index 5960a3a83b..df7f03ca6e 100644 --- a/Kernel/FileSystem/VirtualFileSystem.cpp +++ b/Kernel/FileSystem/VirtualFileSystem.cpp @@ -65,7 +65,8 @@ ErrorOr VirtualFileSystem::mount(FileSystem& fs, Custody& mount_point, int auto new_mount = TRY(adopt_nonnull_own_or_enomem(new (nothrow) Mount(fs, &mount_point, flags))); return m_mounts.with([&](auto& mounts) -> ErrorOr { auto& inode = mount_point.inode(); - dbgln("VirtualFileSystem: Mounting {} at inode {} with flags {}", + dbgln("VirtualFileSystem: FileSystemID {}, Mounting {} at inode {} with flags {}", + fs.fsid(), fs.class_name(), inode.identifier(), flags); @@ -73,6 +74,10 @@ ErrorOr VirtualFileSystem::mount(FileSystem& fs, Custody& mount_point, int dbgln("VirtualFileSystem: Mounting unsuccessful - inode {} is already a 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++; + }); mounts.append(move(new_mount)); return {}; }); @@ -106,16 +111,24 @@ ErrorOr VirtualFileSystem::remount(Custody& mount_point, int new_flags) return {}; } -ErrorOr VirtualFileSystem::unmount(Inode& guest_inode) +ErrorOr VirtualFileSystem::unmount(Custody& mountpoint_custody) { - dbgln("VirtualFileSystem: unmount called with inode {}", guest_inode.identifier()); + auto& guest_inode = mountpoint_custody.inode(); + auto custody_path = TRY(mountpoint_custody.try_serialize_absolute_path()); + dbgln("VirtualFileSystem: unmount called with inode {} on mountpoint {}", guest_inode.identifier(), custody_path->view()); return m_mounts.with([&](auto& mounts) -> ErrorOr { for (size_t i = 0; i < mounts.size(); ++i) { auto& mount = mounts[i]; if (&mount->guest() != &guest_inode) continue; + auto mountpoint_path = TRY(mount->absolute_path()); + if (custody_path->view() != mountpoint_path->view()) + continue; TRY(mount->guest_fs().prepare_to_unmount()); + mount->guest_fs().mounted_count({}).with([&](auto& mounted_count) { + mounted_count--; + }); dbgln("VirtualFileSystem: Unmounting file system {}...", mount->guest_fs().fsid()); (void)mounts.unstable_take(i); return {}; @@ -142,9 +155,13 @@ ErrorOr VirtualFileSystem::mount_root(FileSystem& fs) m_root_inode = root_inode; auto pseudo_path = TRY(static_cast(fs).file_description().pseudo_path()); - dmesgln("VirtualFileSystem: mounted root from {} ({})", fs.class_name(), pseudo_path); + dmesgln("VirtualFileSystem: mounted root({}) from {} ({})", fs.fsid(), fs.class_name(), pseudo_path); + // Note: Actually add a mount for the filesystem and increment the filesystem mounted count m_mounts.with([&](auto& mounts) { + new_mount->guest_fs().mounted_count({}).with([&](auto& mounted_count) { + mounted_count++; + }); mounts.append(move(new_mount)); }); @@ -241,6 +258,24 @@ ErrorOr VirtualFileSystem::lookup_metadata(Credentials const& cre return custody->inode().metadata(); } +ErrorOr> VirtualFileSystem::find_already_existing_or_create_file_backed_file_system(OpenFileDescription& description, Function>(OpenFileDescription&)> callback) +{ + return TRY(m_file_backed_file_systems_list.with([&](auto& list) -> ErrorOr> { + for (auto& node : list) { + if (&node.file_description() == &description) { + return node; + } + if (&node.file() == &description.file()) { + return node; + } + } + auto fs = TRY(callback(description)); + VERIFY(fs->is_file_backed()); + list.append(static_cast(*fs)); + return static_ptr_cast(fs); + })); +} + ErrorOr> VirtualFileSystem::open(Credentials const& credentials, StringView path, int options, mode_t mode, Custody& base, Optional owner) { if ((options & O_CREAT) && (options & O_DIRECTORY)) diff --git a/Kernel/FileSystem/VirtualFileSystem.h b/Kernel/FileSystem/VirtualFileSystem.h index 75094b4b1b..514217e1e1 100644 --- a/Kernel/FileSystem/VirtualFileSystem.h +++ b/Kernel/FileSystem/VirtualFileSystem.h @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include #include @@ -48,7 +50,7 @@ public: ErrorOr mount(FileSystem&, Custody& mount_point, int flags); ErrorOr bind_mount(Custody& source, Custody& mount_point, int flags); ErrorOr remount(Custody& mount_point, int new_flags); - ErrorOr unmount(Inode& guest_inode); + ErrorOr unmount(Custody& mount_point); ErrorOr> open(Credentials const&, StringView path, int options, mode_t mode, Custody& base, Optional = {}); ErrorOr> create(Credentials const&, StringView path, int options, mode_t mode, Custody& parent_custody, Optional = {}); @@ -71,6 +73,8 @@ public: ErrorOr for_each_mount(Function(Mount const&)>) const; + ErrorOr> find_already_existing_or_create_file_backed_file_system(OpenFileDescription& description, Function>(OpenFileDescription&)> callback); + InodeIdentifier root_inode_id() const; static void sync(); @@ -101,6 +105,7 @@ private: SpinlockProtected> m_root_custody; SpinlockProtected, 16>> m_mounts { LockRank::None }; + SpinlockProtected> m_file_backed_file_systems_list { LockRank::None }; }; } diff --git a/Kernel/Syscalls/mount.cpp b/Kernel/Syscalls/mount.cpp index 4b1f306dcf..a97425ee40 100644 --- a/Kernel/Syscalls/mount.cpp +++ b/Kernel/Syscalls/mount.cpp @@ -39,7 +39,7 @@ static constexpr FileSystemInitializer s_initializers[] = { { "fat"sv, "FATFS"sv, true, true, true, FATFS::try_create, {} }, }; -static ErrorOr> create_filesystem_instance(StringView fs_type, OpenFileDescription* possible_description) +static ErrorOr> find_or_create_filesystem_instance(StringView fs_type, OpenFileDescription* possible_description) { for (auto& initializer_entry : s_initializers) { if (fs_type != initializer_entry.short_name && fs_type != initializer_entry.name) @@ -49,6 +49,9 @@ static ErrorOr> create_filesystem_instance(StringV NonnullLockRefPtr fs = TRY(initializer_entry.create()); return fs; } + // Note: If there's an associated file description with the filesystem, we could + // try to first find it from the VirtualFileSystem filesystem list and if it was not found, + // then create it and add it. VERIFY(initializer_entry.create_with_fd); if (!possible_description) return EBADF; @@ -60,8 +63,7 @@ static ErrorOr> create_filesystem_instance(StringV dbgln("mount: this is not a seekable file"); return ENODEV; } - NonnullLockRefPtr fs = TRY(initializer_entry.create_with_fd(description)); - return fs; + return TRY(VirtualFileSystem::the().find_already_existing_or_create_file_backed_file_system(description, initializer_entry.create_with_fd)); } return ENODEV; } @@ -112,11 +114,11 @@ ErrorOr Process::sys$mount(Userspace u if (!description_or_error.is_error()) { auto description = description_or_error.release_value(); - fs = TRY(create_filesystem_instance(fs_type, description.ptr())); + fs = TRY(find_or_create_filesystem_instance(fs_type, description.ptr())); auto source_pseudo_path = TRY(description->pseudo_path()); dbgln("mount: attempting to mount {} on {}", source_pseudo_path, target); } else { - fs = TRY(create_filesystem_instance(fs_type, {})); + fs = TRY(find_or_create_filesystem_instance(fs_type, {})); } TRY(fs->initialize()); @@ -135,8 +137,7 @@ ErrorOr Process::sys$umount(Userspace user_mountpoint, siz auto mountpoint = TRY(get_syscall_path_argument(user_mountpoint, mountpoint_length)); auto custody = TRY(VirtualFileSystem::the().resolve_path(credentials, mountpoint->view(), current_directory())); - auto& guest_inode = custody->inode(); - TRY(VirtualFileSystem::the().unmount(guest_inode)); + TRY(VirtualFileSystem::the().unmount(*custody)); return 0; }