ln: add WASI support via symlink_path (#11713)

This commit is contained in:
Anthony
2026-05-28 12:53:59 +02:00
committed by GitHub
parent 5e65ceb4a7
commit 179b977b93
7 changed files with 26 additions and 10 deletions
+2 -2
View File
@@ -50,7 +50,7 @@ jobs:
# Tests incompatible with WASI are annotated with
# #[cfg_attr(wasi_runner, ignore)] in the test source files.
# TODO: add integration tests for these tools as WASI support is extended:
# arch b2sum cat cksum cp csplit date dir dircolors fmt join ln
# arch b2sum cat cksum cp csplit date dir dircolors fmt join
# ls md5sum mkdir mv nproc pathchk pr printenv ptx pwd readlink
# realpath rm rmdir seq sha1sum sha224sum sha256sum sha384sum
# sha512sum shred sleep sort split tail touch tsort uname uniq
@@ -61,7 +61,7 @@ jobs:
test_base32:: test_base64:: test_basenc:: test_basename:: \
test_comm:: test_cut:: test_dirname:: test_echo:: \
test_expand:: test_factor:: test_false:: test_fold:: \
test_head:: test_link:: test_nl:: test_numfmt:: \
test_head:: test_link:: test_ln:: test_nl:: test_numfmt:: \
test_od:: test_paste:: test_printf:: test_shuf:: test_sum:: \
test_tee:: test_tr:: test_true:: test_truncate:: \
test_unexpand:: test_unlink:: test_wc::
Generated
+1
View File
@@ -3681,6 +3681,7 @@ version = "0.9.0"
dependencies = [
"clap",
"fluent",
"rustix",
"thiserror 2.0.18",
"uucore",
]
+5 -1
View File
@@ -6,7 +6,7 @@ To find all annotated tests: `grep -rn 'wasi_runner, ignore' tests/`
## Tools not yet covered by integration tests
arch, b2sum, cat, cksum, cp, csplit, date, dir, dircolors, fmt, join, ln, ls, md5sum, mkdir, mv, nproc, pathchk, pr, printenv, ptx, pwd, readlink, realpath, rm, rmdir, seq, sha1sum, sha224sum, sha256sum, sha384sum, sha512sum, shred, sleep, sort, split, tail, touch, tsort, uname, uniq, vdir, yes
arch, b2sum, cat, cksum, cp, csplit, date, dir, dircolors, fmt, join, ls, md5sum, mkdir, mv, nproc, pathchk, pr, printenv, ptx, pwd, readlink, realpath, rm, rmdir, seq, sha1sum, sha224sum, sha256sum, sha384sum, sha512sum, shred, sleep, sort, split, tail, touch, tsort, uname, uniq, vdir, yes
## WASI sandbox: host paths not visible
@@ -31,3 +31,7 @@ WASI does not support spawning child processes. Tests that shell out to other co
## WASI: stdin file position not preserved through wasmtime
When stdin is a seekable file, wasmtime does not preserve the file position between the host and guest. Tests that validate stdin offset behavior after `head` reads are skipped.
## WASI: read_link on absolute paths fails under wasmtime via spawned test harness
`fs::read_link` on an absolute path inside the sandbox (e.g. `/file2`) returns `EPERM` when the WASI binary is launched through `std::process::Command` from the test harness, even though the same call works when wasmtime is invoked directly. This breaks `uucore::fs::canonicalize` for symlink sources, so tests that rely on following a symlink to compute a relative path are skipped.
+3
View File
@@ -25,6 +25,9 @@ uucore = { workspace = true, features = ["backup-control", "fs"] }
thiserror = { workspace = true }
fluent = { workspace = true }
[target.'cfg(target_os = "wasi")'.dependencies]
rustix = { workspace = true, features = ["fs"] }
[[bin]]
name = "ln"
path = "src/main.rs"
+2 -5
View File
@@ -524,9 +524,6 @@ pub fn symlink<P1: AsRef<Path>, P2: AsRef<Path>>(src: P1, dst: P2) -> io::Result
}
#[cfg(target_os = "wasi")]
fn symlink<P1: AsRef<Path>, P2: AsRef<Path>>(_src: P1, _dst: P2) -> io::Result<()> {
Err(io::Error::new(
io::ErrorKind::Unsupported,
"symlinks not supported on this platform",
))
pub fn symlink<P1: AsRef<Path>, P2: AsRef<Path>>(src: P1, dst: P2) -> io::Result<()> {
rustix::fs::symlink(src.as_ref(), dst.as_ref()).map_err(io::Error::from)
}
@@ -91,6 +91,7 @@ use std::{
error::Error,
ffi::{OsStr, OsString},
fmt::{Debug, Display},
fs,
path::{Path, PathBuf},
};
@@ -442,7 +443,11 @@ fn numbered_backup_path(path: &Path) -> PathBuf {
let mut i: u64 = 1;
loop {
let new_path = simple_backup_path(path, OsString::from(format!(".~{i}~")));
if !new_path.exists() {
// Use `symlink_metadata` rather than `exists()` so that a dangling
// symlink still counts as an existing backup (avoiding a silent
// overwrite), and so we do not report a live symlink as missing when
// the target cannot be stat'd.
if fs::symlink_metadata(&new_path).is_err() {
return new_path;
}
i += 1;
@@ -451,7 +456,7 @@ fn numbered_backup_path(path: &Path) -> PathBuf {
fn existing_backup_path<S: AsRef<OsStr>>(path: &Path, suffix: S) -> PathBuf {
let test_path = simple_backup_path(path, OsString::from(".~1~"));
if test_path.exists() {
if fs::symlink_metadata(&test_path).is_ok() {
return numbered_backup_path(path);
}
simple_backup_path(path, suffix.as_ref())
+6
View File
@@ -501,6 +501,7 @@ fn test_symlink_implicit_target_dir() {
}
#[test]
#[cfg_attr(wasi_runner, ignore = "WASI sandbox: host paths not visible")]
fn test_symlink_to_dir_2args() {
let (at, mut ucmd) = at_and_ucmd!();
let filename = "test_symlink_to_dir_2args_file";
@@ -754,6 +755,10 @@ fn test_relative_dst_already_symlink() {
}
#[test]
#[cfg_attr(
wasi_runner,
ignore = "WASI: read_link on absolute paths fails under wasmtime via spawned test harness"
)]
fn test_relative_src_already_symlink() {
let (at, mut ucmd) = at_and_ucmd!();
at.touch("file1");
@@ -967,6 +972,7 @@ fn test_ln_seen_file() {
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(wasi_runner, ignore = "WASI: argv/filenames must be valid UTF-8")]
fn test_ln_non_utf8_paths() {
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;