#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct IntegrityMeta {
pub version: u32,
pub flags: u32,
pub hash_type: u32,
pub broken_xid: u64,
}
const OFF_IM_VERSION: usize = 32; const OFF_IM_FLAGS: usize = 36; const OFF_IM_HASH_TYPE: usize = 40; const OFF_IM_BROKEN_XID: usize = 48; const INTEGRITY_META_MIN_LEN: usize = OFF_IM_BROKEN_XID + 8;
impl IntegrityMeta {
pub fn parse(block: &[u8]) -> crate::Result<Self> {
if block.len() < INTEGRITY_META_MIN_LEN {
return Err(crate::ApfsError::FieldOutOfRange {
structure: "integrity_meta_phys",
field: "block.len",
value: block.len() as u64,
cap: INTEGRITY_META_MIN_LEN as u64,
});
}
let stored = crate::object::fletcher64_stored(block);
let computed = crate::object::fletcher64_checksum(block);
if stored != computed {
return Err(crate::ApfsError::ChecksumMismatch {
block: crate::bytes::le_u64(block, 8),
stored,
computed,
});
}
Ok(Self {
version: crate::bytes::le_u32(block, OFF_IM_VERSION),
flags: crate::bytes::le_u32(block, OFF_IM_FLAGS),
hash_type: crate::bytes::le_u32(block, OFF_IM_HASH_TYPE),
broken_xid: crate::bytes::le_u64(block, OFF_IM_BROKEN_XID),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
fn integrity_block(version: u32, flags: u32, hash_type: u32, broken_xid: u64) -> Vec<u8> {
let mut b = vec![0u8; 4096];
b[32..36].copy_from_slice(&version.to_le_bytes());
b[36..40].copy_from_slice(&flags.to_le_bytes());
b[40..44].copy_from_slice(&hash_type.to_le_bytes());
b[48..56].copy_from_slice(&broken_xid.to_le_bytes());
let cks = crate::object::fletcher64_checksum(&b);
b[0..8].copy_from_slice(&cks.to_le_bytes());
b
}
#[test]
fn parses_fields_including_broken_xid() {
let block = integrity_block(1, 0, 1, 42);
let im = IntegrityMeta::parse(&block).expect("parse integrity_meta");
assert_eq!(im.version, 1);
assert_eq!(im.hash_type, 1);
assert_eq!(im.broken_xid, 42);
}
#[test]
fn unbroken_seal_has_zero_broken_xid() {
let block = integrity_block(1, 0, 1, 0);
let im = IntegrityMeta::parse(&block).expect("parse");
assert_eq!(im.broken_xid, 0);
}
#[test]
fn rejects_corrupted_block() {
let mut block = integrity_block(1, 0, 1, 0);
block[100] ^= 0xff;
assert!(IntegrityMeta::parse(&block).is_err());
}
}