dir.rs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. use super::Ext4;
  2. use crate::constants::*;
  3. use crate::ext4_defs::*;
  4. use crate::prelude::*;
  5. use crate::return_error;
  6. impl Ext4 {
  7. /// Find a directory entry that matches a given name under a parent directory
  8. pub(super) fn dir_find_entry(&self, dir: &InodeRef, name: &str) -> Result<DirEntry> {
  9. trace!("Dir find entry: dir {}, name {}", dir.id, name);
  10. let total_blocks: u32 = dir.inode.block_count() as u32;
  11. let mut iblock: LBlockId = 0;
  12. while iblock < total_blocks {
  13. // Get the fs block id
  14. let fblock = self.extent_query(dir, iblock)?;
  15. // Load block from disk
  16. let block = self.read_block(fblock);
  17. // Find the entry in block
  18. let res = Self::find_entry_in_block(&block, name);
  19. if let Some(r) = res {
  20. return Ok(r);
  21. }
  22. iblock += 1;
  23. }
  24. return_error!(
  25. ErrCode::ENOENT,
  26. "Directory entry not found: dir {}, name {}",
  27. dir.id,
  28. name
  29. );
  30. }
  31. /// Add an entry to a directory, memory consistency guaranteed
  32. pub(super) fn dir_add_entry(
  33. &self,
  34. dir: &mut InodeRef,
  35. child: &InodeRef,
  36. name: &str,
  37. ) -> Result<()> {
  38. trace!(
  39. "Dir add entry: dir {}, child {}, name {}",
  40. dir.id,
  41. child.id,
  42. name
  43. );
  44. let total_blocks: u32 = dir.inode.block_count() as u32;
  45. // Try finding a block with enough space
  46. let mut iblock: LBlockId = 0;
  47. while iblock < total_blocks {
  48. // Get the parent physical block id
  49. let fblock = self.extent_query(dir, iblock).unwrap();
  50. // Load the parent block from disk
  51. let mut block = self.read_block(fblock);
  52. // Try inserting the entry to parent block
  53. if self.insert_entry_to_old_block(dir, child, name, &mut block) {
  54. return Ok(());
  55. }
  56. // Current block has no enough space
  57. iblock += 1;
  58. }
  59. // No free block found - needed to allocate a new data block
  60. // Append a new data block
  61. let (_, fblock) = self.inode_append_block(dir)?;
  62. // Load new block
  63. let mut new_block = self.read_block(fblock);
  64. // Write the entry to block
  65. self.insert_entry_to_new_block(dir, child, name, &mut new_block);
  66. // Update inode size
  67. dir.inode.set_size(dir.inode.size() + BLOCK_SIZE as u64);
  68. Ok(())
  69. }
  70. /// Remove a entry from a directory
  71. pub(super) fn dir_remove_entry(&self, dir: &InodeRef, name: &str) -> Result<()> {
  72. trace!("Dir remove entry: dir {}, name {}", dir.id, name);
  73. let total_blocks: u32 = dir.inode.block_count() as u32;
  74. // Check each block
  75. let mut iblock: LBlockId = 0;
  76. while iblock < total_blocks {
  77. // Get the parent physical block id
  78. let fblock = self.extent_query(dir, iblock).unwrap();
  79. // Load the block from disk
  80. let mut block = self.read_block(fblock);
  81. // Try removing the entry
  82. if Self::remove_entry_from_block(&mut block, name) {
  83. self.write_block(&block);
  84. return Ok(());
  85. }
  86. // Current block has no enough space
  87. iblock += 1;
  88. }
  89. // Not found the target entry
  90. return_error!(
  91. ErrCode::ENOENT,
  92. "Directory entry not found: dir {}, name {}",
  93. dir.id,
  94. name
  95. );
  96. }
  97. /// Get all entries under a directory
  98. pub(super) fn dir_get_all_entries(&self, dir: &InodeRef) -> Vec<DirEntry> {
  99. let total_blocks = dir.inode.block_count() as u32;
  100. let mut entries: Vec<DirEntry> = Vec::new();
  101. let mut iblock: LBlockId = 0;
  102. while iblock < total_blocks {
  103. // Get the fs block id
  104. let fblock = self.extent_query(dir, iblock).unwrap();
  105. // Load block from disk
  106. let block = self.read_block(fblock);
  107. // Get all entries from block
  108. Self::get_all_entries_from_block(&block, &mut entries);
  109. iblock += 1;
  110. }
  111. entries
  112. }
  113. /// Find a directory entry that matches a given name in a given block
  114. fn find_entry_in_block(block: &Block, name: &str) -> Option<DirEntry> {
  115. let mut offset = 0;
  116. while offset < BLOCK_SIZE {
  117. let de: DirEntry = block.read_offset_as(offset);
  118. if !de.unused() && de.compare_name(name) {
  119. return Some(de);
  120. }
  121. offset += de.rec_len() as usize;
  122. }
  123. None
  124. }
  125. /// Remove a directory entry that matches a given name from a given block
  126. fn remove_entry_from_block(block: &mut Block, name: &str) -> bool {
  127. let mut offset = 0;
  128. while offset < BLOCK_SIZE {
  129. let mut de: DirEntry = block.read_offset_as(offset);
  130. if !de.unused() && de.compare_name(name) {
  131. // Mark the target entry as unused
  132. de.set_unused();
  133. block.write_offset_as(offset, &de);
  134. return true;
  135. }
  136. offset += de.rec_len() as usize;
  137. }
  138. false
  139. }
  140. /// Get all directory entries from a given block
  141. fn get_all_entries_from_block(block: &Block, entries: &mut Vec<DirEntry>) {
  142. let mut offset = 0;
  143. while offset < BLOCK_SIZE {
  144. let de: DirEntry = block.read_offset_as(offset);
  145. offset += de.rec_len() as usize;
  146. if !de.unused() {
  147. trace!("Dir entry: {:?} {}", de.name(), de.inode());
  148. entries.push(de);
  149. }
  150. }
  151. }
  152. /// Insert a directory entry of a child inode into a new parent block.
  153. /// A new block must have enough space
  154. fn insert_entry_to_new_block(
  155. &self,
  156. dir: &InodeRef,
  157. child: &InodeRef,
  158. name: &str,
  159. dst_blk: &mut Block,
  160. ) {
  161. // Set the entry
  162. let rec_len = BLOCK_SIZE - size_of::<DirEntryTail>();
  163. let new_entry = DirEntry::new(child.id, rec_len as u16, name, child.inode.file_type());
  164. // Write entry to block
  165. dst_blk.write_offset_as(0, &new_entry);
  166. // Set tail
  167. let mut tail = DirEntryTail::new();
  168. tail.set_csum(
  169. &self.read_super_block().uuid(),
  170. dir.id,
  171. dir.inode.generation(),
  172. &dst_blk,
  173. );
  174. // Copy tail to block
  175. let tail_offset = BLOCK_SIZE - size_of::<DirEntryTail>();
  176. dst_blk.write_offset_as(tail_offset, &tail);
  177. // Sync block to disk
  178. self.write_block(&dst_blk);
  179. }
  180. /// Try insert a directory entry of child inode into a parent block.
  181. /// Return true if the entry is successfully inserted.
  182. fn insert_entry_to_old_block(
  183. &self,
  184. dir: &InodeRef,
  185. child: &InodeRef,
  186. name: &str,
  187. dst_blk: &mut Block,
  188. ) -> bool {
  189. let required_size = DirEntry::required_size(name.len());
  190. let mut offset = 0;
  191. while offset < dst_blk.data.len() {
  192. let mut de: DirEntry = dst_blk.read_offset_as(offset);
  193. let rec_len = de.rec_len() as usize;
  194. // Try splitting dir entry
  195. // The size that `de` actually uses
  196. let used_size = de.used_size();
  197. // The rest size
  198. let free_size = rec_len - used_size;
  199. // Compare size
  200. if free_size < required_size {
  201. // No enough space, try next dir ent
  202. offset = offset + rec_len;
  203. continue;
  204. }
  205. // Has enough space
  206. // Update the old entry
  207. de.set_rec_len(used_size as u16);
  208. dst_blk.write_offset_as(offset, &de);
  209. // Insert the new entry
  210. let new_entry =
  211. DirEntry::new(child.id, free_size as u16, name, child.inode.file_type());
  212. dst_blk.write_offset_as(offset + used_size, &new_entry);
  213. // Set tail csum
  214. let tail_offset = BLOCK_SIZE - size_of::<DirEntryTail>();
  215. let mut tail = dst_blk.read_offset_as::<DirEntryTail>(tail_offset);
  216. tail.set_csum(
  217. &self.read_super_block().uuid(),
  218. dir.id,
  219. dir.inode.generation(),
  220. &dst_blk,
  221. );
  222. // Write tail to blk_data
  223. dst_blk.write_offset_as(tail_offset, &tail);
  224. // Sync to disk
  225. self.write_block(&dst_blk);
  226. return true;
  227. }
  228. false
  229. }
  230. }