diff --git a/crates/test-programs/src/bin/p2_file_read_write.rs b/crates/test-programs/src/bin/p2_file_read_write.rs index 8c39d6de573c..0c2b82045c87 100644 --- a/crates/test-programs/src/bin/p2_file_read_write.rs +++ b/crates/test-programs/src/bin/p2_file_read_write.rs @@ -5,6 +5,11 @@ fn main() { let preopens = wasi::filesystem::preopens::get_directories(); let (dir, _) = &preopens[0]; + assert_eq!( + dir.read_via_stream(0).err(), + Some(wasi::filesystem::types::ErrorCode::IsDirectory) + ); + let filename = "test.txt"; let file = dir .open_at( diff --git a/crates/test-programs/src/bin/p3_filesystem_file_read_write.rs b/crates/test-programs/src/bin/p3_filesystem_file_read_write.rs index b4a9518cbeb9..2559a8f83bc9 100644 --- a/crates/test-programs/src/bin/p3_filesystem_file_read_write.rs +++ b/crates/test-programs/src/bin/p3_filesystem_file_read_write.rs @@ -1,5 +1,7 @@ use futures::join; -use test_programs::p3::wasi::filesystem::types::{DescriptorFlags, OpenFlags, PathFlags}; +use test_programs::p3::wasi::filesystem::types::{ + DescriptorFlags, ErrorCode, OpenFlags, PathFlags, +}; use test_programs::p3::{wasi, wit_stream}; struct Component; @@ -11,6 +13,13 @@ impl test_programs::p3::exports::wasi::cli::run::Guest for Component { let preopens = wasi::filesystem::preopens::get_directories(); let (dir, _) = &preopens[0]; + let (_data_rx, data_fut) = dir.read_via_stream(0); + let err = data_fut.await.expect_err("directory read should fail"); + assert!( + matches!(err, ErrorCode::IsDirectory), + "unexpected error: {err:?}" + ); + let filename = "test.txt"; { let file = dir diff --git a/crates/wasi/src/filesystem.rs b/crates/wasi/src/filesystem.rs index a8e90bf9895e..e7b9190bb68c 100644 --- a/crates/wasi/src/filesystem.rs +++ b/crates/wasi/src/filesystem.rs @@ -490,6 +490,9 @@ impl Descriptor { pub(crate) fn file(&self) -> Result<&File, ErrorCode> { match self { Descriptor::File(f) => Ok(f), + // File-only ops such as advise stay bad-descriptor on a dir + // (wasi-testsuite filesystem-advise). read-via-stream maps Dir + // to is-directory on its own. Descriptor::Dir(_) => Err(ErrorCode::BadDescriptor), } } diff --git a/crates/wasi/src/p2/host/filesystem.rs b/crates/wasi/src/p2/host/filesystem.rs index 3fc37a45ad07..579c74129a05 100644 --- a/crates/wasi/src/p2/host/filesystem.rs +++ b/crates/wasi/src/p2/host/filesystem.rs @@ -367,8 +367,12 @@ impl HostDescriptor for WasiFilesystemCtxView<'_> { fd: Resource, offset: types::Filesize, ) -> FsResult> { - // Trap if fd lookup fails: - let f = self.table.get(&fd)?.file()?; + // Trap if fd lookup fails. A directory is is-directory, not + // bad-descriptor (POSIX EISDIR on read). + let f = match self.table.get(&fd)? { + Descriptor::File(f) => f, + Descriptor::Dir(_) => return Err(ErrorCode::IsDirectory.into()), + }; // Create a stream view for it. let reader: DynInputStream = Box::new(FileInputStream::new(f, offset)); diff --git a/crates/wasi/src/p3/filesystem/host.rs b/crates/wasi/src/p3/filesystem/host.rs index 54437ce23fa0..8eade7cde2f5 100644 --- a/crates/wasi/src/p3/filesystem/host.rs +++ b/crates/wasi/src/p3/filesystem/host.rs @@ -521,8 +521,17 @@ impl types::HostDescriptorWithStore for WasiFilesystem { fd: Resource, offset: Filesize, ) -> wasmtime::Result<(StreamReader, FutureReader>)> { - let file = get_file(store.get().table, &fd)?; - let file = file.clone(); + let file = match get_descriptor(store.get().table, &fd)? { + Descriptor::File(file) => file.clone(), + Descriptor::Dir(_) => { + return Ok(( + StreamReader::new(&mut store, iter::empty())?, + FutureReader::new(&mut store, async move { + wasmtime::error::Ok(Err(ErrorCode::IsDirectory)) + })?, + )); + } + }; let (result_tx, result_rx) = oneshot::channel(); Ok(( StreamReader::new(