From 80006ea1370c7ecdaf57b46fa0ca307a1f7cf005 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Mon, 20 Feb 2023 01:46:00 +0200 Subject: [PATCH] Kernel: Initial work on filesystem We support now ext2 directory listing. File reading is not yet supported. --- kernel/Makefile | 3 + kernel/include/kernel/ATA.h | 2 +- kernel/include/kernel/DiskIO.h | 40 +- kernel/include/kernel/FS/Ext2.h | 188 +++++++++ kernel/include/kernel/FS/FileSystem.h | 15 + kernel/include/kernel/FS/Inode.h | 28 ++ kernel/include/kernel/FS/VirtualFileSystem.h | 26 ++ kernel/kernel/ATA.cpp | 5 +- kernel/kernel/DiskIO.cpp | 157 +++++--- kernel/kernel/FS/Ext2.cpp | 392 +++++++++++++++++++ kernel/kernel/FS/VirtualFileSystem.cpp | 26 ++ kernel/kernel/Shell.cpp | 88 +++-- 12 files changed, 867 insertions(+), 103 deletions(-) create mode 100644 kernel/include/kernel/FS/Ext2.h create mode 100644 kernel/include/kernel/FS/FileSystem.h create mode 100644 kernel/include/kernel/FS/Inode.h create mode 100644 kernel/include/kernel/FS/VirtualFileSystem.h create mode 100644 kernel/kernel/FS/Ext2.cpp create mode 100644 kernel/kernel/FS/VirtualFileSystem.cpp diff --git a/kernel/Makefile b/kernel/Makefile index b25eddf1..b124a5c6 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -38,6 +38,8 @@ kernel/CPUID.o \ kernel/Debug.o \ kernel/DiskIO.o \ kernel/font.o \ +kernel/FS/Ext2.o \ +kernel/FS/VirtualFileSystem.o \ kernel/Input.o \ kernel/InterruptController.o \ kernel/kernel.o \ @@ -95,6 +97,7 @@ $(ARCHDIR)/crtbegin.o $(ARCHDIR)/crtend.o: always: mkdir -p $(BUILDDIR)/$(ARCHDIR) mkdir -p $(BUILDDIR)/kernel + mkdir -p $(BUILDDIR)/kernel/FS clean: rm -rf $(BUILDDIR) diff --git a/kernel/include/kernel/ATA.h b/kernel/include/kernel/ATA.h index 39986bd7..ac731204 100644 --- a/kernel/include/kernel/ATA.h +++ b/kernel/include/kernel/ATA.h @@ -41,7 +41,7 @@ namespace Kernel virtual uint32_t sector_size() const override { return m_sector_words * 2; } virtual const char* type() const override { return "PATA"; } - virtual bool read(uint32_t lba, uint32_t sector_count, uint8_t* buffer) override; + virtual bool read_sectors(uint32_t lba, uint32_t sector_count, uint8_t* buffer) override; protected: virtual bool initialize() override; diff --git a/kernel/include/kernel/DiskIO.h b/kernel/include/kernel/DiskIO.h index 24a61136..e8eadbf5 100644 --- a/kernel/include/kernel/DiskIO.h +++ b/kernel/include/kernel/DiskIO.h @@ -5,17 +5,40 @@ namespace Kernel { + struct GUID + { + uint32_t data1; + uint16_t data2; + uint16_t data3; + uint8_t data4[8]; + }; + class DiskDevice { public: struct Partition { - uint8_t type_guid[16]; - uint8_t guid[16]; - uint64_t start_lba; - uint64_t end_lba; - uint64_t attributes; - char name[72]; + Partition(DiskDevice&, const GUID&, const GUID&, uint64_t, uint64_t, uint64_t, const char*); + + const GUID& type() const { return m_type; } + const GUID& guid() const { return m_guid; } + uint64_t lba_start() const { return m_lba_start; } + uint64_t lba_end() const { return m_lba_end; } + uint64_t attributes() const { return m_attributes; } + const char* name() const { return m_name; } + const DiskDevice& device() const { return m_device; } + + bool read_sectors(uint32_t lba, uint32_t sector_count, uint8_t* buffer); + bool is_used() const { uint8_t zero[16] {}; return memcmp(&m_type, zero, 16); } + + private: + DiskDevice& m_device; + const GUID m_type; + const GUID m_guid; + const uint64_t m_lba_start; + const uint64_t m_lba_end; + const uint64_t m_attributes; + char m_name[72]; }; public: @@ -24,9 +47,11 @@ namespace Kernel virtual bool initialize() = 0; bool initialize_partitions(); - virtual bool read(uint32_t lba, uint32_t sector_count, uint8_t* buffer) = 0; + virtual bool read_sectors(uint32_t lba, uint32_t sector_count, uint8_t* buffer) = 0; virtual uint32_t sector_size() const = 0; + BAN::Vector& partitions() { return m_partitions; } + private: BAN::Vector m_partitions; }; @@ -39,6 +64,7 @@ namespace Kernel private: DiskIO(); + void try_add_device(DiskDevice*); private: BAN::Vector m_devices; diff --git a/kernel/include/kernel/FS/Ext2.h b/kernel/include/kernel/FS/Ext2.h new file mode 100644 index 00000000..4bb4e7eb --- /dev/null +++ b/kernel/include/kernel/FS/Ext2.h @@ -0,0 +1,188 @@ +#pragma once + +#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; + uint16_t __padding; + //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 : public Inode + { + public: + virtual bool is_directory() const override; + virtual bool is_regular_file() const override; + + 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 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; + + private: + Ext2Inode() {} + Ext2Inode(Ext2FS* fs, Ext2::Inode inode, BAN::StringView name) + : m_fs(fs) + , m_inode(inode) + , m_name(name) + {} + + private: + Ext2FS* m_fs = nullptr; + Ext2::Inode m_inode; + BAN::String m_name; + + friend class Ext2FS; + }; + + class Ext2FS : public FileSystem + { + public: + static BAN::ErrorOr create(DiskDevice::Partition&); + + virtual const BAN::RefCounted root_inode() const override { return m_root_inode; } + + private: + Ext2FS(DiskDevice::Partition& partition) + : m_partition(partition) + {} + + BAN::ErrorOr initialize_superblock(); + BAN::ErrorOr initialize_block_group_descriptors(); + BAN::ErrorOr initialize_root_inode(); + + BAN::ErrorOr read_inode(uint32_t); + BAN::ErrorOr> read_block(uint32_t); + + const Ext2::Superblock& superblock() const { return m_superblock; } + + const Ext2::Inode& ext2_root_inode() const; + + private: + DiskDevice::Partition& m_partition; + + BAN::RefCounted m_root_inode; + + Ext2::Superblock m_superblock; + BAN::Vector m_block_group_descriptors; + + friend class Ext2Inode; + }; + +} \ No newline at end of file diff --git a/kernel/include/kernel/FS/FileSystem.h b/kernel/include/kernel/FS/FileSystem.h new file mode 100644 index 00000000..52e5c588 --- /dev/null +++ b/kernel/include/kernel/FS/FileSystem.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +namespace Kernel +{ + + class FileSystem + { + public: + virtual const BAN::RefCounted root_inode() const = 0; + }; + +} \ No newline at end of file diff --git a/kernel/include/kernel/FS/Inode.h b/kernel/include/kernel/FS/Inode.h new file mode 100644 index 00000000..9cbc9d64 --- /dev/null +++ b/kernel/include/kernel/FS/Inode.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include + +namespace Kernel +{ + + class Inode + { + public: + virtual bool is_directory() const = 0; + virtual bool is_regular_file() const = 0; + + virtual uint16_t uid() const = 0; + virtual uint16_t gid() const = 0; + virtual uint32_t size() const = 0; + + 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; + }; + +} \ No newline at end of file diff --git a/kernel/include/kernel/FS/VirtualFileSystem.h b/kernel/include/kernel/FS/VirtualFileSystem.h new file mode 100644 index 00000000..9e32dabb --- /dev/null +++ b/kernel/include/kernel/FS/VirtualFileSystem.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace Kernel +{ + + class VirtualFileSystem : public FileSystem + { + public: + static void initialize(BAN::RefCounted root_inode); + static VirtualFileSystem& get(); + static bool is_initialized(); + + virtual const BAN::RefCounted root_inode() const override { return m_root_inode; } + + private: + VirtualFileSystem(BAN::RefCounted root_inode) + : m_root_inode(root_inode) + {} + + private: + BAN::RefCounted m_root_inode; + }; + +} \ No newline at end of file diff --git a/kernel/kernel/ATA.cpp b/kernel/kernel/ATA.cpp index bbd0fdfe..a8362851 100644 --- a/kernel/kernel/ATA.cpp +++ b/kernel/kernel/ATA.cpp @@ -136,13 +136,10 @@ namespace Kernel dprintln("using {} sector size", m_sector_words * 2); } - - - return true; } - bool PATADevice::read(uint32_t lba, uint32_t sector_count, uint8_t* buffer) + bool PATADevice::read_sectors(uint32_t lba, uint32_t sector_count, uint8_t* buffer) { return read_lba28(lba, sector_count, buffer); } diff --git a/kernel/kernel/DiskIO.cpp b/kernel/kernel/DiskIO.cpp index 2d53b3db..10845720 100644 --- a/kernel/kernel/DiskIO.cpp +++ b/kernel/kernel/DiskIO.cpp @@ -1,8 +1,12 @@ #include #include #include +#include +#include #include +#include + #define ATA_DEVICE_PRIMARY 0x1F0 #define ATA_DEVICE_SECONDARY 0x170 #define ATA_DEVICE_SLAVE_BIT 0x10 @@ -19,7 +23,7 @@ namespace Kernel uint64_t my_lba; uint64_t first_lba; uint64_t last_lba; - uint8_t guid[16]; + GUID guid; uint64_t partition_entry_lba; uint32_t partition_entry_count; uint32_t partition_entry_size; @@ -114,6 +118,25 @@ namespace Kernel return result; } + template + static T big_endian_to_host(const uint8_t* data) + { + T result = 0; + for (size_t i = 0; i < sizeof(T); i++) + result |= data[i] << (8 * (sizeof(T) - i - 1)); + return result; + } + + static GUID parse_guid(const uint8_t* guid) + { + GUID result; + result.data1 = big_endian_to_host(guid + 0); + result.data2 = big_endian_to_host(guid + 4); + result.data3 = big_endian_to_host(guid + 6); + memcpy(result.data4, guid + 8, 8); + return result; + } + static bool is_valid_gpt_header(const GPTHeader& header, uint32_t sector_size) { if (memcmp(header.signature, "EFI PART", 8) != 0) @@ -137,19 +160,19 @@ namespace Kernel return true; } - static GPTHeader parse_gpt_header(const BAN::Vector lba1) + static GPTHeader parse_gpt_header(const BAN::Vector& lba1) { GPTHeader header; memset(&header, 0, sizeof(header)); memcpy(header.signature, lba1.data(), 8); - memcpy(header.guid, lba1.data() + 56, 16); header.revision = little_endian_to_host(lba1.data() + 8); header.size = little_endian_to_host(lba1.data() + 12); header.crc32 = little_endian_to_host(lba1.data() + 16); header.my_lba = little_endian_to_host(lba1.data() + 24); header.first_lba = little_endian_to_host(lba1.data() + 40); header.last_lba = little_endian_to_host(lba1.data() + 48); + header.guid = parse_guid(lba1.data() + 56); header.partition_entry_lba = little_endian_to_host(lba1.data() + 72); header.partition_entry_count = little_endian_to_host(lba1.data() + 80); header.partition_entry_size = little_endian_to_host(lba1.data() + 84); @@ -160,7 +183,7 @@ namespace Kernel bool DiskDevice::initialize_partitions() { BAN::Vector lba1(sector_size()); - if (!read(1, 1, lba1.data())) + if (!read_sectors(1, 1, lba1.data())) return false; GPTHeader header = parse_gpt_header(lba1); @@ -170,14 +193,13 @@ namespace Kernel return false; } - BAN::Vector entry_array; - { - uint32_t bytes = header.partition_entry_count * header.partition_entry_size; - uint32_t sectors = (bytes + sector_size() - 1) / sector_size(); - MUST(entry_array.resize(sectors * sector_size())); - if (!read(header.partition_entry_lba, sectors, entry_array.data())) - return false; - } + uint32_t size = header.partition_entry_count * header.partition_entry_size; + if (uint32_t remainder = size % sector_size()) + size += sector_size() - remainder; + + BAN::Vector entry_array(size); + if (!read_sectors(header.partition_entry_lba, size / sector_size(), entry_array.data())) + return false; if (!is_valid_gpt_crc32(header, lba1, entry_array)) { @@ -188,31 +210,15 @@ namespace Kernel for (uint32_t i = 0; i < header.partition_entry_count; i++) { uint8_t* partition_data = entry_array.data() + header.partition_entry_size * i; - - Partition partition; - memcpy(partition.type_guid, partition_data, 16); - memcpy(partition.guid, partition_data + 16, 16); - memcpy(partition.name, partition_data + 56, 72); - partition.start_lba = little_endian_to_host(partition_data + 32); - partition.end_lba = little_endian_to_host(partition_data + 40); - partition.attributes = little_endian_to_host(partition_data + 48); - - MUST(m_partitions.push_back(partition)); - } - - for (const Partition& partition : m_partitions) - { - uint8_t zero[16] = {}; - if (memcmp(partition.type_guid, zero, 16) == 0) - continue; - - dprintln("partition:"); - dprintln(" type {16H}{16H}", *(uint64_t*)partition.type_guid, *(uint64_t*)(partition.type_guid + 1)); - dprintln(" guid {16H}{16H}", *(uint64_t*)partition.guid, *(uint64_t*)(partition.guid + 1)); - dprintln(" start {16H}", partition.start_lba); - dprintln(" end {16H}", partition.end_lba); - dprintln(" attr {16H}", partition.attributes); - dprintln(" name {}", partition.name); + MUST(m_partitions.emplace_back( + *this, + parse_guid(partition_data + 0), + parse_guid(partition_data + 16), + little_endian_to_host(partition_data + 32), + little_endian_to_host(partition_data + 40), + little_endian_to_host(partition_data + 48), + (const char*)(partition_data + 56) + )); } return true; @@ -224,6 +230,24 @@ namespace Kernel { ASSERT(s_instance == nullptr); s_instance = new DiskIO(); + +#if 1 + for (DiskDevice* device : s_instance->m_devices) + { + for (auto& partition : device->partitions()) + { + if (!partition.is_used()) + continue; + + if (memcmp(&partition.type(), "\x0F\xC6\x3D\xAF\x84\x83\x47\x72\x8E\x79\x3D\x69\xD8\x47\x7D\xE4", 16) == 0) + { + auto ext2fs = MUST(Ext2FS::create(partition)); + VirtualFileSystem::initialize(ext2fs->root_inode()); + } + } + } +#endif + return true; } @@ -235,27 +259,46 @@ namespace Kernel DiskIO::DiskIO() { - auto add_ata_device = [this](uint16_t io_base, uint16_t ctl_base, uint8_t slave_bit) + try_add_device(ATADevice::create(ATA_DEVICE_PRIMARY, ATA_DEVICE_PRIMARY + 0x206, 0)); + try_add_device(ATADevice::create(ATA_DEVICE_PRIMARY, ATA_DEVICE_PRIMARY + 0x206, ATA_DEVICE_SLAVE_BIT)); + try_add_device(ATADevice::create(ATA_DEVICE_SECONDARY, ATA_DEVICE_SECONDARY + 0x206, 0)); + try_add_device(ATADevice::create(ATA_DEVICE_SECONDARY, ATA_DEVICE_SECONDARY + 0x206, ATA_DEVICE_SLAVE_BIT)); + } + + void DiskIO::try_add_device(DiskDevice* device) + { + if (!device) + return; + if (!device->initialize()) { - DiskDevice* device = ATADevice::create(io_base, ctl_base, slave_bit); - if (!device) - return; - if (!device->initialize()) - { - delete device; - return; - } - if (!device->initialize_partitions()) - { - delete device; - return; - } - MUST(m_devices.push_back(device)); - }; - add_ata_device(ATA_DEVICE_PRIMARY, ATA_DEVICE_PRIMARY + 0x206, 0); - add_ata_device(ATA_DEVICE_PRIMARY, ATA_DEVICE_PRIMARY + 0x206, ATA_DEVICE_SLAVE_BIT); - add_ata_device(ATA_DEVICE_SECONDARY, ATA_DEVICE_SECONDARY + 0x206, 0); - add_ata_device(ATA_DEVICE_SECONDARY, ATA_DEVICE_SECONDARY + 0x206, ATA_DEVICE_SLAVE_BIT); + delete device; + return; + } + if (!device->initialize_partitions()) + { + delete device; + return; + } + MUST(m_devices.push_back(device)); + } + + + DiskDevice::Partition::Partition(DiskDevice& device, const GUID& type, const GUID& guid, uint64_t start, uint64_t end, uint64_t attr, const char* name) + : m_device(device) + , m_type(type) + , m_guid(guid) + , m_lba_start(start) + , m_lba_end(end) + , m_attributes(attr) + { + memcpy(m_name, name, sizeof(m_name)); + } + + bool DiskDevice::Partition::read_sectors(uint32_t lba, uint32_t sector_count, uint8_t* buffer) + { + const uint32_t sectors_in_partition = m_lba_end - m_lba_start; + ASSERT(lba + sector_count < sectors_in_partition); + return m_device.read_sectors(m_lba_start + lba, sector_count, buffer); } } \ No newline at end of file diff --git a/kernel/kernel/FS/Ext2.cpp b/kernel/kernel/FS/Ext2.cpp new file mode 100644 index 00000000..1c6574e7 --- /dev/null +++ b/kernel/kernel/FS/Ext2.cpp @@ -0,0 +1,392 @@ +#include +#include +#include + +#include + +#define EXT2_DEBUG_PRINT 1 + +namespace Kernel +{ + + namespace Ext2::Enum + { + + 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 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 -- + IFSOKC = 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, + }; + + } + + bool Ext2Inode::is_directory() const + { + return m_inode.mode & Ext2::Enum::IFDIR; + } + + bool Ext2Inode::is_regular_file() const + { + return m_inode.mode & Ext2::Enum::IFREG; + } + + BAN::ErrorOr> Ext2Inode::read_all() const + { + return BAN::Error::from_string("not implemented"); + } + + BAN::ErrorOr> Ext2Inode::directory_find(BAN::StringView name) const + { + 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) + { + Ext2::LinkedDirectoryEntry* entry = (Ext2::LinkedDirectoryEntry*)entry_addr; + + BAN::StringView entry_name = BAN::StringView(entry->name, entry->name_len); + if (entry->inode && name == entry_name) + { + Ext2::Inode asked_inode = TRY(m_fs->read_inode(entry->inode)); + return BAN::RefCounted(new Ext2Inode(m_fs, BAN::move(asked_inode), entry_name)); + } + + entry_addr += entry->rec_len; + } + } + + return BAN::Error::from_string("Could not find the asked inode"); + } + + BAN::ErrorOr>> Ext2Inode::directory_inodes() const + { + 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) + { + 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; + } + } + + // FIXME: for now we can just assert that we found everything in direct pointers + ASSERT(data_blocks_found == data_block_count); + + return inodes; + } + + BAN::ErrorOr Ext2FS::create(DiskDevice::Partition& partition) + { + Ext2FS* ext2fs = new Ext2FS(partition); + if (ext2fs == nullptr) + return BAN::Error::from_string("Could not allocate Ext2FS"); + TRY(ext2fs->initialize_superblock()); + TRY(ext2fs->initialize_block_group_descriptors()); + TRY(ext2fs->initialize_root_inode()); + return ext2fs; + } + + BAN::ErrorOr Ext2FS::initialize_superblock() + { + const uint32_t sector_size = m_partition.device().sector_size(); + ASSERT(1024 % sector_size == 0); + + // Read superblock from disk + { + uint8_t* superblock_buffer = (uint8_t*)kmalloc(1024); + if (superblock_buffer == nullptr) + return BAN::Error::from_string("Could not allocate memory for superblocks"); + BAN::ScopeGuard _([superblock_buffer] { kfree(superblock_buffer); }); + + uint32_t lba = 1024 / sector_size; + uint32_t sector_count = 1024 / sector_size; + + if (!m_partition.read_sectors(lba, sector_count, superblock_buffer)) + return BAN::Error::from_string("Could not read from partition"); + + memcpy(&m_superblock, superblock_buffer, sizeof(Ext2::Superblock)); + } + + if (m_superblock.magic != 0xEF53) + return BAN::Error::from_string("Not a ext2 filesystem"); + + if (m_superblock.rev_level < 1) + { + memset(m_superblock.__extension_start, 0, sizeof(Ext2::Superblock) - offsetof(Ext2::Superblock, Ext2::Superblock::__extension_start)); + m_superblock.first_ino = 11; + m_superblock.inode_size = 128; + } + + 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)); + ASSERT(!(m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_META_BG)); + ASSERT(!(m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_RECOVER)); + +#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); +#endif + + return {}; + } + + BAN::ErrorOr Ext2FS::initialize_block_group_descriptors() + { + const uint32_t sector_size = m_partition.device().sector_size(); + const uint32_t block_size = 1024 << m_superblock.log_block_size; + const uint32_t sectors_per_block = block_size / sector_size; + ASSERT(block_size % sector_size == 0); + + uint32_t number_of_block_groups = BAN::Math::div_round_up(m_superblock.inodes_count, m_superblock.inodes_per_group); + uint32_t number_of_block_groups_check = BAN::Math::div_round_up(m_superblock.blocks_count, m_superblock.blocks_per_group); + if (number_of_block_groups != number_of_block_groups_check) + return BAN::Error::from_string("Ambiguous number of blocks"); + + uint32_t block_group_descriptor_table_block = m_superblock.first_data_block + 1; + uint32_t block_group_descriptor_table_sector_count = BAN::Math::div_round_up(32u * number_of_block_groups, sector_size); + + uint8_t* block_group_descriptor_table_buffer = (uint8_t*)kmalloc(block_group_descriptor_table_sector_count * sector_size); + if (block_group_descriptor_table_buffer == nullptr) + return BAN::Error::from_string("Could not allocate memory for block group descriptor table"); + BAN::ScopeGuard _([block_group_descriptor_table_buffer] { kfree(block_group_descriptor_table_buffer); }); + + if (!m_partition.read_sectors( + block_group_descriptor_table_block * sectors_per_block, + block_group_descriptor_table_sector_count, + block_group_descriptor_table_buffer + )) + return BAN::Error::from_string("Could not read from partition"); + + TRY(m_block_group_descriptors.resize(number_of_block_groups)); + + for (uint32_t i = 0; i < number_of_block_groups; i++) + { + memcpy(&m_block_group_descriptors[i], block_group_descriptor_table_buffer + 32u * i, sizeof(Ext2::BlockGroupDescriptor)); + +#if EXT2_DEBUG_PRINT + dprintln("block group descriptor {}", i); + dprintln(" block bitmap {}", m_block_group_descriptors[i].block_bitmap); + dprintln(" inode bitmap {}", m_block_group_descriptors[i].inode_bitmap); + dprintln(" inode table {}", m_block_group_descriptors[i].inode_table); + dprintln(" unalloc blocks {}", m_block_group_descriptors[i].free_blocks_count); + dprintln(" unalloc inodes {}", m_block_group_descriptors[i].free_inodes_count); +#endif + } + + return {}; + } + + BAN::ErrorOr Ext2FS::initialize_root_inode() + { + m_root_inode = BAN::RefCounted(new Ext2Inode(this, TRY(read_inode(Ext2::Enum::ROOT_INO)), "")); +#if EXT2_DEBUG_PRINT + dprintln("root inode:"); + dprintln(" created {}", ext2_root_inode().ctime); + dprintln(" modified {}", ext2_root_inode().mtime); + dprintln(" accessed {}", ext2_root_inode().atime); +#endif + return {}; + } + + BAN::ErrorOr Ext2FS::read_inode(uint32_t inode) + { + uint32_t block_size = 1024 << m_superblock.log_block_size; + + uint32_t inode_block_group = (inode - 1) / m_superblock.inodes_per_group; + uint32_t local_inode_index = (inode - 1) % m_superblock.inodes_per_group; + + uint32_t inode_table_offset_blocks = (local_inode_index * m_superblock.inode_size) / block_size; + uint32_t inode_block_offset = (local_inode_index * m_superblock.inode_size) % block_size; + + uint32_t inode_block = m_block_group_descriptors[inode_block_group].inode_table + inode_table_offset_blocks; + + auto inode_block_buffer = TRY(read_block(inode_block)); + Ext2::Inode ext2_inode; + memcpy(&ext2_inode, inode_block_buffer.data() + inode_block_offset, sizeof(Ext2::Inode)); + return ext2_inode; + } + + BAN::ErrorOr> Ext2FS::read_block(uint32_t block) + { + const uint32_t sector_size = m_partition.device().sector_size(); + uint32_t block_size = 1024 << m_superblock.log_block_size; + ASSERT(block_size % sector_size == 0); + uint32_t sectors_per_block = block_size / sector_size; + + BAN::Vector block_buffer; + TRY(block_buffer.resize(block_size)); + + if (!m_partition.read_sectors(block * sectors_per_block, sectors_per_block, block_buffer.data())) + return BAN::Error::from_string("Could not read from partition"); + + return block_buffer; + } + + const Ext2::Inode& Ext2FS::ext2_root_inode() const + { + return reinterpret_cast(m_root_inode.operator->())->m_inode; + } + +} \ No newline at end of file diff --git a/kernel/kernel/FS/VirtualFileSystem.cpp b/kernel/kernel/FS/VirtualFileSystem.cpp new file mode 100644 index 00000000..1443841c --- /dev/null +++ b/kernel/kernel/FS/VirtualFileSystem.cpp @@ -0,0 +1,26 @@ +#include + +namespace Kernel +{ + + static VirtualFileSystem* s_instance = nullptr; + + void VirtualFileSystem::initialize(BAN::RefCounted root_inode) + { + ASSERT(s_instance == nullptr); + s_instance = new VirtualFileSystem(root_inode); + ASSERT(s_instance); + } + + VirtualFileSystem& VirtualFileSystem::get() + { + ASSERT(s_instance); + return *s_instance; + } + + bool VirtualFileSystem::is_initialized() + { + return s_instance != nullptr; + } + +} \ No newline at end of file diff --git a/kernel/kernel/Shell.cpp b/kernel/kernel/Shell.cpp index 669b02e0..be138f91 100644 --- a/kernel/kernel/Shell.cpp +++ b/kernel/kernel/Shell.cpp @@ -9,6 +9,8 @@ #include #include +#include + #include #define TTY_PRINT(...) Formatter::print([this](char c) { m_tty->putchar(c); }, __VA_ARGS__) @@ -144,10 +146,7 @@ argument_done: else if (arguments.front() == "date") { if (arguments.size() != 1) - { - TTY_PRINTLN("'date' does not support command line arguments"); - return; - } + return TTY_PRINTLN("'date' does not support command line arguments"); auto time = RTC::get_current_time(); TTY_PRINTLN("{}", time); } @@ -155,7 +154,7 @@ argument_done: { if (arguments.size() > 1) { - TTY_PRINT("{}", arguments[1]); + return TTY_PRINT("{}", arguments[1]); for (size_t i = 2; i < arguments.size(); i++) TTY_PRINT(" {}", arguments[i]); } @@ -164,10 +163,7 @@ argument_done: else if (arguments.front() == "clear") { if (arguments.size() != 1) - { - TTY_PRINTLN("'clear' does not support command line arguments"); - return; - } + return TTY_PRINTLN("'clear' does not support command line arguments"); m_tty->clear(); m_tty->set_cursor_position(0, 0); } @@ -205,29 +201,19 @@ argument_done: else if (arguments.front() == "memory") { if (arguments.size() != 1) - { - TTY_PRINTLN("'memory' does not support command line arguments"); - return; - } + return TTY_PRINTLN("'memory' does not support command line arguments"); kmalloc_dump_info(); } else if (arguments.front() == "sleep") { if (arguments.size() != 1) - { - TTY_PRINTLN("'sleep' does not support command line arguments"); - return; - } + return TTY_PRINTLN("'sleep' does not support command line arguments"); PIT::sleep(5000); - TTY_PRINTLN("done"); } else if (arguments.front() == "cpuinfo") { if (arguments.size() != 1) - { - TTY_PRINTLN("'cpuinfo' does not support command line arguments"); - return; - } + return TTY_PRINTLN("'cpuinfo' does not support command line arguments"); uint32_t ecx, edx; auto vendor = CPUID::get_vendor(); @@ -248,17 +234,11 @@ argument_done: else if (arguments.front() == "random") { if (arguments.size() != 1) - { - TTY_PRINTLN("'random' does not support command line arguments"); - return; - } + return TTY_PRINTLN("'random' does not support command line arguments"); uint32_t ecx, edx; CPUID::get_features(ecx, edx); if (!(ecx & CPUID::Features::ECX_RDRND)) - { - TTY_PRINTLN("cpu does not support RDRAND instruction"); - return; - } + return TTY_PRINTLN("cpu does not support RDRAND instruction"); for (int i = 0; i < 10; i++) { @@ -270,16 +250,56 @@ argument_done: else if (arguments.front() == "reboot") { if (arguments.size() != 1) - { - TTY_PRINTLN("'reboot' does not support command line arguments"); - return; - } + return TTY_PRINTLN("'reboot' does not support command line arguments"); uint8_t good = 0x02; while (good & 0x02) good = IO::inb(0x64); IO::outb(0x64, 0xFE); asm volatile("cli; hlt"); } + else if (arguments.front() == "ls") + { + if (!VirtualFileSystem::is_initialized()) + return TTY_PRINTLN("VFS not initialized :("); + + if (arguments.size() > 2) + return TTY_PRINTLN("usage: 'ls [path]'"); + + BAN::StringView path = (arguments.size() == 2) ? arguments[1].sv() : "/"; + if (path.front() != '/') + return TTY_PRINTLN("ls currently works only with absolute paths"); + path = path.substring(1); + + auto directory = VirtualFileSystem::get().root_inode(); + ASSERT(directory->is_directory()); + + if (arguments.size() == 2) + { + auto path_parts = MUST(arguments[1].sv().split('/')); + for (auto part : path_parts) + { + auto inode_or_error = directory->directory_find(part); + if (inode_or_error.is_error()) + return TTY_PRINTLN("{}", inode_or_error.get_error().get_message()); + directory = inode_or_error.value(); + if (!directory->is_directory()) + return TTY_PRINTLN("expected argument to be path to directory"); + } + } + + auto inodes_or_error = directory->directory_inodes(); + if (inodes_or_error.is_error()) + return TTY_PRINTLN("{}", inodes_or_error.get_error().get_message()); + auto& inodes = inodes_or_error.value(); + + TTY_PRINTLN("/{}", path); + for (auto& inode : inodes) + if (inode->is_directory()) + TTY_PRINTLN(" {7} \e[34m{}\e[m", inode->size(), inode->name()); + for (auto& inode : inodes) + if (!inode->is_directory()) + TTY_PRINTLN(" {7} {}", inode->size(), inode->name()); + } else { TTY_PRINTLN("unrecognized command '{}'", arguments.front());