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