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
This commit is contained in:
Bananymous 2023-10-25 21:43:36 +03:00
parent fda4a4ad24
commit 091c5b6a66
9 changed files with 160 additions and 18 deletions

View File

@ -58,7 +58,7 @@ 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&);
BAN::ErrorOr<void> delete_inode(uint32_t); void delete_inode(uint32_t);
BAN::ErrorOr<void> resize_inode(uint32_t, size_t); BAN::ErrorOr<void> resize_inode(uint32_t, size_t);
void read_block(uint32_t, BlockBufferWrapper&); void read_block(uint32_t, BlockBufferWrapper&);
@ -68,6 +68,7 @@ namespace Kernel
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::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; }

View File

@ -13,6 +13,8 @@ namespace Kernel
class Ext2Inode final : public Inode class Ext2Inode final : public Inode
{ {
public: public:
~Ext2Inode();
virtual ino_t ino() const override { return m_ino; }; virtual ino_t ino() const override { return m_ino; };
virtual Mode mode() const override { return { m_inode.mode }; } virtual Mode mode() const override { return { m_inode.mode }; }
virtual nlink_t nlink() const override { return m_inode.links_count; } virtual nlink_t nlink() const override { return m_inode.links_count; }
@ -32,6 +34,7 @@ namespace Kernel
virtual BAN::ErrorOr<void> list_next_inodes_impl(off_t, DirectoryEntryList*, size_t) override; virtual BAN::ErrorOr<void> list_next_inodes_impl(off_t, DirectoryEntryList*, size_t) override;
virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override; virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override;
virtual BAN::ErrorOr<void> create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) override; virtual BAN::ErrorOr<void> create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) override;
virtual BAN::ErrorOr<void> unlink_impl(BAN::StringView) override;
virtual BAN::ErrorOr<BAN::String> link_target_impl() override; virtual BAN::ErrorOr<BAN::String> link_target_impl() override;
@ -45,6 +48,8 @@ namespace Kernel
BAN::ErrorOr<void> link_inode_to_directory(Ext2Inode&, BAN::StringView name); BAN::ErrorOr<void> link_inode_to_directory(Ext2Inode&, BAN::StringView name);
void cleanup_from_fs();
BAN::ErrorOr<uint32_t> allocate_new_block(); BAN::ErrorOr<uint32_t> allocate_new_block();
BAN::ErrorOr<void> sync(); BAN::ErrorOr<void> sync();

View File

@ -91,7 +91,7 @@ namespace Kernel
BAN::ErrorOr<void> list_next_inodes(off_t, DirectoryEntryList*, size_t); BAN::ErrorOr<void> list_next_inodes(off_t, DirectoryEntryList*, size_t);
BAN::ErrorOr<void> create_file(BAN::StringView, mode_t, uid_t, gid_t); BAN::ErrorOr<void> create_file(BAN::StringView, mode_t, uid_t, gid_t);
BAN::ErrorOr<void> create_directory(BAN::StringView, mode_t, uid_t, gid_t); BAN::ErrorOr<void> create_directory(BAN::StringView, mode_t, uid_t, gid_t);
BAN::ErrorOr<void> delete_inode(BAN::StringView); BAN::ErrorOr<void> unlink(BAN::StringView);
// Link API // Link API
BAN::ErrorOr<BAN::String> link_target(); BAN::ErrorOr<BAN::String> link_target();
@ -109,7 +109,7 @@ namespace Kernel
virtual BAN::ErrorOr<void> list_next_inodes_impl(off_t, DirectoryEntryList*, size_t) { return BAN::Error::from_errno(ENOTSUP); } virtual BAN::ErrorOr<void> list_next_inodes_impl(off_t, DirectoryEntryList*, size_t) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) { return BAN::Error::from_errno(ENOTSUP); } virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) { return BAN::Error::from_errno(ENOTSUP); } virtual BAN::ErrorOr<void> create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> delete_inode_impl(BAN::StringView) { return BAN::Error::from_errno(ENOTSUP); } virtual BAN::ErrorOr<void> unlink_impl(BAN::StringView) { return BAN::Error::from_errno(ENOTSUP); }
// Link API // Link API
virtual BAN::ErrorOr<BAN::String> link_target_impl() { return BAN::Error::from_errno(ENOTSUP); } virtual BAN::ErrorOr<BAN::String> link_target_impl() { return BAN::Error::from_errno(ENOTSUP); }

View File

@ -97,7 +97,7 @@ namespace Kernel
virtual BAN::ErrorOr<void> list_next_inodes_impl(off_t, DirectoryEntryList*, size_t) override; virtual BAN::ErrorOr<void> list_next_inodes_impl(off_t, DirectoryEntryList*, size_t) override;
virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override; virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override;
virtual BAN::ErrorOr<void> create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) override; virtual BAN::ErrorOr<void> create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) override;
virtual BAN::ErrorOr<void> delete_inode_impl(BAN::StringView) override; virtual BAN::ErrorOr<void> unlink_impl(BAN::StringView) override;
private: private:
static constexpr size_t m_name_max = NAME_MAX; static constexpr size_t m_name_max = NAME_MAX;

