mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-29 12:07:46 +00:00
refactor: move shared chown logic to uucore
This commit is contained in:
parent
7153a595c6
commit
4e251706be
7 changed files with 214 additions and 221 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -2126,7 +2126,6 @@ name = "uu_chgrp"
|
||||||
version = "0.0.7"
|
version = "0.0.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"uu_chown",
|
|
||||||
"uucore",
|
"uucore",
|
||||||
"uucore_procs",
|
"uucore_procs",
|
||||||
]
|
]
|
||||||
|
@ -2147,10 +2146,8 @@ name = "uu_chown"
|
||||||
version = "0.0.7"
|
version = "0.0.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"glob",
|
|
||||||
"uucore",
|
"uucore",
|
||||||
"uucore_procs",
|
"uucore_procs",
|
||||||
"walkdir",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3165,6 +3162,7 @@ dependencies = [
|
||||||
"termion",
|
"termion",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"time",
|
"time",
|
||||||
|
"walkdir",
|
||||||
"wild",
|
"wild",
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
"z85",
|
"z85",
|
||||||
|
|
|
@ -18,7 +18,6 @@ path = "src/chgrp.rs"
|
||||||
clap = { version = "2.33", features = ["wrap_help"] }
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] }
|
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] }
|
||||||
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" }
|
||||||
uu_chown = { version=">=0.0.6", package="uu_chown", path="../chown"}
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "chgrp"
|
name = "chgrp"
|
||||||
|
|
|
@ -9,10 +9,11 @@
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
use uu_chown::{Chowner, IfFrom};
|
|
||||||
pub use uucore::entries;
|
pub use uucore::entries;
|
||||||
use uucore::error::{FromIo, UResult, USimpleError};
|
use uucore::error::{FromIo, UResult, USimpleError};
|
||||||
use uucore::perms::{Verbosity, VerbosityLevel};
|
use uucore::perms::{
|
||||||
|
ChownExecutor, IfFrom, Verbosity, VerbosityLevel, FTS_COMFOLLOW, FTS_LOGICAL, FTS_PHYSICAL,
|
||||||
|
};
|
||||||
|
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
|
|
||||||
|
@ -50,11 +51,7 @@ pub mod options {
|
||||||
pub static ARG_FILES: &str = "FILE";
|
pub static ARG_FILES: &str = "FILE";
|
||||||
}
|
}
|
||||||
|
|
||||||
const FTS_COMFOLLOW: u8 = 1;
|
fn get_usage() -> String {
|
||||||
const FTS_PHYSICAL: u8 = 1 << 1;
|
|
||||||
const FTS_LOGICAL: u8 = 1 << 2;
|
|
||||||
|
|
||||||
fn usage() -> String {
|
|
||||||
format!(
|
format!(
|
||||||
"{0} [OPTION]... GROUP FILE...\n {0} [OPTION]... --reference=RFILE FILE...",
|
"{0} [OPTION]... GROUP FILE...\n {0} [OPTION]... --reference=RFILE FILE...",
|
||||||
uucore::execution_phrase()
|
uucore::execution_phrase()
|
||||||
|
@ -67,7 +64,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
.collect_str(InvalidEncodingHandling::ConvertLossy)
|
.collect_str(InvalidEncodingHandling::ConvertLossy)
|
||||||
.accept_any();
|
.accept_any();
|
||||||
|
|
||||||
let usage = usage();
|
let usage = get_usage();
|
||||||
|
|
||||||
let mut app = uu_app().usage(&usage[..]);
|
let mut app = uu_app().usage(&usage[..]);
|
||||||
|
|
||||||
|
@ -172,7 +169,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let executor = Chowner {
|
let executor = ChownExecutor {
|
||||||
bit_flag,
|
bit_flag,
|
||||||
dest_gid,
|
dest_gid,
|
||||||
verbosity: Verbosity {
|
verbosity: Verbosity {
|
||||||
|
|
|
@ -16,10 +16,8 @@ path = "src/chown.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "2.33", features = ["wrap_help"] }
|
clap = { version = "2.33", features = ["wrap_help"] }
|
||||||
glob = "0.3.0"
|
|
||||||
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] }
|
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["entries", "fs", "perms"] }
|
||||||
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" }
|
||||||
walkdir = "2.2"
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "chown"
|
name = "chown"
|
||||||
|
|
|
@ -5,26 +5,22 @@
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
// spell-checker:ignore (ToDO) COMFOLLOW Chowner Passwd RFILE RFILE's derefer dgid duid
|
// spell-checker:ignore (ToDO) COMFOLLOW Passwd RFILE RFILE's derefer dgid duid
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
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::perms::{
|
||||||
use uucore::libc::{gid_t, uid_t};
|
ChownExecutor, IfFrom, Verbosity, VerbosityLevel, FTS_COMFOLLOW, FTS_LOGICAL, FTS_PHYSICAL,
|
||||||
use uucore::perms::{wrap_chown, Verbosity, VerbosityLevel};
|
};
|
||||||
|
|
||||||
use uucore::error::{FromIo, UResult, USimpleError};
|
use uucore::error::{FromIo, UResult, USimpleError};
|
||||||
|
|
||||||
use clap::{crate_version, App, Arg};
|
use clap::{crate_version, App, Arg};
|
||||||
|
|
||||||
use walkdir::WalkDir;
|
use std::fs;
|
||||||
|
|
||||||
use std::fs::{self, Metadata};
|
|
||||||
use std::os::unix::fs::MetadataExt;
|
use std::os::unix::fs::MetadataExt;
|
||||||
|
|
||||||
use std::convert::AsRef;
|
|
||||||
use std::path::Path;
|
|
||||||
use uucore::InvalidEncodingHandling;
|
use uucore::InvalidEncodingHandling;
|
||||||
|
|
||||||
static ABOUT: &str = "change file owner and group";
|
static ABOUT: &str = "change file owner and group";
|
||||||
|
@ -57,11 +53,7 @@ pub mod options {
|
||||||
static ARG_OWNER: &str = "owner";
|
static ARG_OWNER: &str = "owner";
|
||||||
static ARG_FILES: &str = "files";
|
static ARG_FILES: &str = "files";
|
||||||
|
|
||||||
const FTS_COMFOLLOW: u8 = 1;
|
fn get_usage() -> String {
|
||||||
const FTS_PHYSICAL: u8 = 1 << 1;
|
|
||||||
const FTS_LOGICAL: u8 = 1 << 2;
|
|
||||||
|
|
||||||
fn usage() -> String {
|
|
||||||
format!(
|
format!(
|
||||||
"{0} [OPTION]... [OWNER][:[GROUP]] FILE...\n{0} [OPTION]... --reference=RFILE FILE...",
|
"{0} [OPTION]... [OWNER][:[GROUP]] FILE...\n{0} [OPTION]... --reference=RFILE FILE...",
|
||||||
uucore::execution_phrase()
|
uucore::execution_phrase()
|
||||||
|
@ -74,7 +66,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
.collect_str(InvalidEncodingHandling::Ignore)
|
.collect_str(InvalidEncodingHandling::Ignore)
|
||||||
.accept_any();
|
.accept_any();
|
||||||
|
|
||||||
let usage = usage();
|
let usage = get_usage();
|
||||||
|
|
||||||
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||||
|
|
||||||
|
@ -150,7 +142,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
dest_uid = u;
|
dest_uid = u;
|
||||||
dest_gid = g;
|
dest_gid = g;
|
||||||
}
|
}
|
||||||
let executor = Chowner {
|
let executor = ChownExecutor {
|
||||||
bit_flag,
|
bit_flag,
|
||||||
dest_uid,
|
dest_uid,
|
||||||
dest_gid,
|
dest_gid,
|
||||||
|
@ -289,195 +281,6 @@ fn parse_spec(spec: &str) -> UResult<(Option<u32>, Option<u32>)> {
|
||||||
Ok((uid, gid))
|
Ok((uid, gid))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum IfFrom {
|
|
||||||
All,
|
|
||||||
User(u32),
|
|
||||||
Group(u32),
|
|
||||||
UserGroup(u32, u32),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Chowner {
|
|
||||||
pub dest_uid: Option<u32>,
|
|
||||||
pub dest_gid: Option<u32>,
|
|
||||||
pub bit_flag: u8,
|
|
||||||
pub verbosity: Verbosity,
|
|
||||||
pub filter: IfFrom,
|
|
||||||
pub files: Vec<String>,
|
|
||||||
pub recursive: bool,
|
|
||||||
pub preserve_root: bool,
|
|
||||||
pub dereference: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! unwrap {
|
|
||||||
($m:expr, $e:ident, $err:block) => {
|
|
||||||
match $m {
|
|
||||||
Ok(meta) => meta,
|
|
||||||
Err($e) => $err,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Chowner {
|
|
||||||
pub fn exec(&self) -> UResult<()> {
|
|
||||||
let mut ret = 0;
|
|
||||||
for f in &self.files {
|
|
||||||
ret |= self.traverse(f);
|
|
||||||
}
|
|
||||||
if ret != 0 {
|
|
||||||
return Err(ret.into());
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn traverse<P: AsRef<Path>>(&self, root: P) -> i32 {
|
|
||||||
let follow_arg = self.dereference || self.bit_flag != FTS_PHYSICAL;
|
|
||||||
let path = root.as_ref();
|
|
||||||
let meta = match self.obtain_meta(path, follow_arg) {
|
|
||||||
Some(m) => m,
|
|
||||||
_ => return 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Prohibit only if:
|
|
||||||
// (--preserve-root and -R present) &&
|
|
||||||
// (
|
|
||||||
// (argument is not symlink && resolved to be '/') ||
|
|
||||||
// (argument is symlink && should follow argument && resolved to be '/')
|
|
||||||
// )
|
|
||||||
if self.recursive && self.preserve_root {
|
|
||||||
let may_exist = if follow_arg {
|
|
||||||
path.canonicalize().ok()
|
|
||||||
} else {
|
|
||||||
let real = resolve_relative_path(path);
|
|
||||||
if real.is_dir() {
|
|
||||||
Some(real.canonicalize().expect("failed to get real path"))
|
|
||||||
} else {
|
|
||||||
Some(real.into_owned())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(p) = may_exist {
|
|
||||||
if p.parent().is_none() {
|
|
||||||
show_error!("it is dangerous to operate recursively on '/'");
|
|
||||||
show_error!("use --no-preserve-root to override this failsafe");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let ret = if self.matched(meta.uid(), meta.gid()) {
|
|
||||||
match wrap_chown(
|
|
||||||
path,
|
|
||||||
&meta,
|
|
||||||
self.dest_uid,
|
|
||||||
self.dest_gid,
|
|
||||||
follow_arg,
|
|
||||||
self.verbosity.clone(),
|
|
||||||
) {
|
|
||||||
Ok(n) => {
|
|
||||||
if !n.is_empty() {
|
|
||||||
show_error!("{}", n);
|
|
||||||
}
|
|
||||||
0
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
if self.verbosity.level != VerbosityLevel::Silent {
|
|
||||||
show_error!("{}", e);
|
|
||||||
}
|
|
||||||
1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
if !self.recursive {
|
|
||||||
ret
|
|
||||||
} else {
|
|
||||||
ret | self.dive_into(&root)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dive_into<P: AsRef<Path>>(&self, root: P) -> i32 {
|
|
||||||
let mut ret = 0;
|
|
||||||
let root = root.as_ref();
|
|
||||||
let follow = self.dereference || self.bit_flag & FTS_LOGICAL != 0;
|
|
||||||
for entry in WalkDir::new(root).follow_links(follow).min_depth(1) {
|
|
||||||
let entry = unwrap!(entry, e, {
|
|
||||||
ret = 1;
|
|
||||||
show_error!("{}", e);
|
|
||||||
continue;
|
|
||||||
});
|
|
||||||
let path = entry.path();
|
|
||||||
let meta = match self.obtain_meta(path, follow) {
|
|
||||||
Some(m) => m,
|
|
||||||
_ => {
|
|
||||||
ret = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if !self.matched(meta.uid(), meta.gid()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = match wrap_chown(
|
|
||||||
path,
|
|
||||||
&meta,
|
|
||||||
self.dest_uid,
|
|
||||||
self.dest_gid,
|
|
||||||
follow,
|
|
||||||
self.verbosity.clone(),
|
|
||||||
) {
|
|
||||||
Ok(n) => {
|
|
||||||
if !n.is_empty() {
|
|
||||||
show_error!("{}", n);
|
|
||||||
}
|
|
||||||
0
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
if self.verbosity.level != VerbosityLevel::Silent {
|
|
||||||
show_error!("{}", e);
|
|
||||||
}
|
|
||||||
1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
fn obtain_meta<P: AsRef<Path>>(&self, path: P, follow: bool) -> Option<Metadata> {
|
|
||||||
let path = path.as_ref();
|
|
||||||
let meta = if follow {
|
|
||||||
unwrap!(path.metadata(), e, {
|
|
||||||
match self.verbosity.level {
|
|
||||||
VerbosityLevel::Silent => (),
|
|
||||||
_ => show_error!("cannot access '{}': {}", path.display(), e),
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
unwrap!(path.symlink_metadata(), e, {
|
|
||||||
match self.verbosity.level {
|
|
||||||
VerbosityLevel::Silent => (),
|
|
||||||
_ => show_error!("cannot dereference '{}': {}", path.display(), e),
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
Some(meta)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn matched(&self, uid: uid_t, gid: gid_t) -> bool {
|
|
||||||
match self.filter {
|
|
||||||
IfFrom::All => true,
|
|
||||||
IfFrom::User(u) => u == uid,
|
|
||||||
IfFrom::Group(g) => g == gid,
|
|
||||||
IfFrom::UserGroup(u, g) => u == uid && g == gid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -26,6 +26,7 @@ lazy_static = { version="1.3", optional=true }
|
||||||
nix = { version="<= 0.19", optional=true }
|
nix = { version="<= 0.19", optional=true }
|
||||||
platform-info = { version="<= 0.1", optional=true }
|
platform-info = { version="<= 0.1", optional=true }
|
||||||
time = { version="<= 0.1.43", optional=true }
|
time = { version="<= 0.1.43", optional=true }
|
||||||
|
walkdir = { version="2.3.2", optional=true }
|
||||||
# * "problem" dependencies (pinned)
|
# * "problem" dependencies (pinned)
|
||||||
data-encoding = { version="2.1", optional=true }
|
data-encoding = { version="2.1", optional=true }
|
||||||
data-encoding-macro = { version="0.1.12", optional=true }
|
data-encoding-macro = { version="0.1.12", optional=true }
|
||||||
|
@ -50,7 +51,7 @@ entries = ["libc"]
|
||||||
fs = ["libc"]
|
fs = ["libc"]
|
||||||
fsext = ["libc", "time"]
|
fsext = ["libc", "time"]
|
||||||
mode = ["libc"]
|
mode = ["libc"]
|
||||||
perms = ["libc"]
|
perms = ["libc", "walkdir"]
|
||||||
process = ["libc"]
|
process = ["libc"]
|
||||||
ringbuffer = []
|
ringbuffer = []
|
||||||
signals = []
|
signals = []
|
||||||
|
|
|
@ -3,8 +3,12 @@
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
|
use crate::error::UResult;
|
||||||
pub use crate::features::entries;
|
pub use crate::features::entries;
|
||||||
|
use crate::fs::resolve_relative_path;
|
||||||
|
use crate::show_error;
|
||||||
use libc::{self, gid_t, lchown, uid_t};
|
use libc::{self, gid_t, lchown, uid_t};
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use std::io::Error as IOError;
|
use std::io::Error as IOError;
|
||||||
use std::io::Result as IOResult;
|
use std::io::Result as IOResult;
|
||||||
|
@ -146,3 +150,196 @@ pub fn wrap_chown<P: AsRef<Path>>(
|
||||||
}
|
}
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum IfFrom {
|
||||||
|
All,
|
||||||
|
User(u32),
|
||||||
|
Group(u32),
|
||||||
|
UserGroup(u32, u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ChownExecutor {
|
||||||
|
pub dest_uid: Option<u32>,
|
||||||
|
pub dest_gid: Option<u32>,
|
||||||
|
pub bit_flag: u8,
|
||||||
|
pub verbosity: Verbosity,
|
||||||
|
pub filter: IfFrom,
|
||||||
|
pub files: Vec<String>,
|
||||||
|
pub recursive: bool,
|
||||||
|
pub preserve_root: bool,
|
||||||
|
pub dereference: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! unwrap {
|
||||||
|
($m:expr, $e:ident, $err:block) => {
|
||||||
|
match $m {
|
||||||
|
Ok(meta) => meta,
|
||||||
|
Err($e) => $err,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const FTS_COMFOLLOW: u8 = 1;
|
||||||
|
pub const FTS_PHYSICAL: u8 = 1 << 1;
|
||||||
|
pub const FTS_LOGICAL: u8 = 1 << 2;
|
||||||
|
|
||||||
|
impl ChownExecutor {
|
||||||
|
pub fn exec(&self) -> UResult<()> {
|
||||||
|
let mut ret = 0;
|
||||||
|
for f in &self.files {
|
||||||
|
ret |= self.traverse(f);
|
||||||
|
}
|
||||||
|
if ret != 0 {
|
||||||
|
return Err(ret.into());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn traverse<P: AsRef<Path>>(&self, root: P) -> i32 {
|
||||||
|
let follow_arg = self.dereference || self.bit_flag != FTS_PHYSICAL;
|
||||||
|
let path = root.as_ref();
|
||||||
|
let meta = match self.obtain_meta(path, follow_arg) {
|
||||||
|
Some(m) => m,
|
||||||
|
_ => return 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prohibit only if:
|
||||||
|
// (--preserve-root and -R present) &&
|
||||||
|
// (
|
||||||
|
// (argument is not symlink && resolved to be '/') ||
|
||||||
|
// (argument is symlink && should follow argument && resolved to be '/')
|
||||||
|
// )
|
||||||
|
if self.recursive && self.preserve_root {
|
||||||
|
let may_exist = if follow_arg {
|
||||||
|
path.canonicalize().ok()
|
||||||
|
} else {
|
||||||
|
let real = resolve_relative_path(path);
|
||||||
|
if real.is_dir() {
|
||||||
|
Some(real.canonicalize().expect("failed to get real path"))
|
||||||
|
} else {
|
||||||
|
Some(real.into_owned())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(p) = may_exist {
|
||||||
|
if p.parent().is_none() {
|
||||||
|
show_error!("it is dangerous to operate recursively on '/'");
|
||||||
|
show_error!("use --no-preserve-root to override this failsafe");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret = if self.matched(meta.uid(), meta.gid()) {
|
||||||
|
match wrap_chown(
|
||||||
|
path,
|
||||||
|
&meta,
|
||||||
|
self.dest_uid,
|
||||||
|
self.dest_gid,
|
||||||
|
follow_arg,
|
||||||
|
self.verbosity.clone(),
|
||||||
|
) {
|
||||||
|
Ok(n) => {
|
||||||
|
if !n.is_empty() {
|
||||||
|
show_error!("{}", n);
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if self.verbosity.level != VerbosityLevel::Silent {
|
||||||
|
show_error!("{}", e);
|
||||||
|
}
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
if !self.recursive {
|
||||||
|
ret
|
||||||
|
} else {
|
||||||
|
ret | self.dive_into(&root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dive_into<P: AsRef<Path>>(&self, root: P) -> i32 {
|
||||||
|
let mut ret = 0;
|
||||||
|
let root = root.as_ref();
|
||||||
|
let follow = self.dereference || self.bit_flag & FTS_LOGICAL != 0;
|
||||||
|
for entry in WalkDir::new(root).follow_links(follow).min_depth(1) {
|
||||||
|
let entry = unwrap!(entry, e, {
|
||||||
|
ret = 1;
|
||||||
|
show_error!("{}", e);
|
||||||
|
continue;
|
||||||
|
});
|
||||||
|
let path = entry.path();
|
||||||
|
let meta = match self.obtain_meta(path, follow) {
|
||||||
|
Some(m) => m,
|
||||||
|
_ => {
|
||||||
|
ret = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !self.matched(meta.uid(), meta.gid()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = match wrap_chown(
|
||||||
|
path,
|
||||||
|
&meta,
|
||||||
|
self.dest_uid,
|
||||||
|
self.dest_gid,
|
||||||
|
follow,
|
||||||
|
self.verbosity.clone(),
|
||||||
|
) {
|
||||||
|
Ok(n) => {
|
||||||
|
if !n.is_empty() {
|
||||||
|
show_error!("{}", n);
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if self.verbosity.level != VerbosityLevel::Silent {
|
||||||
|
show_error!("{}", e);
|
||||||
|
}
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn obtain_meta<P: AsRef<Path>>(&self, path: P, follow: bool) -> Option<Metadata> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let meta = if follow {
|
||||||
|
unwrap!(path.metadata(), e, {
|
||||||
|
match self.verbosity.level {
|
||||||
|
VerbosityLevel::Silent => (),
|
||||||
|
_ => show_error!("cannot access '{}': {}", path.display(), e),
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
unwrap!(path.symlink_metadata(), e, {
|
||||||
|
match self.verbosity.level {
|
||||||
|
VerbosityLevel::Silent => (),
|
||||||
|
_ => show_error!("cannot dereference '{}': {}", path.display(), e),
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
})
|
||||||
|
};
|
||||||
|
Some(meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn matched(&self, uid: uid_t, gid: gid_t) -> bool {
|
||||||
|
match self.filter {
|
||||||
|
IfFrom::All => true,
|
||||||
|
IfFrom::User(u) => u == uid,
|
||||||
|
IfFrom::Group(g) => g == gid,
|
||||||
|
IfFrom::UserGroup(u, g) => u == uid && g == gid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue