forked from Bananymous/banan-os
				
			Kernel: Split ext2 implementation to multiple files
This commit is contained in:
		
							parent
							
								
									d810644018
								
							
						
					
					
						commit
						7933265095
					
				|  | @ -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 | ||||
|  |  | |||
|  | @ -1,225 +0,0 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <BAN/String.h> | ||||
| #include <BAN/StringView.h> | ||||
| #include <kernel/Storage/StorageDevice.h> | ||||
| #include <kernel/FS/FileSystem.h> | ||||
| 
 | ||||
| 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<BAN::String> link_target() override; | ||||
| 		 | ||||
| 		virtual BAN::ErrorOr<void> directory_read_next_entries(off_t, DirectoryEntryList*, size_t) override; | ||||
| 		virtual BAN::ErrorOr<BAN::RefPtr<Inode>> directory_find_inode(BAN::StringView) override; | ||||
| 
 | ||||
| 		virtual BAN::ErrorOr<size_t> read(size_t, void*, size_t) override; | ||||
| 		virtual BAN::ErrorOr<size_t> write(size_t, const void*, size_t) override; | ||||
| 		virtual BAN::ErrorOr<void> truncate(size_t) override; | ||||
| 
 | ||||
| 		virtual BAN::ErrorOr<void> create_file(BAN::StringView, mode_t, uid_t, gid_t) override; | ||||
| 
 | ||||
| 	private: | ||||
| 		BAN::ErrorOr<void> for_data_block_index(uint32_t, const BAN::Function<void(uint32_t&)>&, bool allocate); | ||||
| 
 | ||||
| 		BAN::ErrorOr<uint32_t> data_block_index(uint32_t); | ||||
| 		BAN::ErrorOr<uint32_t> allocate_new_block(); | ||||
| 		BAN::ErrorOr<void> 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<BAN::RefPtr<Inode>> create(Ext2FS&, uint32_t); | ||||
| 
 | ||||
| 	private: | ||||
| 		Ext2FS& m_fs; | ||||
| 		Ext2::Inode m_inode; | ||||
| 		const uint32_t m_ino; | ||||
| 
 | ||||
| 		friend class Ext2FS; | ||||
| 		friend class BAN::RefPtr<Ext2Inode>; | ||||
| 	}; | ||||
| 
 | ||||
| 	class Ext2FS final : public FileSystem | ||||
| 	{ | ||||
| 	public:	 | ||||
| 		static BAN::ErrorOr<Ext2FS*> create(Partition&); | ||||
| 
 | ||||
| 		virtual BAN::RefPtr<Inode> root_inode() override { return m_root_inode; } | ||||
| 
 | ||||
| 	private: | ||||
| 		Ext2FS(Partition& partition) | ||||
| 			: m_partition(partition) | ||||
| 		{} | ||||
| 
 | ||||
| 		BAN::ErrorOr<void> initialize_superblock(); | ||||
| 		BAN::ErrorOr<void> initialize_root_inode(); | ||||
| 
 | ||||
| 		BAN::ErrorOr<uint32_t> create_inode(const Ext2::Inode&); | ||||
| 		BAN::ErrorOr<void> delete_inode(uint32_t); | ||||
| 		BAN::ErrorOr<void> resize_inode(uint32_t, size_t); | ||||
| 
 | ||||
| 		void read_block(uint32_t, BAN::Span<uint8_t>); | ||||
| 		void write_block(uint32_t, BAN::Span<const uint8_t>); | ||||
| 		void sync_superblock(); | ||||
| 
 | ||||
| 		BAN::ErrorOr<uint32_t> 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<BlockLocation> 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<Inode> m_root_inode; | ||||
| 		BAN::Vector<uint32_t> m_superblock_backups; | ||||
| 
 | ||||
| 		Ext2::Superblock m_superblock; | ||||
| 
 | ||||
| 		friend class Ext2Inode; | ||||
| 	}; | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,262 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| 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, | ||||
| 		}; | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,58 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <kernel/Storage/StorageDevice.h> | ||||
| #include <kernel/FS/FileSystem.h> | ||||
| #include <kernel/FS/Ext2/Inode.h> | ||||
| 
 | ||||
