|
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))] |
@@ -2545,3 +2549,86 @@ fn test_install_unprivileged_option_u_skips_chown() { |
2545 | 2549 | assert!(at.file_exists(dst_ok)); |
2546 | 2550 | assert_eq!(at.metadata(dst_ok).uid(), geteuid()); |
2547 | 2551 | } |
| 2552 | + |
| 2553 | +#[test] |
| 2554 | +#[cfg(target_os = "linux")] |
| 2555 | +fn test_install_set_owner_nonexistent_uid_and_gid() { |
| 2556 | + let file = File::open("/etc/login.defs").unwrap(); |
| 2557 | + let reader = BufReader::new(file); |
| 2558 | + let mut uid_min: u32 = 0; |
| 2559 | + let mut uid_max: u32 = 0; |
| 2560 | + let mut gid_min: u32 = 0; |
| 2561 | + let mut gid_max: u32 = 0; |
| 2562 | + for line in reader.lines() { |
| 2563 | + let line = line.unwrap(); |
| 2564 | + if line.starts_with("UID_MIN") { |
| 2565 | + let tokens: Vec<&str> = line.split_whitespace().collect(); |
| 2566 | + uid_min = tokens[1].parse().unwrap(); |
| 2567 | + } |
| 2568 | + if line.starts_with("UID_MAX") { |
| 2569 | + let tokens: Vec<&str> = line.split_whitespace().collect(); |
| 2570 | + uid_max = tokens[1].parse().unwrap(); |
| 2571 | + } |
| 2572 | + if line.starts_with("GID_MIN") { |
| 2573 | + let tokens: Vec<&str> = line.split_whitespace().collect(); |
| 2574 | + gid_min = tokens[1].parse().unwrap(); |
| 2575 | + } |
| 2576 | + if line.starts_with("GID_MAX") { |
| 2577 | + let tokens: Vec<&str> = line.split_whitespace().collect(); |
| 2578 | + gid_max = tokens[1].parse().unwrap(); |
| 2579 | + } |
| 2580 | + } |
| 2581 | + let file = File::open("/etc/passwd").unwrap(); |
| 2582 | + let reader = BufReader::new(file); |
| 2583 | + |
| 2584 | + let mut uids: Vec<u32> = vec![]; |
| 2585 | + let mut gids: Vec<u32> = vec![]; |
| 2586 | + for line in reader.lines() { |
| 2587 | + let line = line.unwrap(); |
| 2588 | + let tokens: Vec<&str> = line.split(':').collect(); |
| 2589 | + let uid: u32 = tokens[2].parse().unwrap(); |
| 2590 | + if (uid_min..=uid_max).contains(&uid) { |
| 2591 | + uids.push(uid); |
| 2592 | + } |
| 2593 | + let gid: u32 = tokens[3].parse().unwrap(); |
| 2594 | + if (gid_min..=gid_max).contains(&gid) { |
| 2595 | + gids.push(gid); |
| 2596 | + } |
| 2597 | + } |
| 2598 | + uids.sort_unstable(); |
| 2599 | + |
| 2600 | + let next_uid = if let Some(uid) = uids.last() { |
| 2601 | + *uid + 1 |
| 2602 | + } else { |
| 2603 | + uid_min |
| 2604 | + }; |
| 2605 | + |
| 2606 | + let next_gid = if let Some(gid) = gids.last() { |
| 2607 | + *gid + 1 |
| 2608 | + } else { |
| 2609 | + gid_min |
| 2610 | + }; |
| 2611 | + |
| 2612 | + let ts = TestScenario::new(util_name!()); |
| 2613 | + let at = &ts.fixtures; |
| 2614 | + at.touch("a"); |
| 2615 | + |
| 2616 | + if let Ok(result) = run_ucmd_as_root( |
| 2617 | + &ts, |
| 2618 | + &[ |
| 2619 | + format!("-o{next_uid}").as_str(), |
| 2620 | + format!("-g{next_gid}").as_str(), |
| 2621 | + "a", |
| 2622 | + "b", |
| 2623 | + ], |
| 2624 | + ) { |
| 2625 | + result.success(); |
| 2626 | + assert!(at.file_exists("b")); |
| 2627 | + |
| 2628 | + let metadata = fs::metadata(at.plus("b")).unwrap(); |
| 2629 | + assert_eq!(metadata.uid(), next_uid); |
| 2630 | + assert_eq!(metadata.gid(), next_gid); |
| 2631 | + } else { |
| 2632 | + println!("Test skipped; requires root user"); |
| 2633 | + } |
| 2634 | +} |
0 commit comments