sausage-vfs: review fixes (fallible growth, symlink flags, stream cancel, sized reads, timestamps, error kinds, linker shadowing)

API: Layer::stat(path, follow), Layer::readlink, Open::follow,
Stat::symlink and Entry::symlink. Process::idle documents the shutdown
order: abort, await the task, await idle, release locks.
This commit is contained in:
2026-10-23 00:49:46 +01:00
parent 7bbcd47837
commit 151550242b
14 changed files with 673 additions and 97 deletions
+63 -4
View File
@@ -55,6 +55,14 @@ async fn run(topic: &str, shared: Arc<Memory>) {
let (engine, component) = guest();
let world = tempfile::tempdir().unwrap();
std::fs::create_dir(world.path().join("tmp")).unwrap();
let outside = tempfile::tempdir().unwrap();
if topic == "review_links" {
std::fs::create_dir(world.path().join("target")).unwrap();
std::fs::write(world.path().join("target/keep"), b"keep").unwrap();
std::fs::write(outside.path().join("secret"), b"secret").unwrap();
std::os::unix::fs::symlink("target", world.path().join("link")).unwrap();
std::os::unix::fs::symlink(outside.path(), world.path().join("outside-link")).unwrap();
}
let mut mounts = Mounts::default();
mounts.mount(
"/",
@@ -73,13 +81,20 @@ async fn run(topic: &str, shared: Arc<Memory>) {
table: &mut s.table,
})
.unwrap();
let mut wasi = WasiCtxBuilder::new();
wasi.inherit_stdio().env("VFS_TOPIC", topic);
if topic == "review_preopen" {
wasi.preopened_dir(
world.path(),
"/native",
wasmtime_wasi::filesystem::FsPerms::ReadWrite,
)
.unwrap();
}
let mut store = Store::new(
engine,
State {
wasi: WasiCtxBuilder::new()
.inherit_stdio()
.env("VFS_TOPIC", topic)
.build(),
wasi: wasi.build(),
table: ResourceTable::new(),
fs: Adapter::new(mounts),
},
@@ -150,3 +165,47 @@ async fn sqlite_transactions() {
async fn named_temporary_files() {
isolated("tempfile").await;
}
/// Stream drop preserves writes and small reads observe later mutations.
#[tokio::test(flavor = "multi_thread")]
async fn review_streams() {
isolated("review_streams").await;
}
/// Timestamp conversion reports overflow and accepts directories.
#[tokio::test(flavor = "multi_thread")]
async fn review_times() {
isolated("review_times").await;
}
/// Path errors distinguish trailing slashes, busy roots, and ordinary files.
#[tokio::test(flavor = "multi_thread")]
async fn review_paths() {
isolated("review_paths").await;
}
/// Symlink traversal does not delete targets or escape the host root.
#[tokio::test(flavor = "multi_thread")]
async fn review_links() {
isolated("review_links").await;
}
/// The guest sees only the virtual root preopen.
#[tokio::test(flavor = "multi_thread")]
async fn review_preopen() {
isolated("review_preopen").await;
}
/// Adding the adapter restores duplicate-import errors on the caller's linker.
#[test]
fn linker_shadowing_is_reset() {
let engine = Engine::default();
let mut linker = Linker::<State>::new(&engine);
wasmtime_wasi::p2::add_to_linker_async(&mut linker).unwrap();
adapter::add_to_linker(&mut linker, |s| View {
adapter: &mut s.fs,
table: &mut s.table,
})
.unwrap();
assert!(wasmtime_wasi::p2::add_to_linker_async(&mut linker).is_err());
}