Эх сурвалжийг харах

refactor: wrap ext4_rs lib

liujingx 1 жил өмнө
commit
f188231dec

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+/target

+ 30 - 0
Cargo.lock

@@ -0,0 +1,30 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "bitflags"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "ext4_rs"
+version = "0.1.0"
+dependencies = [
+ "bitflags",
+ "byteorder",
+ "log",
+]
+
+[[package]]
+name = "log"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"

+ 9 - 0
Cargo.toml

@@ -0,0 +1,9 @@
+[package]
+name = "ext4_rs"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+bitflags = "2.2.1"
+byteorder = "1.5.0"
+log = "0.4"

BIN
doc/bugs.png


BIN
doc/checksum.png


+ 237 - 0
doc/doc.md

@@ -0,0 +1,237 @@
+# ext4 jbd2
+
+https://github.com/yuoo655/ext4_rs
+
+https://github.com/yuoo655/jbd2_rs
+
+
+# ext4 与 ext2/ext3 区别
+| 功能                     | ext2 | ext3 | ext4 |
+|-------------------------|------|------|------|
+| 日志记录                | ❌    | ✅   | ✅   |
+| 目录索引                | ❌    | ✅   | ✅   |
+| 在线扩容                | ❌    | ✅   | ✅   |
+| 在线碎片整理            | ❌    | ❌   | ✅   |
+| 校验和                  | ❌    | ❌   | ✅   |
+| 是否采用extent          | ❌    | ❌   | ✅   |
+| 无限数量的子目录        | ❌    | ❌   | ✅   |
+| 预分配                  | ❌    | ❌   | ✅   |
+...
+
+# ext4支持状态
+| 操作         |支持情况| 
+|--------------|------|
+| mount        | ✅   |
+| open         | ✅   |
+| lsdir        | ✅   |
+| mkdir        | ✅   |
+| read_file    | ✅   |
+| read_link    | ✅   |
+| create_file  | ✅   |
+| write_file   | ✅   |
+| link         | ✅   |
+| umount       | ❌   |
+| file_remove  | ❌   |
+| dir_remove   | ❌   |
+
+# jbd2支持状态
+| 操作                | 支持情况 |
+|---------------------|----------|
+| load_journal        | ✅       |
+| journal_start       | ✅       |
+| transaction_start   | ✅       |
+| write_transaction   | ✅       |
+| transaction_stop    | ✅       |
+| journal_stop        | ✅       |
+| recover             | ✅       |
+| revoke block        | ❌       |
+| checksum            | ❌       |
+...
+
+# 独立组件
+
+![img](fs.png)
+
+
+```rust
+pub trait BlockDevice: Send + Sync + Any + Debug {
+    fn read_offset(&self, offset: usize) -> Vec<u8>;
+    fn write_offset(&self, offset: usize, data: &[u8]);
+}
+
+pub trait Jbd2: Send + Sync + Any + Debug {
+    fn load_journal(&mut self);
+    fn journal_start(&mut self);
+    fn transaction_start(&mut self);
+    fn write_transaction(&mut self, block_id: usize, block_data: Vec<u8>);
+    fn transaction_stop(&mut self);
+    fn journal_stop(&mut self);
+    fn recover(&mut self);
+}
+```
+# 打开文件
+
+从挂载点开始ext4_dir_find_entry遍历目录来,对比文件名,找到目标文件,提取direntry中的inode号,这一步也就是查找文件路径到文件inode的过程。
+
+```rust
+fn ext4_generic_open(path){
+    loop {
+        ext4_dir_find_entry(path)
+
+        if is_goal {
+            file.inode = dir_search_result.dentry.inode;
+            return Ok(EOK);
+        }
+    }
+}
+```
+
+# 读文件
+
+由于ext4默认所有文件都使用extent。extent记录了文件逻辑块号对应磁盘存储的物理块号。读取文件的过程就是寻找文件所有extent的过程。找到extent之后便可从extent中获取物理块号读出数据
+
+```rust
+pub struct Ext4Inode {
+    ...
+    pub block: [u32; 15],
+    ...
+}
+```
+![img](extent.png)
+
+
+```rust
+
+    pub fn ext4_file_read(&self, ext4_file: &mut Ext4File) -> Vec<u8> {
+
+        ...
+
+        ext4_find_all_extent(&inode_ref, &mut extents);
+
+        // 遍历extents向量,对每个extent,计算它的物理块号,然后调用read_block函数来读取数据块
+        for extent in extents {
+            let block_data = inode_ref.fs().block_device.read_offset(block_num as usize * BLOCK_SIZE);
+            file_data.extend(block_data);
+        }
+
+        file_data
+    }
+```
+
+# 创建文件
+
+- alloc inode
+- init inode
+- link
+
+分配inode
+```rust
+r = ext4_fs_alloc_inode(&mut child_inode_ref, ftype);
+```
+
+寻找inode位图找到第一个可用的位
+```rust
+ext4_bmap_bit_find_clr(data, 0, inodes_in_bg, &mut idx_in_bg);
+ext4_bmap_bit_set(&mut raw_data, idx_in_bg);
+```
+
+设置相应的inode计数
+```rust 
+/* Modify filesystem counters */
+bg.set_free_inodes_count(&super_block, free_inodes);
+/* Increment used directories counter */
+if is_dir {
+    used_dirs += 1;
+    bg.set_used_dirs_count(&super_block, used_dirs);
+}
+/* Decrease unused inodes count */
+bg.set_itable_unused(&super_block, unused);
+/* Update superblock */
+super_block.decrease_free_inodes_count();
+```
+
+init inode设置inode基础信息
+```rust
+...
+inode.ext4_inode_set_mode(mode);
+inode.ext4_inode_set_links_cnt(0);
+...
+```
+
+init inode extent 信息
+```rust
+pub fn ext4_extent_tree_init(inode_ref: &mut Ext4Inode) {
+    /* Initialize extent root header */
+    let mut header = unsafe { *ext4_inode_get_extent_header(inode_ref) };
+    ext4_extent_header_set_depth(&mut header, 0);
+    ext4_extent_header_set_entries_count(&mut header, 0);
+    ext4_extent_header_set_generation(&mut header, 0);
+    ext4_extent_header_set_magic(&mut header, EXT4_EXTENT_MAGIC);
+    ext4_extent_header_set_max_entries_count(&mut header, 4 as u16);
+}
+```
+
+再接着link inode号到文件名,目录项,父目录,首先找到父目录的目录项,再把当前文件的目录项添加到父目录项的尾部。
+```rust
+ext4_link::<Hal>(&mp,&root_inode,&mut child_inode_ref,path,name_len,false){
+
+    ext4_dir_find_entry::<A>(&parent_inode, &path, len as u32, &mut dir_search_result);
+
+    /* Add entry to parent directory */
+    ext4_dir_add_entry::<A>(parent_inode, child_inode, path, len);
+
+}
+```
+
+# 写文件
+
+查找文件逻辑块对应的物理块,如果没有对应物理块则分配一个物理块。
+```rust
+    pub fn ext4_file_write(&self, ext4_file: &mut Ext4File, data: &[u8], size: usize) {
+        ...
+        let mut size = size;
+        while size >= block_size {
+            while iblk_idx < iblock_last {
+                if iblk_idx < ifile_blocks {
+                    ext4_fs_append_inode_dblk(&mut inode_ref, &mut (iblk_idx as u32), &mut fblk);
+                }
+
+                iblk_idx += 1;
+
+                ...
+            }
+            size -= block_size;
+        }
+
+        for i in 0..fblock_count {
+            ...
+            self.block_device
+                .write_offset(offset, &data[idx..(idx + BLOCK_SIZE as usize)]);
+        }
+        ...
+        inode_ref.write_back_inode();
+    }
+```
+
+分配物理块同样要从block bitmap中查询, 当分配完物理块后,就可以填写extent信息了。再把记录了逻辑块和物理块对应信息的extent插入extent树中。最后在相应的物理块中写入数据。
+```rust
+ext4_balloc_alloc_block()
+ext4_ext_insert_extent()
+```
+
+# checksum
+创建文件,写入文件都涉及对meta data的修改。所有meta data都有crc检验信息。修改元数据后,需设置校验信息,然后写入磁盘
+![img](checksum.png)
+
+```rust
+例
+pub fn sync_inode_to_disk_with_csum(
+    &mut self,
+    block_device: Arc<dyn BlockDevice>,
+    super_block: &Ext4Superblock,
+    inode_id: u32,
+) -> Result<()> {
+    self.set_inode_checksum(super_block, inode_id);
+    self.sync_inode_to_disk(block_device, super_block, inode_id)
+}
+```

BIN
doc/ext4-disk-layout.jpg


BIN
doc/extent.png


BIN
doc/fs.png


BIN
doc/linux_ext.png


+ 166 - 0
src/constants.rs

@@ -0,0 +1,166 @@
+#![allow(unused)]
+
+use bitflags::bitflags;
+
+pub const EOK: usize = 0;
+
+#[allow(non_camel_case_types)]
+pub type ext4_lblk_t = u32;
+#[allow(non_camel_case_types)]
+pub type ext4_fsblk_t = u64;
+
+pub const EXT4_INODE_FLAG_EXTENTS: usize = 0x00080000; /* Inode uses extents */
+pub const EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE: u16 = 32;
+pub const EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE: u16 = 64;
+pub const EXT4_CRC32_INIT: u32 = 0xFFFFFFFF;
+pub const EXT4_EXTENT_MAGIC: u16 = 0xF30A;
+pub const EXT_INIT_MAX_LEN: u16 = 32768;
+pub const EXT_UNWRITTEN_MAX_LEN: u16 = 65535;
+
+pub const EXT4_GOOD_OLD_INODE_SIZE: u16 = 128;
+
+pub const EXT4_INODE_MODE_FIFO: usize = 0x1000;
+pub const EXT4_INODE_MODE_CHARDEV: usize = 0x2000;
+pub const EXT4_INODE_MODE_DIRECTORY: usize = 0x4000;
+pub const EXT4_INODE_MODE_BLOCKDEV: usize = 0x6000;
+pub const EXT4_INODE_MODE_FILE: usize = 0x8000;
+pub const EXT4_INODE_MODE_SOFTLINK: usize = 0xA000;
+pub const EXT4_INODE_MODE_SOCKET: usize = 0xC000;
+pub const EXT4_INODE_MODE_TYPE_MASK: u16 = 0xF000;
+
+pub const EXT_MAX_BLOCKS: ext4_lblk_t = core::u32::MAX;
+
+/// Maximum bytes in a path
+pub const PATH_MAX: usize = 4096;
+
+/// Maximum bytes in a file name
+pub const NAME_MAX: usize = 255;
+
+/// The upper limit for resolving symbolic links
+pub const SYMLINKS_MAX: usize = 40;
+
+#[allow(non_camel_case_types)]
+#[derive(Debug, PartialEq)]
+pub enum LibcOpenFlags {
+    O_ACCMODE,
+    O_RDONLY,
+    O_WRONLY,
+    O_RDWR,
+    O_CREAT,
+    O_EXCL,
+    O_NOCTTY,
+    O_TRUNC,
+    O_APPEND,
+    O_NONBLOCK,
+    O_SYNC,
+    O_ASYNC,
+    O_LARGEFILE,
+    O_DIRECTORY,
+    O_NOFOLLOW,
+    O_CLOEXEC,
+    O_DIRECT,
+    O_NOATIME,
+    O_PATH,
+    O_DSYNC,
+}
+
+pub const O_ACCMODE: u32 = 0o0003;
+pub const O_RDONLY: u32 = 0o00;
+pub const O_WRONLY: u32 = 0o01;
+pub const O_RDWR: u32 = 0o02;
+pub const O_CREAT: u32 = 0o0100;
+pub const O_EXCL: u32 = 0o0200;
+pub const O_NOCTTY: u32 = 0o0400;
+pub const O_TRUNC: u32 = 0o01000;
+pub const O_APPEND: u32 = 0o02000;
+pub const O_NONBLOCK: u32 = 0o04000;
+pub const O_SYNC: u32 = 0o4010000;
+pub const O_ASYNC: u32 = 0o020000;
+pub const O_LARGEFILE: u32 = 0o0100000;
+pub const O_DIRECTORY: u32 = 0o0200000;
+pub const O_NOFOLLOW: u32 = 0o0400000;
+pub const O_CLOEXEC: u32 = 0o2000000;
+pub const O_DIRECT: u32 = 0o040000;
+pub const O_NOATIME: u32 = 0o1000000;
+pub const O_PATH: u32 = 0o10000000;
+pub const O_DSYNC: u32 = 0o010000;
+
+pub const EPERM: usize = 1; /* Operation not permitted */
+pub const ENOENT: usize = 2; /* No such file or directory */
+pub const ESRCH: usize = 3; /* No such process */
+pub const EINTR: usize = 4; /* Interrupted system call */
+pub const EIO: usize = 5; /* I/O error */
+pub const ENXIO: usize = 6; /* No such device or address */
+pub const E2BIG: usize = 7; /* Argument list too long */
+pub const ENOEXEC: usize = 8; /* Exec format error */
+pub const EBADF: usize = 9; /* Bad file number */
+pub const ECHILD: usize = 10; /* No child processes */
+pub const EAGAIN: usize = 11; /* Try again */
+pub const ENOMEM: usize = 12; /* Out of memory */
+pub const EACCES: usize = 13; /* Permission denied */
+pub const EFAULT: usize = 14; /* Bad address */
+pub const ENOTBLK: usize = 15; /* Block device required */
+pub const EBUSY: usize = 16; /* Device or resource busy */
+pub const EEXIST: usize = 17; /* File exists */
+pub const EXDEV: usize = 18; /* Cross-device link */
+pub const ENODEV: usize = 19; /* No such device */
+pub const ENOTDIR: usize = 20; /* Not a directory */
+pub const EISDIR: usize = 21; /* Is a directory */
+pub const EINVAL: usize = 22; /* Invalid argument */
+pub const ENFILE: usize = 23; /* File table overflow */
+pub const EMFILE: usize = 24; /* Too many open files */
+pub const ENOTTY: usize = 25; /* Not a typewriter */
+pub const ETXTBSY: usize = 26; /* Text file busy */
+pub const EFBIG: usize = 27; /* File too large */
+pub const ENOSPC: usize = 28; /* No space left on device */
+pub const ESPIPE: usize = 29; /* Illegal seek */
+pub const EROFS: usize = 30; /* Read-only file system */
+pub const EMLINK: usize = 31; /* Too many links */
+pub const EPIPE: usize = 32; /* Broken pipe */
+pub const EDOM: usize = 33; /* Math argument out of domain of func */
+pub const ERANGE: usize = 34; /* Math result not representable */
+
+bitflags! {
+    pub struct OpenFlag: u32 {
+        // 以下是open/fcntl的一些常量,和C语言的值相同
+        const O_ACCMODE = 0o0003;
+        const O_RDONLY = 0o00;
+        const O_WRONLY = 0o01;
+        const O_RDWR = 0o02;
+        const O_CREAT = 0o0100;
+        const O_EXCL = 0o0200;
+        const O_NOCTTY = 0o0400;
+        const O_TRUNC = 0o01000;
+        const O_APPEND = 0o02000;
+        const O_NONBLOCK = 0o04000;
+        const O_NDELAY = Self::O_NONBLOCK.bits();
+        const O_SYNC = 0o4010000;
+        const O_FSYNC = Self::O_SYNC.bits();
+        const O_ASYNC = 0o020000;
+        const O_LARGEFILE = 0o0100000;
+        const O_DIRECTORY = 0o0200000;
+        const O_NOFOLLOW = 0o0400000;
+        const O_CLOEXEC = 0o2000000;
+        const O_DIRECT = 0o040000;
+        const O_NOATIME = 0o1000000;
+        const O_PATH = 0o10000000;
+        const O_DSYNC = 0o010000;
+        const O_TMPFILE = 0o20000000 | Self::O_DIRECTORY.bits();
+    }
+}
+
+bitflags! {
+    #[derive(Debug, PartialEq, Eq)]
+    pub struct InodeMode: u16 {
+        const S_IFSOCK = 0xC000;
+        const S_IFLNK = 0xA000;
+        const S_IFREG = 0x8000;
+        const S_IFBLK = 0x6000;
+        const S_IFDIR = 0x4000;
+        const S_IFCHR = 0x2000;
+        const S_IFIFO = 0x1000;
+    }
+}
+
+pub const BASE_OFFSET: usize = 1024;
+pub const BLOCK_SIZE: usize = 4096;

+ 1405 - 0
src/ext4.rs

