| 1 | |
| 2 | |
| 3 | |
| 4 | use std::fs; |
| 5 | use std::path::Path; |
| 6 | use std::process::{Command, Stdio}; |
| 7 | |
| 8 | use forge_runner::sandbox::idmap::{IdMap, UsernsMode, remove_tree}; |
| 9 | |
| 10 | #[test] |
| 11 | fn remove_tree_deletes_files_owned_by_subordinate_ids() { |
| 12 | let map = match IdMap::resolve(UsernsMode::Subuid) { |
| 13 | Ok(map) => map, |
| 14 | Err(e) => { |
| 15 | eprintln!("skipping: {e}"); |
| 16 | return; |
| 17 | } |
| 18 | }; |
| 19 | let dir = tempfile::tempdir().expect("tempdir"); |
| 20 | let job = dir.path().join("job"); |
| 21 | fs::create_dir_all(job.join("sub")).expect("create job dir"); |
| 22 | let file = job.join("sub/f"); |
| 23 | fs::write(&file, b"x").expect("write file"); |
| 24 | |
| 25 | let chown = Command::new("unshare") |
| 26 | .args(["--map-auto", "--map-root-user", "chown", "1000:1000"]) |
| 27 | .arg(&file) |
| 28 | .stdout(Stdio::null()) |
| 29 | .stderr(Stdio::null()) |
| 30 | .status(); |
| 31 | if !chown.is_ok_and(|s| s.success()) { |
| 32 | eprintln!("skipping: unshare --map-auto cannot chown"); |
| 33 | return; |
| 34 | } |
| 35 | |
| 36 | let exe = Path::new(env!("CARGO_BIN_EXE_forge-runner")); |
| 37 | remove_tree(&map, &job, exe).expect("remove_tree"); |
| 38 | assert!(!job.exists(), "{} should be gone", job.display()); |
| 39 | } |
| 40 | |
| 41 | #[test] |
| 42 | fn remove_tree_treats_a_missing_path_as_done() { |
| 43 | let dir = tempfile::tempdir().expect("tempdir"); |
| 44 | let missing = dir.path().join("missing"); |
| 45 | let exe = Path::new(env!("CARGO_BIN_EXE_forge-runner")); |
| 46 | remove_tree(&IdMap::single(), &missing, exe).expect("single mode"); |
| 47 | let map = IdMap { |
| 48 | uid: 1000, |
| 49 | gid: 1000, |
| 50 | sub_uid: Some((524288, 65536)), |
| 51 | sub_gid: Some((524288, 65536)), |
| 52 | }; |
| 53 | remove_tree(&map, &missing, exe).expect("subuid mode"); |
| 54 | } |