1
0
mirror of https://github.com/BurntSushi/ripgrep.git synced 2024-12-12 19:18:24 +02:00

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
This commit is contained in:
Jakub Wieczorek 2019-09-03 21:47:33 +00:00 committed by Andrew Gallant
parent b435eaafc8
commit 322fc75a3d

View File

@ -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"]);
}
}