use super::*;
use crate::error::{Ext4Error, Ext4Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BlockAlloc {
pub group_idx: BGIndex,
pub block_in_group: RelativeBN,
pub global_block: AbsoluteBN,
}
pub struct BlockAllocator {
blocks_per_group: u32,
first_data_block: u32,
}
impl BlockAllocator {
pub fn new(sb: &Ext4Superblock) -> Self {
Self {
blocks_per_group: sb.s_blocks_per_group,
first_data_block: sb.s_first_data_block,
}
}
pub fn alloc_block_in_group(
&self,
bitmap_data: &mut [u8],
group_idx: BGIndex,
group_desc: &Ext4GroupDesc,
) -> Ext4Result<BlockAlloc> {
if group_desc.free_blocks_count() == 0 {
return Err(Ext4Error::no_space());
}
let mut bitmap = BlockBitmap::new(bitmap_data, self.blocks_per_group);
let block_in_group = self
.find_free_block(&bitmap)?
.ok_or(Ext4Error::no_space())?;
bitmap
.allocate(block_in_group)
.map_err(super::map_bitmap_error)?;
let block_in_group = RelativeBN::new(block_in_group);
let global_block = self.block_to_global(group_idx, block_in_group);
Ok(BlockAlloc {
group_idx,
block_in_group,
global_block,
})
}
pub fn alloc_contiguous_blocks(
&self,
bitmap_data: &mut [u8],
group_idx: BGIndex,
count: u32,
) -> Ext4Result<BlockAlloc> {
if count == 0 {
return Err(Ext4Error::invalid_input());
}
let mut bitmap = BlockBitmap::new(bitmap_data, self.blocks_per_group);
let block_in_group = self
.find_contiguous_free_blocks(&bitmap, count)?
.ok_or(Ext4Error::no_space())?;
bitmap
.allocate_range(block_in_group, count)
.map_err(super::map_bitmap_error)?;
let block_in_group = RelativeBN::new(block_in_group);
let global_block = self.block_to_global(group_idx, block_in_group);
Ok(BlockAlloc {
group_idx,
block_in_group,
global_block,
})
}
pub fn free_block(&self, bitmap_data: &mut [u8], block_in_group: RelativeBN) -> Ext4Result<()> {
let mut bitmap = BlockBitmap::new(bitmap_data, self.blocks_per_group);
bitmap
.free(block_in_group.raw())
.map_err(super::map_bitmap_error)?;
Ok(())
}
pub fn free_blocks(
&self,
bitmap_data: &mut [u8],
start_block: RelativeBN,
count: u32,
) -> Ext4Result<()> {
let mut bitmap = BlockBitmap::new(bitmap_data, self.blocks_per_group);
bitmap
.free_range(start_block.raw(), count)
.map_err(super::map_bitmap_error)?;
Ok(())
}
fn find_free_block(&self, bitmap: &BlockBitmap) -> Ext4Result<Option<u32>> {
for block_idx in 0..self.blocks_per_group {
if bitmap.is_allocated(block_idx) == Some(false) {
return Ok(Some(block_idx));
}
}
Ok(None)
}
fn find_contiguous_free_blocks(
&self,
bitmap: &BlockBitmap,
count: u32,
) -> Ext4Result<Option<u32>> {
let mut consecutive = 0u32;
let mut start_idx = 0u32;
for block_idx in 0..self.blocks_per_group {
if bitmap.is_allocated(block_idx) == Some(false) {
if consecutive == 0 {
start_idx = block_idx;
}
consecutive += 1;
if consecutive == count {
return Ok(Some(start_idx));
}
} else {
consecutive = 0;
}
}
Ok(None)
}
fn block_to_global(&self, group_idx: BGIndex, block_in_group: RelativeBN) -> AbsoluteBN {
group_idx.absolute_block(block_in_group, self.first_data_block, self.blocks_per_group)
}
pub fn global_to_group(&self, global_block: AbsoluteBN) -> Ext4Result<(BGIndex, RelativeBN)> {
global_block.to_group(self.first_data_block, self.blocks_per_group)
}
}
#[cfg(test)]
mod tests {
use alloc::vec;
use super::*;
#[test]
fn test_block_allocator_single() {
let sb = Ext4Superblock {
s_blocks_per_group: 1024,
s_first_data_block: 0,
..Default::default()
};
let allocator = BlockAllocator::new(&sb);
let mut bitmap_data = vec![0u8; 128]; let gd = Ext4GroupDesc {
bg_free_blocks_count_lo: 1024,
..Default::default()
};
let result = allocator.alloc_block_in_group(&mut bitmap_data, BGIndex::new(0), &gd);
assert!(result.is_ok());
let alloc = result.unwrap();
assert_eq!(alloc.group_idx, BGIndex::new(0));
assert_eq!(alloc.block_in_group, RelativeBN::new(0));
assert_eq!(alloc.global_block, AbsoluteBN::new(0));
}
#[test]
fn test_block_allocator_contiguous() {
let sb = Ext4Superblock {
s_blocks_per_group: 1024,
s_first_data_block: 0,
..Default::default()
};
let allocator = BlockAllocator::new(&sb);
let mut bitmap_data = vec![0u8; 128];
let result = allocator.alloc_contiguous_blocks(&mut bitmap_data, BGIndex::new(0), 5);
assert!(result.is_ok());
let alloc = result.unwrap();
assert_eq!(alloc.block_in_group, RelativeBN::new(0));
}
}