Add -r/--run and -c/--cleanup, fix nsenter under root

-r/--run mounts an image, runs a command under bwrap in the
foreground (default /bin/sh, overridable via -- <command> [args...]),
then unmounts and cleans up when it exits. -c/--cleanup deletes a
layer and its ancestor chain from local storage (containers-storage
delete-layer, walking parents via `layer --json`), since -u only ever
unmounted.

bwrap needs to see the merged mount from inside the private namespace
containers-storage mount creates when running rootless; run_bwrap()
locates the live fuse-overlayfs process and runs bwrap via nsenter
into its namespaces. When running as root no such namespace exists
(containers-storage doesn't need to reexec for privilege), so nsenter
fails with EINVAL; detect geteuid() == 0 and skip it automatically
there. -n/--no-nsenter forces it off manually for any other case.

process.cpp gains run_process_foreground() (inherited stdio, for the
interactive bwrap run) and the relocated find_in_path(), now shared
with bwrap.cpp's nsenter lookup.

Also: meson test only ran -m, leaking a layer on every run; it now
runs tests/run_test.py, which drives mount -> umount -> cleanup and
fails if any step does.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-17 07:44:24 +00:00
parent 839551a648
commit c0bef0d989
10 changed files with 525 additions and 98 deletions
+17
View File
@@ -25,3 +25,20 @@
// support (e.g. stock Android kernels), where blindly passing every
// --unshare-xxx flag to bwrap would make it fail outright.
std::vector<std::string> detect_bwrap_unshare_args();
// Assembles the full bwrap argv (program name included) to run `command` with
// `root` bound as the sandbox's filesystem root, using whichever --unshare-xxx
// flags the kernel supports (see detect_bwrap_unshare_args()).
std::vector<std::string> build_bwrap_args(const std::string& root,
const std::vector<std::string>& command);
// Runs bwrap against `root` (the merged mount path from mount_layer()) in the
// foreground and waits for it to exit. If `use_nsenter` is true, first locates the
// fuse-overlayfs process serving `root` and runs bwrap via nsenter into that
// process's user+mount namespaces -- needed because containers-storage mount
// (rootless) creates the overlay mount inside a private namespace invisible to a
// plain child process on kernels where fuse-overlayfs isolates it that way. Pass
// use_nsenter=false on kernels where the mount is already directly visible
// (observed on kernels older than 4.18, per fuse-overlayfs's own release notes).
// Returns bwrap's exit code, or -1 on failure to launch.
int run_bwrap(const std::string& root, const std::vector<std::string>& command, bool use_nsenter);