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
This commit is contained in:
Bananymous 2024-09-17 15:54:33 +03:00
parent 97ee370ffe
commit d4ea720239
4 changed files with 188 additions and 152 deletions

View File

@ -61,17 +61,17 @@ namespace Kernel
BAN::ErrorOr<void> initialize_root_inode(); BAN::ErrorOr<void> initialize_root_inode();
BAN::ErrorOr<uint32_t> create_inode(const Ext2::Inode&); BAN::ErrorOr<uint32_t> create_inode(const Ext2::Inode&);
void delete_inode(uint32_t ino); BAN::ErrorOr<void> delete_inode(uint32_t ino);
BAN::ErrorOr<void> resize_inode(uint32_t, size_t); BAN::ErrorOr<void> resize_inode(uint32_t, size_t);
void read_block(uint32_t, BlockBufferWrapper&); BAN::ErrorOr<void> read_block(uint32_t, BlockBufferWrapper&);
void write_block(uint32_t, const BlockBufferWrapper&); BAN::ErrorOr<void> write_block(uint32_t, const BlockBufferWrapper&);
void sync_superblock(); BAN::ErrorOr<void> sync_superblock();
BlockBufferWrapper get_block_buffer(); BlockBufferWrapper get_block_buffer();
BAN::ErrorOr<uint32_t> reserve_free_block(uint32_t primary_bgd); BAN::ErrorOr<uint32_t> reserve_free_block(uint32_t primary_bgd);
void release_block(uint32_t block); BAN::ErrorOr<void> release_block(uint32_t block);
BAN::HashMap<ino_t, BAN::RefPtr<Ext2Inode>>& inode_cache() { return m_inode_cache; } BAN::HashMap<ino_t, BAN::RefPtr<Ext2Inode>>& inode_cache() { return m_inode_cache; }
@ -82,7 +82,7 @@ namespace Kernel
uint32_t block; uint32_t block;
uint32_t offset; uint32_t offset;
}; };
BlockLocation locate_inode(uint32_t); BAN::ErrorOr<BlockLocation> locate_inode(uint32_t);
BlockLocation locate_block_group_descriptior(uint32_t); BlockLocation locate_block_group_descriptior(uint32_t);
uint32_t block_size() const { return 1024 << superblock().log_block_size; } uint32_t block_size() const { return 1024 << superblock().log_block_size; }

View File

@ -52,19 +52,19 @@ namespace Kernel
// NOTE: the inode might have more blocks than what this suggests if it has been shrinked // 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(); } uint32_t max_used_data_block_count() const { return size() / blksize(); }
BAN::Optional<uint32_t> block_from_indirect_block(uint32_t block, uint32_t index, uint32_t depth); BAN::ErrorOr<BAN::Optional<uint32_t>> block_from_indirect_block(uint32_t block, uint32_t index, uint32_t depth);
BAN::Optional<uint32_t> fs_block_of_data_block_index(uint32_t data_block_index); BAN::ErrorOr<BAN::Optional<uint32_t>> fs_block_of_data_block_index(uint32_t data_block_index);
BAN::ErrorOr<void> link_inode_to_directory(Ext2Inode&, BAN::StringView name); BAN::ErrorOr<void> link_inode_to_directory(Ext2Inode&, BAN::StringView name);
BAN::ErrorOr<bool> is_directory_empty(); BAN::ErrorOr<bool> is_directory_empty();
void cleanup_indirect_block(uint32_t block, uint32_t depth); BAN::ErrorOr<void> cleanup_indirect_block(uint32_t block, uint32_t depth);
BAN::ErrorOr<void> cleanup_default_links(); BAN::ErrorOr<void> cleanup_default_links();
void cleanup_from_fs(); BAN::ErrorOr<void> cleanup_from_fs();
BAN::ErrorOr<uint32_t> allocate_new_block_to_indirect_block(uint32_t& block, uint32_t index, uint32_t depth); BAN::ErrorOr<uint32_t> allocate_new_block_to_indirect_block(uint32_t& block, uint32_t index, uint32_t depth);
BAN::ErrorOr<uint32_t> allocate_new_block(uint32_t data_block_index); BAN::ErrorOr<uint32_t> allocate_new_block(uint32_t data_block_index);
void sync(); BAN::ErrorOr<void> sync();
uint32_t block_group() const; uint32_t block_group() const;

