From 55c660b986174293985b21f078866b65fdcb323c Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 17 Nov 2020 22:13:58 +0100 Subject: [PATCH] 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() {