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

@@ -59,7 +59,7 @@ pub fn receive_device(endpoint: CapSlot) -> Device {
pub fn give_device(endpoint: CapSlot, device: Device) -> Result<()> {
for (what, cap) in [(MMIO, device.mmio), (IRQ, device.irq)] {
if let Some(cap) = cap {
syscall::call(endpoint, [SETUP, what, device.kind, 0], Some(cap))?;
syscall::call(endpoint, [SETUP, what, device.kind, 0], Some(cap.into()))?;
}
}
syscall::call(endpoint, [SETUP, DONE, 0, 0], None).map(|_| ())

View File

@@ -60,7 +60,11 @@ pub fn subscribe_input(ep: CapSlot) -> Option<CapSlot> {
// Signal only (Delegate just to send it; the transfer strips it).
let signal = PermissionSet::empty().with(Permission::Write).with(Permission::Delegate);
let copy = syscall::mint(notify, Badge::NONE, signal).ok()?;
let sent = syscall::call(ep, [types::display::input::SUBSCRIBE, 0, 0, 0], Some(copy));
let sent = syscall::call(
ep,
[types::display::input::SUBSCRIBE, 0, 0, 0],
Some(copy.into()),
);
syscall::drop_cap(copy);
(sent.ok()?.words()[0] == 0).then_some(notify)
}

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),
}
}

View File

@@ -4,6 +4,8 @@ use super::{call0, raw, slot_of, Error, Result};
use types::bounded::CapSlot;
use types::syscall_abi::*;
pub use types::syscall_abi::Give;
/// What a receiving call got.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Received {
@@ -48,7 +50,7 @@ pub fn create_endpoint() -> Option<CapSlot> {
slot_of(call0(SYS_CREATE_ENDPOINT)).ok()
}
pub fn send_msg(endpoint: CapSlot, words: [usize; 4], cap: Option<CapSlot>) -> Result<()> {
pub fn send_msg(endpoint: CapSlot, words: [usize; 4], cap: Option<Give>) -> Result<()> {
raw(
SYS_SEND,
message_regs(endpoint.value() as usize, words, cap),
@@ -61,7 +63,7 @@ pub fn receive_msg(endpoint: CapSlot) -> Result<Received> {
}
/// Sends and waits for the reply.
pub fn call(endpoint: CapSlot, words: [usize; 4], cap: Option<CapSlot>) -> Result<Received> {
pub fn call(endpoint: CapSlot, words: [usize; 4], cap: Option<Give>) -> Result<Received> {
raw(
SYS_CALL,
message_regs(endpoint.value() as usize, words, cap),
@@ -80,12 +82,12 @@ pub fn request(endpoint: CapSlot, words: [usize; 4]) -> Result<[usize; 4]> {
}
/// Answers the last `Call` this thread received.
pub fn reply(words: [usize; 4], cap: Option<CapSlot>) -> Result<()> {
pub fn reply(words: [usize; 4], cap: Option<Give>) -> Result<()> {
raw(SYS_REPLY, message_regs(0, words, cap)).map(|_| ())
}
/// Replies to the pending `Call` (if any), then receives the next message.
pub fn reply_recv(endpoint: CapSlot, words: [usize; 4], cap: Option<CapSlot>) -> Result<Received> {
pub fn reply_recv(endpoint: CapSlot, words: [usize; 4], cap: Option<Give>) -> Result<Received> {
raw(
SYS_REPLY_RECV,
message_regs(endpoint.value() as usize, words, cap),