Skip to main content

proot/
state.rs

1//! Supervisor-wide shared state.
2
3use crate::config::Config;
4use crate::fs::Fs;
5use crate::fs::binding::DirCache;
6use crate::fs::glue::{self, Placeholder};
7use crate::procs::{Ids, ProcTable, TraceeInfo};
8use crate::tracee::mem::Mem;
9use std::sync::atomic::{AtomicBool, Ordering};
10use std::sync::{Arc, Mutex};
11
12/// One guest: a filesystem view and the launch options that go with it.
13///
14/// The first tracee gets the view the command line describes.  A tracee
15/// that execs proot or bwrap gets a view of its own, composed through the
16/// one it asked from, which its descendants inherit (see `exec::nested`).
17#[derive(Debug)]
18pub struct View {
19    /// Root and bindings, in host terms.
20    pub fs: Fs,
21    /// Value reported by `uname(2)` in the `release` field, if faked.
22    pub kernel_release: Option<Vec<u8>>,
23    /// Value reported by `uname(2)` in the `nodename` field, if faked.
24    pub hostname: Option<Vec<u8>>,
25    /// Pretend to be root (`-0`).
26    pub fake_root: bool,
27    /// Directories removed once the last process of the view is gone (a
28    /// nested bwrap's materialized root).
29    cleanup_dirs: Vec<Vec<u8>>,
30    /// Mount points created for the bindings, removed with the view.
31    placeholders: Mutex<Vec<Placeholder>>,
32}
33
34impl View {
35    /// Builds a view from a configuration whose paths are host paths, and
36    /// creates the mount points its bindings are missing (see [`glue`]).
37    ///
38    /// # Arguments
39    /// * `config` - Root, bindings and flags.
40    /// * `procs` - Process table shared with the supervisor.
41    /// * `cache` - Directory cache shared with the supervisor's other views.
42    /// * `cleanup` - Remove `config.cleanup_dirs` when the view is dropped.
43    pub fn new(config: &Config, procs: Arc<ProcTable>, cache: Arc<DirCache>, cleanup: bool) -> View {
44        let fs = Fs::with_cache(config, procs, cache);
45        let placeholders = Mutex::new(glue::create(&fs));
46        View {
47            fs,
48            kernel_release: config.kernel_release.clone(),
49            hostname: config.hostname.clone(),
50            fake_root: config.fake_root,
51            cleanup_dirs: if cleanup {
52                config.cleanup_dirs.clone()
53            } else {
54                Vec::new()
55            },
56            placeholders,
57        }
58    }
59
60    /// Removes the mount points created for this view's bindings.
61    ///
62    /// Called when the guest is done; later calls do nothing.
63    pub fn remove_placeholders(&self) {
64        let placeholders = std::mem::take(&mut *self.placeholders.lock().unwrap_or_else(|e| e.into_inner()));
65        if !placeholders.is_empty() {
66            glue::remove(&placeholders);
67            self.fs.flush_cache();
68        }
69    }
70}
71
72impl Drop for View {
73    fn drop(&mut self) {
74        use std::os::unix::ffi::OsStrExt;
75        self.remove_placeholders();
76        for dir in &self.cleanup_dirs {
77            let _ = std::fs::remove_dir_all(std::ffi::OsStr::from_bytes(dir));
78        }
79    }
80}
81
82/// Everything handlers need, shared read-mostly between all threads.
83pub struct Ctx {
84    pub config: Config,
85    /// The view of the first tracee, and of every process without one of its own.
86    pub root: Arc<View>,
87    /// Directory cache shared by every view.
88    pub cache: Arc<DirCache>,
89    pub mem: Mem,
90    /// Per-process bookkeeping, shared with `fs` for `/proc` magic links.
91    pub procs: Arc<ProcTable>,
92    /// True when running without ptrace: the former ptrace tier is served
93    /// through notifications with in-place rewrites (see `syscall::direct`).
94    pub no_ptrace: bool,
95    /// The mailbox descriptor of the guest-stub restricted mode, installed
96    /// into every process that execs (see `guest_stub`); `None` elsewhere.
97    pub mailbox: Option<i32>,
98    /// Set once any process has a view of its own; until then every lookup
99    /// answers the root view without touching the process table.
100    nested: AtomicBool,
101}
102
103impl Ctx {
104    /// Builds the shared state from a configuration.
105    ///
106    /// # Arguments
107    /// * `config` - Parsed command line.
108    /// * `no_ptrace` - Run without a ptrace tier.
109    pub fn new(config: Config, no_ptrace: bool) -> Ctx {
110        let procs = Arc::new(ProcTable::new(no_ptrace));
111        let cache = Arc::new(DirCache::new());
112        let root = Arc::new(View::new(&config, procs.clone(), cache.clone(), false));
113        Ctx {
114            config,
115            root,
116            cache,
117            mem: Mem,
118            procs,
119            no_ptrace,
120            mailbox: None,
121            nested: AtomicBool::new(false),
122        }
123    }
124
125    /// Whether guests run the stub of the guest-stub restricted mode.
126    ///
127    /// # Returns
128    /// `true` when a mailbox was set up.
129    pub fn trap_mode(&self) -> bool {
130        self.mailbox.is_some()
131    }
132
133    /// The view the process of thread `tid` lives in.
134    ///
135    /// # Arguments
136    /// * `tid` - Thread or process id.
137    pub fn view(&self, tid: i32) -> Arc<View> {
138        if !self.nested.load(Ordering::Relaxed) {
139            return self.root.clone();
140        }
141        self.procs.view_of(tid).unwrap_or_else(|| self.root.clone())
142    }
143
144    /// Moves the process of thread `tid` into `view`, with `cwd` as its
145    /// working directory there when the working directory is virtual.
146    ///
147    /// # Arguments
148    /// * `tid` - Thread or process id.
149    /// * `view` - The view to enter.
150    /// * `cwd` - Working directory as a guest path of `view`.
151    pub fn enter_view(&self, tid: i32, view: Arc<View>, cwd: Vec<u8>) {
152        self.nested.store(true, Ordering::Relaxed);
153        self.procs.update(tid, |t| {
154            t.view = Some(view);
155            if t.cwd.is_some() {
156                t.cwd = Some(cwd);
157            }
158        });
159    }
160
161    /// Returns a copy of what is known about the process of thread `tid`.
162    pub fn tracee(&self, tid: i32) -> TraceeInfo {
163        self.procs.info(tid)
164    }
165
166    /// Records the executable the process of thread `tid` is now running.
167    pub fn set_exe(&self, tid: i32, guest: Vec<u8>, host: Vec<u8>) {
168        self.procs.set_exe(tid, guest, host);
169    }
170
171    /// The virtual identity of the process of thread `tid` (root until changed).
172    pub fn ids(&self, tid: i32) -> Ids {
173        self.procs.ids(tid)
174    }
175
176    /// Replaces the virtual identity of the process of thread `tid`.
177    pub fn set_ids(&self, tid: i32, ids: Ids) {
178        self.procs.set_ids(tid, ids);
179    }
180
181    /// Whether the process of thread `tid` still reads as dumpable.
182    pub fn dumpable(&self, tid: i32) -> bool {
183        self.procs.dumpable(tid)
184    }
185
186    /// Records the dumpable flag the process of thread `tid` set.
187    pub fn set_dumpable(&self, tid: i32, dumpable: bool) {
188        self.procs.set_dumpable(tid, dumpable);
189    }
190
191    /// Supplementary groups of the process of thread `tid`, if `setgroups` was called.
192    pub fn groups(&self, tid: i32) -> Option<Vec<u32>> {
193        self.procs.groups(tid)
194    }
195
196    /// Records the supplementary groups of the process of thread `tid`.
197    pub fn set_groups(&self, tid: i32, groups: Vec<u32>) {
198        self.procs.set_groups(tid, groups);
199    }
200
201    /// Copies the bookkeeping of the process of `parent` to a newly forked `child`.
202    pub fn fork(&self, parent: i32, child: i32) {
203        self.procs.fork(parent, child);
204    }
205
206    /// Forgets a process or thread that exited.
207    pub fn forget(&self, pid: i32) {
208        self.procs.forget(pid);
209    }
210
211    /// Thread-group id of a thread.
212    pub fn tgid(&self, tid: i32) -> i32 {
213        self.procs.tgid(tid)
214    }
215}