1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 03:27:44 +00:00

uucore/perms: correct some error messages

- prevent duplicate errors from both us and `walkdir` by instructing `walkdir'
  to skip directories we failed to read metadata for.
- don't directly display `walkdir`'s errors, but format them ourselves to
  match gnu's format
This commit is contained in:
Michael Debertol 2021-09-01 17:34:40 +02:00
parent a517671d55
commit 18fc4076cf
2 changed files with 80 additions and 29 deletions

View file

@ -5,6 +5,7 @@
//! Common functions to manage permissions //! Common functions to manage permissions
use crate::error::strip_errno;
use crate::error::UResult; use crate::error::UResult;
pub use crate::features::entries; pub use crate::features::entries;
use crate::fs::resolve_relative_path; use crate::fs::resolve_relative_path;
@ -172,15 +173,6 @@ pub struct ChownExecutor {
pub dereference: bool, pub dereference: bool,
} }
macro_rules! unwrap {
($m:expr, $e:ident, $err:block) => {
match $m {
Ok(meta) => meta,
Err($e) => $err,
}
};
}
pub const FTS_COMFOLLOW: u8 = 1; pub const FTS_COMFOLLOW: u8 = 1;
pub const FTS_PHYSICAL: u8 = 1 << 1; pub const FTS_PHYSICAL: u8 = 1 << 1;
pub const FTS_LOGICAL: u8 = 1 << 2; pub const FTS_LOGICAL: u8 = 1 << 2;
@ -269,17 +261,42 @@ impl ChownExecutor {
let mut ret = 0; let mut ret = 0;
let root = root.as_ref(); let root = root.as_ref();
let follow = self.dereference || self.bit_flag & FTS_LOGICAL != 0; let follow = self.dereference || self.bit_flag & FTS_LOGICAL != 0;
for entry in WalkDir::new(root).follow_links(follow).min_depth(1) { let mut iterator = WalkDir::new(root)
let entry = unwrap!(entry, e, { .follow_links(follow)
ret = 1; .min_depth(1)
show_error!("{}", e); .into_iter();
continue; // We can't use a for loop because we need to manipulate the iterator inside the loop.
}); while let Some(entry) = iterator.next() {
let entry = match entry {
Err(e) => {
ret = 1;
if let Some(path) = e.path() {
show_error!(
"cannot access '{}': {}",
path.display(),
if let Some(error) = e.io_error() {
strip_errno(error)
} else {
"Too many levels of symbolic links".into()
}
)
} else {
show_error!("{}", e)
}
continue;
}
Ok(entry) => entry,
};
let path = entry.path(); let path = entry.path();
let meta = match self.obtain_meta(path, follow) { let meta = match self.obtain_meta(path, follow) {
Some(m) => m, Some(m) => m,
_ => { _ => {
ret = 1; ret = 1;
if entry.file_type().is_dir() {
// Instruct walkdir to skip this directory to avoid getting another error
// when walkdir tries to query the children of this directory.
iterator.skip_current_dir();
}
continue; continue;
} }
}; };
@ -316,23 +333,20 @@ impl ChownExecutor {
fn obtain_meta<P: AsRef<Path>>(&self, path: P, follow: bool) -> Option<Metadata> { fn obtain_meta<P: AsRef<Path>>(&self, path: P, follow: bool) -> Option<Metadata> {
let path = path.as_ref(); let path = path.as_ref();
let meta = if follow { let meta = if follow {
unwrap!(path.metadata(), e, { path.metadata()
match self.verbosity.level {
VerbosityLevel::Silent => (),
_ => show_error!("cannot access '{}': {}", path.display(), e),
}
return None;
})
} else { } else {
unwrap!(path.symlink_metadata(), e, { path.symlink_metadata()
};
match meta {
Err(e) => {
match self.verbosity.level { match self.verbosity.level {
VerbosityLevel::Silent => (), VerbosityLevel::Silent => (),
_ => show_error!("cannot dereference '{}': {}", path.display(), e), _ => show_error!("cannot access '{}': {}", path.display(), strip_errno(&e)),
} }
return None; None
}) }
}; Ok(meta) => Some(meta),
Some(meta) }
} }
#[inline] #[inline]

View file

@ -230,7 +230,7 @@ fn test_big_h() {
} }
#[test] #[test]
#[cfg(target_os = "linux")] #[cfg(not(target_vendor = "apple"))]
fn basic_succeeds() { fn basic_succeeds() {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
let one_group = nix::unistd::getgroups().unwrap(); let one_group = nix::unistd::getgroups().unwrap();
@ -251,3 +251,40 @@ fn test_no_change() {
at.touch("file"); at.touch("file");
ucmd.arg("").arg(at.plus("file")).succeeds(); ucmd.arg("").arg(at.plus("file")).succeeds();
} }
#[test]
#[cfg(not(target_vendor = "apple"))]
fn test_permission_denied() {
use std::os::unix::prelude::PermissionsExt;
if let Some(group) = nix::unistd::getgroups().unwrap().first() {
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("dir");
at.touch("dir/file");
std::fs::set_permissions(at.plus("dir"), PermissionsExt::from_mode(0o0000)).unwrap();
ucmd.arg("-R")
.arg(group.as_raw().to_string())
.arg("dir")
.fails()
.stderr_only("chgrp: cannot access 'dir': Permission denied");
}
}
#[test]
#[cfg(not(target_vendor = "apple"))]
fn test_subdir_permission_denied() {
use std::os::unix::prelude::PermissionsExt;
if let Some(group) = nix::unistd::getgroups().unwrap().first() {
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("dir");
at.mkdir("dir/subdir");
at.touch("dir/subdir/file");
std::fs::set_permissions(at.plus("dir/subdir"), PermissionsExt::from_mode(0o0000)).unwrap();
ucmd.arg("-R")
.arg(group.as_raw().to_string())
.arg("dir")
.fails()
.stderr_only("chgrp: cannot access 'dir/subdir': Permission denied");
}
}