mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 03:27:44 +00:00
chown: refactor
This commit is contained in:
parent
d4b2766c4b
commit
7e4a708e7c
2 changed files with 138 additions and 94 deletions
|
@ -8,9 +8,6 @@
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
//
|
//
|
||||||
|
|
||||||
#![cfg_attr(feature="clippy", feature(plugin))]
|
|
||||||
#![cfg_attr(feature="clippy", plugin(clippy))]
|
|
||||||
|
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
use libc::{uid_t, gid_t, c_char, c_int};
|
use libc::{uid_t, gid_t, c_char, c_int};
|
||||||
|
|
||||||
|
@ -20,9 +17,12 @@ extern crate uucore;
|
||||||
extern crate getopts;
|
extern crate getopts;
|
||||||
use getopts::Options;
|
use getopts::Options;
|
||||||
|
|
||||||
|
extern crate walkdir;
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
pub mod passwd;
|
pub mod passwd;
|
||||||
|
|
||||||
use std::fs;
|
use std::fs::{self, Metadata};
|
||||||
use std::os::unix::fs::MetadataExt;
|
use std::os::unix::fs::MetadataExt;
|
||||||
|
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
|
@ -34,8 +34,6 @@ use std::convert::AsRef;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::os::unix::ffi::OsStrExt;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
static NAME: &'static str = "chown";
|
static NAME: &'static str = "chown";
|
||||||
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
|
@ -44,6 +42,7 @@ const FTS_PHYSICAL: u8 = 1 << 1;
|
||||||
const FTS_LOGICAL: u8 = 1 << 2;
|
const FTS_LOGICAL: u8 = 1 << 2;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
#[cfg_attr(all(target_os = "macos", target_arch = "x86"), link_name = "lchown$UNIX2003")]
|
||||||
pub fn lchown(path: *const c_char, uid: uid_t, gid: gid_t) -> c_int;
|
pub fn lchown(path: *const c_char, uid: uid_t, gid: gid_t) -> c_int;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,12 +96,20 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
let mut bit_flag = FTS_PHYSICAL;
|
let mut bit_flag = FTS_PHYSICAL;
|
||||||
let mut preserve_root = false;
|
let mut preserve_root = false;
|
||||||
let mut derefer = -1;
|
let mut derefer = -1;
|
||||||
|
let flags: &[char] = &['H', 'L', 'P'];
|
||||||
for opt in &args {
|
for opt in &args {
|
||||||
match opt.as_str() {
|
match opt.as_str() {
|
||||||
// If more than one is specified, only the final one takes effect.
|
// If more than one is specified, only the final one takes effect.
|
||||||
s if s.contains('H') => bit_flag = FTS_COMFOLLOW | FTS_PHYSICAL,
|
s if s.contains(flags) => {
|
||||||
s if s.contains('L') => bit_flag = FTS_LOGICAL,
|
if let Some(idx) = s.rfind(flags) {
|
||||||
s if s.contains('P') => bit_flag = FTS_PHYSICAL,
|
match s.chars().nth(idx).unwrap() {
|
||||||
|
'H' => bit_flag = FTS_COMFOLLOW | FTS_PHYSICAL,
|
||||||
|
'L' => bit_flag = FTS_LOGICAL,
|
||||||
|
'P' => bit_flag = FTS_PHYSICAL,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
"--no-preserve-root" => preserve_root = false,
|
"--no-preserve-root" => preserve_root = false,
|
||||||
"--preserve-root" => preserve_root = true,
|
"--preserve-root" => preserve_root = true,
|
||||||
"--dereference" => derefer = 1,
|
"--dereference" => derefer = 1,
|
||||||
|
@ -167,7 +174,6 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
let dest_uid: Option<u32>;
|
let dest_uid: Option<u32>;
|
||||||
let dest_gid: Option<u32>;
|
let dest_gid: Option<u32>;
|
||||||
if let Some(file) = matches.opt_str("reference") {
|
if let Some(file) = matches.opt_str("reference") {
|
||||||
// matches.opt_present("reference")
|
|
||||||
match fs::metadata(&file) {
|
match fs::metadata(&file) {
|
||||||
Ok(meta) => {
|
Ok(meta) => {
|
||||||
dest_gid = Some(meta.gid());
|
dest_gid = Some(meta.gid());
|
||||||
|
@ -238,11 +244,12 @@ fn parse_spec(spec: &str) -> Result<(Option<u32>, Option<u32>), String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
enum Verbosity {
|
enum Verbosity {
|
||||||
Silent,
|
Silent,
|
||||||
Changes,
|
Changes,
|
||||||
Normal,
|
|
||||||
Verbose,
|
Verbose,
|
||||||
|
Normal,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum IfFrom {
|
enum IfFrom {
|
||||||
|
@ -264,6 +271,15 @@ struct Chowner {
|
||||||
dereference: bool,
|
dereference: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! unwrap {
|
||||||
|
($m:expr, $e:ident, $err:block) => (
|
||||||
|
match $m {
|
||||||
|
Ok(meta) => meta,
|
||||||
|
Err($e) => $err,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
impl Chowner {
|
impl Chowner {
|
||||||
fn exec(&self) -> i32 {
|
fn exec(&self) -> i32 {
|
||||||
let mut ret = 0;
|
let mut ret = 0;
|
||||||
|
@ -274,23 +290,19 @@ impl Chowner {
|
||||||
ret = 1;
|
ret = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ret = self.traverse(f);
|
ret |= self.traverse(f);
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chown<P: AsRef<Path>>(&self, path: P, follow: bool) -> IOResult<()> {
|
fn chown<P: AsRef<Path>>(&self, path: P, duid: uid_t, dgid: gid_t, follow: bool) -> IOResult<()> {
|
||||||
let s = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
|
let path = path.as_ref();
|
||||||
|
let s = CString::new(path.as_os_str().as_bytes()).unwrap();
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
if follow {
|
if follow {
|
||||||
libc::chown(s.as_ptr(),
|
libc::chown(s.as_ptr(), duid, dgid)
|
||||||
self.dest_uid.unwrap_or((0 as uid_t).wrapping_sub(1)),
|
|
||||||
self.dest_gid.unwrap_or((0 as gid_t).wrapping_sub(1)))
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
lchown(s.as_ptr(),
|
lchown(s.as_ptr(), duid, dgid)
|
||||||
self.dest_uid.unwrap_or((0 as uid_t).wrapping_sub(1)),
|
|
||||||
self.dest_gid.unwrap_or((0 as gid_t).wrapping_sub(1)))
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if ret == 0 {
|
if ret == 0 {
|
||||||
|
@ -301,84 +313,118 @@ impl Chowner {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn traverse<P: AsRef<Path>>(&self, root: P) -> i32 {
|
fn traverse<P: AsRef<Path>>(&self, root: P) -> i32 {
|
||||||
let mut ret = 0;
|
let follow_arg = self.dereference || self.bit_flag != FTS_PHYSICAL;
|
||||||
let follow_arg = self.dereference || self.recursive && self.bit_flag != FTS_PHYSICAL;
|
let path = root.as_ref();
|
||||||
let root_path = root.as_ref();
|
let meta = match self.obtain_meta(path, follow_arg) {
|
||||||
let meta = if follow_arg {
|
Some(m) => m,
|
||||||
match root_path.metadata() {
|
_ => return 1,
|
||||||
Ok(meta) => meta,
|
|
||||||
Err(e) => {
|
|
||||||
show_info!("cannot dereference '{}': {}", root_path.display(), e);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match root_path.symlink_metadata() {
|
|
||||||
Ok(meta) => meta,
|
|
||||||
Err(e) => {
|
|
||||||
show_info!("cannot access '{}': {}", root_path.display(), e);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
if self.matched(meta.uid(), meta.gid()) {
|
|
||||||
if let Err(e) = self.chown(root.as_ref(), follow_arg) {
|
|
||||||
show_info!("changing ownership of '{}': {}", root_path.display(), e);
|
|
||||||
ret = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !meta.is_dir() || !self.recursive {
|
let ret = if self.matched(meta.uid(), meta.gid()) {
|
||||||
|
self.wrap_chown(path, &meta, follow_arg)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
if !self.recursive {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut dirs = vec![];
|
self.dive_into(&root)
|
||||||
dirs.push(Arc::new(root_path.to_path_buf()));
|
}
|
||||||
while !dirs.is_empty() {
|
|
||||||
let dir = dirs.pop().expect("Poping directory");
|
fn dive_into<P: AsRef<Path>>(&self, root: P) -> i32 {
|
||||||
for entry in dir.read_dir().unwrap() {
|
let mut ret = 0;
|
||||||
let entry = entry.unwrap();
|
let root = root.as_ref();
|
||||||
let path = Arc::new(entry.path());
|
let follow = self.bit_flag & FTS_LOGICAL != 0;
|
||||||
let smeta = if self.bit_flag & FTS_LOGICAL != 0 {
|
for entry in WalkDir::new(root).follow_links(follow).min_depth(1) {
|
||||||
match path.metadata() {
|
let entry = unwrap!(entry, e, {
|
||||||
Ok(meta) => meta,
|
ret = 1;
|
||||||
Err(e) => {
|
show_info!("{}", e);
|
||||||
show_info!("cannot access '{}': {}", path.display(), e);
|
continue;
|
||||||
ret = 1;
|
});
|
||||||
continue;
|
let path = entry.path();
|
||||||
}
|
let meta = match self.obtain_meta(path, follow) {
|
||||||
}
|
Some(m) => m,
|
||||||
} else {
|
_ => {
|
||||||
match path.symlink_metadata() {
|
ret = 1;
|
||||||
Ok(meta) => meta,
|
continue;
|
||||||
Err(e) => {
|
|
||||||
show_info!("cannot dereference '{}': {}", path.display(), e);
|
|
||||||
ret = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if smeta.is_dir() {
|
|
||||||
dirs.push(path.clone());
|
|
||||||
}
|
}
|
||||||
let meta = if self.bit_flag == FTS_PHYSICAL {
|
};
|
||||||
smeta
|
|
||||||
} else {
|
if !self.matched(meta.uid(), meta.gid()) {
|
||||||
match path.metadata() {
|
continue;
|
||||||
Ok(meta) => meta,
|
}
|
||||||
Err(e) => {
|
|
||||||
show_info!("cannot dereference '{}': {}", path.display(), e);
|
ret = self.wrap_chown(path, &meta, true);
|
||||||
ret = 1;
|
}
|
||||||
continue;
|
ret
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
fn obtain_meta<P: AsRef<Path>>(&self, path: P, follow: bool) -> Option<Metadata> {
|
||||||
if self.matched(meta.uid(), meta.gid()) {
|
use self::Verbosity::*;
|
||||||
if let Err(e) = self.chown(&*path, true) {
|
let path = path.as_ref();
|
||||||
ret = 1;
|
let meta = if follow {
|
||||||
show_info!("changing ownership of '{}': {}", path.display(), e);
|
unwrap!(path.metadata(), e, {
|
||||||
}
|
match self.verbosity {
|
||||||
|
Silent => (),
|
||||||
|
_ => show_info!("cannot access '{}': {}", path.display(), e),
|
||||||
}
|
}
|
||||||
|
return None;
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
unwrap!(path.symlink_metadata(), e, {
|
||||||
|
match self.verbosity {
|
||||||
|
Silent => (),
|
||||||
|
_ => show_info!("cannot dereference '{}': {}", path.display(), e),
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
})
|
||||||
|
};
|
||||||
|
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(meta.uid());
|
||||||
|
let dest_gid = self.dest_gid.unwrap_or(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(),
|
||||||
|
passwd::uid2usr(meta.uid()).unwrap(),
|
||||||
|
passwd::gid2grp(meta.gid()).unwrap(),
|
||||||
|
passwd::uid2usr(dest_uid).unwrap(),
|
||||||
|
passwd::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(),
|
||||||
|
passwd::uid2usr(meta.uid()).unwrap(),
|
||||||
|
passwd::gid2grp(meta.gid()).unwrap(),
|
||||||
|
passwd::uid2usr(dest_uid).unwrap(),
|
||||||
|
passwd::gid2grp(dest_gid).unwrap());
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
} else if self.verbosity == Verbose {
|
||||||
|
println!("ownership of {} retained as {}:{}",
|
||||||
|
path.display(),
|
||||||
|
passwd::uid2usr(dest_uid).unwrap(),
|
||||||
|
passwd::gid2grp(dest_gid).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
|
|
|
@ -12,7 +12,6 @@ mod test_passwd {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_getuid() {
|
fn test_getuid() {
|
||||||
assert_eq!(0, getuid("root").unwrap());
|
assert_eq!(0, getuid("root").unwrap());
|
||||||
assert_eq!(99, getuid("99").unwrap());
|
|
||||||
assert!(getuid("88888888").is_err());
|
assert!(getuid("88888888").is_err());
|
||||||
assert!(getuid("agroupthatdoesntexist").is_err());
|
assert!(getuid("agroupthatdoesntexist").is_err());
|
||||||
}
|
}
|
||||||
|
@ -20,7 +19,6 @@ mod test_passwd {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_getgid() {
|
fn test_getgid() {
|
||||||
assert_eq!(0, getgid("root").unwrap());
|
assert_eq!(0, getgid("root").unwrap());
|
||||||
assert_eq!(99, getgid("99").unwrap());
|
|
||||||
assert!(getgid("88888888").is_err());
|
assert!(getgid("88888888").is_err());
|
||||||
assert!(getgid("agroupthatdoesntexist").is_err());
|
assert!(getgid("agroupthatdoesntexist").is_err());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue