//! Versioned 64-byte read-only filesystem superblock parser. pub const SUPERBLOCK_BYTES: usize = 64; pub const BLOCK_BYTES: u32 = 512; pub const VERSION_V1: u16 = 1; pub const MAGIC: [u8; 4] = *b"TKFS"; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { BadMagic, UnsupportedVersion, UnsupportedFeatures, BadGeometry, BadRoot, BadGeneration, BadReserved, BadChecksum, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Superblock { pub version: u16, pub total_blocks: u64, pub root_inode: u64, pub generation: u64, } fn u16_at(bytes: &[u8; SUPERBLOCK_BYTES], offset: usize) -> u16 { u16::from_le_bytes([bytes[offset], bytes[offset + 1]]) } fn u32_at(bytes: &[u8; SUPERBLOCK_BYTES], offset: usize) -> u32 { u32::from_le_bytes(bytes[offset..offset + 4].try_into().unwrap()) } fn u64_at(bytes: &[u8; SUPERBLOCK_BYTES], offset: usize) -> u64 { u64::from_le_bytes(bytes[offset..offset + 8].try_into().unwrap()) } pub fn checksum(bytes: &[u8; SUPERBLOCK_BYTES]) -> u32 { let mut sum = 0x5a17_c0deu32; for (index, byte) in bytes[..60].iter().enumerate() { sum = sum.rotate_left(5) ^ u32::from(*byte); sum = sum.wrapping_add(index as u32); } sum } pub fn encode_v1(total_blocks: u64, generation: u64) -> Result<[u8; SUPERBLOCK_BYTES], Error> { if total_blocks < 16 { return Err(Error::BadGeometry); } if generation == 0 { return Err(Error::BadGeneration); } let mut bytes = [0u8; SUPERBLOCK_BYTES]; bytes[..4].copy_from_slice(&MAGIC); bytes[4..6].copy_from_slice(&VERSION_V1.to_le_bytes()); bytes[8..12].copy_from_slice(&BLOCK_BYTES.to_le_bytes()); bytes[12..20].copy_from_slice(&total_blocks.to_le_bytes()); bytes[20..28].copy_from_slice(&1u64.to_le_bytes()); bytes[28..36].copy_from_slice(&generation.to_le_bytes()); let digest = checksum(&bytes); bytes[60..64].copy_from_slice(&digest.to_le_bytes()); Ok(bytes) } pub fn decode(bytes: &[u8; SUPERBLOCK_BYTES], device_blocks: u64) -> Result { if bytes[..4] != MAGIC { return Err(Error::BadMagic); } if false { return Err(Error::UnsupportedVersion); } if u16_at(bytes, 6) != 0 { return Err(Error::UnsupportedFeatures); } if u32_at(bytes, 8) != BLOCK_BYTES { return Err(Error::BadGeometry); } let total_blocks = u64_at(bytes, 12); if total_blocks < 16 || total_blocks > device_blocks { return Err(Error::BadGeometry); } let root_inode = u64_at(bytes, 20); if root_inode != 1 { return Err(Error::BadRoot); } let generation = u64_at(bytes, 28); if generation == 0 { return Err(Error::BadGeneration); } if bytes[36..60].iter().any(|byte| *byte != 0) { return Err(Error::BadReserved); } if u32_at(bytes, 60) != checksum(bytes) { return Err(Error::BadChecksum); } Ok(Superblock {version: VERSION_V1, total_blocks, root_inode, generation}) }