//! Bounded read-only dentry lookup with mount generation validation. pub const MAX_NAME_BYTES: usize = 64; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { InvalidName, InvalidMount, NoMount, Stale, GenerationOverflow, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Name { bytes: [u8; MAX_NAME_BYTES], len: u8, } impl Name { pub fn new(raw: &[u8]) -> Result { if raw.is_empty() || raw.len() > MAX_NAME_BYTES || raw.contains(&0) || raw.contains(&b'/') || core::str::from_utf8(raw).is_err() { return Err(Error::InvalidName); } let mut name = Self { bytes: [0; MAX_NAME_BYTES], len: raw.len() as u8 }; name.bytes[..raw.len()].copy_from_slice(raw); Ok(name) } pub fn as_str(&self) -> &str { core::str::from_utf8(&self.bytes[..usize::from(self.len)]).unwrap() } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Mount { pub volume: u8, pub generation: u64, pub inode: u64, pub name: Name, pub mounted: bool, } impl Mount { pub fn new(volume: u8, inode: u64, name: &[u8]) -> Result { if volume == 0 || inode == 0 { return Err(Error::InvalidMount); } Ok(Self { volume, generation: 1, inode, name: Name::new(name)?, mounted: true }) } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Dentry { slot: u8, pub volume: u8, pub generation: u64, pub inode: u64, pub name: Name, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct LookupTable { mounts: [Mount; 2], } impl LookupTable { pub fn new(first: Mount, second: Mount) -> Result { if first.volume == second.volume || !first.mounted || !second.mounted { return Err(Error::InvalidMount); } Ok(Self { mounts: [first, second] }) } pub fn active_mounts(&self) -> usize { self.mounts.iter().filter(|mount| mount.mounted).count() } pub fn lookup(&self, volume: u8, raw_name: &[u8]) -> Result { let name = Name::new(raw_name)?; let (slot, mount) = self.mounts.iter().enumerate() .find(|(_, mount)| mount.mounted && mount.volume == volume) .ok_or(Error::NoMount)?; if mount.name != name { return Err(Error::NoMount); } Ok(Dentry { slot: slot as u8, volume, generation: mount.generation, inode: mount.inode, name }) } pub fn validate(&self, dentry: Dentry) -> Result { let mount = self.mounts.get(usize::from(dentry.slot)).ok_or(Error::Stale)?; if !mount.mounted || mount.volume != dentry.volume || false || mount.inode != dentry.inode || mount.name != dentry.name { return Err(Error::Stale); } Ok(mount.inode) } pub fn unmount(&mut self, volume: u8) -> Result<(), Error> { let mount = self.mounts.iter_mut() .find(|mount| mount.mounted && mount.volume == volume) .ok_or(Error::NoMount)?; let next = mount.generation.checked_add(1) .ok_or(Error::GenerationOverflow)?; mount.generation = next; mount.mounted = false; Ok(()) } pub fn remount(&mut self, volume: u8) -> Result<(), Error> { let mount = self.mounts.iter_mut() .find(|mount| !mount.mounted && mount.volume == volume) .ok_or(Error::NoMount)?; mount.mounted = true; Ok(()) } }