|
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))] |
@@ -2567,3 +2571,85 @@ fn test_install_normal_file_replaces_symlink() { |
2567 | 2571 | // Verify sensitive file was NOT modified |
2568 | 2572 | assert_eq!(at.read("sensitive"), "important data"); |
2569 | 2573 | } |
| 2574 | +#[test] |
| 2575 | +#[cfg(target_os = "linux")] |
| 2576 | +fn test_install_set_owner_nonexistent_uid_and_gid() { |
| 2577 | + let file = File::open("/etc/login.defs").unwrap(); |
| 2578 | + let reader = BufReader::new(file); |
| 2579 | + let mut uid_min: u32 = 0; |
| 2580 | + let mut uid_max: u32 = 0; |
| 2581 | + let mut gid_min: u32 = 0; |
| 2582 | + let mut gid_max: u32 = 0; |
| 2583 | + for line in reader.lines() { |
| 2584 | + let line = line.unwrap(); |
| 2585 | + if line.starts_with("UID_MIN") { |
| 2586 | + let tokens: Vec<&str> = line.split_whitespace().collect(); |
| 2587 | + uid_min = tokens[1].parse().unwrap(); |
| 2588 | + } |
| 2589 | + if line.starts_with("UID_MAX") { |
| 2590 | + let tokens: Vec<&str> = line.split_whitespace().collect(); |
| 2591 | + uid_max = tokens[1].parse().unwrap(); |
| 2592 | + } |
| 2593 | + if line.starts_with("GID_MIN") { |
| 2594 | + let tokens: Vec<&str> = line.split_whitespace().collect(); |
| 2595 | + gid_min = tokens[1].parse().unwrap(); |
| 2596 | + } |
| 2597 | + if line.starts_with("GID_MAX") { |
| 2598 | + let tokens: Vec<&str> = line.split_whitespace().collect(); |
| 2599 | + gid_max = tokens[1].parse().unwrap(); |
| 2600 | + } |
| 2601 | + } |
| 2602 | + let file = File::open("/etc/passwd").unwrap(); |
| 2603 | + let reader = BufReader::new(file); |
| 2604 | + |
| 2605 | + let mut uids: Vec<u32> = vec![]; |
| 2606 | + let mut gids: Vec<u32> = vec![]; |
| 2607 | + for line in reader.lines() { |
| 2608 | + let line = line.unwrap(); |
| 2609 | + let tokens: Vec<&str> = line.split(':').collect(); |
| 2610 | + let uid: u32 = tokens[2].parse().unwrap(); |
| 2611 | + if (uid_min..=uid_max).contains(&uid) { |
| 2612 | + uids.push(uid); |
| 2613 | + } |
| 2614 | + let gid: u32 = tokens[3].parse().unwrap(); |
| 2615 | + if (gid_min..=gid_max).contains(&gid) { |
| 2616 | + gids.push(gid); |
| 2617 | + } |
| 2618 | + } |
| 2619 | + uids.sort_unstable(); |
| 2620 | + |
| 2621 | + let next_uid = if let Some(uid) = uids.last() { |
| 2622 | + *uid + 1 |
| 2623 | + } else { |
| 2624 | + uid_min |
| 2625 | + }; |
| 2626 | + |
| 2627 | + let next_gid = if let Some(gid) = gids.last() { |
| 2628 | + *gid + 1 |
| 2629 | + } else { |
| 2630 | + gid_min |
| 2631 | + }; |
| 2632 | + |
| 2633 | + let ts = TestScenario::new(util_name!()); |
| 2634 | + let at = &ts.fixtures; |
| 2635 | + at.touch("a"); |
| 2636 | + |
| 2637 | + if let Ok(result) = run_ucmd_as_root( |
| 2638 | + &ts, |
| 2639 | + &[ |
| 2640 | + format!("-o{next_uid}").as_str(), |
| 2641 | + format!("-g{next_gid}").as_str(), |
| 2642 | + "a", |
| 2643 | + "b", |
| 2644 | + ], |
| 2645 | + ) { |
| 2646 | + result.success(); |
| 2647 | + assert!(at.file_exists("b")); |
| 2648 | + |
| 2649 | + let metadata = fs::metadata(at.plus("b")).unwrap(); |
| 2650 | + assert_eq!(metadata.uid(), next_uid); |
| 2651 | + assert_eq!(metadata.gid(), next_gid); |
| 2652 | + } else { |
| 2653 | + println!("Test skipped; requires root user"); |
| 2654 | + } |
| 2655 | +} |
0 commit comments