From 710d7b0397d3d25f7573c7faaf08809abfbde1be Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 21 Apr 2026 15:30:06 +0200 Subject: [PATCH 1/2] ls: restore WASI ".." metadata fallback in collect_directory_entries The ls refactor in #9851 dropped the WASI guard added in #11633, so `ls -al` at a preopened root once again fails with "Capabilities insufficient" when stat'ing "..". Extract the logic into a small `dotdot_path` helper and add a regression test covering `ls -al`. --- src/uu/ls/src/ls.rs | 16 +++++++++++++++- tests/by-util/test_ls.rs | 17 +++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 1f8889acfac..392aed0972e 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -1248,6 +1248,20 @@ pub fn list_with_output( Ok(()) } +/// Build the path for the ".." entry of `parent`. +/// +/// On WASI the sandbox may block access to ".." at the preopened root, +/// so fall back to the parent path itself when its metadata can't be +/// read. On other targets this is just `parent/..`. +fn dotdot_path(parent: &Path) -> PathBuf { + let dotdot = parent.join(".."); + #[cfg(target_os = "wasi")] + if dotdot.metadata().is_err() { + return parent.to_path_buf(); + } + dotdot +} + fn collect_directory_entries( entries: &mut Vec, path_data: &PathData, @@ -1266,7 +1280,7 @@ fn collect_directory_entries( false, )); entries.push(PathData::new( - path_data.path().join("..").into(), + dotdot_path(path_data.path()).into(), None, Some(OsStr::new("..").into()), config, diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 307038ee099..9434d1913b4 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -7244,3 +7244,20 @@ fn test_ls_a_dotdot_no_error_on_wasi() { .stdout_contains("..") .no_stderr(); } + +#[test] +#[cfg(target_os = "wasi")] +fn test_ls_al_no_capabilities_insufficient_on_wasi() { + // `ls -al` reads metadata for every entry including "..". Without the + // WASI fallback, stat on ".." at the preopened root returns + // ERRNO_NOTCAPABLE, which surfaces to the user as "Capabilities + // insufficient". Guard against that regression here. + let scene = TestScenario::new(util_name!()); + let out = scene.ucmd().arg("-al").succeeds(); + out.no_stderr(); + assert!( + !out.stdout_str().contains("Capabilities insufficient"), + "ls -al stdout leaked a WASI capability error: {}", + out.stdout_str() + ); +} From 8d8fc74402d4d801a53f11a858bacb42d52f4c77 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Wed, 22 Apr 2026 08:31:02 +0200 Subject: [PATCH 2/2] Update spell-checker ignore words in test_ls.rs --- tests/by-util/test_ls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 9434d1913b4..8eb10f5a01e 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. // spell-checker:ignore (words) READMECAREFULLY birthtime doesntexist oneline somebackup lrwx somefile somegroup somehiddenbackup somehiddenfile tabsize aaaaaaaa bbbb cccc dddddddd ncccc neee naaaaa nbcdef nfffff dired subdired tmpfs mdir COLORTERM mexe bcdef mfoo timefile -// spell-checker:ignore (words) fakeroot setcap drwxr bcdlps mdangling mentry awith acolons NOFILE +// spell-checker:ignore (words) fakeroot setcap drwxr bcdlps mdangling mentry awith acolons NOFILE NOTCAPABLE #![allow( clippy::similar_names, clippy::too_many_lines,