Kernel: Fix ext2 inode deletion

fsck now reports clean filesystem even after deleting files
This commit is contained in:
Bananymous 2023-10-26 02:05:05 +03:00
parent 126edea119
commit d09310f388
5 changed files with 152 additions and 31 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&);
void delete_inode(uint32_t); 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&); void read_block(uint32_t, BlockBufferWrapper&);

View File

@ -47,11 +47,13 @@ namespace Kernel
uint32_t fs_block_of_data_block_index(uint32_t data_block_index); 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<void> cleanup_default_links();
void cleanup_from_fs(); void cleanup_from_fs();
BAN::ErrorOr<uint32_t> allocate_new_block(); BAN::ErrorOr<uint32_t> allocate_new_block();
BAN::ErrorOr<void> sync(); void sync();
uint32_t block_group() const; uint32_t block_group() const;

View File

@ -184,6 +184,8 @@ namespace Kernel
write_block(bgd->inode_bitmap, inode_bitmap); write_block(bgd->inode_bitmap, inode_bitmap);
bgd->free_inodes_count--; bgd->free_inodes_count--;
if (Inode::Mode(ext2_inode.mode).ifdir())
bgd->used_dirs_count++;
write_block(bgd_location.block, bgd_buffer); 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;
@ -216,34 +218,49 @@ namespace Kernel
{ {
LockGuard _(m_lock); LockGuard _(m_lock);
ASSERT(ino >= superblock().first_ino);
ASSERT(ino <= superblock().inodes_count); ASSERT(ino <= superblock().inodes_count);
auto bgd_buffer = get_block_buffer(); auto bgd_buffer = get_block_buffer();
auto bitmap_buffer = get_block_buffer(); auto bitmap_buffer = get_block_buffer();
auto inode_buffer = get_block_buffer();
const uint32_t inode_group = (ino - 1) / superblock().inodes_per_group; const uint32_t inode_group = (ino - 1) / superblock().inodes_per_group;
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); 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
read_block(bgd.inode_bitmap, bitmap_buffer); 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); 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 = inode_buffer.span().slice(inode_location.offset).as<Ext2::Inode>();
bool is_directory = Inode::Mode(inode.mode).ifdir();
memset(&inode, 0x00, m_superblock.inode_size);
write_block(inode_location.block, inode_buffer);
// update bgd counts
bgd.free_inodes_count++; bgd.free_inodes_count++;
if (is_directory)
bgd.used_dirs_count--;
write_block(bgd_location.block, bgd_buffer); write_block(bgd_location.block, bgd_buffer);
// update superblock inode count
m_superblock.free_inodes_count++;
sync_superblock();
// 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);
dprintln("succesfully deleted inode {}", ino);
} }
void Ext2FS::read_block(uint32_t block, BlockBufferWrapper& buffer) void Ext2FS::read_block(uint32_t block, BlockBufferWrapper& buffer)
@ -395,7 +412,8 @@ namespace Kernel
bgd.free_blocks_count++; bgd.free_blocks_count++;
write_block(bgd_location.block, bgd_buffer); write_block(bgd_location.block, bgd_buffer);
dprintln("successfully freed block {}", block); m_superblock.free_blocks_count++;
sync_superblock();
} }
Ext2FS::BlockLocation Ext2FS::locate_inode(uint32_t ino) Ext2FS::BlockLocation Ext2FS::locate_inode(uint32_t ino)

View File

