Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions src/uu/install/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,14 @@ fn behavior(matches: &ArgMatches) -> UResult<Behavior> {
} else {
match usr2uid(&owner) {
Ok(u) => Some(u),
Err(_) => return Err(InstallError::InvalidUser(owner.clone()).into()),
// When using -o500 option and there's no user with uid 500 on the system
// usr2uid returns an Err value and the whole install operation fails.
// GNU coreutils installs a file with uid 500 in the same situation
// so just return the supplied owner as uid if it's an integer value
Err(_) => match owner.parse::<u32>() {
Ok(u) => Some(u),
Err(_) => return Err(InstallError::InvalidUser(owner.clone()).into()),
},
}
};

Expand All @@ -419,7 +426,14 @@ fn behavior(matches: &ArgMatches) -> UResult<Behavior> {
} else {
match grp2gid(&group) {
Ok(g) => Some(g),
Err(_) => return Err(InstallError::InvalidGroup(group.clone()).into()),
// When using -g500 option and there's no group with gid 500 on the system
// grp2gid returns an Err value and the whole install operation fails.
// GNU coreutils installs a file with gid 500 in the same situation
// so just return the supplied group as gid if it's an integer value
Err(_) => match group.parse::<u32>() {
Ok(g) => Some(g),
Err(_) => return Err(InstallError::InvalidGroup(group.clone()).into()),
},
}
};

Expand Down
86 changes: 86 additions & 0 deletions tests/by-util/test_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
use filetime::FileTime;
use std::fs;
#[cfg(target_os = "linux")]
use std::fs::File;
#[cfg(target_os = "linux")]
use std::io::{BufRead, BufReader};
#[cfg(target_os = "linux")]
use std::os::unix::ffi::OsStringExt;
use std::os::unix::fs::{MetadataExt, PermissionsExt};
#[cfg(not(windows))]
Expand Down Expand Up @@ -2567,3 +2571,85 @@ fn test_install_normal_file_replaces_symlink() {
// Verify sensitive file was NOT modified
assert_eq!(at.read("sensitive"), "important data");
}
#[test]
#[cfg(target_os = "linux")]
fn test_install_set_owner_nonexistent_uid_and_gid() {
let file = File::open("/etc/login.defs").unwrap();
let reader = BufReader::new(file);
let mut uid_min: u32 = 0;
let mut uid_max: u32 = 0;
let mut gid_min: u32 = 0;
let mut gid_max: u32 = 0;
for line in reader.lines() {
let line = line.unwrap();
if line.starts_with("UID_MIN") {
let tokens: Vec<&str> = line.split_whitespace().collect();
uid_min = tokens[1].parse().unwrap();
}
if line.starts_with("UID_MAX") {
let tokens: Vec<&str> = line.split_whitespace().collect();
uid_max = tokens[1].parse().unwrap();
}
if line.starts_with("GID_MIN") {
let tokens: Vec<&str> = line.split_whitespace().collect();
gid_min = tokens[1].parse().unwrap();
}
if line.starts_with("GID_MAX") {
let tokens: Vec<&str> = line.split_whitespace().collect();
gid_max = tokens[1].parse().unwrap();
}
}
let file = File::open("/etc/passwd").unwrap();
let reader = BufReader::new(file);

let mut uids: Vec<u32> = vec![];
let mut gids: Vec<u32> = vec![];
for line in reader.lines() {
let line = line.unwrap();
let tokens: Vec<&str> = line.split(':').collect();
let uid: u32 = tokens[2].parse().unwrap();
if (uid_min..=uid_max).contains(&uid) {
uids.push(uid);
}
let gid: u32 = tokens[3].parse().unwrap();
if (gid_min..=gid_max).contains(&gid) {
gids.push(gid);
}
}
uids.sort_unstable();

let next_uid = if let Some(uid) = uids.last() {
*uid + 1
} else {
uid_min
};

let next_gid = if let Some(gid) = gids.last() {
*gid + 1
} else {
gid_min
};

let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
at.touch("a");

if let Ok(result) = run_ucmd_as_root(
&ts,
&[
format!("-o{next_uid}").as_str(),
format!("-g{next_gid}").as_str(),
"a",
"b",
],
) {
result.success();
assert!(at.file_exists("b"));

let metadata = fs::metadata(at.plus("b")).unwrap();
assert_eq!(metadata.uid(), next_uid);
assert_eq!(metadata.gid(), next_gid);
} else {
println!("Test skipped; requires root user");
}
}
Loading