From 763de90fdaf0748c9a21ea2c44475c9fd88f2e9f Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 15 Nov 2020 23:22:52 +0100 Subject: [PATCH 1/6] refactor(chgrp): move chgrp perms function into the uucore to reuse it into install Will move chown later --- src/uu/chgrp/Cargo.toml | 2 +- src/uu/chgrp/src/chgrp.rs | 90 +++----------------------- src/uucore/Cargo.toml | 1 + src/uucore/src/lib/features.rs | 4 ++ src/uucore/src/lib/features/perms.rs | 96 ++++++++++++++++++++++++++++ src/uucore/src/lib/lib.rs | 2 + 6 files changed, 114 insertions(+), 81 deletions(-) create mode 100644 src/uucore/src/lib/features/perms.rs diff --git a/src/uu/chgrp/Cargo.toml b/src/uu/chgrp/Cargo.toml index 3a6d6b18d..9f2e8c4a7 100644 --- a/src/uu/chgrp/Cargo.toml +++ b/src/uu/chgrp/Cargo.toml @@ -15,7 +15,7 @@ edition = "2018" path = "src/chgrp.rs" [dependencies] -uucore = { version=">=0.0.4", package="uucore", path="../../uucore", features=["entries", "fs"] } +uucore = { version=">=0.0.4", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] } uucore_procs = { version=">=0.0.4", package="uucore_procs", path="../../uucore_procs" } walkdir = "2.2" diff --git a/src/uu/chgrp/src/chgrp.rs b/src/uu/chgrp/src/chgrp.rs index e60efa0c6..9fc72ab20 100644 --- a/src/uu/chgrp/src/chgrp.rs +++ b/src/uu/chgrp/src/chgrp.rs @@ -11,23 +11,18 @@ extern crate uucore; pub use uucore::entries; use uucore::fs::resolve_relative_path; -use uucore::libc::{self, gid_t, lchown}; +use uucore::libc::gid_t; +use uucore::perms::{wrap_chgrp, Verbosity}; extern crate walkdir; use walkdir::WalkDir; -use std::io::Error as IOError; -use std::io::Result as IOResult; - use std::fs; use std::fs::Metadata; use std::os::unix::fs::MetadataExt; use std::path::Path; -use std::ffi::CString; -use std::os::unix::ffi::OsStrExt; - static SYNTAX: &str = "chgrp [OPTION]... GROUP FILE...\n or : chgrp [OPTION]... --reference=RFILE FILE..."; static SUMMARY: &str = "Change the group of each FILE to GROUP."; @@ -165,14 +160,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { executor.exec() } -#[derive(PartialEq, Debug)] -enum Verbosity { - Silent, - Changes, - Verbose, - Normal, -} - struct Chgrper { dest_gid: gid_t, bit_flag: u8, @@ -201,23 +188,6 @@ impl Chgrper { ret } - fn chgrp>(&self, path: P, dgid: gid_t, follow: bool) -> IOResult<()> { - let path = path.as_ref(); - let s = CString::new(path.as_os_str().as_bytes()).unwrap(); - let ret = unsafe { - if follow { - libc::chown(s.as_ptr(), (0 as gid_t).wrapping_sub(1), dgid) - } else { - lchown(s.as_ptr(), (0 as gid_t).wrapping_sub(1), dgid) - } - }; - if ret == 0 { - Ok(()) - } else { - Err(IOError::last_os_error()) - } - } - #[cfg(windows)] fn is_bind_root>(&self, root: P) -> bool { // TODO: is there an equivalent on Windows? @@ -269,7 +239,13 @@ impl Chgrper { } } - let ret = self.wrap_chgrp(path, &meta, follow_arg); + let ret = wrap_chgrp( + path, + &meta, + self.dest_gid, + follow_arg, + self.verbosity.clone(), + ); if !self.recursive { ret @@ -297,7 +273,7 @@ impl Chgrper { } }; - ret = self.wrap_chgrp(path, &meta, follow); + ret = wrap_chgrp(path, &meta, self.dest_gid, follow, self.verbosity.clone()); } ret } @@ -324,50 +300,4 @@ impl Chgrper { }; Some(meta) } - - fn wrap_chgrp>(&self, path: P, meta: &Metadata, follow: bool) -> i32 { - use self::Verbosity::*; - let mut ret = 0; - let dest_gid = self.dest_gid; - let path = path.as_ref(); - if let Err(e) = self.chgrp(path, dest_gid, follow) { - match self.verbosity { - Silent => (), - _ => { - show_info!("changing group of '{}': {}", path.display(), e); - if self.verbosity == Verbose { - println!( - "failed to change group of {} from {} to {}", - path.display(), - entries::gid2grp(meta.gid()).unwrap(), - entries::gid2grp(dest_gid).unwrap() - ); - }; - } - } - ret = 1; - } else { - let changed = dest_gid != meta.gid(); - if changed { - match self.verbosity { - Changes | Verbose => { - println!( - "changed group of {} from {} to {}", - path.display(), - entries::gid2grp(meta.gid()).unwrap(), - entries::gid2grp(dest_gid).unwrap() - ); - } - _ => (), - }; - } else if self.verbosity == Verbose { - println!( - "group of {} retained as {}", - path.display(), - entries::gid2grp(dest_gid).unwrap() - ); - } - } - ret - } } diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index db3b35004..94b7502d3 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -43,6 +43,7 @@ entries = ["libc"] fs = ["libc"] mode = ["libc"] parse_time = [] +perms = ["libc"] process = ["libc"] signals = [] utf8 = [] diff --git a/src/uucore/src/lib/features.rs b/src/uucore/src/lib/features.rs index d5927fc6f..c26225cb7 100644 --- a/src/uucore/src/lib/features.rs +++ b/src/uucore/src/lib/features.rs @@ -13,11 +13,15 @@ pub mod zero_copy; // ** non-windows #[cfg(all(not(windows), feature = "mode"))] pub mod mode; + // ** unix-only #[cfg(all(unix, feature = "entries"))] pub mod entries; +#[cfg(all(unix, feature = "perms"))] +pub mod perms; #[cfg(all(unix, feature = "process"))] pub mod process; + #[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))] pub mod signals; #[cfg(all( diff --git a/src/uucore/src/lib/features/perms.rs b/src/uucore/src/lib/features/perms.rs new file mode 100644 index 000000000..9eca7ea0e --- /dev/null +++ b/src/uucore/src/lib/features/perms.rs @@ -0,0 +1,96 @@ +// This file is part of the uutils coreutils package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +pub use crate::features::entries; +use libc::{self, gid_t, lchown}; + +#[macro_use] +pub use crate::*; + +use std::io::Error as IOError; +use std::io::Result as IOResult; + +use std::ffi::CString; +use std::fs::Metadata; +use std::os::unix::fs::MetadataExt; + +use std::os::unix::ffi::OsStrExt; +use std::path::Path; + +#[derive(PartialEq, Clone, Debug)] +pub enum Verbosity { + Silent, + Changes, + Verbose, + Normal, +} + +fn chgrp>(path: P, dgid: gid_t, follow: bool) -> IOResult<()> { + let path = path.as_ref(); + let s = CString::new(path.as_os_str().as_bytes()).unwrap(); + let ret = unsafe { + if follow { + libc::chown(s.as_ptr(), (0 as gid_t).wrapping_sub(1), dgid) + } else { + lchown(s.as_ptr(), (0 as gid_t).wrapping_sub(1), dgid) + } + }; + if ret == 0 { + Ok(()) + } else { + Err(IOError::last_os_error()) + } +} + +pub fn wrap_chgrp>( + path: P, + meta: &Metadata, + dest_gid: gid_t, + follow: bool, + verbosity: Verbosity, +) -> i32 { + use self::Verbosity::*; + let mut ret = 0; + let path = path.as_ref(); + if let Err(e) = chgrp(path, dest_gid, follow) { + match verbosity { + Silent => (), + _ => { + show_info!("changing group of '{}': {}", path.display(), e); + if verbosity == Verbose { + println!( + "failed to change group of {} from {} to {}", + path.display(), + entries::gid2grp(meta.gid()).unwrap(), + entries::gid2grp(dest_gid).unwrap() + ); + }; + } + } + ret = 1; + } else { + let changed = dest_gid != meta.gid(); + if changed { + match verbosity { + Changes | Verbose => { + println!( + "changed group of {} from {} to {}", + path.display(), + entries::gid2grp(meta.gid()).unwrap(), + entries::gid2grp(dest_gid).unwrap() + ); + } + _ => (), + }; + } else if verbosity == Verbose { + println!( + "group of {} retained as {}", + path.display(), + entries::gid2grp(dest_gid).unwrap() + ); + } + } + ret +} diff --git a/src/uucore/src/lib/lib.rs b/src/uucore/src/lib/lib.rs index 005759514..5d08dc9ac 100644 --- a/src/uucore/src/lib/lib.rs +++ b/src/uucore/src/lib/lib.rs @@ -53,6 +53,8 @@ pub use crate::features::mode; // ** unix-only #[cfg(all(unix, feature = "entries"))] pub use crate::features::entries; +#[cfg(all(unix, feature = "perms"))] +pub use crate::features::perms; #[cfg(all(unix, feature = "process"))] pub use crate::features::process; #[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))] From 015e18731f83b4e212b31ad040f8e7414bbfb6fb Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Mon, 16 Nov 2020 22:54:41 +0100 Subject: [PATCH 2/6] feature(install): install group support --- src/uu/install/Cargo.toml | 2 +- src/uu/install/src/install.rs | 71 +++++++++++++++++++---------------- tests/by-util/test_install.rs | 22 +++++++++++ 3 files changed, 62 insertions(+), 33 deletions(-) diff --git a/src/uu/install/Cargo.toml b/src/uu/install/Cargo.toml index 93803048b..2ef2a1f33 100644 --- a/src/uu/install/Cargo.toml +++ b/src/uu/install/Cargo.toml @@ -20,7 +20,7 @@ path = "src/install.rs" [dependencies] clap = "2.33" libc = ">= 0.2" -uucore = { version=">=0.0.4", package="uucore", path="../../uucore", features=["mode"] } +uucore = { version=">=0.0.4", package="uucore", path="../../uucore", features=["mode", "perms"] } uucore_procs = { version=">=0.0.4", package="uucore_procs", path="../../uucore_procs" } [dev-dependencies] diff --git a/src/uu/install/src/install.rs b/src/uu/install/src/install.rs index 627803a02..64b3ec2e0 100644 --- a/src/uu/install/src/install.rs +++ b/src/uu/install/src/install.rs @@ -16,6 +16,9 @@ mod mode; extern crate uucore; use clap::{App, Arg, ArgMatches}; +use uucore::perms::{wrap_chgrp, Verbosity}; +use uucore::entries::grp2gid; + use std::fs; use std::path::{Path, PathBuf}; use std::result::Result; @@ -27,6 +30,7 @@ pub struct Behavior { main_function: MainFunction, specified_mode: Option, suffix: String, + group: String, verbose: bool, } @@ -126,12 +130,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .help("(unimplemented) create all leading components of DEST except the last, then copy SOURCE to DEST") ) .arg( - // TODO implement flag Arg::with_name(OPT_GROUP) .short("g") .long(OPT_GROUP) - .help("(unimplemented) set group ownership, instead of process's current group") + .help("set group ownership, instead of process's current group") .value_name("GROUP") + .takes_value(true) ) .arg( Arg::with_name(OPT_MODE) @@ -139,6 +143,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .long(OPT_MODE) .help("set permission mode (as in chmod), instead of rwxr-xr-x") .value_name("MODE") + .takes_value(true) + .min_values(1) ) .arg( // TODO implement flag @@ -176,6 +182,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .long(OPT_SUFFIX) .help("(unimplemented) override the usual backup suffix") .value_name("SUFFIX") + .takes_value(true) + .min_values(1) ) .arg( // TODO implement flag @@ -214,7 +222,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .help("(unimplemented) set security context of files and directories") .value_name("CONTEXT") ) - .arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true)) + .arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true).min_values(1)) .get_matches_from(args); let paths: Vec = matches @@ -258,8 +266,6 @@ fn check_unimplemented<'a>(matches: &ArgMatches) -> Result<(), &'a str> { Err("--compare, -C") } else if matches.is_present(OPT_CREATED) { Err("-D") - } else if matches.is_present(OPT_GROUP) { - Err("--group, -g") } else if matches.is_present(OPT_OWNER) { Err("--owner, -o") } else if matches.is_present(OPT_PRESERVE_TIMESTAMPS) { @@ -292,7 +298,7 @@ fn check_unimplemented<'a>(matches: &ArgMatches) -> Result<(), &'a str> { /// In event of failure, returns an integer intended as a program return code. /// fn behavior(matches: &ArgMatches) -> Result { - let main_function = if matches.is_present("directory") { + let main_function = if matches.is_present(OPT_DIRECTORY) { MainFunction::Directory } else { MainFunction::Standard @@ -310,11 +316,6 @@ fn behavior(matches: &ArgMatches) -> Result { } }, None => { - show_error!( - "option '--mode' requires an argument\n \ - Try '{} --help' for more information.", - executable!() - ); return Err(1); } } @@ -326,11 +327,6 @@ fn behavior(matches: &ArgMatches) -> Result { match matches.value_of(OPT_SUFFIX) { Some(x) => x, None => { - show_error!( - "option '--suffix' requires an argument\n\ - Try '{} --help' for more information.", - executable!() - ); return Err(1); } } @@ -338,10 +334,13 @@ fn behavior(matches: &ArgMatches) -> Result { "~" }; + let group = matches.value_of(OPT_GROUP).unwrap_or_else(|| ""); + Ok(Behavior { main_function, specified_mode, suffix: backup_suffix.to_string(), + group: group.to_string(), verbose: matches.is_present(OPT_VERBOSE), }) } @@ -400,22 +399,16 @@ fn is_new_file_path(path: &Path) -> bool { /// Returns an integer intended as a program return code. /// fn standard(paths: Vec, b: Behavior) -> i32 { - if paths.len() < 2 { - println!("{} requires at least 2 arguments.", executable!()); - 1 - } else { - let sources = &paths[0..paths.len() - 1] - .iter() - .map(PathBuf::from) - .collect::>(); - let target = Path::new(paths.last().unwrap()); + let sources = &paths[0..paths.len() - 1] + .iter() + .map(PathBuf::from) + .collect::>(); + let target = Path::new(paths.last().unwrap()); - if (target.is_file() || is_new_file_path(target)) && sources.len() == 1 { - /* If the target already exist or directly creatable */ - copy_file_to_file(&sources[0], &target.to_path_buf(), &b) - } else { - copy_files_into_dir(sources, &target.to_path_buf(), &b) - } + if (target.is_file() || is_new_file_path(target)) && sources.len() == 1 { + copy_file_to_file(&sources[0], &target.to_path_buf(), &b) + } else { + copy_files_into_dir(sources, &target.to_path_buf(), &b) } } @@ -495,7 +488,7 @@ fn copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> Result<(), ()> { if let Err(err) = io_result { show_error!( - "install: cannot install ‘{}’ to ‘{}’: {}", + "cannot install ‘{}’ to ‘{}’: {}", from.display(), to.display(), err @@ -507,6 +500,20 @@ fn copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> Result<(), ()> { return Err(()); } + if b.group != "" { + let meta = match fs::metadata(to) { + Ok(meta) => meta, + Err(f) => crash!(1, "{}", f.to_string()), + }; + + let group_id = match grp2gid(&b.group) { + Ok(g) => g, + _ => crash!(1, "no such group: {}", b.group), + }; + wrap_chgrp(to.as_path(), &meta, group_id, false, Verbosity::Normal); + + } + if b.verbose { show_info!("'{}' -> '{}'", from.display(), to.display()); } diff --git a/tests/by-util/test_install.rs b/tests/by-util/test_install.rs index ec38d11b0..a88099912 100644 --- a/tests/by-util/test_install.rs +++ b/tests/by-util/test_install.rs @@ -1,5 +1,6 @@ use crate::common::util::*; use std::os::unix::fs::PermissionsExt; +use rust_users::*; #[test] fn test_install_help() { @@ -206,6 +207,27 @@ fn test_install_target_new_file() { assert!(at.file_exists(&format!("{}/{}", dir, file))); } +#[test] +fn test_install_target_new_file_with_group() { + let (at, mut ucmd) = at_and_ucmd!(); + let file = "test_install_target_new_filer_file_j"; + let dir = "test_install_target_new_file_dir_j"; + let gid = get_effective_gid(); + + at.touch(file); + at.mkdir(dir); + ucmd.arg(file) + .arg("--group") + .arg(gid.to_string()) + .arg(format!("{}/{}", dir, file)) + .succeeds() + .no_stderr(); + + assert!(at.file_exists(file)); + assert!(at.file_exists(&format!("{}/{}", dir, file))); +} + + #[test] fn test_install_target_new_file_failing_nonexistent_parent() { let (at, mut ucmd) = at_and_ucmd!(); From 55c660b986174293985b21f078866b65fdcb323c Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 17 Nov 2020 22:13:58 +0100 Subject: [PATCH 3/6] feature(install): move chown functions into uucore and have install owner support use it --- Cargo.lock | 2 + src/uu/chown/Cargo.toml | 2 +- src/uu/chown/src/chown.rs | 107 +++++---------------------- src/uu/install/src/install.rs | 45 ++++++++--- src/uucore/src/lib/features/perms.rs | 78 ++++++++++++++++++- tests/by-util/test_chown.rs | 15 +++- tests/by-util/test_install.rs | 21 +++++- 7 files changed, 167 insertions(+), 103 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 955626985..43fada101 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] name = "advapi32-sys" version = "0.2.0" diff --git a/src/uu/chown/Cargo.toml b/src/uu/chown/Cargo.toml index 59a957cf8..5f7169bda 100644 --- a/src/uu/chown/Cargo.toml +++ b/src/uu/chown/Cargo.toml @@ -17,7 +17,7 @@ path = "src/chown.rs" [dependencies] clap = "2.33" glob = "0.3.0" -uucore = { version=">=0.0.4", package="uucore", path="../../uucore", features=["entries", "fs"] } +uucore = { version=">=0.0.4", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] } uucore_procs = { version=">=0.0.4", package="uucore_procs", path="../../uucore_procs" } walkdir = "2.2" diff --git a/src/uu/chown/src/chown.rs b/src/uu/chown/src/chown.rs index 3c8a9f0c1..8dba8e174 100644 --- a/src/uu/chown/src/chown.rs +++ b/src/uu/chown/src/chown.rs @@ -11,7 +11,8 @@ extern crate uucore; pub use uucore::entries::{self, Group, Locate, Passwd}; use uucore::fs::resolve_relative_path; -use uucore::libc::{self, gid_t, lchown, uid_t}; +use uucore::libc::{gid_t, uid_t}; +use uucore::perms::{wrap_chown, Verbosity}; extern crate clap; use clap::{App, Arg}; @@ -22,9 +23,6 @@ use walkdir::WalkDir; use std::fs::{self, Metadata}; use std::os::unix::fs::MetadataExt; -use std::io; -use std::io::Result as IOResult; - use std::convert::AsRef; use std::path::Path; @@ -304,14 +302,6 @@ fn parse_spec(spec: &str) -> Result<(Option, Option), String> { } } -#[derive(PartialEq, Debug)] -enum Verbosity { - Silent, - Changes, - Verbose, - Normal, -} - enum IfFrom { All, User(u32), @@ -349,29 +339,6 @@ impl Chowner { ret } - fn chown>( - &self, - path: P, - duid: uid_t, - dgid: gid_t, - follow: bool, - ) -> IOResult<()> { - let path = path.as_ref(); - let s = CString::new(path.as_os_str().as_bytes()).unwrap(); - let ret = unsafe { - if follow { - libc::chown(s.as_ptr(), duid, dgid) - } else { - lchown(s.as_ptr(), duid, dgid) - } - }; - if ret == 0 { - Ok(()) - } else { - Err(io::Error::last_os_error()) - } - } - fn traverse>(&self, root: P) -> i32 { let follow_arg = self.dereference || self.bit_flag != FTS_PHYSICAL; let path = root.as_ref(); @@ -408,7 +375,14 @@ impl Chowner { } let ret = if self.matched(meta.uid(), meta.gid()) { - self.wrap_chown(path, &meta, follow_arg) + wrap_chown( + path, + &meta, + self.dest_uid, + self.dest_gid, + follow_arg, + self.verbosity.clone(), + ) } else { 0 }; @@ -443,7 +417,14 @@ impl Chowner { continue; } - ret = self.wrap_chown(path, &meta, follow); + ret = wrap_chown( + path, + &meta, + self.dest_uid, + self.dest_gid, + follow, + self.verbosity.clone(), + ) } ret } @@ -471,58 +452,6 @@ impl Chowner { Some(meta) } - fn wrap_chown>(&self, path: P, meta: &Metadata, follow: bool) -> i32 { - use self::Verbosity::*; - let mut ret = 0; - let dest_uid = self.dest_uid.unwrap_or_else(|| meta.uid()); - let dest_gid = self.dest_gid.unwrap_or_else(|| meta.gid()); - let path = path.as_ref(); - if let Err(e) = self.chown(path, dest_uid, dest_gid, follow) { - match self.verbosity { - Silent => (), - _ => { - show_info!("changing ownership of '{}': {}", path.display(), e); - if self.verbosity == Verbose { - println!( - "failed to change ownership of {} from {}:{} to {}:{}", - path.display(), - entries::uid2usr(meta.uid()).unwrap(), - entries::gid2grp(meta.gid()).unwrap(), - entries::uid2usr(dest_uid).unwrap(), - entries::gid2grp(dest_gid).unwrap() - ); - }; - } - } - ret = 1; - } else { - let changed = dest_uid != meta.uid() || dest_gid != meta.gid(); - if changed { - match self.verbosity { - Changes | Verbose => { - println!( - "changed ownership of {} from {}:{} to {}:{}", - path.display(), - entries::uid2usr(meta.uid()).unwrap(), - entries::gid2grp(meta.gid()).unwrap(), - entries::uid2usr(dest_uid).unwrap(), - entries::gid2grp(dest_gid).unwrap() - ); - } - _ => (), - }; - } else if self.verbosity == Verbose { - println!( - "ownership of {} retained as {}:{}", - path.display(), - entries::uid2usr(dest_uid).unwrap(), - entries::gid2grp(dest_gid).unwrap() - ); - } - } - ret - } - #[inline] fn matched(&self, uid: uid_t, gid: gid_t) -> bool { match self.filter { diff --git a/src/uu/install/src/install.rs b/src/uu/install/src/install.rs index 64b3ec2e0..6260baa29 100644 --- a/src/uu/install/src/install.rs +++ b/src/uu/install/src/install.rs @@ -16,10 +16,11 @@ mod mode; extern crate uucore; use clap::{App, Arg, ArgMatches}; -use uucore::perms::{wrap_chgrp, Verbosity}; -use uucore::entries::grp2gid; +use uucore::entries::{grp2gid, usr2uid}; +use uucore::perms::{wrap_chgrp, wrap_chown, Verbosity}; use std::fs; +use std::os::unix::fs::MetadataExt; use std::path::{Path, PathBuf}; use std::result::Result; @@ -30,6 +31,7 @@ pub struct Behavior { main_function: MainFunction, specified_mode: Option, suffix: String, + owner: String, group: String, verbose: bool, } @@ -147,12 +149,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .min_values(1) ) .arg( - // TODO implement flag Arg::with_name(OPT_OWNER) .short("o") .long(OPT_OWNER) - .help("(unimplemented) set ownership (super-user only)") + .help("set ownership (super-user only)") .value_name("OWNER") + .takes_value(true) ) .arg( // TODO implement flag @@ -266,8 +268,6 @@ fn check_unimplemented<'a>(matches: &ArgMatches) -> Result<(), &'a str> { Err("--compare, -C") } else if matches.is_present(OPT_CREATED) { Err("-D") - } else if matches.is_present(OPT_OWNER) { - Err("--owner, -o") } else if matches.is_present(OPT_PRESERVE_TIMESTAMPS) { Err("--preserve-timestamps, -p") } else if matches.is_present(OPT_STRIP) { @@ -334,13 +334,18 @@ fn behavior(matches: &ArgMatches) -> Result { "~" }; - let group = matches.value_of(OPT_GROUP).unwrap_or_else(|| ""); - Ok(Behavior { main_function, specified_mode, suffix: backup_suffix.to_string(), - group: group.to_string(), + owner: matches + .value_of(OPT_OWNER) + .unwrap_or_else(|| "") + .to_string(), + group: matches + .value_of(OPT_GROUP) + .unwrap_or_else(|| "") + .to_string(), verbose: matches.is_present(OPT_VERBOSE), }) } @@ -500,6 +505,27 @@ fn copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> Result<(), ()> { return Err(()); } + if b.owner != "" { + let meta = match fs::metadata(to) { + Ok(meta) => meta, + Err(f) => crash!(1, "{}", f.to_string()), + }; + + let owner_id = match usr2uid(&b.owner) { + Ok(g) => g, + _ => crash!(1, "no such user: {}", b.owner), + }; + let gid = meta.gid(); + wrap_chown( + to.as_path(), + &meta, + Some(owner_id), + Some(gid), + false, + Verbosity::Normal, + ); + } + if b.group != "" { let meta = match fs::metadata(to) { Ok(meta) => meta, @@ -511,7 +537,6 @@ fn copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> Result<(), ()> { _ => crash!(1, "no such group: {}", b.group), }; wrap_chgrp(to.as_path(), &meta, group_id, false, Verbosity::Normal); - } if b.verbose { diff --git a/src/uucore/src/lib/features/perms.rs b/src/uucore/src/lib/features/perms.rs index 9eca7ea0e..6ea1cfe8f 100644 --- a/src/uucore/src/lib/features/perms.rs +++ b/src/uucore/src/lib/features/perms.rs @@ -4,7 +4,7 @@ // file that was distributed with this source code. pub use crate::features::entries; -use libc::{self, gid_t, lchown}; +use libc::{self, gid_t, lchown, uid_t}; #[macro_use] pub use crate::*; @@ -94,3 +94,79 @@ pub fn wrap_chgrp>( } ret } + +fn chown>(path: P, duid: uid_t, dgid: gid_t, follow: bool) -> IOResult<()> { + let path = path.as_ref(); + let s = CString::new(path.as_os_str().as_bytes()).unwrap(); + let ret = unsafe { + if follow { + libc::chown(s.as_ptr(), duid, dgid) + } else { + lchown(s.as_ptr(), duid, dgid) + } + }; + if ret == 0 { + Ok(()) + } else { + Err(IOError::last_os_error()) + } +} + +pub fn wrap_chown>( + path: P, + meta: &Metadata, + dest_uid: Option, + dest_gid: Option, + follow: bool, + verbosity: Verbosity, +) -> i32 { + use self::Verbosity::*; + let mut ret = 0; + let dest_uid = dest_uid.unwrap_or_else(|| meta.uid()); + let dest_gid = dest_gid.unwrap_or_else(|| meta.gid()); + let path = path.as_ref(); + if let Err(e) = chown(path, dest_uid, dest_gid, follow) { + match verbosity { + Silent => (), + _ => { + show_info!("changing ownership of '{}': {}", path.display(), e); + if verbosity == Verbose { + println!( + "failed to change ownership of {} from {}:{} to {}:{}", + path.display(), + entries::uid2usr(meta.uid()).unwrap(), + entries::gid2grp(meta.gid()).unwrap(), + entries::uid2usr(dest_uid).unwrap(), + entries::gid2grp(dest_gid).unwrap() + ); + }; + } + } + ret = 1; + } else { + let changed = dest_uid != meta.uid() || dest_gid != meta.gid(); + if changed { + match verbosity { + Changes | Verbose => { + println!( + "changed ownership of {} from {}:{} to {}:{}", + path.display(), + entries::uid2usr(meta.uid()).unwrap(), + entries::gid2grp(meta.gid()).unwrap(), + entries::uid2usr(dest_uid).unwrap(), + entries::gid2grp(dest_gid).unwrap() + ); + } + _ => (), + }; + } else if verbosity == Verbose { + println!( + "ownership of {} retained as {}:{}", + path.display(), + entries::uid2usr(dest_uid).unwrap(), + entries::gid2grp(dest_gid).unwrap() + ); + } + } + ret +} diff --git a/tests/by-util/test_chown.rs b/tests/by-util/test_chown.rs index a943967f2..9e526bca9 100644 --- a/tests/by-util/test_chown.rs +++ b/tests/by-util/test_chown.rs @@ -1,7 +1,7 @@ use crate::common::util::*; +use rust_users::*; extern crate chown; -// pub use self::uu_chown::*; #[cfg(test)] mod test_passgrp { @@ -378,4 +378,17 @@ fn test_root_preserve() { assert!(result .stderr .contains("chown: it is dangerous to operate recursively")); + +#[cfg(target_os = "linux")] +fn test_big_p() { + if get_effective_uid() != 0 { + new_ucmd!() + .arg("-RP") + .arg("bin") + .arg("/proc/self/cwd") + .fails() + .stderr_is( + "chown: changing ownership of '/proc/self/cwd': Operation not permitted (os error 1)\n", + ); + } } diff --git a/tests/by-util/test_install.rs b/tests/by-util/test_install.rs index a88099912..8066bdc25 100644 --- a/tests/by-util/test_install.rs +++ b/tests/by-util/test_install.rs @@ -1,6 +1,6 @@ use crate::common::util::*; -use std::os::unix::fs::PermissionsExt; use rust_users::*; +use std::os::unix::fs::PermissionsExt; #[test] fn test_install_help() { @@ -227,6 +227,25 @@ fn test_install_target_new_file_with_group() { assert!(at.file_exists(&format!("{}/{}", dir, file))); } +#[test] +fn test_install_target_new_file_with_owner() { + let (at, mut ucmd) = at_and_ucmd!(); + let file = "test_install_target_new_filer_file_j"; + let dir = "test_install_target_new_file_dir_j"; + let uid = get_effective_uid(); + + at.touch(file); + at.mkdir(dir); + ucmd.arg(file) + .arg("--owner") + .arg(uid.to_string()) + .arg(format!("{}/{}", dir, file)) + .succeeds() + .no_stderr(); + + assert!(at.file_exists(file)); + assert!(at.file_exists(&format!("{}/{}", dir, file))); +} #[test] fn test_install_target_new_file_failing_nonexistent_parent() { From 3024ade0717ec3406582c3d04e4a3faffd149be8 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Wed, 18 Nov 2020 23:06:43 +0100 Subject: [PATCH 4/6] refactor(chgrp, install): Show the error in the program instead of the lib --- src/uu/chgrp/src/chgrp.rs | 31 ++++++++++++++++++-- src/uu/chown/src/chown.rs | 35 ++++++++++++++++++++--- src/uu/install/src/install.rs | 20 +++++++++++-- src/uucore/src/lib/features/perms.rs | 42 ++++++++++++++++------------ tests/by-util/test_chgrp.rs | 3 +- 5 files changed, 101 insertions(+), 30 deletions(-) diff --git a/src/uu/chgrp/src/chgrp.rs b/src/uu/chgrp/src/chgrp.rs index 9fc72ab20..b4c3360c5 100644 --- a/src/uu/chgrp/src/chgrp.rs +++ b/src/uu/chgrp/src/chgrp.rs @@ -239,13 +239,24 @@ impl Chgrper { } } - let ret = wrap_chgrp( + let ret = match wrap_chgrp( path, &meta, self.dest_gid, follow_arg, self.verbosity.clone(), - ); + ) { + Ok(n) => { + show_info!("{}", n); + 0 + } + Err(e) => { + if self.verbosity != Verbosity::Silent { + show_info!("{}", e); + } + 1 + } + }; if !self.recursive { ret @@ -273,8 +284,22 @@ impl Chgrper { } }; - ret = wrap_chgrp(path, &meta, self.dest_gid, follow, self.verbosity.clone()); + ret = match wrap_chgrp(path, &meta, self.dest_gid, follow, self.verbosity.clone()) { + Ok(n) => { + if n != "" { + show_info!("{}", n); + } + 0 + } + Err(e) => { + if self.verbosity != Verbosity::Silent { + show_info!("{}", e); + } + 1 + } + } } + ret } diff --git a/src/uu/chown/src/chown.rs b/src/uu/chown/src/chown.rs index 8dba8e174..47279747f 100644 --- a/src/uu/chown/src/chown.rs +++ b/src/uu/chown/src/chown.rs @@ -375,14 +375,28 @@ impl Chowner { } let ret = if self.matched(meta.uid(), meta.gid()) { - wrap_chown( + match wrap_chown( path, &meta, self.dest_uid, self.dest_gid, follow_arg, self.verbosity.clone(), - ) + ) { + Ok(n) => { + if n != "" { + show_info!("{}", n); + } + 0 + } + Err(e) => { + + if self.verbosity != Verbosity::Silent { + show_info!("{}", e); + } + 1 + } + } } else { 0 }; @@ -417,14 +431,27 @@ impl Chowner { continue; } - ret = wrap_chown( + ret = match wrap_chown( path, &meta, self.dest_uid, self.dest_gid, follow, self.verbosity.clone(), - ) + ) { + Ok(n) => { + if n != "" { + show_info!("{}", n); + } + 0 + } + Err(e) => { + if self.verbosity != Verbosity::Silent { + show_info!("{}", e); + } + 1 + } + } } ret } diff --git a/src/uu/install/src/install.rs b/src/uu/install/src/install.rs index 6260baa29..ce6ba3bf1 100644 --- a/src/uu/install/src/install.rs +++ b/src/uu/install/src/install.rs @@ -516,14 +516,21 @@ fn copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> Result<(), ()> { _ => crash!(1, "no such user: {}", b.owner), }; let gid = meta.gid(); - wrap_chown( + match wrap_chown( to.as_path(), &meta, Some(owner_id), Some(gid), false, Verbosity::Normal, - ); + ) { + Ok(n) => { + if n != "" { + show_info!("{}", n); + } + } + Err(e) => show_info!("{}", e), + } } if b.group != "" { @@ -536,7 +543,14 @@ fn copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> Result<(), ()> { Ok(g) => g, _ => crash!(1, "no such group: {}", b.group), }; - wrap_chgrp(to.as_path(), &meta, group_id, false, Verbosity::Normal); + match wrap_chgrp(to.as_path(), &meta, group_id, false, Verbosity::Normal) { + Ok(n) => { + if n != "" { + show_info!("{}", n); + } + } + Err(e) => show_info!("{}", e), + } } if b.verbose { diff --git a/src/uucore/src/lib/features/perms.rs b/src/uucore/src/lib/features/perms.rs index 6ea1cfe8f..25f49012c 100644 --- a/src/uucore/src/lib/features/perms.rs +++ b/src/uucore/src/lib/features/perms.rs @@ -19,6 +19,8 @@ use std::os::unix::fs::MetadataExt; use std::os::unix::ffi::OsStrExt; use std::path::Path; +//type PermResult = Result; + #[derive(PartialEq, Clone, Debug)] pub enum Verbosity { Silent, @@ -50,18 +52,20 @@ pub fn wrap_chgrp>( dest_gid: gid_t, follow: bool, verbosity: Verbosity, -) -> i32 { +) -> Result { use self::Verbosity::*; - let mut ret = 0; let path = path.as_ref(); + let mut out: String = String::new(); + if let Err(e) = chgrp(path, dest_gid, follow) { match verbosity { Silent => (), _ => { - show_info!("changing group of '{}': {}", path.display(), e); + out = format!("changing group of '{}': {}", path.display(), e); if verbosity == Verbose { - println!( - "failed to change group of {} from {} to {}", + out = format!( + "{}\nfailed to change group of {} from {} to {}", + out, path.display(), entries::gid2grp(meta.gid()).unwrap(), entries::gid2grp(dest_gid).unwrap() @@ -69,13 +73,13 @@ pub fn wrap_chgrp>( }; } } - ret = 1; + return Err(out); } else { let changed = dest_gid != meta.gid(); if changed { match verbosity { Changes | Verbose => { - println!( + out = format!( "changed group of {} from {} to {}", path.display(), entries::gid2grp(meta.gid()).unwrap(), @@ -85,14 +89,14 @@ pub fn wrap_chgrp>( _ => (), }; } else if verbosity == Verbose { - println!( + out = format!( "group of {} retained as {}", path.display(), entries::gid2grp(dest_gid).unwrap() ); } } - ret + Ok(out) } fn chown>(path: P, duid: uid_t, dgid: gid_t, follow: bool) -> IOResult<()> { @@ -119,20 +123,22 @@ pub fn wrap_chown>( dest_gid: Option, follow: bool, verbosity: Verbosity, -) -> i32 { +) -> Result { use self::Verbosity::*; - let mut ret = 0; let dest_uid = dest_uid.unwrap_or_else(|| meta.uid()); let dest_gid = dest_gid.unwrap_or_else(|| meta.gid()); let path = path.as_ref(); + let mut out: String = String::new(); + if let Err(e) = chown(path, dest_uid, dest_gid, follow) { match verbosity { Silent => (), _ => { - show_info!("changing ownership of '{}': {}", path.display(), e); + out = format!("changing ownership of '{}': {}", path.display(), e); if verbosity == Verbose { - println!( - "failed to change ownership of {} from {}:{} to {}:{}", + out = format!( + "{}\nfailed to change ownership of {} from {}:{} to {}:{}", + out, path.display(), entries::uid2usr(meta.uid()).unwrap(), entries::gid2grp(meta.gid()).unwrap(), @@ -142,13 +148,13 @@ pub fn wrap_chown>( }; } } - ret = 1; + return Err(out); } else { let changed = dest_uid != meta.uid() || dest_gid != meta.gid(); if changed { match verbosity { Changes | Verbose => { - println!( + out = format!( "changed ownership of {} from {}:{} to {}:{}", path.display(), entries::uid2usr(meta.uid()).unwrap(), @@ -160,7 +166,7 @@ pub fn wrap_chown>( _ => (), }; } else if verbosity == Verbose { - println!( + out = format!( "ownership of {} retained as {}:{}", path.display(), entries::uid2usr(dest_uid).unwrap(), @@ -168,5 +174,5 @@ pub fn wrap_chown>( ); } } - ret + Ok(out) } diff --git a/tests/by-util/test_chgrp.rs b/tests/by-util/test_chgrp.rs index 3bd0c69e5..adc0f6981 100644 --- a/tests/by-util/test_chgrp.rs +++ b/tests/by-util/test_chgrp.rs @@ -110,8 +110,7 @@ fn test_reference() { .arg("--reference=/etc/passwd") .arg("/etc") .fails() - .stderr_is("chgrp: changing group of '/etc': Operation not permitted (os error 1)\n") - .stdout_is("failed to change group of /etc from root to root\n"); + .stderr_is("chgrp: changing group of '/etc': Operation not permitted (os error 1)\nfailed to change group of /etc from root to root"); } } From dca1f2808556a4de1916a6b25bd98c7edd6bd29f Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 19 Nov 2020 22:28:13 +0100 Subject: [PATCH 5/6] install: various improvements in output & tests --- Cargo.lock | 2 -- src/uu/chown/src/chown.rs | 4 ---- src/uu/install/src/install.rs | 10 ++------- src/uucore/src/lib/features/perms.rs | 17 ++++++--------- tests/by-util/test_chgrp.rs | 2 +- tests/by-util/test_chown.rs | 8 +++++-- tests/by-util/test_install.rs | 32 ++++++++++++++++++++++------ 7 files changed, 41 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 43fada101..955626985 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,3 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. [[package]] name = "advapi32-sys" version = "0.2.0" diff --git a/src/uu/chown/src/chown.rs b/src/uu/chown/src/chown.rs index 47279747f..460f1da86 100644 --- a/src/uu/chown/src/chown.rs +++ b/src/uu/chown/src/chown.rs @@ -26,9 +26,6 @@ use std::os::unix::fs::MetadataExt; use std::convert::AsRef; use std::path::Path; -use std::ffi::CString; -use std::os::unix::ffi::OsStrExt; - static ABOUT: &str = "change file owner and group"; static VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -390,7 +387,6 @@ impl Chowner { 0 } Err(e) => { - if self.verbosity != Verbosity::Silent { show_info!("{}", e); } diff --git a/src/uu/install/src/install.rs b/src/uu/install/src/install.rs index ce6ba3bf1..37f69a2f0 100644 --- a/src/uu/install/src/install.rs +++ b/src/uu/install/src/install.rs @@ -338,14 +338,8 @@ fn behavior(matches: &ArgMatches) -> Result { main_function, specified_mode, suffix: backup_suffix.to_string(), - owner: matches - .value_of(OPT_OWNER) - .unwrap_or_else(|| "") - .to_string(), - group: matches - .value_of(OPT_GROUP) - .unwrap_or_else(|| "") - .to_string(), + owner: matches.value_of(OPT_OWNER).unwrap_or("").to_string(), + group: matches.value_of(OPT_GROUP).unwrap_or("").to_string(), verbose: matches.is_present(OPT_VERBOSE), }) } diff --git a/src/uucore/src/lib/features/perms.rs b/src/uucore/src/lib/features/perms.rs index 25f49012c..bbe53ac2a 100644 --- a/src/uucore/src/lib/features/perms.rs +++ b/src/uucore/src/lib/features/perms.rs @@ -6,9 +6,6 @@ pub use crate::features::entries; use libc::{self, gid_t, lchown, uid_t}; -#[macro_use] -pub use crate::*; - use std::io::Error as IOError; use std::io::Result as IOResult; @@ -19,8 +16,6 @@ use std::os::unix::fs::MetadataExt; use std::os::unix::ffi::OsStrExt; use std::path::Path; -//type PermResult = Result; - #[derive(PartialEq, Clone, Debug)] pub enum Verbosity { Silent, @@ -64,7 +59,7 @@ pub fn wrap_chgrp>( out = format!("changing group of '{}': {}", path.display(), e); if verbosity == Verbose { out = format!( - "{}\nfailed to change group of {} from {} to {}", + "{}\nfailed to change group of '{}' from {} to {}", out, path.display(), entries::gid2grp(meta.gid()).unwrap(), @@ -80,7 +75,7 @@ pub fn wrap_chgrp>( match verbosity { Changes | Verbose => { out = format!( - "changed group of {} from {} to {}", + "changed group of '{}' from {} to {}", path.display(), entries::gid2grp(meta.gid()).unwrap(), entries::gid2grp(dest_gid).unwrap() @@ -90,7 +85,7 @@ pub fn wrap_chgrp>( }; } else if verbosity == Verbose { out = format!( - "group of {} retained as {}", + "group of '{}' retained as {}", path.display(), entries::gid2grp(dest_gid).unwrap() ); @@ -137,7 +132,7 @@ pub fn wrap_chown>( out = format!("changing ownership of '{}': {}", path.display(), e); if verbosity == Verbose { out = format!( - "{}\nfailed to change ownership of {} from {}:{} to {}:{}", + "{}\nfailed to change ownership of '{}' from {}:{} to {}:{}", out, path.display(), entries::uid2usr(meta.uid()).unwrap(), @@ -155,7 +150,7 @@ pub fn wrap_chown>( match verbosity { Changes | Verbose => { out = format!( - "changed ownership of {} from {}:{} to {}:{}", + "changed ownership of '{}' from {}:{} to {}:{}", path.display(), entries::uid2usr(meta.uid()).unwrap(), entries::gid2grp(meta.gid()).unwrap(), @@ -167,7 +162,7 @@ pub fn wrap_chown>( }; } else if verbosity == Verbose { out = format!( - "ownership of {} retained as {}:{}", + "ownership of '{}' retained as {}:{}", path.display(), entries::uid2usr(dest_uid).unwrap(), entries::gid2grp(dest_gid).unwrap() diff --git a/tests/by-util/test_chgrp.rs b/tests/by-util/test_chgrp.rs index adc0f6981..d5afaf3a7 100644 --- a/tests/by-util/test_chgrp.rs +++ b/tests/by-util/test_chgrp.rs @@ -110,7 +110,7 @@ fn test_reference() { .arg("--reference=/etc/passwd") .arg("/etc") .fails() - .stderr_is("chgrp: changing group of '/etc': Operation not permitted (os error 1)\nfailed to change group of /etc from root to root"); + .stderr_is("chgrp: changing group of '/etc': Operation not permitted (os error 1)\nfailed to change group of '/etc' from root to root"); } } diff --git a/tests/by-util/test_chown.rs b/tests/by-util/test_chown.rs index 9e526bca9..afd6f567f 100644 --- a/tests/by-util/test_chown.rs +++ b/tests/by-util/test_chown.rs @@ -1,5 +1,6 @@ use crate::common::util::*; -use rust_users::*; +#[cfg(target_os = "linux")] +use rust_users::get_effective_uid; extern crate chown; @@ -345,7 +346,9 @@ fn test_chown_recursive() { // As seems to be a configuration issue, ignoring it return; } - assert!(result.stdout.contains("ownership of a/a retained as")); + + assert!(result.stderr.contains("ownership of 'a/a' retained as")); + assert!(result.stderr.contains("ownership of 'z/y' retained as")); assert!(result.success); } @@ -378,6 +381,7 @@ fn test_root_preserve() { assert!(result .stderr .contains("chown: it is dangerous to operate recursively")); +} #[cfg(target_os = "linux")] fn test_big_p() { diff --git a/tests/by-util/test_install.rs b/tests/by-util/test_install.rs index 8066bdc25..89ae515a7 100644 --- a/tests/by-util/test_install.rs +++ b/tests/by-util/test_install.rs @@ -216,13 +216,23 @@ fn test_install_target_new_file_with_group() { at.touch(file); at.mkdir(dir); - ucmd.arg(file) + let result = ucmd + .arg(file) .arg("--group") .arg(gid.to_string()) .arg(format!("{}/{}", dir, file)) - .succeeds() - .no_stderr(); + .run(); + println!("stderr = {:?}", result.stderr); + println!("stdout = {:?}", result.stdout); + + if is_ci() && result.stderr.contains("error: no such group:") { + // In the CI, some server are failing to return the group. + // As seems to be a configuration issue, ignoring it + return; + } + + assert!(result.success); assert!(at.file_exists(file)); assert!(at.file_exists(&format!("{}/{}", dir, file))); } @@ -236,13 +246,23 @@ fn test_install_target_new_file_with_owner() { at.touch(file); at.mkdir(dir); - ucmd.arg(file) + let result = ucmd + .arg(file) .arg("--owner") .arg(uid.to_string()) .arg(format!("{}/{}", dir, file)) - .succeeds() - .no_stderr(); + .run(); + println!("stderr = {:?}", result.stderr); + println!("stdout = {:?}", result.stdout); + + if is_ci() && result.stderr.contains("error: no such user:") { + // In the CI, some server are failing to return the user id. + // As seems to be a configuration issue, ignoring it + return; + } + + assert!(result.success); assert!(at.file_exists(file)); assert!(at.file_exists(&format!("{}/{}", dir, file))); } From f287309b72ff61b132af50179e7d9c394bfd4e02 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 12 Dec 2020 14:43:56 +0100 Subject: [PATCH 6/6] perms: add some docs --- src/uucore/src/lib/features/perms.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/uucore/src/lib/features/perms.rs b/src/uucore/src/lib/features/perms.rs index bbe53ac2a..66db15451 100644 --- a/src/uucore/src/lib/features/perms.rs +++ b/src/uucore/src/lib/features/perms.rs @@ -16,6 +16,7 @@ use std::os::unix::fs::MetadataExt; use std::os::unix::ffi::OsStrExt; use std::path::Path; +/// The various level of verbosity #[derive(PartialEq, Clone, Debug)] pub enum Verbosity { Silent, @@ -24,6 +25,7 @@ pub enum Verbosity { Normal, } +/// Actually perform the change of group on a path fn chgrp>(path: P, dgid: gid_t, follow: bool) -> IOResult<()> { let path = path.as_ref(); let s = CString::new(path.as_os_str().as_bytes()).unwrap(); @@ -41,6 +43,9 @@ fn chgrp>(path: P, dgid: gid_t, follow: bool) -> IOResult<()> { } } +/// Perform the change of group on a path +/// with the various options +/// and error messages management pub fn wrap_chgrp>( path: P, meta: &Metadata, @@ -94,6 +99,7 @@ pub fn wrap_chgrp>( Ok(out) } +/// Actually perform the change of owner on a path fn chown>(path: P, duid: uid_t, dgid: gid_t, follow: bool) -> IOResult<()> { let path = path.as_ref(); let s = CString::new(path.as_os_str().as_bytes()).unwrap(); @@ -111,6 +117,9 @@ fn chown>(path: P, duid: uid_t, dgid: gid_t, follow: bool) -> IOR } } +/// Perform the change of owner on a path +/// with the various options +/// and error messages management pub fn wrap_chown>( path: P, meta: &Metadata,