11/// Builder for running commands inside a target os tree using bubblewrap (bwrap).
2+ use std:: borrow:: Cow ;
3+ use std:: env;
24use std:: ffi:: OsStr ;
5+ use std:: os:: fd:: AsRawFd ;
36use std:: process:: Command ;
47
58use anyhow:: Result ;
9+ use cap_std_ext:: camino:: { Utf8Path , Utf8PathBuf } ;
10+ use cap_std_ext:: cap_std:: fs:: Dir ;
611
712use crate :: CommandRunExt ;
813
914/// Builder for running commands inside a target directory using bwrap.
10- #[ derive( Debug , Default ) ]
15+ #[ derive( Debug ) ]
1116pub struct BwrapCmd < ' a > {
1217 /// The target directory to use as root for the container
13- chroot_path : & ' a str ,
18+ chroot_path : Cow < ' a , Utf8Path > ,
1419 /// Bind mounts in format (source, target)
1520 bind_mounts : Vec < ( & ' a str , & ' a str ) > ,
1621 /// Device nodes to bind into the container
@@ -20,17 +25,36 @@ pub struct BwrapCmd<'a> {
2025}
2126
2227impl < ' a > BwrapCmd < ' a > {
23- /// Create a new BwrapCmd builder with the given root directory.
24- pub fn new ( path : & ' a str ) -> Self {
28+ /// Create a new BwrapCmd builder with a root directory as a File Descriptor.
29+ #[ allow( dead_code) ]
30+ pub fn new_with_dir ( path : & ' a Dir ) -> Self {
31+ let fd_path: String = format ! ( "/proc/self/fd/{}" , path. as_raw_fd( ) ) ;
2532 Self {
26- chroot_path : path,
27- ..Default :: default ( )
33+ chroot_path : Cow :: Owned ( Utf8PathBuf :: from ( & fd_path) ) ,
34+ bind_mounts : Vec :: new ( ) ,
35+ devices : Vec :: new ( ) ,
36+ env_vars : Vec :: new ( ) ,
37+ }
38+ }
39+
40+ /// Create a new BwrapCmd builder with a root directory
41+ pub fn new ( path : & ' a Utf8Path ) -> Self {
42+ Self {
43+ chroot_path : Cow :: Borrowed ( path) ,
44+ bind_mounts : Vec :: new ( ) ,
45+ devices : Vec :: new ( ) ,
46+ env_vars : Vec :: new ( ) ,
2847 }
2948 }
3049
3150 /// Add a bind mount from source to target inside the container.
32- pub fn bind ( mut self , source : & ' a str , target : & ' a str ) -> Self {
33- self . bind_mounts . push ( ( source, target) ) ;
51+ pub fn bind (
52+ mut self ,
53+ source : & ' a impl AsRef < Utf8Path > ,
54+ target : & ' a impl AsRef < Utf8Path > ,
55+ ) -> Self {
56+ self . bind_mounts
57+ . push ( ( source. as_ref ( ) . as_str ( ) , target. as_ref ( ) . as_str ( ) ) ) ;
3458 self
3559 }
3660
@@ -51,14 +75,22 @@ impl<'a> BwrapCmd<'a> {
5175 let mut cmd = Command :: new ( "bwrap" ) ;
5276
5377 // Bind the root filesystem
54- cmd. args ( [ "--bind" , self . chroot_path , "/" ] ) ;
78+ cmd. args ( [ "--bind" , self . chroot_path . as_str ( ) , "/" ] ) ;
5579
5680 // Setup API filesystems
5781 // See https://systemd.io/API_FILE_SYSTEMS/
5882 cmd. args ( [ "--proc" , "/proc" ] ) ;
5983 cmd. args ( [ "--dev" , "/dev" ] ) ;
6084 cmd. args ( [ "--ro-bind" , "/sys" , "/sys" ] ) ;
6185
86+ // the $PATH available in the bwrap is a default one so let's forward the
87+ // existing $PATH of inject a reasonnable default.
88+ let inject_path = env:: var ( "PATH" ) . unwrap_or ( {
89+ String :: from ( "bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin" )
90+ } ) ;
91+
92+ cmd. args ( [ "--setenv" , "PATH" , inject_path. as_str ( ) ] ) ;
93+
6294 // Add bind mounts
6395 for ( source, target) in & self . bind_mounts {
6496 cmd. args ( [ "--bind" , source, target] ) ;
0 commit comments