| namespace Kernel | ||||
| { | ||||
| 
 | ||||
| 	class Ext2FS final : public FileSystem | ||||
| 	{ | ||||
| 	public:	 | ||||
| 		static BAN::ErrorOr<Ext2FS*> create(Partition&); | ||||
| 
 | ||||
| 		virtual BAN::RefPtr<Inode> root_inode() override { return m_root_inode; } | ||||
| 
 | ||||
| 	private: | ||||
| 		Ext2FS(Partition& partition) | ||||
| 			: m_partition(partition) | ||||
| 		{} | ||||
| 
 | ||||
| 		BAN::ErrorOr<void> initialize_superblock(); | ||||
| 		BAN::ErrorOr<void> initialize_root_inode(); | ||||
| 
 | ||||
| 		BAN::ErrorOr<uint32_t> create_inode(const Ext2::Inode&); | ||||
| 		BAN::ErrorOr<void> delete_inode(uint32_t); | ||||
| 		BAN::ErrorOr<void> resize_inode(uint32_t, size_t); | ||||
| 
 | ||||
| 		void read_block(uint32_t, BAN::Span<uint8_t>); | ||||
| 		void write_block(uint32_t, BAN::Span<const uint8_t>); | ||||
| 		void sync_superblock(); | ||||
| 
 | ||||
| 		BAN::ErrorOr<uint32_t> 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<BlockLocation> 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<Inode> m_root_inode; | ||||
| 		BAN::Vector<uint32_t> m_superblock_backups; | ||||
| 
 | ||||
| 		Ext2::Superblock m_superblock; | ||||
| 
 | ||||
| 		friend class Ext2Inode; | ||||
| 	}; | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,67 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <BAN/String.h> | ||||
| #include <BAN/StringView.h> | ||||
| #include <kernel/FS/Ext2/Definitions.h> | ||||
| #include <kernel/FS/Inode.h> | ||||
| 
 | ||||
| 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<BAN::String> link_target() override; | ||||
| 		 | ||||
| 		virtual BAN::ErrorOr<void> directory_read_next_entries(off_t, DirectoryEntryList*, size_t) override; | ||||
| 		virtual BAN::ErrorOr<BAN::RefPtr<Inode>> directory_find_inode(BAN::StringView) override; | ||||
| 
 | ||||
| 		virtual BAN::ErrorOr<size_t> read(size_t, void*, size_t) override; | ||||
| 		virtual BAN::ErrorOr<size_t> write(size_t, const void*, size_t) override; | ||||
| 		virtual BAN::ErrorOr<void> truncate(size_t) override; | ||||
| 
 | ||||
| 		virtual BAN::ErrorOr<void> create_file(BAN::StringView, mode_t, uid_t, gid_t) override; | ||||
| 
 | ||||
| 	private: | ||||
| 		BAN::ErrorOr<void> for_data_block_index(uint32_t, const BAN::Function<void(uint32_t&)>&, bool allocate); | ||||
| 
 | ||||
| 		BAN::ErrorOr<uint32_t> data_block_index(uint32_t); | ||||
| 		BAN::ErrorOr<uint32_t> allocate_new_block(); | ||||
| 		BAN::ErrorOr<void> 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<BAN::RefPtr<Inode>> create(Ext2FS&, uint32_t); | ||||
| 
 | ||||
| 	private: | ||||
| 		Ext2FS& m_fs; | ||||
| 		Ext2::Inode m_inode; | ||||
| 		const uint32_t m_ino; | ||||
| 
 | ||||
| 		friend class Ext2FS; | ||||
| 		friend class BAN::RefPtr<Ext2Inode>; | ||||
| 	}; | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,396 @@ | |||
| #include <BAN/ScopeGuard.h> | ||||
| #include <kernel/FS/Ext2/FileSystem.h> | ||||
| 
 | ||||
| #define EXT2_DEBUG_PRINT 1 | ||||
| 
 | ||||
