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..b4c3360c5 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,24 @@ impl Chgrper { } } - let ret = self.wrap_chgrp(path, &meta, follow_arg); + 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 @@ -297,8 +284,22 @@ impl Chgrper { } }; - ret = self.wrap_chgrp(path, &meta, follow); + 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 } @@ -324,50 +325,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/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..460f1da86 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,15 +23,9 @@ 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; -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"); @@ -304,14 +299,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 +336,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 +372,27 @@ impl Chowner { } let ret = if self.matched(meta.uid(), meta.gid()) { - self.wrap_chown(path, &meta, follow_arg) + 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 }; @@ -443,7 +427,27 @@ impl Chowner { continue; } - ret = self.wrap_chown(path, &meta, follow); + 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 } @@ -471,58 +475,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/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..37f69a2f0 100644 --- a/src/uu/install/src/install.rs +++ b/src/uu/install/src/install.rs @@ -16,7 +16,11 @@ mod mode; extern crate uucore; use clap::{App, Arg, ArgMatches}; +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; @@ -27,6 +31,8 @@ pub struct Behavior { main_function: MainFunction, specified_mode: Option, suffix: String, + owner: String, + group: String, verbose: bool, } @@ -126,12 +132,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,14 +145,16 @@ 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 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 @@ -176,6 +184,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 +224,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,10 +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_GROUP) { - Err("--group, -g") - } 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) { @@ -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); } } @@ -342,6 +338,8 @@ fn behavior(matches: &ArgMatches) -> Result { main_function, specified_mode, suffix: backup_suffix.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), }) } @@ -400,22 +398,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 +487,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 +499,54 @@ 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(); + 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 != "" { + 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), + }; + 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 { show_info!("'{}' -> '{}'", from.display(), to.display()); } 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..66db15451 --- /dev/null +++ b/src/uucore/src/lib/features/perms.rs @@ -0,0 +1,182 @@ +// 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, uid_t}; + +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; + +/// The various level of verbosity +#[derive(PartialEq, Clone, Debug)] +pub enum Verbosity { + Silent, + Changes, + Verbose, + 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(); + 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()) + } +} + +/// Perform the change of group on a path +/// with the various options +/// and error messages management +pub fn wrap_chgrp>( + path: P, + meta: &Metadata, + dest_gid: gid_t, + follow: bool, + verbosity: Verbosity, +) -> Result { + use self::Verbosity::*; + let path = path.as_ref(); + let mut out: String = String::new(); + + if let Err(e) = chgrp(path, dest_gid, follow) { + match verbosity { + Silent => (), + _ => { + out = format!("changing group of '{}': {}", path.display(), e); + if verbosity == Verbose { + out = format!( + "{}\nfailed to change group of '{}' from {} to {}", + out, + path.display(), + entries::gid2grp(meta.gid()).unwrap(), + entries::gid2grp(dest_gid).unwrap() + ); + }; + } + } + return Err(out); + } else { + let changed = dest_gid != meta.gid(); + if changed { + match verbosity { + Changes | Verbose => { + out = format!( + "changed group of '{}' from {} to {}", + path.display(), + entries::gid2grp(meta.gid()).unwrap(), + entries::gid2grp(dest_gid).unwrap() + ); + } + _ => (), + }; + } else if verbosity == Verbose { + out = format!( + "group of '{}' retained as {}", + path.display(), + entries::gid2grp(dest_gid).unwrap() + ); + } + } + 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(); + 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()) + } +} + +/// Perform the change of owner on a path +/// with the various options +/// and error messages management +pub fn wrap_chown>( + path: P, + meta: &Metadata, + dest_uid: Option, + dest_gid: Option, + follow: bool, + verbosity: Verbosity, +) -> Result { + use self::Verbosity::*; + 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 => (), + _ => { + out = format!("changing ownership of '{}': {}", path.display(), e); + if verbosity == Verbose { + out = format!( + "{}\nfailed to change ownership of '{}' from {}:{} to {}:{}", + out, + path.display(), + entries::uid2usr(meta.uid()).unwrap(), + entries::gid2grp(meta.gid()).unwrap(), + entries::uid2usr(dest_uid).unwrap(), + entries::gid2grp(dest_gid).unwrap() + ); + }; + } + } + return Err(out); + } else { + let changed = dest_uid != meta.uid() || dest_gid != meta.gid(); + if changed { + match verbosity { + Changes | Verbose => { + out = format!( + "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 { + out = format!( + "ownership of '{}' retained as {}:{}", + path.display(), + entries::uid2usr(dest_uid).unwrap(), + entries::gid2grp(dest_gid).unwrap() + ); + } + } + Ok(out) +} 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"))] diff --git a/tests/by-util/test_chgrp.rs b/tests/by-util/test_chgrp.rs index 3bd0c69e5..d5afaf3a7 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"); } } diff --git a/tests/by-util/test_chown.rs b/tests/by-util/test_chown.rs index a943967f2..afd6f567f 100644 --- a/tests/by-util/test_chown.rs +++ b/tests/by-util/test_chown.rs @@ -1,7 +1,8 @@ use crate::common::util::*; +#[cfg(target_os = "linux")] +use rust_users::get_effective_uid; extern crate chown; -// pub use self::uu_chown::*; #[cfg(test)] mod test_passgrp { @@ -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); } @@ -379,3 +382,17 @@ fn test_root_preserve() { .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 ec38d11b0..89ae515a7 100644 --- a/tests/by-util/test_install.rs +++ b/tests/by-util/test_install.rs @@ -1,4 +1,5 @@ use crate::common::util::*; +use rust_users::*; use std::os::unix::fs::PermissionsExt; #[test] @@ -206,6 +207,66 @@ 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); + let result = ucmd + .arg(file) + .arg("--group") + .arg(gid.to_string()) + .arg(format!("{}/{}", dir, file)) + .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))); +} + +#[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); + let result = ucmd + .arg(file) + .arg("--owner") + .arg(uid.to_string()) + .arg(format!("{}/{}", dir, file)) + .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))); +} + #[test] fn test_install_target_new_file_failing_nonexistent_parent() { let (at, mut ucmd) = at_and_ucmd!();