From a711462ef4f1321e91661809576d84cb4462eb64 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Fri, 1 Sep 2023 15:10:23 +0300 Subject: [PATCH] Kernel: Split ext2 implementation to multiple files --- kernel/CMakeLists.txt | 3 +- kernel/include/kernel/FS/Ext2.h | 225 -------- kernel/include/kernel/FS/Ext2/Definitions.h | 262 +++++++++ kernel/include/kernel/FS/Ext2/FileSystem.h | 58 ++ kernel/include/kernel/FS/Ext2/Inode.h | 67 +++ kernel/kernel/FS/Ext2/FileSystem.cpp | 396 +++++++++++++ kernel/kernel/FS/{Ext2.cpp => Ext2/Inode.cpp} | 539 +----------------- kernel/kernel/FS/VirtualFileSystem.cpp | 2 +- kernel/kernel/Storage/StorageDevice.cpp | 1 - 9 files changed, 791 insertions(+), 762 deletions(-) delete mode 100644 kernel/include/kernel/FS/Ext2.h create mode 100644 kernel/include/kernel/FS/Ext2/Definitions.h create mode 100644 kernel/include/kernel/FS/Ext2/FileSystem.h create mode 100644 kernel/include/kernel/FS/Ext2/Inode.h create mode 100644 kernel/kernel/FS/Ext2/FileSystem.cpp rename kernel/kernel/FS/{Ext2.cpp => Ext2/Inode.cpp} (50%) diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 4e1823a1..72c04b85 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -19,7 +19,8 @@ set(KERNEL_SOURCES kernel/Errors.cpp kernel/Font.cpp kernel/FS/DevFS/FileSystem.cpp - kernel/FS/Ext2.cpp + kernel/FS/Ext2/FileSystem.cpp + kernel/FS/Ext2/Inode.cpp kernel/FS/Inode.cpp kernel/FS/Pipe.cpp kernel/FS/RamFS/FileSystem.cpp diff --git a/kernel/include/kernel/FS/Ext2.h b/kernel/include/kernel/FS/Ext2.h deleted file mode 100644 index 111469c4..00000000 --- a/kernel/include/kernel/FS/Ext2.h +++ /dev/null @@ -1,225 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace Kernel -{ - - namespace Ext2 - { - - struct Superblock - { - uint32_t inodes_count; - uint32_t blocks_count; - uint32_t r_blocks_count; - uint32_t free_blocks_count; - uint32_t free_inodes_count; - uint32_t first_data_block; - uint32_t log_block_size; - uint32_t log_frag_size; - uint32_t blocks_per_group; - uint32_t frags_per_group; - uint32_t inodes_per_group; - uint32_t mtime; - uint32_t wtime; - uint16_t mnt_count; - uint16_t max_mnt_count; - uint16_t magic; - uint16_t state; - uint16_t errors; - uint16_t minor_rev_level; - uint32_t lastcheck; - uint32_t checkinterval; - uint32_t creator_os; - uint32_t rev_level; - uint16_t def_resuid; - uint16_t def_resgid; - - // -- EXT2_DYNAMIC_REV Specific -- - uint8_t __extension_start[0]; - uint32_t first_ino; - uint16_t inode_size; - uint16_t block_group_nr; - uint32_t feature_compat; - uint32_t feature_incompat; - uint32_t feature_ro_compat; - uint8_t uuid[16]; - uint8_t volume_name[16]; - char last_mounted[64]; - uint32_t algo_bitmap; - - // -- Performance Hints -- - uint8_t s_prealloc_blocks; - uint8_t s_prealloc_dir_blocks; - uint16_t __alignment; - - // -- Journaling Support -- - uint8_t journal_uuid[16]; - uint32_t journal_inum; - uint32_t journal_dev; - uint32_t last_orphan; - - // -- Directory Indexing Support -- - uint32_t hash_seed[4]; - uint8_t def_hash_version; - uint8_t __padding[3]; - - // -- Other options -- - uint32_t default_mount_options; - uint32_t first_meta_bg; - }; - - struct BlockGroupDescriptor - { - uint32_t block_bitmap; - uint32_t inode_bitmap; - uint32_t inode_table; - 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 - { - uint16_t mode; - uint16_t uid; - uint32_t size; - uint32_t atime; - uint32_t ctime; - uint32_t mtime; - uint32_t dtime; - uint16_t gid; - uint16_t links_count; - uint32_t blocks; - uint32_t flags; - uint32_t osd1; - uint32_t block[15]; - uint32_t generation; - uint32_t file_acl; - uint32_t dir_acl; - uint32_t faddr; - uint32_t osd2[3]; - }; - - struct LinkedDirectoryEntry - { - uint32_t inode; - uint16_t rec_len; - uint8_t name_len; - uint8_t file_type; - char name[0]; - }; - - } - - class Ext2FS; - - class Ext2Inode final : public Inode - { - public: - virtual ino_t ino() const override { return m_ino; }; - virtual Mode mode() const override { return { m_inode.mode }; } - virtual nlink_t nlink() const override { return m_inode.links_count; } - virtual uid_t uid() const override { return m_inode.uid; } - virtual gid_t gid() const override { return m_inode.gid; } - virtual off_t size() const override { return m_inode.size; } - virtual timespec atime() const override { return timespec { .tv_sec = m_inode.atime, .tv_nsec = 0 }; } - virtual timespec mtime() const override { return timespec { .tv_sec = m_inode.mtime, .tv_nsec = 0 }; } - virtual timespec ctime() const override { return timespec { .tv_sec = m_inode.ctime, .tv_nsec = 0 }; } - virtual blksize_t blksize() const override; - virtual blkcnt_t blocks() const override; - virtual dev_t dev() const override { return 0; } - virtual dev_t rdev() const override { return 0; } - - virtual BAN::ErrorOr link_target() override; - - virtual BAN::ErrorOr directory_read_next_entries(off_t, DirectoryEntryList*, size_t) override; - virtual BAN::ErrorOr> directory_find_inode(BAN::StringView) override; - - virtual BAN::ErrorOr read(size_t, void*, size_t) override; - virtual BAN::ErrorOr write(size_t, const void*, size_t) override; - virtual BAN::ErrorOr truncate(size_t) override; - - virtual BAN::ErrorOr create_file(BAN::StringView, mode_t, uid_t, gid_t) override; - - private: - BAN::ErrorOr for_data_block_index(uint32_t, const BAN::Function&, bool allocate); - - BAN::ErrorOr data_block_index(uint32_t); - BAN::ErrorOr allocate_new_block(); - BAN::ErrorOr sync(); - - uint32_t block_group() const; - - private: - Ext2Inode(Ext2FS& fs, Ext2::Inode inode, uint32_t ino) - : m_fs(fs) - , m_inode(inode) - , m_ino(ino) - {} - static BAN::ErrorOr> create(Ext2FS&, uint32_t); - - private: - Ext2FS& m_fs; - Ext2::Inode m_inode; - const uint32_t m_ino; - - friend class Ext2FS; - friend class BAN::RefPtr; - }; - - class Ext2FS final : public FileSystem - { - public: - static BAN::ErrorOr create(Partition&); - - virtual BAN::RefPtr root_inode() override { return m_root_inode; } - - private: - Ext2FS(Partition& partition) - : m_partition(partition) - {} - - BAN::ErrorOr initialize_superblock(); - BAN::ErrorOr initialize_root_inode(); - - BAN::ErrorOr create_inode(const Ext2::Inode&); - BAN::ErrorOr delete_inode(uint32_t); - BAN::ErrorOr resize_inode(uint32_t, size_t); - - void read_block(uint32_t, BAN::Span); - void write_block(uint32_t, BAN::Span); - void sync_superblock(); - - BAN::ErrorOr reserve_free_block(uint32_t primary_bgd); - - 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: - Partition& m_partition; - - BAN::RefPtr m_root_inode; - BAN::Vector m_superblock_backups; - - Ext2::Superblock m_superblock; - - friend class Ext2Inode; - }; - -} \ No newline at end of file diff --git a/kernel/include/kernel/FS/Ext2/Definitions.h b/kernel/include/kernel/FS/Ext2/Definitions.h new file mode 100644 index 00000000..24bd476e --- /dev/null +++ b/kernel/include/kernel/FS/Ext2/Definitions.h @@ -0,0 +1,262 @@ +#pragma once + +#include + +namespace Kernel::Ext2 +{ + + struct Superblock + { + uint32_t inodes_count; + uint32_t blocks_count; + uint32_t r_blocks_count; + uint32_t free_blocks_count; + uint32_t free_inodes_count; + uint32_t first_data_block; + uint32_t log_block_size; + uint32_t log_frag_size; + uint32_t blocks_per_group; + uint32_t frags_per_group; + uint32_t inodes_per_group; + uint32_t mtime; + uint32_t wtime; + uint16_t mnt_count; + uint16_t max_mnt_count; + uint16_t magic; + uint16_t state; + uint16_t errors; + uint16_t minor_rev_level; + uint32_t lastcheck; + uint32_t checkinterval; + uint32_t creator_os; + uint32_t rev_level; + uint16_t def_resuid; + uint16_t def_resgid; + + // -- EXT2_DYNAMIC_REV Specific -- + uint8_t __extension_start[0]; + uint32_t first_ino; + uint16_t inode_size; + uint16_t block_group_nr; + uint32_t feature_compat; + uint32_t feature_incompat; + uint32_t feature_ro_compat; + uint8_t uuid[16]; + uint8_t volume_name[16]; + char last_mounted[64]; + uint32_t algo_bitmap; + + // -- Performance Hints -- + uint8_t s_prealloc_blocks; + uint8_t s_prealloc_dir_blocks; + uint16_t __alignment; + + // -- Journaling Support -- + uint8_t journal_uuid[16]; + uint32_t journal_inum; + uint32_t journal_dev; + uint32_t last_orphan; + + // -- Directory Indexing Support -- + uint32_t hash_seed[4]; + uint8_t def_hash_version; + uint8_t __padding[3]; + + // -- Other options -- + uint32_t default_mount_options; + uint32_t first_meta_bg; + }; + + struct BlockGroupDescriptor + { + uint32_t block_bitmap; + uint32_t inode_bitmap; + uint32_t inode_table; + 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 + { + uint16_t mode; + uint16_t uid; + uint32_t size; + uint32_t atime; + uint32_t ctime; + uint32_t mtime; + uint32_t dtime; + uint16_t gid; + uint16_t links_count; + uint32_t blocks; + uint32_t flags; + uint32_t osd1; + uint32_t block[15]; + uint32_t generation; + uint32_t file_acl; + uint32_t dir_acl; + uint32_t faddr; + uint32_t osd2[3]; + }; + + struct LinkedDirectoryEntry + { + uint32_t inode; + uint16_t rec_len; + uint8_t name_len; + uint8_t file_type; + char name[0]; + }; + + namespace Enum + { + + constexpr uint16_t SUPER_MAGIC = 0xEF53; + + enum State + { + VALID_FS = 1, + ERROR_FS = 2, + }; + + enum Errors + { + ERRORS_CONTINUE = 1, + ERRORS_RO = 2, + ERRORS_PANIC = 3, + }; + + enum CreatorOS + { + OS_LINUX = 0, + OS_HURD = 1, + OS_MASIX = 2, + OS_FREEBSD = 3, + OS_LITES = 4, + }; + + enum RevLevel + { + GOOD_OLD_REV = 0, + DYNAMIC_REV = 1, + }; + + enum Rev0Constant + { + GOOD_OLD_FIRST_INO = 11, + GOOD_OLD_INODE_SIZE = 128, + }; + + enum FeatureCompat + { + FEATURE_COMPAT_DIR_PREALLOC = 0x0001, + FEATURE_COMPAT_IMAGIC_INODES = 0x0002, + FEATURE_COMPAT_HAS_JOURNAL = 0x0004, + FEATURE_COMPAT_EXT_ATTR = 0x0008, + FEATURE_COMPAT_RESIZE_INO = 0x0010, + FEATURE_COMPAT_DIR_INDEX = 0x0020, + }; + + enum FeaturesIncompat + { + FEATURE_INCOMPAT_COMPRESSION = 0x0001, + FEATURE_INCOMPAT_FILETYPE = 0x0002, + FEATURE_INCOMPAT_RECOVER = 0x0004, + FEATURE_INCOMPAT_JOURNAL_DEV = 0x0008, + FEATURE_INCOMPAT_META_BG = 0x0010, + }; + + enum FeaturesRoCompat + { + FEATURE_RO_COMPAT_SPARSE_SUPER = 0x0001, + FEATURE_RO_COMPAT_LARGE_FILE = 0x0002, + FEATURE_RO_COMPAT_BTREE_DIR = 0x0004, + }; + + enum AlgoBitmap + { + LZV1_ALG = 0, + LZRW3A_ALG = 1, + GZIP_ALG = 2, + BZIP2_ALG = 3, + LZO_ALG = 4, + }; + + enum ReservedInodes + { + BAD_INO = 1, + ROOT_INO = 2, + ACL_IDX_INO = 3, + ACL_DATA_INO = 4, + BOOT_LOADER_INO = 5, + UNDEL_DIR_INO = 6, + }; + + enum InodeMode + { + // -- file format -- + IFSOCK = 0xC000, + IFLNK = 0xA000, + IFREG = 0x8000, + IFBLK = 0x6000, + IFDIR = 0x4000, + IFCHR = 0x2000, + IFIFO = 0x1000, + + // -- process execution user/group override -- + ISUID = 0x0800, + ISGID = 0x0400, + ISVTX = 0x0200, + + // -- access rights -- + IRUSR = 0x0100, + IWUSR = 0x0080, + IXUSR = 0x0040, + IRGRP = 0x0020, + IWGRP = 0x0010, + IXGRP = 0x0008, + IROTH = 0x0004, + IWOTH = 0x0002, + IXOTH = 0x0001, + }; + + enum InodeFlags + { + SECRM_FL = 0x00000001, + UNRM_FL = 0x00000002, + COMPR_FL = 0x00000004, + SYNC_FL = 0x00000008, + IMMUTABLE_FL = 0x00000010, + APPEND_FL = 0x00000020, + NODUMP_FL = 0x00000040, + NOATIME_FL = 0x00000080, + // -- Reserved for compression usage -- + DIRTY_FL = 0x00000100, + COMPRBLK_FL = 0x00000200, + NOCOMPR_FL = 0x00000400, + ECOMPR_FL = 0x00000800, + // -- End of compression flags -- + BTREE_FL = 0x00001000, + INDEX_FL = 0x00001000, + IMAGIC_FL = 0x00002000, + JOURNAL_DATA_FL = 0x00004000, + RESERVED_FL = 0x80000000, + }; + + enum FileType + { + UNKNOWN = 0, + REG_FILE = 1, + DIR = 2, + CHRDEV = 3, + BLKDEV = 4, + FIFO = 5, + SOCK = 6, + SYMLINK = 7, + }; + + } + +} \ No newline at end of file diff --git a/kernel/include/kernel/FS/Ext2/FileSystem.h b/kernel/include/kernel/FS/Ext2/FileSystem.h new file mode 100644 index 00000000..e184c4a3 --- /dev/null +++ b/kernel/include/kernel/FS/Ext2/FileSystem.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include + +namespace Kernel +{ + + class Ext2FS final : public FileSystem + { + public: + static BAN::ErrorOr create(Partition&); + + virtual BAN::RefPtr root_inode() override { return m_root_inode; } + + private: + Ext2FS(Partition& partition) + : m_partition(partition) + {} + + BAN::ErrorOr initialize_superblock(); + BAN::ErrorOr initialize_root_inode(); + + BAN::ErrorOr create_inode(const Ext2::Inode&); + BAN::ErrorOr delete_inode(uint32_t); + BAN::ErrorOr resize_inode(uint32_t, size_t); + + void read_block(uint32_t, BAN::Span); + void write_block(uint32_t, BAN::Span); + void sync_superblock(); + + BAN::ErrorOr reserve_free_block(uint32_t primary_bgd); + + 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: + Partition& m_partition; + + BAN::RefPtr m_root_inode; + BAN::Vector m_superblock_backups; + + Ext2::Superblock m_superblock; + + friend class Ext2Inode; + }; + +} \ No newline at end of file diff --git a/kernel/include/kernel/FS/Ext2/Inode.h b/kernel/include/kernel/FS/Ext2/Inode.h new file mode 100644 index 00000000..c706ad35 --- /dev/null +++ b/kernel/include/kernel/FS/Ext2/Inode.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include +#include + +namespace Kernel +{ + + class Ext2FS; + + class Ext2Inode final : public Inode + { + public: + virtual ino_t ino() const override { return m_ino; }; + virtual Mode mode() const override { return { m_inode.mode }; } + virtual nlink_t nlink() const override { return m_inode.links_count; } + virtual uid_t uid() const override { return m_inode.uid; } + virtual gid_t gid() const override { return m_inode.gid; } + virtual off_t size() const override { return m_inode.size; } + virtual timespec atime() const override { return timespec { .tv_sec = m_inode.atime, .tv_nsec = 0 }; } + virtual timespec mtime() const override { return timespec { .tv_sec = m_inode.mtime, .tv_nsec = 0 }; } + virtual timespec ctime() const override { return timespec { .tv_sec = m_inode.ctime, .tv_nsec = 0 }; } + virtual blksize_t blksize() const override; + virtual blkcnt_t blocks() const override; + virtual dev_t dev() const override { return 0; } + virtual dev_t rdev() const override { return 0; } + + virtual BAN::ErrorOr link_target() override; + + virtual BAN::ErrorOr directory_read_next_entries(off_t, DirectoryEntryList*, size_t) override; + virtual BAN::ErrorOr> directory_find_inode(BAN::StringView) override; + + virtual BAN::ErrorOr read(size_t, void*, size_t) override; + virtual BAN::ErrorOr write(size_t, const void*, size_t) override; + virtual BAN::ErrorOr truncate(size_t) override; + + virtual BAN::ErrorOr create_file(BAN::StringView, mode_t, uid_t, gid_t) override; + + private: + BAN::ErrorOr for_data_block_index(uint32_t, const BAN::Function&, bool allocate); + + BAN::ErrorOr data_block_index(uint32_t); + BAN::ErrorOr allocate_new_block(); + BAN::ErrorOr sync(); + + uint32_t block_group() const; + + private: + Ext2Inode(Ext2FS& fs, Ext2::Inode inode, uint32_t ino) + : m_fs(fs) + , m_inode(inode) + , m_ino(ino) + {} + static BAN::ErrorOr> create(Ext2FS&, uint32_t); + + private: + Ext2FS& m_fs; + Ext2::Inode m_inode; + const uint32_t m_ino; + + friend class Ext2FS; + friend class BAN::RefPtr; + }; + +} \ No newline at end of file diff --git a/kernel/kernel/FS/Ext2/FileSystem.cpp b/kernel/kernel/FS/Ext2/FileSystem.cpp new file mode 100644 index 00000000..36699364 --- /dev/null +++ b/kernel/kernel/FS/Ext2/FileSystem.cpp @@ -0,0 +1,396 @@ +#include +#include + +#define EXT2_DEBUG_PRINT 1 + +namespace Kernel +{ + + BAN::ErrorOr Ext2FS::create(Partition& partition) + { + Ext2FS* ext2fs = new Ext2FS(partition); + if (ext2fs == nullptr) + return BAN::Error::from_errno(ENOMEM); + BAN::ScopeGuard guard([ext2fs] { delete ext2fs; }); + TRY(ext2fs->initialize_superblock()); + TRY(ext2fs->initialize_root_inode()); + guard.disable(); + return ext2fs; + } + + BAN::ErrorOr Ext2FS::initialize_superblock() + { + // Read superblock from disk + { + const uint32_t sector_size = m_partition.device().sector_size(); + ASSERT(1024 % sector_size == 0); + + const uint32_t lba = 1024 / sector_size; + const uint32_t sector_count = BAN::Math::div_round_up(sizeof(Ext2::Superblock), sector_size); + + BAN::Vector superblock_buffer; + TRY(superblock_buffer.resize(sector_count * sector_size)); + + TRY(m_partition.read_sectors(lba, sector_count, superblock_buffer.data())); + + memcpy(&m_superblock, superblock_buffer.data(), sizeof(Ext2::Superblock)); + } + + if (m_superblock.magic != Ext2::Enum::SUPER_MAGIC) + return BAN::Error::from_error_code(ErrorCode::Ext2_Invalid); + + if (m_superblock.rev_level == Ext2::Enum::GOOD_OLD_REV) + { + memset(m_superblock.__extension_start, 0, sizeof(Ext2::Superblock) - offsetof(Ext2::Superblock, Ext2::Superblock::__extension_start)); + m_superblock.first_ino = Ext2::Enum::GOOD_OLD_FIRST_INO; + m_superblock.inode_size = Ext2::Enum::GOOD_OLD_INODE_SIZE; + } + + const uint32_t number_of_block_groups = BAN::Math::div_round_up(superblock().inodes_count, superblock().inodes_per_group); + const 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_error_code(ErrorCode::Ext2_Corrupted); + + if (!(m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_FILETYPE)) + { + dwarnln("Directory entries without filetype not supported"); + return BAN::Error::from_errno(ENOTSUP); + } + if (m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_COMPRESSION) + { + dwarnln("Required FEATURE_INCOMPAT_COMPRESSION"); + return BAN::Error::from_errno(ENOTSUP); + } + if (m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_JOURNAL_DEV) + { + dwarnln("Required FEATURE_INCOMPAT_JOURNAL_DEV"); + return BAN::Error::from_errno(ENOTSUP); + } + if (m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_META_BG) + { + dwarnln("Required FEATURE_INCOMPAT_META_BG"); + return BAN::Error::from_errno(ENOTSUP); + } + if (m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_RECOVER) + { + dwarnln("Required FEATURE_INCOMPAT_RECOVER"); + return BAN::Error::from_errno(ENOTSUP); + } + +#if EXT2_DEBUG_PRINT + dprintln("EXT2"); + dprintln(" inodes {}", m_superblock.inodes_count); + dprintln(" blocks {}", m_superblock.blocks_count); + dprintln(" version {}.{}", m_superblock.rev_level, m_superblock.minor_rev_level); + dprintln(" first data at {}", m_superblock.first_data_block); + dprintln(" block size {}", 1024 << m_superblock.log_block_size); + dprintln(" inode size {}", m_superblock.inode_size); + dprintln(" inodes/group {}", m_superblock.inodes_per_group); +#endif + + { + BAN::Vector block_buffer; + TRY(block_buffer.resize(block_size())); + + if (superblock().rev_level == Ext2::Enum::GOOD_OLD_REV) + { + // In revision 0 all blockgroups contain superblock backup + TRY(m_superblock_backups.reserve(number_of_block_groups - 1)); + for (uint32_t i = 1; i < number_of_block_groups; i++) + MUST(block_buffer.push_back(i)); + } + else + { + // In other revision superblock backups are on blocks 1 and powers of 3, 5 and 7 + TRY(m_superblock_backups.push_back(1)); + for (uint32_t i = 3; i < number_of_block_groups; i *= 3) + TRY(m_superblock_backups.push_back(i)); + for (uint32_t i = 5; i < number_of_block_groups; i *= 5) + TRY(m_superblock_backups.push_back(i)); + for (uint32_t i = 7; i < number_of_block_groups; i *= 7) + TRY(m_superblock_backups.push_back(i)); + + // We don't really care if this succeeds or not + (void)m_superblock_backups.shrink_to_fit(); + } + + for (uint32_t bg : m_superblock_backups) + { + read_block(superblock().first_data_block + superblock().blocks_per_group * bg, block_buffer.span()); + Ext2::Superblock& superblock_backup = *(Ext2::Superblock*)block_buffer.data(); + if (superblock_backup.magic != Ext2::Enum::SUPER_MAGIC) + derrorln("superblock backup at block {} is invalid ({4H})", bg, superblock_backup.magic); + } + } + + return {}; + } + + BAN::ErrorOr Ext2FS::initialize_root_inode() + { + m_root_inode = TRY(Ext2Inode::create(*this, Ext2::Enum::ROOT_INO)); + +#if EXT2_DEBUG_PRINT + dprintln("root inode:"); + dprintln(" created {}", root_inode()->ctime().tv_sec); + dprintln(" modified {}", root_inode()->mtime().tv_sec); + dprintln(" accessed {}", root_inode()->atime().tv_sec); +#endif + return {}; + } + + BAN::ErrorOr Ext2FS::create_inode(const Ext2::Inode& ext2_inode) + { + ASSERT(ext2_inode.size == 0); + + if (m_superblock.free_inodes_count == 0) + return BAN::Error::from_errno(ENOSPC); + + const uint32_t block_size = this->block_size(); + + BAN::Vector bgd_buffer; + TRY(bgd_buffer.resize(block_size)); + + BAN::Vector inode_bitmap; + TRY(inode_bitmap.resize(block_size)); + + uint32_t current_group = -1; + BlockLocation bgd_location {}; + Ext2::BlockGroupDescriptor* bgd = nullptr; + + for (uint32_t ino = superblock().inodes_per_group + 1; ino <= superblock().inodes_count; ino++) + { + const uint32_t ino_group = (ino - 1) / superblock().inodes_per_group; + const uint32_t ino_index = (ino - 1) % superblock().inodes_per_group; + + if (ino_group != current_group) + { + current_group = ino_group; + + bgd_location = locate_block_group_descriptior(current_group); + read_block(bgd_location.block, bgd_buffer.span()); + + bgd = (Ext2::BlockGroupDescriptor*)(bgd_buffer.data() + bgd_location.offset); + if (bgd->free_inodes_count == 0) + { + ino = superblock().first_ino + (current_group + 1) * superblock().inodes_per_group - 1; + continue; + } + + read_block(bgd->inode_bitmap, inode_bitmap.span()); + } + + const uint32_t ino_bitmap_byte = ino_index / 8; + const uint32_t ino_bitmap_bit = ino_index % 8; + if (inode_bitmap[ino_bitmap_byte] & (1 << ino_bitmap_bit)) + continue; + + inode_bitmap[ino_bitmap_byte] |= 1 << ino_bitmap_bit; + write_block(bgd->inode_bitmap, inode_bitmap.span()); + + bgd->free_inodes_count--; + write_block(bgd_location.block, bgd_buffer.span()); + + const uint32_t inode_table_offset = ino_index * superblock().inode_size; + const BlockLocation inode_location + { + .block = inode_table_offset / block_size + bgd->inode_table, + .offset = inode_table_offset % block_size + }; + + dprintln("allocated inode {} at offset {2H}", + ino, + 2048 + (inode_location.block - 2) * block_size + inode_location.offset + ); + dprintln("bitmap byte at {2H} with bit {2H}", + 2048 + (bgd->inode_bitmap - 2) * block_size + ino_bitmap_byte, + 1 << ino_bitmap_bit + ); + + // NOTE: we don't need inode bitmap anymore, so we can reuse it + auto& inode_buffer = inode_bitmap; + + read_block(inode_location.block, inode_buffer.span()); + memcpy(inode_buffer.data() + inode_location.offset, &ext2_inode, sizeof(Ext2::Inode)); + if (superblock().inode_size > sizeof(Ext2::Inode)) + memset(inode_buffer.data() + inode_location.offset + sizeof(Ext2::Inode), 0, superblock().inode_size - sizeof(Ext2::Inode)); + write_block(inode_location.block, inode_buffer.span()); + + m_superblock.free_inodes_count--; + sync_superblock(); + + return ino; + } + + derrorln("Corrupted file system. Superblock indicates free inodes but none were found."); + return BAN::Error::from_error_code(ErrorCode::Ext2_Corrupted); + } + + void Ext2FS::read_block(uint32_t block, BAN::Span buffer) + { + const uint32_t sector_size = m_partition.device().sector_size(); + const uint32_t block_size = this->block_size(); + const uint32_t sectors_per_block = block_size / sector_size; + const uint32_t sectors_before = 2048 / sector_size; + + ASSERT(block >= 2); + ASSERT(buffer.size() >= block_size); + MUST(m_partition.read_sectors(sectors_before + (block - 2) * sectors_per_block, sectors_per_block, buffer.data())); + } + + void Ext2FS::write_block(uint32_t block, BAN::Span buffer) + { + const uint32_t sector_size = m_partition.device().sector_size(); + const uint32_t block_size = this->block_size(); + const uint32_t sectors_per_block = block_size / sector_size; + const uint32_t sectors_before = 2048 / sector_size; + + ASSERT(block >= 2); + ASSERT(buffer.size() >= block_size); + MUST(m_partition.write_sectors(sectors_before + (block - 2) * sectors_per_block, sectors_per_block, buffer.data())); + } + + void Ext2FS::sync_superblock() + { + const uint32_t sector_size = m_partition.device().sector_size(); + ASSERT(1024 % sector_size == 0); + + const uint32_t superblock_bytes = + (m_superblock.rev_level == Ext2::Enum::GOOD_OLD_REV) + ? offsetof(Ext2::Superblock, __extension_start) + : sizeof(Ext2::Superblock); + + const uint32_t lba = 1024 / sector_size; + const uint32_t sector_count = BAN::Math::div_round_up(superblock_bytes, sector_size); + + BAN::Vector superblock_buffer; + MUST(superblock_buffer.resize(sector_count * sector_size)); + + MUST(m_partition.read_sectors(lba, sector_count, superblock_buffer.data())); + if (memcmp(superblock_buffer.data(), &m_superblock, superblock_bytes)) + { + memcpy(superblock_buffer.data(), &m_superblock, superblock_bytes); + MUST(m_partition.write_sectors(lba, sector_count, superblock_buffer.data())); + } + } + + BAN::ErrorOr Ext2FS::reserve_free_block(uint32_t primary_bgd) + { + if (m_superblock.r_blocks_count >= m_superblock.free_blocks_count) + return BAN::Error::from_errno(ENOSPC); + + const uint32_t block_size = this->block_size(); + + BAN::Vector bgd_buffer; + TRY(bgd_buffer.resize(block_size)); + + BAN::Vector block_bitmap; + TRY(block_bitmap.resize(block_size)); + + auto check_block_group = + [&](uint32_t block_group) -> uint32_t + { + auto bgd_location = locate_block_group_descriptior(block_group); + read_block(bgd_location.block, bgd_buffer.span()); + + auto& bgd = *(Ext2::BlockGroupDescriptor*)(bgd_buffer.data() + bgd_location.offset); + if (bgd.free_blocks_count == 0) + return 0; + + read_block(bgd.block_bitmap, block_bitmap.span()); + for (uint32_t block_offset = 0; block_offset < m_superblock.blocks_per_group; block_offset++) + { + uint32_t byte = block_offset / 8; + uint32_t bit = block_offset % 8; + if (block_bitmap[byte] & (1 << bit)) + continue; + + block_bitmap[byte] |= 1 << bit; + write_block(bgd.block_bitmap, block_bitmap.span()); + + bgd.free_blocks_count--; + write_block(bgd_location.block, bgd_buffer.span()); + + m_superblock.free_blocks_count--; + sync_superblock(); + + return m_superblock.first_data_block + m_superblock.blocks_per_group * block_group + block_offset; + } + + derrorln("Corrupted file system. Block group descriptor indicates free blocks but none were found"); + return 0; + }; + + if (auto ret = check_block_group(primary_bgd)) + return ret; + + uint32_t number_of_block_groups = BAN::Math::div_round_up(m_superblock.blocks_count, m_superblock.blocks_per_group); + for (uint32_t block_group = 0; block_group < number_of_block_groups; block_group++) + if (block_group != primary_bgd) + if (auto ret = check_block_group(block_group)) + return ret; + + derrorln("Corrupted file system. Superblock indicates free blocks but none were found."); + return BAN::Error::from_error_code(ErrorCode::Ext2_Corrupted); + } + + BAN::ErrorOr Ext2FS::locate_inode(uint32_t ino) + { + ASSERT(ino < superblock().inodes_count); + + const uint32_t block_size = this->block_size(); + + BAN::Vector bgd_buffer; + TRY(bgd_buffer.resize(block_size)); + + 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.span()); + + auto& bgd = *(Ext2::BlockGroupDescriptor*)(bgd_buffer.data() + bgd_location.offset); + + const uint32_t inode_byte_offset = inode_index * superblock().inode_size; + BlockLocation location + { + .block = inode_byte_offset / block_size + bgd.inode_table, + .offset = inode_byte_offset % block_size + }; + +#if VERIFY_INODE_EXISTANCE + const uint32_t inode_bitmap_block = bgd.inode_bitmap; + + // NOTE: we can reuse the bgd_buffer since it is not needed anymore + auto& inode_bitmap = bgd_buffer; + + read_block(inode_bitmap_block, inode_bitmap.span()); + + const uint32_t byte = inode_index / 8; + const uint32_t bit = inode_index % 8; + ASSERT(inode_bitmap[byte] & (1 << bit)); +#endif + + return location; + } + + Ext2FS::BlockLocation Ext2FS::locate_block_group_descriptior(uint32_t group_index) + { + const uint32_t block_size = this->block_size(); + + const uint32_t block_group_count = BAN::Math::div_round_up(superblock().inodes_count, superblock().inodes_per_group); + ASSERT(group_index < block_group_count); + + // Block Group Descriptor table is always after the superblock + // Superblock begins at byte 1024 and is exactly 1024 bytes wide + const uint32_t bgd_byte_offset = 2048 + sizeof(Ext2::BlockGroupDescriptor) * group_index; + + return + { + .block = bgd_byte_offset / block_size, + .offset = bgd_byte_offset % block_size + }; + } + +} \ No newline at end of file diff --git a/kernel/kernel/FS/Ext2.cpp b/kernel/kernel/FS/Ext2/Inode.cpp similarity index 50% rename from kernel/kernel/FS/Ext2.cpp rename to kernel/kernel/FS/Ext2/Inode.cpp index 0a2ce884..e43aeceb 100644 --- a/kernel/kernel/FS/Ext2.cpp +++ b/kernel/kernel/FS/Ext2/Inode.cpp @@ -1,163 +1,13 @@ -#include -#include -#include +#include +#include +#include #include -#define EXT2_DEBUG_PRINT 0 -#define VERIFY_INODE_EXISTANCE 1 +#define EXT2_DEBUG_PRINT 1 namespace Kernel { - namespace Ext2::Enum - { - - constexpr uint16_t SUPER_MAGIC = 0xEF53; - - enum State - { - VALID_FS = 1, - ERROR_FS = 2, - }; - - enum Errors - { - ERRORS_CONTINUE = 1, - ERRORS_RO = 2, - ERRORS_PANIC = 3, - }; - - enum CreatorOS - { - OS_LINUX = 0, - OS_HURD = 1, - OS_MASIX = 2, - OS_FREEBSD = 3, - OS_LITES = 4, - }; - - enum RevLevel - { - GOOD_OLD_REV = 0, - DYNAMIC_REV = 1, - }; - - enum Rev0Constant - { - GOOD_OLD_FIRST_INO = 11, - GOOD_OLD_INODE_SIZE = 128, - }; - - enum FeatureCompat - { - FEATURE_COMPAT_DIR_PREALLOC = 0x0001, - FEATURE_COMPAT_IMAGIC_INODES = 0x0002, - FEATURE_COMPAT_HAS_JOURNAL = 0x0004, - FEATURE_COMPAT_EXT_ATTR = 0x0008, - FEATURE_COMPAT_RESIZE_INO = 0x0010, - FEATURE_COMPAT_DIR_INDEX = 0x0020, - }; - - enum FeaturesIncompat - { - FEATURE_INCOMPAT_COMPRESSION = 0x0001, - FEATURE_INCOMPAT_FILETYPE = 0x0002, - FEATURE_INCOMPAT_RECOVER = 0x0004, - FEATURE_INCOMPAT_JOURNAL_DEV = 0x0008, - FEATURE_INCOMPAT_META_BG = 0x0010, - }; - - enum FeaturesRoCompat - { - FEATURE_RO_COMPAT_SPARSE_SUPER = 0x0001, - FEATURE_RO_COMPAT_LARGE_FILE = 0x0002, - FEATURE_RO_COMPAT_BTREE_DIR = 0x0004, - }; - - enum AlgoBitmap - { - LZV1_ALG = 0, - LZRW3A_ALG = 1, - GZIP_ALG = 2, - BZIP2_ALG = 3, - LZO_ALG = 4, - }; - - enum ReservedInodes - { - BAD_INO = 1, - ROOT_INO = 2, - ACL_IDX_INO = 3, - ACL_DATA_INO = 4, - BOOT_LOADER_INO = 5, - UNDEL_DIR_INO = 6, - }; - - enum InodeMode - { - // -- file format -- - IFSOCK = 0xC000, - IFLNK = 0xA000, - IFREG = 0x8000, - IFBLK = 0x6000, - IFDIR = 0x4000, - IFCHR = 0x2000, - IFIFO = 0x1000, - - // -- process execution user/group override -- - ISUID = 0x0800, - ISGID = 0x0400, - ISVTX = 0x0200, - - // -- access rights -- - IRUSR = 0x0100, - IWUSR = 0x0080, - IXUSR = 0x0040, - IRGRP = 0x0020, - IWGRP = 0x0010, - IXGRP = 0x0008, - IROTH = 0x0004, - IWOTH = 0x0002, - IXOTH = 0x0001, - }; - - enum InodeFlags - { - SECRM_FL = 0x00000001, - UNRM_FL = 0x00000002, - COMPR_FL = 0x00000004, - SYNC_FL = 0x00000008, - IMMUTABLE_FL = 0x00000010, - APPEND_FL = 0x00000020, - NODUMP_FL = 0x00000040, - NOATIME_FL = 0x00000080, - // -- Reserved for compression usage -- - DIRTY_FL = 0x00000100, - COMPRBLK_FL = 0x00000200, - NOCOMPR_FL = 0x00000400, - ECOMPR_FL = 0x00000800, - // -- End of compression flags -- - BTREE_FL = 0x00001000, - INDEX_FL = 0x00001000, - IMAGIC_FL = 0x00002000, - JOURNAL_DATA_FL = 0x00004000, - RESERVED_FL = 0x80000000, - }; - - enum FileType - { - UNKNOWN = 0, - REG_FILE = 1, - DIR = 2, - CHRDEV = 3, - BLKDEV = 4, - FIFO = 5, - SOCK = 6, - SYMLINK = 7, - }; - - } - blksize_t Ext2Inode::blksize() const { return m_fs.block_size(); @@ -556,7 +406,7 @@ namespace Kernel .mtime = (uint32_t)current_time.tv_sec, .dtime = 0, .gid = (uint16_t)gid, - .links_count = 1, + .links_count = 5, .blocks = 0, .flags = 0, .osd1 = 0, @@ -725,383 +575,4 @@ needs_new_block: return BAN::Error::from_errno(ENOENT); } - BAN::ErrorOr Ext2FS::create(Partition& partition) - { - Ext2FS* ext2fs = new Ext2FS(partition); - if (ext2fs == nullptr) - return BAN::Error::from_errno(ENOMEM); - BAN::ScopeGuard guard([ext2fs] { delete ext2fs; }); - TRY(ext2fs->initialize_superblock()); - TRY(ext2fs->initialize_root_inode()); - guard.disable(); - return ext2fs; - } - - BAN::ErrorOr Ext2FS::initialize_superblock() - { - // Read superblock from disk - { - const uint32_t sector_size = m_partition.device().sector_size(); - ASSERT(1024 % sector_size == 0); - - const uint32_t lba = 1024 / sector_size; - const uint32_t sector_count = BAN::Math::div_round_up(sizeof(Ext2::Superblock), sector_size); - - BAN::Vector superblock_buffer; - TRY(superblock_buffer.resize(sector_count * sector_size)); - - TRY(m_partition.read_sectors(lba, sector_count, superblock_buffer.data())); - - memcpy(&m_superblock, superblock_buffer.data(), sizeof(Ext2::Superblock)); - } - - if (m_superblock.magic != Ext2::Enum::SUPER_MAGIC) - return BAN::Error::from_error_code(ErrorCode::Ext2_Invalid); - - if (m_superblock.rev_level == Ext2::Enum::GOOD_OLD_REV) - { - memset(m_superblock.__extension_start, 0, sizeof(Ext2::Superblock) - offsetof(Ext2::Superblock, Ext2::Superblock::__extension_start)); - m_superblock.first_ino = Ext2::Enum::GOOD_OLD_FIRST_INO; - m_superblock.inode_size = Ext2::Enum::GOOD_OLD_INODE_SIZE; - } - - const uint32_t number_of_block_groups = BAN::Math::div_round_up(superblock().inodes_count, superblock().inodes_per_group); - const 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_error_code(ErrorCode::Ext2_Corrupted); - - if (!(m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_FILETYPE)) - { - dwarnln("Directory entries without filetype not supported"); - return BAN::Error::from_errno(ENOTSUP); - } - if (m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_COMPRESSION) - { - dwarnln("Required FEATURE_INCOMPAT_COMPRESSION"); - return BAN::Error::from_errno(ENOTSUP); - } - if (m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_JOURNAL_DEV) - { - dwarnln("Required FEATURE_INCOMPAT_JOURNAL_DEV"); - return BAN::Error::from_errno(ENOTSUP); - } - if (m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_META_BG) - { - dwarnln("Required FEATURE_INCOMPAT_META_BG"); - return BAN::Error::from_errno(ENOTSUP); - } - if (m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_RECOVER) - { - dwarnln("Required FEATURE_INCOMPAT_RECOVER"); - return BAN::Error::from_errno(ENOTSUP); - } - -#if EXT2_DEBUG_PRINT - dprintln("EXT2"); - dprintln(" inodes {}", m_superblock.inodes_count); - dprintln(" blocks {}", m_superblock.blocks_count); - dprintln(" version {}.{}", m_superblock.rev_level, m_superblock.minor_rev_level); - dprintln(" first data at {}", m_superblock.first_data_block); - dprintln(" block size {}", 1024 << m_superblock.log_block_size); - dprintln(" inode size {}", m_superblock.inode_size); -#endif - - { - BAN::Vector block_buffer; - TRY(block_buffer.resize(block_size())); - - if (superblock().rev_level == Ext2::Enum::GOOD_OLD_REV) - { - // In revision 0 all blockgroups contain superblock backup - TRY(m_superblock_backups.reserve(number_of_block_groups - 1)); - for (uint32_t i = 1; i < number_of_block_groups; i++) - MUST(block_buffer.push_back(i)); - } - else - { - // In other revision superblock backups are on blocks 1 and powers of 3, 5 and 7 - TRY(m_superblock_backups.push_back(1)); - for (uint32_t i = 3; i < number_of_block_groups; i *= 3) - TRY(m_superblock_backups.push_back(i)); - for (uint32_t i = 5; i < number_of_block_groups; i *= 5) - TRY(m_superblock_backups.push_back(i)); - for (uint32_t i = 7; i < number_of_block_groups; i *= 7) - TRY(m_superblock_backups.push_back(i)); - - // We don't really care if this succeeds or not - (void)m_superblock_backups.shrink_to_fit(); - } - - for (uint32_t bg : m_superblock_backups) - { - read_block(superblock().first_data_block + superblock().blocks_per_group * bg, block_buffer.span()); - Ext2::Superblock& superblock_backup = *(Ext2::Superblock*)block_buffer.data(); - if (superblock_backup.magic != Ext2::Enum::SUPER_MAGIC) - derrorln("superblock backup at block {} is invalid ({4H})", bg, superblock_backup.magic); - } - } - - return {}; - } - - BAN::ErrorOr Ext2FS::initialize_root_inode() - { - m_root_inode = TRY(Ext2Inode::create(*this, Ext2::Enum::ROOT_INO)); - - //(void)locate_inode(2271); - -#if EXT2_DEBUG_PRINT - dprintln("root inode:"); - dprintln(" created {}", root_inode()->ctime().tv_sec); - dprintln(" modified {}", root_inode()->mtime().tv_sec); - dprintln(" accessed {}", root_inode()->atime().tv_sec); -#endif - return {}; - } - - BAN::ErrorOr Ext2FS::create_inode(const Ext2::Inode& ext2_inode) - { - ASSERT(ext2_inode.size == 0); - - if (m_superblock.free_inodes_count == 0) - return BAN::Error::from_errno(ENOSPC); - - const uint32_t block_size = this->block_size(); - - BAN::Vector bgd_buffer; - TRY(bgd_buffer.resize(block_size)); - - BAN::Vector inode_bitmap; - TRY(inode_bitmap.resize(block_size)); - - uint32_t current_group = -1; - BlockLocation bgd_location {}; - Ext2::BlockGroupDescriptor* bgd = nullptr; - - for (uint32_t ino = superblock().first_ino; ino <= superblock().inodes_count; ino++) - { - const uint32_t ino_group = (ino - 1) / superblock().inodes_per_group; - const uint32_t ino_index = (ino - 1) % superblock().inodes_per_group; - - if (ino_group != current_group) - { - current_group = ino_group; - - bgd_location = locate_block_group_descriptior(current_group); - read_block(bgd_location.block, bgd_buffer.span()); - - bgd = (Ext2::BlockGroupDescriptor*)(bgd_buffer.data() + bgd_location.offset); - if (bgd->free_inodes_count == 0) - { - ino = (current_group + 1) * superblock().inodes_per_group; - continue; - } - - read_block(bgd->inode_bitmap, inode_bitmap.span()); - } - - const uint32_t ino_bitmap_byte = ino_index / 8; - const uint32_t ino_bitmap_bit = ino_index % 8; - if (inode_bitmap[ino_bitmap_byte] & (1 << ino_bitmap_bit)) - continue; - - inode_bitmap[ino_bitmap_byte] |= 1 << ino_bitmap_bit; - write_block(bgd->inode_bitmap, inode_bitmap.span()); - - bgd->free_inodes_count--; - write_block(bgd_location.block, bgd_buffer.span()); - - const uint32_t inode_table_offset = ino_index * superblock().inode_size; - const BlockLocation inode_location - { - .block = inode_table_offset / block_size + bgd->inode_table, - .offset = inode_table_offset % block_size - }; - - // NOTE: we don't need inode bitmap anymore, so we can reuse it - auto& inode_buffer = inode_bitmap; - - read_block(inode_location.block, inode_buffer.span()); - memcpy(inode_buffer.data() + inode_location.offset, &ext2_inode, sizeof(Ext2::Inode)); - if (superblock().inode_size > sizeof(Ext2::Inode)) - memset(inode_buffer.data() + inode_location.offset + sizeof(Ext2::Inode), 0, superblock().inode_size - sizeof(Ext2::Inode)); - write_block(inode_location.block, inode_buffer.span()); - - m_superblock.free_inodes_count--; - sync_superblock(); - - return ino; - } - - derrorln("Corrupted file system. Superblock indicates free inodes but none were found."); - return BAN::Error::from_error_code(ErrorCode::Ext2_Corrupted); - } - - void Ext2FS::read_block(uint32_t block, BAN::Span buffer) - { - const uint32_t sector_size = m_partition.device().sector_size(); - const uint32_t block_size = this->block_size(); - const uint32_t sectors_per_block = block_size / sector_size; - const uint32_t sectors_before = 2048 / sector_size; - - ASSERT(block >= 2); - ASSERT(buffer.size() >= block_size); - MUST(m_partition.read_sectors(sectors_before + (block - 2) * sectors_per_block, sectors_per_block, buffer.data())); - } - - void Ext2FS::write_block(uint32_t block, BAN::Span buffer) - { - const uint32_t sector_size = m_partition.device().sector_size(); - const uint32_t block_size = this->block_size(); - const uint32_t sectors_per_block = block_size / sector_size; - const uint32_t sectors_before = 2048 / sector_size; - - ASSERT(block >= 2); - ASSERT(buffer.size() >= block_size); - MUST(m_partition.write_sectors(sectors_before + (block - 2) * sectors_per_block, sectors_per_block, buffer.data())); - } - - void Ext2FS::sync_superblock() - { - const uint32_t sector_size = m_partition.device().sector_size(); - ASSERT(1024 % sector_size == 0); - - const uint32_t superblock_bytes = - (m_superblock.rev_level == Ext2::Enum::GOOD_OLD_REV) - ? offsetof(Ext2::Superblock, __extension_start) - : sizeof(Ext2::Superblock); - - const uint32_t lba = 1024 / sector_size; - const uint32_t sector_count = BAN::Math::div_round_up(superblock_bytes, sector_size); - - BAN::Vector superblock_buffer; - MUST(superblock_buffer.resize(sector_count * sector_size)); - - MUST(m_partition.read_sectors(lba, sector_count, superblock_buffer.data())); - if (memcmp(superblock_buffer.data(), &m_superblock, superblock_bytes)) - { - memcpy(superblock_buffer.data(), &m_superblock, superblock_bytes); - MUST(m_partition.write_sectors(lba, sector_count, superblock_buffer.data())); - } - } - - BAN::ErrorOr Ext2FS::reserve_free_block(uint32_t primary_bgd) - { - if (m_superblock.r_blocks_count >= m_superblock.free_blocks_count) - return BAN::Error::from_errno(ENOSPC); - - const uint32_t block_size = this->block_size(); - - BAN::Vector bgd_buffer; - TRY(bgd_buffer.resize(block_size)); - - BAN::Vector block_bitmap; - TRY(block_bitmap.resize(block_size)); - - auto check_block_group = - [&](uint32_t block_group) -> uint32_t - { - auto bgd_location = locate_block_group_descriptior(block_group); - read_block(bgd_location.block, bgd_buffer.span()); - - auto& bgd = *(Ext2::BlockGroupDescriptor*)(bgd_buffer.data() + bgd_location.offset); - if (bgd.free_blocks_count == 0) - return 0; - - read_block(bgd.block_bitmap, block_bitmap.span()); - for (uint32_t block_offset = 0; block_offset < m_superblock.blocks_per_group; block_offset++) - { - uint32_t byte = block_offset / 8; - uint32_t bit = block_offset % 8; - if (block_bitmap[byte] & (1 << bit)) - continue; - - block_bitmap[byte] |= 1 << bit; - write_block(bgd.block_bitmap, block_bitmap.span()); - - bgd.free_blocks_count--; - write_block(bgd_location.block, bgd_buffer.span()); - - m_superblock.free_blocks_count--; - sync_superblock(); - - return m_superblock.first_data_block + m_superblock.blocks_per_group * block_group + block_offset; - } - - derrorln("Corrupted file system. Block group descriptor indicates free blocks but none were found"); - return 0; - }; - - if (auto ret = check_block_group(primary_bgd)) - return ret; - - uint32_t number_of_block_groups = BAN::Math::div_round_up(m_superblock.blocks_count, m_superblock.blocks_per_group); - for (uint32_t block_group = 0; block_group < number_of_block_groups; block_group++) - if (block_group != primary_bgd) - if (auto ret = check_block_group(block_group)) - return ret; - - derrorln("Corrupted file system. Superblock indicates free blocks but none were found."); - return BAN::Error::from_error_code(ErrorCode::Ext2_Corrupted); - } - - BAN::ErrorOr Ext2FS::locate_inode(uint32_t ino) - { - ASSERT(ino < superblock().inodes_count); - - const uint32_t block_size = this->block_size(); - - BAN::Vector bgd_buffer; - TRY(bgd_buffer.resize(block_size)); - - 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.span()); - - auto& bgd = *(Ext2::BlockGroupDescriptor*)(bgd_buffer.data() + bgd_location.offset); - - const uint32_t inode_byte_offset = inode_index * superblock().inode_size; - BlockLocation location - { - .block = inode_byte_offset / block_size + bgd.inode_table, - .offset = inode_byte_offset % block_size - }; - -#if VERIFY_INODE_EXISTANCE - const uint32_t inode_bitmap_block = bgd.inode_bitmap; - - // NOTE: we can reuse the bgd_buffer since it is not needed anymore - auto& inode_bitmap = bgd_buffer; - - read_block(inode_bitmap_block, inode_bitmap.span()); - - const uint32_t byte = inode_index / 8; - const uint32_t bit = inode_index % 8; - ASSERT(inode_bitmap[byte] & (1 << bit)); -#endif - - return location; - } - - Ext2FS::BlockLocation Ext2FS::locate_block_group_descriptior(uint32_t group_index) - { - const uint32_t block_size = this->block_size(); - - const uint32_t block_group_count = BAN::Math::div_round_up(superblock().inodes_count, superblock().inodes_per_group); - ASSERT(group_index < block_group_count); - - // Block Group Descriptor table is always after the superblock - // Superblock begins at byte 1024 and is exactly 1024 bytes wide - const uint32_t bgd_byte_offset = 2048 + sizeof(Ext2::BlockGroupDescriptor) * group_index; - - return - { - .block = bgd_byte_offset / block_size, - .offset = bgd_byte_offset % block_size - }; - } - } \ No newline at end of file diff --git a/kernel/kernel/FS/VirtualFileSystem.cpp b/kernel/kernel/FS/VirtualFileSystem.cpp index f1189d53..0c519c87 100644 --- a/kernel/kernel/FS/VirtualFileSystem.cpp +++ b/kernel/kernel/FS/VirtualFileSystem.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/kernel/kernel/Storage/StorageDevice.cpp b/kernel/kernel/Storage/StorageDevice.cpp index bb3695d7..98d12f0a 100644 --- a/kernel/kernel/Storage/StorageDevice.cpp +++ b/kernel/kernel/Storage/StorageDevice.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include