@@ -0,0 +1,1405 @@
+extern crate alloc;
+extern crate log;
+
+use crate::ext4_defs::*;
+use crate::constants::*;
+use crate::prelude::*;
+use crate::return_errno_with_message;
+use crate::utils::*;
+
+#[derive(Debug)]
+pub struct Ext4 {
+    pub block_device: Arc<dyn BlockDevice>,
+    pub super_block: Ext4Superblock,
+    pub block_groups: Vec<Ext4BlockGroup>,
+    pub inodes_per_group: u32,
+    pub blocks_per_group: u32,
+    pub inode_size: usize,
+    pub last_inode_bg_id: u32,
+    pub self_ref: Weak<Self>,
+    pub mount_point: Ext4MountPoint,
+}
+
+impl Ext4 {
+    /// Opens and loads an Ext4 from the `block_device`.
+    pub fn open(block_device: Arc<dyn BlockDevice>) -> Arc<Self> {
+        // Load the superblock
+        // TODO: if the main superblock is corrupted, should we load the backup?
+        let raw_data = block_device.read_offset(BASE_OFFSET);
+        let super_block = Ext4Superblock::try_from(raw_data).unwrap();
+
+        // log::info!("super_block: {:x?}", super_block);
+        let inodes_per_group = super_block.inodes_per_group();
+        let blocks_per_group = super_block.blocks_per_group();
+        let inode_size = super_block.inode_size();
+
+        // Load the block groups information
+        let load_block_groups =
+            |_fs: Weak<Ext4>, block_device: Arc<dyn BlockDevice>| -> Result<Vec<Ext4BlockGroup>> {
+                let block_groups_count = super_block.block_groups_count() as usize;
+                let mut block_groups = Vec::with_capacity(block_groups_count);
+                for idx in 0..block_groups_count {
+                    let block_group =
+                        Ext4BlockGroup::load(block_device.clone(), &super_block, idx).unwrap();
+                    block_groups.push(block_group);
+                }
+                Ok(block_groups)
+            };
+
+        let mount_point = Ext4MountPoint::new("/");
+
+        let ext4: Arc<Ext4> = Arc::new_cyclic(|weak_ref| Self {
+            super_block,
+            inodes_per_group,
+            blocks_per_group,
+            inode_size: inode_size as usize,
+            block_groups: load_block_groups(weak_ref.clone(), block_device.clone()).unwrap(),
+            block_device,
+            self_ref: weak_ref.clone(),
+            mount_point: mount_point,
+            last_inode_bg_id: 0,
+        });
+
+        ext4
+    }
+
+    // 使用libc库定义的常量
+    fn ext4_parse_flags(&self, flags: &str) -> Result<u32> {
+        match flags {
+            "r" | "rb" => Ok(O_RDONLY),
+            "w" | "wb" => Ok(O_WRONLY | O_CREAT | O_TRUNC),
+            "a" | "ab" => Ok(O_WRONLY | O_CREAT | O_APPEND),
+            "r+" | "rb+" | "r+b" => Ok(O_RDWR),
+            "w+" | "wb+" | "w+b" => Ok(O_RDWR | O_CREAT | O_TRUNC),
+            "a+" | "ab+" | "a+b" => Ok(O_RDWR | O_CREAT | O_APPEND),
+            _ => Err(Ext4Error::new(Errnum::EINVAL)),
+        }
+    }
+
+    // start transaction
+    pub fn ext4_trans_start(&self) {}
+
+    // stop transaction
+    pub fn ext4_trans_abort(&self) {}
+
+    pub fn update_super_block(&mut self) {
+        let raw_data = self.block_device.read_offset(BASE_OFFSET);
+        let super_block = Ext4Superblock::try_from(raw_data).unwrap();
+        self.super_block = super_block;
+    }
+
+    pub fn ext4_open(
+        &self,
+        file: &mut Ext4File,
+        path: &str,
+        flags: &str,
+        file_expect: bool,
+    ) -> Result<usize> {
+        // get mount point
+        let mut ptr = Box::new(self.mount_point.clone());
+        file.mp = Box::as_mut(&mut ptr) as *mut Ext4MountPoint;
+
+        // get open flags
+        let iflags = self.ext4_parse_flags(flags).unwrap();
+
+        // file for dir
+        let filetype = if file_expect {
+            DirEntryType::EXT4_DE_REG_FILE
+        } else {
+            DirEntryType::EXT4_DE_DIR
+        };
+
+        if iflags & O_CREAT != 0 {
+            self.ext4_trans_start();
+        }
+
+        let mut root_inode_ref = Ext4InodeRef::get_inode_ref(self.self_ref.clone(), 2);
+
+        let r = self.ext4_generic_open(file, path, iflags, filetype.bits(), &mut root_inode_ref);
+
+        r
+    }
+
+    pub fn ext4_dir_mk(&self, path: &str) -> Result<usize> {
+        let mut file = Ext4File::new();
+        let flags = "w";
+
+        let filetype = DirEntryType::EXT4_DE_DIR;
+
+        // get mount point
+        let mut ptr = Box::new(self.mount_point.clone());
+        file.mp = Box::as_mut(&mut ptr) as *mut Ext4MountPoint;
+
+        // get open flags
+        let iflags = self.ext4_parse_flags(flags).unwrap();
+
+        if iflags & O_CREAT != 0 {
+            self.ext4_trans_start();
+        }
+
+        let mut root_inode_ref = Ext4InodeRef::get_inode_ref(self.self_ref.clone(), 2);
+
+        let r = self.ext4_generic_open(
+            &mut file,
+            path,
+            iflags,
+            filetype.bits(),
+            &mut root_inode_ref,
+        );
+        r
+    }
+
+    pub fn ext4_generic_open(
+        &self,
+        file: &mut Ext4File,
+        path: &str,
+        iflags: u32,
+        ftype: u8,
+        parent_inode: &mut Ext4InodeRef,
+    ) -> Result<usize> {
+        let mut is_goal = false;
+
+        let mut data: Vec<u8> = Vec::with_capacity(BLOCK_SIZE);
+        let ext4_blk = Ext4Block {
+            logical_block_id: 0,
+            disk_block_id: 0,
+            block_data: &mut data,
+            dirty: true,
+        };
+        let de = Ext4DirEntry::default();
+        let mut dir_search_result = Ext4DirSearchResult::new(ext4_blk, de);
+
+        file.flags = iflags;
+
+        // load root inode
+        let root_inode_ref = Ext4InodeRef::get_inode_ref(self.self_ref.clone(), 2);
+
+        // if !parent_inode.is_none() {
+        //     parent_inode.unwrap().inode_num = root_inode_ref.inode_num;
+        // }
+
+        // search dir
+        let mut search_parent = root_inode_ref;
+        let mut search_path = ext4_path_skip(&path, ".");
+        let mut len;
+        loop {
+            search_path = ext4_path_skip(search_path, "/");
+            len = ext4_path_check(search_path, &mut is_goal);
+
+            let r = ext4_dir_find_entry(
+                &mut search_parent,
+                &search_path[..len as usize],
+                len as u32,
+                &mut dir_search_result,
+            );
+
+            // log::info!("dir_search_result.dentry {:?} r {:?}", dir_search_result.dentry, r);
+            if r != EOK {
+                // ext4_dir_destroy_result(&mut root_inode_ref, &mut dir_search_result);
+
+                if r != ENOENT {
+                    // dir search failed with error other than ENOENT
+                    return_errno_with_message!(Errnum::ENOTSUP, "dir search failed");
+                }
+
+                if !((iflags & O_CREAT) != 0) {
+                    return_errno_with_message!(Errnum::ENOENT, "file not found");
+                }
+
+                let mut child_inode_ref = Ext4InodeRef::new(self.self_ref.clone());
+
+                let r = if is_goal {
+                    ext4_fs_alloc_inode(&mut child_inode_ref, ftype)
+                } else {
+                    ext4_fs_alloc_inode(&mut child_inode_ref, DirEntryType::EXT4_DE_DIR.bits())
+                };
+
+                if r != EOK {
+                    return_errno_with_message!(Errnum::EALLOCFIAL, "alloc inode fail");
+                    // break;
+                }
+
+                ext4_fs_inode_blocks_init(&mut child_inode_ref);
+
+                let r = ext4_link(
+                    &mut search_parent,
+                    &mut child_inode_ref,
+                    &search_path[..len as usize],
+                    len as u32,
+                );
+
+                if r != EOK {
+                    /*Fail. Free new inode.*/
+                    return_errno_with_message!(Errnum::ELINKFIAL, "link fail");
+                }
+
+                ext4_fs_put_inode_ref_csum(&mut search_parent);
+                ext4_fs_put_inode_ref_csum(&mut child_inode_ref);
+                ext4_fs_put_inode_ref_csum(parent_inode);
+
+                continue;
+            }
+
+            let _name = get_name(
+                dir_search_result.dentry.name,
+                dir_search_result.dentry.name_len as usize,
+            )
+            .unwrap();
+            // log::info!("find de name{:?} de inode {:x?}", name, dir_search_result.dentry.inode);
+
+            if is_goal {
+                file.inode = dir_search_result.dentry.inode;
+                return Ok(EOK);
+            } else {
+                search_parent = Ext4InodeRef::get_inode_ref(
+                    self.self_ref.clone(),
+                    dir_search_result.dentry.inode,
+                );
+                search_path = &search_path[len..];
+            }
+        }
+    }
+
+    pub fn ext4_file_read(&self, ext4_file: &mut Ext4File) -> Vec<u8> {
+        // 创建一个空的向量,用于存储文件的内容
+        let mut file_data: Vec<u8> = Vec::new();
+
+        // 创建一个空的向量,用于存储文件的所有extent信息
+        let mut extents: Vec<Ext4Extent> = Vec::new();
+
+        let inode_ref = Ext4InodeRef::get_inode_ref(self.self_ref.clone(), ext4_file.inode);
+
+        ext4_find_all_extent(&inode_ref, &mut extents);
+
+        // 遍历extents向量,对每个extent,计算它的物理块号,然后调用read_block函数来读取数据块,并将结果追加到file_data向量中
+        for extent in extents {
+            // 获取extent的起始块号、块数和逻辑块号
+            let start_block = extent.start_lo as u64 | ((extent.start_hi as u64) << 32);
+            let block_count = extent.block_count as u64;
+            let logical_block = extent.first_block as u64;
+            // 计算extent的物理块号
+            let physical_block = start_block + logical_block;
+            // 从file中读取extent的所有数据块,并将结果追加到file_data向量中
+            for i in 0..block_count {
+                let block_num = physical_block + i;
+                let block_data = inode_ref
+                    .fs()
+                    .block_device
+                    .read_offset(block_num as usize * BLOCK_SIZE);
+                file_data.extend(block_data);
+            }
+        }
+        file_data
+    }
+
+    pub fn ext4_file_write(&self, ext4_file: &mut Ext4File, data: &[u8], size: usize) {
+        let super_block_data = self.block_device.read_offset(BASE_OFFSET);
+        let super_block = Ext4Superblock::try_from(super_block_data).unwrap();
+        let mut inode_ref = Ext4InodeRef::get_inode_ref(self.self_ref.clone(), ext4_file.inode);
+        let block_size = super_block.block_size() as usize;
+        let iblock_last = ext4_file.fpos as usize + size / block_size;
+        let mut iblk_idx = ext4_file.fpos as usize / block_size;
+        let ifile_blocks = ext4_file.fsize as usize + block_size - 1 / block_size;
+
+        let mut fblk = 0;
+        let mut fblock_start = 0;
+        let mut fblock_count = 0;
+
+        let mut size = size;
+        while size >= block_size {
+            while iblk_idx < iblock_last {
+                if iblk_idx < ifile_blocks {
+                    ext4_fs_append_inode_dblk(&mut inode_ref, &mut (iblk_idx as u32), &mut fblk);
+                }
+
+                iblk_idx += 1;
+
+                if fblock_start == 0 {
+                    fblock_start = fblk;
+                }
+                fblock_count += 1;
+            }
+            size -= block_size;
+        }
+
+        for i in 0..fblock_count {
+            let idx = i * BLOCK_SIZE as usize;
+            let offset = (fblock_start as usize + i as usize) * BLOCK_SIZE;
+            self.block_device
+                .write_offset(offset, &data[idx..(idx + BLOCK_SIZE as usize)]);
+        }
+        // inode_ref.inner.inode.size = fblock_count as u32 * BLOCK_SIZE as u32;
+        inode_ref.write_back_inode();
+        // let mut inode_ref = Ext4InodeRef::get_inode_ref(self.self_ref.clone(), ext4_file.inode);
+        let mut root_inode_ref = Ext4InodeRef::get_inode_ref(self.self_ref.clone(), 2);
+        root_inode_ref.write_back_inode();
+    }
+
+    pub fn ext4_file_remove(&self, _path: &str) -> Result<usize> {
+        return_errno_with_message!(Errnum::ENOTSUP, "not support");
+    }
+
+    pub fn ext4_dir_remove(&self, _path: &str) -> Result<usize> {
+        return_errno_with_message!(Errnum::ENOTSUP, "not support");
+    }
+}
+
+pub fn ext4_fs_put_inode_ref_csum(inode_ref: &mut Ext4InodeRef) {
+    inode_ref.write_back_inode();
+}
+
+pub fn ext4_fs_put_inode_ref(inode_ref: &mut Ext4InodeRef) {
+    inode_ref.write_back_inode_without_csum();
+}
+
+pub fn ext4_link(
+    parent: &mut Ext4InodeRef,
+    child: &mut Ext4InodeRef,
+    name: &str,
+    name_len: u32,
+) -> usize {
+    // log::info!("link parent inode {:x?} child inode {:x?} name {:?}", parent.inode_num, child.inode_num, name);
+    /* Add entry to parent directory */
+    let _r = ext4_dir_add_entry(parent, child, name, name_len);
+
+    /* Fill new dir -> add '.' and '..' entries.
+     * Also newly allocated inode should have 0 link count.
+    	*/
+    let mut is_dir = false;
+    if child.inner.inode.mode & EXT4_INODE_MODE_TYPE_MASK as u16 == EXT4_INODE_MODE_DIRECTORY as u16
+    {
+        is_dir = true;
+    }
+
+    if is_dir {
+        // add '.' and '..' entries
+        let fs = child.fs().self_ref.clone();
+        let mut child_inode_ref = Ext4InodeRef::new(fs);
+        child_inode_ref.inode_num = child.inode_num;
+        child_inode_ref.inner.inode = child.inner.inode.clone();
+
+        let _r = ext4_dir_add_entry(&mut child_inode_ref, child, ".", 1);
+        child.inner.inode.size = child_inode_ref.inner.inode.size;
+        child.inner.inode.block = child_inode_ref.inner.inode.block;
+        let _r = ext4_dir_add_entry(&mut child_inode_ref, parent, "..", 2);
+
+        child.inner.inode.links_count = 2;
+        parent.inner.inode.links_count += 1;
+
+        return EOK;
+    }
+
+    child.inner.inode.links_count += 1;
+    EOK
+}
+
+pub fn ext4_dir_add_entry(
+    parent: &mut Ext4InodeRef,
+    child: &mut Ext4InodeRef,
+    path: &str,
+    len: u32,
+) -> usize {
+    let mut iblock = 0;
+    let block_size = parent.fs().super_block.block_size();
+    let inode_size = parent.inner.inode.ext4_inode_get_size();
+    // let inode_size = parent.fs().super_block.inode_size_file(&parent.inner.inode);
+    let total_blocks = inode_size as u32 / block_size;
+
+    let mut fblock: ext4_fsblk_t = 0;
+
+    // log::info!("ext4_dir_add_entry parent inode {:x?} inode_size {:x?}", parent.inode_num, inode_size);
+    while iblock < total_blocks {
+        ext4_fs_get_inode_dblk_idx(parent, &mut iblock, &mut fblock, false);
+
+        // load_block
+        let mut data = parent
+            .fs()
+            .block_device
+            .read_offset(fblock as usize * BLOCK_SIZE);
+        let mut ext4_block = Ext4Block {
+            logical_block_id: iblock,
+            disk_block_id: fblock,
+            block_data: &mut data,
+            dirty: false,
+        };
+
+        let r = ext4_dir_try_insert_entry(parent, &mut ext4_block, child, path, len);
+
+        if r == EOK {
+            return EOK;
+            // break;
+        }
+        let mut data: Vec<u8> = Vec::with_capacity(BLOCK_SIZE);
+        let ext4_blk = Ext4Block {
+            logical_block_id: 0,
+            disk_block_id: 0,
+            block_data: &mut data,
+            dirty: true,
+        };
+        let de = Ext4DirEntry::default();
+        let mut dir_search_result = Ext4DirSearchResult::new(ext4_blk, de);
+
+        let r = ext4_dir_find_in_block(&mut ext4_block, path, len, &mut dir_search_result);
+
+        if r {
+            return EOK;
+        }
+
+        iblock += 1;
+    }
+
+    /* No free block found - needed to allocate next data block */
+    iblock = 0;
+    fblock = 0;
+
+    ext4_fs_append_inode_dblk(parent, &mut (iblock as u32), &mut fblock);
+
+    /* Load new block */
+    let block_device = parent.fs().block_device.clone();
+    let mut data = block_device.read_offset(fblock as usize * BLOCK_SIZE);
+    let mut ext4_block = Ext4Block {
+        logical_block_id: iblock,
+        disk_block_id: fblock,
+        block_data: &mut data,
+        dirty: false,
+    };
+
+    let mut new_entry = Ext4DirEntry::default();
+    let el = BLOCK_SIZE - size_of::<Ext4DirEntryTail>();
+    ext4_dir_write_entry(&mut new_entry, el as u16, &child, path, len);
+
+    copy_dir_entry_to_array(&new_entry, &mut ext4_block.block_data, 0);
+
+    // init tail
+    let ptr = ext4_block.block_data.as_mut_ptr();
+    let mut tail = unsafe {
+        *(ptr.add(BLOCK_SIZE - core::mem::size_of::<Ext4DirEntryTail>()) as *mut Ext4DirEntryTail)
+    };
+    tail.rec_len = size_of::<Ext4DirEntryTail>() as u16;
+    tail.reserved_ft = 0xDE;
+    tail.reserved_zero1 = 0;
+    tail.reserved_zero2 = 0;
+
+    tail.ext4_dir_set_csum(
+        &parent.fs().super_block,
+        &new_entry,
+        &ext4_block.block_data[..],
+    );
+
+    let tail_offset = BLOCK_SIZE - size_of::<Ext4DirEntryTail>();
+    copy_diren_tail_to_array(&tail, &mut ext4_block.block_data, tail_offset);
+
+    tail.ext4_dir_set_csum(
+        &parent.fs().super_block,
+        &new_entry,
+        &ext4_block.block_data[..],
+    );
+
+    ext4_block.sync_blk_to_disk(block_device.clone());
+
+    // struct ext4_block b;
+
+    EOK
+}
+
+pub fn ext4_dir_try_insert_entry(
+    parent: &Ext4InodeRef,
+    dst_blk: &mut Ext4Block,
+    child: &mut Ext4InodeRef,
+    name: &str,
+    name_len: u32,
+) -> usize {
+    let mut required_len = core::mem::size_of::<Ext4DirEntry>() + name_len as usize;
+
+    if required_len % 4 != 0 {
+        required_len += 4 - required_len % 4;
+    }
+
+    let mut offset = 0;
+
+    while offset < dst_blk.block_data.len() {
+        let mut de = Ext4DirEntry::try_from(&dst_blk.block_data[offset..]).unwrap();
+        if de.inode == 0 {
+            continue;
+        }
+        let inode = de.inode;
+        let rec_len = de.entry_len;
+
+        // 如果是有效的目录项,尝试分割它
+        if inode != 0 {
+            let used_len = de.name_len as usize;
+            let mut sz = core::mem::size_of::<Ext4FakeDirEntry>() + used_len as usize;
+
+            if used_len % 4 != 0 {
+                sz += 4 - used_len % 4;
+            }
+
+            let free_space = rec_len as usize - sz;
+
+            // 如果有足够的空闲空间
+            if free_space >= required_len {
+                let mut new_entry = Ext4DirEntry::default();
+
+                de.entry_len = sz as u16;
+                ext4_dir_write_entry(&mut new_entry, free_space as u16, &child, name, name_len);
+
+                // update parent new_de to blk_data
+                copy_dir_entry_to_array(&de, &mut dst_blk.block_data, offset);
+                copy_dir_entry_to_array(&new_entry, &mut dst_blk.block_data, offset + sz);
+
+                // set tail csum
+                let mut tail = Ext4DirEntryTail::from(&mut dst_blk.block_data, BLOCK_SIZE).unwrap();
+                let block_device = parent.fs().block_device.clone();
+                tail.ext4_dir_set_csum(
+                    &parent.fs().super_block,
+                    &de,
+                    &dst_blk.block_data[offset..],
+                );
+
+                let parent_de = Ext4DirEntry::try_from(&dst_blk.block_data[..]).unwrap();
+                tail.ext4_dir_set_csum(
+                    &parent.fs().super_block,
+                    &parent_de,
+                    &dst_blk.block_data[..],
+                );
+
+                let tail_offset = BLOCK_SIZE - size_of::<Ext4DirEntryTail>();
+                copy_diren_tail_to_array(&tail, &mut dst_blk.block_data, tail_offset);
+
+                // sync to disk
+                dst_blk.sync_blk_to_disk(block_device.clone());
+
+                return EOK;
+            }
+        }
+        offset = offset + de.entry_len as usize;
+    }
+
+    ENOSPC
+}
+
+// 写入一个ext4目录项
+pub fn ext4_dir_write_entry(
+    en: &mut Ext4DirEntry,
+    entry_len: u16,
+    child: &Ext4InodeRef,
+    name: &str,
+    name_len: u32,
+) {
+    let file_type = (child.inner.inode.mode & EXT4_INODE_MODE_TYPE_MASK) as usize;
+
+    // 设置目录项的类型
+    match file_type {
+        EXT4_INODE_MODE_FILE => en.inner.inode_type = DirEntryType::EXT4_DE_REG_FILE.bits(),
+        EXT4_INODE_MODE_DIRECTORY => en.inner.inode_type = DirEntryType::EXT4_DE_DIR.bits(),
+        EXT4_INODE_MODE_CHARDEV => en.inner.inode_type = DirEntryType::EXT4_DE_CHRDEV.bits(),
+        EXT4_INODE_MODE_BLOCKDEV => en.inner.inode_type = DirEntryType::EXT4_DE_BLKDEV.bits(),
+        EXT4_INODE_MODE_FIFO => en.inner.inode_type = DirEntryType::EXT4_DE_FIFO.bits(),
+        EXT4_INODE_MODE_SOCKET => en.inner.inode_type = DirEntryType::EXT4_DE_SOCK.bits(),
+        EXT4_INODE_MODE_SOFTLINK => en.inner.inode_type = DirEntryType::EXT4_DE_SYMLINK.bits(),
+        _ => log::info!("{}: unknown type", file_type),
+    }
+
+    en.inode = child.inode_num;
+    en.entry_len = entry_len;
+    en.name_len = name_len as u8;
+
+    let en_name_ptr = en.name.as_mut_ptr();
+    unsafe {
+        en_name_ptr.copy_from_nonoverlapping(name.as_ptr(), name_len as usize);
+    }
+    let _name = get_name(en.name, en.name_len as usize).unwrap();
+    // log::info!("ext4_dir_write_entry name {:?}", name);
+}
+
+pub fn ext4_fs_append_inode_dblk(
+    inode_ref: &mut Ext4InodeRef,
+    iblock: &mut ext4_lblk_t,
+    fblock: &mut ext4_fsblk_t,
+) {
+    let inode_size = inode_ref.inner.inode.ext4_inode_get_size();
+    let block_size = BLOCK_SIZE as u64;
+
+    *iblock = ((inode_size + block_size - 1) / block_size) as u32;
+
+    let mut current_fsblk: ext4_fsblk_t = 0;
+    ext4_extent_get_blocks(inode_ref, *iblock, 1, &mut current_fsblk, true, &mut 0);
+
+    let current_block = current_fsblk;
+    *fblock = current_block;
+
+    inode_ref
+        .inner
+        .inode
+        .ext4_inode_set_size(inode_size + BLOCK_SIZE as u64);
+
+    inode_ref.write_back_inode();
+
+    // let mut inode_ref = Ext4InodeRef::get_inode_ref(inode_ref.fs().self_ref.clone(), inode_ref.inode_num);
+
+    // log::info!("ext4_fs_append_inode_dblk inode {:x?} inode_size {:x?}", inode_ref.inode_num, inode_ref.inner.inode.size);
+    // log::info!("fblock {:x?}", fblock);
+}
+
+pub fn ext4_fs_inode_blocks_init(inode_ref: &mut Ext4InodeRef) {
+    // log::info!(
+    //     "ext4_fs_inode_blocks_init mode {:x?}",
+    //     inode_ref.inner.inode.mode
+    // );
+
+    let inode = &mut inode_ref.inner.inode;
+
+    let mode = inode.mode;
+
+    let inode_type = InodeMode::from_bits(mode & EXT4_INODE_MODE_TYPE_MASK as u16).unwrap();
+
+    match inode_type {
+        InodeMode::S_IFDIR => {}
+        InodeMode::S_IFREG => {}
+        /* Reset blocks array. For inode which is not directory or file, just
+         * fill in blocks with 0 */
+        _ => {
+            log::info!("inode_type {:?}", inode_type);
+            return;
+        }
+    }
+
+    /* Initialize extents */
+    inode.ext4_inode_set_flags(EXT4_INODE_FLAG_EXTENTS as u32);
+
+    /* Initialize extent root header */
+    inode.ext4_extent_tree_init();
+    // log::info!("inode iblock {:x?}", inode.block);
+
+    // inode_ref.dirty = true;
+}
+
+pub fn ext4_fs_alloc_inode(child_inode_ref: &mut Ext4InodeRef, filetype: u8) -> usize {
+    let mut is_dir = false;
+
+    let inode_size = child_inode_ref.fs().super_block.inode_size();
+    let extra_size = child_inode_ref.fs().super_block.extra_size();
+
+    if filetype == DirEntryType::EXT4_DE_DIR.bits() {
+        is_dir = true;
+    }
+
+    let mut index = 0;
+    let _rc = ext4_ialloc_alloc_inode(child_inode_ref.fs(), &mut index, is_dir);
+
+    child_inode_ref.inode_num = index;
+
+    let inode = &mut child_inode_ref.inner.inode;
+
+    /* Initialize i-node */
+
+    let mode = if is_dir {
+        0o777 | EXT4_INODE_MODE_DIRECTORY as u16
+    } else if filetype == 0x7 {
+        0o777 | EXT4_INODE_MODE_SOFTLINK as u16
+    } else {
+        let t = ext4_fs_correspond_inode_mode(filetype);
+        // log::info!("ext4_fs_correspond_inode_mode {:x?}", ext4_fs_correspond_inode_mode(filetype));
+        0o666 | t as u16
+    };
+
+    inode.ext4_inode_set_mode(mode);
+    inode.ext4_inode_set_links_cnt(0);
+    inode.ext4_inode_set_uid(0);
+    inode.ext4_inode_set_gid(0);
+    inode.ext4_inode_set_size(0);
+    inode.ext4_inode_set_access_time(0);
+    inode.ext4_inode_set_change_inode_time(0);
+    inode.ext4_inode_set_modif_time(0);
+    inode.ext4_inode_set_del_time(0);
+    inode.ext4_inode_set_flags(0);
+    inode.ext4_inode_set_generation(0);
+
+    if inode_size > EXT4_GOOD_OLD_INODE_SIZE {
+        let extra_size = extra_size;
+        inode.ext4_inode_set_extra_isize(extra_size);
+    }
+
+    EOK
+}
+pub fn ext4_dir_destroy_result(_inode_ref: &mut Ext4InodeRef, result: &mut Ext4DirSearchResult) {
+    result.block.logical_block_id = 0;
+    result.block.disk_block_id = 0;
+    result.dentry = Ext4DirEntry::default();
+}
+
+pub fn ext4_dir_find_entry(
+    parent: &mut Ext4InodeRef,
+    name: &str,
+    name_len: u32,
+    result: &mut Ext4DirSearchResult,
+) -> usize {
+    // log::info!("ext4_dir_find_entry parent {:x?} {:?}",parent.inode_num,  name);
+    let mut iblock = 0;
+    let mut fblock: ext4_fsblk_t = 0;
+
+    let inode_size: u32 = parent.inner.inode.size;
+    let total_blocks: u32 = inode_size / BLOCK_SIZE as u32;
+
+    while iblock < total_blocks {
+        ext4_fs_get_inode_dblk_idx(parent, &mut iblock, &mut fblock, false);
+
+        // load_block
+        let mut data = parent
+            .fs()
+            .block_device
+            .read_offset(fblock as usize * BLOCK_SIZE);
+        let mut ext4_block = Ext4Block {
+            logical_block_id: iblock,
+            disk_block_id: fblock,
+            block_data: &mut data,
+            dirty: false,
+        };
+
+        let r = ext4_dir_find_in_block(&mut ext4_block, name, name_len, result);
+        if r {
+            return EOK;
+        }
+
+        iblock += 1
+    }
+
+    ENOENT
+}
+
+pub fn ext4_extent_get_blocks(
+    inode_ref: &mut Ext4InodeRef,
+    iblock: ext4_lblk_t,
+    max_blocks: u32,
+    result: &mut ext4_fsblk_t,
+    create: bool,
+    blocks_count: &mut u32,
+) {
+    *result = 0;
+    *blocks_count = 0;
+
+    let mut path: Option<Vec<Ext4ExtentPath>> = None;
+    let err = ext4_find_extent(inode_ref, iblock, &mut path, 0);
+
+    let inode = &inode_ref.inner.inode;
+    // 确认ext4_find_extent成功执行
+    if err != EOK {
+        return;
+    }
+
+    let depth = unsafe { *ext4_inode_hdr(inode) }.depth as usize;
+    let mut path = path.unwrap();
+
+    if !path[depth].extent.is_null() {
+        let ex = unsafe { *path[depth].extent };
+        let ee_block = ex.first_block;
+        let ee_start = ext4_ext_pblock(&ex);
+        let ee_len = ext4_ext_get_actual_len(&ex);
+
+        if iblock >= ee_block && iblock < ee_block + ee_len as u32 {
+            let allocated = ee_len - (iblock - ee_block) as u16;
+            *blocks_count = allocated as u32;
+
+            if !create || ext4_ext_is_unwritten(&ex) {
+                *result = (iblock - ee_block + ee_start) as u64;
+                return;
+            }
+        }
+    }
+
+    // 如果没有找到对应的extent,并且create为true,则需要分配和插入新的extent
+    if create {
+        let next = EXT_MAX_BLOCKS;
+
+        let mut allocated = next - iblock;
+        if allocated > max_blocks {
+            allocated = max_blocks;
+        }
+
+        let mut newex: Ext4Extent = Ext4Extent::default();
+
+        let goal = 0;
+
+        let mut alloc_block = 0;
+        ext4_balloc_alloc_block(inode_ref, goal as u64, &mut alloc_block);
+
+        *result = alloc_block;
+
+        // 创建并插入新的extent
+        newex.first_block = iblock;
+        newex.start_lo = alloc_block as u32 & 0xffffffff;
+        newex.start_hi = (((alloc_block as u32) << 31) << 1) as u16;
+        newex.block_count = allocated as u16;
+
+        ext4_ext_insert_extent(inode_ref, &mut path[0], &newex, 0);
+    }
+}
+
+pub fn ext_inode_hdr(inode: &Ext4Inode) -> *const Ext4ExtentHeader {
+    let eh = &inode.block as *const [u32; 15] as *const Ext4ExtentHeader;
+    eh
+}
+
+pub fn ext_depth(inode: &Ext4Inode) -> u16 {
+    let header = ext_inode_hdr(inode);
+    unsafe { (*header).depth }
+}
+
+pub fn ext_last_extent(eh: *const Ext4ExtentHeader) -> *mut Ext4Extent {
+    // 如果头部为空,返回空指针
+    if eh.is_null() {
+        return ptr::null_mut();
+    }
+
+    // 获取头部的extent数
+    let count = unsafe { (*eh).entries_count };
+
+    // 如果extent数为0,返回空指针
+    if count == 0 {
+        return ptr::null_mut();
+    }
+
+    // 获取头部中第一个extent的指针
+    let first = ext_first_extent(eh);
+
+    // 返回头部中最后一个extent的指针,即第一个extent的指针加上extent数减一
+    return unsafe { first.add((count - 1) as usize) };
+}
+
+// ext_first_extent函数
+pub fn ext_first_extent(eh: *const Ext4ExtentHeader) -> *mut Ext4Extent {
+    // 如果头部为空,返回空指针
+    if eh.is_null() {
+        return ptr::null_mut();
+    }
+
+    // // 获取头部的extent数
+    // let count = unsafe { (*eh).entries_count };
+
+    // // 如果extent数为0,返回空指针
+    // if count == 0 {
+    //     return ptr::null_mut();
+    // }
+
+    // 返回头部中第一个extent的指针,即头部的指针加上头部的大小
+    return unsafe { (eh as *mut u8).add(mem::size_of::<Ext4ExtentHeader>()) as *mut Ext4Extent };
+}
+
+pub fn ext4_ext_pblock(ex: &Ext4Extent) -> u32 {
+    let mut block = ex.start_lo;
+    block |= ((ex.start_hi as u32) << 31) << 1;
+    block
+}
+
+pub fn ext4_ext_is_unwritten(ext: &Ext4Extent) -> bool {
+    // 返回extent是否是未写入的
+    ext.block_count > EXT_INIT_MAX_LEN
+}
+
+pub fn ext4_ext_get_actual_len(ext: &Ext4Extent) -> u16 {
+    // 返回extent的实际长度
+    if ext.block_count <= EXT_INIT_MAX_LEN {
+        ext.block_count
+    } else {
+        ext.block_count - EXT_INIT_MAX_LEN
+    }
+}
+
+pub fn ext4_ext_mark_unwritten(ext: *mut Ext4Extent) {
+    unsafe {
+        (*ext).block_count |= EXT_INIT_MAX_LEN;
+    }
+}
+
+pub fn ext4_ext_can_append(ex1: &Ext4Extent, ex2: &Ext4Extent) -> bool {
+    // 检查是否可以将ex2合并到ex1的后面
+    // log::info!(
+    //     "\n\npblock1={:x?} pblock2={:x?}",
+    //     ext4_ext_pblock(ex1),
+    //     ext4_ext_pblock(ex2)
+    // );
+    // log::info!(
+    //     "len1={:x?} len2={:x?}",
+    //     ext4_ext_get_actual_len(ex1),
+    //     ext4_ext_get_actual_len(ex2)
+    // );
+    // log::info!(
+    //     "first_block1={:x?} first_block2={:x?}",
+    //     ex1.first_block, ex2.first_block
+    // );
+    if ext4_ext_pblock(ex1) + ext4_ext_get_actual_len(ex1) as u32 != ext4_ext_pblock(ex2) {
+        return false;
+    }
+
+    if ext4_ext_is_unwritten(ex1) {
+        if ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > EXT_UNWRITTEN_MAX_LEN {
+            return false;
+        }
+    } else if ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > EXT_INIT_MAX_LEN {
+        return false;
+    }
+
+    // 检查逻辑块号是否连续
+    if ex1.first_block + ext4_ext_get_actual_len(ex1) as u32 != ex2.first_block {
+        return false;
+    }
+    return true;
+}
+
+pub fn ext4_ext_can_prepend(ex1: &Ext4Extent, ex2: &Ext4Extent) -> bool {
+    // 检查是否可以将ex2合并到ex1的前面
+    if ext4_ext_pblock(ex2) + ext4_ext_get_actual_len(ex2) as u32 != ext4_ext_pblock(ex1) {
+        return false;
+    }
+    if ext4_ext_is_unwritten(ex1) {
+        if ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > EXT_UNWRITTEN_MAX_LEN {
+            return false;
+        }
+    } else if ext4_ext_get_actual_len(ex1) + ext4_ext_get_actual_len(ex2) > EXT_INIT_MAX_LEN {
+        return false;
+    }
+
+    // 检查逻辑块号是否连续
+    if ex2.first_block + ext4_ext_get_actual_len(ex2) as u32 != ex1.first_block {
+        return false;
+    }
+
+    // 如果以上条件都满足,返回true
+    true
+}
+
+pub fn ext4_ext_insert_extent(
+    inode_ref: &mut Ext4InodeRef,
+    path: &mut Ext4ExtentPath,
+    newext: &Ext4Extent,
+    flags: i32,
+) {
+    let depth = ext_depth(&inode_ref.inner.inode);
+    let mut need_split = false;
+
+    ext4_ext_insert_leaf(inode_ref, path, depth, newext, flags, &mut need_split);
+
+    inode_ref.write_back_inode_without_csum();
+}
+
+pub fn ext4_ext_insert_leaf(
+    _inode_ref: &mut Ext4InodeRef,
+    path: &mut Ext4ExtentPath,
+    _depth: u16,
+    newext: &Ext4Extent,
+    _flags: i32,
+    need_split: &mut bool,
+) -> usize {
+    let eh = path.header;
+    let ex = path.extent;
+    let _last_ex = ext_last_extent(eh);
+
+    let mut diskblock = newext.start_lo;
+    diskblock |= ((newext.start_hi as u32) << 31) << 1;
+
+    unsafe {
+        if !ex.is_null() && ext4_ext_can_append(&*(path.extent), newext) {
+            if ext4_ext_is_unwritten(&*(path.extent)) {
+                ext4_ext_mark_unwritten((*path).extent);
+            }
+            (*(path.extent)).block_count =
+                ext4_ext_get_actual_len(&*(path.extent)) + ext4_ext_get_actual_len(&newext);
+            (*path).p_block = diskblock as u64;
+            return EOK;
+        }
+
+        if !ex.is_null() && ext4_ext_can_prepend(&*(path.extent), newext) {
+            (*(path.extent)).block_count =
+                ext4_ext_get_actual_len(&*(path.extent)) + ext4_ext_get_actual_len(&newext);
+            (*path).p_block = diskblock as u64;
+
+            if ext4_ext_is_unwritten(&*(path.extent)) {
+                ext4_ext_mark_unwritten((*path).extent);
+            }
+            return EOK;
+        }
+    }
+
+    if ex.is_null() {
+        let first_extent = ext_first_extent(eh);
+        path.extent = first_extent;
+        // log::info!("first_extent {:x?}", unsafe{*first_extent});
+        unsafe {
+            if (*eh).entries_count == (*eh).max_entries_count {
+                *need_split = true;
+                return EIO;
+            } else {
+                *(path.extent) = *newext;
+            }
+        }
+    }
+
+    unsafe {
+        if (*eh).entries_count == (*eh).max_entries_count {
+            *need_split = true;
+            *(path.extent) = *newext;
+
+            (*path).p_block = diskblock as u64;
+            return EIO;
+        } else {
+            if ex.is_null() {
+                let first_extent = ext_first_extent(eh);
+                path.extent = first_extent;
+                *(path.extent) = *newext;
+            } else if newext.first_block > (*(path.extent)).first_block {
+                // insert after
+                let next_extent = ex.add(1);
+                path.extent = next_extent;
+            } else {
+            }
+        }
+
+        *(path.extent) = *newext;
+        (*eh).entries_count += 1;
+    }
+    unsafe {
+        *(path.extent) = *newext;
+    }
+
+    return EOK;
+}
+
+pub fn ext4_find_all_extent(inode_ref: &Ext4InodeRef, extents: &mut Vec<Ext4Extent>) {
+    let extent_header = Ext4ExtentHeader::try_from(&inode_ref.inner.inode.block[..2]).unwrap();
+    // log::info!("extent_header {:x?}", extent_header);
+    let data = &inode_ref.inner.inode.block;
+    let depth = extent_header.depth;
+
+    ext4_add_extent(inode_ref, depth, data, extents, true);
+}
+
+pub fn ext4_add_extent(
+    inode_ref: &Ext4InodeRef,
+    depth: u16,
+    data: &[u32],
+    extents: &mut Vec<Ext4Extent>,
+    _first_level: bool,
+) {
+    let extent_header = Ext4ExtentHeader::try_from(data).unwrap();
+    let extent_entries = extent_header.entries_count;
+    // log::info!("extent_entries {:x?}", extent_entries);
+    if depth == 0 {
+        for en in 0..extent_entries {
+            let idx = (3 + en * 3) as usize;
+            let extent = Ext4Extent::try_from(&data[idx..]).unwrap();
+
+            extents.push(extent)
+        }
+        return;
+    }
+
+    for en in 0..extent_entries {
+        let idx = (3 + en * 3) as usize;
+        if idx == 12 {
+            break;
+        }
+        let extent_index = Ext4ExtentIndex::try_from(&data[idx..]).unwrap();
+        let ei_leaf_lo = extent_index.leaf_lo;
+        let ei_leaf_hi = extent_index.leaf_hi;
+        let mut block = ei_leaf_lo;
+        block |= ((ei_leaf_hi as u32) << 31) << 1;
+        let data = inode_ref
+            .fs()
+            .block_device
+            .read_offset(block as usize * BLOCK_SIZE);
+        let data: Vec<u32> = unsafe { core::mem::transmute(data) };
+        ext4_add_extent(inode_ref, depth - 1, &data, extents, false);
+    }
+}
+
+pub fn ext4_idx_pblock(idx: *mut Ext4ExtentIndex) -> u64 {
+    // 如果索引为空,返回0
+    if idx.is_null() {
+        return 0;
+    }
+    // 获取索引的低32位物理块号
+    let mut pblock = unsafe { (*idx).leaf_lo } as u64;
+
+    // 如果支持64位物理块号,获取索引的高16位物理块号
+    let pblock_hi = unsafe { (*idx).leaf_hi };
+    pblock |= ((pblock_hi as ext4_fsblk_t) << 32) as u64;
+    // }
+
+    // 返回索引的物理块号
+    return pblock;
+}
+
+fn ext4_find_extent(
+    inode_ref: &mut Ext4InodeRef,
+    block: ext4_lblk_t,
+    orig_path: &mut Option<Vec<Ext4ExtentPath>>,
+    _flags: u32,
+) -> usize {
+    let inode = &inode_ref.inner.inode;
+    let mut _eh: &Ext4ExtentHeader;
+    let mut path = orig_path.take(); // Take the path out of the Option, which may replace it with None
+    let depth = unsafe { *ext4_inode_hdr(inode) }.depth;
+
+    let mut ppos = 0;
+    let mut i: u16;
+
+    let eh = &inode.block as *const [u32; 15] as *mut Ext4ExtentHeader;
+
+    if let Some(ref mut p) = path {
+        if depth > p[0].maxdepth {
+            p.clear();
+        }
+    }
+    if path.is_none() {
+        let path_depth = depth + 1;
+        path = Some(vec![Ext4ExtentPath::default(); path_depth as usize + 1]);
+        path.as_mut().unwrap()[0].maxdepth = path_depth;
+    }
+
+    let path = path.as_mut().unwrap();
+    path[0].header = eh;
+
+    i = depth;
+    while i > 0 {
+        ext4_ext_binsearch_idx(&mut path[ppos], block);
+        path[ppos].p_block = ext4_idx_pblock(path[ppos].index);
+        path[ppos].depth = i;
+        path[ppos].extent = core::ptr::null_mut();
+
+        i -= 1;
+        ppos += 1;
+    }
+
+    path[ppos].depth = i;
+    path[ppos].extent = core::ptr::null_mut();
+    path[ppos].index = core::ptr::null_mut();
+
+    ext4_ext_binsearch(&mut path[ppos], block);
+    if !path[ppos].extent.is_null() {
+        path[ppos].p_block = ext4_ext_pblock(&unsafe { *(path[ppos].extent) }) as u64;
+    }
+
+    *orig_path = Some(path.clone());
+
+    EOK
+}
+
+pub fn ext4_fs_get_inode_dblk_idx(
+    inode_ref: &mut Ext4InodeRef,
+    iblock: &mut ext4_lblk_t,
+    fblock: &mut ext4_fsblk_t,
+    _extent_create: bool,
+) -> usize {
+    let current_block: ext4_fsblk_t;
+    let mut current_fsblk: ext4_fsblk_t = 0;
+
+    let mut blocks_count = 0;
+    ext4_extent_get_blocks(
+        inode_ref,
+        *iblock,
+        1,
+        &mut current_fsblk,
+        false,
+        &mut blocks_count,
+    );
+
+    current_block = current_fsblk;
+    *fblock = current_block;
+
+    EOK
+}
+
+pub fn ext4_fs_get_inode_dblk_idx_internal(
+    inode_ref: &mut Ext4InodeRef,
+    iblock: &mut ext4_lblk_t,
+    _fblock: &mut ext4_fsblk_t,
+    extent_create: bool,
+    _support_unwritten: bool,
+) {
+    let mut current_fsblk: ext4_fsblk_t = 0;
+
+    let mut blocks_count = 0;
+    ext4_extent_get_blocks(
+        inode_ref,
+        *iblock,
+        1,
+        &mut current_fsblk,
+        extent_create,
+        &mut blocks_count,
+    );
+}
+
+pub fn ext4_dir_find_in_block(
+    block: &Ext4Block,
+    name: &str,
+    name_len: u32,
+    result: &mut Ext4DirSearchResult,
+) -> bool {
+    let mut offset = 0;
+
+    while offset < block.block_data.len() {
+        let de = Ext4DirEntry::try_from(&block.block_data[offset..]).unwrap();
+
+        offset = offset + de.entry_len as usize;
+        if de.inode == 0 {
+            continue;
+        }
+
+        let s = get_name(de.name, de.name_len as usize);
+
+        if let Ok(s) = s {
+            if name_len == de.name_len as u32 {
+                if name.to_string() == s {
+                    result.dentry = de;
+                    return true;
+                }
+            }
+        }
+    }
+
+    false
+}
+
+pub fn ext4_ialloc_alloc_inode(fs: Arc<Ext4>, index: &mut u32, is_dir: bool) {
+    let mut bgid = fs.last_inode_bg_id;
+    let bg_count = fs.super_block.block_groups_count();
+
+    while bgid <= bg_count {
+        if bgid == bg_count {
+            bgid = 0;
+            continue;
+        }
+
+        let block_device = fs.block_device.clone();
+
+        let raw_data = fs.block_device.read_offset(BASE_OFFSET);
+        let mut super_block = Ext4Superblock::try_from(raw_data).unwrap();
+
+        let mut bg =
+            Ext4BlockGroup::load(block_device.clone(), &super_block, bgid as usize).unwrap();
+
+        let mut free_inodes = bg.get_free_inodes_count();
+        let mut used_dirs = bg.get_used_dirs_count(&super_block);
+
+        if free_inodes > 0 {
+            let inode_bitmap_block = bg.get_inode_bitmap_block(&super_block);
+
+            let mut raw_data = fs
+                .block_device
+                .read_offset(inode_bitmap_block as usize * BLOCK_SIZE);
+
+            let inodes_in_bg = super_block.get_inodes_in_group_cnt(bgid);
+
+            let bitmap_size: u32 = inodes_in_bg / 0x8;
+
+            let mut bitmap_data = &mut raw_data[..bitmap_size as usize];
+
+            let mut idx_in_bg = 0 as u32;
+
+            ext4_bmap_bit_find_clr(bitmap_data, 0, inodes_in_bg, &mut idx_in_bg);
+            ext4_bmap_bit_set(&mut bitmap_data, idx_in_bg);
+
+            // update bitmap in disk
+            fs.block_device
+                .write_offset(inode_bitmap_block as usize * BLOCK_SIZE, &bitmap_data);
+
+            bg.set_block_group_ialloc_bitmap_csum(&super_block, &bitmap_data);
+
+            /* Modify filesystem counters */
+            free_inodes -= 1;
+            bg.set_free_inodes_count(&super_block, free_inodes);
+
+            /* Increment used directories counter */
+            if is_dir {
+                used_dirs += 1;
+                bg.set_used_dirs_count(&super_block, used_dirs);
+            }
+
+            /* Decrease unused inodes count */
+            let mut unused = bg.get_itable_unused(&super_block);
+            let free = inodes_in_bg - unused as u32;
+            if idx_in_bg >= free {
+                unused = inodes_in_bg - (idx_in_bg + 1);
+                bg.set_itable_unused(&super_block, unused);
+            }
+
+            bg.sync_to_disk_with_csum(block_device.clone(), bgid as usize, &super_block);
+            // bg.sync_block_group_to_disk(block_device.clone(), bgid as usize, &super_block);
+
+            /* Update superblock */
+            super_block.decrease_free_inodes_count();
+            // super_block.sync_super_block_to_disk(block_device.clone());
+
+            /* Compute the absolute i-nodex number */
+            let inodes_per_group = super_block.inodes_per_group();
+            let inode_num = bgid * inodes_per_group + (idx_in_bg + 1);
+            *index = inode_num;
+
+            // log::info!("alloc inode {:x?}", inode_num);
+            return;
+        }
+
+        bgid += 1;
+    }
+    log::info!("no free inode");
+}
+
+pub fn ext4_balloc_alloc_block(
+    inode_ref: &mut Ext4InodeRef,
+    goal: ext4_fsblk_t,
+    fblock: &mut ext4_fsblk_t,
+) {
+    // let mut alloc: ext4_fsblk_t = 0;
+    // let mut bmp_blk_adr: ext4_fsblk_t;
+    // let mut rel_blk_idx: u32 = 0;
+    // let mut free_blocks: u64;
+    // let mut r: i32;
+
+    let fs = inode_ref.fs();
+
+    let block_device = fs.block_device.clone();
+
+    let super_block_data = block_device.read_offset(BASE_OFFSET);
+    let mut super_block = Ext4Superblock::try_from(super_block_data).unwrap();
+
+    // let inodes_per_group = super_block.inodes_per_group();
+    let blocks_per_group = super_block.blocks_per_group();
+
+    let bgid = goal / blocks_per_group as u64;
+    let idx_in_bg = goal % blocks_per_group as u64;
+
+    let mut bg = Ext4BlockGroup::load(block_device.clone(), &super_block, bgid as usize).unwrap();
+
+    let block_bitmap_block = bg.get_block_bitmap_block(&super_block);
+    let mut raw_data = block_device.read_offset(block_bitmap_block as usize * BLOCK_SIZE);
+    let mut data: &mut Vec<u8> = &mut raw_data;
+    let mut rel_blk_idx = 0 as u32;
+
+    ext4_bmap_bit_find_clr(data, idx_in_bg as u32, 0x8000, &mut rel_blk_idx);
+    *fblock = rel_blk_idx as u64;
+    ext4_bmap_bit_set(&mut data, rel_blk_idx);
+
+    bg.set_block_group_balloc_bitmap_csum(&super_block, &data);
+    block_device.write_offset(block_bitmap_block as usize * BLOCK_SIZE, &data);
+
+    /* Update superblock free blocks count */
+    let super_blk_free_blocks = super_block.free_blocks_count();
+    // super_blk_free_blocks -= 1;
+    super_block.set_free_blocks_count(super_blk_free_blocks);
+    super_block.sync_to_disk(block_device.clone());
+
+    /* Update inode blocks (different block size!) count */
+    let mut inode_blocks = inode_ref.inner.inode.ext4_inode_get_blocks_count();
+    inode_blocks += 8;
+    inode_ref
+        .inner
+        .inode
+        .ext4_inode_set_blocks_count(inode_blocks as u32);
+    inode_ref.write_back_inode();
+
+    /* Update block group free blocks count */
+    let mut fb_cnt = bg.get_free_blocks_count();
+    fb_cnt -= 1;
+    bg.set_free_blocks_count(fb_cnt);
+    bg.sync_to_disk_with_csum(block_device, bgid as usize, &super_block);
+}

