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:
parent
55550e1a6e
commit
163df8abc1
2 changed files with 57 additions and 13 deletions
|
@ -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
|
||||
/// but ...
|
||||
/// it can user.name:groupname
|
||||
/// or username.groupname
|
||||
/// The `spec` can be of the form:
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `"owner:group"`,
|
||||
/// * `"owner"`,
|
||||
/// * `":group"`,
|
||||
///
|
||||
/// * `spec` - The input from the user
|
||||
/// * `sep` - Should be ':' or '.'
|
||||
/// and the owner or group can be specified either as an ID or a
|
||||
/// 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>)> {
|
||||
assert!(['.', ':'].contains(&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
|
||||
};
|
||||
let gid = if !group.is_empty() {
|
||||
Some(
|
||||
Group::locate(group)
|
||||
.map_err(|_| USimpleError::new(1, format!("invalid group: {}", spec.quote())))?
|
||||
.gid,
|
||||
)
|
||||
Some(match Group::locate(group) {
|
||||
Ok(g) => g.gid,
|
||||
Err(_) => match group.parse() {
|
||||
Ok(gid) => gid,
|
||||
Err(_) => {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("invalid group: {}", spec.quote()),
|
||||
));
|
||||
}
|
||||
},
|
||||
})
|
||||
} else {
|
||||
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: "));
|
||||
}
|
||||
|
||||
/// 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)))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -484,6 +484,29 @@ fn test_chown_only_group_id() {
|
|||
.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]
|
||||
fn test_chown_owner_group_id() {
|
||||
// test chown 1111:1111 file.txt
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue