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

chown: show message if file doesn't exist

* print the message "failed to change ownership of" when we try to change a non existing file.

* replace the 4-tuple returned by parse_gid_uid_and_filter by GidUidOwnerFilter struct.

* chain the test in one expression.

* chown: remove unused var "result" in test

---------

Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com>
This commit is contained in:
Moïse Valvassori 2023-05-21 17:54:32 +02:00 committed by GitHub
parent 70765eea82
commit 0130a07579
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 70 additions and 20 deletions

View file

@ -10,7 +10,7 @@
use uucore::display::Quotable;
pub use uucore::entries;
use uucore::error::{FromIo, UResult, USimpleError};
use uucore::perms::{chown_base, options, IfFrom};
use uucore::perms::{chown_base, options, GidUidOwnerFilter, IfFrom};
use uucore::{format_usage, help_about, help_usage};
use clap::{crate_version, Arg, ArgAction, ArgMatches, Command};
@ -21,16 +21,22 @@ use std::os::unix::fs::MetadataExt;
const ABOUT: &str = help_about!("chgrp.md");
const USAGE: &str = help_usage!("chgrp.md");
fn parse_gid_and_uid(matches: &ArgMatches) -> UResult<(Option<u32>, Option<u32>, IfFrom)> {
fn parse_gid_and_uid(matches: &ArgMatches) -> UResult<GidUidOwnerFilter> {
let mut raw_group: String = String::new();
let dest_gid = if let Some(file) = matches.get_one::<String>(options::REFERENCE) {
fs::metadata(file)
.map(|meta| Some(meta.gid()))
.map(|meta| {
let gid = meta.gid();
raw_group = entries::gid2grp(gid).unwrap_or_else(|_| gid.to_string());
Some(gid)
})
.map_err_context(|| format!("failed to get attributes of {}", file.quote()))?
} else {
let group = matches
.get_one::<String>(options::ARG_GROUP)
.map(|s| s.as_str())
.unwrap_or_default();
raw_group = group.to_string();
if group.is_empty() {
None
} else {
@ -45,7 +51,12 @@ fn parse_gid_and_uid(matches: &ArgMatches) -> UResult<(Option<u32>, Option<u32>,
}
}
};
Ok((dest_gid, None, IfFrom::All))
Ok(GidUidOwnerFilter {
dest_gid,
dest_uid: None,
raw_owner: raw_group,
filter: IfFrom::All,
})
}
#[uucore::main]

View file

@ -9,7 +9,7 @@
use uucore::display::Quotable;
pub use uucore::entries::{self, Group, Locate, Passwd};
use uucore::perms::{chown_base, options, IfFrom};
use uucore::perms::{chown_base, options, GidUidOwnerFilter, IfFrom};
use uucore::{format_usage, help_about, help_usage};
use uucore::error::{FromIo, UResult, USimpleError};
@ -23,7 +23,7 @@ static ABOUT: &str = help_about!("chown.md");
const USAGE: &str = help_usage!("chown.md");
fn parse_gid_uid_and_filter(matches: &ArgMatches) -> UResult<(Option<u32>, Option<u32>, IfFrom)> {
fn parse_gid_uid_and_filter(matches: &ArgMatches) -> UResult<GidUidOwnerFilter> {
let filter = if let Some(spec) = matches.get_one::<String>(options::FROM) {
match parse_spec(spec, ':')? {
(Some(uid), None) => IfFrom::User(uid),
@ -37,17 +37,34 @@ fn parse_gid_uid_and_filter(matches: &ArgMatches) -> UResult<(Option<u32>, Optio
let dest_uid: Option<u32>;
let dest_gid: Option<u32>;
let raw_owner: String;
if let Some(file) = matches.get_one::<String>(options::REFERENCE) {
let meta = fs::metadata(file)
.map_err_context(|| format!("failed to get attributes of {}", file.quote()))?;
dest_gid = Some(meta.gid());
dest_uid = Some(meta.uid());
let gid = meta.gid();
let uid = meta.uid();
dest_gid = Some(gid);
dest_uid = Some(uid);
raw_owner = format!(
"{}:{}",
entries::uid2usr(uid).unwrap_or_else(|_| uid.to_string()),
entries::gid2grp(gid).unwrap_or_else(|_| gid.to_string())
);
} else {
let (u, g) = parse_spec(matches.get_one::<String>(options::ARG_OWNER).unwrap(), ':')?;
raw_owner = matches
.get_one::<String>(options::ARG_OWNER)
.unwrap()
.into();
let (u, g) = parse_spec(&raw_owner, ':')?;
dest_uid = u;
dest_gid = g;
}
Ok((dest_gid, dest_uid, filter))
Ok(GidUidOwnerFilter {
dest_gid,
dest_uid,
raw_owner,
filter,
})
}
#[uucore::main]

View file

@ -182,6 +182,7 @@ pub enum TraverseSymlinks {
pub struct ChownExecutor {
pub dest_uid: Option<u32>,
pub dest_gid: Option<u32>,
pub raw_owner: String, // The owner of the file as input by the user in the command line.
pub traverse_symlinks: TraverseSymlinks,
pub verbosity: Verbosity,
pub filter: IfFrom,
@ -208,7 +209,16 @@ impl ChownExecutor {
let path = root.as_ref();
let meta = match self.obtain_meta(path, self.dereference) {
Some(m) => m,
_ => return 1,
_ => {
if self.verbosity.level == VerbosityLevel::Verbose {
println!(
"failed to change ownership of {} to {}",
path.quote(),
self.raw_owner
);
}
return 1;
}
};
// Prohibit only if:
@ -414,7 +424,13 @@ pub mod options {
pub const ARG_FILES: &str = "FILE";
}
type GidUidFilterParser = fn(&ArgMatches) -> UResult<(Option<u32>, Option<u32>, IfFrom)>;
pub struct GidUidOwnerFilter {
pub dest_gid: Option<u32>,
pub dest_uid: Option<u32>,
pub raw_owner: String,
pub filter: IfFrom,
}
type GidUidFilterOwnerParser = fn(&ArgMatches) -> UResult<GidUidOwnerFilter>;
/// Base implementation for `chgrp` and `chown`.
///
@ -428,7 +444,7 @@ pub fn chown_base(
mut command: Command,
args: impl crate::Args,
add_arg_if_not_reference: &'static str,
parse_gid_uid_and_filter: GidUidFilterParser,
parse_gid_uid_and_filter: GidUidFilterOwnerParser,
groups_only: bool,
) -> UResult<()> {
let args: Vec<_> = args.collect();
@ -511,12 +527,18 @@ pub fn chown_base(
} else {
VerbosityLevel::Normal
};
let (dest_gid, dest_uid, filter) = parse_gid_uid_and_filter(&matches)?;
let GidUidOwnerFilter {
dest_gid,
dest_uid,
raw_owner,
filter,
} = parse_gid_uid_and_filter(&matches)?;
let executor = ChownExecutor {
traverse_symlinks,
dest_gid,
dest_uid,
raw_owner,
verbosity: Verbosity {
groups_only,
level: verbosity_level,

View file

@ -730,15 +730,15 @@ fn test_chown_file_notexisting() {
let user_name = String::from(result.stdout_str().trim());
assert!(!user_name.is_empty());
let _result = scene
scene
.ucmd()
.arg(user_name)
.arg(&user_name)
.arg("--verbose")
.arg("not_existing")
.fails();
// TODO: uncomment once "failed to change ownership of '{}' to {}" added to stdout
// result.stderr_contains("retained as");
.fails()
.stdout_contains(format!(
"failed to change ownership of 'not_existing' to {user_name}"
));
// TODO: uncomment once message changed from "cannot dereference" to "cannot access"
// result.stderr_contains("cannot access 'not_existing': No such file or directory");
}