Переглянути джерело

feat: use 256-bit inode and 64-bit block desc

liujingx 10 місяців тому
батько
коміт
6bf1de6802
6 змінених файлів з 112 додано та 226 видалено
  1. 2 1
      ext4_fuse/src/block_dev.rs
  2. 8 0
      src/constants.rs
  3. 41 67
      src/ext4/alloc.rs
  4. 1 0
      src/ext4/journal.rs
  5. 10 1
      src/ext4/mod.rs
  6. 50 157
      src/ext4_defs/block_group.rs

+ 2 - 1
ext4_fuse/src/block_dev.rs

@@ -41,7 +41,8 @@ impl BlockMem {
             .expect("Failed to create temp file");
         // Make ext4 fs
         std::process::Command::new("mkfs.ext4")
-            .args([path, &format!("-b {}", BLOCK_SIZE)])
+            // Enable 64-bit feature and use 256-bit inode size
+            .args([path, &format!("-b {}", BLOCK_SIZE), "-O 64bit", "-I 256"])
             .status()
             .expect("Failed to make ext4 fs");
         // Open the temp file and copy data to memory

+ 8 - 0
src/constants.rs

@@ -28,3 +28,11 @@ pub const INODE_BLOCK_SIZE: usize = 4096;
 
 /// CRC32 initial value
 pub const CRC32_INIT: u32 = 0xFFFFFFFF;
+
+/// The value of super block `inode_size` field.
+/// We implement the larger version of inode size for simplicity.
+pub const SB_GOOD_INODE_SIZE: usize = 256;
+
+/// The value of super block `desc_size` field.
+/// We implement the 64-bit block group descriptor for simplicity.
+pub const SB_GOOD_DESC_SIZE: usize = 64;

+ 41 - 67
src/ext4/alloc.rs

@@ -106,9 +106,9 @@ impl Ext4 {
         let mut bg = self.read_block_group(bgid);
 
         // Load block bitmap
-        let bitmap_block_id = bg.desc.block_bitmap_block(&sb);
+        let bitmap_block_id = bg.desc.block_bitmap_block();
         let mut bitmap_block = self.read_block(bitmap_block_id);
-        let mut bitmap = Bitmap::new(&mut bitmap_block.data);
+        let mut bitmap = Bitmap::new(&mut bitmap_block.data, 8 * BLOCK_SIZE);
 
         // Find the first free block
         let fblock = bitmap
@@ -118,22 +118,19 @@ impl Ext4 {
                 "No free blocks in block group {}",
                 bgid
             ))? as PBlockId;
-
         // Set block group checksum
-        bg.desc.set_block_bitmap_csum(&sb, &bitmap);
+        bg.desc.set_block_bitmap_csum(&sb.uuid(), &bitmap);
         self.write_block(&bitmap_block);
 
-        // Update superblock free blocks count
-        let free_blocks = sb.free_blocks_count() - 1;
-        sb.set_free_blocks_count(free_blocks);
-        self.write_super_block(&sb);
-
-        // Update block group free blocks count
-        let fb_cnt = bg.desc.get_free_blocks_count() - 1;
-        bg.desc.set_free_blocks_count(fb_cnt);
-
+        // Update block group counters
+        bg.desc
+            .set_free_blocks_count(bg.desc.get_free_blocks_count() - 1);
         self.write_block_group_with_csum(&mut bg);
 
+        // Update superblock counters
+        sb.set_free_blocks_count(sb.free_blocks_count() - 1);
+        self.write_super_block(&sb);
+
         trace!("Alloc block {} ok", fblock);
         Ok(fblock)
     }