+ 24 - 0
src/ext4_defs/block.rs

@@ -0,0 +1,24 @@
+use crate::prelude::*;
+use crate::constants::*;
+use super::BlockDevice;
+
+#[derive(Debug)]
+// A single block descriptor
+pub struct Ext4Block<'a> {
+    pub logical_block_id: u32, // 逻辑块号
+
+    // disk block id
+    pub disk_block_id: u64,
+
+    // size BLOCK_SIZE
+    pub block_data: &'a mut Vec<u8>,
+
+    pub dirty: bool,
+}
+
+impl<'a> Ext4Block<'a> {
+    pub fn sync_blk_to_disk(&self, block_device: Arc<dyn BlockDevice>) {
+        let block_id = self.disk_block_id as usize;
+        block_device.write_offset(block_id * BLOCK_SIZE, &self.block_data);
+    }
+}

+ 8 - 0
src/ext4_defs/block_device.rs

@@ -0,0 +1,8 @@
+use alloc::vec::Vec;
+use core::any::Any;
+use core::fmt::Debug;
+
+pub trait BlockDevice: Send + Sync + Any + Debug {
+    fn read_offset(&self, offset: usize) -> Vec<u8>;
+    fn write_offset(&self, offset: usize, data: &[u8]);
+}

+ 266 - 0
src/ext4_defs/block_group.rs

