pub const BTRFS_CSUM_SIZE: usize = 32;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum CsumType {
Crc32c,
Xxhash64,
Sha256,
Blake2,
Unknown(u16),
}
impl CsumType {
#[must_use]
pub fn from_code(code: u16) -> Self {
match code {
0 => CsumType::Crc32c,
1 => CsumType::Xxhash64,
2 => CsumType::Sha256,
3 => CsumType::Blake2,
other => CsumType::Unknown(other),
}
}
}
fn crc32c() -> crc::Crc<u32> {
crc::Crc::<u32>::new(&crc::CRC_32_ISCSI)
}
#[must_use]
pub fn verify_superblock_crc32c(block: &[u8], covered_len: usize) -> bool {
let Some(stored_bytes) = block.get(0..4) else {
return false;
};
let stored = [
stored_bytes[0],
stored_bytes[1],
stored_bytes[2],
stored_bytes[3],
];
if covered_len <= BTRFS_CSUM_SIZE {
return false;
}
let Some(covered) = block.get(BTRFS_CSUM_SIZE..covered_len) else {
return false;
};
let computed = crc32c().checksum(covered);
computed.to_le_bytes() == stored
}
#[must_use]
pub fn superblock_crc_status(
csum_type: CsumType,
block: &[u8],
covered_len: usize,
) -> Option<bool> {
match csum_type {
CsumType::Crc32c => Some(verify_superblock_crc32c(block, covered_len)),
_ => None,
}
}
#[cfg(test)]
mod unit {
use super::{superblock_crc_status, verify_superblock_crc32c, CsumType, BTRFS_CSUM_SIZE};
#[test]
fn csum_type_decodes_every_code() {
assert_eq!(CsumType::from_code(0), CsumType::Crc32c);
assert_eq!(CsumType::from_code(1), CsumType::Xxhash64);
assert_eq!(CsumType::from_code(2), CsumType::Sha256);
assert_eq!(CsumType::from_code(3), CsumType::Blake2);
assert_eq!(CsumType::from_code(99), CsumType::Unknown(99));
}
#[test]
fn verify_rejects_short_and_degenerate_blocks() {
assert!(!verify_superblock_crc32c(&[0u8; 3], 4096));
assert!(!verify_superblock_crc32c(&[0u8; 64], BTRFS_CSUM_SIZE));
assert!(!verify_superblock_crc32c(&[0u8; 40], 4096));
}
#[test]
fn crc_status_is_none_for_non_crc32c_types() {
let block = [0u8; 64];
assert_eq!(superblock_crc_status(CsumType::Xxhash64, &block, 64), None);
assert_eq!(superblock_crc_status(CsumType::Sha256, &block, 64), None);
assert_eq!(superblock_crc_status(CsumType::Blake2, &block, 64), None);
assert_eq!(
superblock_crc_status(CsumType::Unknown(7), &block, 64),
None
);
}
}