From b7007016c0a131c80490c6ac1e528e40b5db195f Mon Sep 17 00:00:00 2001 From: Bananymous Date: Wed, 25 Oct 2023 21:43:36 +0300 Subject: [PATCH] BAN: Implement Ext2 file unlinking Ext2 inodes can now be unlinked from directories and after last inode closes (destructor gets called) we check if link count is 0 and cleanup the inode from filesystem --- kernel/include/kernel/FS/Ext2/FileSystem.h | 3 +- kernel/include/kernel/FS/Ext2/Inode.h | 5 ++ kernel/include/kernel/FS/Inode.h | 4 +- kernel/include/kernel/FS/RamFS/Inode.h | 2 +- kernel/kernel/FS/Ext2/FileSystem.cpp | 70 +++++++++++++++++- kernel/kernel/FS/Ext2/Inode.cpp | 85 ++++++++++++++++++++-- kernel/kernel/FS/Inode.cpp | 4 +- kernel/kernel/FS/ProcFS/FileSystem.cpp | 2 +- kernel/kernel/FS/RamFS/Inode.cpp | 3 +- 9 files changed, 160 insertions(+), 18 deletions(-) diff --git a/kernel/include/kernel/FS/Ext2/FileSystem.h b/kernel/include/kernel/FS/Ext2/FileSystem.h index be6e5bcfeb..ff0ff6e9a7 100644 --- a/kernel/include/kernel/FS/Ext2/FileSystem.h +++ b/kernel/include/kernel/FS/Ext2/FileSystem.h @@ -58,7 +58,7 @@ namespace Kernel BAN::ErrorOr initialize_root_inode(); BAN::ErrorOr create_inode(const Ext2::Inode&); - BAN::ErrorOr delete_inode(uint32_t); + void delete_inode(uint32_t); BAN::ErrorOr resize_inode(uint32_t, size_t); void read_block(uint32_t, BlockBufferWrapper&); @@ -68,6 +68,7 @@ namespace Kernel BlockBufferWrapper get_block_buffer(); BAN::ErrorOr reserve_free_block(uint32_t primary_bgd); + void release_block(uint32_t block); BAN::HashMap>& inode_cache() { return m_inode_cache; } diff --git a/kernel/include/kernel/FS/Ext2/Inode.h b/kernel/include/kernel/FS/Ext2/Inode.h index 14ba10ed24..b42fe34de0 100644 --- a/kernel/include/kernel/FS/Ext2/Inode.h +++ b/kernel/include/kernel/FS/Ext2/Inode.h @@ -13,6 +13,8 @@ namespace Kernel class Ext2Inode final : public Inode { public: + ~Ext2Inode(); + virtual ino_t ino() const override { return m_ino; }; virtual Mode mode() const override { return { m_inode.mode }; } virtual nlink_t nlink() const override { return m_inode.links_count; } @@ -32,6 +34,7 @@ namespace Kernel virtual BAN::ErrorOr list_next_inodes_impl(off_t, DirectoryEntryList*, size_t) override; virtual BAN::ErrorOr create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override; virtual BAN::ErrorOr create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) override; + virtual BAN::ErrorOr unlink_impl(BAN::StringView) override; virtual BAN::ErrorOr link_target_impl() override; @@ -45,6 +48,8 @@ namespace Kernel BAN::ErrorOr link_inode_to_directory(Ext2Inode&, BAN::StringView name); + void cleanup_from_fs(); + BAN::ErrorOr allocate_new_block(); BAN::ErrorOr sync(); diff --git a/kernel/include/kernel/FS/Inode.h b/kernel/include/kernel/FS/Inode.h index f5cd27c2a2..4cdc16e370 100644 --- a/kernel/include/kernel/FS/Inode.h +++ b/kernel/include/kernel/FS/Inode.h @@ -91,7 +91,7 @@ namespace Kernel BAN::ErrorOr list_next_inodes(off_t, DirectoryEntryList*, size_t); BAN::ErrorOr create_file(BAN::StringView, mode_t, uid_t, gid_t); BAN::ErrorOr create_directory(BAN::StringView, mode_t, uid_t, gid_t); - BAN::ErrorOr delete_inode(BAN::StringView); + BAN::ErrorOr unlink(BAN::StringView); // Link API BAN::ErrorOr link_target(); @@ -109,7 +109,7 @@ namespace Kernel virtual BAN::ErrorOr list_next_inodes_impl(off_t, DirectoryEntryList*, size_t) { return BAN::Error::from_errno(ENOTSUP); } virtual BAN::ErrorOr create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) { return BAN::Error::from_errno(ENOTSUP); } virtual BAN::ErrorOr create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) { return BAN::Error::from_errno(ENOTSUP); } - virtual BAN::ErrorOr delete_inode_impl(BAN::StringView) { return BAN::Error::from_errno(ENOTSUP); } + virtual BAN::ErrorOr unlink_impl(BAN::StringView) { return BAN::Error::from_errno(ENOTSUP); } // Link API virtual BAN::ErrorOr link_target_impl() { return BAN::Error::from_errno(ENOTSUP); } diff --git a/kernel/include/kernel/FS/RamFS/Inode.h b/kernel/include/kernel/FS/RamFS/Inode.h index 7cefae9ca7..cc047ec11d 100644 --- a/kernel/include/kernel/FS/RamFS/Inode.h +++ b/kernel/include/kernel/FS/RamFS/Inode.h @@ -97,7 +97,7 @@ namespace Kernel virtual BAN::ErrorOr list_next_inodes_impl(off_t, DirectoryEntryList*, size_t) override; virtual BAN::ErrorOr create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override; virtual BAN::ErrorOr create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) override; - virtual BAN::ErrorOr delete_inode_impl(BAN::StringView) override; + virtual BAN::ErrorOr unlink_impl(BAN::StringView) override; private: static constexpr size_t m_name_max = NAME_MAX; diff --git a/kernel/kernel/FS/Ext2/FileSystem.cpp b/kernel/kernel/FS/Ext2/FileSystem.cpp index 37b1b55c88..8a0dccd809 100644 --- a/kernel/kernel/FS/Ext2/FileSystem.cpp +++ b/kernel/kernel/FS/Ext2/FileSystem.cpp @@ -212,6 +212,40 @@ namespace Kernel return BAN::Error::from_error_code(ErrorCode::Ext2_Corrupted); } + void Ext2FS::delete_inode(uint32_t ino) + { + LockGuard _(m_lock); + + ASSERT(ino <= superblock().inodes_count); + + auto bgd_buffer = get_block_buffer(); + auto bitmap_buffer = get_block_buffer(); + + const uint32_t inode_group = (ino - 1) / superblock().inodes_per_group; + const uint32_t inode_index = (ino - 1) % superblock().inodes_per_group; + + auto bgd_location = locate_block_group_descriptior(inode_group); + read_block(bgd_location.block, bgd_buffer); + + auto& bgd = bgd_buffer.span().slice(bgd_location.offset).as(); + read_block(bgd.inode_bitmap, bitmap_buffer); + + const uint32_t byte = inode_index / 8; + const uint32_t bit = inode_index % 8; + ASSERT(bitmap_buffer[byte] & (1 << bit)); + + bitmap_buffer[byte] &= ~(1 << bit); + write_block(bgd.inode_bitmap, bitmap_buffer); + + bgd.free_inodes_count++; + write_block(bgd_location.block, bgd_buffer); + + if (m_inode_cache.contains(ino)) + m_inode_cache.remove(ino); + + dprintln("succesfully deleted inode {}", ino); + } + void Ext2FS::read_block(uint32_t block, BlockBufferWrapper& buffer) { LockGuard _(m_lock); @@ -255,8 +289,7 @@ namespace Kernel const uint32_t lba = 1024 / sector_size; const uint32_t sector_count = BAN::Math::div_round_up(superblock_bytes, sector_size); - BAN::Vector superblock_buffer; - MUST(superblock_buffer.resize(sector_count * sector_size)); + auto superblock_buffer = get_block_buffer(); MUST(m_partition.read_sectors(lba, sector_count, superblock_buffer.span())); if (memcmp(superblock_buffer.data(), &m_superblock, superblock_bytes)) @@ -266,7 +299,6 @@ namespace Kernel } } - Ext2FS::BlockBufferWrapper Ext2FS::get_block_buffer() { LockGuard _(m_lock); @@ -334,6 +366,38 @@ namespace Kernel return BAN::Error::from_error_code(ErrorCode::Ext2_Corrupted); } + void Ext2FS::release_block(uint32_t block) + { + LockGuard _(m_lock); + + ASSERT(block >= m_superblock.first_data_block); + ASSERT(block < m_superblock.blocks_count); + + const uint32_t block_group = (block - m_superblock.first_data_block) / m_superblock.blocks_per_group; + const uint32_t block_offset = (block - m_superblock.first_data_block) % m_superblock.blocks_per_group; + + auto bgd_buffer = get_block_buffer(); + auto bitmap_buffer = get_block_buffer(); + + auto bgd_location = locate_block_group_descriptior(block_group); + read_block(bgd_location.block, bgd_buffer); + + auto& bgd = bgd_buffer.span().slice(bgd_location.offset).as(); + read_block(bgd.block_bitmap, bitmap_buffer); + + const uint32_t byte = block_offset / 8; + const uint32_t bit = block_offset % 8; + ASSERT(bitmap_buffer[byte] & (1 << bit)); + + bitmap_buffer[byte] &= ~(1 << bit); + write_block(bgd.block_bitmap, bitmap_buffer); + + bgd.free_blocks_count++; + write_block(bgd_location.block, bgd_buffer); + + dprintln("successfully freed block {}", block); + } + Ext2FS::BlockLocation Ext2FS::locate_inode(uint32_t ino) { LockGuard _(m_lock); diff --git a/kernel/kernel/FS/Ext2/Inode.cpp b/kernel/kernel/FS/Ext2/Inode.cpp index 332619ef27..570e3ec6fd 100644 --- a/kernel/kernel/FS/Ext2/Inode.cpp +++ b/kernel/kernel/FS/Ext2/Inode.cpp @@ -41,6 +41,12 @@ namespace Kernel return result; } + Ext2Inode::~Ext2Inode() + { + if ((mode().ifdir() && m_inode.links_count == 1) || m_inode.links_count == 0) + cleanup_from_fs(); + } + #define VERIFY_AND_READ_BLOCK(expr) do { const uint32_t block_index = expr; ASSERT(block_index); m_fs.read_block(block_index, block_buffer); } while (false) #define VERIFY_AND_RETURN(expr) ({ const uint32_t result = expr; ASSERT(result); return result; }) @@ -252,6 +258,21 @@ namespace Kernel return {}; } + void Ext2Inode::cleanup_from_fs() + { + if (mode().ifdir()) + { + // FIXME: cleanup entires '.' and '..' and verify + // there are no other entries + ASSERT_NOT_REACHED(); + } + + ASSERT(m_inode.links_count == 0); + for (uint32_t i = 0; i < blocks(); i++) + m_fs.release_block(fs_block_of_data_block_index(i)); + m_fs.delete_inode(ino()); + } + BAN::ErrorOr Ext2Inode::list_next_inodes_impl(off_t offset, DirectoryEntryList* list, size_t list_size) { ASSERT(mode().ifdir()); @@ -371,9 +392,16 @@ namespace Kernel const uint32_t new_ino = TRY(m_fs.create_inode(initialize_new_inode_info(mode, uid, gid))); - auto inode = MUST(Ext2Inode::create(m_fs, new_ino)); + auto inode_or_error = Ext2Inode::create(m_fs, new_ino); + if (inode_or_error.is_error()) + { + m_fs.delete_inode(new_ino); + return inode_or_error.release_error(); + } - MUST(link_inode_to_directory(*inode, name)); + auto inode = inode_or_error.release_value(); + + TRY(link_inode_to_directory(*inode, name)); return {}; } @@ -386,12 +414,19 @@ namespace Kernel ASSERT(Mode(mode).ifdir()); const uint32_t new_ino = TRY(m_fs.create_inode(initialize_new_inode_info(mode, uid, gid))); - - auto inode = MUST(Ext2Inode::create(m_fs, new_ino)); - MUST(inode->link_inode_to_directory(*inode, "."sv)); - MUST(inode->link_inode_to_directory(*this, ".."sv)); + + auto inode_or_error = Ext2Inode::create(m_fs, new_ino); + if (inode_or_error.is_error()) + { + m_fs.delete_inode(new_ino); + return inode_or_error.release_error(); + } + + auto inode = inode_or_error.release_value(); + TRY(inode->link_inode_to_directory(*inode, "."sv)); + TRY(inode->link_inode_to_directory(*this, ".."sv)); - MUST(link_inode_to_directory(*inode, name)); + TRY(link_inode_to_directory(*inode, name)); return {}; } @@ -496,6 +531,42 @@ needs_new_block: return {}; } + BAN::ErrorOr Ext2Inode::unlink_impl(BAN::StringView name) + { + ASSERT(mode().ifdir()); + + auto block_buffer = m_fs.get_block_buffer(); + + for (uint32_t i = 0; i < blocks(); i++) + { + const uint32_t block = fs_block_of_data_block_index(i); + m_fs.read_block(block, block_buffer); + + blksize_t offset = 0; + while (offset < blksize()) + { + auto& entry = block_buffer.span().slice(offset).as(); + if (entry.inode && name == entry.name) + { + auto inode = TRY(Ext2Inode::create(m_fs, entry.inode)); + if (inode->nlink() == 0) + dprintln("Corrupted filesystem. Deleting inode with 0 links"); + else + inode->m_inode.links_count--; + + TRY(sync()); + + // FIXME: This should expand the last inode if exists + entry.inode = 0; + m_fs.write_block(block, block_buffer); + } + offset += entry.rec_len; + } + } + + return {}; + } + #define READ_OR_ALLOCATE_BASE_BLOCK(index_) \ do { \ if (m_inode.block[index_] != 0) \ diff --git a/kernel/kernel/FS/Inode.cpp b/kernel/kernel/FS/Inode.cpp index 5712e9801e..535a9d354e 100644 --- a/kernel/kernel/FS/Inode.cpp +++ b/kernel/kernel/FS/Inode.cpp @@ -97,13 +97,13 @@ namespace Kernel return create_directory_impl(name, mode, uid, gid); } - BAN::ErrorOr Inode::delete_inode(BAN::StringView name) + BAN::ErrorOr Inode::unlink(BAN::StringView name) { LockGuard _(m_lock); Thread::TerminateBlocker blocker(Thread::current()); if (!mode().ifdir()) return BAN::Error::from_errno(ENOTDIR); - return delete_inode_impl(name); + return unlink_impl(name); } BAN::ErrorOr Inode::link_target() diff --git a/kernel/kernel/FS/ProcFS/FileSystem.cpp b/kernel/kernel/FS/ProcFS/FileSystem.cpp index 1c8078573c..451862f7e6 100644 --- a/kernel/kernel/FS/ProcFS/FileSystem.cpp +++ b/kernel/kernel/FS/ProcFS/FileSystem.cpp @@ -40,7 +40,7 @@ namespace Kernel void ProcFileSystem::on_process_delete(Process& process) { auto path = BAN::String::formatted("{}", process.pid()); - MUST(m_root_inode->delete_inode(path)); + MUST(m_root_inode->unlink(path)); } } diff --git a/kernel/kernel/FS/RamFS/Inode.cpp b/kernel/kernel/FS/RamFS/Inode.cpp index c1e034b8eb..52ce177356 100644 --- a/kernel/kernel/FS/RamFS/Inode.cpp +++ b/kernel/kernel/FS/RamFS/Inode.cpp @@ -263,8 +263,9 @@ namespace Kernel return {}; } - BAN::ErrorOr RamDirectoryInode::delete_inode_impl(BAN::StringView name) + BAN::ErrorOr RamDirectoryInode::unlink_impl(BAN::StringView name) { + // FIXME: delete inodes contents only after they are closed for (size_t i = 0; i < m_entries.size(); i++) { if (name == m_entries[i].name)