@@ -0,0 +1,266 @@
+use crate::constants::*;
+use crate::prelude::*;
+use super::crc::*;
+use super::BlockDevice;
+use super::Ext4Superblock;
+
+#[derive(Debug, Default, Clone, Copy)]
+#[repr(C, packed)]
+pub struct Ext4BlockGroup {
+    block_bitmap_lo: u32,            // 块位图块
+    inode_bitmap_lo: u32,            // 节点位图块
+    inode_table_first_block_lo: u32, // 节点表块
+    free_blocks_count_lo: u16,       // 空闲块数
+    free_inodes_count_lo: u16,       // 空闲节点数
+    used_dirs_count_lo: u16,         // 目录数
+    flags: u16,                      // EXT4_BG_flags (INODE_UNINIT, etc)
+    exclude_bitmap_lo: u32,          // 快照排除位图
+    block_bitmap_csum_lo: u16,       // crc32c(s_uuid+grp_num+bbitmap) LE
+    inode_bitmap_csum_lo: u16,       // crc32c(s_uuid+grp_num+ibitmap) LE
+    itable_unused_lo: u16,           // 未使用的节点数
+    checksum: u16,                   // crc16(sb_uuid+group+desc)
+
+    block_bitmap_hi: u32,            // 块位图块 MSB
+    inode_bitmap_hi: u32,            // 节点位图块 MSB
+    inode_table_first_block_hi: u32, // 节点表块 MSB
+    free_blocks_count_hi: u16,       // 空闲块数 MSB
+    free_inodes_count_hi: u16,       // 空闲节点数 MSB
+    used_dirs_count_hi: u16,         // 目录数 MSB
+    itable_unused_hi: u16,           // 未使用的节点数 MSB
+    exclude_bitmap_hi: u32,          // 快照排除位图 MSB
+    block_bitmap_csum_hi: u16,       // crc32c(s_uuid+grp_num+bbitmap) BE
+    inode_bitmap_csum_hi: u16,       // crc32c(s_uuid+grp_num+ibitmap) BE
+    reserved: u32,                   // 填充
+}
+
+impl TryFrom<&[u8]> for Ext4BlockGroup {
+    type Error = u64;
+    fn try_from(data: &[u8]) -> core::result::Result<Self, u64> {
+        let data = &data[..size_of::<Ext4BlockGroup>()];
+        Ok(unsafe { core::ptr::read(data.as_ptr() as *const _) })
+    }
+}
+
+impl Ext4BlockGroup {
+    pub fn load(
+        block_device: Arc<dyn BlockDevice>,
+        super_block: &Ext4Superblock,
+        block_group_idx: usize,
+        // fs: Weak<Ext4>,
+    ) -> core::result::Result<Self, u64> {
+        let dsc_cnt = BLOCK_SIZE / super_block.desc_size() as usize;
+        let dsc_id = block_group_idx / dsc_cnt;
+        let first_data_block = super_block.first_data_block();
+
+        let block_id = first_data_block as usize + dsc_id + 1;
+        let offset = (block_group_idx % dsc_cnt) * super_block.desc_size() as usize;
+
+        let data = block_device.read_offset(block_id * BLOCK_SIZE);
+
+        let block_group_data =
+            &data[offset as usize..offset as usize + size_of::<Ext4BlockGroup>()];
+
+        let bg = Ext4BlockGroup::try_from(block_group_data);
+
+        bg
+    }
+
+    pub fn get_block_bitmap_block(&self, s: &Ext4Superblock) -> u64 {
+        let mut v = self.block_bitmap_lo as u64;
+        let desc_size = s.desc_size();
+        if desc_size > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE {
+            v |= (self.block_bitmap_hi as u64) << 32;
+        }
+        v
+    }
+
+    pub fn get_inode_bitmap_block(&self, s: &Ext4Superblock) -> u64 {
+        let mut v = self.inode_bitmap_lo as u64;
+        let desc_size = s.desc_size();
+        if desc_size > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE {
+            v |= (self.inode_bitmap_hi as u64) << 32;
+        }
+        v
+    }
+
+    pub fn get_itable_unused(&mut self, s: &Ext4Superblock) -> u32 {
+        let mut v = self.itable_unused_lo as u32;
+        if s.desc_size() > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE {
+            v |= ((self.itable_unused_hi as u64) << 32) as u32;
+        }
+        v
+    }
+
+    pub fn get_used_dirs_count(&self, s: &Ext4Superblock) -> u32 {
+        let mut v = self.used_dirs_count_lo as u32;
+        if s.desc_size() > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE {
+            v |= ((self.used_dirs_count_hi as u64) << 32) as u32;
+        }
+        v
+    }
+
+    pub fn set_used_dirs_count(&mut self, s: &Ext4Superblock, cnt: u32) {
+        self.itable_unused_lo = ((cnt << 16) >> 16) as u16;
+        if s.desc_size() > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE {
+            self.itable_unused_hi = (cnt >> 16) as u16;
+        }
+    }
+
+    pub fn set_itable_unused(&mut self, s: &Ext4Superblock, cnt: u32) {
+        self.itable_unused_lo = ((cnt << 16) >> 16) as u16;
+        if s.desc_size() > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE {
+            self.itable_unused_hi = (cnt >> 16) as u16;
+        }
+    }
+
+    pub fn set_free_inodes_count(&mut self, s: &Ext4Superblock, cnt: u32) {
+        self.free_inodes_count_lo = ((cnt << 16) >> 16) as u16;
+        if s.desc_size() > EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE {
+            self.free_inodes_count_hi = (cnt >> 16) as u16;
+        }
+    }
+
+    pub fn inode_table_first_block_hi(&self) -> u32 {
+        self.inode_table_first_block_hi
+    }
+
+    pub fn inode_table_first_block_lo(&self) -> u32 {
+        self.inode_table_first_block_lo
+    }
+
+    pub fn get_free_inodes_count(&self) -> u32 {
+        ((self.free_inodes_count_hi as u64) << 32) as u32 | self.free_inodes_count_lo as u32
+    }
+
+    pub fn get_inode_table_blk_num(&self) -> u32 {
+        ((self.inode_table_first_block_hi as u64) << 32) as u32 | self.inode_table_first_block_lo
+    }
+
+    pub fn sync_block_group_to_disk(
+        &self,
+        block_device: Arc<dyn BlockDevice>,
+        bgid: usize,
+        super_block: &Ext4Superblock,
+    ) {
+        let dsc_cnt = BLOCK_SIZE / super_block.desc_size() as usize;
+        // let dsc_per_block = dsc_cnt;
+        let dsc_id = bgid / dsc_cnt;
+        // let first_meta_bg = super_block.first_meta_bg;
+        let first_data_block = super_block.first_data_block();
+        let block_id = first_data_block as usize + dsc_id + 1;
+        let offset = (bgid % dsc_cnt) * super_block.desc_size() as usize;
+
+        let data = unsafe {
+            core::slice::from_raw_parts(self as *const _ as *const u8, size_of::<Ext4BlockGroup>())
+        };
+        block_device.write_offset(block_id * BLOCK_SIZE + offset, data);
+    }
+
+    pub fn get_block_group_checksum(&mut self, bgid: u32, super_block: &Ext4Superblock) -> u16 {
+        let desc_size = super_block.desc_size();
+
+        let orig_checksum = self.checksum;
+
+        // 准备:暂时将bg校验和设为0
+        self.checksum = 0;
+
+        // uuid checksum
+        let mut checksum = ext4_crc32c(
+            EXT4_CRC32_INIT,
+            &super_block.uuid(),
+            super_block.uuid().len() as u32,
+        );
+
+        // bgid checksum
+        checksum = ext4_crc32c(checksum, &bgid.to_le_bytes(), 4);
+
+        // cast self to &[u8]
+        let self_bytes =
+            unsafe { core::slice::from_raw_parts(self as *const _ as *const u8, 0x40 as usize) };
+
+        // bg checksum
+        checksum = ext4_crc32c(checksum, self_bytes, desc_size as u32);
+
+        self.checksum = orig_checksum;
+
+        let crc = (checksum & 0xFFFF) as u16;
+
+        crc
+    }
+
+    pub fn set_block_group_checksum(&mut self, bgid: u32, super_block: &Ext4Superblock) {
+        let csum = self.get_block_group_checksum(bgid, super_block);
+        self.checksum = csum;
+    }
+
+    pub fn sync_to_disk_with_csum(
+        &mut self,
+        block_device: Arc<dyn BlockDevice>,
+        bgid: usize,
+        super_block: &Ext4Superblock,
+    ) {
+        self.set_block_group_checksum(bgid as u32, super_block);
+        self.sync_block_group_to_disk(block_device, bgid, super_block)
+    }
+
+    pub fn set_block_group_ialloc_bitmap_csum(&mut self, s: &Ext4Superblock, bitmap: &[u8]) {
+        let desc_size = s.desc_size();
+
+        let csum = ext4_ialloc_bitmap_csum(bitmap, s);
+        let lo_csum = (csum & 0xFFFF).to_le();
+        let hi_csum = (csum >> 16).to_le();
+
+        if (s.features_read_only() & 0x400) >> 10 == 0 {
+            return;
+        }
+        self.inode_bitmap_csum_lo = lo_csum as u16;
+        if desc_size == EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE {
+            self.inode_bitmap_csum_hi = hi_csum as u16;
+        }
+    }
+
+    pub fn set_block_group_balloc_bitmap_csum(&mut self, s: &Ext4Superblock, bitmap: &[u8]) {
+        let desc_size = s.desc_size();
+
+        let csum = ext4_balloc_bitmap_csum(bitmap, s);
+        let lo_csum = (csum & 0xFFFF).to_le();
+        let hi_csum = (csum >> 16).to_le();
+
+        if (s.features_read_only() & 0x400) >> 10 == 0 {
+            return;
+        }
+        self.block_bitmap_csum_lo = lo_csum as u16;
+        if desc_size == EXT4_MAX_BLOCK_GROUP_DESCRIPTOR_SIZE {
+            self.block_bitmap_csum_hi = hi_csum as u16;
+        }
+    }
+
+    pub fn get_free_blocks_count(&self) -> u64 {
+        let mut v = self.free_blocks_count_lo as u64;
+        if self.free_blocks_count_hi != 0 {
+            v |= (self.free_blocks_count_hi as u64) << 32;
+        }
+        v
+    }
+
+    pub fn set_free_blocks_count(&mut self, cnt: u64) {
+        self.free_blocks_count_lo = ((cnt << 32) >> 32) as u16;
+        self.free_blocks_count_hi = (cnt >> 32) as u16;
+    }
+}
+
+pub fn ext4_ialloc_bitmap_csum(bitmap: &[u8], s: &Ext4Superblock) -> u32 {
+    let inodes_per_group = s.inodes_per_group();
+    let uuid = s.uuid();
+    let mut csum = ext4_crc32c(EXT4_CRC32_INIT, &uuid, uuid.len() as u32);
+    csum = ext4_crc32c(csum, bitmap, (inodes_per_group + 7) / 8);
+    csum
+}
+
+pub fn ext4_balloc_bitmap_csum(bitmap: &[u8], s: &Ext4Superblock) -> u32 {
+    let blocks_per_group = s.blocks_per_group();
+    let uuid = s.uuid();
+    let mut csum = ext4_crc32c(EXT4_CRC32_INIT, &uuid, uuid.len() as u32);
+    csum = ext4_crc32c(csum, bitmap, (blocks_per_group / 8) as u32);
+    csum
+}