@@ -150,31 +147,28 @@ impl Ext4 {
         let mut bg = self.read_block_group(bgid);
 
         // Load block bitmap
-        let bitmap_block_id = bg.desc.block_bitmap_block(&sb);
+        let bitmap_block_id = bg.desc.block_bitmap_block();
         let mut bitmap_block = self.read_block(bitmap_block_id);
-        let mut bitmap = Bitmap::new(&mut bitmap_block.data);
+        let mut bitmap = Bitmap::new(&mut bitmap_block.data, 8 * BLOCK_SIZE);
 
         // Free the block
         if bitmap.is_bit_clear(pblock as usize) {
             return_error!(ErrCode::EINVAL, "Block {} is already free", pblock);
         }
         bitmap.clear_bit(pblock as usize);
-
         // Set block group checksum
-        bg.desc.set_block_bitmap_csum(&sb, &bitmap);
+        bg.desc.set_block_bitmap_csum(&sb.uuid(), &bitmap);
         self.write_block(&bitmap_block);
 
-        // Update superblock free blocks count
-        let free_blocks = sb.free_blocks_count() + 1;
-        sb.set_free_blocks_count(free_blocks);
-        self.write_super_block(&sb);
-
-        // Update block group free blocks count
-        let fb_cnt = bg.desc.get_free_blocks_count() + 1;
-        bg.desc.set_free_blocks_count(fb_cnt);
-
+        // Update block group counters
+        bg.desc
+            .set_free_blocks_count(bg.desc.get_free_blocks_count() + 1);
         self.write_block_group_with_csum(&mut bg);
 
+        // Update superblock counters
+        sb.set_free_blocks_count(sb.free_blocks_count() + 1);
+        self.write_super_block(&sb);
+
         trace!("Free block {} ok", pblock);
         Ok(())
     }
@@ -193,12 +187,11 @@ impl Ext4 {
                 bgid += 1;
                 continue;
             }
-
             // Load inode bitmap
-            let bitmap_block_id = bg.desc.inode_bitmap_block(&sb);
+            let bitmap_block_id = bg.desc.inode_bitmap_block();
             let mut bitmap_block = self.read_block(bitmap_block_id);
             let inode_count = sb.inode_count_in_group(bgid) as usize;
-            let mut bitmap = Bitmap::new(&mut bitmap_block.data[..inode_count / 8]);
+            let mut bitmap = Bitmap::new(&mut bitmap_block.data, inode_count);
 
             // Find a free inode
             let idx_in_bg =
@@ -209,42 +202,33 @@ impl Ext4 {
                         "No free inodes in block group {}",
                         bgid
                     ))? as u32;
-
             // Update bitmap in disk
-            bg.desc.set_inode_bitmap_csum(&sb, &bitmap);
+            bg.desc.set_inode_bitmap_csum(&sb.uuid(), &bitmap);
             self.write_block(&bitmap_block);
 
-            // Modify filesystem counters
-            let free_inodes = bg.desc.free_inodes_count() - 1;
-            bg.desc.set_free_inodes_count(&sb, free_inodes);
-
-            // Increase used directories counter
+            // Modify block group counters
+            bg.desc
+                .set_free_inodes_count(bg.desc.free_inodes_count() - 1);
             if is_dir {
-                let used_dirs = bg.desc.used_dirs_count(&sb) + 1;
-                bg.desc.set_used_dirs_count(&sb, used_dirs);
+                bg.desc.set_used_dirs_count(bg.desc.used_dirs_count() + 1);
             }
-
-            // Decrease unused inodes count
-            let mut unused = bg.desc.itable_unused(&sb);
+            let mut unused = bg.desc.itable_unused();
             let free = inode_count as u32 - unused;
             if idx_in_bg >= free {
                 unused = inode_count as u32 - (idx_in_bg + 1);
-                bg.desc.set_itable_unused(&sb, unused);
+                bg.desc.set_itable_unused(unused);
             }
-
             self.write_block_group_with_csum(&mut bg);
 
-            // Update superblock
-            sb.decrease_free_inodes_count();
+            // Update superblock counters
+            sb.set_free_inodes_count(sb.free_inodes_count() - 1);
             self.write_super_block(&sb);
 
             // Compute the absolute i-node number
             let inodes_per_group = sb.inodes_per_group();
             let inode_id = bgid * inodes_per_group + (idx_in_bg + 1);
-
             return Ok(inode_id);
         }
-
         trace!("no free inode");
         return_error!(ErrCode::ENOSPC, "No free inodes in block group {}", bgid);
     }
