|
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))] |
@@ -2660,3 +2664,86 @@ fn test_install_d_symlink_race_condition_concurrent() { |
2660 | 2664 | "Intermediate directory should be a real directory, not a symlink" |
2661 | 2665 | ); |
2662 | 2666 | } |
| 2667 | + |
| 2668 | +#[test] |
| 2669 | +#[cfg(target_os = "linux")] |
| 2670 | +fn test_install_set_owner_nonexistent_uid_and_gid() { |
| 2671 | + let file = File::open("/etc/login.defs").unwrap(); |
| 2672 | + let reader = BufReader::new(file); |
| 2673 | + let mut uid_min: u32 = 0; |
| 2674 | + let mut uid_max: u32 = 0; |
| 2675 | + let mut gid_min: u32 = 0; |
| 2676 | + let mut gid_max: u32 = 0; |
| 2677 | + for line in reader.lines() { |
| 2678 | + let line = line.unwrap(); |
| 2679 | + if line.starts_with("UID_MIN") { |
| 2680 | + let tokens: Vec<&str> = line.split_whitespace().collect(); |
| 2681 | + uid_min = tokens[1].parse().unwrap(); |
| 2682 | + } |
| 2683 | + if line.starts_with("UID_MAX") { |
| 2684 | + let tokens: Vec<&str> = line.split_whitespace().collect(); |
| 2685 | + uid_max = tokens[1].parse().unwrap(); |
| 2686 | + } |
| 2687 | + if line.starts_with("GID_MIN") { |
| 2688 | + let tokens: Vec<&str> = line.split_whitespace().collect(); |
| 2689 | + gid_min = tokens[1].parse().unwrap(); |
| 2690 | + } |
| 2691 | + if line.starts_with("GID_MAX") { |
| 2692 | + let tokens: Vec<&str> = line.split_whitespace().collect(); |
| 2693 | + gid_max = tokens[1].parse().unwrap(); |
| 2694 | + } |
| 2695 | + } |
| 2696 | + let file = File::open("/etc/passwd").unwrap(); |
| 2697 | + let reader = BufReader::new(file); |
| 2698 | + |
| 2699 | + let mut uids: Vec<u32> = vec![]; |
| 2700 | + let mut gids: Vec<u32> = vec![]; |
| 2701 | + for line in reader.lines() { |
| 2702 | + let line = line.unwrap(); |
| 2703 | + let tokens: Vec<&str> = line.split(':').collect(); |
| 2704 | + let uid: u32 = tokens[2].parse().unwrap(); |
| 2705 | + if (uid_min..=uid_max).contains(&uid) { |
| 2706 | + uids.push(uid); |
| 2707 | + } |
| 2708 | + let gid: u32 = tokens[3].parse().unwrap(); |
| 2709 | + if (gid_min..=gid_max).contains(&gid) { |
| 2710 | + gids.push(gid); |
| 2711 | + } |
| 2712 | + } |
| 2713 | + uids.sort_unstable(); |
| 2714 | + |
| 2715 | + let next_uid = if let Some(uid) = uids.last() { |
| 2716 | + *uid + 1 |
| 2717 | + } else { |
| 2718 | + uid_min |
| 2719 | + }; |
| 2720 | + |
| 2721 | + let next_gid = if let Some(gid) = gids.last() { |
| 2722 | + *gid + 1 |
| 2723 | + } else { |
| 2724 | + gid_min |
| 2725 | + }; |
| 2726 | + |
| 2727 | + let ts = TestScenario::new(util_name!()); |
| 2728 | + let at = &ts.fixtures; |
| 2729 | + at.touch("a"); |
| 2730 | + |
| 2731 | + if let Ok(result) = run_ucmd_as_root( |
| 2732 | + &ts, |
| 2733 | + &[ |
| 2734 | + format!("-o{next_uid}").as_str(), |
| 2735 | + format!("-g{next_gid}").as_str(), |
| 2736 | + "a", |
| 2737 | + "b", |
| 2738 | + ], |
| 2739 | + ) { |
| 2740 | + result.success(); |
| 2741 | + assert!(at.file_exists("b")); |
| 2742 | + |
| 2743 | + let metadata = fs::metadata(at.plus("b")).unwrap(); |
| 2744 | + assert_eq!(metadata.uid(), next_uid); |
| 2745 | + assert_eq!(metadata.gid(), next_gid); |
| 2746 | + } else { |
| 2747 | + println!("Test skipped; requires root user"); |
| 2748 | + } |
| 2749 | +} |
0 commit comments