const XOR: u32 = 0xFFFF_FFFF;
pub fn raw_update(crc: u32, data: &[u8]) -> u32 {
crc32c::crc32c_append(crc ^ XOR, data) ^ XOR
}
pub fn fs_seed(uuid: &[u8; 16], csum_seed: Option<u32>) -> u32 {
csum_seed.unwrap_or_else(|| raw_update(XOR, uuid))
}
pub fn superblock(sb: &[u8]) -> u32 {
debug_assert_eq!(sb.len(), 1024);
raw_update(XOR, &sb[..1020])
}
pub fn bitmap(seed: u32, bitmap_bytes: &[u8]) -> u32 {
raw_update(seed, bitmap_bytes)
}
pub fn group_desc(seed: u32, group: u32, desc: &[u8]) -> u16 {
let c = raw_update(seed, &group.to_le_bytes());
let c = raw_update(c, desc);
(c & 0xffff) as u16
}
pub fn inode(seed: u32, inode_num: u32, generation: u32, inode: &[u8]) -> u32 {
let c = raw_update(seed, &inode_num.to_le_bytes());
let c = raw_update(c, &generation.to_le_bytes());
raw_update(c, inode)
}
pub fn dir_block(seed: u32, dir_inode: u32, dir_generation: u32, block_before_tail: &[u8]) -> u32 {
let c = raw_update(seed, &dir_inode.to_le_bytes());
let c = raw_update(c, &dir_generation.to_le_bytes());
raw_update(c, block_before_tail)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn raw_matches_known_check_value() {
assert_eq!(raw_update(XOR, b"123456789"), 0xE306_9283 ^ XOR);
}
#[test]
fn raw_update_chains() {
let a = b"hello, ";
let b = b"world";
let mut joined = Vec::new();
joined.extend_from_slice(a);
joined.extend_from_slice(b);
assert_eq!(raw_update(raw_update(XOR, a), b), raw_update(XOR, &joined));
}
#[test]
fn fs_seed_prefers_explicit() {
let uuid = [0x42u8; 16];
assert_eq!(fs_seed(&uuid, Some(0x1234_5678)), 0x1234_5678);
assert_eq!(fs_seed(&uuid, None), raw_update(XOR, &uuid));
}
#[test]
fn superblock_excludes_csum_field() {
let mut sb_a = vec![0u8; 1024];
let mut sb_b = vec![0u8; 1024];
sb_a[10] = 0xAB;
sb_b[10] = 0xAB;
sb_a[1020..1024].copy_from_slice(&[1, 2, 3, 4]);
sb_b[1020..1024].copy_from_slice(&[9, 9, 9, 9]);
assert_eq!(superblock(&sb_a), superblock(&sb_b));
sb_b[10] = 0xCD;
assert_ne!(superblock(&sb_a), superblock(&sb_b));
}
#[test]
fn group_desc_depends_on_group_number() {
let seed = 0x1111_2222;
let desc = [0u8; 64];
assert_ne!(group_desc(seed, 0, &desc), group_desc(seed, 1, &desc));
}
#[test]
fn inode_checksum_depends_on_number_and_generation() {
let seed = 0xABCD_1234;
let body = [0u8; 256];
let base = inode(seed, 12, 0, &body);
assert_ne!(base, inode(seed, 13, 0, &body));
assert_ne!(base, inode(seed, 12, 1, &body));
}
}