@ -1,4 +1,5 @@
#include <BAN/Function.h> #include <BAN/Function.h>
#include <BAN/ScopeGuard.h>
#include <kernel/FS/Ext2/FileSystem.h> #include <kernel/FS/Ext2/FileSystem.h>
#include <kernel/FS/Ext2/Inode.h> #include <kernel/FS/Ext2/Inode.h>
#include <kernel/Timer/Timer.h> #include <kernel/Timer/Timer.h>
@ -43,7 +44,7 @@ namespace Kernel
Ext2Inode::~Ext2Inode() Ext2Inode::~Ext2Inode()
{ {
if ((mode().ifdir() && m_inode.links_count == 1) || m_inode.links_count == 0) if (m_inode.links_count == 0)
cleanup_from_fs(); cleanup_from_fs();
} }
@ -220,7 +221,7 @@ namespace Kernel
if (new_size < m_inode.size) if (new_size < m_inode.size)
{ {
m_inode.size = new_size; m_inode.size = new_size;
TRY(sync()); sync();
return {}; return {};
} }
@ -243,7 +244,7 @@ namespace Kernel
} }
m_inode.size = new_size; m_inode.size = new_size;
TRY(sync()); sync();
return {}; return {};
} }
@ -254,19 +255,12 @@ namespace Kernel
if (m_inode.mode == mode) if (m_inode.mode == mode)
return {}; return {};
m_inode.mode = (m_inode.mode & Inode::Mode::TYPE_MASK) | mode; m_inode.mode = (m_inode.mode & Inode::Mode::TYPE_MASK) | mode;
TRY(sync()); sync();
return {}; return {};
} }
void Ext2Inode::cleanup_from_fs() 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); ASSERT(m_inode.links_count == 0);
for (uint32_t i = 0; i < blocks(); i++) for (uint32_t i = 0; i < blocks(); i++)
m_fs.release_block(fs_block_of_data_block_index(i)); m_fs.release_block(fs_block_of_data_block_index(i));
@ -383,8 +377,6 @@ namespace Kernel
BAN::ErrorOr<void> Ext2Inode::create_file_impl(BAN::StringView name, mode_t mode, uid_t uid, gid_t gid) BAN::ErrorOr<void> Ext2Inode::create_file_impl(BAN::StringView name, mode_t mode, uid_t uid, gid_t gid)
{ {
// FIXME: handle errors
ASSERT(this->mode().ifdir()); ASSERT(this->mode().ifdir());
if (!(Mode(mode).ifreg())) if (!(Mode(mode).ifreg()))
@ -408,8 +400,6 @@ namespace Kernel
BAN::ErrorOr<void> Ext2Inode::create_directory_impl(BAN::StringView name, mode_t mode, uid_t uid, gid_t gid) BAN::ErrorOr<void> Ext2Inode::create_directory_impl(BAN::StringView name, mode_t mode, uid_t uid, gid_t gid)
{ {
// FIXME: handle errors
ASSERT(this->mode().ifdir()); ASSERT(this->mode().ifdir());
ASSERT(Mode(mode).ifdir()); ASSERT(Mode(mode).ifdir());
@ -423,11 +413,15 @@ namespace Kernel
} }
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)); TRY(inode->link_inode_to_directory(*inode, "."sv));
TRY(inode->link_inode_to_directory(*this, ".."sv)); TRY(inode->link_inode_to_directory(*this, ".."sv));
TRY(link_inode_to_directory(*inode, name)); TRY(link_inode_to_directory(*inode, name));
cleanup.disable();
return {}; return {};
} }
@ -476,7 +470,7 @@ namespace Kernel
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++;
MUST(inode.sync()); inode.sync();
}; };
uint32_t block_index = 0; uint32_t block_index = 0;
@ -531,9 +525,100 @@ needs_new_block:
return {}; return {};
} }
BAN::ErrorOr<bool> Ext2Inode::is_directory_empty()
{
ASSERT(mode().ifdir());
if (m_inode.flags & Ext2::Enum::INDEX_FL)
{
dwarnln("deletion of indexed directory is not supported");
return BAN::Error::from_errno(ENOTSUP);
}
auto block_buffer = m_fs.get_block_buffer();
// Confirm that this doesn't contain anything else than '.' or '..'
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)
{
BAN::StringView entry_name(entry.name, entry.name_len);
if (entry_name != "."sv && entry_name != ".."sv)
return false;
}
offset += entry.rec_len;
}
}
return true;
}
BAN::ErrorOr<void> Ext2Inode::cleanup_default_links()
{
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);
bool modified = false;
blksize_t offset = 0;
while (offset < blksize())
{
auto& entry = block_buffer.span().slice(offset).as<Ext2::LinkedDirectoryEntry>();
if (entry.inode)
{
BAN::StringView entry_name(entry.name, entry.name_len);
if (entry_name == "."sv)
{
m_inode.links_count--;
sync();
}
else if (entry_name == ".."sv)
{
auto parent = TRY(Ext2Inode::create(m_fs, entry.inode));
parent->m_inode.links_count--;
parent->sync();
}
else
ASSERT_NOT_REACHED();
modified = true;
entry.inode = 0;
}
offset += entry.rec_len;
}
if (modified)
m_fs.write_block(block, block_buffer);
}
return {};
}
BAN::ErrorOr<void> Ext2Inode::unlink_impl(BAN::StringView name) BAN::ErrorOr<void> Ext2Inode::unlink_impl(BAN::StringView name)
{ {
ASSERT(mode().ifdir()); ASSERT(mode().ifdir());
if (m_inode.flags & Ext2::Enum::INDEX_FL)
{
dwarnln("deletion from indexed directory is not supported");
return BAN::Error::from_errno(ENOTSUP);
}
auto block_buffer = m_fs.get_block_buffer(); auto block_buffer = m_fs.get_block_buffer();
@ -546,15 +631,31 @@ needs_new_block:
while (offset < blksize()) while (offset < blksize())
{ {
auto& entry = block_buffer.span().slice(offset).as<Ext2::LinkedDirectoryEntry>(); auto& entry = block_buffer.span().slice(offset).as<Ext2::LinkedDirectoryEntry>();
if (entry.inode && name == entry.name) if (entry.inode && name == BAN::StringView(entry.name, entry.name_len))
{ {
auto inode = TRY(Ext2Inode::create(m_fs, entry.inode)); auto inode = TRY(Ext2Inode::create(m_fs, entry.inode));
if (inode->mode().ifdir())
{
if (!TRY(inode->is_directory_empty()))
return BAN::Error::from_errno(ENOTEMPTY);
TRY(inode->cleanup_default_links());
}
if (inode->nlink() == 0) if (inode->nlink() == 0)
dprintln("Corrupted filesystem. Deleting inode with 0 links"); dprintln("Corrupted filesystem. Deleting inode with 0 links");
else else
inode->m_inode.links_count--; inode->m_inode.links_count--;
TRY(sync()); sync();
// NOTE: If this was the last link to inode we must
// remove it from inode cache to trigger cleanup
if (inode->nlink() == 0)
{
auto& cache = m_fs.inode_cache();
if (cache.contains(inode->ino()))
cache.remove(inode->ino());
}
// FIXME: This should expand the last inode if exists // FIXME: This should expand the last inode if exists
entry.inode = 0; entry.inode = 0;
@ -620,7 +721,7 @@ needs_new_block:
{ {
if (mode().ifdir()) if (mode().ifdir())
m_inode.size += blksize(); m_inode.size += blksize();
MUST(sync()); sync();
}; };
// direct block // direct block
@ -676,7 +777,7 @@ needs_new_block:
#undef READ_OR_ALLOCATE_INDIRECT_BLOCK #undef READ_OR_ALLOCATE_INDIRECT_BLOCK
#undef WRITE_BLOCK_AND_RETURN #undef WRITE_BLOCK_AND_RETURN
BAN::ErrorOr<void> Ext2Inode::sync() void Ext2Inode::sync()
{ {
auto inode_location = m_fs.locate_inode(ino()); auto inode_location = m_fs.locate_inode(ino());
auto block_buffer = m_fs.get_block_buffer(); auto block_buffer = m_fs.get_block_buffer();
@ -687,8 +788,6 @@ needs_new_block:
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); 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)

View File

@ -103,6 +103,8 @@ namespace Kernel
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);
if (name == "."sv || name == ".."sv)
return BAN::Error::from_errno(EINVAL);
return unlink_impl(name); return unlink_impl(name);
} }