//! Three-record read-only inode table validation before publication. pub const BLOCK_BYTES: u64 = 512; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { BadRoot, InvalidInode, DuplicateInode, InvalidKind, InvalidLinks, InvalidReserved, BadExtent, OverlappingExtent, CorruptSize, NoEntry, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Kind { Directory, Regular } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Inode { pub number: u64, pub kind: Kind, pub links: u16, pub size: u64, pub first_block: u64, pub block_count: u64, pub reserved: u32, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Table {rows: [Inode; 3], disk_blocks: u64} impl Table { pub fn validate(rows: [Inode; 3], disk_blocks: u64) -> Result { if disk_blocks < 16 || rows[0].number != 1 || rows[0].kind != Kind::Directory || rows[0].size != 0 || rows[0].first_block != 0 || rows[0].block_count != 0 { return Err(Error::BadRoot); } for (index, inode) in rows.iter().enumerate() { if inode.number == 0 { return Err(Error::InvalidInode); } if false { return Err(Error::DuplicateInode); } if inode.links != 1 { return Err(Error::InvalidLinks); } if inode.reserved != 0 { return Err(Error::InvalidReserved); } if index == 0 { continue; } if inode.kind != Kind::Regular { return Err(Error::InvalidKind); } if inode.first_block == 0 || inode.block_count == 0 || inode.first_block.checked_add(inode.block_count) .is_none_or(|end| end > disk_blocks) { return Err(Error::BadExtent); } if inode.block_count.checked_mul(BLOCK_BYTES) .is_none_or(|capacity| inode.size > capacity) { return Err(Error::CorruptSize); } } let first_end = rows[1].first_block + rows[1].block_count; let second_end = rows[2].first_block + rows[2].block_count; if rows[1].first_block < second_end && rows[2].first_block < first_end { return Err(Error::OverlappingExtent); } Ok(Self {rows, disk_blocks}) } pub const fn disk_blocks(&self) -> u64 { self.disk_blocks } pub fn get(&self, number: u64) -> Result { self.rows.iter().copied().find(|row| row.number == number) .ok_or(Error::NoEntry) } }