diff --git a/Cargo.lock b/Cargo.lock index 20328c7..f605640 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -137,6 +137,7 @@ dependencies = [ "regex 1.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "users 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -410,6 +411,15 @@ name = "unicode-width" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "users" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "vec_map" version = "0.8.1" @@ -522,6 +532,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" "checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" +"checksum users 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aa4227e95324a443c9fcb06e03d4d85e91aabe9a5a02aa818688b6918b6af486" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" diff --git a/Cargo.toml b/Cargo.toml index 2cc0819..7ac461a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,9 @@ anyhow = "1.0" version = "2.31.2" features = ["suggestions", "color", "wrap_help"] +[target.'cfg(unix)'.dependencies] +users = "0.10.0" + [target.'cfg(all(unix, not(target_os = "redox")))'.dependencies] libc = "0.2" diff --git a/src/filter/owner.rs b/src/filter/owner.rs index a4da9b6..5b7f64a 100644 --- a/src/filter/owner.rs +++ b/src/filter/owner.rs @@ -1,4 +1,5 @@ use anyhow::{anyhow, Result}; +use std::fs; #[derive(Clone, Copy, Debug, PartialEq)] pub struct OwnerFilter { @@ -13,11 +14,29 @@ impl OwnerFilter { let uid = match fst { Some("") | None => None, - Some(s) => Some(s.parse()?), + Some(s) => { + let maybe_uid = s + .parse() + .ok() + .or_else(|| users::get_user_by_name(s).map(|user| user.uid())); + match maybe_uid { + Some(uid) => Some(uid), + _ => return Err(anyhow!("'{}' is not a recognized user name", s)), + } + } }; let gid = match snd { Some("") | None => None, - Some(s) => Some(s.parse()?), + Some(s) => { + let maybe_gid = s + .parse() + .ok() + .or_else(|| users::get_group_by_name(s).map(|group| group.gid())); + match maybe_gid { + Some(gid) => Some(gid), + _ => return Err(anyhow!("'{}' is not a recognized group name", s)), + } + } }; if uid.is_none() && gid.is_none() { @@ -29,6 +48,15 @@ impl OwnerFilter { Ok(OwnerFilter { uid, gid }) } } + + pub fn matches(&self, md: &fs::Metadata) -> bool { + use std::os::unix::fs::MetadataExt; + + let uid_ok = self.uid.map(|u| u == md.uid()).unwrap_or(true); + let gid_ok = self.gid.map(|g| g == md.gid()).unwrap_or(true); + + uid_ok && gid_ok + } } #[cfg(test)] diff --git a/src/walk.rs b/src/walk.rs index 93f20de..56c0bc0 100644 --- a/src/walk.rs +++ b/src/walk.rs @@ -411,6 +411,19 @@ fn spawn_senders( } } + #[cfg(unix)] + { + if let Some(ref owner_constraint) = config.owner_constraint { + if let Ok(ref metadata) = entry_path.metadata() { + if !owner_constraint.matches(&metadata) { + return ignore::WalkState::Continue; + } + } else { + return ignore::WalkState::Continue; + } + } + } + // Filter out unwanted sizes if it is a file and we have been given size constraints. if !config.size_constraints.is_empty() { if entry_path.is_file() {