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