From 8fa9d17e0f709283377e78f9654b1cb70bcd9e8f Mon Sep 17 00:00:00 2001 From: Kyue Date: Wed, 22 Jul 2026 22:19:08 +0100 Subject: [PATCH] test(ls): add regression test for -aRv walking into ./.. (#13501) ls -aRv used to recurse into the listed "." and ".." entries themselves, walking all the way up past the filesystem root, when combined with -a (show dot entries) and version-sort. Root cause: the old recursion code pushed "."/".." at a fixed prefix of the entry vector and then skipped that fixed prefix count after sorting - which only worked because the default name sort happens to keep them first. Version sort (-v) doesn't, so the skip landed on the wrong entries and "."/".." got treated as regular subdirectories to recurse into. This was already fixed on main as a side effect of the unrelated "enter_directory" traversal refactor in #9851, which replaced the position-based skip with an explicit is_dot_dir flag on each entry that's checked at recursion time regardless of sort order (landed 2026-04-17, 12 days after the 0.8.0 release the issue was filed against - the reporter just hadn't picked up a newer build yet). Verified directly: reproduced the walk-up-to-root behavior with a real 0.8.0 build, confirmed it's gone on current main, and confirmed via git blame + testing the actual pre-refactor code that the position- based skip is the real cause. No behavior change here, just adds coverage so this can't silently regress: the case is not covered by any existing test. --- tests/by-util/test_ls.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 56d74482758..2410e74505b 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -2721,6 +2721,31 @@ fn test_ls_recursive_1() { .stdout_is(out); } +#[test] +fn test_ls_recursive_all_with_version_sort_does_not_walk_up() { + // Regression test for https://github.com/uutils/coreutils/issues/13501: + // combining `-a` (show `.`/`..`) with `-R` (recursive) and `-v` + // (version/natural sort) used to make `ls` recurse into the listed + // `.`/`..` entries themselves, walking all the way up to the + // filesystem root instead of stopping at the leaf directories. + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + at.mkdir("a"); + at.mkdir("a/b"); + at.mkdir("a/b/c"); + + #[cfg(unix)] + let out = "a/b:\n.\n..\nc\n\na/b/c:\n.\n..\n"; + #[cfg(windows)] + let out = "a/b:\n.\n..\nc\n\na/b\\c:\n.\n..\n"; + scene + .ucmd() + .arg("-aRv") + .arg("a/b") + .succeeds() + .stdout_is(out); +} + /// The quoting module regroups tests that check the behavior of ls when /// quoting and escaping special characters with different quoting styles. #[cfg(unix)]