Skip to content

Commit fd781df

Browse files
authored
ls: restore WASI ".." metadata fallback in collect_directory_entries (#11930)
* 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`. * Update spell-checker ignore words in test_ls.rs
1 parent 444e383 commit fd781df

2 files changed

Lines changed: 33 additions & 2 deletions

File tree

src/uu/ls/src/ls.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,20 @@ pub fn list_with_output<O: LsOutput>(
12481248
Ok(())
12491249
}
12501250

1251+
/// Build the path for the ".." entry of `parent`.
1252+
///
1253+
/// On WASI the sandbox may block access to ".." at the preopened root,
1254+
/// so fall back to the parent path itself when its metadata can't be
1255+
/// read. On other targets this is just `parent/..`.
1256+
fn dotdot_path(parent: &Path) -> PathBuf {
1257+
let dotdot = parent.join("..");
1258+
#[cfg(target_os = "wasi")]
1259+
if dotdot.metadata().is_err() {
1260+
return parent.to_path_buf();
1261+
}
1262+
dotdot
1263+
}
1264+
12511265
fn collect_directory_entries<O: LsOutput>(
12521266
entries: &mut Vec<PathData>,
12531267
path_data: &PathData,
@@ -1266,7 +1280,7 @@ fn collect_directory_entries<O: LsOutput>(
12661280
false,
12671281
));
12681282
entries.push(PathData::new(
1269-
path_data.path().join("..").into(),
1283+
dotdot_path(path_data.path()).into(),
12701284
None,
12711285
Some(OsStr::new("..").into()),
12721286
config,

tests/by-util/test_ls.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
55
// 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
6-
// spell-checker:ignore (words) fakeroot setcap drwxr bcdlps mdangling mentry awith acolons NOFILE
6+
// spell-checker:ignore (words) fakeroot setcap drwxr bcdlps mdangling mentry awith acolons NOFILE NOTCAPABLE
77
#![allow(
88
clippy::similar_names,
99
clippy::too_many_lines,
@@ -7244,3 +7244,20 @@ fn test_ls_a_dotdot_no_error_on_wasi() {
72447244
.stdout_contains("..")
72457245
.no_stderr();
72467246
}
7247+
7248+
#[test]
7249+
#[cfg(target_os = "wasi")]
7250+
fn test_ls_al_no_capabilities_insufficient_on_wasi() {
7251+
// `ls -al` reads metadata for every entry including "..". Without the
7252+
// WASI fallback, stat on ".." at the preopened root returns
7253+
// ERRNO_NOTCAPABLE, which surfaces to the user as "Capabilities
7254+
// insufficient". Guard against that regression here.
7255+
let scene = TestScenario::new(util_name!());
7256+
let out = scene.ucmd().arg("-al").succeeds();
7257+
out.no_stderr();
7258+
assert!(
7259+
!out.stdout_str().contains("Capabilities insufficient"),
7260+
"ls -al stdout leaked a WASI capability error: {}",
7261+
out.stdout_str()
7262+
);
7263+
}

0 commit comments

Comments
 (0)