//! A world directory and the one virtual root that host code and guests share. //! //! Layout of a world directory: //! - `world/`: the virtual root `/` that every guest sees. Agents change everything in it. //! - `host/`: host state outside the sandbox (harness storage, later the vault). No guest sees it. use sausage_vfs::{Generated, Host, Layer, Memory, Mounts, Process}; use std::{ path::{Path, PathBuf}, sync::Arc, }; /// An open world. `root` is the filesystem every guest sees as `/`; host code (the harness's file /// tools) reads and writes through the same layer, so both always see the same files. pub struct World { pub root: Arc, /// The host state folder, `/host`. pub host_dir: PathBuf, /// Live process metadata shared with the process table. pub processes: crate::process::Processes, } impl World { /// Open (and create if missing) the world directory `dir`, including `/proc` and `/run` mounts. pub fn open(dir: &Path) -> anyhow::Result { let root_dir = dir.join("world"); let host_dir = dir.join("host"); std::fs::create_dir_all(&root_dir)?; std::fs::create_dir_all(&host_dir)?; let mut mounts = Mounts::default(); mounts.mount( "/", Arc::new(Host::new(&root_dir, Process::default())?), false, ); let processes: crate::process::Processes = Arc::new(std::sync::Mutex::new(Default::default())); let snapshot = processes.clone(); let mut proc = Generated::default(); proc.insert("/processes", move || { let mut text = String::from("PID\tPPID\tSTATE\tCWD\tARGV\n"); for (pid, info) in snapshot.lock().unwrap().iter() { text.push_str(&format!( "{pid}\t{}\t{}\t{}\t{}\n", info.parent, info.state, info.cwd, info.argv.join(" ") )); } Ok(text.into_bytes()) }); mounts.mount("/proc", Arc::new(proc), true); mounts.mount("/run", Arc::new(Memory::default()), false); Ok(World { root: Arc::new(mounts), host_dir, processes, }) } }