mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37: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
|
/// 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)))
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue