From 75c4f35e85bbc479c63fc4f7a34b8888acfb8343 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Thu, 23 Mar 2023 22:26:06 +0200 Subject: [PATCH] Kernel: Add possibiliity to create empty files on Ext2 Big rewrite for Ext2 for more easy and optimized code --- kernel/include/kernel/FS/Ext2.h | 27 +++- kernel/include/kernel/FS/Inode.h | 54 ++++---- kernel/include/kernel/Process.h | 1 + kernel/kernel/FS/Ext2.cpp | 223 ++++++++++++++++++++++++------- kernel/kernel/Process.cpp | 12 ++ kernel/kernel/Shell.cpp | 28 ++-- 6 files changed, 250 insertions(+), 95 deletions(-) diff --git a/kernel/include/kernel/FS/Ext2.h b/kernel/include/kernel/FS/Ext2.h index b2b36fde18..9eac447489 100644 --- a/kernel/include/kernel/FS/Ext2.h +++ b/kernel/include/kernel/FS/Ext2.h @@ -81,6 +81,8 @@ namespace Kernel uint16_t free_blocks_count; uint16_t free_inodes_count; uint16_t used_dirs_count; + uint8_t __padding[2]; + uint8_t __reserved[12]; }; struct Inode @@ -121,16 +123,18 @@ namespace Kernel class Ext2Inode : public Inode { public: - virtual uint16_t uid() const override { return m_inode.uid; } - virtual uint16_t gid() const override { return m_inode.gid; } - virtual uint32_t size() const override { return m_inode.size; } + virtual uid_t uid() const override { return m_inode.uid; } + virtual gid_t gid() const override { return m_inode.gid; } + virtual size_t size() const override { return m_inode.size; } - virtual Mode mode() const override { return { .mode = m_inode.mode }; } + virtual mode_t mode() const override { return m_inode.mode; } virtual BAN::StringView name() const override { return m_name; } virtual BAN::ErrorOr read(size_t, void*, size_t) override; + virtual BAN::ErrorOr create_file(BAN::StringView, mode_t) override; + virtual Type type() const override { return Type::Ext2; } virtual bool operator==(const Inode& other) const override; @@ -177,14 +181,23 @@ namespace Kernel BAN::ErrorOr initialize_superblock(); BAN::ErrorOr initialize_root_inode(); - BAN::ErrorOr read_inode(uint32_t); + BAN::ErrorOr create_inode(const Ext2::Inode&); + BAN::ErrorOr delete_inode(uint32_t); + BAN::ErrorOr resize_inode(uint32_t, size_t); + BAN::ErrorOr> read_block(uint32_t); BAN::ErrorOr write_block(uint32_t, BAN::Span); - BAN::ErrorOr read_block_group_descriptor(uint32_t); - const Ext2::Superblock& superblock() const { return m_superblock; } + struct BlockLocation + { + uint32_t block; + uint32_t offset; + }; + BAN::ErrorOr locate_inode(uint32_t); + BlockLocation locate_block_group_descriptior(uint32_t); + uint32_t block_size() const { return 1024 << superblock().log_block_size; } private: diff --git a/kernel/include/kernel/FS/Inode.h b/kernel/include/kernel/FS/Inode.h index 827d66e001..f00016496f 100644 --- a/kernel/include/kernel/FS/Inode.h +++ b/kernel/include/kernel/FS/Inode.h @@ -4,34 +4,32 @@ #include #include +#include + namespace Kernel { class Inode : public BAN::RefCounted { public: - union Mode + enum Mode : mode_t { - struct - { - uint16_t IXOTH : 1; // 0x0001 - uint16_t IWOTH : 1; // 0x0002 - uint16_t IROTH : 1; // 0x0004 - uint16_t IXGRP : 1; // 0x0008 - uint16_t IWGRP : 1; // 0x0010 - uint16_t IRGRP : 1; // 0x0020 - uint16_t IXUSR : 1; // 0x0040 - uint16_t IWUSR : 1; // 0x0080 - uint16_t IRUSR : 1; // 0x0100 - uint16_t ISVTX : 1; // 0x0200 - uint16_t ISGID : 1; // 0x0400 - uint16_t ISUID : 1; // 0x0800 - uint16_t IFIFO : 1; // 0x1000 - uint16_t IFCHR : 1; // 0x2000 - uint16_t IFDIR : 1; // 0x4000 - uint16_t IFREG : 1; // 0x8000 - }; - uint16_t mode; + IXOTH = 0x0001, + IWOTH = 0x0002, + IROTH = 0x0004, + IXGRP = 0x0008, + IWGRP = 0x0010, + IRGRP = 0x0020, + IXUSR = 0x0040, + IWUSR = 0x0080, + IRUSR = 0x0100, + ISVTX = 0x0200, + ISGID = 0x0400, + ISUID = 0x0800, + IFIFO = 0x1000, + IFCHR = 0x2000, + IFDIR = 0x4000, + IFREG = 0x8000, }; enum class Type @@ -42,14 +40,14 @@ namespace Kernel public: virtual ~Inode() {} - bool ifdir() const { return mode().IFDIR; } - bool ifreg() const { return mode().IFREG; } + bool ifdir() const { return mode() & Mode::IFDIR; } + bool ifreg() const { return mode() & Mode::IFREG; } - virtual uint16_t uid() const = 0; - virtual uint16_t gid() const = 0; - virtual uint32_t size() const = 0; + virtual uid_t uid() const = 0; + virtual gid_t gid() const = 0; + virtual size_t size() const = 0; - virtual Mode mode() const = 0; + virtual mode_t mode() const = 0; virtual BAN::StringView name() const = 0; @@ -58,6 +56,8 @@ namespace Kernel virtual BAN::ErrorOr read(size_t, void*, size_t) = 0; + virtual BAN::ErrorOr create_file(BAN::StringView, mode_t) = 0; + virtual Type type() const = 0; virtual bool operator==(const Inode&) const = 0; diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index f968d37565..5c580fbe00 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -29,6 +29,7 @@ namespace Kernel BAN::ErrorOr open(BAN::StringView, int); BAN::ErrorOr close(int); BAN::ErrorOr read(int, void*, size_t); + BAN::ErrorOr creat(BAN::StringView, mode_t); BAN::StringView working_directory() const { return m_working_directory; } BAN::ErrorOr set_working_directory(BAN::StringView); diff --git a/kernel/kernel/FS/Ext2.cpp b/kernel/kernel/FS/Ext2.cpp index ddaa914399..3be945ed17 100644 --- a/kernel/kernel/FS/Ext2.cpp +++ b/kernel/kernel/FS/Ext2.cpp @@ -1,8 +1,10 @@ #include #include #include +#include #define EXT2_DEBUG_PRINT 0 +#define VERIFY_INODE_EXISTANCE 1 namespace Kernel { @@ -134,12 +136,26 @@ namespace Kernel RESERVED_FL = 0x80000000, }; + enum FileType + { + UNKNOWN = 0, + REG_FILE = 1, + DIR = 2, + CHRDEV = 3, + BLKDEV = 4, + FIFO = 5, + SOCK = 6, + SYMLINK = 7, + }; + } - BAN::ErrorOr> Ext2Inode::create(Ext2FS& fs, uint32_t inode, BAN::StringView name) + BAN::ErrorOr> Ext2Inode::create(Ext2FS& fs, uint32_t inode_inode, BAN::StringView name) { - Ext2::Inode ext2_inode = TRY(fs.read_inode(inode)); - Ext2Inode* result = new Ext2Inode(fs, ext2_inode, name, inode); + auto inode_location = TRY(fs.locate_inode(inode_inode)); + auto inode_buffer = TRY(fs.read_block(inode_location.block)); + auto& inode = *(Ext2::Inode*)(inode_buffer.data() + inode_location.offset); + Ext2Inode* result = new Ext2Inode(fs, inode, name, inode_inode); if (result == nullptr) return BAN::Error::from_errno(ENOMEM); return BAN::RefPtr::adopt(result); @@ -250,6 +266,87 @@ namespace Kernel return n_read; } + BAN::ErrorOr Ext2Inode::create_file(BAN::StringView name, mode_t mode) + { + if (!ifdir()) + return BAN::Error::from_errno(ENOTDIR); + + if (name.size() > 255) + return BAN::Error::from_errno(ENAMETOOLONG); + + auto error_or = directory_find_impl(name); + if (!error_or.is_error()) + return BAN::Error::from_errno(EEXISTS); + if (error_or.error().get_error_code() != ENOENT) + return error_or.error(); + + uint64_t current_time = RTC::get_unix_time(); + + Ext2::Inode ext2_inode; + ext2_inode.mode = mode; + ext2_inode.uid = 0; + ext2_inode.size = 0; + ext2_inode.atime = current_time; + ext2_inode.ctime = current_time; + ext2_inode.mtime = current_time; + ext2_inode.dtime = current_time; + ext2_inode.gid = 0; + ext2_inode.links_count = 1; + ext2_inode.blocks = 0; + ext2_inode.flags = 0; + ext2_inode.osd1 = 0; + memset(ext2_inode.block, 0, sizeof(ext2_inode.block)); + ext2_inode.generation = 0; + ext2_inode.file_acl = 0; + ext2_inode.dir_acl = 0; + ext2_inode.faddr = 0; + memset(ext2_inode.osd2, 0, sizeof(ext2_inode.osd2)); + + uint32_t inode_index = TRY(m_fs.create_inode(ext2_inode)); + + // Insert inode to this directory + uint32_t data_block_count = m_inode.blocks / (2 << m_fs.superblock().log_block_size); + uint32_t block_index = TRY(data_block_index(data_block_count - 1)); + auto block_data = TRY(m_fs.read_block(block_index)); + + const uint8_t* block_data_end = block_data.data() + block_data.size(); + const uint8_t* entry_addr = block_data.data(); + + uint32_t needed_entry_len = sizeof(Ext2::LinkedDirectoryEntry) + name.size(); + + bool insered = false; + while (entry_addr < block_data_end) + { + auto& entry = *(Ext2::LinkedDirectoryEntry*)entry_addr; + + if (needed_entry_len <= entry.rec_len - entry.name_len - sizeof(Ext2::LinkedDirectoryEntry)) + { + entry.rec_len = sizeof(Ext2::LinkedDirectoryEntry) + entry.name_len; + if (uint32_t rem = entry.rec_len % 4) + entry.rec_len += 4 - rem; + + auto& new_entry = *(Ext2::LinkedDirectoryEntry*)(entry_addr + entry.rec_len); + new_entry.inode = inode_index; + new_entry.rec_len = block_data_end - (uint8_t*)&new_entry; + new_entry.name_len = name.size(); + new_entry.file_type = Ext2::Enum::REG_FILE; + memcpy(new_entry.name, name.data(), name.size()); + + TRY(m_fs.write_block(block_index, block_data.span())); + + insered = true; + break; + } + entry_addr += entry.rec_len; + } + + // FIXME: If an entry cannot completely fit in one block, it must be pushed to the + // next data block and the rec_len of the previous entry properly adjusted. + ASSERT(insered); + + return {}; + } + BAN::ErrorOr> Ext2Inode::directory_find_impl(BAN::StringView file_name) { if (!ifdir()) @@ -350,7 +447,7 @@ namespace Kernel } if (m_superblock.magic != 0xEF53) - return BAN::Error::from_c_string("Not a ext2 filesystem"); + return BAN::Error::from_c_string("Not an ext2 filesystem"); if (m_superblock.rev_level < 1) { @@ -359,6 +456,11 @@ namespace Kernel m_superblock.inode_size = 128; } + uint32_t number_of_block_groups = BAN::Math::div_round_up(superblock().inodes_count, superblock().inodes_per_group); + uint32_t number_of_block_groups_check = BAN::Math::div_round_up(superblock().blocks_count, superblock().blocks_per_group); + if (number_of_block_groups != number_of_block_groups_check) + return BAN::Error::from_c_string("Ambiguous number of block groups"); + ASSERT(!(m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_COMPRESSION)); //ASSERT(!(m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_FILETYPE)); ASSERT(!(m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_JOURNAL_DEV)); @@ -390,38 +492,39 @@ namespace Kernel return {}; } - BAN::ErrorOr Ext2FS::find_free_inode_index() + BAN::ErrorOr Ext2FS::create_inode(const Ext2::Inode& ext2_inode) { - ASSERT(false); - return 0; - } + ASSERT(ext2_inode.size == 0); - BAN::ErrorOr Ext2FS::read_inode(uint32_t inode_index) - { - if (inode_index >= superblock().inodes_count) - return BAN::Error::from_format("Asked to read inode {}, but only {} exist in the filesystem", inode_index, superblock().inodes_count); + uint32_t number_of_block_groups = BAN::Math::div_round_up(superblock().inodes_count, superblock().inodes_per_group); + for (uint32_t group = 0; group < number_of_block_groups; group++) + { + auto bgd_location = this->locate_block_group_descriptior(group); + auto bgd_buffer = TRY(this->read_block(bgd_location.block)); - uint32_t block_size = this->block_size(); + auto& bgd = *(Ext2::BlockGroupDescriptor*)(bgd_buffer.data() + bgd_location.offset); + if (bgd.free_inodes_count == 0) + continue; - uint32_t inode_block_group = (inode_index - 1) / superblock().inodes_per_group; - uint32_t local_inode_index = (inode_index - 1) % superblock().inodes_per_group; + auto inode_bitmap = TRY(read_block(bgd.inode_bitmap)); + for (uint32_t inode_offset = 0; inode_offset < superblock().inodes_per_group; inode_offset++) + { + uint32_t byte = inode_offset / 8; + uint32_t bit = inode_offset % 8; + if ((inode_bitmap[byte] & (1 << bit)) == 0) + { + inode_bitmap[byte] |= (1 << bit); + TRY(write_block(bgd.inode_bitmap, inode_bitmap.span())); - uint32_t inode_table_byte_offset = (local_inode_index * superblock().inode_size); + bgd.free_inodes_count--; + TRY(write_block(bgd_location.block, bgd_buffer.span())); - auto block_group_descriptor = TRY(read_block_group_descriptor(inode_block_group)); + return group * superblock().inodes_per_group + inode_offset + 1; + } + } + } - uint32_t inode_block = block_group_descriptor.inode_table + inode_table_byte_offset / block_size; - auto inode_block_buffer = TRY(read_block(inode_block)); - - Ext2::Inode ext2_inode; - memcpy(&ext2_inode, inode_block_buffer.data() + inode_table_byte_offset % block_size, sizeof(Ext2::Inode)); - return ext2_inode; - } - - BAN::ErrorOr Ext2FS::write_inode(uint32_t inode_index, const Ext2::Inode& ext2_inode) - { - ASSERT(false); - return {}; + return BAN::Error::from_c_string("No free inodes available in the whole filesystem"); } BAN::ErrorOr> Ext2FS::read_block(uint32_t block) @@ -439,40 +542,60 @@ namespace Kernel BAN::ErrorOr Ext2FS::write_block(uint32_t block, BAN::Span data) { - const uint32_t sector_size = m_partition.device().sector_size(); - const uint32_t block_size = 1024 << m_superblock.log_block_size; - ASSERT(block_size % sector_size == 0); - ASSERT(data.size() <= block_size); - const uint32_t sectors_per_block = block_size / sector_size; + uint32_t sector_size = m_partition.device().sector_size(); + uint32_t block_size = this->block_size(); + uint32_t sectors_per_block = block_size / sector_size; + ASSERT(data.size() <= block_size); TRY(m_partition.write_sectors(block * sectors_per_block, sectors_per_block, data.data())); return {}; } - BAN::ErrorOr Ext2FS::read_block_group_descriptor(uint32_t index) - { + BAN::ErrorOr Ext2FS::locate_inode(uint32_t inode_index) + { + if (inode_index >= superblock().inodes_count) + return BAN::Error::from_format("Asked to read inode {}, but only {} exist in the filesystem", inode_index, superblock().inodes_count); + uint32_t block_size = this->block_size(); - uint32_t number_of_block_groups = BAN::Math::div_round_up(superblock().inodes_count, superblock().inodes_per_group); - uint32_t number_of_block_groups_check = BAN::Math::div_round_up(superblock().blocks_count, superblock().blocks_per_group); - if (number_of_block_groups != number_of_block_groups_check) - return BAN::Error::from_c_string("Ambiguous number of block groups"); - - ASSERT(index < number_of_block_groups); + uint32_t inode_block_group = (inode_index - 1) / superblock().inodes_per_group; + uint32_t local_inode_index = (inode_index - 1) % superblock().inodes_per_group; - // NOTE: We use 32 because we cannot use the sizeof(Ext2::BlockGroupDescriptor) since I have - // left out 14 bytes of padding/reserved memory from the end of the structure - uint32_t block_group_descriptor_byte_offset = 32u * index; + uint32_t inode_table_byte_offset = (local_inode_index * superblock().inode_size); - uint32_t block_group_descriptor_block = superblock().first_data_block + block_group_descriptor_byte_offset / block_size + 1; + auto bgd_location = locate_block_group_descriptior(inode_block_group); + auto bgd_buffer = TRY(read_block(bgd_location.block)); + auto& bgd = *(Ext2::BlockGroupDescriptor*)(bgd_buffer.data() + bgd_location.offset); - auto block_data = TRY(read_block(block_group_descriptor_block)); +#if VERIFY_INODE_EXISTANCE + ASSERT(superblock().inodes_per_group <= block_size * 8); + auto inode_bitmap = TRY(read_block(bgd.inode_bitmap)); + uint32_t byte = local_inode_index / 8; + uint32_t bit = local_inode_index % 8; + ASSERT(inode_bitmap[byte] & (1 << bit)); +#endif - Ext2::BlockGroupDescriptor result; - memcpy(&result, block_data.data() + block_group_descriptor_byte_offset % block_size, sizeof(Ext2::BlockGroupDescriptor)); + BlockLocation location; + location.block = bgd.inode_table + inode_table_byte_offset / block_size; + location.offset = inode_table_byte_offset % block_size; + return location; + } - return result; + Ext2FS::BlockLocation Ext2FS::locate_block_group_descriptior(uint32_t group_index) + { + uint32_t block_size = this->block_size(); + + uint32_t block_group_count = BAN::Math::div_round_up(superblock().inodes_count, superblock().inodes_per_group); + ASSERT(group_index < block_group_count); + + uint32_t bgd_byte_offset = sizeof(Ext2::BlockGroupDescriptor) * group_index; + uint32_t bgd_table_block = superblock().first_data_block + (bgd_byte_offset / block_size) + 1; + + BlockLocation location; + location.block = bgd_table_block + (bgd_byte_offset / block_size); + location.offset = (bgd_byte_offset % block_size); + return location; } } \ No newline at end of file diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index 208e37bfda..7e1a7e0062 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -72,6 +72,18 @@ namespace Kernel return n_read; } + BAN::ErrorOr Process::creat(BAN::StringView path, mode_t mode) + { + auto absolute_path = TRY(absolute_path_of(path)); + while (absolute_path.sv().back() != '/') + absolute_path.pop_back(); + auto parent_inode = TRY(VirtualFileSystem::get().file_from_absolute_path(absolute_path)); + if (path.count('/') > 0) + return BAN::Error::from_c_string("You can only create files to current working directory"); + TRY(parent_inode.inode->create_file(path, mode)); + return {}; + } + Inode& Process::inode_for_fd(int fd) { MUST(validate_fd(fd)); diff --git a/kernel/kernel/Shell.cpp b/kernel/kernel/Shell.cpp index 13ce3703a8..aa675cd3b0 100644 --- a/kernel/kernel/Shell.cpp +++ b/kernel/kernel/Shell.cpp @@ -364,19 +364,19 @@ argument_done: auto& directory = Process::current()->inode_for_fd(fd); auto inodes = TRY(directory.directory_inodes()); - auto mode_string = [](Inode::Mode mode) + auto mode_string = [](mode_t mode) { static char buffer[11] {}; - buffer[0] = mode.IFDIR ? 'd' : '-'; - buffer[1] = mode.IRUSR ? 'r' : '-'; - buffer[2] = mode.IWUSR ? 'w' : '-'; - buffer[3] = mode.IXUSR ? 'x' : '-'; - buffer[4] = mode.IRGRP ? 'r' : '-'; - buffer[5] = mode.IWGRP ? 'w' : '-'; - buffer[6] = mode.IXGRP ? 'x' : '-'; - buffer[7] = mode.IROTH ? 'r' : '-'; - buffer[8] = mode.IWOTH ? 'w' : '-'; - buffer[9] = mode.IXOTH ? 'x' : '-'; + buffer[0] = (mode & Inode::Mode::IFDIR) ? 'd' : '-'; + buffer[1] = (mode & Inode::Mode::IRUSR) ? 'r' : '-'; + buffer[2] = (mode & Inode::Mode::IWUSR) ? 'w' : '-'; + buffer[3] = (mode & Inode::Mode::IXUSR) ? 'x' : '-'; + buffer[4] = (mode & Inode::Mode::IRGRP) ? 'r' : '-'; + buffer[5] = (mode & Inode::Mode::IWGRP) ? 'w' : '-'; + buffer[6] = (mode & Inode::Mode::IXGRP) ? 'x' : '-'; + buffer[7] = (mode & Inode::Mode::IROTH) ? 'r' : '-'; + buffer[8] = (mode & Inode::Mode::IWOTH) ? 'w' : '-'; + buffer[9] = (mode & Inode::Mode::IXOTH) ? 'x' : '-'; return (const char*)buffer; }; @@ -413,6 +413,12 @@ argument_done: TRY(Process::current()->set_working_directory(path)); TRY(update_prompt()); } + else if (arguments.front() == "touch") + { + if (arguments.size() != 2) + return BAN::Error::from_c_string("usage 'touch path'"); + TRY(Process::current()->creat(arguments[1], 0)); + } else if (arguments.front() == "cksum") { if (arguments.size() < 2)