mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
feature(install): move chown functions into uucore and have install owner support use it
This commit is contained in:
parent
015e18731f
commit
55c660b986
7 changed files with 167 additions and 103 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -1,3 +1,5 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "advapi32-sys"
|
name = "advapi32-sys"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
|
|
@ -17,7 +17,7 @@ path = "src/chown.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
glob = "0.3.0"
|
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" }
|
uucore_procs = { version=">=0.0.4", package="uucore_procs", path="../../uucore_procs" }
|
||||||
walkdir = "2.2"
|
walkdir = "2.2"
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
pub use uucore::entries::{self, Group, Locate, Passwd};
|
pub use uucore::entries::{self, Group, Locate, Passwd};
|
||||||
use uucore::fs::resolve_relative_path;
|
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;
|
extern crate clap;
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
|
@ -22,9 +23,6 @@ use walkdir::WalkDir;
|
||||||
use std::fs::{self, Metadata};
|
use std::fs::{self, Metadata};
|
||||||
use std::os::unix::fs::MetadataExt;
|
use std::os::unix::fs::MetadataExt;
|
||||||
|
|
||||||
use std::io;
|
|
||||||
use std::io::Result as IOResult;
|
|
||||||
|
|
||||||
use std::convert::AsRef;
|
use std::convert::AsRef;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
@ -304,14 +302,6 @@ fn parse_spec(spec: &str) -> Result<(Option<u32>, Option<u32>), String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
|
||||||
enum Verbosity {
|
|
||||||
Silent,
|
|
||||||
Changes,
|
|
||||||
Verbose,
|
|
||||||
Normal,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum IfFrom {
|
enum IfFrom {
|
||||||
All,
|
All,
|
||||||
User(u32),
|
User(u32),
|
||||||
|
@ -349,29 +339,6 @@ impl Chowner {
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chown<P: AsRef<Path>>(
|
|
||||||
&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<P: AsRef<Path>>(&self, root: P) -> i32 {
|
fn traverse<P: AsRef<Path>>(&self, root: P) -> i32 {
|
||||||
let follow_arg = self.dereference || self.bit_flag != FTS_PHYSICAL;
|
let follow_arg = self.dereference || self.bit_flag != FTS_PHYSICAL;
|
||||||
let path = root.as_ref();
|
let path = root.as_ref();
|
||||||
|
@ -408,7 +375,14 @@ impl Chowner {
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret = if self.matched(meta.uid(), meta.gid()) {
|
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 {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
@ -443,7 +417,14 @@ impl Chowner {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = self.wrap_chown(path, &meta, follow);
|
ret = wrap_chown(
|
||||||
|
path,
|
||||||
|
&meta,
|
||||||
|
self.dest_uid,
|
||||||
|
self.dest_gid,
|
||||||
|
follow,
|
||||||
|
self.verbosity.clone(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
@ -471,58 +452,6 @@ impl Chowner {
|
||||||
Some(meta)
|
Some(meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrap_chown<P: AsRef<Path>>(&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]
|
#[inline]
|
||||||
fn matched(&self, uid: uid_t, gid: gid_t) -> bool {
|
fn matched(&self, uid: uid_t, gid: gid_t) -> bool {
|
||||||
match self.filter {
|
match self.filter {
|
||||||
|
|
|
@ -16,10 +16,11 @@ mod mode;
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
|
|
||||||
use clap::{App, Arg, ArgMatches};
|
use clap::{App, Arg, ArgMatches};
|
||||||
use uucore::perms::{wrap_chgrp, Verbosity};
|
use uucore::entries::{grp2gid, usr2uid};
|
||||||
use uucore::entries::grp2gid;
|
use uucore::perms::{wrap_chgrp, wrap_chown, Verbosity};
|
||||||
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::os::unix::fs::MetadataExt;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
|
|
||||||
|
@ -30,6 +31,7 @@ pub struct Behavior {
|
||||||
main_function: MainFunction,
|
main_function: MainFunction,
|
||||||
specified_mode: Option<u32>,
|
specified_mode: Option<u32>,
|
||||||
suffix: String,
|
suffix: String,
|
||||||
|
owner: String,
|
||||||
group: String,
|
group: String,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
}
|
}
|
||||||
|
@ -147,12 +149,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
.min_values(1)
|
.min_values(1)
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
// TODO implement flag
|
|
||||||
Arg::with_name(OPT_OWNER)
|
Arg::with_name(OPT_OWNER)
|
||||||
.short("o")
|
.short("o")
|
||||||
.long(OPT_OWNER)
|
.long(OPT_OWNER)
|
||||||
.help("(unimplemented) set ownership (super-user only)")
|
.help("set ownership (super-user only)")
|
||||||
.value_name("OWNER")
|
.value_name("OWNER")
|
||||||
|
.takes_value(true)
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
// TODO implement flag
|
// TODO implement flag
|
||||||
|
@ -266,8 +268,6 @@ fn check_unimplemented<'a>(matches: &ArgMatches) -> Result<(), &'a str> {
|
||||||
Err("--compare, -C")
|
Err("--compare, -C")
|
||||||
} else if matches.is_present(OPT_CREATED) {
|
} else if matches.is_present(OPT_CREATED) {
|
||||||
Err("-D")
|
Err("-D")
|
||||||
} else if matches.is_present(OPT_OWNER) {
|
|
||||||
Err("--owner, -o")
|
|
||||||
} else if matches.is_present(OPT_PRESERVE_TIMESTAMPS) {
|
} else if matches.is_present(OPT_PRESERVE_TIMESTAMPS) {
|
||||||
Err("--preserve-timestamps, -p")
|
Err("--preserve-timestamps, -p")
|
||||||
} else if matches.is_present(OPT_STRIP) {
|
} else if matches.is_present(OPT_STRIP) {
|
||||||
|
@ -334,13 +334,18 @@ fn behavior(matches: &ArgMatches) -> Result<Behavior, i32> {
|
||||||
"~"
|
"~"
|
||||||
};
|
};
|
||||||
|
|
||||||
let group = matches.value_of(OPT_GROUP).unwrap_or_else(|| "");
|
|
||||||
|
|
||||||
Ok(Behavior {
|
Ok(Behavior {
|
||||||
main_function,
|
main_function,
|
||||||
specified_mode,
|
specified_mode,
|
||||||
suffix: backup_suffix.to_string(),
|
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),
|
verbose: matches.is_present(OPT_VERBOSE),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -500,6 +505,27 @@ fn copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> Result<(), ()> {
|
||||||
return Err(());
|
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 != "" {
|
if b.group != "" {
|
||||||
let meta = match fs::metadata(to) {
|
let meta = match fs::metadata(to) {
|
||||||
Ok(meta) => meta,
|
Ok(meta) => meta,
|
||||||
|
@ -511,7 +537,6 @@ fn copy(from: &PathBuf, to: &PathBuf, b: &Behavior) -> Result<(), ()> {
|
||||||
_ => crash!(1, "no such group: {}", b.group),
|
_ => crash!(1, "no such group: {}", b.group),
|
||||||
};
|
};
|
||||||
wrap_chgrp(to.as_path(), &meta, group_id, false, Verbosity::Normal);
|
wrap_chgrp(to.as_path(), &meta, group_id, false, Verbosity::Normal);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.verbose {
|
if b.verbose {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
pub use crate::features::entries;
|
pub use crate::features::entries;
|
||||||
use libc::{self, gid_t, lchown};
|
use libc::{self, gid_t, lchown, uid_t};
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub use crate::*;
|
pub use crate::*;
|
||||||
|
@ -94,3 +94,79 @@ pub fn wrap_chgrp<P: AsRef<Path>>(
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn chown<P: AsRef<Path>>(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<P: AsRef<Path>>(
|
||||||
|
path: P,
|
||||||
|
meta: &Metadata,
|
||||||
|
dest_uid: Option<u32>,
|
||||||
|
dest_gid: Option<u32>,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::common::util::*;
|
use crate::common::util::*;
|
||||||
|
use rust_users::*;
|
||||||
|
|
||||||
extern crate chown;
|
extern crate chown;
|
||||||
// pub use self::uu_chown::*;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_passgrp {
|
mod test_passgrp {
|
||||||
|
@ -378,4 +378,17 @@ fn test_root_preserve() {
|
||||||
assert!(result
|
assert!(result
|
||||||
.stderr
|
.stderr
|
||||||
.contains("chown: it is dangerous to operate recursively"));
|
.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",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::common::util::*;
|
use crate::common::util::*;
|
||||||
use std::os::unix::fs::PermissionsExt;
|
|
||||||
use rust_users::*;
|
use rust_users::*;
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_install_help() {
|
fn test_install_help() {
|
||||||
|
@ -227,6 +227,25 @@ fn test_install_target_new_file_with_group() {
|
||||||
assert!(at.file_exists(&format!("{}/{}", dir, 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);
|
||||||
|
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]
|
#[test]
|
||||||
fn test_install_target_new_file_failing_nonexistent_parent() {
|
fn test_install_target_new_file_failing_nonexistent_parent() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue