From a7831bf022b1ffb986e9d0c71b1b4358b2cd5635 Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Mon, 8 Dec 2025 11:43:49 +0100 Subject: [PATCH 1/6] filesystem: ErrorCode::IsDirectory when opening director w/o READ On Windows, it was possible to return a directory descriptor if READ wasn't in the permissions. Fixes wasmtime for https://github.com/WebAssembly/wasi-testsuite/issues/176. --- crates/wasi/src/filesystem.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/wasi/src/filesystem.rs b/crates/wasi/src/filesystem.rs index c5e3b54b1a0f..0d695e2fc693 100644 --- a/crates/wasi/src/filesystem.rs +++ b/crates/wasi/src/filesystem.rs @@ -1045,6 +1045,11 @@ impl Dir { .await?; match opened { + #[cfg(windows)] + OpenResult::Dir(dir) if !flags.contains(DescriptorFlags::READ) => { + Err(ErrorCode::IsDirectory) + } + OpenResult::Dir(dir) => Ok(Descriptor::Dir(Dir::new( dir, self.perms, From c96ae2a23edd2bd181e6d5f84c3628d58553f549 Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Mon, 8 Dec 2025 14:15:21 +0100 Subject: [PATCH 2/6] Fix unused-variable error --- crates/wasi/src/filesystem.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/wasi/src/filesystem.rs b/crates/wasi/src/filesystem.rs index 0d695e2fc693..0f73a2800d19 100644 --- a/crates/wasi/src/filesystem.rs +++ b/crates/wasi/src/filesystem.rs @@ -1046,7 +1046,7 @@ impl Dir { match opened { #[cfg(windows)] - OpenResult::Dir(dir) if !flags.contains(DescriptorFlags::READ) => { + OpenResult::Dir(_) if !flags.contains(DescriptorFlags::READ) => { Err(ErrorCode::IsDirectory) } From 1c5e447a9e65b376b992eb4b1ed0effa43721a5d Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Tue, 9 Dec 2025 09:11:31 +0100 Subject: [PATCH 3/6] Fix the sense of the EISDIR compatibility hack I had misremembered the nature of this incompatibility. Fixed (hopefully) with a reference to POSIX. prtest:full --- crates/wasi/src/filesystem.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/wasi/src/filesystem.rs b/crates/wasi/src/filesystem.rs index 0f73a2800d19..0d640e40ab13 100644 --- a/crates/wasi/src/filesystem.rs +++ b/crates/wasi/src/filesystem.rs @@ -1045,8 +1045,11 @@ impl Dir { .await?; match opened { + // Paper over a divergence between Windows and POSIX, where + // POSIX returns EISDIR if you open a directory with the + // WRITE flag: https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html#:~:text=EISDIR #[cfg(windows)] - OpenResult::Dir(_) if !flags.contains(DescriptorFlags::READ) => { + OpenResult::Dir(_) if flags.contains(DescriptorFlags::WRITE) => { Err(ErrorCode::IsDirectory) } From 2c33c8520c1d11fad04e61ab105d75af5562ff6f Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Tue, 9 Dec 2025 09:25:46 +0100 Subject: [PATCH 4/6] Update test expectations prtest:full --- .../src/bin/p1_path_open_preopen.rs | 48 +++++++------------ 1 file changed, 17 insertions(+), 31 deletions(-) diff --git a/crates/test-programs/src/bin/p1_path_open_preopen.rs b/crates/test-programs/src/bin/p1_path_open_preopen.rs index eed1e80c7d2c..91d981855e5f 100644 --- a/crates/test-programs/src/bin/p1_path_open_preopen.rs +++ b/crates/test-programs/src/bin/p1_path_open_preopen.rs @@ -66,37 +66,23 @@ unsafe fn path_open_preopen() { ) .expect("open with O_DIRECTORY and read right"); - if !test_programs::preview1::config().errno_expect_windows() { - // Open OFLAGS_DIRECTORY and read/write rights should fail with isdir: - let err = wasip1::path_open( - FIRST_PREOPEN, - 0, - ".", - wasip1::OFLAGS_DIRECTORY, - wasip1::RIGHTS_FD_READ | wasip1::RIGHTS_FD_WRITE, - 0, - 0, - ) - .err() - .expect("open with O_DIRECTORY and read/write should fail"); - assert_eq!( - err, - wasip1::ERRNO_ISDIR, - "opening directory read/write should fail with ISDIR" - ); - } else { - // Open OFLAGS_DIRECTORY and read/write rights will succeed, only on windows: - let _ = wasip1::path_open( - FIRST_PREOPEN, - 0, - ".", - wasip1::OFLAGS_DIRECTORY, - wasip1::RIGHTS_FD_READ | wasip1::RIGHTS_FD_WRITE, - 0, - 0, - ) - .expect("open with O_DIRECTORY and read/write should succeed on windows"); - } + // Open OFLAGS_DIRECTORY and read/write rights should fail with isdir: + let err = wasip1::path_open( + FIRST_PREOPEN, + 0, + ".", + wasip1::OFLAGS_DIRECTORY, + wasip1::RIGHTS_FD_READ | wasip1::RIGHTS_FD_WRITE, + 0, + 0, + ) + .err() + .expect("open with O_DIRECTORY and read/write should fail"); + assert_eq!( + err, + wasip1::ERRNO_ISDIR, + "opening directory read/write should fail with ISDIR" + ); } fn main() { From d08844c30939c175eaec62db280d4dafeea975df Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Tue, 9 Dec 2025 09:53:19 +0100 Subject: [PATCH 5/6] Update expectations for wasi-testsuite tests path_open_preopen is now working on Windows. --- tests/wasi.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/wasi.rs b/tests/wasi.rs index 054e9c674983..0a467dcf5e4f 100644 --- a/tests/wasi.rs +++ b/tests/wasi.rs @@ -64,8 +64,6 @@ const KNOWN_FAILURES: &[&str] = &[ #[cfg(windows)] "path_open_missing", #[cfg(windows)] - "path_open_preopen", - #[cfg(windows)] "path_open_read_write", #[cfg(windows)] "path_rename", From a010fbec981e003df696257d3fdf932be3d9a690 Mon Sep 17 00:00:00 2001 From: Andy Wingo Date: Tue, 9 Dec 2025 11:14:53 +0100 Subject: [PATCH 6/6] Possible fix for wasip1 file_open test prtest:full --- crates/wasi-common/src/snapshots/preview_1.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/wasi-common/src/snapshots/preview_1.rs b/crates/wasi-common/src/snapshots/preview_1.rs index 2c57fd3068c0..feb87bb5d325 100644 --- a/crates/wasi-common/src/snapshots/preview_1.rs +++ b/crates/wasi-common/src/snapshots/preview_1.rs @@ -814,6 +814,13 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { drop(dir_entry); let fd = match file { + // Paper over a divergence between Windows and POSIX, where + // POSIX returns EISDIR if you open a directory with the + // WRITE flag: https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html#:~:text=EISDIR + #[cfg(windows)] + OpenResult::Dir(_) if write => { + return Err(types::Errno::Isdir.into()); + } OpenResult::File(file) => table.push(Arc::new(FileEntry::new(file, access_mode)))?, OpenResult::Dir(child_dir) => table.push(Arc::new(DirEntry::new(None, child_dir)))?, };