mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
refactor(chmod): move from walker to walkdir, simplify the code and add tests (#1645)
This commit is contained in:
parent
49b32ea68d
commit
576aa29f0f
4 changed files with 96 additions and 59 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -471,6 +471,7 @@ version = "0.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"const_fn 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"const_fn 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"const_fn 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crossbeam-utils 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossbeam-utils 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -1435,7 +1436,7 @@ dependencies = [
|
||||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"uucore 0.0.4",
|
"uucore 0.0.4",
|
||||||
"uucore_procs 0.0.4",
|
"uucore_procs 0.0.4",
|
||||||
"walker 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2381,11 +2382,6 @@ dependencies = [
|
||||||
"winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "walker"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.9.0+wasi-snapshot-preview1"
|
version = "0.9.0+wasi-snapshot-preview1"
|
||||||
|
@ -2664,7 +2660,6 @@ dependencies = [
|
||||||
"checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
"checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||||
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||||
"checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
|
"checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
|
||||||
"checksum walker 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44971d5e5ae4f7904dffb6260ebd3910e7bcae104a94730e04a24cb6af40646b"
|
|
||||||
"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||||
"checksum wasm-bindgen 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e"
|
"checksum wasm-bindgen 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e"
|
||||||
"checksum wasm-bindgen-backend 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62"
|
"checksum wasm-bindgen-backend 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62"
|
||||||
|
|
|
@ -18,7 +18,7 @@ path = "src/chmod.rs"
|
||||||
libc = "0.2.42"
|
libc = "0.2.42"
|
||||||
uucore = { version=">=0.0.4", package="uucore", path="../../uucore", features=["fs", "mode"] }
|
uucore = { version=">=0.0.4", package="uucore", path="../../uucore", features=["fs", "mode"] }
|
||||||
uucore_procs = { version=">=0.0.4", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.4", package="uucore_procs", path="../../uucore_procs" }
|
||||||
walker = "1.0.0"
|
walkdir = "2.2"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "chmod"
|
name = "chmod"
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
extern crate walker;
|
extern crate walkdir;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
|
@ -20,7 +20,7 @@ use std::path::Path;
|
||||||
use uucore::fs::display_permissions_unix;
|
use uucore::fs::display_permissions_unix;
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
use uucore::mode;
|
use uucore::mode;
|
||||||
use walker::Walker;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
const NAME: &str = "chmod";
|
const NAME: &str = "chmod";
|
||||||
static SUMMARY: &str = "Change the mode of each FILE to MODE.
|
static SUMMARY: &str = "Change the mode of each FILE to MODE.
|
||||||
|
@ -150,62 +150,39 @@ impl Chmoder {
|
||||||
for filename in &files {
|
for filename in &files {
|
||||||
let filename = &filename[..];
|
let filename = &filename[..];
|
||||||
let file = Path::new(filename);
|
let file = Path::new(filename);
|
||||||
if file.exists() {
|
if !file.exists() {
|
||||||
if file.is_dir() {
|
|
||||||
if !self.preserve_root || filename != "/" {
|
|
||||||
if self.recursive {
|
|
||||||
let walk_dir = match Walker::new(&file) {
|
|
||||||
Ok(m) => m,
|
|
||||||
Err(f) => {
|
|
||||||
crash!(1, "{}", f.to_string());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// XXX: here (and elsewhere) we see that this impl will have issues
|
|
||||||
// with non-UTF-8 filenames. Using OsString won't fix this because
|
|
||||||
// on Windows OsStrings cannot be built out of non-UTF-8 chars. One
|
|
||||||
// possible fix is to use CStrings rather than Strings in the args
|
|
||||||
// to chmod() and chmod_file().
|
|
||||||
r = self
|
|
||||||
.chmod(
|
|
||||||
walk_dir
|
|
||||||
.filter_map(|x| match x {
|
|
||||||
Ok(o) => match o.path().into_os_string().to_str() {
|
|
||||||
Some(s) => Some(s.to_owned()),
|
|
||||||
None => None,
|
|
||||||
},
|
|
||||||
Err(_) => None,
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
.and(r);
|
|
||||||
r = self.chmod_file(&file, filename).and(r);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
show_error!("could not change permissions of directory '{}'", filename);
|
|
||||||
r = Err(1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
r = self.chmod_file(&file, filename).and(r);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
show_error!("no such file or directory '{}'", filename);
|
show_error!("no such file or directory '{}'", filename);
|
||||||
r = Err(1);
|
return Err(1);
|
||||||
|
}
|
||||||
|
if self.recursive && self.preserve_root && filename == "/" {
|
||||||
|
show_error!(
|
||||||
|
"it is dangerous to operate recursively on '{}'\nuse --no-preserve-root to override this failsafe",
|
||||||
|
filename
|
||||||
|
);
|
||||||
|
return Err(1);
|
||||||
|
}
|
||||||
|
if !self.recursive {
|
||||||
|
r = self.chmod_file(&file).and(r);
|
||||||
|
} else {
|
||||||
|
for entry in WalkDir::new(&filename).into_iter().filter_map(|e| e.ok()) {
|
||||||
|
let file = entry.path();
|
||||||
|
r = self.chmod_file(&file).and(r);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn chmod_file(&self, file: &Path, name: &str) -> Result<(), i32> {
|
fn chmod_file(&self, file: &Path) -> Result<(), i32> {
|
||||||
// chmod is useless on Windows
|
// chmod is useless on Windows
|
||||||
// it doesn't set any permissions at all
|
// it doesn't set any permissions at all
|
||||||
// instead it just sets the readonly attribute on the file
|
// instead it just sets the readonly attribute on the file
|
||||||
Err(0)
|
Err(0)
|
||||||
}
|
}
|
||||||
#[cfg(any(unix, target_os = "redox"))]
|
#[cfg(any(unix, target_os = "redox"))]
|
||||||
fn chmod_file(&self, file: &Path, name: &str) -> Result<(), i32> {
|
fn chmod_file(&self, file: &Path) -> Result<(), i32> {
|
||||||
let mut fperm = match fs::metadata(name) {
|
let mut fperm = match fs::metadata(file) {
|
||||||
Ok(meta) => meta.mode() & 0o7777,
|
Ok(meta) => meta.mode() & 0o7777,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if !self.quiet {
|
if !self.quiet {
|
||||||
|
@ -215,7 +192,7 @@ impl Chmoder {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match self.fmode {
|
match self.fmode {
|
||||||
Some(mode) => self.change_file(fperm, mode, file, name)?,
|
Some(mode) => self.change_file(fperm, mode, file)?,
|
||||||
None => {
|
None => {
|
||||||
let cmode_unwrapped = self.cmode.clone().unwrap();
|
let cmode_unwrapped = self.cmode.clone().unwrap();
|
||||||
for mode in cmode_unwrapped.split(',') {
|
for mode in cmode_unwrapped.split(',') {
|
||||||
|
@ -228,7 +205,7 @@ impl Chmoder {
|
||||||
};
|
};
|
||||||
match result {
|
match result {
|
||||||
Ok(mode) => {
|
Ok(mode) => {
|
||||||
self.change_file(fperm, mode, file, name)?;
|
self.change_file(fperm, mode, file)?;
|
||||||
fperm = mode;
|
fperm = mode;
|
||||||
}
|
}
|
||||||
Err(f) => {
|
Err(f) => {
|
||||||
|
@ -246,7 +223,7 @@ impl Chmoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn change_file(&self, fperm: u32, mode: u32, file: &Path, path: &str) -> Result<(), i32> {
|
fn change_file(&self, fperm: u32, mode: u32, file: &Path) -> Result<(), i32> {
|
||||||
if fperm == mode {
|
if fperm == mode {
|
||||||
if self.verbose && !self.changes {
|
if self.verbose && !self.changes {
|
||||||
show_info!(
|
show_info!(
|
||||||
|
@ -257,9 +234,7 @@ impl Chmoder {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
} else if let Err(err) =
|
} else if let Err(err) = fs::set_permissions(file, fs::Permissions::from_mode(mode)) {
|
||||||
fs::set_permissions(Path::new(path), fs::Permissions::from_mode(mode))
|
|
||||||
{
|
|
||||||
if !self.quiet {
|
if !self.quiet {
|
||||||
show_error!("{}", err);
|
show_error!("{}", err);
|
||||||
}
|
}
|
||||||
|
|
|
@ -279,3 +279,70 @@ fn test_chmod_reference_file() {
|
||||||
mkfile(&at.plus_as_string(REFERENCE_FILE), REFERENCE_PERMS);
|
mkfile(&at.plus_as_string(REFERENCE_FILE), REFERENCE_PERMS);
|
||||||
run_single_test(&tests[0], at, ucmd);
|
run_single_test(&tests[0], at, ucmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_chmod_recursive() {
|
||||||
|
let _guard = UMASK_MUTEX.lock();
|
||||||
|
|
||||||
|
let original_umask = unsafe { umask(0) };
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
at.mkdir("a");
|
||||||
|
at.mkdir("a/b");
|
||||||
|
at.mkdir("a/b/c");
|
||||||
|
at.mkdir("z");
|
||||||
|
mkfile(&at.plus_as_string("a/a"), 0o100444);
|
||||||
|
mkfile(&at.plus_as_string("a/b/b"), 0o100444);
|
||||||
|
mkfile(&at.plus_as_string("a/b/c/c"), 0o100444);
|
||||||
|
mkfile(&at.plus_as_string("z/y"), 0o100444);
|
||||||
|
|
||||||
|
let result = ucmd
|
||||||
|
.arg("-R")
|
||||||
|
.arg("--verbose")
|
||||||
|
.arg("-r,a+w")
|
||||||
|
.arg("a")
|
||||||
|
.arg("z")
|
||||||
|
.succeeds();
|
||||||
|
|
||||||
|
assert_eq!(at.metadata("z/y").permissions().mode(), 0o100222);
|
||||||
|
assert_eq!(at.metadata("a/a").permissions().mode(), 0o100222);
|
||||||
|
assert_eq!(at.metadata("a/b/b").permissions().mode(), 0o100222);
|
||||||
|
assert_eq!(at.metadata("a/b/c/c").permissions().mode(), 0o100222);
|
||||||
|
println!("mode {:o}", at.metadata("a").permissions().mode());
|
||||||
|
assert_eq!(at.metadata("a").permissions().mode(), 0o40333);
|
||||||
|
assert_eq!(at.metadata("z").permissions().mode(), 0o40333);
|
||||||
|
assert!(result.stderr.contains("to 333 (-wx-wx-wx)"));
|
||||||
|
assert!(result.stderr.contains("to 222 (-w--w--w-)"));
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
umask(original_umask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_chmod_non_existing_file() {
|
||||||
|
let (_at, mut ucmd) = at_and_ucmd!();
|
||||||
|
let result = ucmd
|
||||||
|
.arg("-R")
|
||||||
|
.arg("--verbose")
|
||||||
|
.arg("-r,a+w")
|
||||||
|
.arg("dont-exist")
|
||||||
|
.fails();
|
||||||
|
assert_eq!(
|
||||||
|
result.stderr,
|
||||||
|
"chmod: error: no such file or directory 'dont-exist'\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_chmod_preserve_root() {
|
||||||
|
let (_at, mut ucmd) = at_and_ucmd!();
|
||||||
|
let result = ucmd
|
||||||
|
.arg("-R")
|
||||||
|
.arg("--preserve-root")
|
||||||
|
.arg("755")
|
||||||
|
.arg("/")
|
||||||
|
.fails();
|
||||||
|
assert!(result
|
||||||
|
.stderr
|
||||||
|
.contains("chmod: error: it is dangerous to operate recursively on '/'"));
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue