Skip to content

Commit 0f4441f

Browse files
committed
windows: fix OneDrive traversals
This commit fixes a bug on Windows where walkdir refused to traverse directories that resided on OneDrive via its "file on demand" strategy. The specific bug is that Rust's standard library treats a reparse point (which is what OneDrive uses) as distinct from a file or directory, which wreaks havoc on any code that uses FileType::{is_file, is_dir}. We fix this by checking the directory status of a file by looking only at whether its directory bit is set. This bug was originally reported in ripgrep: BurntSushi/ripgrep#705 It has also been filed upstream: rust-lang/rust#46484 And has a pending fix: rust-lang/rust#47956
1 parent 42a5b95 commit 0f4441f

File tree

2 files changed

+41
-7
lines changed

2 files changed

+41
-7
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ appveyor = { repository = "BurntSushi/walkdir" }
1919
[dependencies]
2020
same-file = "1"
2121

22+
[target.'cfg(windows)'.dependencies.winapi]
23+
version = "0.3"
24+
features = ["std", "winnt"]
25+
2226
[dev-dependencies]
2327
docopt = "0.8"
2428
quickcheck = { version = "0.6", default-features = false }

src/lib.rs

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ extern crate quickcheck;
111111
#[cfg(test)]
112112
extern crate rand;
113113
extern crate same_file;
114+
#[cfg(windows)]
115+
extern crate winapi;
114116

115117
use std::cmp::{Ordering, min};
116118
use std::error;
@@ -629,6 +631,13 @@ pub struct DirEntry {
629631
/// The underlying inode number (Unix only).
630632
#[cfg(unix)]
631633
ino: u64,
634+
/// The underlying metadata (Windows only).
635+
///
636+
/// We use this to determine whether an entry is a directory or not, which
637+
/// works around a bug in Rust's standard library:
638+
/// https://github.com/rust-lang/rust/issues/46484
639+
#[cfg(windows)]
640+
metadata: fs::Metadata,
632641
}
633642

634643
impl Iterator for IntoIter {
@@ -791,10 +800,10 @@ impl IntoIter {
791800
if self.opts.follow_links && dent.file_type().is_symlink() {
792801
dent = itry!(self.follow(dent));
793802
}
794-
if dent.file_type().is_dir() {
803+
if dent.is_dir() {
795804
itry!(self.push(&dent));
796805
}
797-
if dent.file_type().is_dir() && self.opts.contents_first {
806+
if dent.is_dir() && self.opts.contents_first {
798807
self.deferred_dirs.push(dent);
799808
None
800809
} else if self.skippable() {
@@ -879,7 +888,7 @@ impl IntoIter {
879888
// The only way a symlink can cause a loop is if it points
880889
// to a directory. Otherwise, it always points to a leaf
881890
// and we can omit any loop checks.
882-
if dent.file_type().is_dir() {
891+
if dent.is_dir() {
883892
self.check_loop(dent.path())?;
884893
}
885894
Ok(dent)
@@ -1030,16 +1039,35 @@ impl DirEntry {
10301039
self.depth
10311040
}
10321041

1042+
/// Returns true if and only if this entry points to a directory.
1043+
///
1044+
/// This works around a bug in Rust's standard library:
1045+
/// https://github.com/rust-lang/rust/issues/46484
1046+
#[cfg(windows)]
1047+
fn is_dir(&self) -> bool {
1048+
use std::os::windows::fs::MetadataExt;
1049+
use winapi::um::winnt::FILE_ATTRIBUTE_DIRECTORY;
1050+
self.metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY != 0
1051+
}
1052+
1053+
/// Returns true if and only if this entry points to a directory.
1054+
#[cfg(not(windows))]
1055+
fn is_dir(&self) -> bool {
1056+
self.ty.is_dir()
1057+
}
1058+
10331059
#[cfg(not(unix))]
10341060
fn from_entry(depth: usize, ent: &fs::DirEntry) -> Result<DirEntry> {
1035-
let ty = ent.file_type().map_err(|err| {
1036-
Error::from_path(depth, ent.path(), err)
1061+
let path = ent.path();
1062+
let md = fs::metadata(&path).map_err(|err| {
1063+
Error::from_path(depth, path.clone(), err)
10371064
})?;
10381065
Ok(DirEntry {
1039-
path: ent.path(),
1040-
ty: ty,
1066+
path: path,
1067+
ty: md.file_type(),
10411068
follow_link: false,
10421069
depth: depth,
1070+
metadata: md,
10431071
})
10441072
}
10451073

@@ -1069,6 +1097,7 @@ impl DirEntry {
10691097
ty: md.file_type(),
10701098
follow_link: true,
10711099
depth: depth,
1100+
metadata: md,
10721101
})
10731102
}
10741103

@@ -1097,6 +1126,7 @@ impl Clone for DirEntry {
10971126
ty: self.ty,
10981127
follow_link: self.follow_link,
10991128
depth: self.depth,
1129+
metadata: self.metadata.clone(),
11001130
}
11011131
}
11021132

0 commit comments

Comments
 (0)