From d4ea720239bc9dbf964c81dad2c6e260240fe180 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Tue, 17 Sep 2024 15:54:33 +0300 Subject: [PATCH] Kernel: Don't crash the kernel if ext2 encounters disk error This will most likely result in a corrupted filesystem, but crashing the kernel is too much :D --- kernel/include/kernel/FS/Ext2/FileSystem.h | 12 +- kernel/include/kernel/FS/Ext2/Inode.h | 10 +- kernel/kernel/FS/Ext2/FileSystem.cpp | 95 +++++---- kernel/kernel/FS/Ext2/Inode.cpp | 223 ++++++++++++--------- 4 files changed, 188 insertions(+), 152 deletions(-) diff --git a/kernel/include/kernel/FS/Ext2/FileSystem.h b/kernel/include/kernel/FS/Ext2/FileSystem.h index cf0bbc27..f7606f25 100644 --- a/kernel/include/kernel/FS/Ext2/FileSystem.h +++ b/kernel/include/kernel/FS/Ext2/FileSystem.h @@ -61,17 +61,17 @@ namespace Kernel BAN::ErrorOr initialize_root_inode(); BAN::ErrorOr create_inode(const Ext2::Inode&); - void delete_inode(uint32_t ino); + BAN::ErrorOr delete_inode(uint32_t ino); BAN::ErrorOr resize_inode(uint32_t, size_t); - void read_block(uint32_t, BlockBufferWrapper&); - void write_block(uint32_t, const BlockBufferWrapper&); - void sync_superblock(); + BAN::ErrorOr read_block(uint32_t, BlockBufferWrapper&); + BAN::ErrorOr write_block(uint32_t, const BlockBufferWrapper&); + BAN::ErrorOr sync_superblock(); BlockBufferWrapper get_block_buffer(); BAN::ErrorOr reserve_free_block(uint32_t primary_bgd); - void release_block(uint32_t block); + BAN::ErrorOr release_block(uint32_t block); BAN::HashMap>& inode_cache() { return m_inode_cache; } @@ -82,7 +82,7 @@ namespace Kernel uint32_t block; uint32_t offset; }; - BlockLocation locate_inode(uint32_t); + BAN::ErrorOr locate_inode(uint32_t); BlockLocation locate_block_group_descriptior(uint32_t); uint32_t block_size() const { return 1024 << superblock().log_block_size; } diff --git a/kernel/include/kernel/FS/Ext2/Inode.h b/kernel/include/kernel/FS/Ext2/Inode.h index 8d8c3db8..78130a95 100644 --- a/kernel/include/kernel/FS/Ext2/Inode.h +++ b/kernel/include/kernel/FS/Ext2/Inode.h @@ -52,19 +52,19 @@ namespace Kernel // NOTE: the inode might have more blocks than what this suggests if it has been shrinked uint32_t max_used_data_block_count() const { return size() / blksize(); } - BAN::Optional block_from_indirect_block(uint32_t block, uint32_t index, uint32_t depth); - BAN::Optional fs_block_of_data_block_index(uint32_t data_block_index); + BAN::ErrorOr> block_from_indirect_block(uint32_t block, uint32_t index, uint32_t depth); + BAN::ErrorOr> fs_block_of_data_block_index(uint32_t data_block_index); BAN::ErrorOr link_inode_to_directory(Ext2Inode&, BAN::StringView name); BAN::ErrorOr is_directory_empty(); - void cleanup_indirect_block(uint32_t block, uint32_t depth); + BAN::ErrorOr cleanup_indirect_block(uint32_t block, uint32_t depth); BAN::ErrorOr cleanup_default_links(); - void cleanup_from_fs(); + BAN::ErrorOr cleanup_from_fs(); BAN::ErrorOr allocate_new_block_to_indirect_block(uint32_t& block, uint32_t index, uint32_t depth); BAN::ErrorOr allocate_new_block(uint32_t data_block_index); - void sync(); + BAN::ErrorOr sync(); uint32_t block_group() const; diff --git a/kernel/kernel/FS/Ext2/FileSystem.cpp b/kernel/kernel/FS/Ext2/FileSystem.cpp index c19d21e0..9c0324cc 100644 --- a/kernel/kernel/FS/Ext2/FileSystem.cpp +++ b/kernel/kernel/FS/Ext2/FileSystem.cpp @@ -63,7 +63,7 @@ namespace Kernel if (m_superblock.rev_level == Ext2::Enum::GOOD_OLD_REV) { - memset(m_superblock.__extension_start, 0, sizeof(Ext2::Superblock) - offsetof(Ext2::Superblock, Ext2::Superblock::__extension_start)); + memset(m_superblock.__extension_start, 0, sizeof(Ext2::Superblock) - offsetof(Ext2::Superblock, __extension_start)); m_superblock.first_ino = Ext2::Enum::GOOD_OLD_FIRST_INO; m_superblock.inode_size = Ext2::Enum::GOOD_OLD_INODE_SIZE; } @@ -140,7 +140,7 @@ namespace Kernel for (uint32_t bg : m_superblock_backups) { - read_block(superblock().first_data_block + superblock().blocks_per_group * bg, block_buffer); + TRY(read_block(superblock().first_data_block + superblock().blocks_per_group * bg, block_buffer)); Ext2::Superblock& superblock_backup = *(Ext2::Superblock*)block_buffer.data(); if (superblock_backup.magic != Ext2::Enum::SUPER_MAGIC) derrorln("superblock backup at block {} is invalid ({4H})", bg, superblock_backup.magic); @@ -184,7 +184,7 @@ namespace Kernel current_group = ino_group; bgd_location = locate_block_group_descriptior(current_group); - read_block(bgd_location.block, bgd_buffer); + TRY(read_block(bgd_location.block, bgd_buffer)); bgd = (Ext2::BlockGroupDescriptor*)(bgd_buffer.data() + bgd_location.offset); if (bgd->free_inodes_count == 0) @@ -193,7 +193,7 @@ namespace Kernel continue; } - read_block(bgd->inode_bitmap, inode_bitmap); + TRY(read_block(bgd->inode_bitmap, inode_bitmap)); } const uint32_t ino_bitmap_byte = ino_index / 8; @@ -202,16 +202,15 @@ namespace Kernel continue; inode_bitmap[ino_bitmap_byte] |= 1 << ino_bitmap_bit; - write_block(bgd->inode_bitmap, inode_bitmap); + TRY(write_block(bgd->inode_bitmap, inode_bitmap)); bgd->free_inodes_count--; if (Inode::Mode(ext2_inode.mode).ifdir()) bgd->used_dirs_count++; - write_block(bgd_location.block, bgd_buffer); + TRY(write_block(bgd_location.block, bgd_buffer)); const uint32_t inode_table_offset = ino_index * superblock().inode_size; - const BlockLocation inode_location - { + const BlockLocation inode_location { .block = inode_table_offset / block_size + bgd->inode_table, .offset = inode_table_offset % block_size }; @@ -219,14 +218,14 @@ namespace Kernel // NOTE: we don't need inode bitmap anymore, so we can reuse it auto& inode_buffer = inode_bitmap; - read_block(inode_location.block, inode_buffer); + TRY(read_block(inode_location.block, inode_buffer)); memcpy(inode_buffer.data() + inode_location.offset, &ext2_inode, sizeof(Ext2::Inode)); if (superblock().inode_size > sizeof(Ext2::Inode)) memset(inode_buffer.data() + inode_location.offset + sizeof(Ext2::Inode), 0, superblock().inode_size - sizeof(Ext2::Inode)); - write_block(inode_location.block, inode_buffer); + TRY(write_block(inode_location.block, inode_buffer)); m_superblock.free_inodes_count--; - sync_superblock(); + TRY(sync_superblock()); return ino; } @@ -235,7 +234,7 @@ namespace Kernel return BAN::Error::from_error_code(ErrorCode::Ext2_Corrupted); } - void Ext2FS::delete_inode(uint32_t ino) + BAN::ErrorOr Ext2FS::delete_inode(uint32_t ino) { LockGuard _(m_mutex); @@ -250,20 +249,20 @@ namespace Kernel 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); + TRY(read_block(bgd_location.block, bgd_buffer)); auto& bgd = bgd_buffer.span().slice(bgd_location.offset).as(); // update inode bitmap - read_block(bgd.inode_bitmap, bitmap_buffer); + TRY(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); + TRY(write_block(bgd.inode_bitmap, bitmap_buffer)); // memset inode to zero or fsck will complain - auto inode_location = locate_inode(ino); - read_block(inode_location.block, inode_buffer); + auto inode_location = TRY(locate_inode(ino)); + TRY(read_block(inode_location.block, inode_buffer)); auto& inode = inode_buffer.span().slice(inode_location.offset).as(); #if EXT2_VERIFY_NO_BLOCKS static const char zero_buffer[sizeof(inode.block)] {}; @@ -271,24 +270,26 @@ namespace Kernel #endif bool is_directory = Inode::Mode(inode.mode).ifdir(); memset(&inode, 0x00, m_superblock.inode_size); - write_block(inode_location.block, inode_buffer); + TRY(write_block(inode_location.block, inode_buffer)); // update bgd counts bgd.free_inodes_count++; if (is_directory) bgd.used_dirs_count--; - write_block(bgd_location.block, bgd_buffer); + TRY(write_block(bgd_location.block, bgd_buffer)); // update superblock inode count m_superblock.free_inodes_count++; - sync_superblock(); + TRY(sync_superblock()); // remove inode from cache if (m_inode_cache.contains(ino)) m_inode_cache.remove(ino); + + return {}; } - void Ext2FS::read_block(uint32_t block, BlockBufferWrapper& buffer) + BAN::ErrorOr Ext2FS::read_block(uint32_t block, BlockBufferWrapper& buffer) { LockGuard _(m_mutex); @@ -298,10 +299,12 @@ namespace Kernel ASSERT(block >= superblock().first_data_block + 1); ASSERT(buffer.size() >= block_size); - MUST(m_block_device->read_blocks(block * sectors_per_block, sectors_per_block, buffer.span())); + TRY(m_block_device->read_blocks(block * sectors_per_block, sectors_per_block, buffer.span())); + + return {}; } - void Ext2FS::write_block(uint32_t block, const BlockBufferWrapper& buffer) + BAN::ErrorOr Ext2FS::write_block(uint32_t block, const BlockBufferWrapper& buffer) { LockGuard _(m_mutex); @@ -311,10 +314,12 @@ namespace Kernel ASSERT(block >= superblock().first_data_block + 1); ASSERT(buffer.size() >= block_size); - MUST(m_block_device->write_blocks(block * sectors_per_block, sectors_per_block, buffer.span())); + TRY(m_block_device->write_blocks(block * sectors_per_block, sectors_per_block, buffer.span())); + + return {}; } - void Ext2FS::sync_superblock() + BAN::ErrorOr Ext2FS::sync_superblock() { LockGuard _(m_mutex); @@ -331,12 +336,14 @@ namespace Kernel auto superblock_buffer = get_block_buffer(); - MUST(m_block_device->read_blocks(lba, sector_count, superblock_buffer.span())); + TRY(m_block_device->read_blocks(lba, sector_count, superblock_buffer.span())); if (memcmp(superblock_buffer.data(), &m_superblock, superblock_bytes)) { memcpy(superblock_buffer.data(), &m_superblock, superblock_bytes); - MUST(m_block_device->write_blocks(lba, sector_count, superblock_buffer.span())); + TRY(m_block_device->write_blocks(lba, sector_count, superblock_buffer.span())); } + + return {}; } Ext2FS::BlockBufferWrapper Ext2FS::get_block_buffer() @@ -356,16 +363,16 @@ namespace Kernel auto block_bitmap = m_buffer_manager.get_buffer(); auto check_block_group = - [&](uint32_t block_group) -> uint32_t + [&](uint32_t block_group) -> BAN::ErrorOr { auto bgd_location = locate_block_group_descriptior(block_group); - read_block(bgd_location.block, bgd_buffer); + TRY(read_block(bgd_location.block, bgd_buffer)); auto& bgd = *(Ext2::BlockGroupDescriptor*)(bgd_buffer.data() + bgd_location.offset); if (bgd.free_blocks_count == 0) return 0; - read_block(bgd.block_bitmap, block_bitmap); + TRY(read_block(bgd.block_bitmap, block_bitmap)); for (uint32_t block_offset = 0; block_offset < m_superblock.blocks_per_group; block_offset++) { const uint32_t fs_block_index = m_superblock.first_data_block + m_superblock.blocks_per_group * block_group + block_offset; @@ -378,13 +385,13 @@ namespace Kernel continue; block_bitmap[byte] |= 1 << bit; - write_block(bgd.block_bitmap, block_bitmap); + TRY(write_block(bgd.block_bitmap, block_bitmap)); bgd.free_blocks_count--; - write_block(bgd_location.block, bgd_buffer); + TRY(write_block(bgd_location.block, bgd_buffer)); m_superblock.free_blocks_count--; - sync_superblock(); + TRY(sync_superblock()); return fs_block_index; } @@ -393,20 +400,20 @@ namespace Kernel return 0; }; - if (auto ret = check_block_group(primary_bgd)) + if (auto ret = TRY(check_block_group(primary_bgd))) return ret; uint32_t number_of_block_groups = BAN::Math::div_round_up(m_superblock.blocks_count, m_superblock.blocks_per_group); for (uint32_t block_group = 0; block_group < number_of_block_groups; block_group++) if (block_group != primary_bgd) - if (auto ret = check_block_group(block_group)) + if (auto ret = TRY(check_block_group(block_group))) return ret; derrorln("Corrupted file system. Superblock indicates free blocks but none were found."); return BAN::Error::from_error_code(ErrorCode::Ext2_Corrupted); } - void Ext2FS::release_block(uint32_t block) + BAN::ErrorOr Ext2FS::release_block(uint32_t block) { LockGuard _(m_mutex); @@ -420,26 +427,28 @@ namespace Kernel auto bitmap_buffer = get_block_buffer(); auto bgd_location = locate_block_group_descriptior(block_group); - read_block(bgd_location.block, bgd_buffer); + TRY(read_block(bgd_location.block, bgd_buffer)); auto& bgd = bgd_buffer.span().slice(bgd_location.offset).as(); - read_block(bgd.block_bitmap, bitmap_buffer); + TRY(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); + TRY(write_block(bgd.block_bitmap, bitmap_buffer)); bgd.free_blocks_count++; - write_block(bgd_location.block, bgd_buffer); + TRY(write_block(bgd_location.block, bgd_buffer)); m_superblock.free_blocks_count++; - sync_superblock(); + TRY(sync_superblock()); + + return {}; } - Ext2FS::BlockLocation Ext2FS::locate_inode(uint32_t ino) + BAN::ErrorOr Ext2FS::locate_inode(uint32_t ino) { LockGuard _(m_mutex); @@ -454,7 +463,7 @@ namespace Kernel auto bgd_location = locate_block_group_descriptior(inode_group); - read_block(bgd_location.block, bgd_buffer); + TRY(read_block(bgd_location.block, bgd_buffer)); auto& bgd = *(Ext2::BlockGroupDescriptor*)(bgd_buffer.data() + bgd_location.offset); diff --git a/kernel/kernel/FS/Ext2/Inode.cpp b/kernel/kernel/FS/Ext2/Inode.cpp index d8d49923..3e9b16cf 100644 --- a/kernel/kernel/FS/Ext2/Inode.cpp +++ b/kernel/kernel/FS/Ext2/Inode.cpp @@ -27,10 +27,10 @@ namespace Kernel if (fs.inode_cache().contains(inode_ino)) return fs.inode_cache()[inode_ino]; - auto inode_location = fs.locate_inode(inode_ino); + auto inode_location = TRY(fs.locate_inode(inode_ino)); auto block_buffer = fs.get_block_buffer(); - fs.read_block(inode_location.block, block_buffer); + TRY(fs.read_block(inode_location.block, block_buffer)); auto& inode = block_buffer.span().slice(inode_location.offset).as(); @@ -41,18 +41,20 @@ namespace Kernel Ext2Inode::~Ext2Inode() { - if (m_inode.links_count == 0) - cleanup_from_fs(); + if (m_inode.links_count > 0) + return; + if (auto ret = cleanup_from_fs(); ret.is_error()) + dwarnln("Could not cleanup inode from FS: {}", ret.error()); } - BAN::Optional Ext2Inode::block_from_indirect_block(uint32_t block, uint32_t index, uint32_t depth) + BAN::ErrorOr> Ext2Inode::block_from_indirect_block(uint32_t block, uint32_t index, uint32_t depth) { if (block == 0) - return {}; + return BAN::Optional(); ASSERT(depth >= 1); auto block_buffer = m_fs.get_block_buffer(); - m_fs.read_block(block, block_buffer); + TRY(m_fs.read_block(block, block_buffer)); const uint32_t indices_per_block = blksize() / sizeof(uint32_t); @@ -60,22 +62,22 @@ namespace Kernel const uint32_t next_block = block_buffer.span().as_span()[(index / divisor) % indices_per_block]; if (next_block == 0) - return {}; + return BAN::Optional(); if (depth == 1) - return next_block; + return BAN::Optional(next_block); return block_from_indirect_block(next_block, index, depth - 1); } - BAN::Optional Ext2Inode::fs_block_of_data_block_index(uint32_t data_block_index) + BAN::ErrorOr> Ext2Inode::fs_block_of_data_block_index(uint32_t data_block_index) { const uint32_t indices_per_block = blksize() / sizeof(uint32_t); if (data_block_index < 12) { if (m_inode.block[data_block_index] == 0) - return {}; - return m_inode.block[data_block_index]; + return BAN::Optional(); + return BAN::Optional(m_inode.block[data_block_index]); } data_block_index -= 12; @@ -129,9 +131,9 @@ namespace Kernel for (uint32_t data_block_index = first_block; data_block_index < last_block; data_block_index++) { - auto block_index = fs_block_of_data_block_index(data_block_index); + auto block_index = TRY(fs_block_of_data_block_index(data_block_index)); if (block_index.has_value()) - m_fs.read_block(block_index.value(), block_buffer); + TRY(m_fs.read_block(block_index.value(), block_buffer)); else memset(block_buffer.data(), 0x00, block_buffer.size()); @@ -168,9 +170,9 @@ namespace Kernel // Write partial block if (offset % block_size) { - auto block_index = fs_block_of_data_block_index(offset / block_size); + auto block_index = TRY(fs_block_of_data_block_index(offset / block_size)); if (block_index.has_value()) - m_fs.read_block(block_index.value(), block_buffer); + TRY(m_fs.read_block(block_index.value(), block_buffer)); else { block_index = TRY(allocate_new_block(offset / block_size));; @@ -181,7 +183,7 @@ namespace Kernel uint32_t to_copy = BAN::Math::min(block_size - block_offset, to_write); memcpy(block_buffer.data() + block_offset, buffer.data(), to_copy); - m_fs.write_block(block_index.value(), block_buffer); + TRY(m_fs.write_block(block_index.value(), block_buffer)); written += to_copy; offset += to_copy; @@ -190,12 +192,12 @@ namespace Kernel while (to_write >= block_size) { - auto block_index = fs_block_of_data_block_index(offset / block_size); + auto block_index = TRY(fs_block_of_data_block_index(offset / block_size)); if (!block_index.has_value()) block_index = TRY(allocate_new_block(offset / block_size)); memcpy(block_buffer.data(), buffer.data() + written, block_buffer.size()); - m_fs.write_block(block_index.value(), block_buffer); + TRY(m_fs.write_block(block_index.value(), block_buffer)); written += block_size; offset += block_size; @@ -204,9 +206,9 @@ namespace Kernel if (to_write > 0) { - auto block_index = fs_block_of_data_block_index(offset / block_size); + auto block_index = TRY(fs_block_of_data_block_index(offset / block_size)); if (block_index.has_value()) - m_fs.read_block(block_index.value(), block_buffer); + TRY(m_fs.read_block(block_index.value(), block_buffer)); else { block_index = TRY(allocate_new_block(offset / block_size)); @@ -214,7 +216,7 @@ namespace Kernel } memcpy(block_buffer.data(), buffer.data() + written, to_write); - m_fs.write_block(block_index.value(), block_buffer); + TRY(m_fs.write_block(block_index.value(), block_buffer)); } return buffer.size(); @@ -227,8 +229,14 @@ namespace Kernel // TODO: we should remove unused blocks on shrink + const auto old_size = m_inode.size; + m_inode.size = new_size; - sync(); + if (auto ret = sync(); ret.is_error()) + { + m_inode.size = old_size; + return ret.release_error(); + } return {}; } @@ -238,23 +246,31 @@ namespace Kernel ASSERT((mode & Inode::Mode::TYPE_MASK) == 0); if (m_inode.mode == mode) return {}; + + const auto old_mode = m_inode.mode; + m_inode.mode = (m_inode.mode & Inode::Mode::TYPE_MASK) | mode; - sync(); + if (auto ret = sync(); ret.is_error()) + { + m_inode.mode = old_mode; + return ret.release_error(); + } + return {}; } - void Ext2Inode::cleanup_indirect_block(uint32_t block, uint32_t depth) + BAN::ErrorOr Ext2Inode::cleanup_indirect_block(uint32_t block, uint32_t depth) { ASSERT(block); if (depth == 0) { - m_fs.release_block(block); - return; + TRY(m_fs.release_block(block)); + return {}; } auto block_buffer = m_fs.get_block_buffer(); - m_fs.read_block(block, block_buffer); + TRY(m_fs.read_block(block, block_buffer)); const uint32_t ids_per_block = blksize() / sizeof(uint32_t); for (uint32_t i = 0; i < ids_per_block; i++) @@ -262,13 +278,14 @@ namespace Kernel const uint32_t next_block = block_buffer.span().as_span()[i]; if (next_block == 0) continue; - cleanup_indirect_block(next_block, depth - 1); + TRY(cleanup_indirect_block(next_block, depth - 1)); } - m_fs.release_block(block); + TRY(m_fs.release_block(block)); + return {}; } - void Ext2Inode::cleanup_from_fs() + BAN::ErrorOr Ext2Inode::cleanup_from_fs() { ASSERT(m_inode.links_count == 0); @@ -278,15 +295,15 @@ namespace Kernel // cleanup direct blocks for (uint32_t i = 0; i < 12; i++) if (m_inode.block[i]) - m_fs.release_block(m_inode.block[i]); + TRY(m_fs.release_block(m_inode.block[i])); // cleanup indirect blocks if (m_inode.block[12]) - cleanup_indirect_block(m_inode.block[12], 1); + TRY(cleanup_indirect_block(m_inode.block[12], 1)); if (m_inode.block[13]) - cleanup_indirect_block(m_inode.block[13], 2); + TRY(cleanup_indirect_block(m_inode.block[13], 2)); if (m_inode.block[14]) - cleanup_indirect_block(m_inode.block[14], 3); + TRY(cleanup_indirect_block(m_inode.block[14], 3)); done: // mark blocks as deleted @@ -294,9 +311,11 @@ done: // FIXME: this is only required since fs does not get // deleting inode from its cache - sync(); + TRY(sync()); - m_fs.delete_inode(ino()); + TRY(m_fs.delete_inode(ino())); + + return {}; } BAN::ErrorOr Ext2Inode::list_next_inodes_impl(off_t offset, struct dirent* list, size_t list_size) @@ -308,11 +327,11 @@ done: return 0; // FIXME: can we actually assume directories have all their blocks allocated - const uint32_t block_index = fs_block_of_data_block_index(offset).value(); + const uint32_t block_index = TRY(fs_block_of_data_block_index(offset)).value(); auto block_buffer = m_fs.get_block_buffer(); - m_fs.read_block(block_index, block_buffer); + TRY(m_fs.read_block(block_index, block_buffer)); // First determine if we have big enough list size_t entry_count = 0; @@ -413,7 +432,7 @@ done: auto inode_or_error = Ext2Inode::create(m_fs, new_ino); if (inode_or_error.is_error()) { - m_fs.delete_inode(new_ino); + TRY(m_fs.delete_inode(new_ino)); return inode_or_error.release_error(); } @@ -434,19 +453,21 @@ done: auto inode_or_error = Ext2Inode::create(m_fs, new_ino); if (inode_or_error.is_error()) { - m_fs.delete_inode(new_ino); + TRY(m_fs.delete_inode(new_ino)); return inode_or_error.release_error(); } auto inode = inode_or_error.release_value(); - BAN::ScopeGuard cleanup([&] { inode->cleanup_from_fs(); }); - TRY(inode->link_inode_to_directory(*inode, "."_sv)); - TRY(inode->link_inode_to_directory(*this, ".."_sv)); + // link . and .. + if (auto ret = inode->link_inode_to_directory(*inode, "."_sv); ret.is_error()) + return ({ TRY(inode->cleanup_from_fs()); ret.release_error(); }); + if (auto ret = inode->link_inode_to_directory(*this, ".."_sv); ret.is_error()) + return ({ TRY(inode->cleanup_from_fs()); ret.release_error(); }); - TRY(link_inode_to_directory(*inode, name)); - - cleanup.disable(); + // link to parent + if (auto ret = link_inode_to_directory(*inode, name); ret.is_error()) + return ({ TRY(inode->cleanup_from_fs()); ret.release_error(); }); return {}; } @@ -475,29 +496,32 @@ done: auto block_buffer = m_fs.get_block_buffer(); - auto write_inode = [&](uint32_t entry_offset, uint32_t entry_rec_len) - { - auto typed_mode = inode.mode(); - uint8_t file_type = (m_fs.superblock().rev_level == Ext2::Enum::GOOD_OLD_REV) ? 0 - : typed_mode.ifreg() ? Ext2::Enum::REG_FILE - : typed_mode.ifdir() ? Ext2::Enum::DIR - : typed_mode.ifchr() ? Ext2::Enum::CHRDEV - : typed_mode.ifblk() ? Ext2::Enum::BLKDEV - : typed_mode.ififo() ? Ext2::Enum::FIFO - : typed_mode.ifsock() ? Ext2::Enum::SOCK - : typed_mode.iflnk() ? Ext2::Enum::SYMLINK - : 0; + auto write_inode = + [&](uint32_t entry_offset, uint32_t entry_rec_len) -> BAN::ErrorOr + { + auto typed_mode = inode.mode(); + uint8_t file_type = (m_fs.superblock().rev_level == Ext2::Enum::GOOD_OLD_REV) ? 0 + : typed_mode.ifreg() ? Ext2::Enum::REG_FILE + : typed_mode.ifdir() ? Ext2::Enum::DIR + : typed_mode.ifchr() ? Ext2::Enum::CHRDEV + : typed_mode.ifblk() ? Ext2::Enum::BLKDEV + : typed_mode.ififo() ? Ext2::Enum::FIFO + : typed_mode.ifsock() ? Ext2::Enum::SOCK + : typed_mode.iflnk() ? Ext2::Enum::SYMLINK + : 0; - auto& new_entry = block_buffer.span().slice(entry_offset).as(); - new_entry.inode = inode.ino(); - new_entry.rec_len = entry_rec_len; - new_entry.name_len = name.size(); - new_entry.file_type = file_type; - memcpy(new_entry.name, name.data(), name.size()); + auto& new_entry = block_buffer.span().slice(entry_offset).as(); + new_entry.inode = inode.ino(); + new_entry.rec_len = entry_rec_len; + new_entry.name_len = name.size(); + new_entry.file_type = file_type; + memcpy(new_entry.name, name.data(), name.size()); - inode.m_inode.links_count++; - inode.sync(); - }; + inode.m_inode.links_count++; + TRY(inode.sync()); + + return {}; + }; uint32_t block_index = 0; uint32_t entry_offset = 0; @@ -512,8 +536,8 @@ done: goto needs_new_block; // Try to insert inode to last data block - block_index = fs_block_of_data_block_index(data_block_count - 1).value(); - m_fs.read_block(block_index, block_buffer); + block_index = TRY(fs_block_of_data_block_index(data_block_count - 1)).value(); + TRY(m_fs.read_block(block_index, block_buffer)); while (entry_offset < block_size) { @@ -525,8 +549,8 @@ done: if (entry.inode == 0 && needed_entry_len <= entry.rec_len) { - write_inode(entry_offset, entry.rec_len); - m_fs.write_block(block_index, block_buffer); + TRY(write_inode(entry_offset, entry.rec_len)); + TRY(m_fs.write_block(block_index, block_buffer)); return {}; } else if (needed_entry_len <= entry.rec_len - entry_min_rec_len) @@ -534,8 +558,8 @@ done: uint32_t new_rec_len = entry.rec_len - entry_min_rec_len; entry.rec_len = entry_min_rec_len; - write_inode(entry_offset + entry.rec_len, new_rec_len); - m_fs.write_block(block_index, block_buffer); + TRY(write_inode(entry_offset + entry.rec_len, new_rec_len)); + TRY(m_fs.write_block(block_index, block_buffer)); return {}; } @@ -547,8 +571,8 @@ needs_new_block: m_inode.size += blksize(); memset(block_buffer.data(), 0x00, block_buffer.size()); - write_inode(0, block_size); - m_fs.write_block(block_index, block_buffer); + TRY(write_inode(0, block_size)); + TRY(m_fs.write_block(block_index, block_buffer)); return {}; } @@ -563,8 +587,8 @@ needs_new_block: for (uint32_t i = 0; i < max_used_data_block_count(); i++) { // FIXME: can we actually assume directories have all their blocks allocated - const uint32_t block_index = fs_block_of_data_block_index(i).value(); - m_fs.read_block(block_index, block_buffer); + const uint32_t block_index = TRY(fs_block_of_data_block_index(i)).value(); + TRY(m_fs.read_block(block_index, block_buffer)); blksize_t offset = 0; while (offset < blksize()) @@ -599,8 +623,8 @@ needs_new_block: for (uint32_t i = 0; i < max_used_data_block_count(); i++) { // FIXME: can we actually assume directories have all their blocks allocated - const uint32_t block_index = fs_block_of_data_block_index(i).value(); - m_fs.read_block(block_index, block_buffer); + const uint32_t block_index = TRY(fs_block_of_data_block_index(i)).value(); + TRY(m_fs.read_block(block_index, block_buffer)); bool modified = false; @@ -616,13 +640,13 @@ needs_new_block: if (entry_name == "."_sv) { m_inode.links_count--; - sync(); + TRY(sync()); } else if (entry_name == ".."_sv) { auto parent = TRY(Ext2Inode::create(m_fs, entry.inode)); parent->m_inode.links_count--; - parent->sync(); + TRY(parent->sync()); } else ASSERT_NOT_REACHED(); @@ -635,7 +659,7 @@ needs_new_block: } if (modified) - m_fs.write_block(block_index, block_buffer); + TRY(m_fs.write_block(block_index, block_buffer)); } return {}; @@ -655,8 +679,8 @@ needs_new_block: for (uint32_t i = 0; i < max_used_data_block_count(); i++) { // FIXME: can we actually assume directories have all their blocks allocated - const uint32_t block_index = fs_block_of_data_block_index(i).value(); - m_fs.read_block(block_index, block_buffer); + const uint32_t block_index = TRY(fs_block_of_data_block_index(i)).value(); + TRY(m_fs.read_block(block_index, block_buffer)); blksize_t offset = 0; while (offset < blksize()) @@ -677,7 +701,7 @@ needs_new_block: else inode->m_inode.links_count--; - sync(); + TRY(sync()); // NOTE: If this was the last link to inode we must // remove it from inode cache to trigger cleanup @@ -690,7 +714,7 @@ needs_new_block: // FIXME: This should expand the last inode if exists entry.inode = 0; - m_fs.write_block(block_index, block_buffer); + TRY(m_fs.write_block(block_index, block_buffer)); } offset += entry.rec_len; } @@ -714,14 +738,14 @@ needs_new_block: auto block_buffer = m_fs.get_block_buffer(); memset(block_buffer.data(), 0x00, block_buffer.size()); - m_fs.write_block(block, block_buffer); + TRY(m_fs.write_block(block, block_buffer)); } if (depth == 0) return block; auto block_buffer = m_fs.get_block_buffer(); - m_fs.read_block(block, block_buffer); + TRY(m_fs.read_block(block, block_buffer)); uint32_t divisor = 1; for (uint32_t i = 1; i < depth; i++) @@ -730,7 +754,9 @@ needs_new_block: uint32_t& new_block = block_buffer.span().as_span()[(index / divisor) % indices_per_fs_block]; uint32_t allocated_block = TRY(allocate_new_block_to_indirect_block(new_block, index, depth - 1)); - m_fs.write_block(block, block_buffer); + TRY(m_fs.write_block(block, block_buffer)); + + TRY(sync()); return allocated_block; } @@ -740,13 +766,12 @@ needs_new_block: const uint32_t inode_blocks_per_fs_block = blksize() / 512; const uint32_t indices_per_fs_block = blksize() / sizeof(uint32_t); - BAN::ScopeGuard syncer([&] { sync(); }); - if (data_block_index < 12) { ASSERT(m_inode.block[data_block_index] == 0); m_inode.block[data_block_index] = TRY(m_fs.reserve_free_block(block_group())); m_inode.blocks += inode_blocks_per_fs_block; + TRY(sync()); return m_inode.block[data_block_index]; } data_block_index -= 12; @@ -765,17 +790,19 @@ needs_new_block: ASSERT_NOT_REACHED(); } - void Ext2Inode::sync() + BAN::ErrorOr Ext2Inode::sync() { - auto inode_location = m_fs.locate_inode(ino()); + auto inode_location = TRY(m_fs.locate_inode(ino())); auto block_buffer = m_fs.get_block_buffer(); - m_fs.read_block(inode_location.block, block_buffer); + TRY(m_fs.read_block(inode_location.block, block_buffer)); if (memcmp(block_buffer.data() + inode_location.offset, &m_inode, sizeof(Ext2::Inode))) { memcpy(block_buffer.data() + inode_location.offset, &m_inode, sizeof(Ext2::Inode)); - m_fs.write_block(inode_location.block, block_buffer); + TRY(m_fs.write_block(inode_location.block, block_buffer)); } + + return {}; } BAN::ErrorOr> Ext2Inode::find_inode_impl(BAN::StringView file_name) @@ -787,8 +814,8 @@ needs_new_block: for (uint32_t i = 0; i < max_used_data_block_count(); i++) { // FIXME: can we actually assume directories have all their blocks allocated - const uint32_t block_index = fs_block_of_data_block_index(i).value(); - m_fs.read_block(block_index, block_buffer); + const uint32_t block_index = TRY(fs_block_of_data_block_index(i)).value(); + TRY(m_fs.read_block(block_index, block_buffer)); BAN::ConstByteSpan entry_span = block_buffer.span(); while (entry_span.size() >= sizeof(Ext2::LinkedDirectoryEntry))