//! Signing in and out (see the vision's Users note). The gatekeeper says //! who is in; init clears away what must go (everyone's files on a new //! keystore, a removed user's, the last guest's), hands storage the user //! and their key, readies their folder and starts their session. Signing //! out stops everything the session ran, locks storage, and the gatekeeper //! runs again. use libkos::fs::Storage; use libkos::syscall; use types::bounded::CapSlot; use types::gatekeeper::{KEY, NAME, REMOVED, SEED, UNLOCK, reply, unlocked}; use types::user::UserFolder; use crate::launch::Launcher; use crate::manifest::{GATEKEEPER, SESSION}; /// Users a keystore holds, so removed at one sign-in at most. const MAX_REMOVED: usize = 8; const LONGEST_NAME: usize = 16; #[derive(Clone, Copy)] pub(crate) struct User { pub(crate) folder: UserFolder, pub(crate) guest: bool, } /// All the gatekeeper hands over. struct Handover { flags: usize, id: u64, key: [usize; 4], seed: [usize; 4], name: [u8; LONGEST_NAME], name_len: usize, removed: [Option; MAX_REMOVED], } impl Drop for Handover { fn drop(&mut self) { self.key.fill(0); self.seed.fill(0); } } /// Waits for the gatekeeper behind `ep` to let someone in. fn ask(ep: CapSlot) -> Result { 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] }; 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")?; if let Some((text, len)) = types::log::text_of(name) { h.name_len = len.min(LONGEST_NAME); h.name[..h.name_len].copy_from_slice(&text[..h.name_len]); } for slot in h.removed.iter_mut() { match syscall::request(ep, [REMOVED, 0, 0, 0]) { Ok([reply::OK, id, ..]) => *slot = Some(id as u64), _ => break, } } Ok(h) } fn user_path(folder: UserFolder, buf: &mut [u8; 32]) -> &[u8] { buf[..6].copy_from_slice(b"users/"); let f = folder.as_bytes(); buf[6..6 + f.len()].copy_from_slice(f); &buf[..6 + f.len()] } /// Removes `folder`'s files; none there is fine. fn purge(fs: &mut Storage, folder: UserFolder) -> Result<(), &'static str> { fs.purge(user_path(folder, &mut [0; 32])).map_err(|_| "cannot remove a user's files") } /// Runs the gatekeeper until someone is in and starts their session. pub(crate) fn sign_in(l: &mut Launcher) -> Result<(), &'static str> { crate::out!("init: loading gatekeeper... "); let gatekeeper = l.launch(&GATEKEEPER)?; crate::outln!("ok{}", gatekeeper.note); let handover = gatekeeper.ep.ok_or("the gatekeeper serves nothing").and_then(ask); // It exits on its own once it has handed over. syscall::drop_cap(gatekeeper.process); let h = handover?; let guest = h.flags & unlocked::GUEST != 0; let folder = if guest { UserFolder::GUEST } else { UserFolder::of(h.id) }; let (fs, scratch) = l.storage(); // While storage is locked: it removes trees only then. if h.flags & unlocked::FRESH != 0 { fs.purge(b"users").map_err(|_| "cannot clear the users of an old keystore")?; crate::outln!("init: a new keystore, every earlier user's files removed"); } for id in h.removed.iter().flatten() { purge(fs, UserFolder::of(*id))?; crate::outln!("init: a removed user's files removed"); } if guest { purge(fs, UserFolder::GUEST)?; } let handed = fs.set_user(h.id, guest).is_ok() && [(false, h.key), (true, h.seed)].iter().all(|(seed, w)| (0..2).all(|half| fs.set_half(*seed, half, [w[half * 2], w[half * 2 + 1]]).is_ok())); if !handed { return Err("storage would not take the key"); } crate::factory::prepare(fs, folder, scratch)?; let name = core::str::from_utf8(&h.name[..h.name_len]).unwrap_or("someone"); crate::outln!("init: {} is in", name); 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 }); start_session(l); Ok(()) } /// Starts what runs for a signed-in user; it is stopped at sign-out. fn start_session(l: &mut Launcher) { let desktop = l.desktop; for p in SESSION.iter().chain(desktop) { crate::out!("init: loading {}... ", p.name); match l.run(p, true) { Ok(note) => crate::outln!("ok{}", note), Err(why) => crate::outln!("SKIP ({})", why), } } } /// Stops the session, locks storage, and lets the next one in. pub(crate) fn sign_out(l: &mut Launcher) { l.spawner.clear(); l.stop_session(); if let Some(display) = l.endpoint("display") { crate::output::post(display, libkos::display::set_user_words(b"")); } let user = l.user.take(); let (fs, _) = l.storage(); let locked = fs.lock().is_ok(); // A guest leaves nothing behind. let cleared = !user.is_some_and(|u| u.guest) || purge(fs, UserFolder::GUEST).is_ok(); crate::outln!("init: signed out"); let next = match locked && cleared { true => sign_in(l), false => Err("storage would not lock"), }; if let Err(why) = next { crate::outln!("init: STOP ({})", why); crate::idle(); } }