Cleanup chown
applet
This commit is contained in:
parent
7fc064a441
commit
e648a8a83a
99
Cargo.lock
generated
99
Cargo.lock
generated
@ -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",
|
||||
|
@ -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"
|
||||
|
@ -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::<String>("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<Self> {
|
||||
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<Self> {
|
||||
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<Group>,
|
||||
recurse: Option<Recurse>,
|
||||
user: User<'a>,
|
||||
group: Option<Group<'a>>,
|
||||
feedback: Option<Feedback>,
|
||||
}
|
||||
|
||||
impl Action {
|
||||
impl Action<'_> {
|
||||
fn apply(&self) -> Result<(), Box<dyn Error>> {
|
||||
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<Self, Box<dyn Error>> {
|
||||
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<dyn Error>> {
|
||||
fn recurse(&self, recurse: Option<Recurse>) -> Result<(), Box<dyn Error>> {
|
||||
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?;
|
||||
|
Loading…
Reference in New Issue
Block a user