Prechádzať zdrojové kódy

feat: block read write

liujingx 11 mesiacov pred
rodič
commit
d2cd5d3d3e

+ 25 - 27
ext4_test/src/main.rs

@@ -1,4 +1,4 @@
-use ext4_rs::{BlockDevice, Ext4, BLOCK_SIZE};
+use ext4_rs::{Block, BlockDevice, Ext4, BLOCK_SIZE};
 use simple_logger::SimpleLogger;
 use std::fs::{File, OpenOptions};
 use std::io::{Read, Seek, SeekFrom, Write};
@@ -19,20 +19,18 @@ impl BlockFile {
 }
 
 impl BlockDevice for BlockFile {
-    fn read_offset(&self, offset: usize) -> Vec<u8> {
-        // info!("Reading block at offset {}", offset);
+    fn read_block(&self, block_id: u64) -> Block {
         let mut file = &self.0;
-        let mut buffer = vec![0u8; BLOCK_SIZE];
-        let _r = file.seek(SeekFrom::Start(offset as u64));
+        let mut buffer = [0u8; BLOCK_SIZE];
+        let _r = file.seek(SeekFrom::Start(block_id * BLOCK_SIZE as u64));
         let _r = file.read_exact(&mut buffer);
-        buffer
+        Block::new(block_id, buffer)
     }
 
-    fn write_offset(&self, offset: usize, data: &[u8]) {
-        // info!("Writing block at offset {}", offset);
+    fn write_block(&self, block: &Block) {
         let mut file = &self.0;
-        let _r = file.seek(SeekFrom::Start(offset as u64));
-        let _r = file.write_all(data);
+        let _r = file.seek(SeekFrom::Start(block.block_id * BLOCK_SIZE as u64));
+        let _r = file.write_all(&block.data);
     }
 }
 
@@ -60,31 +58,31 @@ fn open_ext4() -> Ext4 {
 }
 
 fn mkdir_test(ext4: &mut Ext4) {
-    ext4.ext4_dir_mk("1").expect("mkdir failed");
-    ext4.ext4_dir_mk("1/2").expect("mkdir failed");
-    ext4.ext4_dir_mk("1/2/3").expect("mkdir failed");
-    ext4.ext4_dir_mk("1/2/3/4").expect("mkdir failed");
-    ext4.ext4_dir_mk("2").expect("mkdir failed");
-    ext4.ext4_dir_mk("2/3").expect("mkdir failed");
-    ext4.ext4_dir_mk("2/3/4").expect("mkdir failed");
-    ext4.ext4_dir_mk("3").expect("mkdir failed");
+    ext4.mkdir("1").expect("mkdir failed");
+    ext4.mkdir("1/2").expect("mkdir failed");
+    ext4.mkdir("1/2/3").expect("mkdir failed");
+    ext4.mkdir("1/2/3/4").expect("mkdir failed");
+    ext4.mkdir("2").expect("mkdir failed");
+    ext4.mkdir("2/3").expect("mkdir failed");
+    ext4.mkdir("2/3/4").expect("mkdir failed");
+    ext4.mkdir("3").expect("mkdir failed");
 }
 
 fn open_test(ext4: &mut Ext4) {
-    ext4.ext4_open("1/2/3/4/5", "w+", true)
-        .expect("open failed");
-    ext4.ext4_open("1/2/3/4/5", "r", true).expect("open failed");
-    ext4.ext4_open("1/2/3/4/5", "a", true).expect("open failed");
-    ext4.ext4_open("2/4", "w+", true).expect("open failed");
+    ext4.open("1/2/3/4/5", "w+", true).expect("open failed");
+    ext4.open("1/2/3/4/5", "r", true).expect("open failed");
+    ext4.open("1/2/3/4/5", "a", true).expect("open failed");
+    ext4.open("2/4", "w+", true).expect("open failed");
 }
 
 fn read_write_test(ext4: &mut Ext4) {
     let buffer = "hello world".as_bytes();
-    let mut wfile = ext4.ext4_open("1/2/3/4/5", "w+", true).expect("open failed");
-    ext4.ext4_file_write(&mut wfile, buffer).expect("write failed");
-    let mut rfile = ext4.ext4_open("1/2/3/4/5", "r", true).expect("open failed");
+    let mut wfile = ext4.open("1/2/3/4/5", "w+", true).expect("open failed");
+    ext4.write(&mut wfile, buffer).expect("write failed");
+    let mut rfile = ext4.open("1/2/3/4/5", "r", true).expect("open failed");
     let mut buffer2 = vec![0u8; buffer.len()];
-    ext4.ext4_file_read(&mut rfile, &mut buffer2, buffer.len()).expect("read failed");
+    ext4.read(&mut rfile, &mut buffer2, buffer.len())
+        .expect("read failed");
     assert_eq!(buffer, buffer2);
 }
 

+ 18 - 29
src/ext4/alloc.rs

@@ -15,21 +15,21 @@ impl Ext4 {
 
         // Load block group descriptor
         let mut bg =
-            BlockGroupDesc::load(self.block_device.clone(), &self.super_block, bgid as usize)
-                .unwrap();
-        let block_bmap_offset = bg.get_block_bitmap_block(&self.super_block) as usize * BLOCK_SIZE;
+            BlockGroupDesc::load(self.block_device.clone(), &self.super_block, bgid as usize);
+
         // Load block bitmap
-        let raw_bitmap = &mut self.block_device.read_offset(block_bmap_offset);
-        let mut bitmap = Bitmap::new(raw_bitmap);
-        // Find and first free block
+        let bitmap_block_id = bg.get_block_bitmap_block(&self.super_block);
+        let mut bitmap_block = self.block_device.read_block(bitmap_block_id);
+        let mut bitmap = Bitmap::new(&mut bitmap_block.data);
+
+        // Find the first free block
         let fblock = bitmap
             .find_and_set_first_clear_bit(idx_in_bg as usize, 8 * BLOCK_SIZE)
             .ok_or(Ext4Error::new(ErrCode::ENOSPC))? as PBlockId;
 
         // Set block group checksum
         bg.set_block_bitmap_csum(&self.super_block, &bitmap);
-        self.block_device
-            .write_offset(block_bmap_offset, bitmap.as_raw());
+        self.block_device.write_block(&bitmap_block);
 
         // Update superblock free blocks count
         let free_blocks = self.super_block.free_blocks_count();
@@ -123,12 +123,8 @@ impl Ext4 {
 
         while bgid <= bg_count {
             // Load block group descriptor
-            let mut bg = BlockGroupDesc::load(
-                self.block_device.clone(),
-                &self.super_block,
-                bgid as usize,
-            )
-            .unwrap();
+            let mut bg =
+                BlockGroupDesc::load(self.block_device.clone(), &self.super_block, bgid as usize);
             // If there are no free inodes in this block group, try the next one
             if bg.free_inodes_count() == 0 {
                 bgid += 1;
@@ -136,24 +132,17 @@ impl Ext4 {
             }
 
             // Load inode bitmap
-            let inode_bitmap_block = bg.get_inode_bitmap_block(&self.super_block);
-            let mut raw_data = self
-                .block_device
-                .read_offset(inode_bitmap_block as usize * BLOCK_SIZE);
-            let inode_count = self.super_block.inode_count_in_group(bgid);
-            let bitmap_size: u32 = inode_count / 0x8;
-            let mut bitmap_data = &mut raw_data[..bitmap_size as usize];
-            let mut bitmap = Bitmap::new(&mut bitmap_data);
+            let bitmap_block_id = bg.get_inode_bitmap_block(&self.super_block);
+            let mut bitmap_block = self.block_device.read_block(bitmap_block_id);
+            let inode_count = self.super_block.inode_count_in_group(bgid) as usize;
+            let mut bitmap = Bitmap::new(&mut bitmap_block.data[..inode_count / 8]);
 
             // Find a free inode
-            let idx_in_bg = bitmap
-                .find_and_set_first_clear_bit(0, inode_count as usize)
-                .unwrap() as u32;
+            let idx_in_bg = bitmap.find_and_set_first_clear_bit(0, inode_count).unwrap() as u32;
 
             // Update bitmap in disk
-            self.block_device
-                .write_offset(inode_bitmap_block as usize * BLOCK_SIZE, &bitmap.as_raw());
             bg.set_inode_bitmap_csum(&self.super_block, &bitmap);
+            self.block_device.write_block(&bitmap_block);
 
             // Modify filesystem counters
             let free_inodes = bg.free_inodes_count() - 1;
@@ -167,9 +156,9 @@ impl Ext4 {
 
             // Decrease unused inodes count
             let mut unused = bg.get_itable_unused(&self.super_block);
-            let free = inode_count - unused as u32;
+            let free = inode_count as u32 - unused;
             if idx_in_bg >= free {
-                unused = inode_count - (idx_in_bg + 1);
+                unused = inode_count as u32 - (idx_in_bg + 1);
                 bg.set_itable_unused(&self.super_block, unused);
             }
 

+ 18 - 27
src/ext4/dir.rs

@@ -16,9 +16,9 @@ impl Ext4 {
             debug!("Parent {:?}", parent.inode);
             let fblock = self.extent_get_pblock(parent, iblock)?;
             // Load block from disk
-            let block_data = self.block_device.read_offset(fblock as usize * BLOCK_SIZE);
+            let block = self.block_device.read_block(fblock);
             // Find the entry in block
-            let res = Self::find_entry_in_block(&block_data, name);
+            let res = Self::find_entry_in_block(&block, name);
             if let Ok(r) = res {
                 return Ok(r);
             }
@@ -28,10 +28,10 @@ impl Ext4 {
     }
 
     /// Find a directory entry that matches a given name in a given block
-    fn find_entry_in_block(block_data: &[u8], name: &str) -> Result<DirEntry> {
+    fn find_entry_in_block(block: &Block, name: &str) -> Result<DirEntry> {
         let mut offset = 0;
-        while offset < block_data.len() {
-            let de = DirEntry::from_bytes(&block_data[offset..]);
+        while offset < BLOCK_SIZE {
+            let de = DirEntry::from_bytes(&block.data[offset..]);
             offset += de.rec_len() as usize;
             // Unused dir entry
             if de.unused() {
@@ -65,13 +65,9 @@ impl Ext4 {
             // 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 = Block {
-                pblock_id: fblock,
-                block_data: &mut data,
-            };
+            let mut block = self.block_device.read_block(fblock);
             // Try inserting the entry to parent block
-            self.insert_entry_to_old_block(&mut ext4_block, child, path)?;
+            self.insert_entry_to_old_block(&mut block, child, path)?;
             // Current block has no enough space
             iblock += 1;
         }
@@ -80,12 +76,7 @@ impl Ext4 {
         // Append a new data block
         let (_, fblock) = self.inode_append_block(parent)?;
         // Load new block
-        let block_device = self.block_device.clone();
-        let mut data = block_device.read_offset(fblock as usize * BLOCK_SIZE);
-        let mut new_block = Block {
-            pblock_id: fblock,
-            block_data: &mut data,
-        };
+        let mut new_block = self.block_device.read_block(fblock);
         // Write the entry to block
         self.insert_entry_to_new_block(&mut new_block, child, path)
     }
@@ -108,16 +99,16 @@ impl Ext4 {
         );
 
         // Write entry to block
-        new_entry.copy_to_byte_slice(&mut dst_blk.block_data, 0);
+        new_entry.copy_to_byte_slice(&mut dst_blk.data, 0);
 
         // Set tail
         let mut tail = DirEntryTail::default();
         tail.rec_len = size_of::<DirEntryTail>() as u16;
         tail.reserved_ft = 0xDE;
-        tail.set_csum(&self.super_block, &new_entry, &dst_blk.block_data[..]);
+        tail.set_csum(&self.super_block, &new_entry, &dst_blk.data[..]);
         // Copy tail to block
         let tail_offset = BLOCK_SIZE - size_of::<DirEntryTail>();
-        tail.copy_to_byte_slice(&mut dst_blk.block_data, tail_offset);
+        tail.copy_to_byte_slice(&mut dst_blk.data, tail_offset);
 
         // Sync block to disk
         dst_blk.sync_to_disk(self.block_device.clone());
@@ -135,8 +126,8 @@ impl Ext4 {
         let required_size = DirEntry::required_size(name.len());
         let mut offset = 0;
 
-        while offset < dst_blk.block_data.len() {
-            let mut de = DirEntry::from_bytes(&dst_blk.block_data[offset..]);
+        while offset < dst_blk.data.len() {
+            let mut de = DirEntry::from_bytes(&dst_blk.data[offset..]);
             let rec_len = de.rec_len() as usize;
 
             // Try splitting dir entry
@@ -154,7 +145,7 @@ impl Ext4 {
             // Has enough space
             // Update the old entry
             de.set_rec_len(used_size as u16);
-            de.copy_to_byte_slice(&mut dst_blk.block_data, offset);
+            de.copy_to_byte_slice(&mut dst_blk.data, offset);
             // Insert the new entry
             let new_entry = DirEntry::new(
                 child.inode_id,
@@ -162,15 +153,15 @@ impl Ext4 {
                 name,
                 inode_mode2file_type(child.inode.mode()),
             );
-            new_entry.copy_to_byte_slice(&mut dst_blk.block_data, offset + used_size);
+            new_entry.copy_to_byte_slice(&mut dst_blk.data, offset + used_size);
 
             // Set tail csum
             let mut tail =
-                DirEntryTail::from_bytes(&mut dst_blk.block_data, BLOCK_SIZE).unwrap();
-            tail.set_csum(&self.super_block, &de, &dst_blk.block_data[offset..]);
+                DirEntryTail::from_bytes(&mut dst_blk.data, BLOCK_SIZE).unwrap();
+            tail.set_csum(&self.super_block, &de, &dst_blk.data[offset..]);
             // Write tail to blk_data
             let tail_offset = BLOCK_SIZE - size_of::<DirEntryTail>();
-            tail.copy_to_byte_slice(&mut dst_blk.block_data, tail_offset);
+            tail.copy_to_byte_slice(&mut dst_blk.data, tail_offset);
 
             // Sync to disk
             dst_blk.sync_to_disk(self.block_device.clone());

+ 14 - 22
src/ext4/extent.rs

@@ -9,9 +9,8 @@ impl Ext4 {
     fn find_extent(&self, inode_ref: &InodeRef, iblock: LBlockId) -> Vec<ExtentSearchPath> {
         let mut path: Vec<ExtentSearchPath> = Vec::new();
         let mut ex_node = inode_ref.inode.extent();
-        ex_node.print();
         let mut pblock = 0;
-        let mut block_data: Vec<u8>;
+        let mut block_data: Block;
 
         // Go until leaf
         while ex_node.header().depth() > 0 {
@@ -26,9 +25,9 @@ impl Ext4 {
             // 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);
+            block_data = self.block_device.read_block(next);
             // Load the next extent header
-            ex_node = ExtentNode::from_bytes(&block_data);
+            ex_node = ExtentNode::from_bytes(&block_data.data);
             pblock = next;
         }
 
@@ -52,14 +51,12 @@ impl Ext4 {
         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 block_data: Block;
             let ex_node = if leaf.pblock != 0 {
                 // Load the extent node
-                block_data = self
-                    .block_device
-                    .read_offset(leaf.pblock as usize * BLOCK_SIZE);
+                block_data = self.block_device.read_block(leaf.pblock);
                 // Load the next extent header
-                ExtentNode::from_bytes(&block_data)
+                ExtentNode::from_bytes(&block_data.data)
             } else {
                 // Root node
                 inode_ref.inode.extent()
@@ -83,14 +80,12 @@ 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 mut block_data: Vec<u8>;
+        let mut block_data: Block;
         let ex_node = if leaf.pblock != 0 {
             // Load the extent node
-            block_data = self
-                .block_device
-                .read_offset(leaf.pblock as usize * BLOCK_SIZE);
+            block_data = self.block_device.read_block(leaf.pblock);
             // Load the next extent header
-            ExtentNodeMut::from_bytes(&mut block_data)
+            ExtentNodeMut::from_bytes(&mut block_data.data)
         } else {
             // Root node
             inode_ref.inode.extent_mut()
@@ -124,14 +119,12 @@ impl Ext4 {
         new_ext: &Ext4Extent,
     ) -> bool {
         // Note: block data must be defined here to keep it alive
-        let mut block_data = Vec::<u8>::new();
+        let mut block_data = Block::default();
         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);
+            block_data = self.block_device.read_block(leaf.pblock);
             // Load the next extent header
-            ExtentNodeMut::from_bytes(&mut block_data)
+            ExtentNodeMut::from_bytes(&mut block_data.data)
         } else {
             // Root node
             inode_ref.inode.extent_mut()
@@ -167,9 +160,8 @@ impl Ext4 {
 
         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);
+        if block_data.block_id != 0 {
+            block_data.sync_to_disk(self.block_device.clone());
         } else {
             self.write_back_inode_without_csum(inode_ref);
         }

+ 19 - 35
src/ext4/file.rs

@@ -81,12 +81,7 @@ impl Ext4 {
         })
     }
 
-    pub fn read(
-        &self,
-        file: &mut File,
-        read_buf: &mut [u8],
-        read_size: usize,
-    ) -> Result<usize> {
+    pub fn read(&self, file: &mut File, read_buf: &mut [u8], read_size: usize) -> Result<usize> {
         // Read no bytes
         if read_size == 0 {
             return Ok(0);
@@ -113,36 +108,25 @@ impl Ext4 {
         let mut iblock = start_iblock;
         // Read first block
         if misaligned > 0 {
-            let first_block_read_len = min(BLOCK_SIZE - misaligned, size_to_read);
+            let read_len = min(BLOCK_SIZE - misaligned, size_to_read);
             let fblock = self.extent_get_pblock(&mut inode_ref, start_iblock)?;
-            if fblock != 0 {
-                let block_offset = fblock as usize * BLOCK_SIZE + misaligned;
-                let block_data = self.block_device.read_offset(block_offset);
-                // Copy data from block to the user buffer
-                read_buf[cursor..cursor + first_block_read_len]
-                    .copy_from_slice(&block_data[0..first_block_read_len]);
-            } else {
-                // Handle the unwritten block by zeroing out the respective part of the buffer
-                read_buf[cursor..cursor + first_block_read_len].fill(0);
-            }
-            cursor += first_block_read_len;
-            file.fpos += first_block_read_len;
+            let block = self.block_device.read_block(fblock);
+            // Copy data from block to the user buffer
+            read_buf[cursor..cursor + read_len]
+                .copy_from_slice(block.read_offset(misaligned, read_len));
+            cursor += read_len;
+            file.fpos += read_len;
             iblock += 1;
         }
         // Continue with full block reads
         while cursor < size_to_read {
-            let read_length = min(BLOCK_SIZE, size_to_read - cursor);
+            let read_len = min(BLOCK_SIZE, size_to_read - cursor);
             let fblock = self.extent_get_pblock(&mut inode_ref, iblock)?;
-            if fblock != 0 {
-                let block_data = self.block_device.read_offset(fblock as usize * BLOCK_SIZE);
-                // Copy data from block to the user buffer
-                read_buf[cursor..cursor + read_length].copy_from_slice(&block_data[0..read_length]);
-            } else {
-                // Handle the unwritten block by zeroing out the respective part of the buffer
-                read_buf[cursor..cursor + read_length].fill(0);
-            }
-            cursor += read_length;
-            file.fpos += read_length;
+            let block = self.block_device.read_block(fblock);
+            // Copy data from block to the user buffer
+            read_buf[cursor..cursor + read_len].copy_from_slice(block.read_offset(0, read_len));
+            cursor += read_len;
+            file.fpos += read_len;
             iblock += 1;
         }
 
@@ -171,11 +155,11 @@ impl Ext4 {
         let mut iblock = start_iblock;
         while cursor < size {
             let write_len = min(BLOCK_SIZE, size - cursor);
-            let fblock = self.extent_get_pblock(&mut inode_ref, iblock)? as usize;
-            self.block_device.write_offset(
-                fblock * BLOCK_SIZE + file.fpos % BLOCK_SIZE,
-                &data[cursor..cursor + write_len],
-            );
+            let fblock = self.extent_get_pblock(&mut inode_ref, iblock)?;
+            let mut block = self.block_device.read_block(fblock);
+            block.write_offset(file.fpos % BLOCK_SIZE, &data[cursor..cursor + write_len]);
+            block.sync_to_disk(self.block_device.clone());
+
             cursor += write_len;
             file.fpos += write_len;
             iblock += 1;

+ 3 - 4
src/ext4/mod.rs

@@ -24,8 +24,8 @@ impl Ext4 {
     pub fn load(block_device: Arc<dyn BlockDevice>) -> Result<Self> {
         // Load the superblock
         // TODO: if the main superblock is corrupted, should we load the backup?
-        let raw_data = block_device.read_offset(BASE_OFFSET);
-        let super_block = Superblock::try_from(raw_data).unwrap();
+        let block = block_device.read_block(0);
+        let super_block = block.read_offset_as::<Superblock>(BASE_OFFSET);
         // Root mount point
         let mount_point = MountPoint::new("/");
         // Create Ext4 instance
@@ -54,13 +54,12 @@ impl Ext4 {
     fn write_back_inode_with_csum(&self, inode_ref: &mut InodeRef) {
         inode_ref
             .sync_to_disk_with_csum(self.block_device.clone(), &self.super_block)
-            .unwrap()
+            
     }
 
     /// Write back an inode to block device without checksum
     fn write_back_inode_without_csum(&self, inode_ref: &mut InodeRef) {
         inode_ref
             .sync_to_disk_without_csum(self.block_device.clone(), &self.super_block)
-            .unwrap()
     }
 }

+ 0 - 19
src/ext4_defs/block.rs

@@ -1,19 +0,0 @@
-use crate::prelude::*;
-use crate::constants::*;
-use super::BlockDevice;
-
-#[derive(Debug)]
-// A single block descriptor
-pub struct Block<'a> {
-    /// Physical block id
-    pub pblock_id: PBlockId,
-    /// Raw block data
-    pub block_data: &'a mut [u8],
-}
-
-impl<'a> Block<'a> {
-    pub fn sync_to_disk(&self, block_device: Arc<dyn BlockDevice>) {
-        let block_id = self.pblock_id as usize;
-        block_device.write_offset(block_id * BLOCK_SIZE, &self.block_data);
-    }
-}

+ 77 - 3
src/ext4_defs/block_device.rs

@@ -1,8 +1,82 @@
-use alloc::vec::Vec;
+use crate::constants::*;
+use crate::prelude::*;
 use core::any::Any;
 use core::fmt::Debug;
 
+/// Interface for serializing and deserializing objects to and from bytes.
+pub trait AsBytes
+where
+    Self: Sized,
+{
+    /// Default implementation that interprets the object as a byte array.
+    fn from_bytes(bytes: &[u8]) -> Self {
+        unsafe { core::ptr::read(bytes.as_ptr() as *const Self) }
+    }
+    /// Default implementation that serializes the object to a byte array.
+    fn to_bytes(&self) -> &[u8] {
+        unsafe { core::slice::from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
+    }
+}
+
+/// Block descriptor
+pub struct Block {
+    /// Physical block id
+    pub block_id: PBlockId,
+    /// Raw block data
+    pub data: [u8; BLOCK_SIZE],
+}
+
+impl Default for Block {
+    fn default() -> Self {
+        Self {
+            block_id: 0,
+            data: [0; BLOCK_SIZE],
+        }
+    }
+}
+
+impl Block {
+    /// Create new block with given physical block id and data
+    pub fn new(block_id: PBlockId, data: [u8; BLOCK_SIZE]) -> Self {
+        Self { block_id, data }
+    }
+
+    /// Read `size` bytes at `offset` from block data
+    pub fn read_offset(&self, offset: usize, size: usize) -> &[u8] {
+        &self.data[offset..offset + size]
+    }
+
+    /// Read `size_of::<T>()` bytes at `offset` from block data and interpret it as `T`
+    pub fn read_offset_as<'a, T>(&self, offset: usize) -> T
+    where
+        T: AsBytes,
+    {
+        T::from_bytes(&self.data[offset..offset + size_of::<T>()])
+    }
+
+    /// Write block data to `offset` with `size`
+    pub fn write_offset(&mut self, offset: usize, data: &[u8]) {
+        self.data[offset..offset + data.len()].copy_from_slice(data);
+    }
+
+    /// Transform `T` to bytes and write it to `offset`
+    pub fn write_offset_as<T>(&mut self, offset: usize, value: &T)
+    where
+        T: AsBytes,
+    {
+        self.write_offset(offset, value.to_bytes());
+    }
+
+    /// Write block to disk
+    pub fn sync_to_disk(&self, block_device: Arc<dyn BlockDevice>) {
+        block_device.write_block(self);
+    }
+}
+
+/// Common interface for block devices
 pub trait BlockDevice: Send + Sync + Any + Debug {
-    fn read_offset(&self, offset: usize) -> Vec<u8>;
-    fn write_offset(&self, offset: usize, data: &[u8]);
+    /// Read a block from disk
+    fn read_block(&self, block_id: PBlockId) -> Block;
+    /// Write a block to disk
+    fn write_block(&self, block: &Block);
 }

+ 8 - 25
src/ext4_defs/block_group.rs

@@ -13,6 +13,7 @@ use super::BlockDevice;
 use super::Superblock;
 use crate::constants::*;
 use crate::prelude::*;
+use crate::AsBytes;
 
 #[derive(Debug, Default, Clone, Copy)]
 #[repr(C, packed)]
@@ -43,20 +44,14 @@ pub struct BlockGroupDesc {
     reserved: u32,                   // 填充
 }
 
-impl TryFrom<&[u8]> for BlockGroupDesc {
-    type Error = u64;
-    fn try_from(data: &[u8]) -> core::result::Result<Self, u64> {
-        let data = &data[..size_of::<BlockGroupDesc>()];
-        Ok(unsafe { core::ptr::read(data.as_ptr() as *const _) })
-    }
-}
+impl AsBytes for BlockGroupDesc {}
 
 impl BlockGroupDesc {
     pub fn load(
         block_device: Arc<dyn BlockDevice>,
         super_block: &Superblock,
         block_group_id: usize,
-    ) -> core::result::Result<Self, u64> {
+    ) -> Self {
         let dsc_cnt = BLOCK_SIZE / super_block.desc_size() as usize;
         let dsc_id = block_group_id / dsc_cnt;
         let first_data_block = super_block.first_data_block();
@@ -64,14 +59,8 @@ impl BlockGroupDesc {
         let block_id = first_data_block as usize + dsc_id + 1;
         let offset = (block_group_id % dsc_cnt) * super_block.desc_size() as usize;
 
-        let data = block_device.read_offset(block_id * BLOCK_SIZE);
-
-        let block_group_data =
-            &data[offset as usize..offset as usize + size_of::<BlockGroupDesc>()];
-
-        let bg = BlockGroupDesc::try_from(block_group_data);
-
-        bg
+        let block = block_device.read_block(block_id as PBlockId);
+        block.read_offset_as::<Self>(offset)
     }
 
     pub fn get_block_bitmap_block(&self, s: &Superblock) -> u64 {
@@ -144,20 +133,14 @@ impl BlockGroupDesc {
         super_block: &Superblock,
     ) {
         let dsc_cnt = BLOCK_SIZE / super_block.desc_size() as usize;
-        // let dsc_per_block = dsc_cnt;
         let dsc_id = bgid / dsc_cnt;
-        // let first_meta_bg = super_block.first_meta_bg;
         let first_data_block = super_block.first_data_block();
         let block_id = first_data_block as usize + dsc_id + 1;
         let offset = (bgid % dsc_cnt) * super_block.desc_size() as usize;
 
-        let data = unsafe {
-            core::slice::from_raw_parts(
-                self as *const _ as *const u8,
-                size_of::<BlockGroupDesc>(),
-            )
-        };
-        block_device.write_offset(block_id * BLOCK_SIZE + offset, data);
+        let mut block = block_device.read_block(block_id as PBlockId);
+        block.write_offset_as(offset, self);
+        block.sync_to_disk(block_device);
     }
 
     pub fn calc_checksum(&mut self, bgid: u32, super_block: &Superblock) -> u16 {

+ 24 - 20
src/ext4_defs/inode.rs

@@ -15,6 +15,7 @@ use super::Superblock;
 use super::{ExtentNode, ExtentNodeMut};
 use crate::constants::*;
 use crate::prelude::*;
+use crate::AsBytes;
 
 #[repr(C)]
 #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
@@ -66,6 +67,8 @@ impl Default for Inode {
     }
 }
 
+impl AsBytes for Inode {}
+
 impl Inode {
     pub fn from_bytes(bytes: &[u8]) -> Self {
         unsafe { *(bytes.as_ptr() as *const Inode) }
@@ -273,16 +276,16 @@ impl InodeRef {
         super_block: &Superblock,
         inode_id: InodeId,
     ) -> Self {
-        let pos = Self::inode_disk_pos(super_block, block_device.clone(), inode_id);
-        let data = block_device.read_offset(pos);
-        let inode_data = &data[..core::mem::size_of::<Inode>()];
+        let (block_id, offset) = Self::inode_disk_pos(super_block, block_device.clone(), inode_id);
+        let block = block_device.read_block(block_id);
         Self {
             inode_id,
-            inode: Inode::from_bytes(inode_data),
+            inode: Inode::from_bytes(block.read_offset(offset, size_of::<Inode>())),
         }
     }
 
-    /// Find the position of an inode in the block device.
+    /// Find the position of an inode in the block device. Return the
+    /// block id and the offset within the block.
     ///
     /// Each block group contains `sb.inodes_per_group` inodes.
     /// Because inode 0 is defined not to exist, this formula can
@@ -297,35 +300,36 @@ impl InodeRef {
         super_block: &Superblock,
         block_device: Arc<dyn BlockDevice>,
         inode_id: InodeId,
-    ) -> usize {
+    ) -> (PBlockId, usize) {
         let inodes_per_group = super_block.inodes_per_group();
-        let inode_size = super_block.inode_size();
-        let group = (inode_id - 1) / inodes_per_group;
-        let index = (inode_id - 1) % inodes_per_group;
+        let inode_size = super_block.inode_size() as usize;
+        let group = ((inode_id - 1) / inodes_per_group) as usize;
+        let index = ((inode_id - 1) % inodes_per_group) as usize;
 
-        let bg = BlockGroupDesc::load(block_device, super_block, group as usize).unwrap();
-        bg.inode_table_first_block() as usize * BLOCK_SIZE + (index * inode_size as u32) as usize
+        let bg = BlockGroupDesc::load(block_device, super_block, group);
+        let block_id = bg.inode_table_first_block() + (index * inode_size / BLOCK_SIZE) as PBlockId;
+        let offset = (index * inode_size) % BLOCK_SIZE;
+        (block_id, offset)
     }
 
     pub fn sync_to_disk_without_csum(
         &self,
         block_device: Arc<dyn BlockDevice>,
         super_block: &Superblock,
-    ) -> Result<()> {
-        let disk_pos = Self::inode_disk_pos(super_block, block_device.clone(), self.inode_id);
-        let data = unsafe {
-            core::slice::from_raw_parts(&self.inode as *const _ as *const u8, size_of::<Inode>())
-        };
-        block_device.write_offset(disk_pos, data);
-        Ok(())
+    ) {
+        let (block_id, offset) =
+            Self::inode_disk_pos(super_block, block_device.clone(), self.inode_id);
+        let mut block = block_device.read_block(block_id);
+        block.write_offset_as(offset, &self.inode);
+        block.sync_to_disk(block_device);
     }
 
     pub fn sync_to_disk_with_csum(
         &mut self,
         block_device: Arc<dyn BlockDevice>,
         super_block: &Superblock,
-    ) -> Result<()> {
+    ) {
         self.inode.set_checksum(super_block, self.inode_id);
-        self.sync_to_disk_without_csum(block_device, super_block)
+        self.sync_to_disk_without_csum(block_device, super_block);
     }
 }

+ 0 - 2
src/ext4_defs/mod.rs

@@ -15,7 +15,6 @@
 //! For all other block groups, there is no padding.
 
 mod bitmap;
-mod block;
 mod block_device;
 mod block_group;
 mod crc;
@@ -27,7 +26,6 @@ mod mount_point;
 mod super_block;
 
 pub use bitmap::*;
-pub use block::*;
 pub use block_device::*;
 pub use block_group::*;
 pub use dir_entry::*;

+ 5 - 11
src/ext4_defs/super_block.rs

@@ -8,6 +8,7 @@ use super::BlockDevice;
 use super::Inode;
 use crate::constants::*;
 use crate::prelude::*;
+use crate::AsBytes;
 
 // 结构体表示超级块
 #[repr(C)]
@@ -112,13 +113,7 @@ pub struct Superblock {
     checksum: u32,             // crc32c(superblock)
 }
 
-impl TryFrom<Vec<u8>> for Superblock {
-    type Error = u64;
-    fn try_from(value: Vec<u8>) -> core::result::Result<Self, u64> {
-        let data = &value[..size_of::<Superblock>()];
-        Ok(unsafe { core::ptr::read(data.as_ptr() as *const _) })
-    }
-}
+impl AsBytes for Superblock {}
 
 impl Superblock {
     pub fn first_data_block(&self) -> u32 {
@@ -230,9 +225,8 @@ impl Superblock {
     }
     
     pub fn sync_to_disk(&self, block_device: Arc<dyn BlockDevice>) {
-        let data = unsafe {
-            core::slice::from_raw_parts(self as *const _ as *const u8, size_of::<Superblock>())
-        };
-        block_device.write_offset(BASE_OFFSET, data);
+        let mut block = block_device.read_block(0);
+        block.write_offset_as(BASE_OFFSET, self);
+        block.sync_to_disk(block_device);
     }
 }