@@ -257,15 +241,13 @@ impl Ext4 {
         let inodes_per_group = sb.inodes_per_group();
         let bgid = ((inode_ref.id - 1) / inodes_per_group) as BlockGroupId;
         let idx_in_bg = (inode_ref.id - 1) % inodes_per_group;
-
         // Load block group descriptor
         let mut bg = self.read_block_group(bgid);
-
         // Load inode bitmap
-        let bitmap_block_id = bg.desc.inode_bitmap_block(&sb);
+        let bitmap_block_id = bg.desc.inode_bitmap_block();
         let mut bitmap_block = self.read_block(bitmap_block_id);
         let inode_count = sb.inode_count_in_group(bgid) as usize;
-        let mut bitmap = Bitmap::new(&mut bitmap_block.data[..inode_count / 8]);
+        let mut bitmap = Bitmap::new(&mut bitmap_block.data, inode_count);
 
         // Free the inode
         if bitmap.is_bit_clear(idx_in_bg as usize) {
@@ -277,29 +259,21 @@ impl Ext4 {
             );
         }
         bitmap.clear_bit(idx_in_bg as usize);
-
         // Update bitmap in disk
-        bg.desc.set_inode_bitmap_csum(&sb, &bitmap);
+        bg.desc.set_inode_bitmap_csum(&sb.uuid(), &bitmap);
         self.write_block(&bitmap_block);
 
-        // Modify filesystem counters
-        let free_inodes = bg.desc.free_inodes_count() + 1;
-        bg.desc.set_free_inodes_count(&sb, free_inodes);
-
-        // Increase used directories counter
+        // Update block group counters
+        bg.desc
+            .set_free_inodes_count(bg.desc.free_inodes_count() + 1);
         if inode_ref.inode.is_dir() {
-            let used_dirs = bg.desc.used_dirs_count(&sb) - 1;
-            bg.desc.set_used_dirs_count(&sb, used_dirs);
+            bg.desc.set_used_dirs_count(bg.desc.used_dirs_count() - 1);
         }
-
-        // Decrease unused inodes count
-        let unused = bg.desc.itable_unused(&sb) + 1;
-        bg.desc.set_itable_unused(&sb, unused);
-
+        bg.desc.set_itable_unused(bg.desc.itable_unused() + 1);
         self.write_block_group_with_csum(&mut bg);
 
-        // Update superblock
-        sb.decrease_free_inodes_count();
+        // Update superblock counters
+        sb.set_free_inodes_count(sb.free_inodes_count() - 1);
         self.write_super_block(&sb);
 
         Ok(())

+ 1 - 0
src/ext4/journal.rs

@@ -2,6 +2,7 @@ use super::Ext4;
 
 impl Ext4 {
     /// start transaction
+    #[allow(unused)]
     pub(super) fn trans_start(&self) {}
 
     /// stop transaction

+ 10 - 1
src/ext4/mod.rs

@@ -26,8 +26,17 @@ impl Ext4 {
         let sb = block.read_offset_as::<SuperBlock>(BASE_OFFSET);
         // Check magic number
         if !sb.check_magic() {
-            return_error!(ErrCode::EINVAL, "invalid magic number");
+            return_error!(ErrCode::EINVAL, "Invalid magic number");
         }
+        // Check inode size
+        if !sb.inode_size() == SB_GOOD_INODE_SIZE {
+            return_error!(ErrCode::EINVAL, "Invalid inode size");
+        }
+        // Check block group desc size
+        if !sb.desc_size() as usize != SB_GOOD_DESC_SIZE {
+            return_error!(ErrCode::EINVAL, "Invalid block group desc size");
+        }
+        log::debug!("Ext4 loaded: {:?}", sb);
         // Create Ext4 instance
         Ok(Self { block_device })
     }

+ 50 - 157
src/ext4_defs/block_group.rs

@@ -10,11 +10,19 @@
 use super::crc::*;
 use super::AsBytes;
 use super::Bitmap;
-use super::BlockDevice;
-use super::SuperBlock;
 use crate::constants::*;
 use crate::prelude::*;
 
+/// The Block Group Descriptor.
+///
+/// Each block group on the filesystem has one of these descriptors associated with it.
+/// In ext2, ext3, and ext4 (when the 64bit feature is not enabled), the block group
+/// descriptor was only 32 bytes long and therefore ends at bg_checksum. On an ext4
+/// filesystem with the 64bit feature enabled, the block group descriptor expands to
+/// at least the 64 bytes described below; the size is stored in the superblock.
+///
+/// We only implement the 64 bytes version for simplicity. Guarantee that `sb.desc_size`
+/// equals to 64. This value will be checked when loading the filesystem.
 #[derive(Debug, Default, Clone, Copy)]
 #[repr(C, packed)]
 pub struct BlockGroupDesc {
@@ -47,60 +55,40 @@ pub struct BlockGroupDesc {
 unsafe impl AsBytes for BlockGroupDesc {}
 
 impl BlockGroupDesc {
-    const MIN_BLOCK_GROUP_DESC_SIZE: u16 = 32;
-    const MAX_BLOCK_GROUP_DESC_SIZE: u16 = 64;
-
-    pub fn block_bitmap_block(&self, s: &SuperBlock) -> PBlockId {
-        let mut v = self.block_bitmap_lo as u64;
-        if s.desc_size() > Self::MIN_BLOCK_GROUP_DESC_SIZE {
-            v |= (self.block_bitmap_hi as u64) << 32;
-        }
-        v
+    #[allow(unused)]
+    const MIN_BLOCK_GROUP_DESC_SIZE: usize = 32;
+    #[allow(unused)]
+    const MAX_BLOCK_GROUP_DESC_SIZE: usize = 64;
+
+    pub fn block_bitmap_block(&self) -> PBlockId {
+        (self.block_bitmap_hi as PBlockId) << 32 | self.block_bitmap_lo as PBlockId
     }
 
-    pub fn inode_bitmap_block(&self, s: &SuperBlock) -> PBlockId {
-        let mut v = self.inode_bitmap_lo as u64;
-        if s.desc_size() > Self::MIN_BLOCK_GROUP_DESC_SIZE {
-            v |= (self.inode_bitmap_hi as u64) << 32;
-        }
-        v
+    pub fn inode_bitmap_block(&self) -> PBlockId {
+        (self.inode_bitmap_hi as PBlockId) << 32 | self.inode_bitmap_lo as PBlockId
     }
 
-    pub fn itable_unused(&mut self, s: &SuperBlock) -> u32 {
-        let mut v = self.itable_unused_lo as u32;
-        if s.desc_size() > Self::MIN_BLOCK_GROUP_DESC_SIZE {
-            v |= ((self.itable_unused_hi as u64) << 32) as u32;
-        }
-        v
+    pub fn itable_unused(&self) -> u32 {
+        (self.itable_unused_hi as u32) << 16 | self.itable_unused_lo as u32
     }
 
-    pub fn used_dirs_count(&self, s: &SuperBlock) -> u32 {
-        let mut v = self.used_dirs_count_lo as u32;
-        if s.desc_size() > Self::MIN_BLOCK_GROUP_DESC_SIZE {
-            v |= ((self.used_dirs_count_hi as u64) << 32) as u32;
-        }
-        v
+    pub fn used_dirs_count(&self) -> u32 {
+        (self.used_dirs_count_hi as u32) << 16 | self.used_dirs_count_lo as u32
     }
 
-    pub fn set_used_dirs_count(&mut self, s: &SuperBlock, cnt: u32) {
-        self.itable_unused_lo = ((cnt << 16) >> 16) as u16;
-        if s.desc_size() > Self::MIN_BLOCK_GROUP_DESC_SIZE {
-            self.itable_unused_hi = (cnt >> 16) as u16;
-        }
+    pub fn set_used_dirs_count(&mut self, cnt: u32) {
+        self.used_dirs_count_lo = cnt as u16;
+        self.used_dirs_count_hi = (cnt >> 16) as u16;
     }
 
-    pub fn set_itable_unused(&mut self, s: &SuperBlock, cnt: u32) {
-        self.itable_unused_lo = ((cnt << 16) >> 16) as u16;
-        if s.desc_size() > Self::MIN_BLOCK_GROUP_DESC_SIZE {
-            self.itable_unused_hi = (cnt >> 16) as u16;
-        }
+    pub fn set_itable_unused(&mut self, cnt: u32) {
+        self.itable_unused_lo = cnt as u16;
+        self.itable_unused_hi = (cnt >> 16) as u16;
     }
 
-    pub fn set_free_inodes_count(&mut self, s: &SuperBlock, cnt: u32) {
-        self.free_inodes_count_lo = ((cnt << 16) >> 16) as u16;
-        if s.desc_size() > Self::MIN_BLOCK_GROUP_DESC_SIZE {
-            self.free_inodes_count_hi = (cnt >> 16) as u16;
-        }
+    pub fn set_free_inodes_count(&mut self, cnt: u32) {
+        self.free_inodes_count_lo = cnt as u16;
+        self.free_inodes_count_hi = (cnt >> 16) as u16;
     }
 
     pub fn free_inodes_count(&self) -> u32 {
@@ -112,11 +100,7 @@ impl BlockGroupDesc {
     }
 
     pub fn get_free_blocks_count(&self) -> u64 {
-        let mut v = self.free_blocks_count_lo as u64;
-        if self.free_blocks_count_hi != 0 {
-            v |= (self.free_blocks_count_hi as u64) << 32;
-        }
-        v
+        ((self.free_blocks_count_hi as u64) << 32) | self.free_blocks_count_lo as u64
     }
 
     pub fn set_free_blocks_count(&mut self, cnt: u64) {
@@ -124,52 +108,18 @@ impl BlockGroupDesc {
         self.free_blocks_count_hi = (cnt >> 32) as u16;
     }
 
-    pub fn calc_inode_bitmap_csum(bitmap: &Bitmap, s: &SuperBlock) -> u32 {
-        let inodes_per_group = s.inodes_per_group();
-        let uuid = s.uuid();
-        let mut csum = ext4_crc32c(CRC32_INIT, &uuid, uuid.len() as u32);
-        csum = ext4_crc32c(csum, bitmap.as_raw(), (inodes_per_group + 7) / 8);
-        csum
-    }
-
-    pub fn calc_block_bitmap_csum(bitmap: &Bitmap, s: &SuperBlock) -> u32 {
-        let blocks_per_group = s.blocks_per_group();
-        let uuid = s.uuid();
-        let mut csum = ext4_crc32c(CRC32_INIT, &uuid, uuid.len() as u32);
-        csum = ext4_crc32c(csum, bitmap.as_raw(), (blocks_per_group / 8) as u32);
-        csum
-    }
-
-    pub fn set_inode_bitmap_csum(&mut self, s: &SuperBlock, bitmap: &Bitmap) {
-        let desc_size = s.desc_size();
-
-        let csum = Self::calc_inode_bitmap_csum(&bitmap, s);
-        let lo_csum = (csum & 0xFFFF).to_le();
-        let hi_csum = (csum >> 16).to_le();
-
-        if (s.features_read_only() & 0x400) >> 10 == 0 {
-            return;
-        }
-        self.inode_bitmap_csum_lo = lo_csum as u16;
-        if desc_size == Self::MAX_BLOCK_GROUP_DESC_SIZE {
-            self.inode_bitmap_csum_hi = hi_csum as u16;
-        }
+    pub fn set_inode_bitmap_csum(&mut self, uuid: &[u8], bitmap: &Bitmap) {
+        let mut csum = crc32(CRC32_INIT, &uuid);
+        csum = crc32(csum, &bitmap.as_bytes());
+        self.inode_bitmap_csum_lo = csum as u16;
+        self.block_bitmap_csum_hi = (csum >> 16) as u16;
     }
 
-    pub fn set_block_bitmap_csum(&mut self, s: &SuperBlock, bitmap: &Bitmap) {
-        let desc_size = s.desc_size();
-
-        let csum = Self::calc_block_bitmap_csum(&bitmap, s);
-        let lo_csum = (csum & 0xFFFF).to_le();
-        let hi_csum = (csum >> 16).to_le();
-
-        if (s.features_read_only() & 0x400) >> 10 == 0 {
-            return;
-        }
-        self.block_bitmap_csum_lo = lo_csum as u16;
-        if desc_size == Self::MAX_BLOCK_GROUP_DESC_SIZE {
-            self.block_bitmap_csum_hi = hi_csum as u16;
-        }
+    pub fn set_block_bitmap_csum(&mut self, uuid: &[u8], bitmap: &Bitmap) {
+        let mut csum = crc32(CRC32_INIT, &uuid);
+        csum = crc32(csum, &bitmap.as_bytes());
+        self.block_bitmap_csum_lo = csum as u16;
+        self.block_bitmap_csum_hi = (csum >> 16) as u16;
     }
 }
 
@@ -183,71 +133,14 @@ pub struct BlockGroupRef {
 }
 
 impl BlockGroupRef {
-    /// Load a block group descriptor from the disk
-    pub fn load_from_disk(
-        block_device: &dyn BlockDevice,
-        super_block: &SuperBlock,
-        block_group_id: BlockGroupId,
-    ) -> Self {
-        let (block_id, offset) = Self::disk_pos(super_block, block_group_id);
-        let block = block_device.read_block(block_id as PBlockId);
-        let desc = block.read_offset_as::<BlockGroupDesc>(offset);
-        Self {
-            id: block_group_id,
-            desc,
-        }
-    }
-
-    pub fn sync_to_disk_without_csum(
-        &self,
-        block_device: &dyn BlockDevice,
-        super_block: &SuperBlock,
-    ) {
-        let (block_id, offset) = Self::disk_pos(super_block, self.id);
-        let mut block = block_device.read_block(block_id as PBlockId);
-        block.write_offset_as(offset, &self.desc);
-        block_device.write_block(&block)
-    }
-
-    pub fn sync_to_disk_with_csum(
-        &mut self,
-        block_device: &dyn BlockDevice,
-        super_block: &SuperBlock,
-    ) {
-        self.set_checksum(super_block);
-        self.sync_to_disk_without_csum(block_device, super_block);
-    }
-
-    /// Find the position of a block group descriptor in the block device.
-    /// Return the block id and the offset within the block.
-    fn disk_pos(s: &SuperBlock, block_group_id: BlockGroupId) -> (PBlockId, usize) {
-        let desc_per_block = BLOCK_SIZE as u32 / s.desc_size() as u32;
-        let block_id = s.first_data_block() + block_group_id / desc_per_block + 1;
-        let offset = (block_group_id % desc_per_block) * s.desc_size() as u32;
-        (block_id as PBlockId, offset as usize)
+    pub fn new(id: BlockGroupId, desc: BlockGroupDesc) -> Self {
+        Self { id, desc }
     }
 
-    fn set_checksum(&mut self, super_block: &SuperBlock) {
-        let desc_size = super_block.desc_size();
-
-        // uuid checksum
-        let mut checksum = ext4_crc32c(
-            CRC32_INIT,
-            &super_block.uuid(),
-            super_block.uuid().len() as u32,
-        );
-
-        // bgid checksum
-        checksum = ext4_crc32c(checksum, &self.id.to_le_bytes(), 4);
-
-        // cast self to &[u8]
-        let self_bytes =
-            unsafe { core::slice::from_raw_parts(self as *const _ as *const u8, 0x40 as usize) };
-
-        // bg checksum
-        checksum = ext4_crc32c(checksum, self_bytes, desc_size as u32);
-
-        let crc = (checksum & 0xFFFF) as u16;
-        self.desc.checksum = crc;
+    pub fn set_checksum(&mut self, uuid: &[u8]) {
+        let mut checksum = crc32(CRC32_INIT, uuid);
+        checksum = crc32(checksum, &self.id.to_le_bytes());
+        checksum = crc32(checksum, &self.desc.to_bytes());
+        self.desc.checksum = checksum as u16;
     }
 }