Skip to main content

proot/cli/
host.rs

1//! Where a command line is interpreted: the parsers ask this for everything
2//! that depends on the process that typed it.
3//!
4//! A top-level launch is parsed in the supervisor's own process.  A nested
5//! launch (a tracee exec'ing proot or bwrap, see `exec::nested`) is parsed by
6//! the supervisor on the tracee's behalf: paths are the tracee's guest paths,
7//! the working directory and environment are the tracee's, and descriptors
8//! named on the command line are open in the tracee.
9
10use std::os::unix::ffi::OsStrExt;
11use std::path::Path;
12
13/// The process a command line is interpreted for.
14pub trait Host {
15    /// Whether `path`, as this process names it, exists.
16    fn exists(&self, path: &[u8]) -> bool;
17
18    /// Whether `path`, as this process names it, is a directory (symlinks followed).
19    fn is_dir(&self, path: &[u8]) -> bool;
20
21    /// The absolute host path behind `path` as this process names it.
22    ///
23    /// A path that does not exist is still mapped, so a binding may name a
24    /// mount point that appears later.
25    fn host_path(&self, path: &[u8]) -> Vec<u8>;
26
27    /// The working directory, as this process names it.
28    fn cwd(&self) -> Vec<u8>;
29
30    /// One environment variable.
31    fn var(&self, name: &[u8]) -> Option<Vec<u8>>;
32
33    /// The whole environment.
34    fn vars(&self) -> Vec<(Vec<u8>, Vec<u8>)>;
35
36    /// Reads descriptor `fd` of this process to its end.
37    fn read_fd(&self, fd: i32) -> std::io::Result<Vec<u8>>;
38
39    /// The host path descriptor `fd` of this process is open on.
40    fn fd_path(&self, fd: i32) -> std::io::Result<Vec<u8>>;
41}
42
43/// The supervisor's own process.
44pub struct Local;
45
46impl Host for Local {
47    fn exists(&self, path: &[u8]) -> bool {
48        Path::new(std::ffi::OsStr::from_bytes(path)).exists()
49    }
50
51    fn is_dir(&self, path: &[u8]) -> bool {
52        Path::new(std::ffi::OsStr::from_bytes(path)).is_dir()
53    }
54
55    fn host_path(&self, path: &[u8]) -> Vec<u8> {
56        if path.first() == Some(&b'/') {
57            return path.to_vec();
58        }
59        let mut cwd = self.cwd();
60        if cwd != b"/" {
61            cwd.push(b'/');
62        }
63        cwd.extend_from_slice(path);
64        cwd
65    }
66
67    fn cwd(&self) -> Vec<u8> {
68        std::env::current_dir()
69            .map(|c| c.as_os_str().as_bytes().to_vec())
70            .unwrap_or_else(|_| b"/".to_vec())
71    }
72
73    fn var(&self, name: &[u8]) -> Option<Vec<u8>> {
74        std::env::var_os(std::ffi::OsStr::from_bytes(name)).map(|v| v.as_bytes().to_vec())
75    }
76
77    fn vars(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
78        std::env::vars_os()
79            .map(|(k, v)| (k.as_bytes().to_vec(), v.as_bytes().to_vec()))
80            .collect()
81    }
82
83    fn read_fd(&self, fd: i32) -> std::io::Result<Vec<u8>> {
84        use std::io::Read;
85        use std::os::unix::io::FromRawFd;
86        if fd < 0 {
87            return Err(std::io::Error::from_raw_os_error(libc::EBADF));
88        }
89        let dup = unsafe { libc::dup(fd) };
90        if dup < 0 {
91            return Err(std::io::Error::last_os_error());
92        }
93        let mut f = unsafe { std::fs::File::from_raw_fd(dup) };
94        let mut out = Vec::new();
95        f.read_to_end(&mut out)?;
96        Ok(out)
97    }
98
99    fn fd_path(&self, fd: i32) -> std::io::Result<Vec<u8>> {
100        std::fs::read_link(format!("/proc/self/fd/{fd}")).map(|p| p.as_os_str().as_bytes().to_vec())
101    }
102}