feat(settings): settings app: apps list + per-capability allow/revoke, about (who/role), sign out; system-only caps permissions/session (install refused); pm MANAGE_BADGE copy (list/apps/allow/revoke only), APPS op; init WHO + session pass-on; factory decisions seeded for new system apps; k-ui list_item/toggle, screens/back/scroll
This commit is contained in:
@@ -109,6 +109,19 @@ fn copy_tree(fs: &mut Storage, from: &Path, to: &Path, buf: &mut [u8]) -> Result
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Copies the files in `from` that `to` lacks (no folders: decisions are
|
||||
/// one file per app).
|
||||
fn add_missing(fs: &mut Storage, from: &Path, to: &Path, buf: &mut [u8]) -> Result<(), &'static str> {
|
||||
let Some((list, n)) = entries(fs, from) else { return Ok(()) };
|
||||
for entry in list[..n].iter().filter(|e| !e.is_dir) {
|
||||
let dst = to.join(entry.name()).ok_or("path too long")?;
|
||||
if fs.open(dst.as_bytes()).and_then(|h| fs.close(h)).is_err() {
|
||||
copy_file(fs, &from.join(entry.name()).ok_or("path too long")?, &dst, buf)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Readies `folder`'s areas once storage has its key.
|
||||
pub(crate) fn prepare(fs: &mut Storage, folder: types::user::UserFolder, buf: &mut [u8]) -> Result<(), &'static str> {
|
||||
let name = core::str::from_utf8(folder.as_bytes()).map_err(|_| "a user folder named oddly")?;
|
||||
@@ -121,6 +134,11 @@ pub(crate) fn prepare(fs: &mut Storage, folder: types::user::UserFolder, buf: &m
|
||||
for (area, factory) in AREAS {
|
||||
let path = base.join(area.as_bytes()).ok_or("path too long")?;
|
||||
if fs.list_dir(path.as_bytes(), |_| {}).is_ok() {
|
||||
if area == "permissions" {
|
||||
// A system app new since this folder was made comes with
|
||||
// its decision; one the user changed is left as it is.
|
||||
add_missing(fs, &Path::new(factory), &path, buf)?;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
fs.mkdir(path.as_bytes()).map_err(|_| "cannot make an area")?;
|
||||
|
||||
@@ -164,11 +164,15 @@ impl<'a> Launcher<'a> {
|
||||
let control = crate::devices::system_control().ok_or("no SystemControl to give")?;
|
||||
syscall::delegate(control, child, perms(&[Permission::Execute])).ok_or("cannot give SystemControl")?;
|
||||
}
|
||||
if p.signs_out {
|
||||
if p.session != crate::manifest::Session::No {
|
||||
let badge = Badge::new(types::spawn::SESSION_BADGE);
|
||||
// Delegate only to hand it over: the shell's copy cannot pass it on.
|
||||
let copy = syscall::mint(self.spawn_ep, badge, perms(&[Permission::Execute, Permission::Delegate])).map_err(|_| "cannot mint")?;
|
||||
let given = syscall::delegate(copy, child, perms(&[Permission::Execute]));
|
||||
// Only a program that passes it on may.
|
||||
let rights = match p.session {
|
||||
crate::manifest::Session::PassOn => perms(&[Permission::Execute, Permission::Delegate]),
|
||||
_ => perms(&[Permission::Execute]),
|
||||
};
|
||||
let given = syscall::delegate(copy, child, rights);
|
||||
syscall::drop_cap(copy);
|
||||
given.ok_or("cannot give signing out")?;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
|
||||
use types::budget::Limits;
|
||||
|
||||
/// Whether a program holds the session: who is signed in, signing out.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Session {
|
||||
No,
|
||||
/// For itself only (the shell).
|
||||
Own,
|
||||
/// To pass on to apps granted `session` (the permission manager).
|
||||
PassOn,
|
||||
}
|
||||
|
||||
/// A board device init hands to a program over its endpoint.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Device {
|
||||
@@ -49,8 +59,8 @@ pub struct Program {
|
||||
pub spawns: Option<Limits>,
|
||||
/// May halt the machine.
|
||||
pub system_control: bool,
|
||||
/// May sign the user out (`types::spawn::SIGN_OUT`), in the next slot.
|
||||
pub signs_out: bool,
|
||||
/// The session (`types::spawn::SIGN_OUT`, `WHO`), in the next slot.
|
||||
pub session: Session,
|
||||
/// Takes Ctrl+C: a terminal copy with `types::terminal::CONTROL`, last.
|
||||
pub takes_ctrl_c: bool,
|
||||
/// Started again if it dies (see init's supervise.rs).
|
||||
@@ -84,7 +94,7 @@ const fn server(name: &'static str, file: &'static [u8], limits: Limits) -> Prog
|
||||
console: false,
|
||||
spawns: None,
|
||||
system_control: false,
|
||||
signs_out: false,
|
||||
session: Session::No,
|
||||
takes_ctrl_c: false,
|
||||
restart: true,
|
||||
restarts_clients: false,
|
||||
@@ -141,6 +151,7 @@ pub const SESSION: &[Program] = &[Program {
|
||||
spawns: Some(Limits { pages: 2048, endpoints: 8, notifications: 8, regions: 8 }),
|
||||
// The shell holds handles to the apps it ran, which die with it.
|
||||
restarts_clients: true,
|
||||
session: Session::PassOn,
|
||||
// Installing waits for the user's consent.
|
||||
pinged: false,
|
||||
..server("permission", b"bin/permission", Limits { pages: 96, ..SMALL })
|
||||
@@ -167,7 +178,7 @@ pub const DESKTOP: &[Program] = &[Program {
|
||||
serves: false,
|
||||
uses: &["terminal", "permission"],
|
||||
storage: &[users(b"home", true)],
|
||||
signs_out: true,
|
||||
session: Session::Own,
|
||||
takes_ctrl_c: true,
|
||||
..server("shell", b"bin/shell", SMALL)
|
||||
}];
|
||||
|
||||
@@ -22,12 +22,25 @@ const LONGEST_NAME: usize = 16;
|
||||
pub(crate) struct User {
|
||||
pub(crate) folder: UserFolder,
|
||||
pub(crate) guest: bool,
|
||||
name: [u8; LONGEST_NAME],
|
||||
name_len: usize,
|
||||
/// As `types::spawn::role` says.
|
||||
role: usize,
|
||||
}
|
||||
|
||||
impl User {
|
||||
/// The reply to `types::spawn::WHO`.
|
||||
pub(crate) fn who(&self) -> [usize; 4] {
|
||||
let [a, b, c] = types::name::pack(&self.name[..self.name_len]);
|
||||
[self.role, a, b, c]
|
||||
}
|
||||
}
|
||||
|
||||
/// All the gatekeeper hands over.
|
||||
struct Handover {
|
||||
flags: usize,
|
||||
id: u64,
|
||||
role: usize,
|
||||
key: [usize; 4],
|
||||
seed: [usize; 4],
|
||||
name: [u8; LONGEST_NAME],
|
||||
@@ -44,10 +57,10 @@ impl Drop for Handover {
|
||||
|
||||
/// Waits for the gatekeeper behind `ep` to let someone in.
|
||||
fn ask(ep: CapSlot) -> Result<Handover, &'static str> {
|
||||
let [reply::OK, flags, id, _role] = syscall::request(ep, [UNLOCK, 0, 0, 0]).map_err(|_| "the gatekeeper is gone")? else {
|
||||
let [reply::OK, flags, id, role] = syscall::request(ep, [UNLOCK, 0, 0, 0]).map_err(|_| "the gatekeeper is gone")? else {
|
||||
return Err("the gatekeeper could not unlock");
|
||||
};
|
||||
let mut h = Handover { flags, id: id as u64, key: [0; 4], seed: [0; 4], name: [0; LONGEST_NAME], name_len: 0, removed: [None; MAX_REMOVED] };
|
||||
let mut h = Handover { flags, id: id as u64, role, key: [0; 4], seed: [0; 4], name: [0; LONGEST_NAME], name_len: 0, removed: [None; MAX_REMOVED] };
|
||||
h.key = syscall::request(ep, [KEY, 0, 0, 0]).map_err(|_| "the gatekeeper gave no key")?;
|
||||
h.seed = syscall::request(ep, [SEED, 0, 0, 0]).map_err(|_| "the gatekeeper gave no seed")?;
|
||||
let name = syscall::request(ep, [NAME, 0, 0, 0]).map_err(|_| "the gatekeeper gave no name")?;
|
||||
@@ -111,7 +124,12 @@ pub(crate) fn sign_in(l: &mut Launcher) -> Result<(), &'static str> {
|
||||
if let Some(display) = l.endpoint("display") {
|
||||
crate::output::post(display, libkos::display::set_user_words(name.as_bytes()));
|
||||
}
|
||||
l.user = Some(User { folder, guest });
|
||||
let role = match (guest, h.role) {
|
||||
(true, _) => types::spawn::role::GUEST,
|
||||
(_, unlocked::OWNER) => types::spawn::role::OWNER,
|
||||
_ => types::spawn::role::STANDARD,
|
||||
};
|
||||
l.user = Some(User { folder, guest, name: h.name, name_len: h.name_len, role });
|
||||
start_session(l);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -182,10 +182,14 @@ impl Server for Launcher<'_> {
|
||||
|
||||
fn handle_call(&mut self, badge: usize, words: [usize; 4], cap: Option<CapSlot>) -> ([usize; 4], Option<libkos::syscall::Give>) {
|
||||
if badge == types::spawn::SESSION_BADGE {
|
||||
if words[0] == types::spawn::SIGN_OUT {
|
||||
crate::session::sign_out(self);
|
||||
}
|
||||
return ([MALFORMED, 0, 0, 0], None);
|
||||
return match words[0] {
|
||||
types::spawn::SIGN_OUT => {
|
||||
crate::session::sign_out(self);
|
||||
([MALFORMED, 0, 0, 0], None)
|
||||
}
|
||||
types::spawn::WHO => (self.user.map_or([MALFORMED, 0, 0, 0], |u| u.who()), None),
|
||||
_ => ([MALFORMED, 0, 0, 0], None),
|
||||
};
|
||||
}
|
||||
match (Request::decode(words), cap) {
|
||||
(Some(r), Some(binary)) => {
|
||||
|
||||
Reference in New Issue
Block a user