alloc.rs 6.9 KB

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