| namespace Kernel | ||||
| { | ||||
| 
 | ||||
| 	BAN::ErrorOr<Ext2FS*> 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<void> 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<uint32_t>(sizeof(Ext2::Superblock), sector_size); | ||||
| 
 | ||||
| 			BAN::Vector<uint8_t> 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<uint8_t> 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<void> 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<uint32_t> 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<uint8_t> bgd_buffer; | ||||
| 		TRY(bgd_buffer.resize(block_size)); | ||||
| 
 | ||||
| 		BAN::Vector<uint8_t> 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<uint8_t> 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<const uint8_t> 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<uint32_t>(superblock_bytes, sector_size); | ||||
| 
 | ||||
| 		BAN::Vector<uint8_t> 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<uint32_t> 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<uint8_t> bgd_buffer; | ||||
| 		TRY(bgd_buffer.resize(block_size)); | ||||
| 
 | ||||
| 		BAN::Vector<uint8_t> 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::BlockLocation> Ext2FS::locate_inode(uint32_t ino) | ||||
| 	{ | ||||
| 		ASSERT(ino < superblock().inodes_count); | ||||
| 
 | ||||
| 		const uint32_t block_size = this->block_size(); | ||||
| 
 | ||||
| 		BAN::Vector<uint8_t> 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 | ||||
| 		}; | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
|  | @ -1,163 +1,13 @@ | |||
| #include <BAN/ScopeGuard.h> | ||||
| #include <BAN/StringView.h> | ||||
| #include <kernel/FS/Ext2.h> | ||||
| #include <BAN/Function.h> | ||||
| #include <kernel/FS/Ext2/FileSystem.h> | ||||
| #include <kernel/FS/Ext2/Inode.h> | ||||
| #include <kernel/Timer/Timer.h> | ||||
| 
 | ||||
| #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*> 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<void> 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<uint32_t>(sizeof(Ext2::Superblock), sector_size); | ||||
| 
 | ||||
| 			BAN::Vector<uint8_t> 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<uint8_t> 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<void> 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<uint32_t> 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<uint8_t> bgd_buffer; | ||||
| 		TRY(bgd_buffer.resize(block_size)); | ||||
| 
 | ||||
| 		BAN::Vector<uint8_t> 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<uint8_t> 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<const uint8_t> 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<uint32_t>(superblock_bytes, sector_size); | ||||
| 
 | ||||
| 		BAN::Vector<uint8_t> 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<uint32_t> 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<uint8_t> bgd_buffer; | ||||
| 		TRY(bgd_buffer.resize(block_size)); | ||||
| 
 | ||||
| 		BAN::Vector<uint8_t> 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::BlockLocation> Ext2FS::locate_inode(uint32_t ino) | ||||
| 	{ | ||||
| 		ASSERT(ino < superblock().inodes_count); | ||||
| 
 | ||||
| 		const uint32_t block_size = this->block_size(); | ||||
| 
 | ||||
| 		BAN::Vector<uint8_t> 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 | ||||
| 		}; | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
|  | @ -1,7 +1,7 @@ | |||
| #include <BAN/ScopeGuard.h> | ||||
| #include <BAN/StringView.h> | ||||
| #include <kernel/FS/DevFS/FileSystem.h> | ||||
| #include <kernel/FS/Ext2.h> | ||||
| #include <kernel/FS/Ext2/FileSystem.h> | ||||
| #include <kernel/FS/RamFS/FileSystem.h> | ||||
| #include <kernel/FS/RamFS/Inode.h> | ||||
| #include <kernel/FS/VirtualFileSystem.h> | ||||
|  |  | |||
|  | @ -2,7 +2,6 @@ | |||
| #include <BAN/ScopeGuard.h> | ||||
| #include <BAN/StringView.h> | ||||
| #include <BAN/UTF8.h> | ||||
| #include <kernel/FS/Ext2.h> | ||||
| #include <kernel/FS/VirtualFileSystem.h> | ||||
| #include <kernel/PCI.h> | ||||
| #include <kernel/Storage/StorageDevice.h> | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue