dir.rs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. use super::Ext4;
  2. use crate::constants::*;
  3. use crate::ext4_defs::*;
  4. use crate::prelude::*;
  5. impl Ext4 {
  6. /// Find a directory entry that matches a given name under a parent directory
  7. pub(super) fn dir_find_entry(&self, parent: &InodeRef, name: &str) -> Result<DirEntry> {
  8. info!("Dir find entry {} under parent {}", name, parent.id);
  9. let inode_size: u32 = parent.inode.size;
  10. let total_blocks: u32 = inode_size / BLOCK_SIZE as u32;
  11. let mut iblock: LBlockId = 0;
  12. while iblock < total_blocks {
  13. // Get the fs block id
  14. let fblock = self.extent_get_pblock(parent, iblock)?;
  15. // Load block from disk
  16. let block = self.block_device.read_block(fblock);
  17. // Find the entry in block
  18. let res = Self::find_entry_in_block(&block, name);
  19. if let Ok(r) = res {
  20. return Ok(r);
  21. }
  22. iblock += 1
  23. }
  24. Err(Ext4Error::new(ErrCode::ENOENT))
  25. }
  26. /// Find a directory entry that matches a given name in a given block
  27. fn find_entry_in_block(block: &Block, name: &str) -> Result<DirEntry> {
  28. info!("Dir find entry {} in block {}", name, block.block_id);
  29. let mut offset = 0;
  30. while offset < BLOCK_SIZE {
  31. let de = DirEntry::from_bytes(&block.data[offset..]);
  32. debug!("Dir entry: {} {:?}", de.rec_len(), de.name());
  33. offset += de.rec_len() as usize;
  34. // Unused dir entry
  35. if de.unused() {
  36. continue;
  37. }
  38. // Compare name
  39. if de.compare_name(name) {
  40. return Ok(de);
  41. }
  42. }
  43. Err(Ext4Error::new(ErrCode::ENOENT))
  44. }
  45. /// Add an entry to a directory
  46. pub(super) fn dir_add_entry(
  47. &mut self,
  48. parent: &mut InodeRef,
  49. child: &InodeRef,
  50. path: &str,
  51. ) -> Result<()> {
  52. info!(
  53. "Dir add entry: parent {}, child {}, path {}",
  54. parent.id, child.id, path
  55. );
  56. let inode_size = parent.inode.size();
  57. let total_blocks = inode_size as u32 / BLOCK_SIZE as u32;
  58. // Try finding a block with enough space
  59. let mut iblock: LBlockId = 0;
  60. while iblock < total_blocks {
  61. // Get the parent physical block id, create if not exist
  62. let fblock = self.extent_get_pblock_create(parent, iblock, 1)?;
  63. // Load the parent block from disk
  64. let mut block = self.block_device.read_block(fblock);
  65. // Try inserting the entry to parent block
  66. if self.insert_entry_to_old_block(&mut block, child, path) {
  67. return Ok(());
  68. }
  69. // Current block has no enough space
  70. iblock += 1;
  71. }
  72. // No free block found - needed to allocate a new data block
  73. // Append a new data block
  74. let (_, fblock) = self.inode_append_block(parent)?;
  75. // Load new block
  76. let mut new_block = self.block_device.read_block(fblock);
  77. // Write the entry to block
  78. self.insert_entry_to_new_block(&mut new_block, child, path);
  79. Ok(())
  80. }
  81. /// Insert a directory entry of a child inode into a new parent block.
  82. /// A new block must have enough space
  83. fn insert_entry_to_new_block(&self, dst_blk: &mut Block, child: &InodeRef, name: &str) {
  84. // Set the entry
  85. let rec_len = BLOCK_SIZE - size_of::<DirEntryTail>();
  86. let new_entry = DirEntry::new(
  87. child.id,
  88. rec_len as u16,
  89. name,
  90. inode_mode2file_type(child.inode.mode()),
  91. );
  92. // Write entry to block
  93. new_entry.copy_to_byte_slice(&mut dst_blk.data, 0);
  94. // Set tail
  95. let mut tail = DirEntryTail::default();
  96. tail.rec_len = size_of::<DirEntryTail>() as u16;
  97. tail.reserved_ft = 0xDE;
  98. tail.set_csum(&self.super_block, &new_entry, &dst_blk.data[..]);
  99. // Copy tail to block
  100. let tail_offset = BLOCK_SIZE - size_of::<DirEntryTail>();
  101. dst_blk.write_offset_as(tail_offset, &tail);
  102. // Sync block to disk
  103. dst_blk.sync_to_disk(self.block_device.clone());
  104. }
  105. /// Try insert a directory entry of child inode into a parent block.
  106. /// Return true if the entry is successfully inserted.
  107. fn insert_entry_to_old_block(&self, dst_blk: &mut Block, child: &InodeRef, name: &str) -> bool {
  108. let required_size = DirEntry::required_size(name.len());
  109. let mut offset = 0;
  110. while offset < dst_blk.data.len() {
  111. let mut de = DirEntry::from_bytes(&dst_blk.data[offset..]);
  112. let rec_len = de.rec_len() as usize;
  113. // Try splitting dir entry
  114. // The size that `de` actually uses
  115. let used_size = de.used_size();
  116. // The rest size
  117. let free_size = rec_len - used_size;
  118. // Compare size
  119. if free_size < required_size {
  120. // No enough space, try next dir ent
  121. offset = offset + rec_len;
  122. continue;
  123. }
  124. // Has enough space
  125. // Update the old entry
  126. de.set_rec_len(used_size as u16);
  127. de.copy_to_byte_slice(&mut dst_blk.data, offset);
  128. // Insert the new entry
  129. let new_entry = DirEntry::new(
  130. child.id,
  131. free_size as u16,
  132. name,
  133. inode_mode2file_type(child.inode.mode()),
  134. );
  135. new_entry.copy_to_byte_slice(&mut dst_blk.data, offset + used_size);
  136. // Set tail csum
  137. let tail_offset = BLOCK_SIZE - size_of::<DirEntryTail>();
  138. let mut tail = dst_blk.read_offset_as::<DirEntryTail>(tail_offset);
  139. tail.set_csum(&self.super_block, &de, &dst_blk.data[offset..]);
  140. // Write tail to blk_data
  141. dst_blk.write_offset_as(tail_offset, &tail);
  142. // Sync to disk
  143. dst_blk.sync_to_disk(self.block_device.clone());
  144. return true;
  145. }
  146. false
  147. }
  148. /// Create a new directory. `path` is the absolute path of the new directory.
  149. pub fn mkdir(&mut self, path: &str) -> Result<()> {
  150. // get open flags
  151. let iflags = OpenFlags::from_str("w").unwrap();
  152. self.generic_open(path, iflags, FileType::Directory, &self.read_root_inode())
  153. .map(|_| {
  154. info!("ext4_dir_mk: {} ok", path);
  155. })
  156. }
  157. }