diff --git a/src/lib.rs b/src/lib.rs index 4d41515..6d718bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -821,16 +821,20 @@ impl IntoIter { if self.opts.follow_links && dent.file_type().is_symlink() { dent = itry!(self.follow(dent)); } - let is_normal_dir = !dent.file_type().is_symlink() && dent.is_dir(); - if is_normal_dir { - if self.opts.same_file_system && dent.depth() > 0 { - if itry!(self.is_same_file_system(&dent)) { - itry!(self.push(&dent)); + let should_descend = if !dent.file_type().is_symlink() { + if dent.is_dir() { + if self.opts.same_file_system && dent.depth() > 0 { + // Only descend into directories on the same filesystem as + // the root directory. + itry!(self.is_same_file_system(&dent)) + } else { + // Don't check the filesystem; descend all directories. + true } } else { - itry!(self.push(&dent)); + false } - } else if dent.depth() == 0 && dent.file_type().is_symlink() { + } else if dent.depth() == 0 { // As a special case, if we are processing a root entry, then we // always follow it even if it's a symlink and follow_links is // false. We are careful to not let this change the semantics of @@ -841,11 +845,14 @@ impl IntoIter { let md = itry!(fs::metadata(dent.path()).map_err(|err| { Error::from_path(dent.depth(), dent.path().to_path_buf(), err) })); - if md.file_type().is_dir() { - itry!(self.push(&dent)); - } + md.file_type().is_dir() + } else { + false + }; + if should_descend { + itry!(self.push(&dent)); } - if is_normal_dir && self.opts.contents_first { + if should_descend && self.opts.contents_first { self.deferred_dirs.push(dent); None } else if self.skippable() { diff --git a/src/tests/recursive.rs b/src/tests/recursive.rs index 4119f46..025cded 100644 --- a/src/tests/recursive.rs +++ b/src/tests/recursive.rs @@ -1021,3 +1021,44 @@ fn regression_skip_current_dir() { wd.skip_current_dir(); wd.next(); } + +// Tests that entries are returned in the correct order when contents_first is +// on and the root is a symlink to a directory. +// +// See: https://github.com/BurntSushi/walkdir/issues/163 +#[test] +fn regression_sym_root_dir_nofollow_contents_first() { + let dir = Dir::tmp(); + dir.mkdirp("dir/a/b"); + dir.touch("dir/a-file"); + dir.symlink_dir("dir", "link"); + + let wd = WalkDir::new(dir.join("link")).contents_first(true); + let r = dir.run_recursive(wd); + r.assert_no_errors(); + + let ents = r.ents(); + assert_eq!(4, ents.len()); + + // link (the root) should always be the final entry. + let link = &ents[3]; + assert_eq!(dir.join("link"), link.path()); + assert!(link.path_is_symlink()); + assert_eq!(dir.join("dir"), fs::read_link(link.path()).unwrap()); + + // link/a/b must be followed directly by link/a + let path_link_a_b = dir.join("link").join("a").join("b"); + let path_link_a = dir.join("link").join("a"); + let mut expect_link_a = false; + for ent in ents { + if expect_link_a { + assert_eq!(path_link_a, ent.path()); + break; + } else if ent.path() == path_link_a_b { + expect_link_a = true; + } + } + if !expect_link_a { + panic!("Did not find link/a/b in results"); + } +}