Skip to content

Commit 0d997aa

Browse files
authored
Merge pull request #411 from JRF63/ftw-chown
chown implementation
2 parents bd6aafa + a1b0ce0 commit 0d997aa

File tree

7 files changed

+1762
-1021
lines changed

7 files changed

+1762
-1021
lines changed

tree/chgrp.rs

Lines changed: 38 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -9,37 +9,17 @@
99

1010
mod common;
1111

12-
use self::common::error_string;
12+
use self::common::{chown_traverse, error_string, ChangeOwnershipArgs};
1313
use clap::Parser;
1414
use gettextrs::{bind_textdomain_codeset, gettext, setlocale, textdomain, LocaleCategory};
15-
use std::{cell::RefCell, ffi::CString, io, os::unix::fs::MetadataExt};
15+
use std::{ffi::CString, io};
1616

1717
/// chgrp - change file group ownership
1818
#[derive(Parser)]
1919
#[command(version, about, disable_help_flag = true)]
2020
struct Args {
21-
#[arg(long, action = clap::ArgAction::HelpLong)] // Bec. help clashes with -h
22-
help: Option<bool>,
23-
24-
/// Change symbolic links, rather than the files they point to
25-
#[arg(short = 'h', long, default_value_t = false)]
26-
no_derereference: bool,
27-
28-
/// Follow command line symlinks during -R recursion
29-
#[arg(short = 'H', overrides_with_all = ["follow_cli", "follow_symlinks", "follow_none"])]
30-
follow_cli: bool,
31-
32-
/// Follow symlinks during -R recursion
33-
#[arg(short = 'L', overrides_with_all = ["follow_cli", "follow_symlinks", "follow_none"])]
34-
follow_symlinks: bool,
35-
36-
/// Never follow symlinks during -R recursion
37-
#[arg(short = 'P', overrides_with_all = ["follow_cli", "follow_symlinks", "follow_none"], default_value_t = true)]
38-
follow_none: bool,
39-
40-
/// Recursively change groups of directories and their contents
41-
#[arg(short, short_alias = 'R', long)]
42-
recurse: bool,
21+
#[command(flatten)]
22+
delegate: ChangeOwnershipArgs,
4323

4424
/// A group name from the group database or a numeric group ID
4525
group: String,
@@ -48,89 +28,6 @@ struct Args {
4828
files: Vec<String>,
4929
}
5030

51-
fn chgrp_file(filename: &str, gid: Option<u32>, args: &Args) -> bool {
52-
let recurse = args.recurse;
53-
let no_derereference = args.no_derereference;
54-
55-
let terminate = RefCell::new(false);
56-
57-
ftw::traverse_directory(
58-
filename,
59-
|entry| {
60-
if *terminate.borrow() {
61-
return Ok(false);
62-
}
63-
64-
let md = entry.metadata().unwrap();
65-
66-
// According to the spec:
67-
// "The user ID of the file shall be used as the owner argument."
68-
let uid = md.uid();
69-
70-
// Don't change the group ID if the group argument is empty
71-
let gid = gid.unwrap_or(libc::gid_t::MAX);
72-
73-
let ret = unsafe {
74-
libc::fchownat(
75-
entry.dir_fd(),
76-
entry.file_name().as_ptr(),
77-
uid,
78-
gid,
79-
// Default is to change the file that the symbolic link points to unless the
80-
// -h flag is specified.
81-
if no_derereference {
82-
libc::AT_SYMLINK_NOFOLLOW
83-
} else {
84-
0
85-
},
86-
)
87-
};
88-
if ret != 0 {
89-
let e = io::Error::last_os_error();
90-
let err_str = match e.kind() {
91-
io::ErrorKind::PermissionDenied => {
92-
gettext!("cannot access '{}': {}", entry.path(), error_string(&e))
93-
}
94-
_ => {
95-
gettext!("changing group of '{}': {}", entry.path(), error_string(&e))
96-
}
97-
};
98-
eprintln!("chgrp: {}", err_str);
99-
*terminate.borrow_mut() = true;
100-
return Err(());
101-
}
102-
103-
Ok(recurse)
104-
},
105-
|_| Ok(()), // Do nothing on `postprocess_dir`
106-
|entry, error| {
107-
let e = error.inner();
108-
let err_str = match e.kind() {
109-
io::ErrorKind::PermissionDenied => {
110-
gettext!(
111-
"cannot read directory '{}': {}",
112-
entry.path(),
113-
error_string(&e)
114-
)
115-
}
116-
_ => {
117-
gettext!("changing group of '{}': {}", entry.path(), error_string(&e))
118-
}
119-
};
120-
eprintln!("chgrp: {}", err_str);
121-
*terminate.borrow_mut() = true;
122-
},
123-
ftw::TraverseDirectoryOpts {
124-
follow_symlinks_on_args: args.follow_cli,
125-
follow_symlinks: args.follow_symlinks,
126-
..Default::default()
127-
},
128-
);
129-
130-
let failed = *terminate.borrow();
131-
!failed
132-
}
133-
13431
// lookup string group by name, or parse numeric group ID
13532
fn parse_group(group: &str) -> Result<Option<u32>, String> {
13633
// empty strings are accepted without errors
@@ -155,13 +52,37 @@ fn parse_group(group: &str) -> Result<Option<u32>, String> {
15552
}
15653
}
15754

55+
fn err_handler(e: io::Error, path: ftw::DisplayablePath) {
56+
let err_str = match e.kind() {
57+
io::ErrorKind::PermissionDenied => {
58+
gettext!("cannot read directory '{}': {}", path, error_string(&e))
59+
}
60+
_ => {
61+
gettext!("changing group of '{}': {}", path, error_string(&e))
62+
}
63+
};
64+
eprintln!("chgrp: {}", err_str);
65+
}
66+
67+
fn chown_err_handler(e: io::Error, path: ftw::DisplayablePath) {
68+
let err_str = match e.kind() {
69+
io::ErrorKind::PermissionDenied => {
70+
gettext!("cannot access '{}': {}", path, error_string(&e))
71+
}
72+
_ => {
73+
gettext!("changing group of '{}': {}", path, error_string(&e))
74+
}
75+
};
76+
eprintln!("chgrp: {}", err_str);
77+
}
78+
15879
fn main() -> Result<(), Box<dyn std::error::Error>> {
15980
// parse command line arguments
16081
let mut args = Args::parse();
16182

16283
// Enable `no_derereference` if `-R` is enabled without either `-H` or `-L`
163-
if args.recurse && !(args.follow_cli || args.follow_symlinks) {
164-
args.no_derereference = true;
84+
if args.delegate.recurse && !(args.delegate.follow_cli || args.delegate.follow_symlinks) {
85+
args.delegate.no_dereference = true;
16586
}
16687

16788
// initialize translations
@@ -182,7 +103,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
182103

183104
// apply the group to each file
184105
for filename in &args.files {
185-
let success = chgrp_file(filename, gid, &args);
106+
let success = chown_traverse(
107+
filename,
108+
None,
109+
gid,
110+
&args.delegate,
111+
err_handler,
112+
chown_err_handler,
113+
);
186114
if !success {
187115
exit_code = 1;
188116
}

0 commit comments

Comments
 (0)