+ 75 - 0
src/ext4_defs/crc.rs

@@ -0,0 +1,75 @@
+/* */
+/* CRC LOOKUP TABLE */
+/* ================ */
+/* The following CRC lookup table was generated automagically */
+/* by the Rocksoft^tm Model CRC Algorithm Table Generation */
+/* Program V1.0 using the following model parameters: */
+/* */
+/* Width : 4 bytes. */
+/* Poly : 0x1EDC6F41L */
+/* Reverse : TRUE. */
+/* */
+/* For more information on the Rocksoft^tm Model CRC Algorithm, */
+/* see the document titled "A Painless Guide to CRC Error */
+/* Detection Algorithms" by Ross Williams */
+/* (ross@guest.adelaide.edu.au.). This document is likely to be */
+/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */
+/* */
+const CRC32C_TAB: [u32; 256] = [
+    0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
+    0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
+    0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
+    0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
+    0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
+    0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
+    0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
+    0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
+    0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
+    0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
+    0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
+    0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
+    0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
+    0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
+    0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
+    0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
+    0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
+    0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
+    0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
+    0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
+    0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
+    0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
+    0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
+    0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
+    0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
+    0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
+    0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
+    0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
+    0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
+    0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
+    0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
+    0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351,
+];
+
+/// 计算CRC32校验和
+/// 参数 crc 初始值
+/// 参数 buf 缓冲区
+/// 参数 size 缓冲区大小
+/// 参数 tab 查找表
+fn crc32(crc: u32, buf: &[u8], size: u32, tab: &[u32]) -> u32 {
+    let mut crc = crc;
+    let mut p = buf;
+    let mut size = size as usize;
+
+    // 循环更新crc值
+    while size > 0 {
+        crc = tab[(crc as u8 ^ p[0]) as usize] ^ (crc >> 8);
+        p = &p[1..];
+        size -= 1;
+    }
+
+    crc
+}
+
+pub fn ext4_crc32c(crc: u32, buf: &[u8], size: u32) -> u32 {
+    crc32(crc, buf, size, &CRC32C_TAB)
+}

+ 260 - 0
src/ext4_defs/dir_entry.rs

@@ -0,0 +1,260 @@
+use crate::constants::*;
+use crate::prelude::*;
+use super::crc::*;
+use super::BlockDevice;
+use super::Ext4Block;
+use super::Ext4Superblock;
+
+#[repr(C)]
+pub union Ext4DirEnInternal {
+    pub name_length_high: u8, // 高8位的文件名长度
+    pub inode_type: u8,       // 引用的inode的类型(在rev >= 0.5中)
+}
+
+impl Debug for Ext4DirEnInternal {
+    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+        unsafe {
+            write!(
+                f,
+                "Ext4DirEnInternal {{ name_length_high: {:?} }}",
+                self.name_length_high
+            )
+        }
+    }
+}
+
+impl Default for Ext4DirEnInternal {
+    fn default() -> Self {
+        Self {
+            name_length_high: 0,
+        }
+    }
+}
+
+#[repr(C)]
+#[derive(Debug)]
+pub struct Ext4DirEntry {
+    pub inode: u32,               // 该目录项指向的inode的编号
+    pub entry_len: u16,           // 到下一个目录项的距离
+    pub name_len: u8,             // 低8位的文件名长度
+    pub inner: Ext4DirEnInternal, // 联合体成员
+    pub name: [u8; 255],          // 文件名
+}
+
+impl Default for Ext4DirEntry {
+    fn default() -> Self {
+        Self {
+            inode: 0,
+            entry_len: 0,
+            name_len: 0,
+            inner: Ext4DirEnInternal::default(),
+            name: [0; 255],
+        }
+    }
+}
+
+impl<T> TryFrom<&[T]> for Ext4DirEntry {
+    type Error = u64;
+    fn try_from(data: &[T]) -> core::result::Result<Self, u64> {
+        let data = data;
+        Ok(unsafe { core::ptr::read(data.as_ptr() as *const _) })
+    }
+}
+
+impl Ext4DirEntry {
+    pub fn get_name(&self) -> String {
+        let name_len = self.name_len as usize;
+        let name = &self.name[..name_len];
+        let name = core::str::from_utf8(name).unwrap();
+        name.to_string()
+    }
+
+    pub fn get_name_len(&self) -> usize {
+        let name_len = self.name_len as usize;
+        name_len
+    }
+
+    pub fn ext4_dir_get_csum(&self, s: &Ext4Superblock, blk_data: &[u8]) -> u32 {
+        let ino_index = self.inode;
+        let ino_gen = 0 as u32;
+
+        let uuid = s.uuid();
+
+        let mut csum = ext4_crc32c(EXT4_CRC32_INIT, &uuid, uuid.len() as u32);
+        csum = ext4_crc32c(csum, &ino_index.to_le_bytes(), 4);
+        csum = ext4_crc32c(csum, &ino_gen.to_le_bytes(), 4);
+        let mut data = [0u8; 0xff4];
+        unsafe {
+            core::ptr::copy_nonoverlapping(blk_data.as_ptr(), data.as_mut_ptr(), blk_data.len());
+        }
+        csum = ext4_crc32c(csum, &data[..], 0xff4);
+        csum
+    }
+
+    pub fn write_de_to_blk(&self, dst_blk: &mut Ext4Block, offset: usize) {
+        let count = core::mem::size_of::<Ext4DirEntry>() / core::mem::size_of::<u8>();
+        let data = unsafe { core::slice::from_raw_parts(self as *const _ as *const u8, count) };
+        dst_blk.block_data.splice(
+            offset..offset + core::mem::size_of::<Ext4DirEntry>(),
+            data.iter().cloned(),
+        );
+        // assert_eq!(dst_blk.block_data[offset..offset + core::mem::size_of::<Ext4DirEntry>()], data[..]);
+    }
+}
+
+pub fn copy_dir_entry_to_array(header: &Ext4DirEntry, array: &mut [u8], offset: usize) {
+    unsafe {
+        let de_ptr = header as *const Ext4DirEntry as *const u8;
+        let array_ptr = array as *mut [u8] as *mut u8;
+        let count = core::mem::size_of::<Ext4DirEntry>() / core::mem::size_of::<u8>();
+        core::ptr::copy_nonoverlapping(de_ptr, array_ptr.add(offset), count);
+    }
+}
+
+pub fn copy_diren_tail_to_array(dir_en: &Ext4DirEntryTail, array: &mut [u8], offset: usize) {
+    unsafe {
+        let de_ptr = dir_en as *const Ext4DirEntryTail as *const u8;
+        let array_ptr = array as *mut [u8] as *mut u8;
+        let count = core::mem::size_of::<Ext4DirEntryTail>();
+        core::ptr::copy_nonoverlapping(de_ptr, array_ptr.add(offset), count);
+    }
+}
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+pub struct Ext4DirEntryTail {
+    pub reserved_zero1: u32,
+    pub rec_len: u16,
+    pub reserved_zero2: u8,
+    pub reserved_ft: u8,
+    pub checksum: u32, // crc32c(uuid+inum+dirblock)
+}
+
+impl Ext4DirEntryTail {
+    pub fn from(data: &mut [u8], blocksize: usize) -> Option<Self> {
+        unsafe {
+            let ptr = data as *mut [u8] as *mut u8;
+            let t = *(ptr.add(blocksize - core::mem::size_of::<Ext4DirEntryTail>())
+                as *mut Ext4DirEntryTail);
+            if t.reserved_zero1 != 0 || t.reserved_zero2 != 0 {
+                log::info!("t.reserved_zero1");
+                return None;
+            }
+            if t.rec_len.to_le() != core::mem::size_of::<Ext4DirEntryTail>() as u16 {
+                log::info!("t.rec_len");
+                return None;
+            }
+            if t.reserved_ft != 0xDE {
+                log::info!("t.reserved_ft");
+                return None;
+            }
+            Some(t)
+        }
+    }
+
+    pub fn ext4_dir_set_csum(&mut self, s: &Ext4Superblock, diren: &Ext4DirEntry, blk_data: &[u8]) {
+        let csum = diren.ext4_dir_get_csum(s, blk_data);
+        self.checksum = csum;
+    }
+
+    #[allow(unused)]
+    pub fn write_de_tail_to_blk(&self, dst_blk: &mut Ext4Block, offset: usize) {
+        let data = unsafe { core::slice::from_raw_parts(self as *const _ as *const u8, 0x20) };
+        dst_blk.block_data.splice(
+            offset..offset + core::mem::size_of::<Ext4DirEntryTail>(),
+            data.iter().cloned(),
+        );
+        assert_eq!(
+            dst_blk.block_data[offset..offset + core::mem::size_of::<Ext4DirEntryTail>()],
+            data[..]
+        );
+    }
+
+    #[allow(unused)]
+    pub fn sync_de_tail_to_disk(
+        &self,
+        block_device: Arc<dyn BlockDevice>,
+        dst_blk: &mut Ext4Block,
+    ) {
+        let offset = BASE_OFFSET as usize - core::mem::size_of::<Ext4DirEntryTail>();
+
+        let data = unsafe {
+            core::slice::from_raw_parts(
+                self as *const _ as *const u8,
+                core::mem::size_of::<Ext4DirEntryTail>(),
+            )
+        };
+        dst_blk.block_data.splice(
+            offset..offset + core::mem::size_of::<Ext4DirEntryTail>(),
+            data.iter().cloned(),
+        );
+        assert_eq!(
+            dst_blk.block_data[offset..offset + core::mem::size_of::<Ext4DirEntryTail>()],
+            data[..]
+        );
+        block_device.write_offset(
+            dst_blk.disk_block_id as usize * BLOCK_SIZE,
+            &dst_blk.block_data,
+        );
+    }
+}
+
+#[allow(unused)]
+pub fn copy_diren_to_array(diren: &Ext4DirEntry, array: &mut [u8]) {
+    unsafe {
+        let diren_ptr = diren as *const Ext4DirEntry as *const u8;
+        let array_ptr = array as *mut [u8] as *mut u8;
+        core::ptr::copy_nonoverlapping(diren_ptr, array_ptr, core::mem::size_of::<Ext4DirEntry>());
+    }
+}
+
+pub struct Ext4DirSearchResult<'a> {
+    pub block: Ext4Block<'a>,
+    pub dentry: Ext4DirEntry,
+}
+
+impl<'a> Ext4DirSearchResult<'a> {
+    pub fn new(block: Ext4Block<'a>, dentry: Ext4DirEntry) -> Self {
+        Self { block, dentry }
+    }
+}
+
+/// fake dir entry
+#[repr(C)]
+pub struct Ext4FakeDirEntry {
+    inode: u32,
+    entry_length: u16,
+    name_length: u8,
+    inode_type: u8,
+}
+
+bitflags! {
+    #[derive(PartialEq, Eq)]
+    pub struct DirEntryType: u8 {
+        const EXT4_DE_UNKNOWN = 0;
+        const EXT4_DE_REG_FILE = 1;
+        const EXT4_DE_DIR = 2;
+        const EXT4_DE_CHRDEV = 3;
+        const EXT4_DE_BLKDEV = 4;
+        const EXT4_DE_FIFO = 5;
+        const EXT4_DE_SOCK = 6;
+        const EXT4_DE_SYMLINK = 7;
+    }
+}
+
+pub fn ext4_fs_correspond_inode_mode(filetype: u8) -> u32 {
+    let file_type = DirEntryType::from_bits(filetype).unwrap();
+    match file_type {
+        DirEntryType::EXT4_DE_DIR => EXT4_INODE_MODE_DIRECTORY as u32,
+        DirEntryType::EXT4_DE_REG_FILE => EXT4_INODE_MODE_FILE as u32,
+        DirEntryType::EXT4_DE_SYMLINK => EXT4_INODE_MODE_SOFTLINK as u32,
+        DirEntryType::EXT4_DE_CHRDEV => EXT4_INODE_MODE_CHARDEV as u32,
+        DirEntryType::EXT4_DE_BLKDEV => EXT4_INODE_MODE_BLOCKDEV as u32,
+        DirEntryType::EXT4_DE_FIFO => EXT4_INODE_MODE_FIFO as u32,
+        DirEntryType::EXT4_DE_SOCK => EXT4_INODE_MODE_SOCKET as u32,
+        _ => {
+            // FIXME: unsupported filetype
+            EXT4_INODE_MODE_FILE as u32
+        }
+    }
+}

