xref: /aosp_15_r20/external/crosvm/ext2/src/blockgroup.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2024 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker //! Defines structs for metadata of block groups.
6*bb4ee6a4SAndroid Build Coastguard Worker 
7*bb4ee6a4SAndroid Build Coastguard Worker use std::collections::BTreeMap;
8*bb4ee6a4SAndroid Build Coastguard Worker 
9*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::Result;
10*bb4ee6a4SAndroid Build Coastguard Worker use zerocopy::AsBytes;
11*bb4ee6a4SAndroid Build Coastguard Worker use zerocopy_derive::FromBytes;
12*bb4ee6a4SAndroid Build Coastguard Worker use zerocopy_derive::FromZeroes;
13*bb4ee6a4SAndroid Build Coastguard Worker 
14*bb4ee6a4SAndroid Build Coastguard Worker use crate::arena::Arena;
15*bb4ee6a4SAndroid Build Coastguard Worker use crate::arena::BlockId;
16*bb4ee6a4SAndroid Build Coastguard Worker use crate::bitmap::BitMap;
17*bb4ee6a4SAndroid Build Coastguard Worker use crate::inode::Inode;
18*bb4ee6a4SAndroid Build Coastguard Worker use crate::inode::InodeNum;
19*bb4ee6a4SAndroid Build Coastguard Worker use crate::superblock::SuperBlock;
20*bb4ee6a4SAndroid Build Coastguard Worker 
21*bb4ee6a4SAndroid Build Coastguard Worker /// The size of a block in bytes.
22*bb4ee6a4SAndroid Build Coastguard Worker /// We only support 4K-byte blocks.
23*bb4ee6a4SAndroid Build Coastguard Worker pub const BLOCK_SIZE: usize = 4096;
24*bb4ee6a4SAndroid Build Coastguard Worker 
25*bb4ee6a4SAndroid Build Coastguard Worker /// A block group descriptor.
26*bb4ee6a4SAndroid Build Coastguard Worker ///
27*bb4ee6a4SAndroid Build Coastguard Worker /// See [the specification](https://www.nongnu.org/ext2-doc/ext2.html#block-group-descriptor-table) for the details.
28*bb4ee6a4SAndroid Build Coastguard Worker #[repr(C)]
29*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Default, Debug, Copy, Clone, FromZeroes, FromBytes, AsBytes)]
30*bb4ee6a4SAndroid Build Coastguard Worker pub(crate) struct BlockGroupDescriptor {
31*bb4ee6a4SAndroid Build Coastguard Worker     /// Index of the first block of the block bitmap.
32*bb4ee6a4SAndroid Build Coastguard Worker     pub block_bitmap: u32,
33*bb4ee6a4SAndroid Build Coastguard Worker     /// Index of the first block of the inode bitmap.
34*bb4ee6a4SAndroid Build Coastguard Worker     pub inode_bitmap: u32,
35*bb4ee6a4SAndroid Build Coastguard Worker     /// Index of the first block of the inode table.
36*bb4ee6a4SAndroid Build Coastguard Worker     pub inode_table: u32,
37*bb4ee6a4SAndroid Build Coastguard Worker     /// Number of free blocks.
38*bb4ee6a4SAndroid Build Coastguard Worker     pub free_blocks_count: u16,
39*bb4ee6a4SAndroid Build Coastguard Worker     /// Number of free inodes.
40*bb4ee6a4SAndroid Build Coastguard Worker     pub free_inodes_count: u16,
41*bb4ee6a4SAndroid Build Coastguard Worker     /// Number of directories.
42*bb4ee6a4SAndroid Build Coastguard Worker     pub used_dirs_count: u16,
43*bb4ee6a4SAndroid Build Coastguard Worker     pad: u16,
44*bb4ee6a4SAndroid Build Coastguard Worker     reserved: [u8; 12],
45*bb4ee6a4SAndroid Build Coastguard Worker }
46*bb4ee6a4SAndroid Build Coastguard Worker 
47*bb4ee6a4SAndroid Build Coastguard Worker pub(crate) struct GroupMetaData<'a> {
48*bb4ee6a4SAndroid Build Coastguard Worker     pub group_desc: &'a mut BlockGroupDescriptor,
49*bb4ee6a4SAndroid Build Coastguard Worker     pub block_bitmap: BitMap<'a>,
50*bb4ee6a4SAndroid Build Coastguard Worker     pub inode_bitmap: BitMap<'a>,
51*bb4ee6a4SAndroid Build Coastguard Worker 
52*bb4ee6a4SAndroid Build Coastguard Worker     pub inode_table: BTreeMap<InodeNum, &'a mut Inode>,
53*bb4ee6a4SAndroid Build Coastguard Worker 
54*bb4ee6a4SAndroid Build Coastguard Worker     pub first_free_block: u32,
55*bb4ee6a4SAndroid Build Coastguard Worker     pub first_free_inode: u32,
56*bb4ee6a4SAndroid Build Coastguard Worker }
57*bb4ee6a4SAndroid Build Coastguard Worker 
58*bb4ee6a4SAndroid Build Coastguard Worker impl<'a> GroupMetaData<'a> {
59*bb4ee6a4SAndroid Build Coastguard Worker     // Write GroupMetaData to the first block group.
60*bb4ee6a4SAndroid Build Coastguard Worker     // This data need to be copied to other block gropups' descriptor tables.
new(arena: &'a Arena<'a>, sb: &mut SuperBlock, group_id: u16) -> Result<Self>61*bb4ee6a4SAndroid Build Coastguard Worker     pub fn new(arena: &'a Arena<'a>, sb: &mut SuperBlock, group_id: u16) -> Result<Self> {
62*bb4ee6a4SAndroid Build Coastguard Worker         let gd_size = std::mem::size_of::<BlockGroupDescriptor>() as u32;
63*bb4ee6a4SAndroid Build Coastguard Worker         let num_blocks_for_gds = (gd_size * sb.num_groups() as u32).div_ceil(BLOCK_SIZE as u32);
64*bb4ee6a4SAndroid Build Coastguard Worker 
65*bb4ee6a4SAndroid Build Coastguard Worker         let inodes_per_block = BLOCK_SIZE as u64 / sb.inode_size as u64;
66*bb4ee6a4SAndroid Build Coastguard Worker         let num_blocks_for_inode_table =
67*bb4ee6a4SAndroid Build Coastguard Worker             (sb.inodes_per_group as usize).div_ceil(inodes_per_block as usize);
68*bb4ee6a4SAndroid Build Coastguard Worker 
69*bb4ee6a4SAndroid Build Coastguard Worker         // Allocate a block group descriptor at Block 1.
70*bb4ee6a4SAndroid Build Coastguard Worker         let group_desc = arena.allocate::<BlockGroupDescriptor>(
71*bb4ee6a4SAndroid Build Coastguard Worker             BlockId::from(1),
72*bb4ee6a4SAndroid Build Coastguard Worker             std::mem::size_of::<BlockGroupDescriptor>() * group_id as usize,
73*bb4ee6a4SAndroid Build Coastguard Worker         )?;
74*bb4ee6a4SAndroid Build Coastguard Worker 
75*bb4ee6a4SAndroid Build Coastguard Worker         // First blocks for block_bitmap, inode_bitmap, and inode_table.
76*bb4ee6a4SAndroid Build Coastguard Worker         let super_block_id = group_id as u32 * sb.blocks_per_group;
77*bb4ee6a4SAndroid Build Coastguard Worker         let group_desc_id = super_block_id + 1;
78*bb4ee6a4SAndroid Build Coastguard Worker         group_desc.block_bitmap = group_desc_id + num_blocks_for_gds;
79*bb4ee6a4SAndroid Build Coastguard Worker         group_desc.inode_bitmap = group_desc.block_bitmap + 1;
80*bb4ee6a4SAndroid Build Coastguard Worker         group_desc.inode_table = group_desc.inode_bitmap + 1;
81*bb4ee6a4SAndroid Build Coastguard Worker 
82*bb4ee6a4SAndroid Build Coastguard Worker         // First free block is the one after inode table.
83*bb4ee6a4SAndroid Build Coastguard Worker         let first_free_block = group_desc.inode_table + num_blocks_for_inode_table as u32;
84*bb4ee6a4SAndroid Build Coastguard Worker         // Free blocks are from `first_free_block` to `blocks_per_group`, inclusive.
85*bb4ee6a4SAndroid Build Coastguard Worker         group_desc.free_blocks_count =
86*bb4ee6a4SAndroid Build Coastguard Worker             (sb.blocks_per_group * (group_id as u32 + 1) - first_free_block) as u16;
87*bb4ee6a4SAndroid Build Coastguard Worker         sb.free_blocks_count += group_desc.free_blocks_count as u32;
88*bb4ee6a4SAndroid Build Coastguard Worker 
89*bb4ee6a4SAndroid Build Coastguard Worker         // 10 inodes should be reserved in ext2.
90*bb4ee6a4SAndroid Build Coastguard Worker         let reserved_inode = if group_id == 0 { 10 } else { 0 };
91*bb4ee6a4SAndroid Build Coastguard Worker         let first_free_inode = group_id as u32 * sb.inodes_per_group + reserved_inode + 1;
92*bb4ee6a4SAndroid Build Coastguard Worker         group_desc.free_inodes_count = sb.inodes_per_group as u16 - reserved_inode as u16;
93*bb4ee6a4SAndroid Build Coastguard Worker         sb.free_inodes_count -= reserved_inode;
94*bb4ee6a4SAndroid Build Coastguard Worker 
95*bb4ee6a4SAndroid Build Coastguard Worker         // Initialize block bitmap block.
96*bb4ee6a4SAndroid Build Coastguard Worker         let bmap = arena.allocate::<[u8; BLOCK_SIZE]>(BlockId::from(group_desc.block_bitmap), 0)?;
97*bb4ee6a4SAndroid Build Coastguard Worker         let valid_bmap_bytes = (sb.blocks_per_group / 8) as usize;
98*bb4ee6a4SAndroid Build Coastguard Worker         // Unused parts in the block is marked as 1.
99*bb4ee6a4SAndroid Build Coastguard Worker         bmap[valid_bmap_bytes..].iter_mut().for_each(|x| *x = 0xff);
100*bb4ee6a4SAndroid Build Coastguard Worker         // Interpret the region as BitMap and mask bits for blocks used for metadata.
101*bb4ee6a4SAndroid Build Coastguard Worker         let mut block_bitmap = BitMap::from_slice_mut(&mut bmap[..valid_bmap_bytes]);
102*bb4ee6a4SAndroid Build Coastguard Worker         block_bitmap.mark_first_elems(
103*bb4ee6a4SAndroid Build Coastguard Worker             (first_free_block - group_id as u32 * sb.blocks_per_group) as usize,
104*bb4ee6a4SAndroid Build Coastguard Worker             true,
105*bb4ee6a4SAndroid Build Coastguard Worker         );
106*bb4ee6a4SAndroid Build Coastguard Worker 
107*bb4ee6a4SAndroid Build Coastguard Worker         let imap = arena.allocate::<[u8; BLOCK_SIZE]>(BlockId::from(group_desc.inode_bitmap), 0)?;
108*bb4ee6a4SAndroid Build Coastguard Worker         let valid_imap_bytes = (sb.inodes_per_group / 8) as usize;
109*bb4ee6a4SAndroid Build Coastguard Worker         // Unused parts in the block is marked as 1.
110*bb4ee6a4SAndroid Build Coastguard Worker         imap[valid_imap_bytes..].iter_mut().for_each(|x| *x = 0xff);
111*bb4ee6a4SAndroid Build Coastguard Worker         // Interpret the region as BitMap and mask bits for reserved inodes.
112*bb4ee6a4SAndroid Build Coastguard Worker         let mut inode_bitmap =
113*bb4ee6a4SAndroid Build Coastguard Worker             BitMap::from_slice_mut(&mut imap[..(sb.inodes_per_group / 8) as usize]);
114*bb4ee6a4SAndroid Build Coastguard Worker         inode_bitmap.mark_first_elems(reserved_inode as usize, true);
115*bb4ee6a4SAndroid Build Coastguard Worker 
116*bb4ee6a4SAndroid Build Coastguard Worker         Ok(GroupMetaData {
117*bb4ee6a4SAndroid Build Coastguard Worker             group_desc,
118*bb4ee6a4SAndroid Build Coastguard Worker             block_bitmap,
119*bb4ee6a4SAndroid Build Coastguard Worker             inode_bitmap,
120*bb4ee6a4SAndroid Build Coastguard Worker 
121*bb4ee6a4SAndroid Build Coastguard Worker             inode_table: BTreeMap::new(),
122*bb4ee6a4SAndroid Build Coastguard Worker 
123*bb4ee6a4SAndroid Build Coastguard Worker             first_free_block,
124*bb4ee6a4SAndroid Build Coastguard Worker             first_free_inode,
125*bb4ee6a4SAndroid Build Coastguard Worker         })
126*bb4ee6a4SAndroid Build Coastguard Worker     }
127*bb4ee6a4SAndroid Build Coastguard Worker }
128*bb4ee6a4SAndroid Build Coastguard Worker 
129*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(test)]
130*bb4ee6a4SAndroid Build Coastguard Worker mod test {
131*bb4ee6a4SAndroid Build Coastguard Worker     use base::MemoryMappingBuilder;
132*bb4ee6a4SAndroid Build Coastguard Worker 
133*bb4ee6a4SAndroid Build Coastguard Worker     use super::*;
134*bb4ee6a4SAndroid Build Coastguard Worker     use crate::Builder;
135*bb4ee6a4SAndroid Build Coastguard Worker 
136*bb4ee6a4SAndroid Build Coastguard Worker     // Check if `GroupMetaData` is correctly initialized from `SuperBlock` with one block group.
137*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
test_group_metadata_with_one_block_group()138*bb4ee6a4SAndroid Build Coastguard Worker     fn test_group_metadata_with_one_block_group() {
139*bb4ee6a4SAndroid Build Coastguard Worker         let blocks_per_group = 1024;
140*bb4ee6a4SAndroid Build Coastguard Worker         let num_groups = 1;
141*bb4ee6a4SAndroid Build Coastguard Worker         let size = BLOCK_SIZE as u32 * blocks_per_group * num_groups;
142*bb4ee6a4SAndroid Build Coastguard Worker         let mut mem = MemoryMappingBuilder::new(size as usize).build().unwrap();
143*bb4ee6a4SAndroid Build Coastguard Worker         let arena = Arena::new(BLOCK_SIZE, &mut mem).unwrap();
144*bb4ee6a4SAndroid Build Coastguard Worker         let sb = SuperBlock::new(
145*bb4ee6a4SAndroid Build Coastguard Worker             &arena,
146*bb4ee6a4SAndroid Build Coastguard Worker             &Builder {
147*bb4ee6a4SAndroid Build Coastguard Worker                 inodes_per_group: 1024,
148*bb4ee6a4SAndroid Build Coastguard Worker                 blocks_per_group,
149*bb4ee6a4SAndroid Build Coastguard Worker                 size,
150*bb4ee6a4SAndroid Build Coastguard Worker                 root_dir: None,
151*bb4ee6a4SAndroid Build Coastguard Worker             },
152*bb4ee6a4SAndroid Build Coastguard Worker         )
153*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap();
154*bb4ee6a4SAndroid Build Coastguard Worker         let group = GroupMetaData::new(&arena, sb, 0).unwrap();
155*bb4ee6a4SAndroid Build Coastguard Worker 
156*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(sb.block_group_nr, 1);
157*bb4ee6a4SAndroid Build Coastguard Worker 
158*bb4ee6a4SAndroid Build Coastguard Worker         // First a few blocks are used for specific purposes.
159*bb4ee6a4SAndroid Build Coastguard Worker         // Their indexes are arbitrary but we can assume the following values unless we use much
160*bb4ee6a4SAndroid Build Coastguard Worker         // larger parameters:
161*bb4ee6a4SAndroid Build Coastguard Worker         // 0: 1024-byte offset + superblock
162*bb4ee6a4SAndroid Build Coastguard Worker         // 1: group descriptor(s)
163*bb4ee6a4SAndroid Build Coastguard Worker         // 2: block bitmap
164*bb4ee6a4SAndroid Build Coastguard Worker         // 3: inode bitmap
165*bb4ee6a4SAndroid Build Coastguard Worker         // 4+ : inode table
166*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(group.group_desc.block_bitmap, 2);
167*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(group.group_desc.inode_bitmap, 3);
168*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(group.group_desc.inode_table, 4);
169*bb4ee6a4SAndroid Build Coastguard Worker 
170*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
171*bb4ee6a4SAndroid Build Coastguard Worker             group.group_desc.free_blocks_count as u32,
172*bb4ee6a4SAndroid Build Coastguard Worker             sb.free_blocks_count
173*bb4ee6a4SAndroid Build Coastguard Worker         );
174*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
175*bb4ee6a4SAndroid Build Coastguard Worker             group.group_desc.free_inodes_count as u32,
176*bb4ee6a4SAndroid Build Coastguard Worker             sb.free_inodes_count
177*bb4ee6a4SAndroid Build Coastguard Worker         );
178*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(group.block_bitmap.len(), sb.blocks_per_group as usize);
179*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
180*bb4ee6a4SAndroid Build Coastguard Worker             group.block_bitmap.count_zeros(),
181*bb4ee6a4SAndroid Build Coastguard Worker             group.group_desc.free_blocks_count as usize,
182*bb4ee6a4SAndroid Build Coastguard Worker         );
183*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
184*bb4ee6a4SAndroid Build Coastguard Worker             group.inode_bitmap.count_zeros(),
185*bb4ee6a4SAndroid Build Coastguard Worker             group.group_desc.free_inodes_count as usize,
186*bb4ee6a4SAndroid Build Coastguard Worker         );
187*bb4ee6a4SAndroid Build Coastguard Worker     }
188*bb4ee6a4SAndroid Build Coastguard Worker 
189*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
test_group_metadata_with_multiple_block_groups()190*bb4ee6a4SAndroid Build Coastguard Worker     fn test_group_metadata_with_multiple_block_groups() {
191*bb4ee6a4SAndroid Build Coastguard Worker         let blocks_per_group = 1024u32;
192*bb4ee6a4SAndroid Build Coastguard Worker         let num_groups = 10u32;
193*bb4ee6a4SAndroid Build Coastguard Worker         let mem_size = BLOCK_SIZE as u32 * blocks_per_group * num_groups;
194*bb4ee6a4SAndroid Build Coastguard Worker         let mut mem = MemoryMappingBuilder::new(mem_size as usize)
195*bb4ee6a4SAndroid Build Coastguard Worker             .build()
196*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap();
197*bb4ee6a4SAndroid Build Coastguard Worker         let arena = Arena::new(BLOCK_SIZE, &mut mem).unwrap();
198*bb4ee6a4SAndroid Build Coastguard Worker         let sb = SuperBlock::new(
199*bb4ee6a4SAndroid Build Coastguard Worker             &arena,
200*bb4ee6a4SAndroid Build Coastguard Worker             &Builder {
201*bb4ee6a4SAndroid Build Coastguard Worker                 inodes_per_group: 512,
202*bb4ee6a4SAndroid Build Coastguard Worker                 blocks_per_group,
203*bb4ee6a4SAndroid Build Coastguard Worker                 size: mem_size,
204*bb4ee6a4SAndroid Build Coastguard Worker                 root_dir: None,
205*bb4ee6a4SAndroid Build Coastguard Worker             },
206*bb4ee6a4SAndroid Build Coastguard Worker         )
207*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap();
208*bb4ee6a4SAndroid Build Coastguard Worker 
209*bb4ee6a4SAndroid Build Coastguard Worker         let groups = (0..num_groups)
210*bb4ee6a4SAndroid Build Coastguard Worker             .map(|group_id| GroupMetaData::new(&arena, sb, group_id as u16).unwrap())
211*bb4ee6a4SAndroid Build Coastguard Worker             .collect::<Vec<_>>();
212*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
213*bb4ee6a4SAndroid Build Coastguard Worker             groups
214*bb4ee6a4SAndroid Build Coastguard Worker                 .iter()
215*bb4ee6a4SAndroid Build Coastguard Worker                 .map(|gd| gd.group_desc.free_blocks_count as u32)
216*bb4ee6a4SAndroid Build Coastguard Worker                 .sum::<u32>(),
217*bb4ee6a4SAndroid Build Coastguard Worker             sb.free_blocks_count
218*bb4ee6a4SAndroid Build Coastguard Worker         );
219*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
220*bb4ee6a4SAndroid Build Coastguard Worker             groups
221*bb4ee6a4SAndroid Build Coastguard Worker                 .iter()
222*bb4ee6a4SAndroid Build Coastguard Worker                 .map(|gd| gd.group_desc.free_inodes_count as u32)
223*bb4ee6a4SAndroid Build Coastguard Worker                 .sum::<u32>(),
224*bb4ee6a4SAndroid Build Coastguard Worker             sb.free_inodes_count
225*bb4ee6a4SAndroid Build Coastguard Worker         );
226*bb4ee6a4SAndroid Build Coastguard Worker     }
227*bb4ee6a4SAndroid Build Coastguard Worker }
228