1use nix::env::clearenv;
2use nix::sys::wait;
3use nix::{mount, sched, unistd};
4use std::fs;
5use std::path::{Path, PathBuf};
6use uuid::Uuid;
7
8pub fn run_in_sandbox(
9 new_root: &Path,
10 prepare_sandbox: impl Fn() -> Result<(), String>,
11 run: impl Fn() -> isize,
12) -> Result<i32, String> {
13 let forked_logic = || -> isize {
14 if let Err(err) = unsafe { clearenv() } {
15 eprintln!("Could not clear environment variables: {}", err);
16 return 255;
17 }
18 if let Err(err) = prepare_sandbox() {
19 eprintln!("Error preparing the sandbox: {}", err);
20 return 255;
21 }
22 if let Err(err) = pivot_root(new_root) {
23 eprintln!("Error setting up builder sandbox: {}", err);
24 return 255;
25 }
26 run()
27 };
28
29 let pid = sched::clone(
30 Box::new(forked_logic),
31 &mut vec![0u8; 1024 * 1024],
32 sched::CloneFlags::CLONE_NEWNS | sched::CloneFlags::CLONE_NEWUSER,
33 Some(libc::SIGCHLD),
34 )
35 .map_err(|err| format!("Failed to start the builder process. Error: {}", err))?;
36
37 match wait::waitpid(pid, None) {
38 Ok(wait::WaitStatus::Exited(_, exit_code)) => Ok(exit_code),
39 Ok(wait::WaitStatus::Signaled(_, signal, core_dumped)) => {
40 eprintln!(
41 "Builder killed by signal {} (core dumped: {})",
42 signal, core_dumped,
43 );
44 Ok(255)
45 }
46 Ok(state) => {
47 eprintln!("Unexpected builder process state: {:?}", state);
48 Ok(255)
49 }
50 Err(err) => {
51 eprintln!("Error waiting for the builder process: {}", err);
52 Ok(255)
53 }
54 }
55}
56
57pub fn mount_paths<'a>(
58 paths: impl Iterator<Item = &'a Path>,
59 new_root: &Path,
60) -> Result<(), String> {
61 for path in paths {
62 mount_path(path, new_root)?;
63 }
64 Ok(())
65}
66
67pub fn mount_path(path: &Path, new_root: &Path) -> Result<(), String> {
68 let target_path = prepare_mount_path(path, new_root)?;
69 mount::mount(
70 Some(path),
71 &target_path,
72 None::<&str>,
73 mount::MsFlags::MS_BIND | mount::MsFlags::MS_REC,
74 None::<&str>,
75 )
76 .map_err(|e| {
77 format!(
78 "Failed to bind mount {:?} to {:?}. Error: {}",
79 path, target_path, e
80 )
81 })
82}
83
84pub fn pivot_root(new_root: &Path) -> Result<(), String> {
85 mount_rootfs(new_root)?;
86 let old_root_name = Uuid::new_v4().to_string();
87 let old_root = new_root.join(&old_root_name);
88 let old_root_absolute = Path::new("/").join(old_root_name);
89 fs::create_dir_all(&old_root).map_err(|e| format!("Error creating oldroot: {}", e))?;
90 unistd::pivot_root(new_root, &old_root)
91 .map_err(|e| format!("Error pivoting to new root: {}", e))?;
92 unistd::chdir("/").map_err(|e| format!("Error cd'ing to new root: {}", e))?;
93 mount::umount2(&old_root_absolute, mount::MntFlags::MNT_DETACH)
94 .map_err(|e| format!("Error unmounting old root: {}", e))?;
95 std::fs::remove_dir_all(&old_root_absolute)
96 .map_err(|e| format!("Error removing old root: {}", e))
97}
98
99fn mount_rootfs(new_root: &Path) -> Result<(), String> {
100 mount::mount(
103 Some("/"),
104 "/",
105 None::<&str>,
106 mount::MsFlags::MS_PRIVATE | mount::MsFlags::MS_REC,
107 None::<&str>,
108 )
109 .map_err(|e| format!("Error mounting old root: {}", e))?;
110
111 mount::mount(
112 Some(new_root),
113 new_root,
114 None::<&str>,
115 mount::MsFlags::MS_BIND | mount::MsFlags::MS_REC,
116 None::<&str>,
117 )
118 .map_err(|e| format!("Failed to mount new root: {}", e))
119}
120
121fn prepare_mount_path(source_path: &Path, new_root: &Path) -> Result<PathBuf, String> {
122 let path_without_root = source_path.strip_prefix("/").map_err(|e| {
123 format!(
124 "Could not remove '/' from path {:?}. Error: {}",
125 source_path, e
126 )
127 })?;
128 let target_path = new_root.join(path_without_root);
129 if source_path.is_dir() {
130 fs::create_dir_all(&target_path)
131 .map_err(|e| format!("Error creating directory {:?}: {}", target_path, e))?;
132 } else {
133 if let Some(parent) = target_path.parent() {
134 fs::create_dir_all(parent).map_err(|e| {
135 format!(
136 "Error creating parent directories for {:?}: {}",
137 source_path, e
138 )
139 })?;
140 }
141 fs::write(&target_path, "")
142 .map_err(|e| format!("Error creating empty target file {:?}: {}", source_path, e))?;
143 }
144 return Ok(target_path);
145}