//! Process identity is minted from a kernel owned process ID.
//! User requests may inspect an assigned identity, but cannot replace it.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Principal(u64);
impl Principal {
pub const fn id(self) -> u64 { self.0 }
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Error {
Full,
Duplicate,
Unknown,
Access,
Exhausted,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct Binding
{
process: P,
principal: Principal,
}
pub struct Registry {
next: u64,
bindings: [Option>; N],
}
impl Registry {
pub fn new() -> Self {
Self { next: 1, bindings: [None; N] }
}
/// Called only after the kernel has obtained a genuine ProcessId.
pub fn bind_kernel(&mut self, process: P) -> Result {
if self.bindings.iter().flatten().any(|row| row.process == process) {
return Err(Error::Duplicate);
}
let slot = self.bindings.iter_mut().find(|slot| slot.is_none())
.ok_or(Error::Full)?;
let following = self.next.checked_add(1).ok_or(Error::Exhausted)?;
let principal = Principal(self.next);
*slot = Some(Binding { process, principal });
self.next = following;
Ok(principal)
}
pub fn principal(&self, process: P) -> Result {
self.bindings.iter().flatten()
.find(|row| row.process == process)
.map(|row| row.principal).ok_or(Error::Unknown)
}
/// The syscall path always returns an explicit denial for a live process.
/// `requested` is deliberately never used to construct a Principal.
pub fn request_user_change(&self, process: P, _requested: u64) -> Result<(), Error> {
self.principal(process)?;
Err(Error::Access)
}
pub fn retire_kernel(&mut self, process: P) -> Result<(), Error> {
let slot = self.bindings.iter_mut()
.find(|slot| slot.is_some_and(|row| row.process == process))
.ok_or(Error::Unknown)?;
*slot = None;
Ok(())
}
pub fn snapshot(&self) -> (u64, [Option<(P, u64)>; N]) {
(self.next, self.bindings.map(|row| row.map(|row| (row.process, row.principal.id()))))
}
}
impl Default for Registry {
fn default() -> Self { Self::new() }
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Handle(u64);
impl Handle {
pub const fn id(self) -> u64 { self.0 }
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Capability {
ReadOnlyFile { object: u64, allow_child: bool },
KernelDevice { object: u64 },
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct Grant
{
handle: Handle,
owner: P,
capability: Capability,
}
pub struct HandleSet {
next: u64,
grants: [Option>; M],
}
impl HandleSet {
pub fn new() -> Self {
Self { next: 1, grants: [None; M] }
}
pub fn issue_kernel(
&mut self, identities: &Registry, owner: P, capability: Capability,
) -> Result {
identities.principal(owner)?;
let slot = self.grants.iter_mut().find(|slot| slot.is_none())
.ok_or(Error::Full)?;
let following = self.next.checked_add(1).ok_or(Error::Exhausted)?;
let handle = Handle(self.next);
*slot = Some(Grant { handle, owner, capability });
self.next = following;
Ok(handle)
}
pub fn lookup(&self, owner: P, handle: u64) -> Result {
self.grants.iter().flatten()
.find(|grant| grant.owner == owner && grant.handle.id() == handle)
.map(|grant| grant.capability).ok_or(Error::Unknown)
}
/// The caller supplies `actual_parent` from the live process table record.
/// A child receives only a named, explicitly inheritable read-only file.
pub fn inherit_explicit(
&mut self, identities: &Registry, parent: P, child: P,
actual_parent: Option
, requested: u64,
) -> Result {
identities.principal(parent)?;
identities.principal(child)?;
if parent == child || actual_parent != Some(parent) {
return Err(Error::Access);
}
let inherited = match self.lookup(parent, requested)? {
Capability::ReadOnlyFile { object, allow_child: true } =>
Capability::ReadOnlyFile { object, allow_child: false },
_ => return Err(Error::Access),
};
let slot = self.grants.iter_mut().find(|slot| slot.is_none())
.ok_or(Error::Full)?;
let following = self.next.checked_add(1).ok_or(Error::Exhausted)?;
let handle = Handle(self.next);
*slot = Some(Grant { handle, owner: child, capability: inherited });
self.next = following;
Ok(handle)
}
pub fn close_owner(&mut self, owner: P) -> usize {
let mut closed = 0;
for slot in &mut self.grants {
if slot.is_some_and(|grant| grant.owner == owner) {
*slot = None;
closed += 1;
}
}
closed
}
pub fn snapshot(&self) -> (u64, [Option<(u64, P, Capability)>; M]) {
(self.next, self.grants.map(|grant| grant.map(|grant|
(grant.handle.id(), grant.owner, grant.capability))))
}
}
impl Default for HandleSet {
fn default() -> Self { Self::new() }
}