1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 03:27:44 +00:00

fixup! chown: allow setting arbitrary numeric user ID

This commit is contained in:
Jeffrey Finkelstein 2022-05-09 10:19:57 -04:00 committed by Sylvestre Ledru
parent 55550e1a6e
commit 163df8abc1
2 changed files with 57 additions and 13 deletions

View file

@ -167,17 +167,18 @@ pub fn uu_app<'a>() -> Command<'a> {
) )
} }
/// Parse the username and groupname /// Parse the owner/group specifier string into a user ID and a group ID.
/// ///
/// In theory, it should be username:groupname /// The `spec` can be of the form:
/// but ...
/// it can user.name:groupname
/// or username.groupname
/// ///
/// # Arguments /// * `"owner:group"`,
/// * `"owner"`,
/// * `":group"`,
/// ///
/// * `spec` - The input from the user /// and the owner or group can be specified either as an ID or a
/// * `sep` - Should be ':' or '.' /// name. The `sep` argument specifies which character to use as a
/// separator between the owner and group; calling code should set
/// this to `':'`.
fn parse_spec(spec: &str, sep: char) -> UResult<(Option<u32>, Option<u32>)> { fn parse_spec(spec: &str, sep: char) -> UResult<(Option<u32>, Option<u32>)> {
assert!(['.', ':'].contains(&sep)); assert!(['.', ':'].contains(&sep));
let mut args = spec.splitn(2, sep); let mut args = spec.splitn(2, sep);
@ -215,11 +216,18 @@ fn parse_spec(spec: &str, sep: char) -> UResult<(Option<u32>, Option<u32>)> {
None None
}; };
let gid = if !group.is_empty() { let gid = if !group.is_empty() {
Some( Some(match Group::locate(group) {
Group::locate(group) Ok(g) => g.gid,
.map_err(|_| USimpleError::new(1, format!("invalid group: {}", spec.quote())))? Err(_) => match group.parse() {
.gid, Ok(gid) => gid,
) Err(_) => {
return Err(USimpleError::new(
1,
format!("invalid group: {}", spec.quote()),
));
}
},
})
} else { } else {
None None
}; };
@ -238,4 +246,17 @@ mod test {
assert!(format!("{}", parse_spec("::", ':').err().unwrap()).starts_with("invalid group: ")); assert!(format!("{}", parse_spec("::", ':').err().unwrap()).starts_with("invalid group: "));
assert!(format!("{}", parse_spec("..", ':').err().unwrap()).starts_with("invalid group: ")); assert!(format!("{}", parse_spec("..", ':').err().unwrap()).starts_with("invalid group: "));
} }
/// Test for parsing IDs that don't correspond to a named user or group.
#[test]
fn test_parse_spec_nameless_ids() {
// This assumes that there is no named user with ID 12345.
assert!(matches!(parse_spec("12345", ':'), Ok((Some(12345), None))));
// This assumes that there is no named group with ID 54321.
assert!(matches!(parse_spec(":54321", ':'), Ok((None, Some(54321)))));
assert!(matches!(
parse_spec("12345:54321", ':'),
Ok((Some(12345), Some(54321)))
));
}
} }

View file

@ -484,6 +484,29 @@ fn test_chown_only_group_id() {
.stderr_contains(&"failed to change"); .stderr_contains(&"failed to change");
} }
/// Test for setting the group to a group ID for a group that does not exist.
///
/// For example:
///
/// $ touch f && chown :12345 f
///
/// succeeds with exit status 0 and outputs nothing. The group of the
/// file is set to 12345, even though no group with that ID exists.
///
/// This test must be run as root, because only the root user can
/// transfer ownership of a file.
#[test]
fn test_chown_only_group_id_nonexistent_group() {
let ts = TestScenario::new(util_name!());
let at = ts.fixtures.clone();
at.touch("f");
if let Ok(result) = run_ucmd_as_root(&ts, &[":12345", "f"]) {
result.success().no_stdout().no_stderr();
} else {
print!("Test skipped; requires root user");
}
}
#[test] #[test]
fn test_chown_owner_group_id() { fn test_chown_owner_group_id() {
// test chown 1111:1111 file.txt // test chown 1111:1111 file.txt