Explorar el Código

refactor: ext4 directory methods

liujingx hace 11 meses
padre
commit
ae76f97d4e
Se han modificado 7 ficheros con 259 adiciones y 347 borrados
  1. 5 5
      src/ext4/alloc.rs
  2. 149 225
      src/ext4/dir.rs
  3. 8 16
      src/ext4/file.rs
  4. 3 4
      src/ext4/link.rs
  5. 0 13
      src/ext4/utils.rs
  6. 1 1
      src/ext4_defs/block.rs
  7. 93 83
      src/ext4_defs/dir_entry.rs

+ 5 - 5
src/ext4/alloc.rs

@@ -208,13 +208,13 @@ impl Ext4 {
         // inode_ref.dirty = true;
     }
     
-    pub fn ext4_fs_alloc_inode(&self, child_inode_ref: &mut Ext4InodeRef, filetype: u8) -> usize {
+    pub fn ext4_fs_alloc_inode(&self, child_inode_ref: &mut Ext4InodeRef, filetype: FileType) -> usize {
         let mut is_dir = false;
     
         let inode_size = self.super_block.inode_size();
         let extra_size = self.super_block.extra_size();
-    
-        if filetype == DirEntryType::EXT4_DE_DIR.bits() {
+
+        if filetype == FileType::Directory {
             is_dir = true;
         }
     
@@ -229,10 +229,10 @@ impl Ext4 {
     
         let mode = if is_dir {
             0o777 | EXT4_INODE_MODE_DIRECTORY as u16
-        } else if filetype == 0x7 {
+        } else if filetype == FileType::SymLink {
             0o777 | EXT4_INODE_MODE_SOFTLINK as u16
         } else {
-            let t = ext4_fs_correspond_inode_mode(filetype);
+            let t = file_type2inode_mode(filetype);
             // log::info!("ext4_fs_correspond_inode_mode {:x?}", ext4_fs_correspond_inode_mode(filetype));
             0o666 | t as u16
         };

+ 149 - 225
src/ext4/dir.rs

@@ -5,25 +5,86 @@ use crate::ext4_defs::*;
 use crate::prelude::*;
 
 impl Ext4 {
-    pub fn ext4_dir_add_entry(
+    /// Find a directory entry that matches a given name under a parent directory
+    ///
+    /// Save the result in `Ext4DirSearchResult`
+    pub fn dir_find_entry(
+        &self,
+        parent: &mut Ext4InodeRef,
+        name: &str,
+        result: &mut Ext4DirSearchResult,
+    ) -> usize {
+        let mut iblock: Ext4LogicBlockId = 0;
+        let mut fblock: Ext4FsBlockId = 0;
+
+        let inode_size: u32 = parent.inode.size;
+        let total_blocks: u32 = inode_size / BLOCK_SIZE as u32;
+
+        while iblock < total_blocks {
+            // Get the fs block id
+            self.ext4_fs_get_inode_dblk_idx(parent, &mut iblock, &mut fblock, false);
+            // Load block from disk
+            let mut data = self.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,
+            };
+            // Find the entry in block
+            let r = Self::find_entry_in_block(&mut ext4_block, name, result);
+            if r {
+                return EOK;
+            }
+            iblock += 1
+        }
+        ENOENT
+    }
+
+    /// Find a directory entry that matches a given name in a given block
+    ///
+    /// Save the result in `Ext4DirSearchResult`
+    fn find_entry_in_block(
+        block: &Ext4Block,
+        name: &str,
+        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 += de.rec_len() as usize;
+            // Unused dir entry
+            if de.unused() {
+                continue;
+            }
+            // Compare name
+            if de.compare_name(name) {
+                result.dentry = de;
+                return true;
+            }
+        }
+        false
+    }
+
+    /// Add an entry to a directory
+    pub fn dir_add_entry(
         &self,
         parent: &mut Ext4InodeRef,
         child: &mut Ext4InodeRef,
         path: &str,
-        len: u32,
     ) -> usize {
-        let mut iblock = 0;
         let block_size = self.super_block.block_size();
         let inode_size = parent.inode.size();
         let total_blocks = inode_size as u32 / block_size;
 
+        let mut iblock = 0;
         let mut fblock: Ext4FsBlockId = 0;
 
-        // log::info!("ext4_dir_add_entry parent inode {:x?} inode_size {:x?}", parent.inode_num, inode_size);
+        // Try finding a block with enough space
         while iblock < total_blocks {
+            // Get the parent fs block id
             self.ext4_fs_get_inode_dblk_idx(parent, &mut iblock, &mut fblock, false);
-
-            // load_block
+            // Load the parent block from disk
             let mut data = self.block_device.read_offset(fblock as usize * BLOCK_SIZE);
             let mut ext4_block = Ext4Block {
                 logical_block_id: iblock,
@@ -31,268 +92,137 @@ impl Ext4 {
                 block_data: &mut data,
                 dirty: false,
             };
-            let r = self.ext4_dir_try_insert_entry(parent, &mut ext4_block, child, path, len);
+            // Try inserting the entry to parent block
+            let r = self.insert_entry_to_old_block(&mut ext4_block, child, path);
             if r == EOK {
                 return EOK;
             }
-
-            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 = Self::ext4_dir_find_in_block(&mut ext4_block, path, len, &mut dir_search_result);
-            if r {
-                return EOK;
-            }
-
+            // Current block has no enough space
             iblock += 1;
         }
 
-        /* No free block found - needed to allocate next data block */
+        // No free block found - needed to allocate a new data block
         iblock = 0;
         fblock = 0;
-
-        self.ext4_fs_append_inode_dblk(parent, &mut (iblock as u32), &mut fblock);
-
-        /* Load new block */
+        // Append a new data block
+        self.ext4_fs_append_inode_dblk(parent, &mut iblock, &mut fblock);
+        // Load new block
         let block_device = self.block_device.clone();
         let mut data = block_device.read_offset(fblock as usize * BLOCK_SIZE);
-        let mut ext4_block = Ext4Block {
+        let mut new_block = Ext4Block {
             logical_block_id: iblock,
             disk_block_id: fblock,
             block_data: &mut data,
             dirty: false,
         };
+        // Write the entry to block
+        self.insert_entry_to_new_block(&mut new_block, child, path);
 
+        EOK
+    }
+
+    /// Insert a directory entry of a child inode into a new parent block.
+    /// A new block must have enough space
+    fn insert_entry_to_new_block(
+        &self,
+        dst_blk: &mut Ext4Block,
+        child: &mut Ext4InodeRef,
+        name: &str,
+    ) {
+        // Set the entry
         let mut new_entry = Ext4DirEntry::default();
-        let el = BLOCK_SIZE - size_of::<Ext4DirEntryTail>();
-        self.ext4_dir_write_entry(&mut new_entry, el as u16, &child, path, len);
+        let rec_len = BLOCK_SIZE - size_of::<Ext4DirEntryTail>();
+        Self::set_dir_entry(&mut new_entry, rec_len as u16, &child, name);
 
-        new_entry.copy_to_byte_slice(&mut ext4_block.block_data, 0);
+        // Write to block
+        new_entry.copy_to_byte_slice(&mut dst_blk.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)
-        };
+        // Set tail
+        let mut tail = Ext4DirEntryTail::default();
         tail.rec_len = size_of::<Ext4DirEntryTail>() as u16;
         tail.reserved_ft = 0xDE;
         tail.reserved_zero1 = 0;
         tail.reserved_zero2 = 0;
+        tail.set_csum(&self.super_block, &new_entry, &dst_blk.block_data[..]);
 
-        tail.ext4_dir_set_csum(&self.super_block, &new_entry, &ext4_block.block_data[..]);
-
+        // Copy to block
         let tail_offset = BLOCK_SIZE - size_of::<Ext4DirEntryTail>();
-        tail.copy_to_byte_slice(&mut ext4_block.block_data, tail_offset);
+        tail.copy_to_byte_slice(&mut dst_blk.block_data, tail_offset);
 
-        tail.ext4_dir_set_csum(&self.super_block, &new_entry, &ext4_block.block_data[..]);
-
-        ext4_block.sync_blk_to_disk(block_device.clone());
-
-        // struct ext4_block b;
-
-        EOK
+        // Sync to disk
+        dst_blk.sync_to_disk(self.block_device.clone());
     }
 
-    pub fn ext4_dir_try_insert_entry(
+    /// Try insert a directory entry of child inode into a parent block.
+    /// Return `ENOSPC` if parent block has no enough space.
+    fn insert_entry_to_old_block(
         &self,
-        _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 required_size = Ext4DirEntry::required_size(name.len());
         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 {
+            if de.unused() {
                 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;
-                    self.ext4_dir_write_entry(
-                        &mut new_entry,
-                        free_space as u16,
-                        &child,
-                        name,
-                        name_len,
-                    );
-
-                    // update parent new_de to blk_data
-                    de.copy_to_byte_slice(&mut dst_blk.block_data, offset);
-                    new_entry.copy_to_byte_slice(&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 = self.block_device.clone();
-                    tail.ext4_dir_set_csum(&self.super_block, &de, &dst_blk.block_data[offset..]);
-
-                    let parent_de = Ext4DirEntry::try_from(&dst_blk.block_data[..]).unwrap();
-                    tail.ext4_dir_set_csum(&self.super_block, &parent_de, &dst_blk.block_data[..]);
-
-                    let tail_offset = BLOCK_SIZE - size_of::<Ext4DirEntryTail>();
-                    tail.copy_to_byte_slice(&mut dst_blk.block_data, tail_offset);
-
-                    // sync to disk
-                    dst_blk.sync_blk_to_disk(block_device.clone());
-
-                    return EOK;
-                }
+            // Split valid dir entry
+            let rec_len = de.rec_len();
+
+            // The actual size that `de` uses
+            let used_size = de.used_size();
+            // The rest size
+            let free_size = rec_len as usize - used_size;
+            // Compare size
+            if free_size < required_size {
+                // No enough space, try next dir ent
+                offset = offset + rec_len as usize;
+                continue;
             }
-            offset = offset + de.entry_len as usize;
+            // Has enough space
+            // Set the entry
+            de.set_rec_len(free_size as u16);
+            let mut new_entry = Ext4DirEntry::default();
+            Self::set_dir_entry(&mut new_entry, free_size as u16, &child, name);
+
+            // Write dir entries to blk_data
+            de.copy_to_byte_slice(&mut dst_blk.block_data, offset);
+            new_entry.copy_to_byte_slice(&mut dst_blk.block_data, offset + used_size);
+
+            // Set tail csum
+            let mut tail = Ext4DirEntryTail::from(&mut dst_blk.block_data, BLOCK_SIZE).unwrap();
+            tail.set_csum(&self.super_block, &de, &dst_blk.block_data[offset..]);
+            let parent_de = Ext4DirEntry::try_from(&dst_blk.block_data[..]).unwrap();
+            tail.set_csum(&self.super_block, &parent_de, &dst_blk.block_data[..]);
+
+            // Write tail to blk_data
+            let tail_offset = BLOCK_SIZE - size_of::<Ext4DirEntryTail>();
+            tail.copy_to_byte_slice(&mut dst_blk.block_data, tail_offset);
+
+            // Sync to disk
+            dst_blk.sync_to_disk(self.block_device.clone());
+
+            return EOK;
         }
-
         ENOSPC
     }
 
-    // 写入一个ext4目录项
-    pub fn ext4_dir_write_entry(
-        &self,
-        en: &mut Ext4DirEntry,
-        entry_len: u16,
-        child: &Ext4InodeRef,
-        name: &str,
-        name_len: u32,
-    ) {
-        let file_type = (child.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_id;
-        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_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(
-        &self,
-        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: Ext4FsBlockId = 0;
-
-        let inode_size: u32 = parent.inode.size;
-        let total_blocks: u32 = inode_size / BLOCK_SIZE as u32;
-
-        while iblock < total_blocks {
-            self.ext4_fs_get_inode_dblk_idx(parent, &mut iblock, &mut fblock, false);
-
-            // load_block
-            let mut data = self.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 = Self::ext4_dir_find_in_block(&mut ext4_block, name, name_len, result);
-            if r {
-                return EOK;
-            }
-
-            iblock += 1
-        }
-
-        ENOENT
-    }
-
-    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
+    /// Set the directory entry for an inode
+    fn set_dir_entry(en: &mut Ext4DirEntry, rec_len: u16, child: &Ext4InodeRef, name: &str) {
+        en.set_inode(child.inode_id);
+        en.set_rec_len(rec_len);
+        en.set_entry_type(child.inode.mode());
+        en.set_name(name);
     }
 
     pub fn ext4_dir_mk(&self, path: &str) -> Result<usize> {
         let mut file = Ext4File::new();
         let flags = "w";
 
-        let filetype = DirEntryType::EXT4_DE_DIR;
+        let filetype = FileType::Directory;
 
         // get mount point
         let mut ptr = Box::new(self.mount_point.clone());
@@ -307,13 +237,7 @@ impl Ext4 {
 
         let mut root_inode_ref = self.get_root_inode_ref();
 
-        let r = self.ext4_generic_open(
-            &mut file,
-            path,
-            iflags,
-            filetype.bits(),
-            &mut root_inode_ref,
-        );
+        let r = self.ext4_generic_open(&mut file, path, iflags, filetype, &mut root_inode_ref);
         r
     }
 }

+ 8 - 16
src/ext4/file.rs

@@ -11,7 +11,7 @@ impl Ext4 {
         file: &mut Ext4File,
         path: &str,
         iflags: u32,
-        ftype: u8,
+        ftype: FileType,
         parent_inode: &mut Ext4InodeRef,
     ) -> Result<usize> {
         let mut is_goal = false;
@@ -43,10 +43,9 @@ impl Ext4 {
             search_path = ext4_path_skip(search_path, "/");
             len = ext4_path_check(search_path, &mut is_goal);
 
-            let r = self.ext4_dir_find_entry(
+            let r = self.dir_find_entry(
                 &mut search_parent,
                 &search_path[..len as usize],
-                len as u32,
                 &mut dir_search_result,
             );
 
@@ -68,7 +67,7 @@ impl Ext4 {
                 let r = if is_goal {
                     self.ext4_fs_alloc_inode(&mut child_inode_ref, ftype)
                 } else {
-                    self.ext4_fs_alloc_inode(&mut child_inode_ref, DirEntryType::EXT4_DE_DIR.bits())
+                    self.ext4_fs_alloc_inode(&mut child_inode_ref, FileType::Directory)
                 };
 
                 if r != EOK {
@@ -82,7 +81,6 @@ impl Ext4 {
                     &mut search_parent,
                     &mut child_inode_ref,
                     &search_path[..len as usize],
-                    len as u32,
                 );
 
                 if r != EOK {
@@ -96,19 +94,13 @@ impl Ext4 {
 
                 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;
+                file.inode = dir_search_result.dentry.inode();
                 return Ok(EOK);
             } else {
-                search_parent = self.get_inode_ref(dir_search_result.dentry.inode);
+                search_parent = self.get_inode_ref(dir_search_result.dentry.inode());
                 search_path = &search_path[len..];
             }
         }
@@ -130,9 +122,9 @@ impl Ext4 {
 
         // file for dir
         let filetype = if file_expect {
-            DirEntryType::EXT4_DE_REG_FILE
+            FileType::RegularFile
         } else {
-            DirEntryType::EXT4_DE_DIR
+            FileType::Directory
         };
 
         if iflags & O_CREAT != 0 {
@@ -141,7 +133,7 @@ impl Ext4 {
 
         let mut root_inode_ref = self.get_root_inode_ref();
 
-        let r = self.ext4_generic_open(file, path, iflags, filetype.bits(), &mut root_inode_ref);
+        let r = self.ext4_generic_open(file, path, iflags, filetype, &mut root_inode_ref);
 
         r
     }

+ 3 - 4
src/ext4/link.rs

@@ -8,11 +8,10 @@ impl Ext4 {
         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 = self.ext4_dir_add_entry(parent, child, name, name_len);
+        let _r = self.dir_add_entry(parent, child, name);
     
         /* Fill new dir -> add '.' and '..' entries.
          * Also newly allocated inode should have 0 link count.
@@ -29,10 +28,10 @@ impl Ext4 {
             child_inode_ref.inode_id = child.inode_id;
             child_inode_ref.inode = child.inode.clone();
     
-            let _r = self.ext4_dir_add_entry(&mut child_inode_ref, child, ".", 1);
+            let _r = self.dir_add_entry(&mut child_inode_ref, child, ".");
             child.inode.size = child_inode_ref.inode.size;
             child.inode.block = child_inode_ref.inode.block;
-            let _r = self.ext4_dir_add_entry(&mut child_inode_ref, parent, "..", 2);
+            let _r = self.dir_add_entry(&mut child_inode_ref, parent, "..");
     
             child.inode.links_count = 2;
             parent.inode.links_count += 1;

+ 0 - 13
src/ext4/utils.rs

@@ -87,19 +87,6 @@ pub fn ext4_path_check(path: &str, is_goal: &mut bool) -> usize {
     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
-}
-
 // 使用libc库定义的常量
 pub fn ext4_parse_flags(flags: &str) -> Result<u32> {
     match flags {

+ 1 - 1
src/ext4_defs/block.rs

@@ -17,7 +17,7 @@ pub struct Ext4Block<'a> {
 }
 
 impl<'a> Ext4Block<'a> {
-    pub fn sync_blk_to_disk(&self, block_device: Arc<dyn BlockDevice>) {
+    pub fn sync_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);
     }

+ 93 - 83
src/ext4_defs/dir_entry.rs

@@ -4,16 +4,16 @@
 //! linear array of directory entries.
 
 use super::crc::*;
-use super::BlockDevice;
 use super::Ext4Block;
 use super::Ext4Superblock;
 use crate::constants::*;
 use crate::prelude::*;
+use alloc::string::FromUtf8Error;
 
 #[repr(C)]
 pub union Ext4DirEnInner {
-    pub name_length_high: u8, // 高8位的文件名长度
-    pub inode_type: u8,       // 引用的inode的类型(在rev >= 0.5中)
+    pub name_length_high: u8,     // 高8位的文件名长度
+    pub inode_type: FileType, // 引用的inode的类型(在rev >= 0.5中)
 }
 
 impl Debug for Ext4DirEnInner {
@@ -39,18 +39,18 @@ impl Default for Ext4DirEnInner {
 #[repr(C)]
 #[derive(Debug)]
 pub struct Ext4DirEntry {
-    pub inode: u32,            // 该目录项指向的inode的编号
-    pub entry_len: u16,        // 到下一个目录项的距离
-    pub name_len: u8,          // 低8位的文件名长度
-    pub inner: Ext4DirEnInner, // 联合体成员
-    pub name: [u8; 255],       // 文件名
+    inode: u32,            // 该目录项指向的inode的编号
+    rec_len: u16,          // 到下一个目录项的距离
+    name_len: u8,          // 低8位的文件名长度
+    inner: Ext4DirEnInner, // 联合体成员
+    name: [u8; 255],       // 文件名
 }
 
 impl Default for Ext4DirEntry {
     fn default() -> Self {
         Self {
             inode: 0,
-            entry_len: 0,
+            rec_len: 0,
             name_len: 0,
             inner: Ext4DirEnInner::default(),
             name: [0; 255],
@@ -67,15 +67,56 @@ impl<T> TryFrom<&[T]> for Ext4DirEntry {
 }
 
 impl Ext4DirEntry {
-    pub fn name(&self) -> String {
+    pub fn name(&self) -> core::result::Result<String, FromUtf8Error> {
         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()
+        String::from_utf8(name.to_vec())
     }
 
-    pub fn name_len(&self) -> usize {
-        self.name_len as usize
+    pub fn compare_name(&self, name: &str) -> bool {
+        &self.name[..name.len()] == name.as_bytes()
+    }
+
+    pub fn set_name(&mut self, name: &str) {
+        self.name_len = name.len() as u8;
+        self.name[..name.len()].copy_from_slice(name.as_bytes());
+    }
+
+    pub fn rec_len(&self) -> u16 {
+        self.rec_len
+    }
+
+    pub fn set_rec_len(&mut self, len: u16) {
+        self.rec_len = len;
+    }
+
+    pub fn inode(&self) -> u32 {
+        self.inode
+    }
+
+    pub fn set_inode(&mut self, inode: u32) {
+        self.inode = inode;
+    }
+
+    /// Unused directory entries are signified by inode = 0
+    pub fn unused(&self) -> bool {
+        self.inode == 0
+    }
+
+    /// Set the dir entry's inode type given the corresponding inode mode
+    pub fn set_entry_type(&mut self, inode_mode: u16) {
+        self.inner.inode_type = inode_mode2file_type(inode_mode);
+    }
+
+    /// Get the required size to save this directory entry, 4-byte aligned
+    pub fn required_size(name_len: usize) -> usize {
+        // u32 + u16 + u8 + Ext4DirEnInner + name -> align to 4
+        (core::mem::size_of::<Ext4FakeDirEntry>() + name_len + 3) / 4 * 4
+    }
+
+    /// Get the used size of this directory entry, 4-bytes alighed
+    pub fn used_size(&self) -> usize {
+        Self::required_size(self.name_len as usize)
     }
 
     pub fn calc_csum(&self, s: &Ext4Superblock, blk_data: &[u8]) -> u32 {
@@ -116,7 +157,7 @@ impl Ext4DirEntry {
 }
 
 #[repr(C)]
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, Default)]
 pub struct Ext4DirEntryTail {
     pub reserved_zero1: u32,
     pub rec_len: u16,
@@ -147,51 +188,10 @@ impl Ext4DirEntryTail {
         }
     }
 
-    pub fn ext4_dir_set_csum(&mut self, s: &Ext4Superblock, diren: &Ext4DirEntry, blk_data: &[u8]) {
+    pub fn set_csum(&mut self, s: &Ext4Superblock, diren: &Ext4DirEntry, blk_data: &[u8]) {
         self.checksum = diren.calc_csum(s, blk_data);
     }
 
-    #[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,
-        );
-    }
-
     pub fn copy_to_byte_slice(&self, slice: &mut [u8], offset: usize) {
         let de_ptr = self as *const Ext4DirEntryTail as *const u8;
         let slice_ptr = slice as *mut [u8] as *mut u8;
@@ -213,7 +213,7 @@ impl<'a> Ext4DirSearchResult<'a> {
     }
 }
 
-/// fake dir entry
+/// Fake dir entry. A normal entry without `name` field`
 #[repr(C)]
 pub struct Ext4FakeDirEntry {
     inode: u32,
@@ -222,33 +222,43 @@ pub struct Ext4FakeDirEntry {
     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;
-    }
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+#[repr(u8)]
+pub enum FileType {
+    Unknown,
+    RegularFile,
+    Directory,
+    CharacterDev,
+    BlockDev,
+    Fifo,
+    Socket,
+    SymLink,
 }
 
-pub fn ext4_fs_correspond_inode_mode(filetype: u8) -> u32 {
-    let file_type = DirEntryType::from_bits(filetype).unwrap();
+pub fn inode_mode2file_type(inode_mode: u16) -> FileType {
+    let file_type = (inode_mode & EXT4_INODE_MODE_TYPE_MASK) as usize;
     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
-        }
+        EXT4_INODE_MODE_FILE => FileType::RegularFile,
+        EXT4_INODE_MODE_DIRECTORY => FileType::Directory,
+        EXT4_INODE_MODE_CHARDEV => FileType::CharacterDev,
+        EXT4_INODE_MODE_BLOCKDEV => FileType::BlockDev,
+        EXT4_INODE_MODE_FIFO => FileType::Fifo,
+        EXT4_INODE_MODE_SOCKET => FileType::Socket,
+        EXT4_INODE_MODE_SOFTLINK => FileType::SymLink,
+        _ => FileType::Unknown,
     }
 }
+
+pub fn file_type2inode_mode(dirent_type: FileType) -> u16 {
+    let inode_mode = match dirent_type {
+        FileType::RegularFile => EXT4_INODE_MODE_FILE,
+        FileType::Directory => EXT4_INODE_MODE_DIRECTORY,
+        FileType::SymLink => EXT4_INODE_MODE_SOFTLINK,
+        FileType::CharacterDev => EXT4_INODE_MODE_CHARDEV,
+        FileType::BlockDev => EXT4_INODE_MODE_BLOCKDEV,
+        FileType::Fifo => EXT4_INODE_MODE_FIFO,
+        FileType::Socket => EXT4_INODE_MODE_SOCKET,
+        _ => EXT4_INODE_MODE_FILE,
+    };
+    inode_mode as u16
+}