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}