Explorar el Código

feat: dir remove entry

liujingx hace 11 meses
padre
commit
3495d06705
Se han modificado 5 ficheros con 115 adiciones y 54 borrados
  1. 64 28
      src/ext4/dir.rs
  2. 5 3
      src/ext4/file.rs
  3. 4 4
      src/ext4_defs/block_device.rs
  4. 41 18
      src/ext4_defs/dir_entry.rs
  5. 1 1
      src/ext4_error.rs

+ 64 - 28
src/ext4/dir.rs

@@ -21,27 +21,7 @@ impl Ext4 {
             if let Ok(r) = res {
                 return Ok(r);
             }
-            iblock += 1
-        }
-        Err(Ext4Error::new(ErrCode::ENOENT))
-    }
-
-    /// Find a directory entry that matches a given name in a given block
-    fn find_entry_in_block(block: &Block, name: &str) -> Result<DirEntry> {
-        info!("Dir find entry {} in block {}", name, block.block_id);
-        let mut offset = 0;
-        while offset < BLOCK_SIZE {
-            let de = DirEntry::from_bytes(&block.data[offset..]);
-            debug!("Dir entry: {} {:?}", de.rec_len(), de.name());
-            offset += de.rec_len() as usize;
-            // Unused dir entry
-            if de.unused() {
-                continue;
-            }
-            // Compare name
-            if de.compare_name(name) {
-                return Ok(de);
-            }
+            iblock += 1;
         }
         Err(Ext4Error::new(ErrCode::ENOENT))
     }
@@ -63,8 +43,8 @@ impl Ext4 {
         // Try finding a block with enough space
         let mut iblock: LBlockId = 0;
         while iblock < total_blocks {
-            // Get the parent physical block id, create if not exist
-            let fblock = self.extent_get_pblock_create(parent, iblock, 1)?;
+            // Get the parent physical block id
+            let fblock = self.extent_get_pblock(parent, iblock)?;
             // Load the parent block from disk
             let mut block = self.block_device.read_block(fblock);
             // Try inserting the entry to parent block
@@ -86,6 +66,62 @@ impl Ext4 {
         Ok(())
     }
 
+    /// Remove a entry from a directory
+    pub(super) fn dir_remove_entry(&mut self, parent: &mut InodeRef, path: &str) -> Result<()> {
+        info!("Dir remove entry: parent {}, path {}", parent.id, path);
+        let inode_size = parent.inode.size();
+        let total_blocks = inode_size as u32 / BLOCK_SIZE as u32;
+
+        // Check each block
+        let mut iblock: LBlockId = 0;
+        while iblock < total_blocks {
+            // Get the parent physical block id
+            let fblock = self.extent_get_pblock(parent, iblock)?;
+            // Load the block from disk
+            let mut block = self.block_device.read_block(fblock);
+            // Try removing the entry
+            if self.remove_entry_from_block(&mut block, path) {
+                return Ok(());
+            }
+            // Current block has no enough space
+            iblock += 1;
+        }
+
+        // Not found the target entry
+        Err(Ext4Error::new(ErrCode::ENOENT))
+    }
+
+    /// Find a directory entry that matches a given name in a given block
+    fn find_entry_in_block(block: &Block, name: &str) -> Result<DirEntry> {
+        info!("Dir find entry {} in block {}", name, block.block_id);
+        let mut offset = 0;
+        while offset < BLOCK_SIZE {
+            let de: DirEntry = block.read_offset_as(offset);
+            debug!("Dir entry: {} {:?}", de.rec_len(), de.name());
+            offset += de.rec_len() as usize;
+            if !de.unused() && de.compare_name(name) {
+                return Ok(de);
+            }
+        }
+        Err(Ext4Error::new(ErrCode::ENOENT))
+    }
+
+    /// Remove a directory entry that matches a given name from a given block
+    fn remove_entry_from_block(&self, block: &mut Block, name: &str) -> bool {
+        info!("Dir remove entry {} from block {}", name, block.block_id);
+        let mut offset = 0;
+        while offset < BLOCK_SIZE {
+            let mut de: DirEntry = block.read_offset_as(offset);
+            offset += de.rec_len() as usize;
+            if !de.unused() && de.compare_name(name) {
+                // Mark the target entry as unused
+                de.set_unused();
+                return true;
+            }
+        }
+        false
+    }
+
     /// Insert a directory entry of a child inode into a new parent block.
     /// A new block must have enough space
     fn insert_entry_to_new_block(&self, dst_blk: &mut Block, child: &InodeRef, name: &str) {
@@ -98,7 +134,7 @@ impl Ext4 {
             inode_mode2file_type(child.inode.mode()),
         );
         // Write entry to block
-        new_entry.copy_to_byte_slice(&mut dst_blk.data, 0);
+        dst_blk.write_offset_as(0, &new_entry);
 
         // Set tail
         let mut tail = DirEntryTail::default();
@@ -120,7 +156,7 @@ impl Ext4 {
         let mut offset = 0;
 
         while offset < dst_blk.data.len() {
-            let mut de = DirEntry::from_bytes(&dst_blk.data[offset..]);
+            let mut de: DirEntry = dst_blk.read_offset_as(offset);
             let rec_len = de.rec_len() as usize;
 
             // Try splitting dir entry
@@ -128,7 +164,7 @@ impl Ext4 {
             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
@@ -138,7 +174,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.data, offset);
+            dst_blk.write_offset_as(offset, &de);
             // Insert the new entry
             let new_entry = DirEntry::new(
                 child.id,
@@ -146,7 +182,7 @@ impl Ext4 {
                 name,
                 inode_mode2file_type(child.inode.mode()),
             );
-            new_entry.copy_to_byte_slice(&mut dst_blk.data, offset + used_size);
+            dst_blk.write_offset_as(offset + used_size, &new_entry);
 
             // Set tail csum
             let tail_offset = BLOCK_SIZE - size_of::<DirEntryTail>();

+ 5 - 3
src/ext4/file.rs

@@ -45,7 +45,7 @@ impl Ext4 {
                     }?;
                     // Link the new inode
                     self.link(&mut parent, &mut child, path)
-                        .map_err(|_| Ext4Error::with_message(ErrCode::ELINKFIAL, "link fail"))?;
+                        .map_err(|_| Ext4Error::with_message(ErrCode::ELINKFAIL, "link fail"))?;
                     // Write back parent and child
                     self.write_inode_with_csum(&mut parent);
                     self.write_inode_with_csum(&mut child);
@@ -169,7 +169,9 @@ impl Ext4 {
         Ok(())
     }
 
-    pub fn ext4_file_remove(&self, _path: &str) -> Result<usize> {
-        return_errno_with_message!(ErrCode::ENOTSUP, "not support");
+    pub fn remove_file(&self, path: &str) -> Result<()> {
+        
+        
+        Ok(())
     }
 }

+ 4 - 4
src/ext4_defs/block_device.rs

@@ -8,7 +8,7 @@ pub trait AsBytes
 where
     Self: Sized,
 {
-    /// Default implementation that interprets the object as a byte array.
+    /// Default implementation that deserializes the object from a byte array.
     fn from_bytes(bytes: &[u8]) -> Self {
         unsafe { core::ptr::read(bytes.as_ptr() as *const Self) }
     }
@@ -41,17 +41,17 @@ impl Block {
         Self { block_id, data }
     }
 
-    /// Read `size` bytes at `offset` from block data
+    /// Read `size` bytes from `offset` in 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`
+    /// Read bytes from `offset` in 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>()])
+        T::from_bytes(&self.data[offset..])
     }
 
     /// Write block data to `offset` with `size`

+ 41 - 18
src/ext4_defs/dir_entry.rs

@@ -58,6 +58,36 @@ impl Default for DirEntry {
     }
 }
 
+/// The actual size of the directory entry is determined by `name_len`.
+/// So we need to implement `AsBytes` methods specifically for `DirEntry`.
+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 {
@@ -75,11 +105,6 @@ impl DirEntry {
         }
     }
 
-    /// Load a directory entry from bytes
-    pub fn from_bytes(bytes: &[u8]) -> Self {
-        unsafe { core::ptr::read(bytes.as_ptr() as *const _) }
-    }
-
     pub fn name(&self) -> core::result::Result<String, FromUtf8Error> {
         let name_len = self.name_len as usize;
         let name = &self.name[..name_len];
@@ -117,12 +142,17 @@ impl DirEntry {
         self.inode == 0
     }
 
+    /// Set a directory entry as unused
+    pub fn set_unused(&mut self) {
+        self.inode = 0
+    }
+
     /// Set the dir entry's inode type given the corresponding inode mode
     pub fn set_entry_type(&mut self, inode_mode: u16) {
         self.inner.inode_type = inode_mode2file_type(inode_mode);
     }
 
-    /// Get the required size to save this directory entry, 4-byte aligned
+    /// 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
@@ -149,15 +179,6 @@ impl DirEntry {
         csum = ext4_crc32c(csum, &data[..], 0xff4);
         csum
     }
-
-    pub fn copy_to_byte_slice(&self, slice: &mut [u8], offset: usize) {
-        let de_ptr = self as *const DirEntry as *const u8;
-        let slice_ptr = slice as *mut [u8] as *mut u8;
-        let count = self.used_size();
-        unsafe {
-            core::ptr::copy_nonoverlapping(de_ptr, slice_ptr.add(offset), count);
-        }
-    }
 }
 
 #[repr(C)]
@@ -182,11 +203,13 @@ impl DirEntryTail {
 #[repr(C)]
 pub struct FakeDirEntry {
     inode: u32,
-    entry_length: u16,
-    name_length: u8,
-    inode_type: u8,
+    rec_len: u16,
+    name_len: u8,
+    inode_type: FileType,
 }
 
+impl AsBytes for FakeDirEntry {}
+
 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
 #[repr(u8)]
 pub enum FileType {

+ 1 - 1
src/ext4_error.rs

@@ -25,7 +25,7 @@ pub enum ErrCode {
     ENOTEMPTY = 39,  /* Directory not empty */
     ENODATA = 61,    /* No data available */
     ENOTSUP = 95,    /* Not supported */
-    ELINKFIAL = 97,  /* Link failed */
+    ELINKFAIL = 97,  /* Link failed */
     EALLOCFIAL = 98, /* Inode alloc failed */
 }