diff --git a/src/lib.rs b/src/lib.rs index 4b96df4..3ffa369 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -844,14 +844,18 @@ 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() @@ -867,11 +871,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 e415b91..7b12ea1 100644 --- a/src/tests/recursive.rs +++ b/src/tests/recursive.rs @@ -1090,3 +1090,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"); + } +}