+ 443 - 0
src/ext4_defs/extent.rs

@@ -0,0 +1,443 @@
+use super::Ext4Inode;
+use crate::constants::*;
+use core::mem::size_of;
+
+#[derive(Debug, Default, Clone, Copy)]
+#[repr(C)]
+pub struct Ext4ExtentHeader {
+    /// Magic number, 0xF30A.
+    pub magic: u16,
+
+    /// Number of valid entries following the header.
+    pub entries_count: u16,
+
+    /// Maximum number of entries that could follow the header.
+    pub max_entries_count: u16,
+
+    /// Depth of this extent node in the extent tree.
+    /// 0 = this extent node points to data blocks;
+    /// otherwise, this extent node points to other extent nodes.
+    /// The extent tree can be at most 5 levels deep:
+    /// a logical block number can be at most 2^32,
+    /// and the smallest n that satisfies 4*(((blocksize - 12)/12)^n) >= 2^32 is 5.
+    pub depth: u16,
+
+    /// Generation of the tree. (Used by Lustre, but not standard ext4).
+    pub generation: u32,
+}
+
+impl<T> TryFrom<&[T]> for Ext4ExtentHeader {
+    type Error = u64;
+    fn try_from(data: &[T]) -> core::result::Result<Self, u64> {
+        let data = data;
+        Ok(unsafe { core::ptr::read(data.as_ptr() as *const _) })
+    }
+}
+
+impl Ext4ExtentHeader {
+    // 获取extent header的魔数
+    pub fn get_magic(&self) -> u16 {
+        self.magic
+    }
+
+    // 设置extent header的魔数
+    pub fn set_magic(&mut self, magic: u16) {
+        self.magic = magic;
+    }
+
+    // 获取extent header的条目数
+    pub fn get_entries_count(&self) -> u16 {
+        self.entries_count
+    }
+
+    // 设置extent header的条目数
+    pub fn set_entries_count(&mut self, count: u16) {
+        self.entries_count = count;
+    }
+
+    // 获取extent header的最大条目数
+    pub fn get_max_entries_count(&self) -> u16 {
+        self.max_entries_count
+    }
+
+    // 设置extent header的最大条目数
+    pub fn set_max_entries_count(&mut self, max_count: u16) {
+        self.max_entries_count = max_count;
+    }
+
+    // 获取extent header的深度
+    pub fn get_depth(&self) -> u16 {
+        self.depth
+    }
+
+    // 设置extent header的深度
+    pub fn set_depth(&mut self, depth: u16) {
+        self.depth = depth;
+    }
+
+    // 获取extent header的生成号
+    pub fn get_generation(&self) -> u32 {
+        self.generation
+    }
+
+    // 设置extent header的生成号
+    pub fn set_generation(&mut self, generation: u32) {
+        self.generation = generation;
+    }
+
+    pub fn ext4_extent_header_depth(&self) -> u16 {
+        self.depth
+    }
+
+    pub fn ext4_extent_header_set_depth(&mut self, depth: u16) {
+        self.depth = depth;
+    }
+    pub fn ext4_extent_header_set_entries_count(&mut self, entries_count: u16) {
+        self.entries_count = entries_count;
+    }
+    pub fn ext4_extent_header_set_generation(&mut self, generation: u32) {
+        self.generation = generation;
+    }
+    pub fn ext4_extent_header_set_magic(&mut self) {
+        self.magic = EXT4_EXTENT_MAGIC;
+    }
+
+    pub fn ext4_extent_header_set_max_entries_count(&mut self, max_entries_count: u16) {
+        self.max_entries_count = max_entries_count;
+    }
+}
+
+#[derive(Debug, Default, Clone, Copy)]
+#[repr(C)]
+pub struct Ext4ExtentIndex {
+    /// This index node covers file blocks from ‘block’ onward.
+    pub first_block: u32,
+
+    /// Lower 32-bits of the block number of the extent node that is
+    /// the next level lower in the tree. The tree node pointed to
+    /// can be either another internal node or a leaf node, described below.
+    pub leaf_lo: u32,
+
+    /// Upper 16-bits of the previous field.
+    pub leaf_hi: u16,
+
+    pub padding: u16,
+}
+
+impl<T> TryFrom<&[T]> for Ext4ExtentIndex {
+    type Error = u64;
+    fn try_from(data: &[T]) -> core::result::Result<Self, u64> {
+        let data = &data[..size_of::<Ext4ExtentIndex>()];
+        Ok(unsafe { core::ptr::read(data.as_ptr() as *const _) })
+    }
+}
+
+#[derive(Debug, Default, Clone, Copy)]
+#[repr(C)]
+pub struct Ext4Extent {
+    /// First file block number that this extent covers.
+    pub first_block: u32,
+
+    /// Number of blocks covered by extent.
+    /// If the value of this field is <= 32768, the extent is initialized.
+    /// If the value of the field is > 32768, the extent is uninitialized
+    /// and the actual extent length is ee_len - 32768.
+    /// Therefore, the maximum length of a initialized extent is 32768 blocks,
+    /// and the maximum length of an uninitialized extent is 32767.
+    pub block_count: u16,
+
+    /// Upper 16-bits of the block number to which this extent points.
+    pub start_hi: u16,
+
+    /// Lower 32-bits of the block number to which this extent points.
+    pub start_lo: u32,
+}
+
+impl<T> TryFrom<&[T]> for Ext4Extent {
+    type Error = u64;
+    fn try_from(data: &[T]) -> core::result::Result<Self, u64> {
+        let data = &data[..size_of::<Ext4Extent>()];
+        Ok(unsafe { core::ptr::read(data.as_ptr() as *const _) })
+    }
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct Ext4ExtentPath {
+    // Physical block number
+    pub p_block: u64,
+    // Single block descriptor
+    // pub block: Ext4Block,
+    // Depth of this extent node
+    pub depth: u16,
+    // Max depth of the extent tree
+    pub maxdepth: u16,
+    // Pointer to the extent header
+    pub header: *mut Ext4ExtentHeader,
+    // Pointer to the index in the current node
+    pub index: *mut Ext4ExtentIndex,
+    // Pointer to the extent in the current node
+    pub extent: *mut Ext4Extent,
+}
+
+impl Default for Ext4ExtentPath {
+    fn default() -> Self {
+        Self {
+            p_block: 0,
+            // block: Ext4Block::default(),
+            depth: 0,
+            maxdepth: 0,
+            header: core::ptr::null_mut(),
+            index: core::ptr::null_mut(),
+            extent: core::ptr::null_mut(),
+        }
+    }
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct Ext4ExtentPathOld {
+    // Physical block number
+    pub p_block: u32,
+    // Single block descriptor
+    // pub block: Ext4Block,
+    // Depth of this extent node
+    pub depth: u16,
+    // Max depth of the extent tree
+    pub maxdepth: u16,
+    // Pointer to the extent header
+    pub header: *const Ext4ExtentHeader,
+    // Pointer to the index in the current node
+    pub index: *const Ext4ExtentIndex,
+    // Pointer to the extent in the current node
+    pub extent: *const Ext4Extent,
+}
+
+impl Default for Ext4ExtentPathOld {
+    fn default() -> Self {
+        Self {
+            p_block: 0,
+            // block: Ext4Block::default(),
+            depth: 0,
+            maxdepth: 0,
+            header: core::ptr::null_mut(),
+            index: core::ptr::null_mut(),
+            extent: core::ptr::null_mut(),
+        }
+    }
+}
+
+#[allow(unused)]
+pub fn ext4_first_extent(hdr: *const Ext4ExtentHeader) -> *const Ext4Extent {
+    unsafe {
+        let offset = core::mem::size_of::<Ext4ExtentHeader>();
+
+        (hdr as *const u8).add(offset) as *const Ext4Extent
+    }
+}
+
+pub fn ext4_first_extent_mut(hdr: *mut Ext4ExtentHeader) -> *mut Ext4Extent {
+    unsafe {
+        let offset = core::mem::size_of::<Ext4ExtentHeader>();
+
+        (hdr as *mut u8).add(offset) as *mut Ext4Extent
+    }
+}
+
+#[allow(unused)]
+pub fn ext4_last_extent(hdr: *const Ext4ExtentHeader) -> *const Ext4Extent {
+    unsafe {
+        let hdr_size = core::mem::size_of::<Ext4ExtentHeader>();
+        let ext_size = core::mem::size_of::<Ext4Extent>();
+        let hdr_ref = core::mem::transmute::<*const Ext4ExtentHeader, &Ext4ExtentHeader>(hdr);
+        let ext_count = hdr_ref.entries_count as usize;
+        (hdr as *const u8).add(hdr_size + (ext_count - 1) * ext_size) as *const Ext4Extent
+    }
+}
+
+pub fn ext4_last_extent_mut(hdr: *mut Ext4ExtentHeader) -> *mut Ext4Extent {
+    unsafe {
+        let hdr_size = core::mem::size_of::<Ext4ExtentHeader>();
+        let ext_size = core::mem::size_of::<Ext4Extent>();
+        let hdr_ref = core::mem::transmute::<*mut Ext4ExtentHeader, &Ext4ExtentHeader>(hdr);
+        let ext_count = hdr_ref.entries_count as usize;
+
+        (hdr as *mut u8).add(hdr_size + (ext_count - 1) * ext_size) as *mut Ext4Extent
+    }
+}
+
+#[allow(unused)]
+pub fn ext4_first_extent_index(hdr: *const Ext4ExtentHeader) -> *const Ext4ExtentIndex {
+    unsafe {
+        let offset = core::mem::size_of::<Ext4ExtentHeader>();
+
+        (hdr as *const u8).add(offset) as *const Ext4ExtentIndex
+    }
+}
+
+pub fn ext4_first_extent_index_mut(hdr: *mut Ext4ExtentHeader) -> *mut Ext4ExtentIndex {
+    unsafe {
+        let offset = core::mem::size_of::<Ext4ExtentHeader>();
+
+        (hdr as *mut u8).add(offset) as *mut Ext4ExtentIndex
+    }
+}
+
+#[allow(unused)]
+pub fn ext4_last_extent_index(hdr: *const Ext4ExtentHeader) -> *const Ext4ExtentIndex {
+    unsafe {
+        let hdr_size = core::mem::size_of::<Ext4ExtentHeader>();
+        let ext_size = core::mem::size_of::<Ext4ExtentIndex>();
+        let hdr_ref = core::mem::transmute::<*const Ext4ExtentHeader, &Ext4ExtentHeader>(hdr);
+        let ext_count = hdr_ref.entries_count as usize;
+        (hdr as *const u8).add(hdr_size + (ext_count - 1) * ext_size) as *const Ext4ExtentIndex
+    }
+}
+
+pub fn ext4_last_extent_index_mut(hdr: *mut Ext4ExtentHeader) -> *mut Ext4ExtentIndex {
+    unsafe {
+        let hdr_size = core::mem::size_of::<Ext4ExtentHeader>();
+        let ext_size = core::mem::size_of::<Ext4ExtentIndex>();
+        let hdr_ref = core::mem::transmute::<*mut Ext4ExtentHeader, &Ext4ExtentHeader>(hdr);
+        let ext_count = hdr_ref.entries_count as usize;
+        (hdr as *mut u8).add(hdr_size + (ext_count - 1) * ext_size) as *mut Ext4ExtentIndex
+    }
+}
+
+pub fn ext4_inode_hdr(inode: &Ext4Inode) -> *const Ext4ExtentHeader {
+    let eh = &inode.block as *const [u32; 15] as *const Ext4ExtentHeader;
+    eh
+}
+
+#[allow(unused)]
+pub fn ext4_inode_hdr_mut(inode: &mut Ext4Inode) -> *mut Ext4ExtentHeader {
+    let eh = &mut inode.block as *mut [u32; 15] as *mut Ext4ExtentHeader;
+    eh
+}
+
+/// 定义ext4_ext_binsearch函数,接受一个指向ext4_extent_path的可变引用和一个逻辑块号
+///
+/// 返回一个布尔值,表示是否找到了对应的extent
+pub fn ext4_ext_binsearch(path: &mut Ext4ExtentPath, block: u32) -> bool {
+    // 获取extent header的引用
+    // let eh = unsafe { &*path.header };
+    let eh = path.header;
+
+    unsafe {
+        if (*eh).entries_count == 0 {
+            return false;
+        }
+    }
+
+    // 定义左右两个指针,分别指向第一个和最后一个extent
+    let mut l = unsafe { ext4_first_extent_mut(eh).add(1) };
+    let mut r = ext4_last_extent_mut(eh);
+
+    // 如果extent header中没有有效的entry,直接返回false
+    unsafe {
+        if (*eh).entries_count == 0 {
+            return false;
+        }
+    }
+    // 使用while循环进行二分查找
+    while l <= r {
+        // 计算中间指针
+        let m = unsafe { l.add((r as usize - l as usize) / 2) };
+        // 获取中间指针所指向的extent的引用
+        let ext = unsafe { &*m };
+        // 比较逻辑块号和extent的第一个块号
+        if block < ext.first_block {
+            // 如果逻辑块号小于extent的第一个块号,说明目标在左半边,将右指针移动到中间指针的左边
+            r = unsafe { m.sub(1) };
+        } else {
+            // 如果逻辑块号大于或等于extent的第一个块号,说明目标在右半边,将左指针移动到中间指针的右边
+            l = unsafe { m.add(1) };
+        }
+    }
+    // 循环结束后,将path的extent字段设置为左指针的前一个位置
+    path.extent = unsafe { l.sub(1) };
+    // 返回true,表示找到了对应的extent
+    true
+}
+
+pub fn ext4_ext_binsearch_idx(path: &mut Ext4ExtentPath, block: ext4_lblk_t) -> bool {
+    // 获取extent header的引用
+    let eh = path.header;
+
+    // 定义左右两个指针,分别指向第一个和最后一个extent
+    let mut l = unsafe { ext4_first_extent_index_mut(eh).add(1) };
+    let mut r = ext4_last_extent_index_mut(eh);
+
+    // 如果extent header中没有有效的entry,直接返回false
+    unsafe {
+        if (*eh).entries_count == 0 {
+            return false;
+        }
+    }
+    // 使用while循环进行二分查找
+    while l <= r {
+        // 计算中间指针
+        let m = unsafe { l.add((r as usize - l as usize) / 2) };
+        // 获取中间指针所指向的extent的引用
+        let ext = unsafe { &*m };
+        // 比较逻辑块号和extent的第一个块号
+        if block < ext.first_block {
+            // 如果逻辑块号小于extent的第一个块号,说明目标在左半边,将右指针移动到中间指针的左边
+            r = unsafe { m.sub(1) };
+        } else {
+            // 如果逻辑块号大于或等于extent的第一个块号,说明目标在右半边,将左指针移动到中间指针的右边
+            l = unsafe { m.add(1) };
+        }
+    }
+    // 循环结束后,将path的extent字段设置为左指针的前一个位置
+    path.index = unsafe { l.sub(1) };
+    // 返回true,表示找到了对应的extent
+    true
+}
+
+#[allow(unused)]
+pub fn ext4_ext_find_extent(eh: *mut Ext4ExtentHeader, block: ext4_lblk_t) -> *mut Ext4Extent {
+    // 初始化一些变量
+    let mut low: i32;
+    let mut high: i32;
+    let mut mid: i32;
+    let mut ex: *mut Ext4Extent;
+
+    // 如果头部的extent数为0,返回空指针
+    if eh.is_null() || unsafe { (*eh).entries_count } == 0 {
+        return core::ptr::null_mut();
+    }
+
+    // 从头部获取第一个extent的指针
+    ex = ext4_first_extent_mut(eh);
+
+    // 如果头部的深度不为0,返回空指针
+    if unsafe { (*eh).depth } != 0 {
+        return core::ptr::null_mut();
+    }
+
+    // 使用二分查找法在extent数组中查找逻辑块号
+    low = 0;
+    high = unsafe { (*eh).entries_count - 1 } as i32;
+    while low <= high {
+        // 计算中间位置
+        mid = (low + high) / 2;
+
+        // 获取中间位置的extent的指针
+        ex = unsafe { ex.add(mid as usize) };
+
+        // 比较extent的逻辑块号和目标逻辑块号
+        if block >= unsafe { (*ex).first_block } {
+            // 如果目标逻辑块号大于等于extent的逻辑块号,说明目标在右半部分
+            low = mid + 1;
+        } else {
+            // 如果目标逻辑块号小于extent的逻辑块号,说明目标在左半部分
+            high = mid - 1;
+        }
+    }
+
+    // 如果没有找到目标,返回最后一个小于目标的extent的指针
+    if high < 0 {
+        return core::ptr::null_mut();
+    } else {
+        return unsafe { ex.add(high as usize) };
+    }
+}

+ 66 - 0
src/ext4_defs/file.rs

@@ -0,0 +1,66 @@
+use super::Ext4MountPoint;
+use crate::prelude::*;
+
+/// 文件描述符
+pub struct Ext4File {
+    /// 挂载点句柄
+    pub mp: *mut Ext4MountPoint,
+    /// 文件 inode id
+    pub inode: u32,
+    /// 打开标志
+    pub flags: u32,
+    /// 文件大小
+    pub fsize: u64,
+    /// 实际文件位置
+    pub fpos: usize,
+}
+
+impl Ext4File {
+    pub fn new() -> Self {
+        Self {
+            mp: core::ptr::null_mut(),
+            inode: 0,
+            flags: 0,
+            fsize: 0,
+            fpos: 0,
+        }
+    }
+}
+
+#[derive(Debug, PartialEq)]
+pub enum Ext4OpenFlags {
+    ReadOnly,
+    WriteOnly,
+    WriteCreateTrunc,
+    WriteCreateAppend,
+    ReadWrite,
+    ReadWriteCreateTrunc,
+    ReadWriteCreateAppend,
+}
+
+// 实现一个从字符串转换为标志的函数
+// 使用core::str::FromStr特性[^1^][1]
+impl core::str::FromStr for Ext4OpenFlags {
+    type Err = String;
+
+    fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
+        match s {
+            "r" | "rb" => Ok(Ext4OpenFlags::ReadOnly),
+            "w" | "wb" => Ok(Ext4OpenFlags::WriteOnly),
+            "a" | "ab" => Ok(Ext4OpenFlags::WriteCreateAppend),
+            "r+" | "rb+" | "r+b" => Ok(Ext4OpenFlags::ReadWrite),
+            "w+" | "wb+" | "w+b" => Ok(Ext4OpenFlags::ReadWriteCreateTrunc),
+            "a+" | "ab+" | "a+b" => Ok(Ext4OpenFlags::ReadWriteCreateAppend),
+            _ => Err(alloc::format!("Unknown open mode: {}", s)),
+        }
+    }
+}
+
+#[derive(Copy, PartialEq, Eq, Clone, Debug)]
+#[allow(unused)]
+pub enum SeekFrom {
+    Start(usize),
+    End(isize),
+    Current(isize),
+}
+

+ 277 - 0
src/ext4_defs/inode.rs

@@ -0,0 +1,277 @@
+use crate::constants::*;
+use super::crc::*;
+use crate::prelude::*;
+use super::BlockDevice;
+use super::Ext4BlockGroup;
+use super::Ext4ExtentHeader;
+use super::Ext4Superblock;
+
+#[repr(C)]
+#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
+pub struct Linux2 {
+    pub l_i_blocks_high: u16, // 原来是l_i_reserved1
+    pub l_i_file_acl_high: u16,
+    pub l_i_uid_high: u16,    // 这两个字段
+    pub l_i_gid_high: u16,    // 原来是reserved2[0]
+    pub l_i_checksum_lo: u16, // crc32c(uuid+inum+inode) LE
+    pub l_i_reserved: u16,
+}
+
+#[repr(C)]
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
+pub struct Ext4Inode {
+    pub mode: u16,
+    pub uid: u16,
+    pub size: u32,
+    pub atime: u32,
+    pub ctime: u32,
+    pub mtime: u32,
+    pub dtime: u32,
+    pub gid: u16,
+    pub links_count: u16,
+    pub blocks: u32,
+    pub flags: u32,
+    pub osd1: u32,
+    pub block: [u32; 15],
+    pub generation: u32,
+    pub file_acl: u32,
+    pub size_hi: u32,
+    pub faddr: u32,   /* Obsoleted fragment address */
+    pub osd2: Linux2, // 操作系统相关的字段2
+
+    pub i_extra_isize: u16,
+    pub i_checksum_hi: u16,  // crc32c(uuid+inum+inode) BE
+    pub i_ctime_extra: u32,  // 额外的修改时间(nsec << 2 | epoch)
+    pub i_mtime_extra: u32,  // 额外的文件修改时间(nsec << 2 | epoch)
+    pub i_atime_extra: u32,  // 额外的访问时间(nsec << 2 | epoch)
+    pub i_crtime: u32,       // 文件创建时间
+    pub i_crtime_extra: u32, // 额外的文件创建时间(nsec << 2 | epoch)
+    pub i_version_hi: u32,   // 64位版本的高32位
+}
+
+impl TryFrom<&[u8]> for Ext4Inode {
+    type Error = u64;
+    fn try_from(data: &[u8]) -> core::result::Result<Self, u64> {
+        let data = &data[..size_of::<Ext4Inode>()];
+        Ok(unsafe { core::ptr::read(data.as_ptr() as *const _) })
+    }
+}
+
+impl Ext4Inode {
+    pub fn ext4_get_inode_flags(&self) -> u32 {
+        self.flags
+    }
+    pub fn ext4_get_inode_mode(&self) -> u16 {
+        self.mode
+    }
+
+    pub fn ext4_inode_set_flags(&mut self, f: u32) {
+        self.flags |= f;
+    }
+
+    pub fn ext4_inode_set_mode(&mut self, mode: u16) {
+        self.mode |= mode;
+    }
+
+    pub fn ext4_inode_set_links_cnt(&mut self, cnt: u16) {
+        self.links_count = cnt;
+    }
+
+    pub fn ext4_inode_set_uid(&mut self, uid: u16) {
+        self.uid = uid;
+    }
+
+    pub fn ext4_inode_set_gid(&mut self, gid: u16) {
+        self.gid = gid;
+    }
+
+    pub fn ext4_inode_set_size(&mut self, size: u64) {
+        self.size = ((size << 32) >> 32) as u32;
+        self.size_hi = (size >> 32) as u32;
+    }
+
+    pub fn ext4_inode_get_size(&mut self) -> u64 {
+        self.size as u64 | ((self.size_hi as u64) << 32)
+    }
+
+    pub fn ext4_inode_set_access_time(&mut self, access_time: u32) {
+        self.atime = access_time;
+    }
+
+    pub fn ext4_inode_set_change_inode_time(&mut self, change_inode_time: u32) {
+        self.ctime = change_inode_time;
+    }
+
+    pub fn ext4_inode_set_modif_time(&mut self, modif_time: u32) {
+        self.mtime = modif_time;
+    }
+
+    pub fn ext4_inode_set_del_time(&mut self, del_time: u32) {
+        self.dtime = del_time;
+    }
+
+    pub fn ext4_inode_set_blocks_count(&mut self, blocks_count: u32) {
+        self.blocks = blocks_count;
+    }
+
+    pub fn ext4_inode_set_generation(&mut self, generation: u32) {
+        self.generation = generation;
+    }
+
+    pub fn ext4_inode_set_extra_isize(&mut self, extra_isize: u16) {
+        self.i_extra_isize = extra_isize;
+    }
+
+    #[allow(unused)]
+    fn get_checksum(&self, super_block: &Ext4Superblock) -> u32 {
+        let inode_size = super_block.inode_size();
+        let mut v: u32 = self.osd2.l_i_checksum_lo as u32;
+        if inode_size > 128 {
+            v |= (self.i_checksum_hi as u32) << 16;
+        }
+        v
+    }
+
+    pub fn set_inode_checksum_value(
+        &mut self,
+        super_block: &Ext4Superblock,
+        _inode_id: u32,
+        checksum: u32,
+    ) {
+        let inode_size = super_block.inode_size();
+
+        self.osd2.l_i_checksum_lo = ((checksum << 16) >> 16) as u16;
+        if inode_size > 128 {
+            self.i_checksum_hi = (checksum >> 16) as u16;
+        }
+    }
+
+    pub fn ext4_inode_get_extent_header(&mut self) -> *mut Ext4ExtentHeader {
+        let header_ptr = (&mut self.block) as *mut [u32; 15] as *mut Ext4ExtentHeader;
+        header_ptr
+    }
+
+    pub fn ext4_extent_tree_init(&mut self) {
+        let mut header = Ext4ExtentHeader::default();
+        header.ext4_extent_header_set_depth(0);
+        header.ext4_extent_header_set_entries_count(0);
+        header.ext4_extent_header_set_generation(0);
+        header.ext4_extent_header_set_magic();
+        header.ext4_extent_header_set_max_entries_count(4 as u16);
+
+        unsafe {
+            let header_ptr = &header as *const Ext4ExtentHeader as *const u32;
+            let array_ptr = &mut self.block as *mut [u32; 15] as *mut u32;
+            core::ptr::copy_nonoverlapping(header_ptr, array_ptr, 3);
+        }
+    }
+
+    pub fn ext4_inode_get_blocks_count(&self) -> u64 {
+        let mut blocks = self.blocks as u64;
+        if self.osd2.l_i_blocks_high != 0 {
+            blocks |= (self.osd2.l_i_blocks_high as u64) << 32;
+        }
+        blocks
+    }
+
+    // pub fn ext4_inode_set_blocks_count(&mut self, inode_blocks: u64){
+    //     self.blocks = inode_blocks as u32;
+    //     self.osd2.l_i_blocks_high = (inode_blocks >> 32) as u16;
+    // }
+
+    pub fn get_inode_disk_pos(
+        &self,
+        super_block: &Ext4Superblock,
+        block_device: Arc<dyn BlockDevice>,
+        inode_id: u32,
+    ) -> usize {
+        let inodes_per_group = super_block.inodes_per_group();
+        let inode_size = super_block.inode_size();
+        let group = (inode_id - 1) / inodes_per_group;
+        let index = (inode_id - 1) % inodes_per_group;
+
+        let bg = Ext4BlockGroup::load(block_device, super_block, group as usize).unwrap();
+
+        let inode_table_blk_num =
+            ((bg.inode_table_first_block_hi() as u64) << 32) | bg.inode_table_first_block_lo() as u64;
+        let offset =
+            inode_table_blk_num as usize * BLOCK_SIZE + (index * inode_size as u32) as usize;
+        offset
+    }
+
+    pub fn sync_inode_to_disk(
+        &self,
+        block_device: Arc<dyn BlockDevice>,
+        super_block: &Ext4Superblock,
+        inode_id: u32,
+    ) -> Result<()> {
+        let disk_pos = self.get_inode_disk_pos(super_block, block_device.clone(), inode_id);
+        let data = unsafe {
+            core::slice::from_raw_parts(self as *const _ as *const u8, size_of::<Ext4Inode>())
+        };
+        block_device.write_offset(disk_pos, data);
+
+        Ok(())
+    }
+
+    pub fn get_inode_checksum(&mut self, inode_id: u32, super_block: &Ext4Superblock) -> u32 {
+        let inode_size = super_block.inode_size();
+
+        let ino_index = inode_id as u32;
+        let ino_gen = self.generation;
+
+        // Preparation: temporarily set bg checksum to 0
+        self.osd2.l_i_checksum_lo = 0;
+        self.i_checksum_hi = 0;
+
+        let mut checksum = ext4_crc32c(
+            EXT4_CRC32_INIT,
+            &super_block.uuid(),
+            super_block.uuid().len() as u32,
+        );
+        checksum = ext4_crc32c(checksum, &ino_index.to_le_bytes(), 4);
+        checksum = ext4_crc32c(checksum, &ino_gen.to_le_bytes(), 4);
+
+        let mut raw_data = [0u8; 0x100];
+        copy_inode_to_array(&self, &mut raw_data);
+
+        // inode checksum
+        checksum = ext4_crc32c(checksum, &raw_data, inode_size as u32);
+
+        self.set_inode_checksum_value(super_block, inode_id, checksum);
+
+        if inode_size == 128 {
+            checksum &= 0xFFFF;
+        }
+
+        checksum
+    }
+
+    pub fn set_inode_checksum(&mut self, super_block: &Ext4Superblock, inode_id: u32) {
+        let inode_size = super_block.inode_size();
+        let checksum = self.get_inode_checksum(inode_id, super_block);
+
+        self.osd2.l_i_checksum_lo = ((checksum << 16) >> 16) as u16;
+        if inode_size > 128 {
+            self.i_checksum_hi = (checksum >> 16) as u16;
+        }
+    }
+
+    pub fn sync_inode_to_disk_with_csum(
+        &mut self,
+        block_device: Arc<dyn BlockDevice>,
+        super_block: &Ext4Superblock,
+        inode_id: u32,
+    ) -> Result<()> {
+        self.set_inode_checksum(super_block, inode_id);
+        self.sync_inode_to_disk(block_device, super_block, inode_id)
+    }
+}
+
+pub fn copy_inode_to_array(inode: &Ext4Inode, array: &mut [u8]) {
+    unsafe {
+        let inode_ptr = inode as *const Ext4Inode as *const u8;
+        let array_ptr = array as *mut [u8] as *mut u8;
+        core::ptr::copy_nonoverlapping(inode_ptr, array_ptr, 0x9c);
+    }
+}

+ 105 - 0
src/ext4_defs/inode_ref.rs

@@ -0,0 +1,105 @@
+use super::Ext4Inode;
+use crate::Ext4;
+use crate::prelude::*;
+use crate::constants::*;
+
+pub struct Ext4InodeRef {
+    pub inode_num: u32,
+    pub inner: InodeRefInner,
+    pub fs: Weak<Ext4>,
+}
+
+impl Ext4InodeRef {
+    pub fn new(fs: Weak<Ext4>) -> Self {
+        let inner = InodeRefInner {
+            inode: Ext4Inode::default(),
+            weak_self: Weak::new(),
+        };
+        let inode = Self {
+            inode_num: 0,
+            inner,
+            fs,
+        };
+        inode
+    }
+
+    pub fn fs(&self) -> Arc<Ext4> {
+        self.fs.upgrade().unwrap()
+    }
+
+    pub fn get_inode_ref(fs: Weak<Ext4>, inode_num: u32) -> Self {
+        let fs_clone = fs.clone();
+
+        let fs = fs.upgrade().unwrap();
+        let super_block = fs.super_block;
+
+        let inodes_per_group = super_block.inodes_per_group();
+        let inode_size = super_block.inode_size() as u64;
+        let group = (inode_num - 1) / inodes_per_group;
+        let index = (inode_num - 1) % inodes_per_group;
+        let group = fs.block_groups[group as usize];
+        let inode_table_blk_num = group.get_inode_table_blk_num();
+        let offset =
+            inode_table_blk_num as usize * BLOCK_SIZE + index as usize * inode_size as usize;
+
+        let data = fs.block_device.read_offset(offset);
+        let inode_data = &data[..core::mem::size_of::<Ext4Inode>()];
+        let inode = Ext4Inode::try_from(inode_data).unwrap();
+
+        let inner = InodeRefInner {
+            inode,
+            weak_self: Weak::new(),
+        };
+        let inode = Self {
+            inode_num,
+            inner,
+            fs: fs_clone,
+        };
+
+        inode
+    }
+
+    pub fn write_back_inode(&mut self) {
+        let fs = self.fs();
+        let block_device = fs.block_device.clone();
+        let super_block = fs.super_block.clone();
+        let inode_id = self.inode_num;
+        self.inner
+            .inode
+            .sync_inode_to_disk_with_csum(block_device, &super_block, inode_id)
+            .unwrap()
+    }
+
+    pub fn write_back_inode_without_csum(&mut self) {
+        let fs = self.fs();
+        let block_device = fs.block_device.clone();
+        let super_block = fs.super_block.clone();
+        let inode_id = self.inode_num;
+        self.inner
+            .inode
+            .sync_inode_to_disk(block_device, &super_block, inode_id)
+            .unwrap()
+    }
+}
+
+pub struct InodeRefInner {
+    pub inode: Ext4Inode,
+    pub weak_self: Weak<Ext4InodeRef>,
+}
+
+impl InodeRefInner {
+    pub fn inode(&self) -> Arc<Ext4InodeRef> {
+        self.weak_self.upgrade().unwrap()
+    }
+
+    pub fn write_back_inode(&mut self) {
+        let weak_inode_ref = self.weak_self.clone().upgrade().unwrap();
+        let fs = weak_inode_ref.fs();
+        let block_device = fs.block_device.clone();
+        let super_block = fs.super_block.clone();
+        let inode_id = weak_inode_ref.inode_num;
+        self.inode
+            .sync_inode_to_disk_with_csum(block_device, &super_block, inode_id)
+            .unwrap()
+    }
+}

+ 22 - 0
src/ext4_defs/mod.rs

@@ -0,0 +1,22 @@
+mod block;
+mod block_device;
+mod block_group;
+mod crc;
+mod dir_entry;
+mod extent;
+mod file;
+mod inode;
+mod inode_ref;
+mod mount_point;
+mod super_block;
+
+pub use block::*;
+pub use block_device::*;
+pub use block_group::*;
+pub use dir_entry::*;
+pub use extent::*;
+pub use file::*;
+pub use inode::*;
+pub use inode_ref::*;
+pub use mount_point::*;
+pub use super_block::*;

+ 27 - 0
src/ext4_defs/mount_point.rs

@@ -0,0 +1,27 @@
+use crate::prelude::*;
+
+/// Mount point descriptor
+#[derive(Clone)]
+pub struct Ext4MountPoint {
+    /**@brief   Mount done flag.*/
+    pub mounted: bool,
+    /**@brief   Mount point name (@ref ext4_mount)*/
+    pub mount_name: CString,
+    // pub mount_name_string: String,
+}
+
+impl Ext4MountPoint {
+    pub fn new(name: &str) -> Self {
+        Self {
+            mounted: false,
+            mount_name: CString::new(name).unwrap(),
+            // mount_name_string: name.to_string(),
+        }
+    }
+}
+
+impl Debug for Ext4MountPoint {
+    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+        write!(f, "Ext4MountPoint {{ mount_name: {:?} }}", self.mount_name)
+    }
+}

