feat(storage): scoped grants (subtree, ro/rw), narrow-only, release revokes all holders; IPC KEEP_DELEGATE opt-in; shell cd/pwd, programs get ro cwd

This commit is contained in:
2026-10-27 20:20:34 +01:00
parent c603a578c0
commit 1fc5a54f06
38 changed files with 1237 additions and 170 deletions

View File

@@ -24,6 +24,8 @@ pub enum FsError {
ReadOnly,
/// A path longer than the server accepts.
PathTooLong,
/// Outside this connection's scope, or beyond its rights.
Denied,
/// The server refused, or the call itself failed.
Failed,
}
@@ -76,6 +78,7 @@ impl Storage {
match r[0] {
reply::NOT_FOUND => Err(FsError::NotFound),
reply::READ_ONLY => Err(FsError::ReadOnly),
reply::DENIED => Err(FsError::Denied),
reply::ERROR | reply::NOT_ATTACHED => Err(FsError::Failed),
_ => Ok(r),
}
@@ -100,6 +103,20 @@ impl Storage {
self.expect([opcode, len, 0, 0], tag)
}
/// A storage capability to `path` inside this connection's scope,
/// writable only if `write` and this one is. It can be passed on;
/// `release` takes it back.
pub fn grant(&mut self, path: &[u8], write: bool) -> Result<CapSlot> {
let len = self.put_path(path)?;
let reply = syscall::call(self.ep, [op::GRANT, len, usize::from(write), 0], None)
.map_err(|_| FsError::Failed)?;
match (reply.words()[0], reply) {
(reply::GRANTED, syscall::Received::Message { cap: Some(cap), .. }) => Ok(cap),
(reply::DENIED, _) => Err(FsError::Denied),
_ => Err(FsError::Failed),
}
}
pub fn open(&mut self, path: &[u8]) -> Result<FileHandle> {
self.with_path(op::OPEN, path, reply::HANDLE).map(|h| FileHandle(h as u16))
}
@@ -181,3 +198,13 @@ impl Storage {
Ok(count)
}
}
/// Takes back `grant` and everything granted from it, from every holder;
/// the slot is empty afterwards.
pub fn release(grant: CapSlot) -> Result<()> {
match syscall::request(grant, [op::RELEASE, 0, 0, 0]) {
Ok([reply::OK, ..]) => Ok(()),
Ok([reply::DENIED, ..]) => Err(FsError::Denied),
_ => Err(FsError::Failed),
}
}