use log::error;
use crate::{
checksum::{ext4_block_bitmap_csum32, ext4_group_desc_csum16, ext4_inode_bitmap_csum32},
crc32c::crc32c::ext4_superblock_has_metadata_csum,
endian::DiskFormat,
error::{Ext4Error, Ext4Result},
superblock::Ext4Superblock,
};
#[repr(C)]
#[derive(Debug, Clone, Copy, Default)]
pub struct Ext4GroupDesc {
pub bg_block_bitmap_lo: u32,
pub bg_inode_bitmap_lo: u32,
pub bg_inode_table_lo: u32,
pub bg_free_blocks_count_lo: u16,
pub bg_free_inodes_count_lo: u16,
pub bg_used_dirs_count_lo: u16,
pub bg_flags: u16,
pub bg_exclude_bitmap_lo: u32,
pub bg_block_bitmap_csum_lo: u16,
pub bg_inode_bitmap_csum_lo: u16,
pub bg_itable_unused_lo: u16,
pub bg_checksum: u16,
pub bg_block_bitmap_hi: u32,
pub bg_inode_bitmap_hi: u32,
pub bg_inode_table_hi: u32,
pub bg_free_blocks_count_hi: u16,
pub bg_free_inodes_count_hi: u16,
pub bg_used_dirs_count_hi: u16,
pub bg_itable_unused_hi: u16,
pub bg_exclude_bitmap_hi: u32,
pub bg_block_bitmap_csum_hi: u16,
pub bg_inode_bitmap_csum_hi: u16,
pub bg_reserved: u32,
}
impl Ext4GroupDesc {
pub const GOOD_OLD_DESC_SIZE: usize = 32;
pub const EXT4_DESC_SIZE_64BIT: usize = 64;
pub const EXT4_BG_INODE_UNINIT: u16 = 0x0001;
pub const EXT4_BG_BLOCK_UNINIT: u16 = 0x0002;
pub const EXT4_BG_INODE_ZEROED: u16 = 0x0004;
pub fn update_checksum(
&mut self,
superblock: &Ext4Superblock,
group_id: u32,
block_bitmap: Option<&[u8]>,
inode_bitmap: Option<&[u8]>,
) {
if !ext4_superblock_has_metadata_csum(superblock) {
return;
}
if let Some(bm) = block_bitmap {
let csum = ext4_block_bitmap_csum32(superblock, bm);
self.bg_block_bitmap_csum_lo = (csum & 0xFFFF) as u16;
self.bg_block_bitmap_csum_hi = ((csum >> 16) & 0xFFFF) as u16;
}
if let Some(bm) = inode_bitmap {
let csum = ext4_inode_bitmap_csum32(superblock, bm);
self.bg_inode_bitmap_csum_lo = (csum & 0xFFFF) as u16;
self.bg_inode_bitmap_csum_hi = ((csum >> 16) & 0xFFFF) as u16;
}
let mut desc_for_csum = *self;
desc_for_csum.bg_checksum = 0;
let desc_size = superblock.get_desc_size() as usize;
let desc_size = core::cmp::min(desc_size, Ext4GroupDesc::EXT4_DESC_SIZE_64BIT);
let mut raw_desc_bytes = [0u8; Ext4GroupDesc::EXT4_DESC_SIZE_64BIT];
desc_for_csum.to_disk_bytes(&mut raw_desc_bytes);
self.bg_checksum =
ext4_group_desc_csum16(superblock, group_id, &raw_desc_bytes[..desc_size]);
}
pub fn verify_checksum(&self, superblock: &Ext4Superblock, group_id: u32) -> Ext4Result<()> {
if !ext4_superblock_has_metadata_csum(superblock) {
return Ok(());
}
let mut desc_for_csum = *self;
desc_for_csum.bg_checksum = 0;
let desc_size = superblock.get_desc_size() as usize;
let desc_size = core::cmp::min(desc_size, Ext4GroupDesc::EXT4_DESC_SIZE_64BIT);
let mut raw_desc_bytes = [0u8; Ext4GroupDesc::EXT4_DESC_SIZE_64BIT];
desc_for_csum.to_disk_bytes(&mut raw_desc_bytes);
let expected = ext4_group_desc_csum16(superblock, group_id, &raw_desc_bytes[..desc_size]);
if expected != self.bg_checksum {
error!(
"Group descriptor checksum mismatch: group={} stored={:#06x} expected={:#06x} \
desc_size={} block_bitmap={} inode_bitmap={} inode_table={} free_blocks={} \
free_inodes={} used_dirs={} itable_unused={} flags={:#x} block_bitmap_csum={:#x} \
inode_bitmap_csum={:#x}",
group_id,
self.bg_checksum,
expected,
desc_size,
self.block_bitmap(),
self.inode_bitmap(),
self.inode_table(),
self.free_blocks_count(),
self.free_inodes_count(),
self.used_dirs_count(),
self.itable_unused(),
self.bg_flags,
self.block_bitmap_csum(),
self.inode_bitmap_csum()
);
return Err(Ext4Error::checksum());
}
Ok(())
}
pub fn block_bitmap(&self) -> u64 {
(self.bg_block_bitmap_hi as u64) << 32 | self.bg_block_bitmap_lo as u64
}
pub fn inode_bitmap(&self) -> u64 {
(self.bg_inode_bitmap_hi as u64) << 32 | self.bg_inode_bitmap_lo as u64
}
pub fn inode_table(&self) -> u64 {
(self.bg_inode_table_hi as u64) << 32 | self.bg_inode_table_lo as u64
}
pub fn free_blocks_count(&self) -> u32 {
(self.bg_free_blocks_count_hi as u32) << 16 | self.bg_free_blocks_count_lo as u32
}
pub fn free_inodes_count(&self) -> u32 {
(self.bg_free_inodes_count_hi as u32) << 16 | self.bg_free_inodes_count_lo as u32
}
pub fn used_dirs_count(&self) -> u32 {
(self.bg_used_dirs_count_hi as u32) << 16 | self.bg_used_dirs_count_lo as u32
}
pub fn itable_unused(&self) -> u32 {
(self.bg_itable_unused_hi as u32) << 16 | self.bg_itable_unused_lo as u32
}
pub fn exclude_bitmap(&self) -> u64 {
(self.bg_exclude_bitmap_hi as u64) << 32 | self.bg_exclude_bitmap_lo as u64
}
pub fn block_bitmap_csum(&self) -> u32 {
(self.bg_block_bitmap_csum_hi as u32) << 16 | self.bg_block_bitmap_csum_lo as u32
}
pub fn inode_bitmap_csum(&self) -> u32 {
(self.bg_inode_bitmap_csum_hi as u32) << 16 | self.bg_inode_bitmap_csum_lo as u32
}
pub fn is_uninit_bg(&self) -> bool {
self.bg_flags & Self::EXT4_BG_INODE_UNINIT != 0
}
pub fn is_block_bitmap_uninit(&self) -> bool {
self.bg_flags & Self::EXT4_BG_BLOCK_UNINIT != 0
}
pub fn is_inode_bitmap_uninit(&self) -> bool {
self.bg_flags & Self::EXT4_BG_INODE_UNINIT != 0
}
pub fn is_inode_table_zeroed(&self) -> bool {
self.bg_flags & Self::EXT4_BG_INODE_ZEROED != 0
}
}