#pragma once #include "fs/VFS.h" #include "fs/devices/DeviceRegistry.h" #include #define EXT2_MAGIC 0xef53 #define EXT2_REQUIRED_COMPAT_D_TYPE 0x0002 namespace Ext2 { struct [[gnu::packed]] Superblock { u32 nr_inodes; u32 nr_blocks; u32 nr_reserved_blocks; u32 nr_free_blocks; u32 nr_free_inodes; u32 first_data_block; u32 log_block_size; u32 log_fragment_size; u32 blocks_per_block_group; u32 fragments_per_block_group; u32 inodes_per_block_group; u32 last_mount_time; u32 last_write_time; u16 mounts_since_last_fsck; u16 mounts_allowed_before_fsck; u16 signature; u16 fs_state; u16 error_action; u16 minor_version; u32 last_fsck_time; u32 fsck_time_interval; u32 os_id; u32 major_version; u16 reserved_block_uid; u16 reserved_block_gid; struct [[gnu::packed]] { u32 first_non_reserved_inode; u16 inode_size; u16 this_block_group; u32 optional_features; u32 required_features; u32 ro_features; u8 fsid[16]; u8 name[16]; u8 last_mountpoint[64]; u32 compression_algs; u8 nr_preallocated_blocks_for_files; u8 nr_preallocated_blocks_for_dirs; u16 unused; u8 journal_id[16]; u32 journal_inode; u32 journal_device; u32 orphan_inode_head; } ext_superblock; u8 padding[1024 - 236]; }; struct [[gnu::packed]] BlockGroupDescriptor { u32 block_usage_addr; u32 inode_usage_addr; u32 inode_table_start; u16 nr_free_blocks; u16 nr_free_inodes; u16 nr_directories; u8 padding[32 - 18]; }; struct [[gnu::packed]] RawInode { u16 mode; u16 uid; u32 size_low; u32 atime; u32 create_time; u32 mtime; u32 delete_time; u16 gid; u16 nlinks; u32 sectors_used; u32 flags; u32 os_specific_1; u32 direct_pointers[12]; u32 singly_indirect_ptr; u32 doubly_indirect_ptr; u32 triply_indirect_ptr; u32 gen_number; u32 extended_attrs; u32 size_high; u32 frag_addr; u32 os_specific_2[3]; }; struct [[gnu::packed]] RawDirectoryEntry { u32 inum; u16 size; union { u16 name_length; struct { u8 name_length_low; u8 type; }; }; char name[4]; // Names should be padded to a multiple of 4 bytes. }; static_assert(sizeof(Superblock) == 1024); static_assert(sizeof(BlockGroupDescriptor) == 32); static_assert(sizeof(RawInode) == 128); class Inode; class FileSystem : public VFS::FileSystem { public: SharedPtr root_inode() const override { return m_root_inode; } Result> create_file_inode(mode_t) override { return err(EROFS); } Result> create_dir_inode(SharedPtr, mode_t) override { return err(EROFS); } Result> create_device_inode(u32, u32, mode_t) override { return err(EROFS); } Result> create_symlink_inode(StringView) override { return err(EROFS); } Result set_mount_dir(SharedPtr) override; Result reset_mount_dir() override; bool is_readonly() const override { return true; } static Result> create(SharedPtr host_device); dev_t host_device_id() const override { return m_host_device_id; } Result> find_inode_by_number(ino_t inode, bool initialize_dir_now = false); Result find_block_group_descriptor(u32 index); virtual ~FileSystem() = default; private: FileSystem(); SharedPtr m_root_inode; SharedPtr m_host_device; dev_t m_host_device_id; Superblock m_superblock; u64 m_block_size; u64 m_block_groups; u32 m_inode_size { 128 }; bool m_dirs_have_type_field { false }; bool m_uses_extended_size { false }; // FIXME: This inode cache will keep all inodes in it alive despite having no other references to it, but we're // not worrying about that as for now the filesystem implementation is read-only. HashMap> m_inode_cache; HashMap m_block_group_descriptor_cache; friend class Inode; }; }