wasmtime icon indicating copy to clipboard operation
wasmtime copied to clipboard

wasip1 path_remove_directory fails with paths with trailing slashes

Open wingo opened this issue 4 months ago • 4 comments

Test Case

For the wasi-testsuite test remove_directory_trailing_slashes, we mkdir dir.cleanup, then try to rrmdir dir.cleanup/ (note trailing slash). This fails because it uses the "get the parent then remove the leaf" strategy, and the parent of dir.cleanup/ is dir.cleanup, and the leaf is ..

A trace: https://github.com/WebAssembly/wasi-testsuite/issues/101#issuecomment-3214123363

I would expect that the rmdir removes trailing slashes.

Filing this bug here as I am not sure if this is a failure of cap-std or if callers are required to remove trailing slashes already.

Versions and Environment

Wasmtime version or commit: 1a88c70a6ec088ee213658488549306f8951405d (august 19)

wingo avatar Aug 25 '25 07:08 wingo

Given that cap-std needs to sometimes normalize paths, consider using some wrapper around std::path::Path for normalized paths. Otherwise it's too easy to pass an absolute path to some routine that needs a normalized relative path.

wingo avatar Sep 04 '25 13:09 wingo

Same failure in wasip3:

use std::process;
extern crate wit_bindgen;

wit_bindgen::generate!({
    inline: r"
  package test:test;

  world test {
      include wasi:filesystem/[email protected];
      include wasi:cli/[email protected];
  }
",
    additional_derives: [PartialEq, Eq, Hash, Clone],
    // Work around https://github.com/bytecodealliance/wasm-tools/issues/2285.
    features:["clocks-timezone"],
    async: [
        "wasi:cli/[email protected]#run",
    ],
    generate_all
});

use wasi::filesystem::types::{PathFlags,ErrorCode};

async fn test_filesystem() {
    match &wasi::filesystem::preopens::get_directories()[..] {
        [(dirfd, _)] => {
            dirfd.create_directory_at("qqq/".to_string())
                .await.expect("mkdir qqq/");
            dirfd.remove_directory_at("qqq/".to_string())
                .await.expect("rmdir qqq/");
        },
        [..] => {
            eprintln!("usage: run with one open dir");
            process::exit(1)
        }
    }
}

struct Component;
export!(Component);
impl exports::wasi::cli::run::Guest for Component {
    async fn run() -> Result<(), ()> {
        test_filesystem().await;
        Ok(())
    }
}

fn main() {
    unreachable!("main is a stub");
}

The mkdir qqq/ straces as:

mkdirat(3, "qqq", 0777)                 = 0

Whereas the rmdir qqq/ straces as:

openat2(3, "qqq/", {flags=O_RDONLY|O_CLOEXEC|O_PATH|O_DIRECTORY, resolve=RESOLVE_NO_MAGICLINKS|RESOLVE_BENEATH}, 24) = 11
unlinkat(11, ".", AT_REMOVEDIR)         = -1 EINVAL (Invalid argument)

wingo avatar Sep 05 '25 10:09 wingo

@sunfishcode are you still interested in investigating this?

alexcrichton avatar Oct 07 '25 16:10 alexcrichton

Gentle ping here, this is the only wasip1 test in wasi-testsuite that wasmtime fails.

wingo avatar Dec 08 '25 11:12 wingo