From efaca469ee39f5014c1eb7f45ba149afb26908b8 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Mon, 20 Feb 2023 10:25:15 +0200 Subject: [PATCH] Kernel: Ext2Inode now supports indirect blocks through for_each_block() --- kernel/include/kernel/FS/Ext2.h | 9 +- kernel/include/kernel/FS/Inode.h | 6 +- kernel/kernel/FS/Ext2.cpp | 206 ++++++++++++++++++++++--------- kernel/kernel/Shell.cpp | 26 ++++ 4 files changed, 186 insertions(+), 61 deletions(-) diff --git a/kernel/include/kernel/FS/Ext2.h b/kernel/include/kernel/FS/Ext2.h index 4bb4e7eb9c..9d295995ea 100644 --- a/kernel/include/kernel/FS/Ext2.h +++ b/kernel/include/kernel/FS/Ext2.h @@ -131,9 +131,12 @@ namespace Kernel virtual BAN::StringView name() const override { return m_name; } - virtual BAN::ErrorOr> read_all() const override; - virtual BAN::ErrorOr>> directory_inodes() const override; - virtual BAN::ErrorOr> directory_find(BAN::StringView) const override; + virtual BAN::ErrorOr> read_all() override; + virtual BAN::ErrorOr>> directory_inodes() override; + virtual BAN::ErrorOr> directory_find(BAN::StringView) override; + + private: + BAN::ErrorOr for_each_block(BAN::Function(const BAN::Vector&)>&); private: Ext2Inode() {} diff --git a/kernel/include/kernel/FS/Inode.h b/kernel/include/kernel/FS/Inode.h index 9cbc9d64d9..bb0af382b1 100644 --- a/kernel/include/kernel/FS/Inode.h +++ b/kernel/include/kernel/FS/Inode.h @@ -20,9 +20,9 @@ namespace Kernel virtual BAN::StringView name() const = 0; - virtual BAN::ErrorOr> read_all() const = 0; - virtual BAN::ErrorOr>> directory_inodes() const = 0; - virtual BAN::ErrorOr> directory_find(BAN::StringView) const = 0; + virtual BAN::ErrorOr> read_all() = 0; + virtual BAN::ErrorOr>> directory_inodes() = 0; + virtual BAN::ErrorOr> directory_find(BAN::StringView) = 0; }; } \ No newline at end of file diff --git a/kernel/kernel/FS/Ext2.cpp b/kernel/kernel/FS/Ext2.cpp index 1c6574e726..a7ab22493e 100644 --- a/kernel/kernel/FS/Ext2.cpp +++ b/kernel/kernel/FS/Ext2.cpp @@ -148,86 +148,182 @@ namespace Kernel return m_inode.mode & Ext2::Enum::IFREG; } - BAN::ErrorOr> Ext2Inode::read_all() const + BAN::ErrorOr Ext2Inode::for_each_block(BAN::Function(const BAN::Vector&)>& func) { - return BAN::Error::from_string("not implemented"); + uint32_t data_blocks_left = m_inode.blocks / (2 << m_fs->superblock().log_block_size); + uint32_t block_array_block_count = (1024 << m_fs->superblock().log_block_size) / sizeof(uint32_t); + + // Direct blocks + for (uint32_t i = 0; i < 12; i++) + { + if (m_inode.block[i] == 0) + continue; + + auto block_data = TRY(m_fs->read_block(m_inode.block[i])); + if (!TRY(func(block_data))) + return {}; + if (--data_blocks_left == 0) + return {}; + } + + // Singly indirect block + if (m_inode.block[12]) + { + auto block_array = TRY(m_fs->read_block(m_inode.block[12])); + for (uint32_t i = 0; i < block_array_block_count; i++) + { + uint32_t block = ((uint32_t*)block_array.data())[i]; + if (block == 0) + continue; + auto block_data = TRY(m_fs->read_block(block)); + if (!TRY(func(block_data))) + return {}; + if (--data_blocks_left == 0) + return {}; + } + } + + // Doubly indirect blocks + if (m_inode.block[13]) + { + auto singly_indirect_array = TRY(m_fs->read_block(m_inode.block[13])); + for (uint32_t i = 0; i < block_array_block_count; i++) + { + uint32_t singly_indirect_block = ((uint32_t*)singly_indirect_array.data())[i]; + auto block_array = TRY(m_fs->read_block(singly_indirect_block)); + for (uint32_t j = 0; j < block_array_block_count; j++) + { + uint32_t block = ((uint32_t*)block_array.data())[j]; + if (block == 0) + continue; + auto block_data = TRY(m_fs->read_block(block)); + if (!TRY(func(block_data))) + return {}; + if (--data_blocks_left == 0) + return {}; + } + } + } + + // Triply indirect blocks + if (m_inode.block[14]) + { + auto doubly_indirect_array = TRY(m_fs->read_block(m_inode.block[14])); + for (uint32_t i = 0; i < block_array_block_count; i++) + { + uint32_t doubly_indirect_block = ((uint32_t*)doubly_indirect_array.data())[i]; + auto singly_indirect_array = TRY(m_fs->read_block(doubly_indirect_block)); + for (uint32_t j = 0; j < block_array_block_count; j++) + { + uint32_t singly_indirect_block = ((uint32_t*)singly_indirect_array.data())[j]; + auto block_array = TRY(m_fs->read_block(singly_indirect_block)); + for (uint32_t k = 0; k < block_array_block_count; k++) + { + uint32_t block = ((uint32_t*)block_array.data())[k]; + if (block == 0) + continue; + auto block_data = TRY(m_fs->read_block(block)); + if (!TRY(func(block_data))) + return {}; + if (--data_blocks_left == 0) + return {}; + } + } + } + } + + return BAN::Error::from_string("Inode did not contain enough blocks"); } - BAN::ErrorOr> Ext2Inode::directory_find(BAN::StringView name) const + BAN::ErrorOr> Ext2Inode::read_all() + { + if (is_directory()) + return BAN::Error::from_string("Inode is a directory"); + + BAN::Vector data_buffer; + TRY(data_buffer.resize(m_inode.size)); + + uint32_t bytes_done = 0; + uint32_t bytes_left = m_inode.size; + + BAN::Function(const BAN::Vector&)> read_func( + [&](const BAN::Vector& block_data) + { + uint32_t to_copy = BAN::Math::min(block_data.size(), bytes_left); + memcpy(data_buffer.data() + bytes_done, block_data.data(), to_copy); + bytes_done += to_copy; + bytes_left -= to_copy; + return bytes_left > 0; + } + ); + + TRY(for_each_block(read_func)); + ASSERT(bytes_left == 0); + + return data_buffer; + } + + BAN::ErrorOr> Ext2Inode::directory_find(BAN::StringView file_name) { if (!is_directory()) return BAN::Error::from_string("Inode is not a directory"); - uint32_t data_block_count = m_inode.blocks / (2 << m_fs->superblock().log_block_size); - uint32_t data_blocks_found = 0; - - for (uint32_t data_block = 0; data_block < 12 && data_blocks_found < data_block_count; data_block++) - { - if (m_inode.block[0] == 0) - continue; - data_blocks_found++; - - auto inode_data = TRY(m_fs->read_block(m_inode.block[data_block])); - uintptr_t inode_data_end = (uintptr_t)inode_data.data() + inode_data.size(); - - uintptr_t entry_addr = (uintptr_t)inode_data.data(); - while (entry_addr < inode_data_end) + BAN::RefCounted result; + BAN::Function(const BAN::Vector&)> function( + [&](const BAN::Vector& block_data) -> BAN::ErrorOr { - Ext2::LinkedDirectoryEntry* entry = (Ext2::LinkedDirectoryEntry*)entry_addr; - - BAN::StringView entry_name = BAN::StringView(entry->name, entry->name_len); - if (entry->inode && name == entry_name) + uintptr_t block_data_end = (uintptr_t)block_data.data() + block_data.size(); + uintptr_t entry_addr = (uintptr_t)block_data.data(); + while (entry_addr < block_data_end) { - Ext2::Inode asked_inode = TRY(m_fs->read_inode(entry->inode)); - return BAN::RefCounted(new Ext2Inode(m_fs, BAN::move(asked_inode), entry_name)); + Ext2::LinkedDirectoryEntry* entry = (Ext2::LinkedDirectoryEntry*)entry_addr; + BAN::StringView entry_name = BAN::StringView(entry->name, entry->name_len); + if (entry->inode && file_name == entry_name) + { + Ext2::Inode asked_inode = TRY(m_fs->read_inode(entry->inode)); + result = BAN::RefCounted(new Ext2Inode(m_fs, BAN::move(asked_inode), entry_name)); + return false; + } + entry_addr += entry->rec_len; } - - entry_addr += entry->rec_len; + return true; } - } + ); + TRY(for_each_block(function)); + if (result) + return result; return BAN::Error::from_string("Could not find the asked inode"); } - BAN::ErrorOr>> Ext2Inode::directory_inodes() const + BAN::ErrorOr>> Ext2Inode::directory_inodes() { if (!is_directory()) return BAN::Error::from_string("Inode is not a directory"); - uint32_t data_block_count = m_inode.blocks / (2 << m_fs->superblock().log_block_size); - uint32_t data_blocks_found = 0; - BAN::Vector> inodes; - - // FIXME: implement indirect pointers - for (uint32_t data_block = 0; data_block < 12 && data_blocks_found < data_block_count; data_block++) - { - if (m_inode.block[0] == 0) - continue; - data_blocks_found++; - - auto inode_data = TRY(m_fs->read_block(m_inode.block[data_block])); - uintptr_t inode_data_end = (uintptr_t)inode_data.data() + inode_data.size(); - - uintptr_t entry_addr = (uintptr_t)inode_data.data(); - while (entry_addr < inode_data_end) + BAN::Function(const BAN::Vector&)> function( + [&](const BAN::Vector& block_data) -> BAN::ErrorOr { - Ext2::LinkedDirectoryEntry* entry = (Ext2::LinkedDirectoryEntry*)entry_addr; - - if (entry->inode) + uintptr_t block_data_end = (uintptr_t)block_data.data() + block_data.size(); + uintptr_t entry_addr = (uintptr_t)block_data.data(); + while (entry_addr < block_data_end) { - BAN::StringView entry_name = BAN::StringView(entry->name, entry->name_len); - Ext2::Inode current_inode = TRY(m_fs->read_inode(entry->inode)); - auto ref_counted_inode = BAN::RefCounted(new Ext2Inode(m_fs, BAN::move(current_inode), entry_name)); - TRY(inodes.push_back(BAN::move(ref_counted_inode))); + Ext2::LinkedDirectoryEntry* entry = (Ext2::LinkedDirectoryEntry*)entry_addr; + if (entry->inode) + { + BAN::StringView entry_name = BAN::StringView(entry->name, entry->name_len); + Ext2::Inode current_inode = TRY(m_fs->read_inode(entry->inode)); + auto ref_counted_inode = BAN::RefCounted(new Ext2Inode(m_fs, BAN::move(current_inode), entry_name)); + TRY(inodes.push_back(BAN::move(ref_counted_inode))); + } + entry_addr += entry->rec_len; } - - entry_addr += entry->rec_len; + return true; } - } + ); - // FIXME: for now we can just assert that we found everything in direct pointers - ASSERT(data_blocks_found == data_block_count); + TRY(for_each_block(function)); return inodes; } diff --git a/kernel/kernel/Shell.cpp b/kernel/kernel/Shell.cpp index be138f91bc..652c59cb1b 100644 --- a/kernel/kernel/Shell.cpp +++ b/kernel/kernel/Shell.cpp @@ -300,6 +300,32 @@ argument_done: if (!inode->is_directory()) TTY_PRINTLN(" {7} {}", inode->size(), inode->name()); } + else if (arguments.front() == "cat") + { + if (!VirtualFileSystem::is_initialized()) + return TTY_PRINTLN("VFS not initialized :("); + + if (arguments.size() > 2) + return TTY_PRINTLN("usage: 'cat path'"); + + auto file = VirtualFileSystem::get().root_inode(); + + auto path_parts = MUST(arguments[1].sv().split('/')); + for (auto part : path_parts) + { + auto inode_or_error = file->directory_find(part); + if (inode_or_error.is_error()) + return TTY_PRINTLN("{}", inode_or_error.get_error().get_message()); + file = inode_or_error.value(); + } + + auto data_or_error = file->read_all(); + if (data_or_error.is_error()) + return TTY_PRINTLN("{}", data_or_error.get_error().get_message()); + + auto& data = data_or_error.value(); + TTY_PRINTLN("{}", BAN::StringView((const char*)data.data(), data.size())); + } else { TTY_PRINTLN("unrecognized command '{}'", arguments.front());