diff --git a/Cargo.lock b/Cargo.lock index 8807634..4beadb6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,12 +13,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - [[package]] name = "bitflags" version = "1.3.2" @@ -31,12 +25,6 @@ version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - [[package]] name = "clap" version = "4.1.1" @@ -88,61 +76,12 @@ dependencies = [ "roff", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" -dependencies = [ - "cfg-if", -] - [[package]] name = "data-encoding" version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" -[[package]] -name = "either" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" - [[package]] name = "errno" version = "0.2.8" @@ -233,15 +172,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" -[[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] - [[package]] name = "num_cpus" version = "1.15.0" @@ -258,28 +188,6 @@ version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" -[[package]] -name = "rayon" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - [[package]] name = "roff" version = "0.2.1" @@ -309,12 +217,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - [[package]] name = "shitbox" version = "0.1.0" @@ -328,7 +230,6 @@ dependencies = [ "hostname", "libc", "num_cpus", - "rayon", "termcolor", "textwrap", "walkdir", diff --git a/Cargo.toml b/Cargo.toml index 5ef0ee9..4f592a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,6 @@ data-encoding = "2.3" hostname = { version = "0.3", features = ["set"] } libc = "0.2" num_cpus = "1.15" -rayon = "1.6.1" termcolor = "1.1" textwrap = { version = "0.16", default-features = false, features = ["smawk"] } walkdir = "2.3.2" diff --git a/src/cmd/chown/mod.rs b/src/cmd/chown/mod.rs index 025e3ee..798ef89 100644 --- a/src/cmd/chown/mod.rs +++ b/src/cmd/chown/mod.rs @@ -1,9 +1,9 @@ use super::Cmd; -use crate::{fs::FileType, pw}; -use clap::{Arg, ArgAction, ArgGroup, Command, ValueHint}; +use crate::pw; +use clap::{Arg, ArgAction, ArgGroup, ArgMatches, Command, ValueHint}; use std::{ error::Error, - fs::{self, File}, + fs::File, io, os::{fd::AsRawFd, unix::prelude::MetadataExt}, path::PathBuf, @@ -40,7 +40,6 @@ impl Cmd for Chown { .help("operate on files and directories recursively") .short('R') .long("recursive") - .requires("links") .action(ArgAction::SetTrue), Arg::new("cli-traverse") .help( @@ -80,59 +79,19 @@ impl Cmd for Chown { let Some(matches) = matches else { return Err(Box::new(io::Error::new(io::ErrorKind::Other, "no input"))); }; - let recurse = if matches.get_flag("recursive") { - if matches.get_flag("full-traverse") { - Some(Recurse { - traversal: Traversal::FullLinks, - same_filesystem: matches.get_flag("same-filesystem"), - }) - } else if matches.get_flag("cli-traverse") { - Some(Recurse { - traversal: Traversal::CliLinks, - same_filesystem: matches.get_flag("same-filesystem"), - }) - } else { - Some(Recurse { - traversal: Traversal::NoLinks, - same_filesystem: matches.get_flag("same-filesystem"), - }) - } - } else { - None - }; - let feedback = if matches.get_flag("verbose") { - Some(Feedback::Full) - } else if matches.get_flag("changes") { - Some(Feedback::Changes) - } else { - None - }; + let recurse = Recurse::from_matches(matches); + let feedback = Feedback::from_matches(matches); let (user, group) = if let Some(who) = matches.get_one::("user") { if let Some((u, g)) = who.split_once(':') { let uid = pw::get_uid_for_name(u) .ok_or(io::Error::new(io::ErrorKind::Other, "cannot get uid"))?; let gid = pw::get_gid_for_groupname(g) .ok_or(io::Error::new(io::ErrorKind::Other, "cannot get gid"))?; - ( - User { - name: u.to_string(), - uid, - }, - Some(Group { - name: g.to_string(), - gid, - }), - ) + (User { name: u, uid }, Some(Group { name: g, gid })) } else { let uid = pw::get_uid_for_name(who) .ok_or(io::Error::new(io::ErrorKind::Other, "cannot get uid"))?; - ( - User { - name: who.to_string(), - uid, - }, - None, - ) + (User { name: who, uid }, None) } } else { return Err(Box::new(io::Error::new( @@ -146,9 +105,17 @@ impl Cmd for Chown { path: PathBuf::from(f), user: user.clone(), group: group.clone(), - recurse, feedback, }; + if let Some(r) = recurse { + if action.path.is_dir() { + action.recurse(recurse)?; + } else if action.path.is_symlink() { + if r.traversal != Traversal::NoLinks { + action.recurse(recurse)?; + } + } + } action.apply()?; } } @@ -168,10 +135,13 @@ enum Traversal { } impl Traversal { - fn increment(&self) -> Self { - match self { - Self::CliLinks => Self::NoLinks, - _ => *self, + fn from_matches(matches: &ArgMatches) -> Self { + if matches.get_flag("full-traverse") { + Traversal::FullLinks + } else if matches.get_flag("cli-traverse") { + Self::CliLinks + } else { + Self::NoLinks } } } @@ -183,10 +153,14 @@ struct Recurse { } impl Recurse { - fn increment(&self) -> Self { - Self { - traversal: self.traversal.increment(), - same_filesystem: self.same_filesystem, + fn from_matches(matches: &ArgMatches) -> Option { + if matches.get_flag("recursive") { + Some(Self { + traversal: Traversal::from_matches(matches), + same_filesystem: matches.get_flag("same-filesystem"), + }) + } else { + None } } } @@ -197,28 +171,39 @@ enum Feedback { Changes, } +impl Feedback { + fn from_matches(matches: &ArgMatches) -> Option { + if matches.get_flag("verbose") { + Some(Feedback::Full) + } else if matches.get_flag("changes") { + Some(Feedback::Changes) + } else { + None + } + } +} + #[derive(Clone, Debug)] -struct User { - name: String, +struct User<'a> { + name: &'a str, uid: u32, } #[derive(Clone, Debug)] -struct Group { - name: String, +struct Group<'a> { + name: &'a str, gid: u32, } #[derive(Debug)] -struct Action { +struct Action<'a> { path: PathBuf, - user: User, - group: Option, - recurse: Option, + user: User<'a>, + group: Option>, feedback: Option, } -impl Action { +impl Action<'_> { fn apply(&self) -> Result<(), Box> { let fd = File::open(&self.path)?; let meta = fd.metadata()?; @@ -234,66 +219,19 @@ impl Action { return Err(io::Error::last_os_error().into()); } } - let ft = FileType::from(meta); - match ft { - FileType::File => {} - FileType::Symlink => { - let tgt = fs::read_link(&self.path)?; - if tgt.is_dir() { - if let Some(r) = self.recurse { - if r.traversal != Traversal::NoLinks { - self.recurse()?; - } - } - } - } - FileType::Dir => { - if let Some(r) = self.recurse { - if r.traversal != Traversal::NoLinks { - self.recurse()?; - } - } - } - } + drop(fd); if let Some(feedback) = self.feedback { match feedback { Feedback::Full => { if self.user.uid != uid || self.group.as_ref().map(|x| x.gid) != Some(gid) { - if let Some(g) = &self.group { - println!( - "{} changed to {}:{}", - &self.path.display(), - &self.user.name, - &g.name - ); - } else { - println!("{} changed to {}", &self.path.display(), &self.user.name); - } + self.display_changes(uid, gid)?; } else { - if let Some(g) = &self.group { - println!( - "{} retained as {}:{}", - &self.path.display(), - &self.user.name, - &g.name - ); - } else { - println!("{} retained as {}", &self.path.display(), &self.user.name); - } + self.display_retained(); } } Feedback::Changes => { if self.user.uid != uid || self.group.as_ref().map(|x| x.gid) != Some(gid) { - if let Some(g) = &self.group { - println!( - "{} changed to {}:{}", - self.path.display(), - &self.user.name, - &g.name - ); - } else { - println!("{}, changed to {}", self.path.display(), &self.user.name); - } + self.display_changes(uid, gid)?; } } } @@ -301,28 +239,56 @@ impl Action { Ok(()) } + fn display_changes(&self, uid: u32, gid: u32) -> Result<(), std::str::Utf8Error> { + let username = pw::get_username_for_uid(uid)?; + let groupname = pw::get_groupname_for_gid(gid)?; + if let Some(g) = &self.group { + println!( + "{} changed from {username}:{groupname} to {}:{}", + &self.path.display(), + &self.user.name, + &g.name + ); + } else { + println!( + "{} changed from {username} to {}", + &self.path.display(), + &self.user.name + ); + } + Ok(()) + } + + fn display_retained(&self) { + if let Some(g) = &self.group { + println!( + "{} retained as {}:{}", + &self.path.display(), + &self.user.name, + &g.name + ); + } else { + println!("{} retained as {}", &self.path.display(), &self.user.name); + } + } + fn into_child(&self, entry: DirEntry) -> Result> { let path = entry.path().to_path_buf(); - let recurse = if let Some(r) = self.recurse { - Some(r.increment()) - } else { - None - }; Ok(Self { path, user: self.user.clone(), group: self.group.clone(), - recurse, feedback: self.feedback, }) } - fn recurse(&self) -> Result<(), Box> { + fn recurse(&self, recurse: Option) -> Result<(), Box> { let walker = WalkDir::new(&self.path) - .same_file_system(self.recurse.map_or(false, |x| !x.same_filesystem)) - .follow_links(self.recurse.map_or(false, |x| match x.traversal { - Traversal::NoLinks | Traversal::CliLinks => false, - _ => true, + .max_open(1) + .same_file_system(recurse.map_or(false, |x| x.same_filesystem)) + .follow_links(recurse.map_or(false, |x| match x.traversal { + Traversal::FullLinks => true, + _ => false, })); for entry in walker { let entry = entry?;