|
8 | 8 | use filetime::FileTime; |
9 | 9 | use std::fs; |
10 | 10 | #[cfg(target_os = "linux")] |
| 11 | +use std::fs::File; |
| 12 | +#[cfg(target_os = "linux")] |
| 13 | +use std::io::{BufRead, BufReader}; |
| 14 | +#[cfg(target_os = "linux")] |
11 | 15 | use std::os::unix::ffi::OsStringExt; |
12 | 16 | use std::os::unix::fs::{MetadataExt, PermissionsExt}; |
13 | 17 | #[cfg(not(windows))] |
@@ -2489,3 +2493,86 @@ fn test_install_non_utf8_paths() { |
2489 | 2493 |
|
2490 | 2494 | ucmd.arg("-D").arg(source_file).arg(&target_path).succeeds(); |
2491 | 2495 | } |
| 2496 | + |
| 2497 | +#[test] |
| 2498 | +#[cfg(target_os = "linux")] |
| 2499 | +fn test_install_set_owner_nonexistent_uid_and_gid() { |
| 2500 | + let file = File::open("/etc/login.defs").unwrap(); |
| 2501 | + let reader = BufReader::new(file); |
| 2502 | + let mut uid_min: u32 = 0; |
| 2503 | + let mut uid_max: u32 = 0; |
| 2504 | + let mut gid_min: u32 = 0; |
| 2505 | + let mut gid_max: u32 = 0; |
| 2506 | + for line in reader.lines() { |
| 2507 | + let line = line.unwrap(); |
| 2508 | + if line.starts_with("UID_MIN") { |
| 2509 | + let tokens: Vec<&str> = line.split_whitespace().collect(); |
| 2510 | + uid_min = tokens[1].parse().unwrap(); |
| 2511 | + } |
| 2512 | + if line.starts_with("UID_MAX") { |
| 2513 | + let tokens: Vec<&str> = line.split_whitespace().collect(); |
| 2514 | + uid_max = tokens[1].parse().unwrap(); |
| 2515 | + } |
| 2516 | + if line.starts_with("GID_MIN") { |
| 2517 | + let tokens: Vec<&str> = line.split_whitespace().collect(); |
| 2518 | + gid_min = tokens[1].parse().unwrap(); |
| 2519 | + } |
| 2520 | + if line.starts_with("GID_MAX") { |
| 2521 | + let tokens: Vec<&str> = line.split_whitespace().collect(); |
| 2522 | + gid_max = tokens[1].parse().unwrap(); |
| 2523 | + } |
| 2524 | + } |
| 2525 | + let file = File::open("/etc/passwd").unwrap(); |
| 2526 | + let reader = BufReader::new(file); |
| 2527 | + |
| 2528 | + let mut uids: Vec<u32> = vec![]; |
| 2529 | + let mut gids: Vec<u32> = vec![]; |
| 2530 | + for line in reader.lines() { |
| 2531 | + let line = line.unwrap(); |
| 2532 | + let tokens: Vec<&str> = line.split(':').collect(); |
| 2533 | + let uid: u32 = tokens[2].parse().unwrap(); |
| 2534 | + if (uid_min..=uid_max).contains(&uid) { |
| 2535 | + uids.push(uid); |
| 2536 | + } |
| 2537 | + let gid: u32 = tokens[3].parse().unwrap(); |
| 2538 | + if (gid_min..=gid_max).contains(&gid) { |
| 2539 | + gids.push(gid); |
| 2540 | + } |
| 2541 | + } |
| 2542 | + uids.sort_unstable(); |
| 2543 | + |
| 2544 | + let next_uid = if let Some(uid) = uids.last() { |
| 2545 | + *uid + 1 |
| 2546 | + } else { |
| 2547 | + uid_min |
| 2548 | + }; |
| 2549 | + |
| 2550 | + let next_gid = if let Some(gid) = gids.last() { |
| 2551 | + *gid + 1 |
| 2552 | + } else { |
| 2553 | + gid_min |
| 2554 | + }; |
| 2555 | + |
| 2556 | + let ts = TestScenario::new(util_name!()); |
| 2557 | + let at = &ts.fixtures; |
| 2558 | + at.touch("a"); |
| 2559 | + |
| 2560 | + if let Ok(result) = run_ucmd_as_root( |
| 2561 | + &ts, |
| 2562 | + &[ |
| 2563 | + format!("-o{next_uid}").as_str(), |
| 2564 | + format!("-g{next_gid}").as_str(), |
| 2565 | + "a", |
| 2566 | + "b", |
| 2567 | + ], |
| 2568 | + ) { |
| 2569 | + result.success(); |
| 2570 | + assert!(at.file_exists("b")); |
| 2571 | + |
| 2572 | + let metadata = fs::metadata(at.plus("b")).unwrap(); |
| 2573 | + assert_eq!(metadata.uid(), next_uid); |
| 2574 | + assert_eq!(metadata.gid(), next_gid); |
| 2575 | + } else { |
| 2576 | + println!("Test skipped; requires root user"); |
| 2577 | + } |
| 2578 | +} |
0 commit comments