View File

@ -212,6 +212,40 @@ 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)
{
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<Ext2::BlockGroupDescriptor>();
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) void Ext2FS::read_block(uint32_t block, BlockBufferWrapper& buffer)
{ {
LockGuard _(m_lock); LockGuard _(m_lock);
@ -255,8 +289,7 @@ namespace Kernel
const uint32_t lba = 1024 / sector_size; const uint32_t lba = 1024 / sector_size;
const uint32_t sector_count = BAN::Math::div_round_up<uint32_t>(superblock_bytes, sector_size); const uint32_t sector_count = BAN::Math::div_round_up<uint32_t>(superblock_bytes, sector_size);
BAN::Vector<uint8_t> superblock_buffer; auto superblock_buffer = get_block_buffer();
MUST(superblock_buffer.resize(sector_count * sector_size));
MUST(m_partition.read_sectors(lba, sector_count, superblock_buffer.span())); MUST(m_partition.read_sectors(lba, sector_count, superblock_buffer.span()));
if (memcmp(superblock_buffer.data(), &m_superblock, superblock_bytes)) if (memcmp(superblock_buffer.data(), &m_superblock, superblock_bytes))
@ -266,7 +299,6 @@ namespace Kernel
} }
} }
Ext2FS::BlockBufferWrapper Ext2FS::get_block_buffer() Ext2FS::BlockBufferWrapper Ext2FS::get_block_buffer()
{ {
LockGuard _(m_lock); LockGuard _(m_lock);
@ -334,6 +366,38 @@ namespace Kernel
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)
{
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<Ext2::BlockGroupDescriptor>();
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) Ext2FS::BlockLocation Ext2FS::locate_inode(uint32_t ino)
{ {
LockGuard _(m_lock); LockGuard _(m_lock);

View File

@ -41,6 +41,12 @@ namespace Kernel
return result; 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_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; }) #define VERIFY_AND_RETURN(expr) ({ const uint32_t result = expr; ASSERT(result); return result; })
@ -252,6 +258,21 @@ namespace Kernel
return {}; 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<void> Ext2Inode::list_next_inodes_impl(off_t offset, DirectoryEntryList* list, size_t list_size) BAN::ErrorOr<void> Ext2Inode::list_next_inodes_impl(off_t offset, DirectoryEntryList* list, size_t list_size)
{ {
ASSERT(mode().ifdir()); 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))); 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 {}; return {};
} }
@ -387,11 +415,18 @@ namespace Kernel
const uint32_t new_ino = TRY(m_fs.create_inode(initialize_new_inode_info(mode, uid, gid))); 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);
MUST(inode->link_inode_to_directory(*inode, "."sv)); if (inode_or_error.is_error())
MUST(inode->link_inode_to_directory(*this, ".."sv)); {
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(inode->link_inode_to_directory(*inode, "."sv));
TRY(inode->link_inode_to_directory(*this, ".."sv));
TRY(link_inode_to_directory(*inode, name));
return {}; return {};
} }
@ -496,6 +531,42 @@ needs_new_block:
return {}; return {};
} }
BAN::ErrorOr<void> 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<Ext2::LinkedDirectoryEntry>();
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_) \ #define READ_OR_ALLOCATE_BASE_BLOCK(index_) \
do { \ do { \
if (m_inode.block[index_] != 0) \ if (m_inode.block[index_] != 0) \

View File

@ -97,13 +97,13 @@ namespace Kernel
return create_directory_impl(name, mode, uid, gid); return create_directory_impl(name, mode, uid, gid);
} }
BAN::ErrorOr<void> Inode::delete_inode(BAN::StringView name) BAN::ErrorOr<void> Inode::unlink(BAN::StringView name)
{ {
LockGuard _(m_lock); LockGuard _(m_lock);
Thread::TerminateBlocker blocker(Thread::current()); Thread::TerminateBlocker blocker(Thread::current());
if (!mode().ifdir()) if (!mode().ifdir())
return BAN::Error::from_errno(ENOTDIR); return BAN::Error::from_errno(ENOTDIR);
return delete_inode_impl(name); return unlink_impl(name);
} }
BAN::ErrorOr<BAN::String> Inode::link_target() BAN::ErrorOr<BAN::String> Inode::link_target()

View File

@ -40,7 +40,7 @@ namespace Kernel
void ProcFileSystem::on_process_delete(Process& process) void ProcFileSystem::on_process_delete(Process& process)
{ {
auto path = BAN::String::formatted("{}", process.pid()); auto path = BAN::String::formatted("{}", process.pid());
MUST(m_root_inode->delete_inode(path)); MUST(m_root_inode->unlink(path));
} }
} }

View File

@ -263,8 +263,9 @@ namespace Kernel
return {}; return {};
} }
BAN::ErrorOr<void> RamDirectoryInode::delete_inode_impl(BAN::StringView name) BAN::ErrorOr<void> 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++) for (size_t i = 0; i < m_entries.size(); i++)
{ {
if (name == m_entries[i].name) if (name == m_entries[i].name)