use crate::Ext4Read;
use crate::block_index::FsBlockIndex;
use crate::checksum::Checksum;
use crate::error::{CorruptKind, Ext4Error};
use crate::features::{IncompatibleFeatures, ReadOnlyCompatibleFeatures};
use crate::superblock::Superblock;
use crate::util::{read_u16le, read_u32le, u64_from_hilo, usize_from_u32};
use alloc::vec;
use alloc::vec::Vec;
pub(crate) type BlockGroupIndex = u32;
#[derive(Debug)]
pub(crate) struct BlockGroupDescriptor {
pub(crate) inode_table_first_block: FsBlockIndex,
checksum: u16,
}
impl BlockGroupDescriptor {
const BG_CHECKSUM_OFFSET: usize = 0x1e;
fn from_bytes(superblock: &Superblock, bytes: &[u8]) -> Self {
const BG_INODE_TABLE_HI_OFFSET: usize = 0x28;
let bg_inode_table_lo = read_u32le(bytes, 0x8);
let bg_checksum = read_u16le(bytes, Self::BG_CHECKSUM_OFFSET);
let bg_inode_table_hi = if superblock
.incompatible_features
.contains(IncompatibleFeatures::IS_64BIT)
{
read_u32le(bytes, BG_INODE_TABLE_HI_OFFSET)
} else {
0
};
let inode_table_first_block =
u64_from_hilo(bg_inode_table_hi, bg_inode_table_lo);
Self {
inode_table_first_block,
checksum: bg_checksum,
}
}
fn get_start_byte(
sb: &Superblock,
bgd_index: BlockGroupIndex,
) -> Option<u64> {
let bgd_start_block: u32 = if sb.block_size == 1024 { 2 } else { 1 };
let bgd_per_block = sb
.block_size
.to_u32()
.checked_div(u32::from(sb.block_group_descriptor_size))?;
let block_index = bgd_start_block
.checked_add(bgd_index.checked_div(bgd_per_block)?)?;
let offset_within_block = (bgd_index.checked_rem(bgd_per_block)?)
.checked_mul(u32::from(sb.block_group_descriptor_size))?;
u64::from(block_index)
.checked_mul(sb.block_size.to_u64())?
.checked_add(u64::from(offset_within_block))
}
fn read(
sb: &Superblock,
reader: &mut dyn Ext4Read,
bgd_index: BlockGroupIndex,
) -> Result<Self, Ext4Error> {
let block_group_descriptor_size =
usize::from(sb.block_group_descriptor_size);
let mut data = vec![0; block_group_descriptor_size];
let start = Self::get_start_byte(sb, bgd_index)
.ok_or(CorruptKind::BlockGroupDescriptor(bgd_index))?;
reader.read(start, &mut data).map_err(Ext4Error::Io)?;
let block_group_descriptor = Self::from_bytes(sb, &data);
let has_metadata_checksums = sb
.read_only_compatible_features
.contains(ReadOnlyCompatibleFeatures::METADATA_CHECKSUMS);
if has_metadata_checksums {
let mut checksum = Checksum::with_seed(sb.checksum_seed);
checksum.update_u32_le(bgd_index);
checksum.update(&data[..Self::BG_CHECKSUM_OFFSET]);
checksum.update_u16_le(0);
checksum.update(&data[Self::BG_CHECKSUM_OFFSET + 2..]);
let checksum = u16::try_from(checksum.finalize() & 0xffff).unwrap();
if checksum != block_group_descriptor.checksum {
return Err(CorruptKind::BlockGroupDescriptorChecksum(
bgd_index,
)
.into());
}
} else if sb
.read_only_compatible_features
.contains(ReadOnlyCompatibleFeatures::GROUP_DESCRIPTOR_CHECKSUMS)
{
}
Ok(block_group_descriptor)
}
pub(crate) fn read_all(
sb: &Superblock,
reader: &mut dyn Ext4Read,
) -> Result<Vec<Self>, Ext4Error> {
let mut block_group_descriptors =
Vec::with_capacity(usize_from_u32(sb.num_block_groups));
for bgd_index in 0..sb.num_block_groups {
let bgd = Self::read(sb, reader, bgd_index)?;
block_group_descriptors.push(bgd);
}
Ok(block_group_descriptors)
}
}