//! Two-handle read-only open table backed by validated dentry generations. use crate::os_vfs_lookup::{Dentry, LookupTable}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { InvalidFlags, Stale, Erofs, NoSpace, IdOverflow, BadHandle, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct OpenFlags { pub read: bool, pub write: bool, pub create: bool, } impl OpenFlags { pub const READ_ONLY: Self = Self { read: true, write: false, create: false }; } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Handle { pub id: u64, pub volume: u8, pub inode: u64, pub mount_generation: u64, pub read: bool, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct OpenTable { handles: [Option; 2], next_id: u64, } impl OpenTable { pub const fn new() -> Self { Self { handles: [None; 2], next_id: 1 } } pub fn active_handles(&self) -> usize { self.handles.iter().filter(|handle| handle.is_some()).count() } pub fn open(&mut self, lookup: &LookupTable, dentry: Dentry, flags: OpenFlags) -> Result { if !flags.read && !flags.write && !flags.create { return Err(Error::InvalidFlags); } lookup.validate(dentry).map_err(|_| Error::Stale)?; if flags.write || flags.create { return Err(Error::Erofs); } let slot = self.handles.iter().position(|handle| handle.is_none()) .ok_or(Error::NoSpace)?; let next = self.next_id.checked_add(1).ok_or(Error::IdOverflow)?; let handle = Handle { id: self.next_id, volume: dentry.volume, inode: dentry.inode, mount_generation: dentry.generation, read: true }; self.handles[slot] = Some(handle); self.next_id = next; Ok(handle) } pub fn close(&mut self, handle: Handle) -> Result<(), Error> { let slot = self.handles.iter().position(|active| *active == Some(handle)) .ok_or(Error::BadHandle)?; self.handles[slot] = None; Ok(()) } }