+ 255 - 0
src/ext4_defs/super_block.rs

@@ -0,0 +1,255 @@
+use crate::constants::*;
+use crate::prelude::*;
+use super::BlockDevice;
+use super::Ext4Inode;
+
+// 结构体表示超级块
+#[repr(C)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct Ext4Superblock {
+    inodes_count: u32,             // 节点数
+    blocks_count_lo: u32,          // 块数
+    reserved_blocks_count_lo: u32, // 保留块数
+    free_blocks_count_lo: u32,     // 空闲块数
+    free_inodes_count: u32,        // 空闲节点数
+    first_data_block: u32,         // 第一个数据块
+    log_block_size: u32,           // 块大小
+    log_cluster_size: u32,         // 废弃的片段大小
+    blocks_per_group: u32,         // 每组块数
+    frags_per_group: u32,          // 废弃的每组片段数
+    inodes_per_group: u32,         // 每组节点数
+    mount_time: u32,               // 挂载时间
+    write_time: u32,               // 写入时间
+    mount_count: u16,              // 挂载次数
+    max_mount_count: u16,          // 最大挂载次数
+    magic: u16,                    // 魔数,0xEF53
+    state: u16,                    // 文件系统状态
+    errors: u16,                   // 检测到错误时的行为
+    minor_rev_level: u16,          // 次版本号
+    last_check_time: u32,          // 最后检查时间
+    check_interval: u32,           // 检查间隔
+    creator_os: u32,               // 创建者操作系统
+    rev_level: u32,                // 版本号
+    def_resuid: u16,               // 保留块的默认uid
+    def_resgid: u16,               // 保留块的默认gid
+
+    // 仅适用于EXT4_DYNAMIC_REV超级块的字段
+    first_inode: u32,            // 第一个非保留节点
+    inode_size: u16,             // 节点结构的大小
+    block_group_index: u16,      // 此超级块的块组索引
+    features_compatible: u32,    // 兼容特性集
+    features_incompatible: u32,  // 不兼容特性集
+    features_read_only: u32,     // 只读兼容特性集
+    uuid: [u8; 16],              // 卷的128位uuid
+    volume_name: [u8; 16],       // 卷名
+    last_mounted: [u8; 64],      // 最后挂载的目录
+    algorithm_usage_bitmap: u32, // 用于压缩的算法
+
+    // 性能提示。只有当EXT4_FEATURE_COMPAT_DIR_PREALLOC标志打开时,才进行目录预分配
+    s_prealloc_blocks: u8,      // 尝试预分配的块数
+    s_prealloc_dir_blocks: u8,  // 为目录预分配的块数
+    s_reserved_gdt_blocks: u16, // 在线增长时每组保留的描述符数
+
+    // 如果EXT4_FEATURE_COMPAT_HAS_JOURNAL设置,表示支持日志
+    journal_uuid: [u8; 16],    // 日志超级块的UUID
+    journal_inode_number: u32, // 日志文件的节点号
+    journal_dev: u32,          // 日志文件的设备号
+    last_orphan: u32,          // 待删除节点的链表头
+    hash_seed: [u32; 4],       // HTREE散列种子
+    default_hash_version: u8,  // 默认的散列版本
+    journal_backup_type: u8,
+    desc_size: u16,            // 组描述符的大小
+    default_mount_opts: u32,   // 默认的挂载选项
+    first_meta_bg: u32,        // 第一个元数据块组
+    mkfs_time: u32,            // 文件系统创建的时间
+    journal_blocks: [u32; 17], // 日志节点的备份
+
+    // 如果EXT4_FEATURE_COMPAT_64BIT设置,表示支持64位
+    blocks_count_hi: u32,          // 块数
+    reserved_blocks_count_hi: u32, // 保留块数
+    free_blocks_count_hi: u32,     // 空闲块数
+    min_extra_isize: u16,          // 所有节点至少有#字节
+    want_extra_isize: u16,         // 新节点应该保留#字节
+    flags: u32,                    // 杂项标志
+    raid_stride: u16,              // RAID步长
+    mmp_interval: u16,             // MMP检查的等待秒数
+    mmp_block: u64,                // 多重挂载保护的块
+    raid_stripe_width: u32,        // 所有数据磁盘上的块数(N * 步长)
+    log_groups_per_flex: u8,       // FLEX_BG组的大小
+    checksum_type: u8,
+    reserved_pad: u16,
+    kbytes_written: u64,          // 写入的千字节数
+    snapshot_inum: u32,           // 活动快照的节点号
+    snapshot_id: u32,             // 活动快照的顺序ID
+    snapshot_r_blocks_count: u64, // 为活动快照的未来使用保留的块数
+    snapshot_list: u32,           // 磁盘上快照列表的头节点号
+    error_count: u32,             // 文件系统错误的数目
+    first_error_time: u32,        // 第一次发生错误的时间
+    first_error_ino: u32,         // 第一次发生错误的节点号
+    first_error_block: u64,       // 第一次发生错误的块号
+    first_error_func: [u8; 32],   // 第一次发生错误的函数
+    first_error_line: u32,        // 第一次发生错误的行号
+    last_error_time: u32,         // 最近一次发生错误的时间
+    last_error_ino: u32,          // 最近一次发生错误的节点号
+    last_error_line: u32,         // 最近一次发生错误的行号
+    last_error_block: u64,        // 最近一次发生错误的块号
+    last_error_func: [u8; 32],    // 最近一次发生错误的函数
+    mount_opts: [u8; 64],
+    usr_quota_inum: u32,       // 用于跟踪用户配额的节点
+    grp_quota_inum: u32,       // 用于跟踪组配额的节点
+    overhead_clusters: u32,    // 文件系统中的开销块/簇
+    backup_bgs: [u32; 2],      // 有sparse_super2超级块的组
+    encrypt_algos: [u8; 4],    // 使用的加密算法
+    encrypt_pw_salt: [u8; 16], // 用于string2key算法的盐
+    lpf_ino: u32,              // lost+found节点的位置
+    padding: [u32; 100],       // 块的末尾的填充
+    checksum: u32,             // crc32c(superblock)
+}
+
+impl TryFrom<Vec<u8>> for Ext4Superblock {
+    type Error = u64;
+    fn try_from(value: Vec<u8>) -> core::result::Result<Self, u64> {
+        let data = &value[..size_of::<Ext4Superblock>()];
+        Ok(unsafe { core::ptr::read(data.as_ptr() as *const _) })
+    }
+}
+
+impl Ext4Superblock {
+    /// Returns the size of inode structure.
+    pub fn inode_size(&self) -> u16 {
+        self.inode_size
+    }
+
+    /// Returns the size of inode structure.
+    pub fn inode_size_file(&self, inode: &Ext4Inode) -> u64 {
+        let mode = inode.mode;
+
+        // 获取inode的低32位大小
+        let mut v = inode.size as u64;
+        // 如果文件系统的版本号大于0,并且inode的类型是文件
+        if self.rev_level > 0 && (mode & EXT4_INODE_MODE_TYPE_MASK) == EXT4_INODE_MODE_FILE as u16 {
+            // 获取inode的高32位大小,并左移32位
+            let hi = (inode.size_hi as u64) << 32;
+            // 用或运算符将低32位和高32位拼接为一个u64值
+            v |= hi;
+        }
+
+        // 返回inode的大小
+        v
+    }
+
+    pub fn uuid(&self) -> [u8; 16] {
+        self.uuid
+    }
+
+    pub fn first_data_block(&self) -> u32 {
+        self.first_data_block
+    }
+
+    pub fn free_inodes_count(&self) -> u32 {
+        self.free_inodes_count
+    }
+
+    pub fn features_read_only(&self) -> u32 {
+        self.features_read_only
+    }
+
+    /// Returns total number of inodes.
+    pub fn total_inodes(&self) -> u32 {
+        self.inodes_count
+    }
+
+    /// Returns the number of blocks in each block group.
+    pub fn blocks_per_group(&self) -> u32 {
+        self.blocks_per_group
+    }
+
+    /// Returns the size of block.
+    pub fn block_size(&self) -> u32 {
+        1024 << self.log_block_size
+    }
+
+    /// Returns the number of inodes in each block group.
+    pub fn inodes_per_group(&self) -> u32 {
+        self.inodes_per_group
+    }
+
+    /// Returns the number of block groups.
+    pub fn block_groups_count(&self) -> u32 {
+        (((self.blocks_count_hi.to_le() as u64) << 32) as u32 | self.blocks_count_lo)
+            / self.blocks_per_group
+    }
+
+    pub fn desc_size(&self) -> u16 {
+        let size = self.desc_size;
+
+        if size < EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE {
+            return EXT4_MIN_BLOCK_GROUP_DESCRIPTOR_SIZE as u16;
+        } else {
+            size
+        }
+    }
+
+    pub fn extra_size(&self) -> u16 {
+        self.want_extra_isize
+    }
+
+    pub fn get_inodes_in_group_cnt(&self, bgid: u32) -> u32 {
+        let block_group_count = self.block_groups_count();
+        let inodes_per_group = self.inodes_per_group;
+
+        let total_inodes = ((self.inodes_count as u64) << 32) as u32;
+        if bgid < block_group_count - 1 {
+            inodes_per_group
+        } else {
+            total_inodes - ((block_group_count - 1) * inodes_per_group)
+        }
+    }
+
+    pub fn decrease_free_inodes_count(&mut self) {
+        self.free_inodes_count -= 1;
+    }
+
+    pub fn free_blocks_count(&self) -> u64 {
+        self.free_blocks_count_lo as u64 | ((self.free_blocks_count_hi as u64) << 32).to_le()
+    }
+
+    pub fn set_free_blocks_count(&mut self, free_blocks: u64) {
+        self.free_blocks_count_lo = ((free_blocks << 32) >> 32).to_le() as u32;
+        self.free_blocks_count_hi = (free_blocks >> 32) as u32;
+    }
+
+    pub fn sync_to_disk(&self, block_device: Arc<dyn BlockDevice>) {
+        let data = unsafe {
+            core::slice::from_raw_parts(self as *const _ as *const u8, size_of::<Ext4Superblock>())
+        };
+        block_device.write_offset(BASE_OFFSET, data);
+    }
+
+    pub fn sync_to_disk_with_csum(&self, block_device: Arc<dyn BlockDevice>) {
+        let data = unsafe {
+            core::slice::from_raw_parts(self as *const _ as *const u8, size_of::<Ext4Superblock>())
+        };
+        block_device.write_offset(BASE_OFFSET, data);
+    }
+
+    pub fn sync_super_block_to_disk(&self, block_device: Arc<dyn BlockDevice>) {
+        let data = unsafe {
+            core::slice::from_raw_parts(self as *const _ as *const u8, size_of::<Ext4Superblock>())
+        };
+        block_device.write_offset(BASE_OFFSET, data);
+    }
+}
+
+#[allow(unused)]
+pub fn ext4_inodes_in_group_cnt(bgid: u32, s: &Ext4Superblock) -> u32 {
+    let block_group_count = s.block_groups_count();
+    let inodes_per_group = s.inodes_per_group;
+    let total_inodes = ((s.inodes_count as u64) << 32) as u32;
+
+    if bgid < block_group_count - 1 {
+        inodes_per_group
+    } else {
+        total_inodes - ((block_group_count - 1) * inodes_per_group)
+    }
+}