View File

@ -63,7 +63,7 @@ namespace Kernel
if (m_superblock.rev_level == Ext2::Enum::GOOD_OLD_REV) 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.first_ino = Ext2::Enum::GOOD_OLD_FIRST_INO;
m_superblock.inode_size = Ext2::Enum::GOOD_OLD_INODE_SIZE; m_superblock.inode_size = Ext2::Enum::GOOD_OLD_INODE_SIZE;
} }
@ -140,7 +140,7 @@ namespace Kernel
for (uint32_t bg : m_superblock_backups) 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(); Ext2::Superblock& superblock_backup = *(Ext2::Superblock*)block_buffer.data();
if (superblock_backup.magic != Ext2::Enum::SUPER_MAGIC) if (superblock_backup.magic != Ext2::Enum::SUPER_MAGIC)
derrorln("superblock backup at block {} is invalid ({4H})", bg, superblock_backup.magic); derrorln("superblock backup at block {} is invalid ({4H})", bg, superblock_backup.magic);
@ -184,7 +184,7 @@ namespace Kernel
current_group = ino_group; current_group = ino_group;
bgd_location = locate_block_group_descriptior(current_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); bgd = (Ext2::BlockGroupDescriptor*)(bgd_buffer.data() + bgd_location.offset);
if (bgd->free_inodes_count == 0) if (bgd->free_inodes_count == 0)
@ -193,7 +193,7 @@ namespace Kernel
continue; 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; const uint32_t ino_bitmap_byte = ino_index / 8;
@ -202,16 +202,15 @@ namespace Kernel
continue; continue;
inode_bitmap[ino_bitmap_byte] |= 1 << ino_bitmap_bit; 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--; bgd->free_inodes_count--;
if (Inode::Mode(ext2_inode.mode).ifdir()) if (Inode::Mode(ext2_inode.mode).ifdir())
bgd->used_dirs_count++; 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 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, .block = inode_table_offset / block_size + bgd->inode_table,
.offset = inode_table_offset % block_size .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 // NOTE: we don't need inode bitmap anymore, so we can reuse it
auto& inode_buffer = inode_bitmap; 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)); memcpy(inode_buffer.data() + inode_location.offset, &ext2_inode, sizeof(Ext2::Inode));
if (superblock().inode_size > 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)); 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--; m_superblock.free_inodes_count--;
sync_superblock(); TRY(sync_superblock());
return ino; return ino;
} }
@ -235,7 +234,7 @@ namespace Kernel
return BAN::Error::from_error_code(ErrorCode::Ext2_Corrupted); return BAN::Error::from_error_code(ErrorCode::Ext2_Corrupted);
} }
void Ext2FS::delete_inode(uint32_t ino) BAN::ErrorOr<void> Ext2FS::delete_inode(uint32_t ino)
{ {
LockGuard _(m_mutex); LockGuard _(m_mutex);
@ -250,20 +249,20 @@ namespace Kernel
const uint32_t inode_index = (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); 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<Ext2::BlockGroupDescriptor>(); auto& bgd = bgd_buffer.span().slice(bgd_location.offset).as<Ext2::BlockGroupDescriptor>();
// update inode bitmap // 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 byte = inode_index / 8;
const uint32_t bit = inode_index % 8; const uint32_t bit = inode_index % 8;
ASSERT(bitmap_buffer[byte] & (1 << bit)); ASSERT(bitmap_buffer[byte] & (1 << bit));
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 // memset inode to zero or fsck will complain
auto inode_location = locate_inode(ino); auto inode_location = TRY(locate_inode(ino));
read_block(inode_location.block, inode_buffer); TRY(read_block(inode_location.block, inode_buffer));
auto& inode = inode_buffer.span().slice(inode_location.offset).as<Ext2::Inode>(); auto& inode = inode_buffer.span().slice(inode_location.offset).as<Ext2::Inode>();
#if EXT2_VERIFY_NO_BLOCKS #if EXT2_VERIFY_NO_BLOCKS
static const char zero_buffer[sizeof(inode.block)] {}; static const char zero_buffer[sizeof(inode.block)] {};
@ -271,24 +270,26 @@ namespace Kernel
#endif #endif
bool is_directory = Inode::Mode(inode.mode).ifdir(); bool is_directory = Inode::Mode(inode.mode).ifdir();
memset(&inode, 0x00, m_superblock.inode_size); 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 // update bgd counts
bgd.free_inodes_count++; bgd.free_inodes_count++;
if (is_directory) if (is_directory)
bgd.used_dirs_count--; bgd.used_dirs_count--;
write_block(bgd_location.block, bgd_buffer); TRY(write_block(bgd_location.block, bgd_buffer));
// update superblock inode count // update superblock inode count
m_superblock.free_inodes_count++; m_superblock.free_inodes_count++;
sync_superblock(); TRY(sync_superblock());
// remove inode from cache // remove inode from cache
if (m_inode_cache.contains(ino)) if (m_inode_cache.contains(ino))
m_inode_cache.remove(ino); m_inode_cache.remove(ino);
return {};
} }
void Ext2FS::read_block(uint32_t block, BlockBufferWrapper& buffer) BAN::ErrorOr<void> Ext2FS::read_block(uint32_t block, BlockBufferWrapper& buffer)
{ {
LockGuard _(m_mutex); LockGuard _(m_mutex);
@ -298,10 +299,12 @@ namespace Kernel
ASSERT(block >= superblock().first_data_block + 1); ASSERT(block >= superblock().first_data_block + 1);
ASSERT(buffer.size() >= block_size); 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<void> Ext2FS::write_block(uint32_t block, const BlockBufferWrapper& buffer)
{ {
LockGuard _(m_mutex); LockGuard _(m_mutex);
@ -311,10 +314,12 @@ namespace Kernel
ASSERT(block >= superblock().first_data_block + 1); ASSERT(block >= superblock().first_data_block + 1);
ASSERT(buffer.size() >= block_size); 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<void> Ext2FS::sync_superblock()
{ {
LockGuard _(m_mutex); LockGuard _(m_mutex);
@ -331,12 +336,14 @@ namespace Kernel
auto superblock_buffer = get_block_buffer(); 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)) if (memcmp(superblock_buffer.data(), &m_superblock, superblock_bytes))
{ {
memcpy(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() Ext2FS::BlockBufferWrapper Ext2FS::get_block_buffer()
@ -356,16 +363,16 @@ namespace Kernel
auto block_bitmap = m_buffer_manager.get_buffer(); auto block_bitmap = m_buffer_manager.get_buffer();
auto check_block_group = auto check_block_group =
[&](uint32_t block_group) -> uint32_t [&](uint32_t block_group) -> BAN::ErrorOr<uint32_t>
{ {
auto bgd_location = locate_block_group_descriptior(block_group); 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); auto& bgd = *(Ext2::BlockGroupDescriptor*)(bgd_buffer.data() + bgd_location.offset);
if (bgd.free_blocks_count == 0) if (bgd.free_blocks_count == 0)
return 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++) 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; 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; continue;
block_bitmap[byte] |= 1 << bit; block_bitmap[byte] |= 1 << bit;
write_block(bgd.block_bitmap, block_bitmap); TRY(write_block(bgd.block_bitmap, block_bitmap));
bgd.free_blocks_count--; bgd.free_blocks_count--;
write_block(bgd_location.block, bgd_buffer); TRY(write_block(bgd_location.block, bgd_buffer));
m_superblock.free_blocks_count--; m_superblock.free_blocks_count--;
sync_superblock(); TRY(sync_superblock());
return fs_block_index; return fs_block_index;
} }
@ -393,20 +400,20 @@ namespace Kernel
return 0; return 0;
}; };
if (auto ret = check_block_group(primary_bgd)) if (auto ret = TRY(check_block_group(primary_bgd)))
return ret; return ret;
uint32_t number_of_block_groups = BAN::Math::div_round_up(m_superblock.blocks_count, m_superblock.blocks_per_group); 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++) for (uint32_t block_group = 0; block_group < number_of_block_groups; block_group++)
if (block_group != primary_bgd) if (block_group != primary_bgd)
if (auto ret = check_block_group(block_group)) if (auto ret = TRY(check_block_group(block_group)))
return ret; return ret;
derrorln("Corrupted file system. Superblock indicates free blocks but none were found."); derrorln("Corrupted file system. Superblock indicates free blocks but none were found.");
return BAN::Error::from_error_code(ErrorCode::Ext2_Corrupted); return BAN::Error::from_error_code(ErrorCode::Ext2_Corrupted);
} }
void Ext2FS::release_block(uint32_t block) BAN::ErrorOr<void> Ext2FS::release_block(uint32_t block)
{ {
LockGuard _(m_mutex); LockGuard _(m_mutex);
@ -420,26 +427,28 @@ namespace Kernel
auto bitmap_buffer = get_block_buffer(); auto bitmap_buffer = get_block_buffer();
auto bgd_location = locate_block_group_descriptior(block_group); 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<Ext2::BlockGroupDescriptor>(); auto& bgd = bgd_buffer.span().slice(bgd_location.offset).as<Ext2::BlockGroupDescriptor>();
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 byte = block_offset / 8;
const uint32_t bit = block_offset % 8; const uint32_t bit = block_offset % 8;
ASSERT(bitmap_buffer[byte] & (1 << bit)); ASSERT(bitmap_buffer[byte] & (1 << bit));
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++; bgd.free_blocks_count++;
write_block(bgd_location.block, bgd_buffer); TRY(write_block(bgd_location.block, bgd_buffer));
m_superblock.free_blocks_count++; m_superblock.free_blocks_count++;
sync_superblock(); TRY(sync_superblock());
return {};
} }
Ext2FS::BlockLocation Ext2FS::locate_inode(uint32_t ino) BAN::ErrorOr<Ext2FS::BlockLocation> Ext2FS::locate_inode(uint32_t ino)
{ {
LockGuard _(m_mutex); LockGuard _(m_mutex);
@ -454,7 +463,7 @@ namespace Kernel
auto bgd_location = locate_block_group_descriptior(inode_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 = *(Ext2::BlockGroupDescriptor*)(bgd_buffer.data() + bgd_location.offset); auto& bgd = *(Ext2::BlockGroupDescriptor*)(bgd_buffer.data() + bgd_location.offset);

View File

@ -27,10 +27,10 @@ namespace Kernel
if (fs.inode_cache().contains(inode_ino)) if (fs.inode_cache().contains(inode_ino))
return fs.inode_cache()[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(); 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<Ext2::Inode>(); auto& inode = block_buffer.span().slice(inode_location.offset).as<Ext2::Inode>();
@ -41,18 +41,20 @@ namespace Kernel
Ext2Inode::~Ext2Inode() Ext2Inode::~Ext2Inode()
{ {
if (m_inode.links_count == 0) if (m_inode.links_count > 0)
cleanup_from_fs(); return;
if (auto ret = cleanup_from_fs(); ret.is_error())
dwarnln("Could not cleanup inode from FS: {}", ret.error());
} }
BAN::Optional<uint32_t> Ext2Inode::block_from_indirect_block(uint32_t block, uint32_t index, uint32_t depth) BAN::ErrorOr<BAN::Optional<uint32_t>> Ext2Inode::block_from_indirect_block(uint32_t block, uint32_t index, uint32_t depth)
{ {
if (block == 0) if (block == 0)
return {}; return BAN::Optional<uint32_t>();
ASSERT(depth >= 1); ASSERT(depth >= 1);
auto block_buffer = m_fs.get_block_buffer(); 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); 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<uint32_t>()[(index / divisor) % indices_per_block]; const uint32_t next_block = block_buffer.span().as_span<uint32_t>()[(index / divisor) % indices_per_block];
if (next_block == 0) if (next_block == 0)
return {}; return BAN::Optional<uint32_t>();
if (depth == 1) if (depth == 1)
return next_block; return BAN::Optional<uint32_t>(next_block);
return block_from_indirect_block(next_block, index, depth - 1); return block_from_indirect_block(next_block, index, depth - 1);
} }
BAN::Optional<uint32_t> Ext2Inode::fs_block_of_data_block_index(uint32_t data_block_index) BAN::ErrorOr<BAN::Optional<uint32_t>> Ext2Inode::fs_block_of_data_block_index(uint32_t data_block_index)
{ {
const uint32_t indices_per_block = blksize() / sizeof(uint32_t); const uint32_t indices_per_block = blksize() / sizeof(uint32_t);
if (data_block_index < 12) if (data_block_index < 12)
{ {
if (m_inode.block[data_block_index] == 0) if (m_inode.block[data_block_index] == 0)
return {}; return BAN::Optional<uint32_t>();
return m_inode.block[data_block_index]; return BAN::Optional<uint32_t>(m_inode.block[data_block_index]);
} }
data_block_index -= 12; 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++) 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()) 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 else
memset(block_buffer.data(), 0x00, block_buffer.size()); memset(block_buffer.data(), 0x00, block_buffer.size());
@ -168,9 +170,9 @@ namespace Kernel
// Write partial block // Write partial block
if (offset % block_size) 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()) 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 else
{ {
block_index = TRY(allocate_new_block(offset / block_size));; block_index = TRY(allocate_new_block(offset / block_size));;
@ -181,7 +183,7 @@ namespace Kernel
uint32_t to_copy = BAN::Math::min<uint32_t>(block_size - block_offset, to_write); uint32_t to_copy = BAN::Math::min<uint32_t>(block_size - block_offset, to_write);
memcpy(block_buffer.data() + block_offset, buffer.data(), to_copy); 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; written += to_copy;
offset += to_copy; offset += to_copy;
@ -190,12 +192,12 @@ namespace Kernel
while (to_write >= block_size) 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()) if (!block_index.has_value())
block_index = TRY(allocate_new_block(offset / block_size)); block_index = TRY(allocate_new_block(offset / block_size));
memcpy(block_buffer.data(), buffer.data() + written, block_buffer.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; written += block_size;
offset += block_size; offset += block_size;
@ -204,9 +206,9 @@ namespace Kernel
if (to_write > 0) 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()) 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 else
{ {
block_index = TRY(allocate_new_block(offset / block_size)); block_index = TRY(allocate_new_block(offset / block_size));
@ -214,7 +216,7 @@ namespace Kernel
} }
memcpy(block_buffer.data(), buffer.data() + written, to_write); 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(); return buffer.size();
@ -227,8 +229,14 @@ namespace Kernel
// TODO: we should remove unused blocks on shrink // TODO: we should remove unused blocks on shrink
const auto old_size = m_inode.size;
m_inode.size = new_size; m_inode.size = new_size;
sync(); if (auto ret = sync(); ret.is_error())
{
m_inode.size = old_size;
return ret.release_error();
}
return {}; return {};
} }
@ -238,23 +246,31 @@ namespace Kernel
ASSERT((mode & Inode::Mode::TYPE_MASK) == 0); ASSERT((mode & Inode::Mode::TYPE_MASK) == 0);
if (m_inode.mode == mode) if (m_inode.mode == mode)
return {}; return {};
const auto old_mode = m_inode.mode;
m_inode.mode = (m_inode.mode & Inode::Mode::TYPE_MASK) | 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 {}; return {};
} }
void Ext2Inode::cleanup_indirect_block(uint32_t block, uint32_t depth) BAN::ErrorOr<void> Ext2Inode::cleanup_indirect_block(uint32_t block, uint32_t depth)
{ {
ASSERT(block); ASSERT(block);
if (depth == 0) if (depth == 0)
{ {
m_fs.release_block(block); TRY(m_fs.release_block(block));
return; return {};
} }
auto block_buffer = m_fs.get_block_buffer(); 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); const uint32_t ids_per_block = blksize() / sizeof(uint32_t);
for (uint32_t i = 0; i < ids_per_block; i++) 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<uint32_t>()[i]; const uint32_t next_block = block_buffer.span().as_span<uint32_t>()[i];
if (next_block == 0) if (next_block == 0)
continue; 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<void> Ext2Inode::cleanup_from_fs()
{ {
ASSERT(m_inode.links_count == 0); ASSERT(m_inode.links_count == 0);
@ -278,15 +295,15 @@ namespace Kernel
// cleanup direct blocks // cleanup direct blocks
for (uint32_t i = 0; i < 12; i++) for (uint32_t i = 0; i < 12; i++)
if (m_inode.block[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 // cleanup indirect blocks
if (m_inode.block[12]) 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]) 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]) if (m_inode.block[14])
cleanup_indirect_block(m_inode.block[14], 3); TRY(cleanup_indirect_block(m_inode.block[14], 3));
done: done:
// mark blocks as deleted // mark blocks as deleted
@ -294,9 +311,11 @@ done:
// FIXME: this is only required since fs does not get // FIXME: this is only required since fs does not get
// deleting inode from its cache // deleting inode from its cache
sync(); TRY(sync());
m_fs.delete_inode(ino()); TRY(m_fs.delete_inode(ino()));
return {};
} }
BAN::ErrorOr<size_t> Ext2Inode::list_next_inodes_impl(off_t offset, struct dirent* list, size_t list_size) BAN::ErrorOr<size_t> Ext2Inode::list_next_inodes_impl(off_t offset, struct dirent* list, size_t list_size)
@ -308,11 +327,11 @@ done:
return 0; return 0;
// FIXME: can we actually assume directories have all their blocks allocated // 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(); 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 // First determine if we have big enough list
size_t entry_count = 0; size_t entry_count = 0;
@ -413,7 +432,7 @@ done:
auto inode_or_error = Ext2Inode::create(m_fs, new_ino); auto inode_or_error = Ext2Inode::create(m_fs, new_ino);
if (inode_or_error.is_error()) 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(); return inode_or_error.release_error();
} }
@ -434,19 +453,21 @@ done:
auto inode_or_error = Ext2Inode::create(m_fs, new_ino); auto inode_or_error = Ext2Inode::create(m_fs, new_ino);
if (inode_or_error.is_error()) 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(); return inode_or_error.release_error();
} }
auto inode = inode_or_error.release_value(); auto inode = inode_or_error.release_value();
BAN::ScopeGuard cleanup([&] { inode->cleanup_from_fs(); });
TRY(inode->link_inode_to_directory(*inode, "."_sv)); // link . and ..
TRY(inode->link_inode_to_directory(*this, ".."_sv)); 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)); // link to parent
if (auto ret = link_inode_to_directory(*inode, name); ret.is_error())
cleanup.disable(); return ({ TRY(inode->cleanup_from_fs()); ret.release_error(); });
return {}; return {};
} }
@ -475,29 +496,32 @@ done:
auto block_buffer = m_fs.get_block_buffer(); auto block_buffer = m_fs.get_block_buffer();
auto write_inode = [&](uint32_t entry_offset, uint32_t entry_rec_len) auto write_inode =
{ [&](uint32_t entry_offset, uint32_t entry_rec_len) -> BAN::ErrorOr<void>
auto typed_mode = inode.mode(); {
uint8_t file_type = (m_fs.superblock().rev_level == Ext2::Enum::GOOD_OLD_REV) ? 0 auto typed_mode = inode.mode();
: typed_mode.ifreg() ? Ext2::Enum::REG_FILE uint8_t file_type = (m_fs.superblock().rev_level == Ext2::Enum::GOOD_OLD_REV) ? 0
: typed_mode.ifdir() ? Ext2::Enum::DIR : typed_mode.ifreg() ? Ext2::Enum::REG_FILE
: typed_mode.ifchr() ? Ext2::Enum::CHRDEV : typed_mode.ifdir() ? Ext2::Enum::DIR
: typed_mode.ifblk() ? Ext2::Enum::BLKDEV : typed_mode.ifchr() ? Ext2::Enum::CHRDEV
: typed_mode.ififo() ? Ext2::Enum::FIFO : typed_mode.ifblk() ? Ext2::Enum::BLKDEV
: typed_mode.ifsock() ? Ext2::Enum::SOCK : typed_mode.ififo() ? Ext2::Enum::FIFO
: typed_mode.iflnk() ? Ext2::Enum::SYMLINK : typed_mode.ifsock() ? Ext2::Enum::SOCK
: 0; : typed_mode.iflnk() ? Ext2::Enum::SYMLINK
: 0;
auto& new_entry = block_buffer.span().slice(entry_offset).as<Ext2::LinkedDirectoryEntry>(); auto& new_entry = block_buffer.span().slice(entry_offset).as<Ext2::LinkedDirectoryEntry>();
new_entry.inode = inode.ino(); new_entry.inode = inode.ino();
new_entry.rec_len = entry_rec_len; new_entry.rec_len = entry_rec_len;
new_entry.name_len = name.size(); new_entry.name_len = name.size();
new_entry.file_type = file_type; new_entry.file_type = file_type;
memcpy(new_entry.name, name.data(), name.size()); memcpy(new_entry.name, name.data(), name.size());
inode.m_inode.links_count++; inode.m_inode.links_count++;
inode.sync(); TRY(inode.sync());
};
return {};
};
uint32_t block_index = 0; uint32_t block_index = 0;
uint32_t entry_offset = 0; uint32_t entry_offset = 0;
@ -512,8 +536,8 @@ done:
goto needs_new_block; goto needs_new_block;
// Try to insert inode to last data block // Try to insert inode to last data block
block_index = fs_block_of_data_block_index(data_block_count - 1).value(); block_index = TRY(fs_block_of_data_block_index(data_block_count - 1)).value();
m_fs.read_block(block_index, block_buffer); TRY(m_fs.read_block(block_index, block_buffer));
while (entry_offset < block_size) while (entry_offset < block_size)
{ {
@ -525,8 +549,8 @@ done:
if (entry.inode == 0 && needed_entry_len <= entry.rec_len) if (entry.inode == 0 && needed_entry_len <= entry.rec_len)
{ {
write_inode(entry_offset, entry.rec_len); TRY(write_inode(entry_offset, entry.rec_len));
m_fs.write_block(block_index, block_buffer); TRY(m_fs.write_block(block_index, block_buffer));
return {}; return {};
} }
else if (needed_entry_len <= entry.rec_len - entry_min_rec_len) 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; uint32_t new_rec_len = entry.rec_len - entry_min_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); TRY(write_inode(entry_offset + entry.rec_len, new_rec_len));
m_fs.write_block(block_index, block_buffer); TRY(m_fs.write_block(block_index, block_buffer));
return {}; return {};
} }
@ -547,8 +571,8 @@ needs_new_block:
m_inode.size += blksize(); m_inode.size += blksize();
memset(block_buffer.data(), 0x00, block_buffer.size()); memset(block_buffer.data(), 0x00, block_buffer.size());
write_inode(0, block_size); TRY(write_inode(0, block_size));
m_fs.write_block(block_index, block_buffer); TRY(m_fs.write_block(block_index, block_buffer));
return {}; return {};
} }
@ -563,8 +587,8 @@ needs_new_block:
for (uint32_t i = 0; i < max_used_data_block_count(); i++) for (uint32_t i = 0; i < max_used_data_block_count(); i++)
{ {
// FIXME: can we actually assume directories have all their blocks allocated // FIXME: can we actually assume directories have all their blocks allocated
const uint32_t block_index = fs_block_of_data_block_index(i).value(); const uint32_t block_index = TRY(fs_block_of_data_block_index(i)).value();
m_fs.read_block(block_index, block_buffer); TRY(m_fs.read_block(block_index, block_buffer));
blksize_t offset = 0; blksize_t offset = 0;
while (offset < blksize()) while (offset < blksize())
@ -599,8 +623,8 @@ needs_new_block:
for (uint32_t i = 0; i < max_used_data_block_count(); i++) for (uint32_t i = 0; i < max_used_data_block_count(); i++)
{ {
// FIXME: can we actually assume directories have all their blocks allocated // FIXME: can we actually assume directories have all their blocks allocated
const uint32_t block_index = fs_block_of_data_block_index(i).value(); const uint32_t block_index = TRY(fs_block_of_data_block_index(i)).value();
m_fs.read_block(block_index, block_buffer); TRY(m_fs.read_block(block_index, block_buffer));
bool modified = false; bool modified = false;
@ -616,13 +640,13 @@ needs_new_block:
if (entry_name == "."_sv) if (entry_name == "."_sv)
{ {
m_inode.links_count--; m_inode.links_count--;
sync(); TRY(sync());
} }
else if (entry_name == ".."_sv) else if (entry_name == ".."_sv)
{ {
auto parent = TRY(Ext2Inode::create(m_fs, entry.inode)); auto parent = TRY(Ext2Inode::create(m_fs, entry.inode));
parent->m_inode.links_count--; parent->m_inode.links_count--;
parent->sync(); TRY(parent->sync());
} }
else else
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
@ -635,7 +659,7 @@ needs_new_block:
} }
if (modified) if (modified)
m_fs.write_block(block_index, block_buffer); TRY(m_fs.write_block(block_index, block_buffer));
} }
return {}; return {};
@ -655,8 +679,8 @@ needs_new_block:
for (uint32_t i = 0; i < max_used_data_block_count(); i++) for (uint32_t i = 0; i < max_used_data_block_count(); i++)
{ {
// FIXME: can we actually assume directories have all their blocks allocated // FIXME: can we actually assume directories have all their blocks allocated
const uint32_t block_index = fs_block_of_data_block_index(i).value(); const uint32_t block_index = TRY(fs_block_of_data_block_index(i)).value();
m_fs.read_block(block_index, block_buffer); TRY(m_fs.read_block(block_index, block_buffer));
blksize_t offset = 0; blksize_t offset = 0;
while (offset < blksize()) while (offset < blksize())
@ -677,7 +701,7 @@ needs_new_block:
else else
inode->m_inode.links_count--; inode->m_inode.links_count--;
sync(); TRY(sync());
// NOTE: If this was the last link to inode we must // NOTE: If this was the last link to inode we must
// remove it from inode cache to trigger cleanup // remove it from inode cache to trigger cleanup
@ -690,7 +714,7 @@ needs_new_block:
// FIXME: This should expand the last inode if exists // FIXME: This should expand the last inode if exists
entry.inode = 0; entry.inode = 0;
m_fs.write_block(block_index, block_buffer); TRY(m_fs.write_block(block_index, block_buffer));
} }
offset += entry.rec_len; offset += entry.rec_len;
} }
@ -714,14 +738,14 @@ needs_new_block:
auto block_buffer = m_fs.get_block_buffer(); auto block_buffer = m_fs.get_block_buffer();
memset(block_buffer.data(), 0x00, block_buffer.size()); 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) if (depth == 0)
return block; return block;
auto block_buffer = m_fs.get_block_buffer(); 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; uint32_t divisor = 1;
for (uint32_t i = 1; i < depth; i++) for (uint32_t i = 1; i < depth; i++)
@ -730,7 +754,9 @@ needs_new_block:
uint32_t& new_block = block_buffer.span().as_span<uint32_t>()[(index / divisor) % indices_per_fs_block]; uint32_t& new_block = block_buffer.span().as_span<uint32_t>()[(index / divisor) % indices_per_fs_block];
uint32_t allocated_block = TRY(allocate_new_block_to_indirect_block(new_block, index, depth - 1)); 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; return allocated_block;
} }
@ -740,13 +766,12 @@ needs_new_block:
const uint32_t inode_blocks_per_fs_block = blksize() / 512; const uint32_t inode_blocks_per_fs_block = blksize() / 512;
const uint32_t indices_per_fs_block = blksize() / sizeof(uint32_t); const uint32_t indices_per_fs_block = blksize() / sizeof(uint32_t);
BAN::ScopeGuard syncer([&] { sync(); });
if (data_block_index < 12) if (data_block_index < 12)
{ {
ASSERT(m_inode.block[data_block_index] == 0); ASSERT(m_inode.block[data_block_index] == 0);
m_inode.block[data_block_index] = TRY(m_fs.reserve_free_block(block_group())); m_inode.block[data_block_index] = TRY(m_fs.reserve_free_block(block_group()));
m_inode.blocks += inode_blocks_per_fs_block; m_inode.blocks += inode_blocks_per_fs_block;
TRY(sync());
return m_inode.block[data_block_index]; return m_inode.block[data_block_index];
} }
data_block_index -= 12; data_block_index -= 12;
@ -765,17 +790,19 @@ needs_new_block:
ASSERT_NOT_REACHED(); ASSERT_NOT_REACHED();
} }
void Ext2Inode::sync() BAN::ErrorOr<void> 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(); 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))) 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)); 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<BAN::RefPtr<Inode>> Ext2Inode::find_inode_impl(BAN::StringView file_name) BAN::ErrorOr<BAN::RefPtr<Inode>> 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++) for (uint32_t i = 0; i < max_used_data_block_count(); i++)
{ {
// FIXME: can we actually assume directories have all their blocks allocated // FIXME: can we actually assume directories have all their blocks allocated
const uint32_t block_index = fs_block_of_data_block_index(i).value(); const uint32_t block_index = TRY(fs_block_of_data_block_index(i)).value();
m_fs.read_block(block_index, block_buffer); TRY(m_fs.read_block(block_index, block_buffer));
BAN::ConstByteSpan entry_span = block_buffer.span(); BAN::ConstByteSpan entry_span = block_buffer.span();
while (entry_span.size() >= sizeof(Ext2::LinkedDirectoryEntry)) while (entry_span.size() >= sizeof(Ext2::LinkedDirectoryEntry))