use crate::block_index::FsBlockIndex;
use crate::checksum::Checksum;
use crate::error::{CorruptKind, Ext4Error};
use crate::journal::JournalSuperblock;
use crate::journal::block_header::JournalBlockHeader;
use crate::util::read_u32be;
use crate::util::usize_from_u32;
use alloc::vec::Vec;
pub(super) fn validate_revocation_block_checksum(
superblock: &JournalSuperblock,
block: &[u8],
) -> Result<(), Ext4Error> {
let checksum_offset = block.len().checked_sub(4).unwrap();
let expected_checksum = read_u32be(block, checksum_offset);
let mut checksum = Checksum::new();
checksum.update(superblock.uuid.as_bytes());
checksum.update(&block[..checksum_offset]);
checksum.update_u32_be(0);
if checksum.finalize() == expected_checksum {
Ok(())
} else {
Err(CorruptKind::JournalRevocationBlockChecksum.into())
}
}
pub(super) fn read_revocation_block_table(
block: &[u8],
table: &mut Vec<FsBlockIndex>,
) -> Result<(), Ext4Error> {
const BLOCK_INDEX_SIZE_IN_BYTES: usize = 8;
let data = &block[JournalBlockHeader::SIZE..
block.len().checked_sub(4).unwrap()];
let num_bytes = usize_from_u32(read_u32be(data, 0));
if num_bytes % BLOCK_INDEX_SIZE_IN_BYTES != 0 {
return Err(CorruptKind::JournalRevocationBlockInvalidTableSize(
num_bytes,
)
.into());
}
let data = &data[size_of::<u32>()..];
let mut data = data.get(..num_bytes).ok_or(
CorruptKind::JournalRevocationBlockInvalidTableSize(num_bytes),
)?;
while !data.is_empty() {
let block_index = u64::from_be_bytes(
data[..BLOCK_INDEX_SIZE_IN_BYTES].try_into().unwrap(),
);
table.push(block_index);
data = &data[BLOCK_INDEX_SIZE_IN_BYTES..];
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::uuid::Uuid;
#[test]
fn test_validate_revocation_block_checksum() {
let superblock = JournalSuperblock {
block_size: 1024,
sequence: 0,
start_block: 0,
uuid: Uuid([0; 16]),
};
let mut block = vec![0; 1024];
assert_eq!(
validate_revocation_block_checksum(&superblock, &block)
.unwrap_err(),
CorruptKind::JournalRevocationBlockChecksum
);
block[1020..].copy_from_slice(&[0x74, 0xef, 0x0e, 0xf6]);
assert!(
validate_revocation_block_checksum(&superblock, &block).is_ok()
);
}
fn create_test_revocation_block() -> Vec<u8> {
let mut block = Vec::new();
block.extend([0; JournalBlockHeader::SIZE]);
block.extend(24u32.to_be_bytes());
block.extend(100u64.to_be_bytes());
block.extend(101u64.to_be_bytes());
block.extend(102u64.to_be_bytes());
block.extend(103u64.to_be_bytes());
block.resize(1024usize, 0u8);
block
}
#[test]
fn test_read_revocation_block_table_success() {
let block = create_test_revocation_block();
let mut table = Vec::new();
read_revocation_block_table(&block, &mut table).unwrap();
assert_eq!(table, [100, 101, 102]);
}
#[test]
fn test_read_revocation_block_table_uneven_size() {
let mut block = create_test_revocation_block();
block[JournalBlockHeader::SIZE
..JournalBlockHeader::SIZE + size_of::<u32>()]
.copy_from_slice(&7u32.to_be_bytes());
let mut table = Vec::new();
assert_eq!(
read_revocation_block_table(&block, &mut table).unwrap_err(),
CorruptKind::JournalRevocationBlockInvalidTableSize(7)
);
}
#[test]
fn test_read_revocation_block_table_size_too_large() {
let mut block = create_test_revocation_block();
block[JournalBlockHeader::SIZE
..JournalBlockHeader::SIZE + size_of::<u32>()]
.copy_from_slice(&1008u32.to_be_bytes());
let mut table = Vec::new();
assert_eq!(
read_revocation_block_table(&block, &mut table).unwrap_err(),
CorruptKind::JournalRevocationBlockInvalidTableSize(1008)
);
}
}