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/Errors.cpp
|
||||||
kernel/Font.cpp
|
kernel/Font.cpp
|
||||||
kernel/FS/DevFS/FileSystem.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/Inode.cpp
|
||||||
kernel/FS/Pipe.cpp
|
kernel/FS/Pipe.cpp
|
||||||
kernel/FS/RamFS/FileSystem.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/Function.h>
|
||||||
#include <BAN/StringView.h>
|
#include <kernel/FS/Ext2/FileSystem.h>
|
||||||
#include <kernel/FS/Ext2.h>
|
#include <kernel/FS/Ext2/Inode.h>
|
||||||
#include <kernel/Timer/Timer.h>
|
#include <kernel/Timer/Timer.h>
|
||||||
|
|
||||||
#define EXT2_DEBUG_PRINT 0
|
#define EXT2_DEBUG_PRINT 1
|
||||||
#define VERIFY_INODE_EXISTANCE 1
|
|
||||||
|
|
||||||
namespace Kernel
|
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
|
blksize_t Ext2Inode::blksize() const
|
||||||
{
|
{
|
||||||
return m_fs.block_size();
|
return m_fs.block_size();
|
||||||
|
@ -556,7 +406,7 @@ namespace Kernel
|
||||||
.mtime = (uint32_t)current_time.tv_sec,
|
.mtime = (uint32_t)current_time.tv_sec,
|
||||||
.dtime = 0,
|
.dtime = 0,
|
||||||
.gid = (uint16_t)gid,
|
.gid = (uint16_t)gid,
|
||||||
.links_count = 1,
|
.links_count = 5,
|
||||||
.blocks = 0,
|
.blocks = 0,
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
.osd1 = 0,
|
.osd1 = 0,
|
||||||
|
@ -725,383 +575,4 @@ needs_new_block:
|
||||||
return BAN::Error::from_errno(ENOENT);
|
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/ScopeGuard.h>
|
||||||
#include <BAN/StringView.h>
|
#include <BAN/StringView.h>
|
||||||
#include <kernel/FS/DevFS/FileSystem.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/FileSystem.h>
|
||||||
#include <kernel/FS/RamFS/Inode.h>
|
#include <kernel/FS/RamFS/Inode.h>
|
||||||
#include <kernel/FS/VirtualFileSystem.h>
|
#include <kernel/FS/VirtualFileSystem.h>
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
#include <BAN/ScopeGuard.h>
|
#include <BAN/ScopeGuard.h>
|
||||||
#include <BAN/StringView.h>
|
#include <BAN/StringView.h>
|
||||||
#include <BAN/UTF8.h>
|
#include <BAN/UTF8.h>
|
||||||
#include <kernel/FS/Ext2.h>
|
|
||||||
#include <kernel/FS/VirtualFileSystem.h>
|
#include <kernel/FS/VirtualFileSystem.h>
|
||||||
#include <kernel/PCI.h>
|
#include <kernel/PCI.h>
|
||||||
#include <kernel/Storage/StorageDevice.h>
|
#include <kernel/Storage/StorageDevice.h>
|
||||||
|
|
Loading…
Reference in New Issue