use std::collections::BTreeMap;
use anyhow::Result;
use zerocopy::AsBytes;
use zerocopy_derive::FromBytes;
use zerocopy_derive::FromZeroes;
use crate::arena::Arena;
use crate::arena::BlockId;
use crate::bitmap::BitMap;
use crate::inode::Inode;
use crate::inode::InodeNum;
use crate::superblock::SuperBlock;
pub const BLOCK_SIZE: usize = 4096;
#[repr(C)]
#[derive(Default, Debug, Copy, Clone, FromZeroes, FromBytes, AsBytes)]
pub(crate) struct BlockGroupDescriptor {
pub block_bitmap: u32,
pub inode_bitmap: u32,
pub inode_table: u32,
pub free_blocks_count: u16,
pub free_inodes_count: u16,
pub used_dirs_count: u16,
pad: u16,
reserved: [u8; 12],
}
pub(crate) struct GroupMetaData<'a> {
pub group_desc: &'a mut BlockGroupDescriptor,
pub block_bitmap: BitMap<'a>,
pub inode_bitmap: BitMap<'a>,
pub inode_table: BTreeMap<InodeNum, &'a mut Inode>,
pub first_free_block: u32,
pub first_free_inode: u32,
}
impl<'a> GroupMetaData<'a> {
pub fn new(arena: &'a Arena<'a>, sb: &mut SuperBlock, group_id: u16) -> Result<Self> {
let gd_size = std::mem::size_of::<BlockGroupDescriptor>() as u32;
let num_blocks_for_gds = (gd_size * sb.num_groups() as u32).div_ceil(BLOCK_SIZE as u32);
let inodes_per_block = BLOCK_SIZE as u64 / sb.inode_size as u64;
let num_blocks_for_inode_table =
(sb.inodes_per_group as usize).div_ceil(inodes_per_block as usize);
let group_desc = arena.allocate::<BlockGroupDescriptor>(
BlockId::from(1),
std::mem::size_of::<BlockGroupDescriptor>() * group_id as usize,
)?;
let super_block_id = group_id as u32 * sb.blocks_per_group;
let group_desc_id = super_block_id + 1;
group_desc.block_bitmap = group_desc_id + num_blocks_for_gds;
group_desc.inode_bitmap = group_desc.block_bitmap + 1;
group_desc.inode_table = group_desc.inode_bitmap + 1;
let first_free_block = group_desc.inode_table + num_blocks_for_inode_table as u32;
group_desc.free_blocks_count =
(sb.blocks_per_group * (group_id as u32 + 1) - first_free_block) as u16;
sb.free_blocks_count += group_desc.free_blocks_count as u32;
let reserved_inode = if group_id == 0 { 10 } else { 0 };
let first_free_inode = group_id as u32 * sb.inodes_per_group + reserved_inode + 1;
group_desc.free_inodes_count = sb.inodes_per_group as u16 - reserved_inode as u16;
sb.free_inodes_count -= reserved_inode;
let bmap = arena.allocate::<[u8; BLOCK_SIZE]>(BlockId::from(group_desc.block_bitmap), 0)?;
let valid_bmap_bytes = (sb.blocks_per_group / 8) as usize;
bmap[valid_bmap_bytes..].iter_mut().for_each(|x| *x = 0xff);
let mut block_bitmap = BitMap::from_slice_mut(&mut bmap[..valid_bmap_bytes]);
block_bitmap.mark_first_elems(
(first_free_block - group_id as u32 * sb.blocks_per_group) as usize,
true,
);
let imap = arena.allocate::<[u8; BLOCK_SIZE]>(BlockId::from(group_desc.inode_bitmap), 0)?;
let valid_imap_bytes = (sb.inodes_per_group / 8) as usize;
imap[valid_imap_bytes..].iter_mut().for_each(|x| *x = 0xff);
let mut inode_bitmap =
BitMap::from_slice_mut(&mut imap[..(sb.inodes_per_group / 8) as usize]);
inode_bitmap.mark_first_elems(reserved_inode as usize, true);
Ok(GroupMetaData {
group_desc,
block_bitmap,
inode_bitmap,
inode_table: BTreeMap::new(),
first_free_block,
first_free_inode,
})
}
}
#[cfg(test)]
mod test {
use base::MemoryMappingBuilder;
use super::*;
use crate::Builder;
#[test]
fn test_group_metadata_with_one_block_group() {
let blocks_per_group = 1024;
let num_groups = 1;
let size = BLOCK_SIZE as u32 * blocks_per_group * num_groups;
let mut mem = MemoryMappingBuilder::new(size as usize).build().unwrap();
let arena = Arena::new(BLOCK_SIZE, &mut mem).unwrap();
let sb = SuperBlock::new(
&arena,
&Builder {
inodes_per_group: 1024,
blocks_per_group,
size,
root_dir: None,
},
)
.unwrap();
let group = GroupMetaData::new(&arena, sb, 0).unwrap();
assert_eq!(sb.block_group_nr, 1);
assert_eq!(group.group_desc.block_bitmap, 2);
assert_eq!(group.group_desc.inode_bitmap, 3);
assert_eq!(group.group_desc.inode_table, 4);
assert_eq!(
group.group_desc.free_blocks_count as u32,
sb.free_blocks_count
);
assert_eq!(
group.group_desc.free_inodes_count as u32,
sb.free_inodes_count
);
assert_eq!(group.block_bitmap.len(), sb.blocks_per_group as usize);
assert_eq!(
group.block_bitmap.count_zeros(),
group.group_desc.free_blocks_count as usize,
);
assert_eq!(
group.inode_bitmap.count_zeros(),
group.group_desc.free_inodes_count as usize,
);
}
#[test]
fn test_group_metadata_with_multiple_block_groups() {
let blocks_per_group = 1024u32;
let num_groups = 10u32;
let mem_size = BLOCK_SIZE as u32 * blocks_per_group * num_groups;
let mut mem = MemoryMappingBuilder::new(mem_size as usize)
.build()
.unwrap();
let arena = Arena::new(BLOCK_SIZE, &mut mem).unwrap();
let sb = SuperBlock::new(
&arena,
&Builder {
inodes_per_group: 512,
blocks_per_group,
size: mem_size,
root_dir: None,
},
)
.unwrap();
let groups = (0..num_groups)
.map(|group_id| GroupMetaData::new(&arena, sb, group_id as u16).unwrap())
.collect::<Vec<_>>();
assert_eq!(
groups
.iter()
.map(|gd| gd.group_desc.free_blocks_count as u32)
.sum::<u32>(),
sb.free_blocks_count
);
assert_eq!(
groups
.iter()
.map(|gd| gd.group_desc.free_inodes_count as u32)
.sum::<u32>(),
sb.free_inodes_count
);
}
}