xtask_todo_lib/devshell/sandbox/mod.rs
1//! Sandbox: export VFS to a temp dir for isolated execution (e.g. rustup/cargo), then sync back.
2//!
3//! ## Isolation (no Podman/Docker)
4//!
5//! Devshell **does not** invoke `podman`, `docker`, or any OCI runtime. Flow: export VFS subtree to a
6//! unique host temp dir (`0o700` on Unix) → run `cargo` / `rustup` from the host `PATH` with `cwd` set
7//! to the export root → sync back → remove the temp dir.
8//!
9//! **Linux optional mount namespace** — set **`DEVSHELL_RUST_MOUNT_NAMESPACE=1`** (or `true` / `yes`) so the
10//! child process calls `unshare(CLONE_NEWNS)` and makes the mount tree private (`MS_REC | MS_PRIVATE`)
11//! before `exec`. That gives a **separate mount namespace** (kernel feature via libc), similar in spirit
12//! to container mount isolation but **without** a container engine. It does **not** hide the host
13//! filesystem from the child; a full root jail would need additional work (e.g. `pivot_root`).
14//!
15//! On non-Linux platforms the env var is ignored.
16//!
17//! ## Unix execute bit on `target/` binaries
18//!
19//! VFS sync uses [`std::fs::write`], which creates files without the execute bit. After a round-trip,
20//! `target/debug/foo` is often **0644** while still a valid ELF. `cargo run` may skip rebuild and then
21//! **execve** fails with **EACCES (Permission denied)**. Before running `cargo`/`rustup`, we walk
22//! `target/` and set **0755** on files that look like **ELF** objects.
23
24mod elf;
25mod error;
26mod export;
27mod paths;
28mod run;
29mod sync;
30
31#[cfg(target_os = "linux")]
32mod linux_mount;
33
34#[cfg(test)]
35mod tests;
36
37pub use error::SandboxError;
38pub use export::export_vfs_to_temp_dir;
39pub use paths::{devshell_export_parent_dir, find_in_path, ENV_EXPORT_BASE};
40pub use run::{run_in_export_dir, run_rust_tool};
41pub use sync::sync_host_dir_to_vfs;
42
43pub(crate) use elf::restore_execute_bits_for_build_artifacts;
44pub(crate) use sync::host_export_root;