Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/windirstat/walkdir.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs86
1 files changed, 79 insertions, 7 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 6ce95a3..d60c2d7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -132,6 +132,8 @@ pub use unix::DirEntryExt;
mod tests;
#[cfg(unix)]
mod unix;
+#[cfg(windows)]
+mod windows;
/// Like try, but for iterators that return [`Option<Result<_, _>>`].
///
@@ -247,6 +249,7 @@ struct WalkDirOptions {
FnMut(&DirEntry,&DirEntry) -> Ordering + Send + Sync + 'static
>>,
contents_first: bool,
+ same_file_system: bool,
}
impl fmt::Debug for WalkDirOptions {
@@ -264,6 +267,7 @@ impl fmt::Debug for WalkDirOptions {
.field("max_depth", &self.max_depth)
.field("sorter", &sorter_str)
.field("contents_first", &self.contents_first)
+ .field("same_file_system", &self.same_file_system)
.finish()
}
}
@@ -283,6 +287,7 @@ impl WalkDir {
max_depth: ::std::usize::MAX,
sorter: None,
contents_first: false,
+ same_file_system: false,
},
root: root.as_ref().to_path_buf(),
}
@@ -448,6 +453,19 @@ impl WalkDir {
self.opts.contents_first = yes;
self
}
+
+ /// Do not cross file system boundaries.
+ ///
+ /// When this option is enabled, directory traversal will not descend into
+ /// directories that are on a different file system from the root path.
+ ///
+ /// Currently, this option is only supported on Unix and Windows. If this
+ /// option is used on an unsupported platform, then directory traversal
+ /// will immediately return an error and will not yield any entries.
+ pub fn same_file_system(mut self, yes: bool) -> Self {
+ self.opts.same_file_system = yes;
+ self
+ }
}
impl IntoIterator for WalkDir {
@@ -463,6 +481,7 @@ impl IntoIterator for WalkDir {
oldest_opened: 0,
depth: 0,
deferred_dirs: vec![],
+ root_device: None,
}
}
}
@@ -512,6 +531,13 @@ pub struct IntoIter {
/// yielded after their contents has been fully yielded. This is only
/// used when `contents_first` is enabled.
deferred_dirs: Vec<DirEntry>,
+ /// The device of the root file path when the first call to `next` was
+ /// made.
+ ///
+ /// If the `same_file_system` option isn't enabled, then this is always
+ /// `None`. Conversely, if it is enabled, this is always `Some(...)` after
+ /// handling the root path.
+ root_device: Option<u64>,
}
/// An ancestor is an item in the directory tree traversed by walkdir, and is
@@ -650,6 +676,11 @@ impl Iterator for IntoIter {
/// an error value. The error will be wrapped in an Option::Some.
fn next(&mut self) -> Option<Result<DirEntry>> {
if let Some(start) = self.start.take() {
+ if self.opts.same_file_system {
+ let result = device_num(&start)
+ .map_err(|e| Error::from_path(0, start.clone(), e));
+ self.root_device = Some(itry!(result));
+ }
let dent = itry!(DirEntry::from_path(0, start, false));
if let Some(result) = self.handle_entry(dent) {
return Some(result);
@@ -668,7 +699,11 @@ impl Iterator for IntoIter {
}
// Unwrap is safe here because we've verified above that
// `self.stack_list` is not empty
- match self.stack_list.last_mut().expect("bug in walkdir").next() {
+ let next = self.stack_list
+ .last_mut()
+ .expect("BUG: stack should be non-empty")
+ .next();
+ match next {
None => self.pop(),
Some(Err(err)) => return Some(Err(err)),
Some(Ok(dent)) => {
@@ -802,7 +837,13 @@ impl IntoIter {
}
let is_normal_dir = !dent.file_type().is_symlink() && dent.is_dir();
if is_normal_dir {
- itry!(self.push(&dent));
+ if self.opts.same_file_system && dent.depth > 0 {
+ if itry!(self.is_same_file_system(&dent)) {
+ itry!(self.push(&dent));
+ }
+ } else {
+ itry!(self.push(&dent));
+ }
}
if is_normal_dir && self.opts.contents_first {
self.deferred_dirs.push(dent);
@@ -820,7 +861,7 @@ impl IntoIter {
// Unwrap is safe here because we've guaranteed that
// `self.deferred_dirs.len()` can never be less than 1
let deferred: DirEntry = self.deferred_dirs.pop()
- .expect("bug in walkdir");
+ .expect("BUG: deferred_dirs should be non-empty");
if !self.skippable() {
return Some(deferred);
}
@@ -874,7 +915,7 @@ impl IntoIter {
}
fn pop(&mut self) {
- self.stack_list.pop().expect("cannot pop from empty stack");
+ self.stack_list.pop().expect("BUG: cannot pop from empty stack");
if self.opts.follow_links {
self.stack_path.pop().expect("BUG: list/path stacks out of sync");
}
@@ -920,6 +961,14 @@ impl IntoIter {
Ok(())
}
+ fn is_same_file_system(&mut self, dent: &DirEntry) -> Result<bool> {
+ let dent_device = device_num(&dent.path)
+ .map_err(|err| Error::from_entry(dent, err))?;
+ Ok(self.root_device
+ .map(|d| d == dent_device)
+ .expect("BUG: called is_same_file_system without root device"))
+ }
+
fn skippable(&self) -> bool {
self.depth < self.opts.min_depth || self.depth > self.opts.max_depth
}
@@ -1584,10 +1633,12 @@ impl fmt::Display for Error {
}
impl From<Error> for io::Error {
- /// Convert the [`Error`] to an [`io::Error`], preserving the original [`Error`] as the ["inner
- /// error"]. Note that this also makes the display of the error include the context.
+ /// Convert the [`Error`] to an [`io::Error`], preserving the original
+ /// [`Error`] as the ["inner error"]. Note that this also makes the display
+ /// of the error include the context.
///
- /// This is different from [`into_io_error`] which returns the original [`io::Error`].
+ /// This is different from [`into_io_error`] which returns the original
+ /// [`io::Error`].
///
/// [`Error`]: struct.Error.html
/// [`io::Error`]: https://doc.rust-lang.org/stable/std/io/struct.Error.html
@@ -1605,3 +1656,24 @@ impl From<Error> for io::Error {
io::Error::new(kind, walk_err)
}
}
+
+#[cfg(unix)]
+fn device_num<P: AsRef<Path>>(path: P)-> std::io::Result<u64> {
+ use std::os::unix::fs::MetadataExt;
+
+ path.as_ref().metadata().map(|md| md.dev())
+}
+
+ #[cfg(windows)]
+fn device_num<P: AsRef<Path>>(path: P) -> std::io::Result<u64> {
+ windows::windows_file_handle_info(path)
+ .map(|info| info.dwVolumeSerialNumber as u64)
+}
+
+#[cfg(not(any(unix, windows)))]
+fn device_num<P: AsRef<Path>>(_: P)-> std::io::Result<u64> {
+ Err(io::Error::new(
+ io::ErrorKind::Other,
+ "walkdir: same_file_system option not supported on this platform",
+ ))
+}