summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJakub Wieczorek <jakub.adam.wieczorek@gmail.com>2019-09-03 21:47:33 +0000
committerAndrew Gallant <jamslam@gmail.com>2020-02-17 17:16:28 -0500
commit322fc75a3da11242c3e23a60bc4cddbc688552b0 (patch)
tree998b5f165e207b7c99ede6bdc71eaf6b646878fd
parentb435eaafc805b1edb12b04d72de0e0ce113059f9 (diff)
ignore: make walker visit untraversable directories
This commit fixes an inconsistency between the serial and the parallel directory walkers around visiting a directory for which the user holds insufficient permissions to descend into. The serial walker does produce a successful entry for a directory that it cannot descend into due to insufficient permissions. However, before this change that has not been the case for the parallel walker, which would produce an `Err` item not only when descending into a directory that it cannot read from but also for the directory entry itself. This change brings the behaviour of the parallel variant in line with that of the serial one. Fixes #1346, Closes #1365
-rw-r--r--ignore/src/walk.rs56
1 files changed, 45 insertions, 11 deletions
diff --git a/ignore/src/walk.rs b/ignore/src/walk.rs
index 365d049d..bbe6d7d7 100644
--- a/ignore/src/walk.rs
+++ b/ignore/src/walk.rs
@@ -1409,16 +1409,7 @@ impl<'s> Worker<'s> {
return;
}
}
- let readdir = match work.read_dir() {
- Ok(readdir) => readdir,
- Err(err) => {
- if self.visitor.visit(Err(err)).is_quit() {
- self.quit_now();
- return;
- }
- continue;
- }
- };
+
let descend = if let Some(root_device) = work.root_device {
match is_same_file_system(root_device, work.dent.path()) {
Ok(true) => true,
@@ -1435,6 +1426,13 @@ impl<'s> Worker<'s> {
true
};
+ // Try to read the directory first before we transfer ownership
+ // to the provided closure. Do not unwrap it immediately, though,
+ // as we may receive an `Err` value e.g. in the case when we do not
+ // have sufficient read permissions to list the directory.
+ // In that case we still want to provide the closure with a valid
+ // entry before passing the error value.
+ let readdir = work.read_dir();
let depth = work.dent.depth();
match self.visitor.visit(Ok(work.dent)) {
WalkState::Continue => {}
@@ -1447,11 +1445,28 @@ impl<'s> Worker<'s> {
if !descend {
continue;
}
+
+ let readdir = match readdir {
+ Ok(readdir) => readdir,
+ Err(err) => {
+ if self.visitor.visit(Err(err)).is_quit() {
+ self.quit_now();
+ return;
+ }
+ continue;
+ }
+ };
+
if self.max_depth.map_or(false, |max| depth >= max) {
continue;
}
for result in readdir {
- let state = self.run_one(&work.ignore, depth + 1, work.root_device, result);
+ let state = self.run_one(
+ &work.ignore,
+ depth + 1,
+ work.root_device,
+ result,
+ );
if state.is_quit() {
self.quit_now();
return;
@@ -2155,4 +2170,23 @@ mod tests {
builder.follow_links(true).same_file_system(true);
assert_paths(td.path(), &builder, &["same_file", "same_file/alink"]);
}
+
+ #[cfg(target_os = "linux")]
+ #[test]
+ fn no_read_permissions() {
+ let dir_path = Path::new("/root");
+
+ // There's no /etc/sudoers.d, skip the test.
+ if !dir_path.is_dir() {
+ return;
+ }
+ // We're the root, so the test won't check what we want it to.
+ if fs::read_dir(&dir_path).is_ok() {
+ return;
+ }
+
+ // Check that we can't descend but get an entry for the parent dir.
+ let builder = WalkBuilder::new(&dir_path);
+ assert_paths(dir_path.parent().unwrap(), &builder, &["root"]);
+ }
}