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:
parent
3566ddab00
commit
b7007016c0
|
@ -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; }
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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); }
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 {};
|
||||||
}
|
}
|
||||||
|
@ -386,12 +414,19 @@ namespace Kernel
|
||||||
ASSERT(Mode(mode).ifdir());
|
ASSERT(Mode(mode).ifdir());
|
||||||
|
|
||||||
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {};
|
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) \
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue