Browse Source

feat: add extent node struct

liujingx 11 tháng trước cách đây
mục cha
commit
4bf0e13296
9 tập tin đã thay đổi với 294 bổ sung210 xóa
  1. 8 21
      src/constants.rs
  2. 33 30
      src/ext4/alloc.rs
  3. 17 8
      src/ext4/dir.rs
  4. 37 26
      src/ext4/extent.rs
  5. 4 4
      src/ext4/file.rs
  6. 5 26
      src/ext4/mod.rs
  7. 4 6
      src/ext4_defs/dir_entry.rs
  8. 168 74
      src/ext4_defs/extent.rs
  9. 18 15
      src/ext4_defs/inode.rs

+ 8 - 21
src/constants.rs

@@ -3,7 +3,7 @@
 use crate::prelude::*;
 use bitflags::bitflags;
 
-pub const EXT4_INODE_FLAG_EXTENTS: usize = 0x00080000; /* Inode uses extents */
+pub const EXT4_INODE_FLAG_EXTENTS: u32 = 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;
@@ -13,13 +13,13 @@ 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_FIFO: u16 = 0x1000;
+pub const EXT4_INODE_MODE_CHARDEV: u16 = 0x2000;
+pub const EXT4_INODE_MODE_DIRECTORY: u16 = 0x4000;
+pub const EXT4_INODE_MODE_BLOCKDEV: u16 = 0x6000;
+pub const EXT4_INODE_MODE_FILE: u16 = 0x8000;
+pub const EXT4_INODE_MODE_SOFTLINK: u16 = 0xA000;
+pub const EXT4_INODE_MODE_SOCKET: u16 = 0xC000;
 pub const EXT4_INODE_MODE_TYPE_MASK: u16 = 0xF000;
 
 pub const EXT_MAX_BLOCKS: LBlockId = core::u32::MAX;
@@ -74,18 +74,5 @@ 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! {
-    #[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;

+ 33 - 30
src/ext4/alloc.rs

@@ -6,8 +6,8 @@ use crate::prelude::*;
 impl Ext4 {
     /// Allocate a new data block for an inode, return the physical block number
     pub(super) fn alloc_block(&mut self, inode_ref: &mut Ext4InodeRef, goal: PBlockId) -> PBlockId {
-        let bgid = goal / self.blocks_per_group as u64;
-        let idx_in_bg = goal % self.blocks_per_group as u64;
+        let bgid = goal / self.super_block.blocks_per_group() as u64;
+        let idx_in_bg = goal % self.super_block.blocks_per_group() as u64;
 
         // Load block group descriptor
         let mut bg =
@@ -44,11 +44,16 @@ impl Ext4 {
 
         bg.sync_to_disk_with_csum(self.block_device.clone(), bgid as usize, &self.super_block);
 
+        info!("Alloc block {}", fblock.unwrap());
+
         fblock.unwrap() as PBlockId
     }
 
     /// Append a data block for an inode, return a pair of (logical block id, physical block id)
-    pub(super) fn inode_append_block(&mut self, inode_ref: &mut Ext4InodeRef) -> (LBlockId, PBlockId) {
+    pub(super) fn inode_append_block(
+        &mut self,
+        inode_ref: &mut Ext4InodeRef,
+    ) -> (LBlockId, PBlockId) {
         let inode_size = inode_ref.inode.size();
         // The new logical block id
         let iblock = ((inode_size + BLOCK_SIZE as u64 - 1) / BLOCK_SIZE as u64) as u32;
@@ -60,33 +65,28 @@ impl Ext4 {
         (iblock, fblock)
     }
 
-    pub(super) fn ext4_fs_inode_blocks_init(inode_ref: &mut Ext4InodeRef) {
-        let inode = &mut inode_ref.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;
-            }
+    /// Allocate(initialize) the root inode of the file system
+    pub(super) fn alloc_root_inode(&mut self) -> Ext4InodeRef {
+        let mut inode = Ext4Inode::default();
+        inode.set_mode(0o777 | EXT4_INODE_MODE_DIRECTORY);
+        inode.extent_init();
+        if self.super_block.inode_size() > EXT4_GOOD_OLD_INODE_SIZE {
+            inode.set_extra_isize(self.super_block.extra_size());
         }
+        let mut root = Ext4InodeRef::new(EXT4_ROOT_INO, inode);
+        let root_self = root.clone();
+        
+        // Add `.` and `..` entries
+        self.dir_add_entry(&mut root, &root_self, ".");
+        self.dir_add_entry(&mut root, &root_self, "..");
+        root.inode.links_count += 2;
 
-        /* Initialize extents */
-        inode.set_flags(EXT4_INODE_FLAG_EXTENTS as u32);
-
-        /* Initialize extent root header */
-        inode.extent_tree_init();
-        // log::info!("inode iblock {:x?}", inode.block);
+        self.write_back_inode_with_csum(&mut root);
 
-        // inode_ref.dirty = true;
+        root
     }
 
-    /// Allocate a new inode in the filesystem, returning the inode and its number
+    /// Allocate a new inode in the file system, returning the inode and its number
     pub(super) fn alloc_inode(&mut self, filetype: FileType) -> Ext4InodeRef {
         // Allocate an inode
         let is_dir = filetype == FileType::Directory;
@@ -95,13 +95,14 @@ impl Ext4 {
         // Initialize the inode
         let mut inode = Ext4Inode::default();
         let mode = if filetype == FileType::Directory {
-            0o777 | EXT4_INODE_MODE_DIRECTORY as u16
+            0o777 | EXT4_INODE_MODE_DIRECTORY
         } else if filetype == FileType::SymLink {
-            0o777 | EXT4_INODE_MODE_SOFTLINK as u16
+            0o777 | EXT4_INODE_MODE_SOFTLINK
         } else {
-            0o666 | file_type2inode_mode(filetype) as u16
+            0o666 | file_type2inode_mode(filetype)
         };
         inode.set_mode(mode);
+        inode.extent_init();
         if self.super_block.inode_size() > EXT4_GOOD_OLD_INODE_SIZE {
             inode.set_extra_isize(self.super_block.extra_size());
         }
@@ -110,12 +111,14 @@ impl Ext4 {
         // Sync the inode to disk
         self.write_back_inode_with_csum(&mut inode_ref);
 
+        info!("Alloc inode {}", inode_ref.inode_id);
+
         inode_ref
     }
 
     /// Allocate a new inode in the filesystem, returning its number.
     fn do_alloc_inode(&mut self, is_dir: bool) -> u32 {
-        let mut bgid = self.last_inode_bg_id;
+        let mut bgid = 0;
         let bg_count = self.super_block.block_groups_count();
 
         while bgid <= bg_count {
@@ -179,7 +182,7 @@ impl Ext4 {
             // Compute the absolute i-node number
             let inodes_per_group = self.super_block.inodes_per_group();
             let inode_num = bgid * inodes_per_group + (idx_in_bg + 1);
-            log::info!("alloc inode {:x?}", inode_num);
+            log::info!("alloc inode {}", inode_num);
 
             return inode_num;
         }

+ 17 - 8
src/ext4/dir.rs

@@ -5,7 +5,8 @@ use crate::prelude::*;
 
 impl Ext4 {
     /// Find a directory entry that matches a given name under a parent directory
-    pub(super) fn dir_find_entry(&self, parent: &mut Ext4InodeRef, name: &str) -> Result<Ext4DirEntry> {
+    pub(super) fn dir_find_entry(&self, parent: &Ext4InodeRef, name: &str) -> Result<Ext4DirEntry> {
+        info!("dir find entry: {} under parent {}", name, parent.inode_id);
         let inode_size: u32 = parent.inode.size;
         let total_blocks: u32 = inode_size / BLOCK_SIZE as u32;
         let mut iblock: LBlockId = 0;
@@ -14,9 +15,9 @@ impl Ext4 {
             // Get the fs block id
             let fblock = self.extent_get_pblock(parent, iblock);
             // Load block from disk
-            let mut block_data = self.block_device.read_offset(fblock as usize * BLOCK_SIZE);
+            let block_data = self.block_device.read_offset(fblock as usize * BLOCK_SIZE);
             // Find the entry in block
-            let res = Self::find_entry_in_block(&mut block_data, name);
+            let res = Self::find_entry_in_block(&block_data, name);
             if let Ok(r) = res {
                 return Ok(r);
             }
@@ -32,6 +33,7 @@ impl Ext4 {
         let mut offset = 0;
         while offset < block_data.len() {
             let de = Ext4DirEntry::try_from(&block_data[offset..]).unwrap();
+            debug!("de {:?}", de.rec_len());
             offset += de.rec_len() as usize;
             // Unused dir entry
             if de.unused() {
@@ -52,15 +54,18 @@ impl Ext4 {
         child: &Ext4InodeRef,
         path: &str,
     ) -> usize {
-        let block_size = self.super_block.block_size();
+        info!(
+            "Adding entry: parent {}, child {}, path {}",
+            parent.inode_id, child.inode_id, path
+        );
         let inode_size = parent.inode.size();
-        let total_blocks = inode_size as u32 / block_size;
+        let total_blocks = inode_size as u32 / BLOCK_SIZE as u32;
 
         // Try finding a block with enough space
         let mut iblock: LBlockId = 0;
         while iblock < total_blocks {
-            // Get the parent physical block id
-            let fblock = self.extent_get_pblock(parent, iblock);
+            // Get the parent physical block id, create if not exist
+            let fblock = self.extent_get_pblock_create(parent, iblock, 1);
             // Load the parent block from disk
             let mut data = self.block_device.read_offset(fblock as usize * BLOCK_SIZE);
             let mut ext4_block = Ext4Block {
@@ -69,6 +74,7 @@ impl Ext4 {
                 block_data: &mut data,
                 dirty: false,
             };
+            debug!("Insert dirent to old block {}", fblock);
             // Try inserting the entry to parent block
             let r = self.insert_entry_to_old_block(&mut ext4_block, child, path);
             if r == EOK {
@@ -90,6 +96,7 @@ impl Ext4 {
             block_data: &mut data,
             dirty: false,
         };
+        debug!("Insert dirent to new block {}", fblock);
         // Write the entry to block
         self.insert_entry_to_new_block(&mut new_block, child, path);
 
@@ -198,6 +205,8 @@ impl Ext4 {
             FileType::Directory,
             &self.get_root_inode_ref(),
         )
-        .map(|_| ())
+        .map(|_| {
+            info!("ext4_dir_mk: {}", path);
+        })
     }
 }

+ 37 - 26
src/ext4/extent.rs

@@ -6,31 +6,34 @@ use core::cmp::min;
 
 impl Ext4 {
     /// Find the given logic block id in the extent tree, return the search path
-    fn find_extent(&self, inode_ref: &mut Ext4InodeRef, iblock: LBlockId) -> Vec<ExtentSearchPath> {
+    fn find_extent(&self, inode_ref: &Ext4InodeRef, iblock: LBlockId) -> Vec<ExtentSearchPath> {
         let mut path: Vec<ExtentSearchPath> = Vec::new();
-        let mut eh = inode_ref.inode.extent_header().clone();
+        let mut ex_node = inode_ref.inode.extent();
         let mut pblock = 0;
         let mut block_data: Vec<u8>;
+
         // Go until leaf
-        while eh.depth() > 0 {
-            let index = eh.extent_index_search(iblock);
+        while ex_node.header().depth() > 0 {
+            let index = ex_node.extent_index_search(iblock);
             if index.is_err() {
                 // TODO: no extent index
                 panic!("Unhandled error");
             }
             path.push(ExtentSearchPath::new_inner(pblock, index));
             // Get the target extent index
-            let ex_idx = eh.extent_index_at(index.unwrap());
+            let ex_idx = ex_node.extent_index_at(index.unwrap());
             // Load the next extent node
             let next = ex_idx.leaf();
             // Note: block data cannot be released until the next assigment
             block_data = self.block_device.read_offset(next as usize * BLOCK_SIZE);
             // Load the next extent header
-            eh = Ext4ExtentHeader::load_from_block(&block_data);
+            ex_node = ExtentNode::from_bytes(&block_data);
             pblock = next;
         }
+
         // Leaf
-        let index = eh.extent_search(iblock);
+        let index = ex_node.extent_search(iblock);
+        debug!("Extent search {} res {:?}", iblock, index);
         path.push(ExtentSearchPath::new_leaf(pblock, index));
 
         path
@@ -38,25 +41,25 @@ impl Ext4 {
 
     /// Given a logic block id, find the corresponding fs block id.
     /// Return 0 if not found.
-    pub(super) fn extent_get_pblock(&self, inode_ref: &mut Ext4InodeRef, iblock: LBlockId) -> PBlockId {
+    pub(super) fn extent_get_pblock(&self, inode_ref: &Ext4InodeRef, iblock: LBlockId) -> PBlockId {
         let path = self.find_extent(inode_ref, iblock);
         // Leaf is the last element of the path
         let leaf = path.last().unwrap();
         if let Ok(index) = leaf.index {
             // Note: block data must be defined here to keep it alive
             let block_data: Vec<u8>;
-            let eh = if leaf.pblock != 0 {
+            let ex_node = if leaf.pblock != 0 {
                 // Load the extent node
                 block_data = self
                     .block_device
                     .read_offset(leaf.pblock as usize * BLOCK_SIZE);
                 // Load the next extent header
-                Ext4ExtentHeader::load_from_block(&block_data)
+                ExtentNode::from_bytes(&block_data)
             } else {
                 // Root node
-                inode_ref.inode.extent_header().clone()
+                inode_ref.inode.extent()
             };
-            let ex = eh.extent_at(index);
+            let ex = ex_node.extent_at(index);
             ex.start_pblock() + (iblock - ex.start_lblock()) as PBlockId
         } else {
             0
@@ -75,22 +78,22 @@ impl Ext4 {
         // Leaf is the last element of the path
         let leaf = path.last().unwrap();
         // Note: block data must be defined here to keep it alive
-        let block_data: Vec<u8>;
-        let eh = if leaf.pblock != 0 {
+        let mut block_data: Vec<u8>;
+        let ex_node = if leaf.pblock != 0 {
             // Load the extent node
             block_data = self
                 .block_device
                 .read_offset(leaf.pblock as usize * BLOCK_SIZE);
             // Load the next extent header
-            Ext4ExtentHeader::load_from_block(&block_data)
+            ExtentNodeMut::from_bytes(&mut block_data)
         } else {
             // Root node
-            inode_ref.inode.extent_header().clone()
+            inode_ref.inode.extent_mut()
         };
         match leaf.index {
             Ok(index) => {
                 // Found, return the corresponding fs block id
-                let ex = eh.extent_at(index);
+                let ex = ex_node.extent_at(index);
                 ex.start_pblock() + (iblock - ex.start_lblock()) as PBlockId
             }
             Err(_) => {
@@ -117,40 +120,48 @@ impl Ext4 {
     ) -> bool {
         // Note: block data must be defined here to keep it alive
         let mut block_data = Vec::<u8>::new();
-        let mut eh = if leaf.pblock != 0 {
+        let mut ex_node = if leaf.pblock != 0 {
             // Load the extent node
             block_data = self
                 .block_device
                 .read_offset(leaf.pblock as usize * BLOCK_SIZE);
             // Load the next extent header
-            Ext4ExtentHeader::load_from_block(&block_data)
+            ExtentNodeMut::from_bytes(&mut block_data)
         } else {
             // Root node
-            inode_ref.inode.extent_header().clone()
+            inode_ref.inode.extent_mut()
         };
         // The position where the new extent should be inserted
         let index = leaf.index.unwrap_err();
-        let targ_ext = eh.extent_mut_at(index);
+        let targ_ext = ex_node.extent_mut_at(index);
         let split: bool;
 
+        debug!("Create extent");
         if targ_ext.is_uninit() {
             // 1. The position has an uninitialized extent
             *targ_ext = new_ext.clone();
+            
+            let en_count = ex_node.header().entries_count() + 1;
+            ex_node.header_mut().set_entries_count(en_count);
             split = false;
         } else {
             // 2. The position has a valid extent
             // Insert the extent and move the following extents
             let mut i = index;
-            while i < eh.entries_count() as usize {
-                *eh.extent_mut_at(i + 1) = *eh.extent_at(i);
+            while i < ex_node.header().entries_count() as usize {
+                *ex_node.extent_mut_at(i + 1) = *ex_node.extent_at(i);
                 i += 1;
             }
-            *eh.extent_mut_at(index) = new_ext.clone();
-            eh.set_entries_count(eh.entries_count() + 1);
+            *ex_node.extent_mut_at(index) = new_ext.clone();
+            
+            let en_count = ex_node.header().entries_count() + 1;
+            ex_node.header_mut().set_entries_count(en_count);
             // Check if the extent node is full
-            split = eh.entries_count() >= eh.max_entries_count();
+            split = ex_node.header().entries_count() >= ex_node.header().max_entries_count();
         }
 
+        ex_node.print();
+        // Write back to disk
         if !block_data.is_empty() {
             self.block_device
                 .write_offset(leaf.pblock as usize * BLOCK_SIZE, &block_data);

+ 4 - 4
src/ext4/file.rs

@@ -21,9 +21,11 @@ impl Ext4 {
         // Search from the given parent inode
         let mut parent = parent_inode.clone();
         let search_path = Self::split_path(path);
-
+        
+        info!("generic open: {}", path);
         for (i, path) in search_path.iter().enumerate() {
-            let res = self.dir_find_entry(&mut parent, path);
+            let res = self.dir_find_entry(&parent, path);
+            debug!("dir_find_entry: {:?}", res);
             match res {
                 Ok(entry) => {
                     parent = self.get_inode_ref(entry.inode());
@@ -42,7 +44,6 @@ impl Ext4 {
                     } else {
                         self.alloc_inode(FileType::Directory)
                     };
-                    Self::ext4_fs_inode_blocks_init(&mut child);
                     // Link the new inode
                     let r = self.ext4_link(&mut parent, &mut child, path);
                     if r != EOK {
@@ -55,7 +56,6 @@ impl Ext4 {
                 }
             }
         }
-
         // Reach the target
         let mut file = Ext4File::default();
         file.inode = parent.inode_id;

+ 5 - 26
src/ext4/mod.rs

@@ -13,11 +13,6 @@ mod link;
 pub struct Ext4 {
     pub block_device: Arc<dyn BlockDevice>,
     pub super_block: Ext4Superblock,
-    pub block_groups: Vec<Ext4BlockGroupDesc>,
-    pub inodes_per_group: u32,
-    pub blocks_per_group: u32,
-    pub inode_size: usize,
-    pub last_inode_bg_id: u32,
     pub mount_point: Ext4MountPoint,
 }
 
@@ -31,33 +26,17 @@ impl Ext4 {
         // 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();
-        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() as usize;
-
-        // Load the block groups description
-        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 =
-                Ext4BlockGroupDesc::load(block_device.clone(), &super_block, idx).unwrap();
-            block_groups.push(block_group);
-        }
-
         // Root mount point
         let mount_point = Ext4MountPoint::new("/");
-
         // Create Ext4 instance
-        Self {
+        let mut ext4 = Self {
             super_block,
-            inodes_per_group,
-            blocks_per_group,
-            inode_size,
-            block_groups,
             block_device,
             mount_point,
-            last_inode_bg_id: 0,
-        }
+        };
+        // Create root directory
+        ext4.alloc_root_inode();
+        ext4
     }
 
     /// Read an inode from block device, return an`Ext4InodeRef` that combines

+ 4 - 6
src/ext4_defs/dir_entry.rs

@@ -12,7 +12,7 @@ use alloc::string::FromUtf8Error;
 
 #[repr(C)]
 pub union Ext4DirEnInner {
-    pub name_length_high: u8,     // 高8位的文件名长度
+    pub name_length_high: u8, // 高8位的文件名长度
     pub inode_type: FileType, // 引用的inode的类型(在rev >= 0.5中)
 }
 
@@ -225,8 +225,7 @@ pub enum FileType {
 }
 
 pub fn inode_mode2file_type(inode_mode: u16) -> FileType {
-    let file_type = (inode_mode & EXT4_INODE_MODE_TYPE_MASK) as usize;
-    match file_type {
+    match inode_mode & EXT4_INODE_MODE_TYPE_MASK {
         EXT4_INODE_MODE_FILE => FileType::RegularFile,
         EXT4_INODE_MODE_DIRECTORY => FileType::Directory,
         EXT4_INODE_MODE_CHARDEV => FileType::CharacterDev,
@@ -239,7 +238,7 @@ pub fn inode_mode2file_type(inode_mode: u16) -> FileType {
 }
 
 pub fn file_type2inode_mode(dirent_type: FileType) -> u16 {
-    let inode_mode = match dirent_type {
+    match dirent_type {
         FileType::RegularFile => EXT4_INODE_MODE_FILE,
         FileType::Directory => EXT4_INODE_MODE_DIRECTORY,
         FileType::SymLink => EXT4_INODE_MODE_SOFTLINK,
@@ -248,6 +247,5 @@ pub fn file_type2inode_mode(dirent_type: FileType) -> u16 {
         FileType::Fifo => EXT4_INODE_MODE_FIFO,
         FileType::Socket => EXT4_INODE_MODE_SOCKET,
         _ => EXT4_INODE_MODE_FILE,
-    };
-    inode_mode as u16
+    }
 }

+ 168 - 74
src/ext4_defs/extent.rs

@@ -52,8 +52,8 @@ impl Ext4ExtentHeader {
     }
 
     /// Loads an extent header from a data block.
-    pub fn load_from_block(block_data: &[u8]) -> Self {
-        unsafe { *(block_data.as_ptr() as *const Ext4ExtentHeader) }
+    pub fn load_from_block(block_data: &[u8]) -> &Self {
+        unsafe { &*(block_data.as_ptr() as *const Ext4ExtentHeader) }
     }
 
     // 获取extent header的魔数
@@ -105,62 +105,6 @@ impl Ext4ExtentHeader {
     pub fn set_generation(&mut self, generation: u32) {
         self.generation = generation;
     }
-
-    pub fn extent_at(&self, index: usize) -> &'static Ext4Extent {
-        unsafe { &*((self as *const Self).add(1) as *const Ext4Extent).add(index) }
-    }
-
-    pub fn extent_mut_at(&mut self, index: usize) -> &'static mut Ext4Extent {
-        unsafe { &mut *((self as *mut Self).add(1) as *mut Ext4Extent).add(index) }
-    }
-
-    pub fn extent_index_at(&self, index: usize) -> &'static Ext4ExtentIndex {
-        unsafe { &*((self as *const Self).add(1) as *const Ext4ExtentIndex).add(index) }
-    }
-
-    pub fn extent_index_mut_at(&mut self, index: usize) -> &'static mut Ext4ExtentIndex {
-        unsafe { &mut *((self as *mut Self).add(1) as *mut Ext4ExtentIndex).add(index) }
-    }
-
-    /// Find the extent that covers the given logical block number.
-    ///
-    /// Return `Ok(index)` if found, and `eh.extent_at(index)` is the extent that covers
-    /// the given logical block number. Return `Err(index)` if not found, and `index` is the
-    /// position where the new extent should be inserted.
-    pub fn extent_search(&self, lblock: LBlockId) -> core::result::Result<usize, usize> {
-        let mut i = 0;
-        while i < self.entries_count as usize {
-            let extent = self.extent_at(i);
-            if extent.start_lblock() <= lblock {
-                if extent.start_lblock() + (extent.block_count() as LBlockId) < lblock {
-                    return if extent.is_uninit() { Err(i) } else { Ok(i) };
-                }
-                i += 1;
-            } else {
-                break;
-            }
-        }
-        Err(i)
-    }
-
-    /// Find the extent index that covers the given logical block number. The extent index
-    /// gives the next lower node to search.
-    ///
-    /// Return `Ok(index)` if found, and `eh.extent_index_at(index)` is the target extent index.
-    /// Return `Err(index)` if not found, and `index` is the position where the new extent index
-    /// should be inserted.
-    pub fn extent_index_search(&self, lblock: LBlockId) -> core::result::Result<usize, usize> {
-        let mut i = 0;
-        while i < self.entries_count as usize {
-            let extent_index = self.extent_index_at(i);
-            if extent_index.first_block <= lblock {
-                i += 1;
-            } else {
-                return Ok(i - 1);
-            }
-        }
-        Err(i)
-    }
 }
 
 #[derive(Debug, Default, Clone, Copy)]
@@ -180,14 +124,6 @@ pub struct Ext4ExtentIndex {
     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 _) })
-    }
-}
-
 impl Ext4ExtentIndex {
     /// The physical block number of the extent node that is the next level lower in the tree
     pub fn leaf(&self) -> PBlockId {
@@ -216,14 +152,6 @@ pub struct Ext4Extent {
     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 _) })
-    }
-}
-
 impl Ext4Extent {
     /// Create a new extent with start logic block number, start physical block number, and block count
     pub fn new(start_lblock: LBlockId, start_pblock: PBlockId, block_count: u16) -> Self {
@@ -300,6 +228,172 @@ impl Ext4Extent {
     }
 }
 
+/// Interpret an immutable byte slice as an extent node. Provide methods to
+/// access the extent header and the following extents or extent indices.
+///
+/// The underlying `raw_data` could be of `[u8;15]` (root node) or a
+/// data block `[u8;BLOCK_SIZE]` (other node).
+pub struct ExtentNode<'a> {
+    raw_data: &'a [u8],
+}
+
+impl<'a> ExtentNode<'a> {
+    /// Interpret a byte slice as an extent node
+    pub fn from_bytes(raw_data: &'a [u8]) -> Self {
+        Self { raw_data }
+    }
+
+    /// Get a immutable reference to the extent header
+    pub fn header(&self) -> &Ext4ExtentHeader {
+        unsafe { &*(self.raw_data.as_ptr() as *const Ext4ExtentHeader) }
+    }
+
+    /// Get a immutable reference to the extent at a given index
+    pub fn extent_at(&self, index: usize) -> &Ext4Extent {
+        unsafe {
+            &*((self.header() as *const Ext4ExtentHeader).add(1) as *const Ext4Extent).add(index)
+        }
+    }
+
+    /// Get a immmutable reference to the extent indexat a given index
+    pub fn extent_index_at(&self, index: usize) -> &Ext4ExtentIndex {
+        unsafe {
+            &*((self.header() as *const Ext4ExtentHeader).add(1) as *const Ext4ExtentIndex)
+                .add(index)
+        }
+    }
+
+    /// Find the extent that covers the given logical block number.
+    ///
+    /// Return `Ok(index)` if found, and `eh.extent_at(index)` is the extent that covers
+    /// the given logical block number. Return `Err(index)` if not found, and `index` is the
+    /// position where the new extent should be inserted.
+    pub fn extent_search(&self, lblock: LBlockId) -> core::result::Result<usize, usize> {
+        let mut i = 0;
+        while i < self.header().entries_count as usize {
+            let extent = self.extent_at(i);
+            if extent.start_lblock() <= lblock {
+                if extent.start_lblock() + (extent.block_count() as LBlockId) > lblock {
+                    return if extent.is_uninit() { Err(i) } else { Ok(i) };
+                }
+                i += 1;
+            } else {
+                break;
+            }
+        }
+        Err(i)
+    }
+
+    /// Find the extent index that covers the given logical block number. The extent index
+    /// gives the next lower node to search.
+    ///
+    /// Return `Ok(index)` if found, and `eh.extent_index_at(index)` is the target extent index.
+    /// Return `Err(index)` if not found, and `index` is the position where the new extent index
+    /// should be inserted.
+    pub fn extent_index_search(&self, lblock: LBlockId) -> core::result::Result<usize, usize> {
+        let mut i = 0;
+        self.print();
+        while i < self.header().entries_count as usize {
+            let extent_index = self.extent_index_at(i);
+            if extent_index.first_block <= lblock {
+                i += 1;
+            } else {
+                return Ok(i - 1);
+            }
+        }
+        Err(i)
+    }
+
+    pub fn print(&self) {
+        debug!("Extent header count {}", self.header().entries_count());
+        let mut i = 0;
+        while i < self.header().entries_count() as usize {
+            let ext = self.extent_at(i);
+            debug!(
+                "extent[{}] start_lblock={}, start_pblock={}, len={}",
+                i,
+                ext.start_lblock(),
+                ext.start_pblock(),
+                ext.block_count()
+            );
+            i += 1;
+        }
+    }
+}
+
+/// Interpret a mutable byte slice as an extent node. Provide methods to
+/// modify the extent header and the following extents or extent indices.
+///
+/// The underlying `raw_data` could be of `[u8;15]` (root node) or a
+/// data block `[u8;BLOCK_SIZE]` (other node).
+pub struct ExtentNodeMut<'a> {
+    raw_data: &'a mut [u8],
+}
+
+impl<'a> ExtentNodeMut<'a> {
+    /// Interpret a byte slice as an extent node
+    pub fn from_bytes(raw_data: &'a mut [u8]) -> Self {
+        Self { raw_data }
+    }
+
+    /// Get a immutable reference to the extent header
+    pub fn header(&self) -> &Ext4ExtentHeader {
+        unsafe { &*(self.raw_data.as_ptr() as *const Ext4ExtentHeader) }
+    }
+
+    /// Get a mutable reference to the extent header
+    pub fn header_mut(&mut self) -> &mut Ext4ExtentHeader {
+        unsafe { &mut *(self.raw_data.as_mut_ptr() as *mut Ext4ExtentHeader) }
+    }
+
+    /// Get a immutable reference to the extent at a given index
+    pub fn extent_at(&self, index: usize) -> &'static Ext4Extent {
+        unsafe {
+            &*((self.header() as *const Ext4ExtentHeader).add(1) as *const Ext4Extent).add(index)
+        }
+    }
+
+    /// Get a mutable reference to the extent at a given index
+    pub fn extent_mut_at(&mut self, index: usize) -> &'static mut Ext4Extent {
+        unsafe {
+            &mut *((self.header_mut() as *mut Ext4ExtentHeader).add(1) as *mut Ext4Extent)
+                .add(index)
+        }
+    }
+
+    /// Get a immutable reference to the extent index at a given index
+    pub fn extent_index_at(&self, index: usize) -> &'static Ext4ExtentIndex {
+        unsafe {
+            &*((self.header() as *const Ext4ExtentHeader).add(1) as *const Ext4ExtentIndex)
+                .add(index)
+        }
+    }
+
+    /// Get a mutable reference to the extent index at a given index
+    pub fn extent_index_mut_at(&mut self, index: usize) -> &'static mut Ext4ExtentIndex {
+        unsafe {
+            &mut *((self.header_mut() as *mut Ext4ExtentHeader).add(1) as *mut Ext4ExtentIndex)
+                .add(index)
+        }
+    }
+
+    pub fn print(&self) {
+        debug!("Extent header count {}", self.header().entries_count());
+        let mut i = 0;
+        while i < self.header().entries_count() as usize {
+            let ext = self.extent_at(i);
+            debug!(
+                "extent[{}] start_lblock={}, start_pblock={}, len={}",
+                i,
+                ext.start_lblock(),
+                ext.start_pblock(),
+                ext.block_count()
+            );
+            i += 1;
+        }
+    }
+}
+
 #[derive(Debug)]
 pub struct ExtentSearchPath {
     /// Marks whether the extent search path is for an inner node or a leaf node.

+ 18 - 15
src/ext4_defs/inode.rs

@@ -12,6 +12,7 @@ use super::BlockDevice;
 use super::Ext4BlockGroupDesc;
 use super::Ext4ExtentHeader;
 use super::Ext4Superblock;
+use super::{ExtentNode, ExtentNodeMut};
 use crate::constants::*;
 use crate::prelude::*;
 
@@ -290,27 +291,29 @@ impl Ext4Inode {
 
     /* Extent methods */
 
-    pub fn extent_header(&self) -> &'static Ext4ExtentHeader {
-        unsafe {
-            (&self.block as *const [u32; 15] as *const Ext4ExtentHeader)
-                .as_ref()
-                .unwrap()
-        }
+    /// Get the immutable extent root node
+    pub fn extent(&self) -> ExtentNode {
+        ExtentNode::from_bytes(unsafe {
+            core::slice::from_raw_parts(self.block.as_ptr() as *const u8, 60)
+        })
     }
 
-    pub fn extent_header_mut(&mut self) -> &'static mut Ext4ExtentHeader {
-        unsafe {
-            (&mut self.block as *mut [u32; 15] as *mut Ext4ExtentHeader)
-                .as_mut()
-                .unwrap()
-        }
+    /// Get the mutable extent root node
+    pub fn extent_mut(&mut self) -> ExtentNodeMut {
+        ExtentNodeMut::from_bytes(unsafe {
+            core::slice::from_raw_parts_mut(self.block.as_mut_ptr() as *mut u8, 60)
+        })
     }
 
-    pub fn extent_depth(&self) -> u16 {
-        self.extent_header().depth()
+    pub fn extent_depth(&mut self) -> u16 {
+        self.extent().header().depth()
     }
 
-    pub fn extent_tree_init(&mut self) {
+    /// Initialize the `flags` and `block` field of inode. Mark the
+    /// inode to use extent for block mapping. Initialize the root
+    /// node of the extent tree
+    pub fn extent_init(&mut self) {
+        self.set_flags(EXT4_INODE_FLAG_EXTENTS);
         let header = Ext4ExtentHeader::new(0, 4, 0, 0);
         let header_ptr = &header as *const Ext4ExtentHeader as *const u32;
         let array_ptr = &mut self.block as *mut [u32; 15] as *mut u32;