//! Native generation-tagged handle table. See os-handles/generation/CONTRACT.md. //! A caller must bind `P` to a live kernel process ID before invoking this //! table. `Object` IDs are kernel-owned names, never user pointers. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Handle(u64); impl Handle { pub const fn from_raw(raw: u64) -> Self { Self(raw) } pub const fn raw(self) -> u64 { self.0 } pub const fn slot(self) -> u32 { self.0 as u32 } pub const fn generation(self) -> u32 { (self.0 >> 32) as u32 } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Object { File(u64), Channel(u64), Task(u64), } impl Object { const fn id(self) -> u64 { match self { Self::File(id) | Self::Channel(id) | Self::Task(id) => id, } } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { BadFd, InvalidObject, Full, IdExhausted, } impl Error { pub const fn errno(self) -> u64 { match self { Self::BadFd => 9, Self::InvalidObject => 22, Self::Full => 24, Self::IdExhausted => 75, } } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct Entry
{ handle: Handle, owner: P, object: Object, } /// Complete scalar audit state. It does not expose a mutable table entry. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Snapshot
{
pub generations: [u32; N],
pub entries: [Option<(u64, P, Object)>; N],
}
pub struct Table {
pub const fn new() -> Self {
Self { entries: [None; N], generations: [0; N] }
}
/// Kernel-only deterministic seed for an isolated overflow probe. A user
/// syscall adapter must never take the seed from caller input.
pub const fn with_generation_seed(generations: [u32; N]) -> Self {
Self { entries: [None; N], generations }
}
pub fn len(&self) -> usize {
self.entries.iter().filter(|entry| entry.is_some()).count()
}
pub fn owned(&self, owner: P) -> usize {
self.entries.iter().flatten().filter(|entry| entry.owner == owner).count()
}
/// Validate all fallible conditions before publishing the entry.
pub fn open(&mut self, owner: P, object: Object) -> Result