+ 105 - 0
src/ext4_error.rs

@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: MPL-2.0
+
+/// Ext4Error number.
+#[repr(i32)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub enum Errnum {
+    EPERM = 1,       /* Operation not permitted */
+    ENOENT = 2,      /* No such file or directory */
+    EIO = 5,         /* I/O error */
+    ENXIO = 6,       /* No such device or address */
+    E2BIG = 7,       /* Argument list too long */
+    ENOMEM = 12,     /* Out of memory */
+    EACCES = 13,     /* Permission denied */
+    EFAULT = 14,     /* Bad address */
+    EEXIST = 17,     /* File exists */
+    ENODEV = 19,     /* No such device */
+    ENOTDIR = 20,    /* Not a directory */
+    EISDIR = 21,     /* Is a directory */
+    EINVAL = 22,     /* Invalid argument */
+    EFBIG = 27,      /* File too large */
+    ENOSPC = 28,     /* No space left on device */
+    EROFS = 30,      /* Read-only file system */
+    EMLINK = 31,     /* Too many links */
+    ERANGE = 34,     /* Math result not representable */
+    ENOTEMPTY = 39,  /* Directory not empty */
+    ENODATA = 61,    /* No data available */
+    ENOTSUP = 95,    /* Not supported */
+    ELINKFIAL = 97,  /* Link failed */
+    EALLOCFIAL = 98, /* Inode alloc failed */
+}
+
+/// error used in this crate
+#[derive(Debug, Clone, Copy)]
+pub struct Ext4Error {
+    errno: Errnum,
+    #[allow(unused)]
+    msg: Option<&'static str>,
+}
+
+impl Ext4Error {
+    pub const fn new(errno: Errnum) -> Self {
+        Ext4Error { errno, msg: None }
+    }
+
+    pub const fn with_message(errno: Errnum, msg: &'static str) -> Self {
+        Ext4Error {
+            errno,
+            msg: Some(msg),
+        }
+    }
+
+    pub const fn error(&self) -> Errnum {
+        self.errno
+    }
+}
+
+impl From<Errnum> for Ext4Error {
+    fn from(errno: Errnum) -> Self {
+        Ext4Error::new(errno)
+    }
+}
+
+impl From<core::str::Utf8Error> for Ext4Error {
+    fn from(_: core::str::Utf8Error) -> Self {
+        Ext4Error::with_message(Errnum::EINVAL, "Invalid utf-8 string")
+    }
+}
+
+impl From<alloc::string::FromUtf8Error> for Ext4Error {
+    fn from(_: alloc::string::FromUtf8Error) -> Self {
+        Ext4Error::with_message(Errnum::EINVAL, "Invalid utf-8 string")
+    }
+}
+
+impl From<core::ffi::FromBytesUntilNulError> for Ext4Error {
+    fn from(_: core::ffi::FromBytesUntilNulError) -> Self {
+        Ext4Error::with_message(Errnum::E2BIG, "Cannot find null in cstring")
+    }
+}
+
+impl From<core::ffi::FromBytesWithNulError> for Ext4Error {
+    fn from(_: core::ffi::FromBytesWithNulError) -> Self {
+        Ext4Error::with_message(Errnum::E2BIG, "Cannot find null in cstring")
+    }
+}
+
+impl From<alloc::ffi::NulError> for Ext4Error {
+    fn from(_: alloc::ffi::NulError) -> Self {
+        Ext4Error::with_message(Errnum::E2BIG, "Cannot find null in cstring")
+    }
+}
+
+#[macro_export]
+macro_rules! return_errno {
+    ($errno: expr) => {
+        return Err($crate::error::Ext4Error::new($errno))
+    };
+}
+
+#[macro_export]
+macro_rules! return_errno_with_message {
+    ($errno: expr, $message: expr) => {
+        return Err(Ext4Error::with_message($errno, $message))
+    };
+}

+ 12 - 0
src/jbd2.rs

@@ -0,0 +1,12 @@
+use crate::prelude::*;
+
+#[allow(unused)]
+pub trait Jbd2: Send + Sync + Any + Debug {
+    fn load_journal(&mut self);
+    fn journal_start(&mut self);
+    fn transaction_start(&mut self);
+    fn write_transaction(&mut self, block_id: usize, block_data: Vec<u8>);
+    fn transaction_stop(&mut self);
+    fn journal_stop(&mut self);
+    fn recover(&mut self);
+}

+ 21 - 0
src/lib.rs

@@ -0,0 +1,21 @@
+//! The Ext4 filesystem implementation in Rust.
+
+#![feature(error_in_core)]
+#![no_std]
+
+extern crate alloc;
+
+mod constants;
+mod ext4;
+mod ext4_defs;
+mod ext4_error;
+mod jbd2;
+mod prelude;
+mod utils;
+
+pub use ext4::*;
+pub use ext4_defs::*;
+pub use ext4_error::*;
+pub use utils::*;
+
+pub const BLOCK_SIZE: usize = 4096;

+ 28 - 0
src/prelude.rs

@@ -0,0 +1,28 @@
+#![allow(unused)]
+#![feature(error_in_core)]
+
+extern crate alloc;
+
+pub(crate) use alloc::boxed::Box;
+pub(crate) use alloc::collections::BTreeMap;
+pub(crate) use alloc::collections::BTreeSet;
+pub(crate) use alloc::collections::LinkedList;
+pub(crate) use alloc::collections::VecDeque;
+pub(crate) use alloc::ffi::CString;
+pub(crate) use alloc::string::String;
+pub(crate) use alloc::string::ToString;
+pub(crate) use alloc::sync::Arc;
+pub(crate) use alloc::sync::Weak;
+pub(crate) use alloc::vec;
+pub(crate) use alloc::vec::Vec;
+pub(crate) use bitflags::bitflags;
+pub(crate) use core::any::Any;
+pub(crate) use core::ffi::CStr;
+pub(crate) use core::fmt::Debug;
+pub(crate) use core::mem::{self, size_of};
+pub(crate) use core::ptr;
+
+pub(crate) use log::{debug, info, trace, warn};
+
+pub(crate) use crate::ext4_error::*;
+pub(crate) type Result<T> = core::result::Result<T, Ext4Error>;

+ 100 - 0
src/utils.rs

@@ -0,0 +1,100 @@
+use crate::prelude::*;
+
+/// 检查位图中的某一位是否被设置
+/// 参数 bmap 位图缓冲区
+/// 参数 bit 要检查的位
+pub fn ext4_bmap_is_bit_set(bmap: &[u8], bit: u32) -> bool {
+    bmap[(bit >> 3) as usize] & (1 << (bit & 7)) != 0
+}
+
+/// 检查位图中的某一位是否被清除
+/// 参数 bmap 位图缓冲区
+/// 参数 bit 要检查的位
+pub fn ext4_bmap_is_bit_clr(bmap: &[u8], bit: u32) -> bool {
+    !ext4_bmap_is_bit_set(bmap, bit)
+}
+
+/// 设置位图中的某一位
+/// 参数 bmap 位图
+/// 参数 bit 要设置的位
+pub fn ext4_bmap_bit_set(bmap: &mut [u8], bit: u32) {
+    bmap[(bit >> 3) as usize] |= 1 << (bit & 7);
+}
+
+// 查找位图中第一个为0的位
+pub fn ext4_bmap_bit_find_clr(bmap: &[u8], sbit: u32, ebit: u32, bit_id: &mut u32) -> bool {
+    let mut i: u32;
+    let mut bcnt = ebit - sbit;
+
+    i = sbit;
+
+    while i & 7 != 0 {
+        if bcnt == 0 {
+            return false;
+        }
+
+        if ext4_bmap_is_bit_clr(bmap, i) {
+            *bit_id = sbit;
+            return true;
+        }
+
+        i += 1;
+        bcnt -= 1;
+    }
+
+    let mut sbit = i;
+    let mut bmap = &bmap[(sbit >> 3) as usize..];
+    while bcnt >= 8 {
+        if bmap[0] != 0xFF {
+            for i in 0..8 {
+                if ext4_bmap_is_bit_clr(bmap, i) {
+                    *bit_id = sbit + i;
+                    return true;
+                }
+            }
+        }
+
+        bmap = &bmap[1..];
+        bcnt -= 8;
+        sbit += 8;
+    }
+
+    for i in 0..bcnt {
+        if ext4_bmap_is_bit_clr(bmap, i) {
+            *bit_id = sbit + i;
+            return true;
+        }
+    }
+
+    false
+}
+
+pub fn ext4_path_skip<'a>(path: &'a str, skip: &str) -> &'a str {
+    let path = &path.trim_start_matches(skip);
+    path
+}
+
+pub fn ext4_path_check(path: &str, is_goal: &mut bool) -> usize {
+    for (i, c) in path.chars().enumerate() {
+        if c == '/' {
+            *is_goal = false;
+            return i;
+        }
+    }
+    let path = path.to_string();
+    *is_goal = true;
+    return path.len();
+}
+
+// A function that takes a &str and returns a &[char]
+pub fn get_name(
+    name: [u8; 255],
+    len: usize,
+) -> core::result::Result<String, alloc::string::FromUtf8Error> {
+    let mut v: Vec<u8> = Vec::new();
+    for i in 0..len {
+        v.push(name[i]);
+    }
+    let s = String::from_utf8(v);
+    s
+}