alloc.rs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. use super::Ext4;
  2. use crate::constants::*;
  3. use crate::ext4_defs::*;
  4. use crate::prelude::*;
  5. impl Ext4 {
  6. /// Allocate a new data block for an inode, return the physical block number
  7. pub(super) fn alloc_block(
  8. &mut self,
  9. inode_ref: &mut Ext4InodeRef,
  10. goal: PBlockId,
  11. ) -> Result<PBlockId> {
  12. let bgid = goal / self.super_block.blocks_per_group() as u64;
  13. let idx_in_bg = goal % self.super_block.blocks_per_group() as u64;
  14. // Load block group descriptor
  15. let mut bg =
  16. Ext4BlockGroupDesc::load(self.block_device.clone(), &self.super_block, bgid as usize)
  17. .unwrap();
  18. let block_bmap_offset = bg.get_block_bitmap_block(&self.super_block) as usize * BLOCK_SIZE;
  19. // Load block bitmap
  20. let raw_bitmap = &mut self.block_device.read_offset(block_bmap_offset);
  21. let mut bitmap = Bitmap::new(raw_bitmap);
  22. // Find and first free block
  23. let fblock = bitmap
  24. .find_and_set_first_clear_bit(idx_in_bg as usize, 8 * BLOCK_SIZE)
  25. .ok_or(Ext4Error::new(ErrCode::ENOSPC))? as PBlockId;
  26. // Set block group checksum
  27. bg.set_block_bitmap_csum(&self.super_block, &bitmap);
  28. self.block_device
  29. .write_offset(block_bmap_offset, bitmap.as_raw());
  30. // Update superblock free blocks count
  31. let free_blocks = self.super_block.free_blocks_count();
  32. self.super_block.set_free_blocks_count(free_blocks); // TODO: why not - 1?
  33. self.super_block.sync_to_disk(self.block_device.clone());
  34. // Update inode blocks (different block size!) count
  35. let inode_blocks = inode_ref.inode.blocks_count();
  36. inode_ref.inode.set_blocks_count(inode_blocks as u32 + 8); // TODO: why + 8?
  37. self.write_back_inode_with_csum(inode_ref);
  38. // Update block group free blocks count
  39. let fb_cnt = bg.get_free_blocks_count();
  40. bg.set_free_blocks_count(fb_cnt - 1);
  41. bg.sync_to_disk_with_csum(self.block_device.clone(), bgid as usize, &self.super_block);
  42. info!("Alloc block {} ok", fblock);
  43. Ok(fblock)
  44. }
  45. /// Append a data block for an inode, return a pair of (logical block id, physical block id)
  46. pub(super) fn inode_append_block(
  47. &mut self,
  48. inode_ref: &mut Ext4InodeRef,
  49. ) -> Result<(LBlockId, PBlockId)> {
  50. let inode_size = inode_ref.inode.size();
  51. // The new logical block id
  52. let iblock = ((inode_size + BLOCK_SIZE as u64 - 1) / BLOCK_SIZE as u64) as u32;
  53. // Check the extent tree to get the physical block id
  54. let fblock = self.extent_get_pblock_create(inode_ref, iblock, 1)?;
  55. // Update the inode
  56. inode_ref.inode.set_size(inode_size + BLOCK_SIZE as u64);
  57. self.write_back_inode_with_csum(inode_ref);
  58. Ok((iblock, fblock))
  59. }
  60. /// Allocate(initialize) the root inode of the file system
  61. pub(super) fn alloc_root_inode(&mut self) -> Result<Ext4InodeRef> {
  62. let mut inode = Ext4Inode::default();
  63. inode.set_mode(0o777 | EXT4_INODE_MODE_DIRECTORY);
  64. inode.extent_init();
  65. if self.super_block.inode_size() > EXT4_GOOD_OLD_INODE_SIZE {
  66. inode.set_extra_isize(self.super_block.extra_size());
  67. }
  68. let mut root = Ext4InodeRef::new(EXT4_ROOT_INO, inode);
  69. let root_self = root.clone();
  70. // Add `.` and `..` entries
  71. self.dir_add_entry(&mut root, &root_self, ".")?;
  72. self.dir_add_entry(&mut root, &root_self, "..")?;
  73. root.inode.links_count += 2;
  74. self.write_back_inode_with_csum(&mut root);
  75. Ok(root)
  76. }
  77. /// Allocate a new inode in the file system, returning the inode and its number
  78. pub(super) fn alloc_inode(&mut self, filetype: FileType) -> Result<Ext4InodeRef> {
  79. // Allocate an inode
  80. let is_dir = filetype == FileType::Directory;
  81. let id = self.do_alloc_inode(is_dir)?;
  82. // Initialize the inode
  83. let mut inode = Ext4Inode::default();
  84. let mode = if filetype == FileType::Directory {
  85. 0o777 | EXT4_INODE_MODE_DIRECTORY
  86. } else if filetype == FileType::SymLink {
  87. 0o777 | EXT4_INODE_MODE_SOFTLINK
  88. } else {
  89. 0o666 | file_type2inode_mode(filetype)
  90. };
  91. inode.set_mode(mode);
  92. inode.extent_init();
  93. if self.super_block.inode_size() > EXT4_GOOD_OLD_INODE_SIZE {
  94. inode.set_extra_isize(self.super_block.extra_size());
  95. }
  96. let mut inode_ref = Ext4InodeRef::new(id, inode);
  97. // Sync the inode to disk
  98. self.write_back_inode_with_csum(&mut inode_ref);
  99. info!("Alloc inode {} ok", inode_ref.inode_id);
  100. Ok(inode_ref)
  101. }
  102. /// Allocate a new inode in the filesystem, returning its number.
  103. fn do_alloc_inode(&mut self, is_dir: bool) -> Result<InodeId> {
  104. let mut bgid = 0;
  105. let bg_count = self.super_block.block_groups_count();
  106. while bgid <= bg_count {
  107. // Load block group descriptor
  108. let mut bg = Ext4BlockGroupDesc::load(
  109. self.block_device.clone(),
  110. &self.super_block,
  111. bgid as usize,
  112. )
  113. .unwrap();
  114. // If there are no free inodes in this block group, try the next one
  115. if bg.free_inodes_count() == 0 {
  116. bgid += 1;
  117. continue;
  118. }
  119. // Load inode bitmap
  120. let inode_bitmap_block = bg.get_inode_bitmap_block(&self.super_block);
  121. let mut raw_data = self
  122. .block_device
  123. .read_offset(inode_bitmap_block as usize * BLOCK_SIZE);
  124. let inode_count = self.super_block.inode_count_in_group(bgid);
  125. let bitmap_size: u32 = inode_count / 0x8;
  126. let mut bitmap_data = &mut raw_data[..bitmap_size as usize];
  127. let mut bitmap = Bitmap::new(&mut bitmap_data);
  128. // Find a free inode
  129. let idx_in_bg = bitmap
  130. .find_and_set_first_clear_bit(0, inode_count as usize)
  131. .unwrap() as u32;
  132. // Update bitmap in disk
  133. self.block_device
  134. .write_offset(inode_bitmap_block as usize * BLOCK_SIZE, &bitmap.as_raw());
  135. bg.set_inode_bitmap_csum(&self.super_block, &bitmap);
  136. // Modify filesystem counters
  137. let free_inodes = bg.free_inodes_count() - 1;
  138. bg.set_free_inodes_count(&self.super_block, free_inodes);
  139. // Increment used directories counter
  140. if is_dir {
  141. let used_dirs = bg.get_used_dirs_count(&self.super_block) - 1;
  142. bg.set_used_dirs_count(&self.super_block, used_dirs);
  143. }
  144. // Decrease unused inodes count
  145. let mut unused = bg.get_itable_unused(&self.super_block);
  146. let free = inode_count - unused as u32;
  147. if idx_in_bg >= free {
  148. unused = inode_count - (idx_in_bg + 1);
  149. bg.set_itable_unused(&self.super_block, unused);
  150. }
  151. bg.sync_to_disk_with_csum(self.block_device.clone(), bgid as usize, &self.super_block);
  152. // Update superblock
  153. self.super_block.decrease_free_inodes_count();
  154. self.super_block.sync_to_disk(self.block_device.clone());
  155. // Compute the absolute i-node number
  156. let inodes_per_group = self.super_block.inodes_per_group();
  157. let inode_id = bgid * inodes_per_group + (idx_in_bg + 1);
  158. return Ok(inode_id);
  159. }
  160. log::info!("no free inode");
  161. Err(Ext4Error::new(ErrCode::ENOSPC))
  162. }
  163. }