From b29f8b011b55dc96891cd7d59c18a743a0f4c7fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mo=C3=AFse=20Valvassori?= Date: Wed, 24 May 2023 09:45:19 +0200 Subject: [PATCH] chown: show "ownership of 'foo' retained" message * Display a message when the owner is not changed. * Display a message when the current user/group doesn't match those specified in the `--from` args. * print messages to stdout * Show the message "ownership of 'foo' retained as 'bar'" for every path entry when `chown -v -R --from=` * fix chown tests: test stdout and not stderr * factorize duplicate code in a function * Display a message when the owner is not changed. * Display a message when the current user/group doesn't match those specified in the `--from` args. * print messages to stdout * Show the message "ownership of 'foo' retained as 'bar'" for every path entry when `chown -v -R --from=` * fix chown tests: test stdout and not stderr * factorize duplicate code in a function * display the retained ownership details according to the destination ownership. --- src/uucore/src/lib/features/perms.rs | 39 ++++ tests/by-util/test_chown.rs | 260 +++++++++++++++++++++++++++ 2 files changed, 299 insertions(+) diff --git a/src/uucore/src/lib/features/perms.rs b/src/uucore/src/lib/features/perms.rs index 638347737..5384b52a1 100644 --- a/src/uucore/src/lib/features/perms.rs +++ b/src/uucore/src/lib/features/perms.rs @@ -271,6 +271,11 @@ impl ChownExecutor { } } } else { + self.print_verbose_ownership_retained_as( + path, + meta.uid(), + self.dest_gid.map(|_| meta.gid()), + ); 0 }; @@ -332,6 +337,11 @@ impl ChownExecutor { }; if !self.matched(meta.uid(), meta.gid()) { + self.print_verbose_ownership_retained_as( + path, + meta.uid(), + self.dest_gid.map(|_| meta.gid()), + ); continue; } @@ -393,6 +403,35 @@ impl ChownExecutor { IfFrom::UserGroup(u, g) => u == uid && g == gid, } } + + fn print_verbose_ownership_retained_as(&self, path: &Path, uid: u32, gid: Option) { + if self.verbosity.level == VerbosityLevel::Verbose { + match (self.dest_uid, self.dest_gid, gid) { + (Some(_), Some(_), Some(gid)) => { + println!( + "ownership of {} retained as {}:{}", + path.quote(), + entries::uid2usr(uid).unwrap_or_else(|_| uid.to_string()), + entries::gid2grp(gid).unwrap_or_else(|_| gid.to_string()), + ); + } + (None, Some(_), Some(gid)) => { + println!( + "ownership of {} retained as {}", + path.quote(), + entries::gid2grp(gid).unwrap_or_else(|_| gid.to_string()), + ); + } + (_, _, _) => { + println!( + "ownership of {} retained as {}", + path.quote(), + entries::uid2usr(uid).unwrap_or_else(|_| uid.to_string()), + ); + } + } + } + } } pub mod options { diff --git a/tests/by-util/test_chown.rs b/tests/by-util/test_chown.rs index fd7e37722..191e4a86b 100644 --- a/tests/by-util/test_chown.rs +++ b/tests/by-util/test_chown.rs @@ -742,3 +742,263 @@ fn test_chown_file_notexisting() { // TODO: uncomment once message changed from "cannot dereference" to "cannot access" // result.stderr_contains("cannot access 'not_existing': No such file or directory"); } + +#[test] +fn test_chown_no_change_to_user_from_user() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + let result = scene.cmd("whoami").run(); + if skipping_test_is_okay(&result, "whoami: cannot find name for user ID") { + return; + } + let user_name = String::from(result.stdout_str().trim()); + assert!(!user_name.is_empty()); + + let file = "f"; + at.touch(file); + scene + .ucmd() + .arg("-v") + .arg("--from=42") + .arg("43") + .arg(file) + .succeeds() + .stdout_only(format!("ownership of '{file}' retained as {user_name}\n")); +} + +#[test] +fn test_chown_no_change_to_user_from_group() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + let result = scene.cmd("whoami").run(); + if skipping_test_is_okay(&result, "whoami: cannot find name for user ID") { + return; + } + let user_name = String::from(result.stdout_str().trim()); + assert!(!user_name.is_empty()); + + let file = "f"; + at.touch(file); + scene + .ucmd() + .arg("-v") + .arg("--from=:42") + .arg("43") + .arg(file) + .succeeds() + .stdout_only(format!("ownership of '{file}' retained as {user_name}\n")); +} + +#[test] +fn test_chown_no_change_to_user_from_user_group() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + let result = scene.cmd("whoami").run(); + if skipping_test_is_okay(&result, "whoami: cannot find name for user ID") { + return; + } + let user_name = String::from(result.stdout_str().trim()); + assert!(!user_name.is_empty()); + + let file = "f"; + at.touch(file); + scene + .ucmd() + .arg("-v") + .arg("--from=42:42") + .arg("43") + .arg(file) + .succeeds() + .stdout_only(format!("ownership of '{file}' retained as {user_name}\n")); +} + +#[test] +fn test_chown_no_change_to_group_from_user() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + let result = scene.cmd("whoami").run(); + if skipping_test_is_okay(&result, "whoami: cannot find name for user ID") { + return; + } + let user_name = String::from(result.stdout_str().trim()); + assert!(!user_name.is_empty()); + + let result = scene.cmd("id").arg("-ng").run(); + if skipping_test_is_okay(&result, "id: cannot find name for group ID") { + return; + } + let group_name = String::from(result.stdout_str().trim()); + assert!(!group_name.is_empty()); + + let file = "f"; + at.touch(file); + scene + .ucmd() + .arg("-v") + .arg("--from=42") + .arg(":43") + .arg(file) + .succeeds() + .stdout_only(format!("ownership of '{file}' retained as {group_name}\n")); +} + +#[test] +fn test_chown_no_change_to_group_from_group() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + let result = scene.cmd("whoami").run(); + if skipping_test_is_okay(&result, "whoami: cannot find name for user ID") { + return; + } + let user_name = String::from(result.stdout_str().trim()); + assert!(!user_name.is_empty()); + let result = scene.cmd("id").arg("-ng").run(); + if skipping_test_is_okay(&result, "id: cannot find name for group ID") { + return; + } + let group_name = String::from(result.stdout_str().trim()); + assert!(!group_name.is_empty()); + + let file = "f"; + at.touch(file); + scene + .ucmd() + .arg("-v") + .arg("--from=:42") + .arg(":43") + .arg(file) + .succeeds() + .stdout_only(format!("ownership of '{file}' retained as {group_name}\n")); +} + +#[test] +fn test_chown_no_change_to_group_from_user_group() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + let result = scene.cmd("whoami").run(); + if skipping_test_is_okay(&result, "whoami: cannot find name for user ID") { + return; + } + let user_name = String::from(result.stdout_str().trim()); + assert!(!user_name.is_empty()); + let result = scene.cmd("id").arg("-ng").run(); + if skipping_test_is_okay(&result, "id: cannot find name for group ID") { + return; + } + let group_name = String::from(result.stdout_str().trim()); + assert!(!group_name.is_empty()); + + let file = "f"; + at.touch(file); + scene + .ucmd() + .arg("-v") + .arg("--from=42:42") + .arg(":43") + .arg(file) + .succeeds() + .stdout_only(format!("ownership of '{file}' retained as {group_name}\n")); +} + +#[test] +fn test_chown_no_change_to_user_group_from_user() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + let result = scene.cmd("whoami").run(); + if skipping_test_is_okay(&result, "whoami: cannot find name for user ID") { + return; + } + let user_name = String::from(result.stdout_str().trim()); + assert!(!user_name.is_empty()); + + let result = scene.cmd("id").arg("-ng").run(); + if skipping_test_is_okay(&result, "id: cannot find name for group ID") { + return; + } + let group_name = String::from(result.stdout_str().trim()); + assert!(!group_name.is_empty()); + + let file = "f"; + at.touch(file); + scene + .ucmd() + .arg("-v") + .arg("--from=42") + .arg("43:43") + .arg(file) + .succeeds() + .stdout_only(format!( + "ownership of '{file}' retained as {user_name}:{group_name}\n" + )); +} + +#[test] +fn test_chown_no_change_to_user_group_from_group() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + let result = scene.cmd("whoami").run(); + if skipping_test_is_okay(&result, "whoami: cannot find name for user ID") { + return; + } + let user_name = String::from(result.stdout_str().trim()); + assert!(!user_name.is_empty()); + let result = scene.cmd("id").arg("-ng").run(); + if skipping_test_is_okay(&result, "id: cannot find name for group ID") { + return; + } + let group_name = String::from(result.stdout_str().trim()); + assert!(!group_name.is_empty()); + + let file = "f"; + at.touch(file); + scene + .ucmd() + .arg("-v") + .arg("--from=:42") + .arg("43:43") + .arg(file) + .succeeds() + .stdout_only(format!( + "ownership of '{file}' retained as {user_name}:{group_name}\n" + )); +} + +#[test] +fn test_chown_no_change_to_user_group_from_user_group() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + let result = scene.cmd("whoami").run(); + if skipping_test_is_okay(&result, "whoami: cannot find name for user ID") { + return; + } + let user_name = String::from(result.stdout_str().trim()); + assert!(!user_name.is_empty()); + let result = scene.cmd("id").arg("-ng").run(); + if skipping_test_is_okay(&result, "id: cannot find name for group ID") { + return; + } + let group_name = String::from(result.stdout_str().trim()); + assert!(!group_name.is_empty()); + + let file = "f"; + at.touch(file); + scene + .ucmd() + .arg("-v") + .arg("--from=42:42") + .arg("43:43") + .arg(file) + .succeeds() + .stdout_only(format!( + "ownership of '{file}' retained as {user_name}:{group_name}\n" + )); +}