Jelajahi Sumber

refactor: directory block

liujingx 10 bulan lalu
induk
melakukan
43419e7164

+ 39 - 147
src/ext4/dir.rs

@@ -6,7 +6,7 @@ use crate::return_error;
 
 impl Ext4 {
     /// Find a directory entry that matches a given name under a parent directory
-    pub(super) fn dir_find_entry(&self, dir: &InodeRef, name: &str) -> Result<DirEntry> {
+    pub(super) fn dir_find_entry(&self, dir: &InodeRef, name: &str) -> Result<InodeId> {
         trace!("Dir find entry: dir {}, name {}", dir.id, name);
         let total_blocks: u32 = dir.inode.block_count() as u32;
         let mut iblock: LBlockId = 0;
@@ -14,9 +14,9 @@ impl Ext4 {
             // Get the fs block id
             let fblock = self.extent_query(dir, iblock)?;
             // Load block from disk
-            let block = self.read_block(fblock);
+            let dir_block = DirBlock::new(self.read_block(fblock));
             // Find the entry in block
-            let res = Self::find_entry_in_block(&block, name);
+            let res = dir_block.get(name);
             if let Some(r) = res {
                 return Ok(r);
             }
@@ -44,31 +44,45 @@ impl Ext4 {
             name
         );
         let total_blocks: u32 = dir.inode.block_count() as u32;
-
-        // Try finding a block with enough space
         let mut iblock: LBlockId = 0;
+        // Try finding a block with enough space
         while iblock < total_blocks {
             // Get the parent physical block id
             let fblock = self.extent_query(dir, iblock).unwrap();
             // Load the parent block from disk
-            let mut block = self.read_block(fblock);
+            let mut dir_block = DirBlock::new(self.read_block(fblock));
             // Try inserting the entry to parent block
-            if self.insert_entry_to_old_block(dir, child, name, &mut block) {
+            if dir_block.insert(name, child.id, child.inode.file_type()) {
+                // Update checksum
+                dir_block.set_checksum(
+                    &self.read_super_block().uuid(),
+                    dir.id,
+                    dir.inode.generation(),
+                );
+                // Write the block back to disk
+                self.write_block(&dir_block.block());
                 return Ok(());
             }
             // Current block has no enough space
             iblock += 1;
         }
-
         // No free block found - needed to allocate a new data block
         // Append a new data block
         let (_, fblock) = self.inode_append_block(dir)?;
-        // Load new block
-        let mut new_block = self.read_block(fblock);
-        // Write the entry to block
-        self.insert_entry_to_new_block(dir, child, name, &mut new_block);
         // Update inode size
         dir.inode.set_size(dir.inode.size() + BLOCK_SIZE as u64);
+        // Load new block
+        let mut new_dir_block = DirBlock::new(self.read_block(fblock));
+        // Write the entry to block
+        new_dir_block.init();
+        new_dir_block.insert(name, child.id, child.inode.file_type());
+        new_dir_block.set_checksum(
+            &self.read_super_block().uuid(),
+            dir.id,
+            dir.inode.generation(),
+        );
+        // Write the block back to disk
+        self.write_block(&new_dir_block.block());
 
         Ok(())
     }
@@ -83,10 +97,17 @@ impl Ext4 {
             // Get the parent physical block id
             let fblock = self.extent_query(dir, iblock).unwrap();
             // Load the block from disk
-            let mut block = self.read_block(fblock);
+            let mut dir_block = DirBlock::new(self.read_block(fblock));
             // Try removing the entry
-            if Self::remove_entry_from_block(&mut block, name) {
-                self.write_block(&block);
+            if dir_block.remove(name) {
+                // Update checksum
+                dir_block.set_checksum(
+                    &self.read_super_block().uuid(),
+                    dir.id,
+                    dir.inode.generation(),
+                );
+                // Write the block back to disk
+                self.write_block(&dir_block.block());
                 return Ok(());
             }
             // Current block has no enough space
@@ -102,7 +123,7 @@ impl Ext4 {
     }
 
     /// Get all entries under a directory
-    pub(super) fn dir_get_all_entries(&self, dir: &InodeRef) -> Vec<DirEntry> {
+    pub(super) fn dir_list_entries(&self, dir: &InodeRef) -> Vec<DirEntry> {
         let total_blocks = dir.inode.block_count() as u32;
         let mut entries: Vec<DirEntry> = Vec::new();
         let mut iblock: LBlockId = 0;
@@ -110,140 +131,11 @@ impl Ext4 {
             // Get the fs block id
             let fblock = self.extent_query(dir, iblock).unwrap();
             // Load block from disk
-            let block = self.read_block(fblock);
+            let dir_block = DirBlock::new(self.read_block(fblock));
             // Get all entries from block
-            Self::get_all_entries_from_block(&block, &mut entries);
+            dir_block.list(&mut entries);
             iblock += 1;
         }
         entries
     }
-
-    /// Find a directory entry that matches a given name in a given block
-    fn find_entry_in_block(block: &Block, name: &str) -> Option<DirEntry> {
-        let mut offset = 0;
-        while offset < BLOCK_SIZE {
-            let de: DirEntry = block.read_offset_as(offset);
-            if !de.unused() && de.compare_name(name) {
-                return Some(de);
-            }
-            offset += de.rec_len() as usize;
-        }
-        None
-    }
-
-    /// Remove a directory entry that matches a given name from a given block
-    fn remove_entry_from_block(block: &mut Block, name: &str) -> bool {
-        let mut offset = 0;
-        while offset < BLOCK_SIZE {
-            let mut de: DirEntry = block.read_offset_as(offset);
-            if !de.unused() && de.compare_name(name) {
-                // Mark the target entry as unused
-                de.set_unused();
-                block.write_offset_as(offset, &de);
-                return true;
-            }
-            offset += de.rec_len() as usize;
-        }
-        false
-    }
-
-    /// Get all directory entries from a given block
-    fn get_all_entries_from_block(block: &Block, entries: &mut Vec<DirEntry>) {
-        let mut offset = 0;
-        while offset < BLOCK_SIZE {
-            let de: DirEntry = block.read_offset_as(offset);
-            offset += de.rec_len() as usize;
-            if !de.unused() {
-                trace!("Dir entry: {:?} {}", de.name(), de.inode());
-                entries.push(de);
-            }
-        }
-    }
-
-    /// 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,
-        dir: &InodeRef,
-        child: &InodeRef,
-        name: &str,
-        dst_blk: &mut Block,
-    ) {
-        // Set the entry
-        let rec_len = BLOCK_SIZE - size_of::<DirEntryTail>();
-        let new_entry = DirEntry::new(child.id, rec_len as u16, name, child.inode.file_type());
-        // Write entry to block
-        dst_blk.write_offset_as(0, &new_entry);
-
-        // Set tail
-        let mut tail = DirEntryTail::new();
-        tail.set_csum(
-            &self.read_super_block().uuid(),
-            dir.id,
-            dir.inode.generation(),
-            &dst_blk,
-        );
-        // Copy tail to block
-        let tail_offset = BLOCK_SIZE - size_of::<DirEntryTail>();
-        dst_blk.write_offset_as(tail_offset, &tail);
-
-        // Sync block to disk
-        self.write_block(&dst_blk);
-    }
-
-    /// Try insert a directory entry of child inode into a parent block.
-    /// Return true if the entry is successfully inserted.
-    fn insert_entry_to_old_block(
-        &self,
-        dir: &InodeRef,
-        child: &InodeRef,
-        name: &str,
-        dst_blk: &mut Block,
-    ) -> bool {
-        let required_size = DirEntry::required_size(name.len());
-        let mut offset = 0;
-
-        while offset < dst_blk.data.len() {
-            let mut de: DirEntry = dst_blk.read_offset_as(offset);
-            let rec_len = de.rec_len() as usize;
-
-            // Try splitting dir entry
-            // The size that `de` actually uses
-            let used_size = de.used_size();
-            // The rest size
-            let free_size = rec_len - used_size;
-
-            // Compare size
-            if free_size < required_size {
-                // No enough space, try next dir ent
-                offset = offset + rec_len;
-                continue;
-            }
-            // Has enough space
-            // Update the old entry
-            de.set_rec_len(used_size as u16);
-            dst_blk.write_offset_as(offset, &de);
-            // Insert the new entry
-            let new_entry =
-                DirEntry::new(child.id, free_size as u16, name, child.inode.file_type());
-            dst_blk.write_offset_as(offset + used_size, &new_entry);
-
-            // Set tail csum
-            let tail_offset = BLOCK_SIZE - size_of::<DirEntryTail>();
-            let mut tail = dst_blk.read_offset_as::<DirEntryTail>(tail_offset);
-            tail.set_csum(
-                &self.read_super_block().uuid(),
-                dir.id,
-                dir.inode.generation(),
-                &dst_blk,
-            );
-            // Write tail to blk_data
-            dst_blk.write_offset_as(tail_offset, &tail);
-
-            // Sync to disk
-            self.write_block(&dst_blk);
-            return true;
-        }
-        false
-    }
 }

+ 2 - 2
src/ext4/high_level.rs

@@ -84,7 +84,7 @@ impl Ext4 {
             match self.dir_find_entry(&cur, &path) {
                 Ok(de) => {
                     // If the object exists, check the type
-                    cur = self.read_inode(de.inode());
+                    cur = self.read_inode(de);
                 }
                 Err(e) => {
                     if e.code() != ErrCode::ENOENT {
@@ -127,7 +127,7 @@ impl Ext4 {
         let mut child = self.read_inode(child_id);
         if child.inode.is_dir() {
             // Check if the directory is empty
-            if self.dir_get_all_entries(&child).len() > 2 {
+            if self.dir_list_entries(&child).len() > 2 {
                 return_error!(ErrCode::ENOTEMPTY, "Directory {} not empty", path);
             }
         }

+ 6 - 6
src/ext4/low_level.rs

@@ -305,7 +305,7 @@ impl Ext4 {
             return_error!(ErrCode::ENOTDIR, "Inode {} is not a directory", parent.id);
         }
         // Cannot unlink directory
-        let child_id = self.dir_find_entry(&parent, name)?.inode();
+        let child_id = self.dir_find_entry(&parent, name)?;
         let mut child = self.read_inode(child_id);
         if child.inode.is_dir() {
             return_error!(ErrCode::EISDIR, "Cannot unlink a directory");
@@ -349,7 +349,7 @@ impl Ext4 {
             );
         }
         let child_entry = self.dir_find_entry(&parent, name)?;
-        let mut child = self.read_inode(child_entry.inode());
+        let mut child = self.read_inode(child_entry);
 
         self.unlink_inode(&mut parent, &mut child, name, false)?;
         self.link_inode(&mut new_parent, &mut child, new_name)
@@ -412,7 +412,7 @@ impl Ext4 {
             return_error!(ErrCode::ENOTDIR, "Inode {} is not a directory", parent.id);
         }
         self.dir_find_entry(&parent, name)
-            .map(|entry| entry.inode())
+            .map(|entry| entry)
     }
 
     /// List all directory entries in a directory.
@@ -434,7 +434,7 @@ impl Ext4 {
         if inode_ref.inode.file_type() != FileType::Directory {
             return_error!(ErrCode::ENOTDIR, "Inode {} is not a directory", inode);
         }
-        Ok(self.dir_get_all_entries(&inode_ref))
+        Ok(self.dir_list_entries(&inode_ref))
     }
 
     /// Remove an empty directory.
@@ -455,13 +455,13 @@ impl Ext4 {
         if !parent.inode.is_dir() {
             return_error!(ErrCode::ENOTDIR, "Inode {} is not a directory", parent.id);
         }
-        let mut child = self.read_inode(self.dir_find_entry(&parent, name)?.inode());
+        let mut child = self.read_inode(self.dir_find_entry(&parent, name)?);
         // Child must be a directory
         if !child.inode.is_dir() {
             return_error!(ErrCode::ENOTDIR, "Inode {} is not a directory", child.id);
         }
         // Child must be empty
-        if self.dir_get_all_entries(&child).len() > 2 {
+        if self.dir_list_entries(&child).len() > 2 {
             return_error!(ErrCode::ENOTEMPTY, "Directory {} is not empty", child.id);
         }
         // Remove directory entry

+ 266 - 0
src/ext4_defs/dir.rs

@@ -0,0 +1,266 @@
+//! A directory is a series of data blocks and that each block contains a
+//! linear array of directory entries.
+
+use super::crc::*;
+use super::AsBytes;
+use super::FileType;
+use crate::constants::*;
+use crate::format_error;
+use crate::prelude::*;
+use crate::Block;
+
+/// Directory entry.
+#[repr(C)]
+#[derive(Debug, Clone)]
+pub struct DirEntry {
+    /// Number of the inode that this directory entry points to.
+    inode: InodeId,
+    /// Distance to the next directory entry.
+    rec_len: u16,
+    /// Length of the file name.
+    name_len: u8,
+    /// File type code.
+    file_type: FileType,
+    /// File name.
+    name: [u8; 255],
+}
+
+/// Fake dir entry. A normal entry without `name` field
+#[repr(C)]
+pub struct FakeDirEntry {
+    inode: u32,
+    rec_len: u16,
+    name_len: u8,
+    file_type: FileType,
+}
+unsafe impl AsBytes for FakeDirEntry {}
+
+/// The actual size of the directory entry is determined by `name_len`.
+/// So we need to implement `AsBytes` methods specifically for `DirEntry`.
+unsafe impl AsBytes for DirEntry {
+    fn from_bytes(bytes: &[u8]) -> Self {
+        let fake_entry = FakeDirEntry::from_bytes(bytes);
+        let mut entry = DirEntry {
+            inode: fake_entry.inode,
+            rec_len: fake_entry.rec_len,
+            name_len: fake_entry.name_len,
+            file_type: fake_entry.file_type,
+            name: [0; 255],
+        };
+        let name_len = entry.name_len as usize;
+        let name_offset = size_of::<FakeDirEntry>();
+        entry.name[..name_len].copy_from_slice(&bytes[name_offset..name_offset + name_len]);
+        entry
+    }
+    fn to_bytes(&self) -> &[u8] {
+        let name_len = self.name_len as usize;
+        unsafe {
+            core::slice::from_raw_parts(
+                self as *const Self as *const u8,
+                size_of::<FakeDirEntry>() + name_len,
+            )
+        }
+    }
+}
+
+impl DirEntry {
+    /// Create a new directory entry
+    pub fn new(inode: InodeId, rec_len: u16, name: &str, file_type: FileType) -> Self {
+        let mut name_bytes = [0u8; 255];
+        let name_len = name.as_bytes().len();
+        name_bytes[..name_len].copy_from_slice(name.as_bytes());
+        Self {
+            inode,
+            rec_len,
+            name_len: name_len as u8,
+            file_type,
+            name: name_bytes,
+        }
+    }
+
+    /// Get the inode number the directory entry points to
+    pub fn inode(&self) -> InodeId {
+        self.inode
+    }
+
+    /// Get the name of the directory entry
+    pub fn name(&self) -> Result<String> {
+        let name_len = self.name_len as usize;
+        let name = &self.name[..name_len];
+        String::from_utf8(name.to_vec()).map_err(|_| {
+            format_error!(
+                ErrCode::EINVAL,
+                "Invalid UTF-8 sequence in directory entry name"
+            )
+        })
+    }
+
+    /// Compare the name of the directory entry with a given name
+    pub fn compare_name(&self, name: &str) -> bool {
+        &self.name[..name.len()] == name.as_bytes()
+    }
+
+    /// Check if the directory entry is unused (inode = 0)
+    pub fn unused(&self) -> bool {
+        self.inode == 0
+    }
+
+    /// Set a directory entry as unused
+    pub fn set_unused(&mut self) {
+        self.inode = 0
+    }
+
+    /// Set the dir entry's file type
+    pub fn set_type(&mut self, file_type: FileType) {
+        self.file_type = file_type;
+    }
+
+    /// Get the required size to save a 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::<FakeDirEntry>() + 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)
+    }
+}
+
+#[derive(Debug, Clone, Copy)]
+#[repr(C)]
+pub struct DirEntryTail {
+    reserved_zero1: u32,
+    rec_len: u16,
+    reserved_zero2: u8,
+    reserved_ft: u8,
+    checksum: u32, // crc32c(uuid+inum+dirblock)
+}
+unsafe impl AsBytes for DirEntryTail {}
+
+impl DirEntryTail {
+    pub fn new() -> Self {
+        Self {
+            reserved_zero1: 0,
+            rec_len: 12,
+            reserved_zero2: 0,
+            reserved_ft: 0xDE,
+            checksum: 0,
+        }
+    }
+
+    pub fn set_checksum(&mut self, uuid: &[u8], ino: InodeId, ino_gen: u32, block: &Block) {
+        let mut csum = crc32(CRC32_INIT, &uuid);
+        csum = crc32(csum, &ino.to_le_bytes());
+        csum = crc32(csum, &ino_gen.to_le_bytes());
+        self.checksum = crc32(csum, &block.data[..size_of::<DirEntryTail>()]);
+    }
+}
+
+/// The block that stores an array of `DirEntry`.
+pub struct DirBlock(Block);
+
+impl DirBlock {
+    /// Wrap a data block to a directory block.
+    pub fn new(block: Block) -> Self {
+        DirBlock(block)
+    }
+
+    /// Get the wrapped block.
+    pub fn block(&self) -> &Block {
+        &self.0
+    }
+
+    /// Initialize a directory block, create an unused entry
+    /// and the dir entry tail.
+    pub fn init(&mut self) {
+        let tail_offset = BLOCK_SIZE - size_of::<DirEntryTail>();
+        let entry = DirEntry::new(0, tail_offset as u16, "", FileType::Unknown);
+        self.0.write_offset_as(0, &entry);
+        let tail = DirEntryTail::new();
+        self.0.write_offset_as(tail_offset, &tail);
+    }
+
+    /// Get a directory entry by name, return the inode id of the entry.
+    pub fn get(&self, name: &str) -> Option<InodeId> {
+        let mut offset = 0;
+        while offset < BLOCK_SIZE {
+            let de: DirEntry = self.0.read_offset_as(offset);
+            if !de.unused() && de.compare_name(name) {
+                return Some(de.inode);
+            }
+            offset += de.rec_len as usize;
+        }
+        None
+    }
+
+    /// Get all directory entries in the block.
+    pub fn list(&self, entries: &mut Vec<DirEntry>) {
+        let mut offset = 0;
+        while offset < BLOCK_SIZE {
+            let de: DirEntry = self.0.read_offset_as(offset);
+            offset += de.rec_len as usize;
+            if !de.unused() {
+                trace!("Dir entry: {:?} {}", de.name(), de.inode);
+                entries.push(de);
+            }
+        }
+    }
+
+    /// Insert a directory entry to the block. Return true if success or false
+    /// if the block doesn't have enough space.
+    pub fn insert(&mut self, name: &str, inode: InodeId, file_type: FileType) -> bool {
+        let required_size = DirEntry::required_size(name.len());
+        let mut offset = 0;
+        while offset < BLOCK_SIZE {
+            // Read a dir entry
+            let mut de: DirEntry = self.0.read_offset_as(offset);
+            let rec_len = de.rec_len as usize;
+            // The size that `de` actually uses
+            let used_size = de.used_size();
+            // The rest size
+            let free_size = rec_len - used_size;
+            // Try splitting dir entry
+            // Compare size
+            if free_size < required_size {
+                // No enough space, try next dir ent
+                offset = offset + rec_len;
+                continue;
+            }
+            // Has enough space
+            // Update the old entry
+            de.rec_len = used_size as u16;
+            self.0.write_offset_as(offset, &de);
+            // Insert the new entry
+            let new_entry = DirEntry::new(inode, free_size as u16, name, file_type);
+            self.0.write_offset_as(offset + used_size, &new_entry);
+            return true;
+        }
+        false
+    }
+
+    /// Remove a directory entry from the block. Return true if success or false
+    /// if the entry doesn't exist.
+    pub fn remove(&mut self, name: &str) -> bool {
+        let mut offset = 0;
+        while offset < BLOCK_SIZE {
+            let mut de: DirEntry = self.0.read_offset_as(offset);
+            if !de.unused() && de.compare_name(name) {
+                // Mark the target entry as unused
+                de.set_unused();
+                self.0.write_offset_as(offset, &de);
+                return true;
+            }
+            offset += de.rec_len as usize;
+        }
+        false
+    }
+
+    /// Calc and set block checksum
+    pub fn set_checksum(&mut self, uuid: &[u8], ino: InodeId, ino_gen: u32) {
+        let tail_offset = BLOCK_SIZE - size_of::<DirEntryTail>();
+        let mut tail: DirEntryTail = self.0.read_offset_as(tail_offset);
+        tail.set_checksum(uuid, ino, ino_gen, &self.0);
+        self.0.write_offset_as(tail_offset, &tail);
+    }
+}

+ 0 - 199
src/ext4_defs/dir_entry.rs

@@ -1,199 +0,0 @@
-//! A directory is a series of data blocks and that each block contains a
-//! linear array of directory entries.
-
-use super::crc::*;
-use super::AsBytes;
-use super::FileType;
-use crate::constants::*;
-use crate::format_error;
-use crate::prelude::*;
-use crate::Block;
-
-#[repr(C)]
-#[derive(Clone, Copy)]
-pub union DirEnInner {
-    name_length_high: u8, // 高8位的文件名长度
-    inode_type: FileType, // 引用的inode的类型(在rev >= 0.5中)
-}
-
-impl Debug for DirEnInner {
-    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
-        unsafe { write!(f, "inode_type: {:?}", self.inode_type) }
-    }
-}
-
-impl Default for DirEnInner {
-    fn default() -> Self {
-        Self {
-            name_length_high: 0,
-        }
-    }
-}
-
-/// Directory entry.
-#[repr(C)]
-#[derive(Debug, Clone)]
-pub struct DirEntry {
-    /// 该目录项指向的inode的编号
-    inode: InodeId,
-    /// 到下一个目录项的距离
-    rec_len: u16,
-    /// 低8位的文件名长度
-    name_len: u8,
-    /// 联合体成员
-    inner: DirEnInner,
-    /// 文件名
-    name: [u8; 255],
-}
-
-/// Fake dir entry. A normal entry without `name` field
-#[repr(C)]
-pub struct FakeDirEntry {
-    inode: u32,
-    rec_len: u16,
-    name_len: u8,
-    inode_type: FileType,
-}
-unsafe impl AsBytes for FakeDirEntry {}
-
-/// The actual size of the directory entry is determined by `name_len`.
-/// So we need to implement `AsBytes` methods specifically for `DirEntry`.
-unsafe impl AsBytes for DirEntry {
-    fn from_bytes(bytes: &[u8]) -> Self {
-        let fake_entry = FakeDirEntry::from_bytes(bytes);
-        let mut entry = DirEntry {
-            inode: fake_entry.inode,
-            rec_len: fake_entry.rec_len,
-            name_len: fake_entry.name_len,
-            inner: DirEnInner {
-                inode_type: fake_entry.inode_type,
-            },
-            name: [0; 255],
-        };
-        let name_len = entry.name_len as usize;
-        let name_offset = size_of::<FakeDirEntry>();
-        entry.name[..name_len].copy_from_slice(&bytes[name_offset..name_offset + name_len]);
-        entry
-    }
-    fn to_bytes(&self) -> &[u8] {
-        let name_len = self.name_len as usize;
-        unsafe {
-            core::slice::from_raw_parts(
-                self as *const Self as *const u8,
-                size_of::<FakeDirEntry>() + name_len,
-            )
-        }
-    }
-}
-
-impl DirEntry {
-    /// Create a new directory entry
-    pub fn new(inode: InodeId, rec_len: u16, name: &str, dirent_type: FileType) -> Self {
-        let mut name_bytes = [0u8; 255];
-        let name_len = name.as_bytes().len();
-        name_bytes[..name_len].copy_from_slice(name.as_bytes());
-        Self {
-            inode,
-            rec_len,
-            name_len: name_len as u8,
-            inner: DirEnInner {
-                inode_type: dirent_type,
-            },
-            name: name_bytes,
-        }
-    }
-
-    pub fn name(&self) -> Result<String> {
-        let name_len = self.name_len as usize;
-        let name = &self.name[..name_len];
-        String::from_utf8(name.to_vec()).map_err(|_| {
-            format_error!(
-                ErrCode::EINVAL,
-                "Invalid UTF-8 sequence in directory entry name"
-            )
-        })
-    }
-
-    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());
-    }
-
-    /// Distance to the next directory entry
-    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) -> InodeId {
-        self.inode
-    }
-
-    pub fn set_inode(&mut self, inode: InodeId) {
-        self.inode = inode;
-    }
-
-    /// Unused directory entries are signified by inode = 0
-    pub fn unused(&self) -> bool {
-        self.inode == 0
-    }
-
-    /// Set a directory entry as unused
-    pub fn set_unused(&mut self) {
-        self.inode = 0
-    }
-
-    /// Set the dir entry's file type
-    pub fn set_type(&mut self, file_type: FileType) {
-        self.inner.inode_type = file_type;
-    }
-
-    /// Get the required size to save a 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::<FakeDirEntry>() + 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)
-    }
-}
-
-#[derive(Debug, Clone, Copy)]
-#[repr(C)]
-pub struct DirEntryTail {
-    reserved_zero1: u32,
-    rec_len: u16,
-    reserved_zero2: u8,
-    reserved_ft: u8,
-    checksum: u32, // crc32c(uuid+inum+dirblock)
-}
-
-unsafe impl AsBytes for DirEntryTail {}
-
-impl DirEntryTail {
-    pub fn new() -> Self {
-        Self {
-            reserved_zero1: 0,
-            rec_len: 12,
-            reserved_zero2: 0,
-            reserved_ft: 0xDE,
-            checksum: 0,
-        }
-    }
-
-    pub fn set_csum(&mut self, uuid: &[u8], ino: InodeId, ino_gen: u32, block: &Block) {
-        let mut csum = crc32(CRC32_INIT, &uuid);
-        csum = crc32(csum, &ino.to_le_bytes());
-        csum = crc32(csum, &ino_gen.to_le_bytes());
-        self.checksum = crc32(csum, &block.data[..size_of::<DirEntryTail>()]);
-    }
-}

+ 2 - 2
src/ext4_defs/mod.rs

@@ -18,7 +18,7 @@ mod bitmap;
 mod block_device;
 mod block_group;
 mod crc;
-mod dir_entry;
+mod dir;
 mod extent;
 mod file;
 mod inode;
@@ -29,7 +29,7 @@ mod xattr;
 pub use bitmap::*;
 pub use block_device::*;
 pub use block_group::*;
-pub use dir_entry::*;
+pub use dir::*;
 pub use extent::*;
 pub use file::*;
 pub use inode::*;

+ 8 - 4
src/ext4_defs/xattr.rs

@@ -153,19 +153,23 @@ impl XattrEntry {
 pub struct XattrBlock(Block);
 
 impl XattrBlock {
+    /// Wrap a data block as `XattrBlock`.
     pub fn new(block: Block) -> Self {
         XattrBlock(block)
     }
 
+    /// Get the wrapped block.
+    pub fn block(self) -> Block {
+        self.0
+    }
+
+    /// Initialize a xattr block, write a `XattrHeader` to the
+    /// beginning of the block.
     pub fn init(&mut self) {
         let header = XattrHeader::new();
         self.0.write_offset_as(0, &header);
     }
 
-    pub fn block(self) -> Block {
-        self.0
-    }
-
     /// Get a xattr by name, return the value.
     pub fn get(&self, name: &str) -> Option<&[u8]> {
         let mut entry_start = size_of::<XattrHeader>();