summaryrefslogtreecommitdiffstats
path: root/ignore/src/walk.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ignore/src/walk.rs')
-rw-r--r--ignore/src/walk.rs20
1 files changed, 19 insertions, 1 deletions
diff --git a/ignore/src/walk.rs b/ignore/src/walk.rs
index 331268a3..b430981f 100644
--- a/ignore/src/walk.rs
+++ b/ignore/src/walk.rs
@@ -1043,7 +1043,7 @@ impl Iterator for WalkEventIter {
None => None,
Some(Err(err)) => Some(Err(err)),
Some(Ok(dent)) => {
- if dent.file_type().is_dir() {
+ if walkdir_is_dir(&dent) {
self.depth += 1;
Some(Ok(WalkEvent::Dir(dent)))
} else {
@@ -1791,6 +1791,24 @@ fn path_equals(dent: &DirEntry, handle: &Handle) -> Result<bool, Error> {
.map_err(|err| Error::Io(err).with_path(dent.path()))
}
+/// Returns true if the given walkdir entry corresponds to a directory.
+///
+/// This is normally just `dent.file_type().is_dir()`, but when we aren't
+/// following symlinks, the root directory entry may be a symlink to a
+/// directory that we *do* follow---by virtue of it being specified by the user
+/// explicitly. In that case, we need to follow the symlink and query whether
+/// it's a directory or not. But we only do this for root entries to avoid an
+/// additional stat check in most cases.
+fn walkdir_is_dir(dent: &walkdir::DirEntry) -> bool {
+ if dent.file_type().is_dir() {
+ return true;
+ }
+ if !dent.file_type().is_symlink() || dent.depth() > 0 {
+ return false;
+ }
+ dent.path().metadata().ok().map_or(false, |md| md.file_type().is_dir())
+}
+
/// Returns true if and only if the given path is on the same device as the
/// given root device.
fn is_same_file_system(root_device: u64, path: &Path) -> Result<bool, Error> {