diff --git a/crates/wasi-common/cap-std-sync/src/dir.rs b/crates/wasi-common/cap-std-sync/src/dir.rs index a6a6f3f5e1..750bd3786d 100644 --- a/crates/wasi-common/cap-std-sync/src/dir.rs +++ b/crates/wasi-common/cap-std-sync/src/dir.rs @@ -138,6 +138,19 @@ impl WasiDir for Dir { &self, cursor: ReaddirCursor, ) -> Result> + Send>, Error> { + // We need to keep a full-fidelity io Error around to check for a special failure mode + // on windows, but also this function can fail due to an illegal byte sequence in a + // filename, which we can't construct an io Error to represent. + enum ReaddirError { + Io(std::io::Error), + IllegalSequence, + } + impl From for ReaddirError { + fn from(e: std::io::Error) -> ReaddirError { + ReaddirError::Io(e) + } + } + // cap_std's read_dir does not include . and .., we should prepend these. // Why does the Ok contain a tuple? We can't construct a cap_std::fs::DirEntry, and we don't // have enough info to make a ReaddirEntity yet. @@ -145,7 +158,7 @@ impl WasiDir for Dir { let rd = vec![ { let name = ".".to_owned(); - Ok((FileType::Directory, dir_meta.ino(), name)) + Ok::<_, ReaddirError>((FileType::Directory, dir_meta.ino(), name)) }, { let name = "..".to_owned(); @@ -163,24 +176,22 @@ impl WasiDir for Dir { let name = entry .file_name() .into_string() - .map_err(|_| Error::illegal_byte_sequence().context("filename"))?; + .map_err(|_| ReaddirError::IllegalSequence)?; Ok((filetype, inode, name)) }); // On Windows, filter out files like `C:\DumpStack.log.tmp` which we // can't get a full metadata for. #[cfg(windows)] - let entries = entries.filter(|entry: &Result<_, wasi_common::Error>| { + let entries = entries.filter(|entry| { use windows_sys::Win32::Foundation::{ ERROR_ACCESS_DENIED, ERROR_SHARING_VIOLATION, }; - if let Err(err) = entry { - if let Some(err) = err.downcast_ref::() { - if err.raw_os_error() == Some(ERROR_SHARING_VIOLATION as i32) - || err.raw_os_error() == Some(ERROR_ACCESS_DENIED as i32) - { - return false; - } + if let Err(ReaddirError::Io(err)) = entry { + if err.raw_os_error() == Some(ERROR_SHARING_VIOLATION as i32) + || err.raw_os_error() == Some(ERROR_ACCESS_DENIED as i32) + { + return false; } } true @@ -197,7 +208,8 @@ impl WasiDir for Dir { inode, name, }), - Err(e) => Err(e), + Err(ReaddirError::Io(e)) => Err(e.into()), + Err(ReaddirError::IllegalSequence) => Err(Error::illegal_byte_sequence()), }) .skip(u64::from(cursor) as usize); diff --git a/crates/wasi-common/cap-std-sync/src/sched/unix.rs b/crates/wasi-common/cap-std-sync/src/sched/unix.rs index 413288b66c..c53acf1a29 100644 --- a/crates/wasi-common/cap-std-sync/src/sched/unix.rs +++ b/crates/wasi-common/cap-std-sync/src/sched/unix.rs @@ -47,7 +47,7 @@ pub async fn poll_oneoff<'a>(poll: &mut Poll<'a>) -> Result<(), Error> { match rustix::io::poll(&mut pollfds, poll_timeout) { Ok(ready) => break ready, Err(rustix::io::Errno::INTR) => continue, - Err(err) => return Err(err.into()), + Err(err) => return Err(std::io::Error::from(err).into()), } }; if ready > 0 { diff --git a/crates/wasi-common/cap-std-sync/src/sched/windows.rs b/crates/wasi-common/cap-std-sync/src/sched/windows.rs index e903e6bf34..c4ad559cc3 100644 --- a/crates/wasi-common/cap-std-sync/src/sched/windows.rs +++ b/crates/wasi-common/cap-std-sync/src/sched/windows.rs @@ -8,7 +8,6 @@ // We suspect there are bugs in this scheduler, however, we have not // taken the time to improve it. See bug #2880. -use anyhow::Context; use once_cell::sync::Lazy; use std::ops::Deref; use std::sync::mpsc::{self, Receiver, RecvTimeoutError, Sender, TryRecvError}; @@ -73,7 +72,7 @@ pub async fn poll_oneoff_<'a>( if !stdin_read_subs.is_empty() { let state = STDIN_POLL .lock() - .map_err(|_| Error::trap("failed to take lock of STDIN_POLL"))? + .map_err(|_| Error::trap(anyhow::Error::msg("failed to take lock of STDIN_POLL")))? .poll(waitmode)?; for readsub in stdin_read_subs.into_iter() { match state { @@ -167,34 +166,36 @@ impl StdinPoll { // Clean up possibly unread result from previous poll. Ok(_) | Err(TryRecvError::Empty) => {} Err(TryRecvError::Disconnected) => { - return Err(Error::trap("StdinPoll notify_rx channel closed")) + return Err(Error::trap(anyhow::Error::msg( + "StdinPoll notify_rx channel closed", + ))) } } // Notify the worker thread to poll stdin self.request_tx .send(()) - .context("request_tx channel closed")?; + .map_err(|_| Error::trap(anyhow::Error::msg("request_tx channel closed")))?; // Wait for the worker thread to send a readiness notification match wait_mode { WaitMode::Timeout(timeout) => match self.notify_rx.recv_timeout(timeout) { Ok(r) => Ok(r), Err(RecvTimeoutError::Timeout) => Ok(PollState::TimedOut), - Err(RecvTimeoutError::Disconnected) => { - Err(Error::trap("StdinPoll notify_rx channel closed")) - } + Err(RecvTimeoutError::Disconnected) => Err(Error::trap(anyhow::Error::msg( + "StdinPoll notify_rx channel closed", + ))), }, WaitMode::Infinite => self .notify_rx .recv() - .context("StdinPoll notify_rx channel closed"), + .map_err(|_| Error::trap(anyhow::Error::msg("StdinPoll notify_rx channel closed"))), WaitMode::Immediate => match self.notify_rx.try_recv() { Ok(r) => Ok(r), Err(TryRecvError::Empty) => Ok(PollState::NotReady), - Err(TryRecvError::Disconnected) => { - Err(Error::trap("StdinPoll notify_rx channel closed")) - } + Err(TryRecvError::Disconnected) => Err(Error::trap(anyhow::Error::msg( + "StdinPoll notify_rx channel closed", + ))), }, } } diff --git a/crates/wasi-common/cap-std-sync/src/stdio.rs b/crates/wasi-common/cap-std-sync/src/stdio.rs index d53702b03f..9d82348af1 100644 --- a/crates/wasi-common/cap-std-sync/src/stdio.rs +++ b/crates/wasi-common/cap-std-sync/src/stdio.rs @@ -128,7 +128,9 @@ macro_rules! wasi_file_write_impl { } async fn write_vectored<'a>(&mut self, bufs: &[io::IoSlice<'a>]) -> Result { let n = (&*self.0.as_filelike_view::()).write_vectored(bufs)?; - Ok(n.try_into().map_err(|c| Error::range().context(c))?) + Ok(n.try_into().map_err(|_| { + Error::range().context("converting write_vectored total length") + })?) } async fn write_vectored_at<'a>( &mut self, diff --git a/crates/wasi-common/src/error.rs b/crates/wasi-common/src/error.rs index d16909e031..b1a5135bb2 100644 --- a/crates/wasi-common/src/error.rs +++ b/crates/wasi-common/src/error.rs @@ -1,145 +1,15 @@ -//! `wasi_common::Error` is now `anyhow::Error`. +//! wasi-common uses an [`Error`] type which represents either a preview 1 [`Errno`] enum, on +//! [`anyhow::Error`] for trapping execution. //! -//! Snapshots (right now only `wasi_common::snapshots::preview_1`) contains -//! all of the logic for transforming an `Error` into the snapshot's own -//! `Errno`. They may do so by downcasting the error into any of: -//! * `std::io::Error` - these are thrown by `std`, `cap_std`, etc for most of -//! the operations WASI is concerned with. -//! * `wasi_common::ErrorKind` - these are a subset of the Errnos, and are -//! constructed directly by wasi-common or an impl rather than coming from the -//! OS or some library which doesn't know about WASI. -//! * `wiggle::GuestError` -//! * `std::num::TryFromIntError` -//! * `std::str::Utf8Error` -//! and then applying specialized logic to translate each of those into -//! `Errno`s. -//! -//! The `wasi_common::ErrorExt` trait provides human-friendly constructors for -//! the `wasi_common::ErrorKind` variants . -//! -//! If you throw an error that does not downcast to one of those, it will turn -//! into a `wiggle::Trap` and terminate execution. -//! -//! The real value of using `anyhow::Error` here is being able to use -//! `anyhow::Result::context` to aid in debugging of errors. +//! The user can construct an [`Error`] out of an [`Errno`] using the `From`/`Into` traits. +//! They may also use [`Error::trap`] to construct an error that traps execution. The contents +//! can be inspected with [`Error::downcast`] and [`Error::downcast_ref`]. Additional context +//! can be provided with the [`Error::context`] method. This context is only observable with the +//! `Display` and `Debug` impls of the error. -pub use anyhow::{Context, Error}; +pub use crate::snapshots::preview_1::error::{Errno, Error, ErrorExt}; use std::fmt; -/// Internal error type for the `wasi-common` crate. -/// -/// This Contains variants of the WASI `$errno` type that are used internally -/// by the crate, and which aren't one-to-one with a `std::io::ErrorKind` -/// error. -/// -/// When the Rust [io_error_more] feature is stabilized, that will enable -/// us to replace several more of these codes with `std::io::ErrorKind` codes. -/// -/// [io_error_more]: https://doc.rust-lang.org/beta/unstable-book/library-features/io-error-more.html -#[derive(Copy, Clone, Debug, PartialEq, Eq, thiserror::Error)] -#[non_exhaustive] -pub enum ErrorKind { - /// Errno::TooBig: Argument list too long - #[error("TooBig: Argument list too long")] - TooBig, - /// Errno::Badf: Bad file descriptor - #[error("Badf: Bad file descriptor")] - Badf, - /// Errno::Ilseq: Illegal byte sequence - #[error("Ilseq: Illegal byte sequence")] - Ilseq, - /// Errno::Io: I/O error - #[error("Io: I/O error")] - Io, - /// Errno::Nametoolong: Filename too long - #[error("Nametoolong: Filename too long")] - Nametoolong, - /// Errno::Notdir: Not a directory or a symbolic link to a directory. - #[error("Notdir: Not a directory or a symbolic link to a directory")] - Notdir, - /// Errno::Notsup: Not supported, or operation not supported on socket. - #[error("Notsup: Not supported, or operation not supported on socket")] - Notsup, - /// Errno::Overflow: Value too large to be stored in data type. - #[error("Overflow: Value too large to be stored in data type")] - Overflow, - /// Errno::Range: Result too large - #[error("Range: Result too large")] - Range, - /// Errno::Spipe: Invalid seek - #[error("Spipe: Invalid seek")] - Spipe, - /// Errno::Perm: Permission denied - #[error("Permission denied")] - Perm, -} - -pub trait ErrorExt { - fn trap(msg: impl Into) -> Self; - fn not_found() -> Self; - fn too_big() -> Self; - fn badf() -> Self; - fn exist() -> Self; - fn illegal_byte_sequence() -> Self; - fn invalid_argument() -> Self; - fn io() -> Self; - fn name_too_long() -> Self; - fn not_dir() -> Self; - fn not_supported() -> Self; - fn overflow() -> Self; - fn range() -> Self; - fn seek_pipe() -> Self; - fn perm() -> Self; -} - -impl ErrorExt for Error { - fn trap(msg: impl Into) -> Self { - anyhow::anyhow!(msg.into()) - } - fn not_found() -> Self { - std::io::Error::from(std::io::ErrorKind::NotFound).into() - } - fn too_big() -> Self { - ErrorKind::TooBig.into() - } - fn badf() -> Self { - ErrorKind::Badf.into() - } - fn exist() -> Self { - std::io::Error::from(std::io::ErrorKind::AlreadyExists).into() - } - fn illegal_byte_sequence() -> Self { - ErrorKind::Ilseq.into() - } - fn invalid_argument() -> Self { - std::io::Error::from(std::io::ErrorKind::InvalidInput).into() - } - fn io() -> Self { - ErrorKind::Io.into() - } - fn name_too_long() -> Self { - ErrorKind::Nametoolong.into() - } - fn not_dir() -> Self { - ErrorKind::Notdir.into() - } - fn not_supported() -> Self { - ErrorKind::Notsup.into() - } - fn overflow() -> Self { - ErrorKind::Overflow.into() - } - fn range() -> Self { - ErrorKind::Range.into() - } - fn seek_pipe() -> Self { - ErrorKind::Spipe.into() - } - fn perm() -> Self { - ErrorKind::Perm.into() - } -} - /// An error returned from the `proc_exit` host syscall. /// /// Embedders can test if an error returned from wasm is this error, in which diff --git a/crates/wasi-common/src/lib.rs b/crates/wasi-common/src/lib.rs index 6f9e949cbe..4a11e47424 100644 --- a/crates/wasi-common/src/lib.rs +++ b/crates/wasi-common/src/lib.rs @@ -66,7 +66,7 @@ pub use cap_rand::RngCore; pub use clocks::{SystemTimeSpec, WasiClocks, WasiMonotonicClock, WasiSystemClock}; pub use ctx::WasiCtx; pub use dir::WasiDir; -pub use error::{Context, Error, ErrorExt, ErrorKind, I32Exit}; +pub use error::{Error, ErrorExt, I32Exit}; pub use file::WasiFile; pub use sched::{Poll, WasiSched}; pub use string_array::StringArrayError; diff --git a/crates/wasi-common/src/snapshots/preview_0.rs b/crates/wasi-common/src/snapshots/preview_0.rs index c59b7bcfa0..a155bad766 100644 --- a/crates/wasi-common/src/snapshots/preview_0.rs +++ b/crates/wasi-common/src/snapshots/preview_0.rs @@ -5,42 +5,92 @@ use crate::sched::{ }; use crate::snapshots::preview_1::types as snapshot1_types; use crate::snapshots::preview_1::wasi_snapshot_preview1::WasiSnapshotPreview1 as Snapshot1; -use crate::{Error, ErrorExt, WasiCtx}; -use anyhow::Result; +use crate::{ErrorExt, WasiCtx}; use cap_std::time::Duration; use std::collections::HashSet; use std::convert::{TryFrom, TryInto}; use std::io::{IoSlice, IoSliceMut}; use std::ops::Deref; -use tracing::debug; use wiggle::GuestPtr; wiggle::from_witx!({ witx: ["$WASI_ROOT/phases/old/snapshot_0/witx/wasi_unstable.witx"], - errors: { errno => Error }, + errors: { errno => trappable Error }, async: *, wasmtime: false, }); +use types::Error; + +impl ErrorExt for Error { + fn not_found() -> Self { + types::Errno::Noent.into() + } + fn too_big() -> Self { + types::Errno::TooBig.into() + } + fn badf() -> Self { + types::Errno::Badf.into() + } + fn exist() -> Self { + types::Errno::Exist.into() + } + fn illegal_byte_sequence() -> Self { + types::Errno::Ilseq.into() + } + fn invalid_argument() -> Self { + types::Errno::Inval.into() + } + fn io() -> Self { + types::Errno::Io.into() + } + fn name_too_long() -> Self { + types::Errno::Nametoolong.into() + } + fn not_dir() -> Self { + types::Errno::Notdir.into() + } + fn not_supported() -> Self { + types::Errno::Notsup.into() + } + fn overflow() -> Self { + types::Errno::Overflow.into() + } + fn range() -> Self { + types::Errno::Range.into() + } + fn seek_pipe() -> Self { + types::Errno::Spipe.into() + } + fn perm() -> Self { + types::Errno::Perm.into() + } +} + impl wiggle::GuestErrorType for types::Errno { fn success() -> Self { Self::Success } } -impl types::UserErrorConversion for WasiCtx { - fn errno_from_error(&mut self, e: Error) -> Result { - debug!("Error: {:?}", e); - let errno = e.try_into()?; - Ok(errno) +impl From for Error { + fn from(err: wiggle::GuestError) -> Error { + snapshot1_types::Error::from(err).into() } } -impl TryFrom for types::Errno { - type Error = Error; - fn try_from(e: Error) -> Result { - let snapshot1_errno: snapshot1_types::Errno = e.try_into()?; - Ok(snapshot1_errno.into()) +impl From for Error { + fn from(error: snapshot1_types::Error) -> Error { + match error.downcast() { + Ok(errno) => Error::from(types::Errno::from(errno)), + Err(trap) => Error::trap(trap), + } + } +} + +impl From for Error { + fn from(_err: std::num::TryFromIntError) -> Error { + types::Errno::Overflow.into() } } @@ -343,11 +393,13 @@ impl wasi_unstable::WasiUnstable for WasiCtx { argv: &GuestPtr<'a, GuestPtr<'a, u8>>, argv_buf: &GuestPtr<'a, u8>, ) -> Result<(), Error> { - Snapshot1::args_get(self, argv, argv_buf).await + Snapshot1::args_get(self, argv, argv_buf).await?; + Ok(()) } async fn args_sizes_get(&mut self) -> Result<(types::Size, types::Size), Error> { - Snapshot1::args_sizes_get(self).await + let s = Snapshot1::args_sizes_get(self).await?; + Ok(s) } async fn environ_get<'a>( @@ -355,15 +407,18 @@ impl wasi_unstable::WasiUnstable for WasiCtx { environ: &GuestPtr<'a, GuestPtr<'a, u8>>, environ_buf: &GuestPtr<'a, u8>, ) -> Result<(), Error> { - Snapshot1::environ_get(self, environ, environ_buf).await + Snapshot1::environ_get(self, environ, environ_buf).await?; + Ok(()) } async fn environ_sizes_get(&mut self) -> Result<(types::Size, types::Size), Error> { - Snapshot1::environ_sizes_get(self).await + let s = Snapshot1::environ_sizes_get(self).await?; + Ok(s) } async fn clock_res_get(&mut self, id: types::Clockid) -> Result { - Snapshot1::clock_res_get(self, id.into()).await + let t = Snapshot1::clock_res_get(self, id.into()).await?; + Ok(t) } async fn clock_time_get( @@ -371,7 +426,8 @@ impl wasi_unstable::WasiUnstable for WasiCtx { id: types::Clockid, precision: types::Timestamp, ) -> Result { - Snapshot1::clock_time_get(self, id.into(), precision).await + let t = Snapshot1::clock_time_get(self, id.into(), precision).await?; + Ok(t) } async fn fd_advise( @@ -381,7 +437,8 @@ impl wasi_unstable::WasiUnstable for WasiCtx { len: types::Filesize, advice: types::Advice, ) -> Result<(), Error> { - Snapshot1::fd_advise(self, fd.into(), offset, len, advice.into()).await + Snapshot1::fd_advise(self, fd.into(), offset, len, advice.into()).await?; + Ok(()) } async fn fd_allocate( @@ -390,15 +447,18 @@ impl wasi_unstable::WasiUnstable for WasiCtx { offset: types::Filesize, len: types::Filesize, ) -> Result<(), Error> { - Snapshot1::fd_allocate(self, fd.into(), offset, len).await + Snapshot1::fd_allocate(self, fd.into(), offset, len).await?; + Ok(()) } async fn fd_close(&mut self, fd: types::Fd) -> Result<(), Error> { - Snapshot1::fd_close(self, fd.into()).await + Snapshot1::fd_close(self, fd.into()).await?; + Ok(()) } async fn fd_datasync(&mut self, fd: types::Fd) -> Result<(), Error> { - Snapshot1::fd_datasync(self, fd.into()).await + Snapshot1::fd_datasync(self, fd.into()).await?; + Ok(()) } async fn fd_fdstat_get(&mut self, fd: types::Fd) -> Result { @@ -410,7 +470,8 @@ impl wasi_unstable::WasiUnstable for WasiCtx { fd: types::Fd, flags: types::Fdflags, ) -> Result<(), Error> { - Snapshot1::fd_fdstat_set_flags(self, fd.into(), flags.into()).await + Snapshot1::fd_fdstat_set_flags(self, fd.into(), flags.into()).await?; + Ok(()) } async fn fd_fdstat_set_rights( @@ -425,7 +486,8 @@ impl wasi_unstable::WasiUnstable for WasiCtx { fs_rights_base.into(), fs_rights_inheriting.into(), ) - .await + .await?; + Ok(()) } async fn fd_filestat_get(&mut self, fd: types::Fd) -> Result { @@ -437,7 +499,8 @@ impl wasi_unstable::WasiUnstable for WasiCtx { fd: types::Fd, size: types::Filesize, ) -> Result<(), Error> { - Snapshot1::fd_filestat_set_size(self, fd.into(), size).await + Snapshot1::fd_filestat_set_size(self, fd.into(), size).await?; + Ok(()) } async fn fd_filestat_set_times( @@ -447,7 +510,8 @@ impl wasi_unstable::WasiUnstable for WasiCtx { mtim: types::Timestamp, fst_flags: types::Fstflags, ) -> Result<(), Error> { - Snapshot1::fd_filestat_set_times(self, fd.into(), atim, mtim, fst_flags.into()).await + Snapshot1::fd_filestat_set_times(self, fd.into(), atim, mtim, fst_flags.into()).await?; + Ok(()) } // NOTE on fd_read, fd_pread, fd_write, fd_pwrite implementations: @@ -594,11 +658,13 @@ impl wasi_unstable::WasiUnstable for WasiCtx { path: &GuestPtr<'a, u8>, path_max_len: types::Size, ) -> Result<(), Error> { - Snapshot1::fd_prestat_dir_name(self, fd.into(), path, path_max_len).await + Snapshot1::fd_prestat_dir_name(self, fd.into(), path, path_max_len).await?; + Ok(()) } async fn fd_renumber(&mut self, from: types::Fd, to: types::Fd) -> Result<(), Error> { - Snapshot1::fd_renumber(self, from.into(), to.into()).await + Snapshot1::fd_renumber(self, from.into(), to.into()).await?; + Ok(()) } async fn fd_seek( @@ -607,15 +673,16 @@ impl wasi_unstable::WasiUnstable for WasiCtx { offset: types::Filedelta, whence: types::Whence, ) -> Result { - Snapshot1::fd_seek(self, fd.into(), offset, whence.into()).await + Ok(Snapshot1::fd_seek(self, fd.into(), offset, whence.into()).await?) } async fn fd_sync(&mut self, fd: types::Fd) -> Result<(), Error> { - Snapshot1::fd_sync(self, fd.into()).await + Snapshot1::fd_sync(self, fd.into()).await?; + Ok(()) } async fn fd_tell(&mut self, fd: types::Fd) -> Result { - Snapshot1::fd_tell(self, fd.into()).await + Ok(Snapshot1::fd_tell(self, fd.into()).await?) } async fn fd_readdir<'a>( @@ -625,7 +692,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { buf_len: types::Size, cookie: types::Dircookie, ) -> Result { - Snapshot1::fd_readdir(self, fd.into(), buf, buf_len, cookie).await + Ok(Snapshot1::fd_readdir(self, fd.into(), buf, buf_len, cookie).await?) } async fn path_create_directory<'a>( @@ -633,7 +700,8 @@ impl wasi_unstable::WasiUnstable for WasiCtx { dirfd: types::Fd, path: &GuestPtr<'a, str>, ) -> Result<(), Error> { - Snapshot1::path_create_directory(self, dirfd.into(), path).await + Snapshot1::path_create_directory(self, dirfd.into(), path).await?; + Ok(()) } async fn path_filestat_get<'a>( @@ -667,7 +735,8 @@ impl wasi_unstable::WasiUnstable for WasiCtx { mtim, fst_flags.into(), ) - .await + .await?; + Ok(()) } async fn path_link<'a>( @@ -686,7 +755,8 @@ impl wasi_unstable::WasiUnstable for WasiCtx { target_fd.into(), target_path, ) - .await + .await?; + Ok(()) } async fn path_open<'a>( @@ -720,7 +790,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { buf: &GuestPtr<'a, u8>, buf_len: types::Size, ) -> Result { - Snapshot1::path_readlink(self, dirfd.into(), path, buf, buf_len).await + Ok(Snapshot1::path_readlink(self, dirfd.into(), path, buf, buf_len).await?) } async fn path_remove_directory<'a>( @@ -728,7 +798,8 @@ impl wasi_unstable::WasiUnstable for WasiCtx { dirfd: types::Fd, path: &GuestPtr<'a, str>, ) -> Result<(), Error> { - Snapshot1::path_remove_directory(self, dirfd.into(), path).await + Snapshot1::path_remove_directory(self, dirfd.into(), path).await?; + Ok(()) } async fn path_rename<'a>( @@ -738,7 +809,8 @@ impl wasi_unstable::WasiUnstable for WasiCtx { dest_fd: types::Fd, dest_path: &GuestPtr<'a, str>, ) -> Result<(), Error> { - Snapshot1::path_rename(self, src_fd.into(), src_path, dest_fd.into(), dest_path).await + Snapshot1::path_rename(self, src_fd.into(), src_path, dest_fd.into(), dest_path).await?; + Ok(()) } async fn path_symlink<'a>( @@ -747,7 +819,8 @@ impl wasi_unstable::WasiUnstable for WasiCtx { dirfd: types::Fd, dest_path: &GuestPtr<'a, str>, ) -> Result<(), Error> { - Snapshot1::path_symlink(self, src_path, dirfd.into(), dest_path).await + Snapshot1::path_symlink(self, src_path, dirfd.into(), dest_path).await?; + Ok(()) } async fn path_unlink_file<'a>( @@ -755,7 +828,8 @@ impl wasi_unstable::WasiUnstable for WasiCtx { dirfd: types::Fd, path: &GuestPtr<'a, str>, ) -> Result<(), Error> { - Snapshot1::path_unlink_file(self, dirfd.into(), path).await + Snapshot1::path_unlink_file(self, dirfd.into(), path).await?; + Ok(()) } // NOTE on poll_oneoff implementation: @@ -901,7 +975,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { }, Err(e) => types::Event { userdata, - error: e.try_into().expect("non-trapping"), + error: types::Errno::from(e.downcast().map_err(Error::trap)?), type_, fd_readwrite: fd_readwrite_empty(), }, @@ -921,7 +995,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { }, Err(e) => types::Event { userdata, - error: e.try_into()?, + error: types::Errno::from(e.downcast().map_err(Error::trap)?), type_, fd_readwrite: fd_readwrite_empty(), }, @@ -933,7 +1007,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { userdata, error: match r { Ok(()) => types::Errno::Success, - Err(e) => e.try_into()?, + Err(e) => types::Errno::from(e.downcast().map_err(Error::trap)?), }, type_, fd_readwrite: fd_readwrite_empty(), @@ -950,11 +1024,12 @@ impl wasi_unstable::WasiUnstable for WasiCtx { } async fn proc_raise(&mut self, _sig: types::Signal) -> Result<(), Error> { - Err(Error::trap("proc_raise unsupported")) + Err(Error::trap(anyhow::Error::msg("proc_raise unsupported"))) } async fn sched_yield(&mut self) -> Result<(), Error> { - Snapshot1::sched_yield(self).await + Snapshot1::sched_yield(self).await?; + Ok(()) } async fn random_get<'a>( @@ -962,7 +1037,8 @@ impl wasi_unstable::WasiUnstable for WasiCtx { buf: &GuestPtr<'a, u8>, buf_len: types::Size, ) -> Result<(), Error> { - Snapshot1::random_get(self, buf, buf_len).await + Snapshot1::random_get(self, buf, buf_len).await?; + Ok(()) } async fn sock_recv<'a>( @@ -971,7 +1047,7 @@ impl wasi_unstable::WasiUnstable for WasiCtx { _ri_data: &types::IovecArray<'a>, _ri_flags: types::Riflags, ) -> Result<(types::Size, types::Roflags), Error> { - Err(Error::trap("sock_recv unsupported")) + Err(Error::trap(anyhow::Error::msg("sock_recv unsupported"))) } async fn sock_send<'a>( @@ -980,11 +1056,11 @@ impl wasi_unstable::WasiUnstable for WasiCtx { _si_data: &types::CiovecArray<'a>, _si_flags: types::Siflags, ) -> Result { - Err(Error::trap("sock_send unsupported")) + Err(Error::trap(anyhow::Error::msg("sock_send unsupported"))) } async fn sock_shutdown(&mut self, _fd: types::Fd, _how: types::Sdflags) -> Result<(), Error> { - Err(Error::trap("sock_shutdown unsupported")) + Err(Error::trap(anyhow::Error::msg("sock_shutdown unsupported"))) } } diff --git a/crates/wasi-common/src/snapshots/preview_1.rs b/crates/wasi-common/src/snapshots/preview_1.rs index cdc4df406f..37abf1f6f8 100644 --- a/crates/wasi-common/src/snapshots/preview_1.rs +++ b/crates/wasi-common/src/snapshots/preview_1.rs @@ -8,19 +8,20 @@ use crate::{ subscription::{RwEventFlags, SubscriptionResult}, Poll, Userdata, }, - Error, ErrorExt, ErrorKind, I32Exit, SystemTimeSpec, WasiCtx, + I32Exit, SystemTimeSpec, WasiCtx, }; -use anyhow::{anyhow, Context, Result}; use cap_std::time::{Duration, SystemClock}; use std::convert::{TryFrom, TryInto}; use std::io::{IoSlice, IoSliceMut}; use std::ops::{Deref, DerefMut}; -use tracing::debug; use wiggle::GuestPtr; +pub mod error; +use error::{Error, ErrorExt}; + wiggle::from_witx!({ witx: ["$WASI_ROOT/phases/snapshot/witx/wasi_snapshot_preview1.witx"], - errors: { errno => Error }, + errors: { errno => trappable Error }, // Note: not every function actually needs to be async, however, nearly all of them do, and // keeping that set the same in this macro and the wasmtime_wiggle / lucet_wiggle macros is // tedious, and there is no cost to having a sync function be async in this case. @@ -34,235 +35,6 @@ impl wiggle::GuestErrorType for types::Errno { } } -impl types::UserErrorConversion for WasiCtx { - fn errno_from_error(&mut self, e: Error) -> Result { - debug!("Error: {:?}", e); - let errno = e.try_into()?; - Ok(errno) - } -} - -impl TryFrom for types::Errno { - type Error = Error; - fn try_from(e: Error) -> Result { - use types::Errno; - if e.is::() { - let e = e.downcast::().unwrap(); - Ok(e.into()) - } else if e.is::() { - let e = e.downcast::().unwrap(); - e.try_into() - } else if e.is::() { - let e = e.downcast::().unwrap(); - Ok(e.into()) - } else if e.is::() { - Ok(Errno::Overflow) - } else if e.is::() { - Ok(Errno::Ilseq) - } else { - Err(e) - } - } -} - -impl From for types::Errno { - fn from(e: ErrorKind) -> types::Errno { - use types::Errno; - match e { - ErrorKind::TooBig => Errno::TooBig, - ErrorKind::Badf => Errno::Badf, - ErrorKind::Ilseq => Errno::Ilseq, - ErrorKind::Io => Errno::Io, - ErrorKind::Nametoolong => Errno::Nametoolong, - ErrorKind::Notdir => Errno::Notdir, - ErrorKind::Notsup => Errno::Notsup, - ErrorKind::Overflow => Errno::Overflow, - ErrorKind::Range => Errno::Range, - ErrorKind::Spipe => Errno::Spipe, - ErrorKind::Perm => Errno::Perm, - } - } -} - -impl From for types::Errno { - fn from(err: wiggle::GuestError) -> Self { - use wiggle::GuestError::*; - match err { - InvalidFlagValue { .. } => Self::Inval, - InvalidEnumValue { .. } => Self::Inval, - PtrOverflow { .. } => Self::Fault, - PtrOutOfBounds { .. } => Self::Fault, - PtrNotAligned { .. } => Self::Inval, - PtrBorrowed { .. } => Self::Fault, - InvalidUtf8 { .. } => Self::Ilseq, - TryFromIntError { .. } => Self::Overflow, - InFunc { err, .. } => types::Errno::from(*err), - SliceLengthsDiffer { .. } => Self::Fault, - BorrowCheckerOutOfHandles { .. } => Self::Fault, - } - } -} - -impl TryFrom for types::Errno { - type Error = Error; - fn try_from(err: std::io::Error) -> Result { - #[cfg(unix)] - fn raw_error_code(err: &std::io::Error) -> Option { - use rustix::io::Errno; - match Errno::from_io_error(err) { - Some(Errno::AGAIN) => Some(types::Errno::Again), - Some(Errno::PIPE) => Some(types::Errno::Pipe), - Some(Errno::PERM) => Some(types::Errno::Perm), - Some(Errno::NOENT) => Some(types::Errno::Noent), - Some(Errno::NOMEM) => Some(types::Errno::Nomem), - Some(Errno::TOOBIG) => Some(types::Errno::TooBig), - Some(Errno::IO) => Some(types::Errno::Io), - Some(Errno::BADF) => Some(types::Errno::Badf), - Some(Errno::BUSY) => Some(types::Errno::Busy), - Some(Errno::ACCESS) => Some(types::Errno::Acces), - Some(Errno::FAULT) => Some(types::Errno::Fault), - Some(Errno::NOTDIR) => Some(types::Errno::Notdir), - Some(Errno::ISDIR) => Some(types::Errno::Isdir), - Some(Errno::INVAL) => Some(types::Errno::Inval), - Some(Errno::EXIST) => Some(types::Errno::Exist), - Some(Errno::FBIG) => Some(types::Errno::Fbig), - Some(Errno::NOSPC) => Some(types::Errno::Nospc), - Some(Errno::SPIPE) => Some(types::Errno::Spipe), - Some(Errno::MFILE) => Some(types::Errno::Mfile), - Some(Errno::MLINK) => Some(types::Errno::Mlink), - Some(Errno::NAMETOOLONG) => Some(types::Errno::Nametoolong), - Some(Errno::NFILE) => Some(types::Errno::Nfile), - Some(Errno::NOTEMPTY) => Some(types::Errno::Notempty), - Some(Errno::LOOP) => Some(types::Errno::Loop), - Some(Errno::OVERFLOW) => Some(types::Errno::Overflow), - Some(Errno::ILSEQ) => Some(types::Errno::Ilseq), - Some(Errno::NOTSUP) => Some(types::Errno::Notsup), - Some(Errno::ADDRINUSE) => Some(types::Errno::Addrinuse), - Some(Errno::CANCELED) => Some(types::Errno::Canceled), - Some(Errno::ADDRNOTAVAIL) => Some(types::Errno::Addrnotavail), - Some(Errno::AFNOSUPPORT) => Some(types::Errno::Afnosupport), - Some(Errno::ALREADY) => Some(types::Errno::Already), - Some(Errno::CONNABORTED) => Some(types::Errno::Connaborted), - Some(Errno::CONNREFUSED) => Some(types::Errno::Connrefused), - Some(Errno::CONNRESET) => Some(types::Errno::Connreset), - Some(Errno::DESTADDRREQ) => Some(types::Errno::Destaddrreq), - Some(Errno::DQUOT) => Some(types::Errno::Dquot), - Some(Errno::HOSTUNREACH) => Some(types::Errno::Hostunreach), - Some(Errno::INPROGRESS) => Some(types::Errno::Inprogress), - Some(Errno::INTR) => Some(types::Errno::Intr), - Some(Errno::ISCONN) => Some(types::Errno::Isconn), - Some(Errno::MSGSIZE) => Some(types::Errno::Msgsize), - Some(Errno::NETDOWN) => Some(types::Errno::Netdown), - Some(Errno::NETRESET) => Some(types::Errno::Netreset), - Some(Errno::NETUNREACH) => Some(types::Errno::Netunreach), - Some(Errno::NOBUFS) => Some(types::Errno::Nobufs), - Some(Errno::NOPROTOOPT) => Some(types::Errno::Noprotoopt), - Some(Errno::NOTCONN) => Some(types::Errno::Notconn), - Some(Errno::NOTSOCK) => Some(types::Errno::Notsock), - Some(Errno::PROTONOSUPPORT) => Some(types::Errno::Protonosupport), - Some(Errno::PROTOTYPE) => Some(types::Errno::Prototype), - Some(Errno::STALE) => Some(types::Errno::Stale), - Some(Errno::TIMEDOUT) => Some(types::Errno::Timedout), - - // On some platforms, these have the same value as other errno values. - #[allow(unreachable_patterns)] - Some(Errno::WOULDBLOCK) => Some(types::Errno::Again), - #[allow(unreachable_patterns)] - Some(Errno::OPNOTSUPP) => Some(types::Errno::Notsup), - - _ => None, - } - } - #[cfg(windows)] - fn raw_error_code(err: &std::io::Error) -> Option { - use windows_sys::Win32::Foundation; - use windows_sys::Win32::Networking::WinSock; - - match err.raw_os_error().map(|code| code as u32) { - Some(Foundation::ERROR_BAD_ENVIRONMENT) => return Some(types::Errno::TooBig), - Some(Foundation::ERROR_FILE_NOT_FOUND) => return Some(types::Errno::Noent), - Some(Foundation::ERROR_PATH_NOT_FOUND) => return Some(types::Errno::Noent), - Some(Foundation::ERROR_TOO_MANY_OPEN_FILES) => return Some(types::Errno::Nfile), - Some(Foundation::ERROR_ACCESS_DENIED) => return Some(types::Errno::Acces), - Some(Foundation::ERROR_SHARING_VIOLATION) => return Some(types::Errno::Acces), - Some(Foundation::ERROR_PRIVILEGE_NOT_HELD) => return Some(types::Errno::Perm), - Some(Foundation::ERROR_INVALID_HANDLE) => return Some(types::Errno::Badf), - Some(Foundation::ERROR_INVALID_NAME) => return Some(types::Errno::Noent), - Some(Foundation::ERROR_NOT_ENOUGH_MEMORY) => return Some(types::Errno::Nomem), - Some(Foundation::ERROR_OUTOFMEMORY) => return Some(types::Errno::Nomem), - Some(Foundation::ERROR_DIR_NOT_EMPTY) => return Some(types::Errno::Notempty), - Some(Foundation::ERROR_NOT_READY) => return Some(types::Errno::Busy), - Some(Foundation::ERROR_BUSY) => return Some(types::Errno::Busy), - Some(Foundation::ERROR_NOT_SUPPORTED) => return Some(types::Errno::Notsup), - Some(Foundation::ERROR_FILE_EXISTS) => return Some(types::Errno::Exist), - Some(Foundation::ERROR_BROKEN_PIPE) => return Some(types::Errno::Pipe), - Some(Foundation::ERROR_BUFFER_OVERFLOW) => return Some(types::Errno::Nametoolong), - Some(Foundation::ERROR_NOT_A_REPARSE_POINT) => return Some(types::Errno::Inval), - Some(Foundation::ERROR_NEGATIVE_SEEK) => return Some(types::Errno::Inval), - Some(Foundation::ERROR_DIRECTORY) => return Some(types::Errno::Notdir), - Some(Foundation::ERROR_ALREADY_EXISTS) => return Some(types::Errno::Exist), - Some(Foundation::ERROR_STOPPED_ON_SYMLINK) => return Some(types::Errno::Loop), - Some(Foundation::ERROR_DIRECTORY_NOT_SUPPORTED) => { - return Some(types::Errno::Isdir) - } - _ => {} - } - - match err.raw_os_error() { - Some(WinSock::WSAEWOULDBLOCK) => Some(types::Errno::Again), - Some(WinSock::WSAECANCELLED) => Some(types::Errno::Canceled), - Some(WinSock::WSA_E_CANCELLED) => Some(types::Errno::Canceled), - Some(WinSock::WSAEBADF) => Some(types::Errno::Badf), - Some(WinSock::WSAEFAULT) => Some(types::Errno::Fault), - Some(WinSock::WSAEINVAL) => Some(types::Errno::Inval), - Some(WinSock::WSAEMFILE) => Some(types::Errno::Mfile), - Some(WinSock::WSAENAMETOOLONG) => Some(types::Errno::Nametoolong), - Some(WinSock::WSAENOTEMPTY) => Some(types::Errno::Notempty), - Some(WinSock::WSAELOOP) => Some(types::Errno::Loop), - Some(WinSock::WSAEOPNOTSUPP) => Some(types::Errno::Notsup), - Some(WinSock::WSAEADDRINUSE) => Some(types::Errno::Addrinuse), - Some(WinSock::WSAEACCES) => Some(types::Errno::Acces), - Some(WinSock::WSAEADDRNOTAVAIL) => Some(types::Errno::Addrnotavail), - Some(WinSock::WSAEAFNOSUPPORT) => Some(types::Errno::Afnosupport), - Some(WinSock::WSAEALREADY) => Some(types::Errno::Already), - Some(WinSock::WSAECONNABORTED) => Some(types::Errno::Connaborted), - Some(WinSock::WSAECONNREFUSED) => Some(types::Errno::Connrefused), - Some(WinSock::WSAECONNRESET) => Some(types::Errno::Connreset), - Some(WinSock::WSAEDESTADDRREQ) => Some(types::Errno::Destaddrreq), - Some(WinSock::WSAEDQUOT) => Some(types::Errno::Dquot), - Some(WinSock::WSAEHOSTUNREACH) => Some(types::Errno::Hostunreach), - Some(WinSock::WSAEINPROGRESS) => Some(types::Errno::Inprogress), - Some(WinSock::WSAEINTR) => Some(types::Errno::Intr), - Some(WinSock::WSAEISCONN) => Some(types::Errno::Isconn), - Some(WinSock::WSAEMSGSIZE) => Some(types::Errno::Msgsize), - Some(WinSock::WSAENETDOWN) => Some(types::Errno::Netdown), - Some(WinSock::WSAENETRESET) => Some(types::Errno::Netreset), - Some(WinSock::WSAENETUNREACH) => Some(types::Errno::Netunreach), - Some(WinSock::WSAENOBUFS) => Some(types::Errno::Nobufs), - Some(WinSock::WSAENOPROTOOPT) => Some(types::Errno::Noprotoopt), - Some(WinSock::WSAENOTCONN) => Some(types::Errno::Notconn), - Some(WinSock::WSAENOTSOCK) => Some(types::Errno::Notsock), - Some(WinSock::WSAEPROTONOSUPPORT) => Some(types::Errno::Protonosupport), - Some(WinSock::WSAEPROTOTYPE) => Some(types::Errno::Prototype), - Some(WinSock::WSAESTALE) => Some(types::Errno::Stale), - Some(WinSock::WSAETIMEDOUT) => Some(types::Errno::Timedout), - _ => None, - } - } - - match raw_error_code(&err) { - Some(errno) => Ok(errno), - None => match err.kind() { - std::io::ErrorKind::NotFound => Ok(types::Errno::Noent), - std::io::ErrorKind::PermissionDenied => Ok(types::Errno::Perm), - std::io::ErrorKind::AlreadyExists => Ok(types::Errno::Exist), - std::io::ErrorKind::InvalidInput => Ok(types::Errno::Inval), - _ => Err(anyhow::anyhow!(err).context(format!("Unknown OS error"))), - }, - } - } -} - #[wiggle::async_trait] impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { async fn args_get<'b>( @@ -311,7 +83,9 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { let now = self.clocks.system.now(precision).into_std(); let d = now .duration_since(std::time::SystemTime::UNIX_EPOCH) - .map_err(|_| Error::trap("current time before unix epoch"))?; + .map_err(|_| { + Error::trap(anyhow::Error::msg("current time before unix epoch")) + })?; Ok(d.as_nanos().try_into()?) } types::Clockid::Monotonic => { @@ -489,8 +263,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { let set_mtim = fst_flags.contains(types::Fstflags::MTIM); let set_mtim_now = fst_flags.contains(types::Fstflags::MTIM_NOW); - let atim = systimespec(set_atim, atim, set_atim_now).context("atim")?; - let mtim = systimespec(set_mtim, mtim, set_mtim_now).context("mtim")?; + let atim = systimespec(set_atim, atim, set_atim_now).map_err(|e| e.context("atim"))?; + let mtim = systimespec(set_mtim, mtim, set_mtim_now).map_err(|e| e.context("mtim"))?; if table.is::(fd) { table @@ -839,8 +613,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { let set_mtim = fst_flags.contains(types::Fstflags::MTIM); let set_mtim_now = fst_flags.contains(types::Fstflags::MTIM_NOW); - let atim = systimespec(set_atim, atim, set_atim_now).context("atim")?; - let mtim = systimespec(set_mtim, mtim, set_mtim_now).context("mtim")?; + let atim = systimespec(set_atim, atim, set_atim_now).map_err(|e| e.context("atim"))?; + let mtim = systimespec(set_mtim, mtim, set_mtim_now).map_err(|e| e.context("mtim"))?; self.table() .get_dir(u32::from(dirfd))? .get_cap(DirCaps::PATH_FILESTAT_SET_TIMES)? @@ -1189,7 +963,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { }, Err(e) => types::Event { userdata, - error: e.try_into().expect("non-trapping"), + error: e.downcast().map_err(Error::trap)?, type_, fd_readwrite: fd_readwrite_empty(), }, @@ -1209,7 +983,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { }, Err(e) => types::Event { userdata, - error: e.try_into()?, + error: e.downcast().map_err(Error::trap)?, type_, fd_readwrite: fd_readwrite_empty(), }, @@ -1221,7 +995,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { userdata, error: match r { Ok(()) => types::Errno::Success, - Err(e) => e.try_into()?, + Err(e) => e.downcast().map_err(Error::trap)?, }, type_, fd_readwrite: fd_readwrite_empty(), @@ -1238,12 +1012,12 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { if status < 126 { I32Exit(status as i32).into() } else { - anyhow!("exit with invalid exit status outside of [0..126)") + anyhow::Error::msg("exit with invalid exit status outside of [0..126)") } } async fn proc_raise(&mut self, _sig: types::Signal) -> Result<(), Error> { - Err(Error::trap("proc_raise unsupported")) + Err(Error::trap(anyhow::Error::msg("proc_raise unsupported"))) } async fn sched_yield(&mut self) -> Result<(), Error> { diff --git a/crates/wasi-common/src/snapshots/preview_1/error.rs b/crates/wasi-common/src/snapshots/preview_1/error.rs new file mode 100644 index 0000000000..157cdcc83c --- /dev/null +++ b/crates/wasi-common/src/snapshots/preview_1/error.rs @@ -0,0 +1,255 @@ +pub use super::types::{Errno, Error}; + +pub trait ErrorExt { + fn not_found() -> Self; + fn too_big() -> Self; + fn badf() -> Self; + fn exist() -> Self; + fn illegal_byte_sequence() -> Self; + fn invalid_argument() -> Self; + fn io() -> Self; + fn name_too_long() -> Self; + fn not_dir() -> Self; + fn not_supported() -> Self; + fn overflow() -> Self; + fn range() -> Self; + fn seek_pipe() -> Self; + fn perm() -> Self; +} + +impl ErrorExt for Error { + fn not_found() -> Self { + Errno::Noent.into() + } + fn too_big() -> Self { + Errno::TooBig.into() + } + fn badf() -> Self { + Errno::Badf.into() + } + fn exist() -> Self { + Errno::Exist.into() + } + fn illegal_byte_sequence() -> Self { + Errno::Ilseq.into() + } + fn invalid_argument() -> Self { + Errno::Inval.into() + } + fn io() -> Self { + Errno::Io.into() + } + fn name_too_long() -> Self { + Errno::Nametoolong.into() + } + fn not_dir() -> Self { + Errno::Notdir.into() + } + fn not_supported() -> Self { + Errno::Notsup.into() + } + fn overflow() -> Self { + Errno::Overflow.into() + } + fn range() -> Self { + Errno::Range.into() + } + fn seek_pipe() -> Self { + Errno::Spipe.into() + } + fn perm() -> Self { + Errno::Perm.into() + } +} + +#[cfg(unix)] +fn from_raw_os_error(err: Option) -> Option { + use rustix::io::Errno as RustixErrno; + if err.is_none() { + return None; + } + Some(match RustixErrno::from_raw_os_error(err.unwrap()) { + RustixErrno::AGAIN => Errno::Again.into(), + RustixErrno::PIPE => Errno::Pipe.into(), + RustixErrno::PERM => Errno::Perm.into(), + RustixErrno::NOENT => Errno::Noent.into(), + RustixErrno::NOMEM => Errno::Nomem.into(), + RustixErrno::TOOBIG => Errno::TooBig.into(), + RustixErrno::IO => Errno::Io.into(), + RustixErrno::BADF => Errno::Badf.into(), + RustixErrno::BUSY => Errno::Busy.into(), + RustixErrno::ACCESS => Errno::Acces.into(), + RustixErrno::FAULT => Errno::Fault.into(), + RustixErrno::NOTDIR => Errno::Notdir.into(), + RustixErrno::ISDIR => Errno::Isdir.into(), + RustixErrno::INVAL => Errno::Inval.into(), + RustixErrno::EXIST => Errno::Exist.into(), + RustixErrno::FBIG => Errno::Fbig.into(), + RustixErrno::NOSPC => Errno::Nospc.into(), + RustixErrno::SPIPE => Errno::Spipe.into(), + RustixErrno::MFILE => Errno::Mfile.into(), + RustixErrno::MLINK => Errno::Mlink.into(), + RustixErrno::NAMETOOLONG => Errno::Nametoolong.into(), + RustixErrno::NFILE => Errno::Nfile.into(), + RustixErrno::NOTEMPTY => Errno::Notempty.into(), + RustixErrno::LOOP => Errno::Loop.into(), + RustixErrno::OVERFLOW => Errno::Overflow.into(), + RustixErrno::ILSEQ => Errno::Ilseq.into(), + RustixErrno::NOTSUP => Errno::Notsup.into(), + RustixErrno::ADDRINUSE => Errno::Addrinuse.into(), + RustixErrno::CANCELED => Errno::Canceled.into(), + RustixErrno::ADDRNOTAVAIL => Errno::Addrnotavail.into(), + RustixErrno::AFNOSUPPORT => Errno::Afnosupport.into(), + RustixErrno::ALREADY => Errno::Already.into(), + RustixErrno::CONNABORTED => Errno::Connaborted.into(), + RustixErrno::CONNREFUSED => Errno::Connrefused.into(), + RustixErrno::CONNRESET => Errno::Connreset.into(), + RustixErrno::DESTADDRREQ => Errno::Destaddrreq.into(), + RustixErrno::DQUOT => Errno::Dquot.into(), + RustixErrno::HOSTUNREACH => Errno::Hostunreach.into(), + RustixErrno::INPROGRESS => Errno::Inprogress.into(), + RustixErrno::INTR => Errno::Intr.into(), + RustixErrno::ISCONN => Errno::Isconn.into(), + RustixErrno::MSGSIZE => Errno::Msgsize.into(), + RustixErrno::NETDOWN => Errno::Netdown.into(), + RustixErrno::NETRESET => Errno::Netreset.into(), + RustixErrno::NETUNREACH => Errno::Netunreach.into(), + RustixErrno::NOBUFS => Errno::Nobufs.into(), + RustixErrno::NOPROTOOPT => Errno::Noprotoopt.into(), + RustixErrno::NOTCONN => Errno::Notconn.into(), + RustixErrno::NOTSOCK => Errno::Notsock.into(), + RustixErrno::PROTONOSUPPORT => Errno::Protonosupport.into(), + RustixErrno::PROTOTYPE => Errno::Prototype.into(), + RustixErrno::STALE => Errno::Stale.into(), + RustixErrno::TIMEDOUT => Errno::Timedout.into(), + + // On some platforms.into(), these have the same value as other errno values. + #[allow(unreachable_patterns)] + RustixErrno::WOULDBLOCK => Errno::Again.into(), + #[allow(unreachable_patterns)] + RustixErrno::OPNOTSUPP => Errno::Notsup.into(), + + _ => return None, + }) +} +#[cfg(windows)] +fn from_raw_os_error(raw_os_error: Option) -> Option { + use windows_sys::Win32::Foundation; + use windows_sys::Win32::Networking::WinSock; + + match raw_os_error.map(|code| code as u32) { + Some(Foundation::ERROR_BAD_ENVIRONMENT) => return Some(Errno::TooBig.into()), + Some(Foundation::ERROR_FILE_NOT_FOUND) => return Some(Errno::Noent.into()), + Some(Foundation::ERROR_PATH_NOT_FOUND) => return Some(Errno::Noent.into()), + Some(Foundation::ERROR_TOO_MANY_OPEN_FILES) => return Some(Errno::Nfile.into()), + Some(Foundation::ERROR_ACCESS_DENIED) => return Some(Errno::Acces.into()), + Some(Foundation::ERROR_SHARING_VIOLATION) => return Some(Errno::Acces.into()), + Some(Foundation::ERROR_PRIVILEGE_NOT_HELD) => return Some(Errno::Perm.into()), + Some(Foundation::ERROR_INVALID_HANDLE) => return Some(Errno::Badf.into()), + Some(Foundation::ERROR_INVALID_NAME) => return Some(Errno::Noent.into()), + Some(Foundation::ERROR_NOT_ENOUGH_MEMORY) => return Some(Errno::Nomem.into()), + Some(Foundation::ERROR_OUTOFMEMORY) => return Some(Errno::Nomem.into()), + Some(Foundation::ERROR_DIR_NOT_EMPTY) => return Some(Errno::Notempty.into()), + Some(Foundation::ERROR_NOT_READY) => return Some(Errno::Busy.into()), + Some(Foundation::ERROR_BUSY) => return Some(Errno::Busy.into()), + Some(Foundation::ERROR_NOT_SUPPORTED) => return Some(Errno::Notsup.into()), + Some(Foundation::ERROR_FILE_EXISTS) => return Some(Errno::Exist.into()), + Some(Foundation::ERROR_BROKEN_PIPE) => return Some(Errno::Pipe.into()), + Some(Foundation::ERROR_BUFFER_OVERFLOW) => return Some(Errno::Nametoolong.into()), + Some(Foundation::ERROR_NOT_A_REPARSE_POINT) => return Some(Errno::Inval.into()), + Some(Foundation::ERROR_NEGATIVE_SEEK) => return Some(Errno::Inval.into()), + Some(Foundation::ERROR_DIRECTORY) => return Some(Errno::Notdir.into()), + Some(Foundation::ERROR_ALREADY_EXISTS) => return Some(Errno::Exist.into()), + Some(Foundation::ERROR_STOPPED_ON_SYMLINK) => return Some(Errno::Loop.into()), + Some(Foundation::ERROR_DIRECTORY_NOT_SUPPORTED) => return Some(Errno::Isdir.into()), + _ => {} + } + + match raw_os_error { + Some(WinSock::WSAEWOULDBLOCK) => Some(Errno::Again.into()), + Some(WinSock::WSAECANCELLED) => Some(Errno::Canceled.into()), + Some(WinSock::WSA_E_CANCELLED) => Some(Errno::Canceled.into()), + Some(WinSock::WSAEBADF) => Some(Errno::Badf.into()), + Some(WinSock::WSAEFAULT) => Some(Errno::Fault.into()), + Some(WinSock::WSAEINVAL) => Some(Errno::Inval.into()), + Some(WinSock::WSAEMFILE) => Some(Errno::Mfile.into()), + Some(WinSock::WSAENAMETOOLONG) => Some(Errno::Nametoolong.into()), + Some(WinSock::WSAENOTEMPTY) => Some(Errno::Notempty.into()), + Some(WinSock::WSAELOOP) => Some(Errno::Loop.into()), + Some(WinSock::WSAEOPNOTSUPP) => Some(Errno::Notsup.into()), + Some(WinSock::WSAEADDRINUSE) => Some(Errno::Addrinuse.into()), + Some(WinSock::WSAEACCES) => Some(Errno::Acces.into()), + Some(WinSock::WSAEADDRNOTAVAIL) => Some(Errno::Addrnotavail.into()), + Some(WinSock::WSAEAFNOSUPPORT) => Some(Errno::Afnosupport.into()), + Some(WinSock::WSAEALREADY) => Some(Errno::Already.into()), + Some(WinSock::WSAECONNABORTED) => Some(Errno::Connaborted.into()), + Some(WinSock::WSAECONNREFUSED) => Some(Errno::Connrefused.into()), + Some(WinSock::WSAECONNRESET) => Some(Errno::Connreset.into()), + Some(WinSock::WSAEDESTADDRREQ) => Some(Errno::Destaddrreq.into()), + Some(WinSock::WSAEDQUOT) => Some(Errno::Dquot.into()), + Some(WinSock::WSAEHOSTUNREACH) => Some(Errno::Hostunreach.into()), + Some(WinSock::WSAEINPROGRESS) => Some(Errno::Inprogress.into()), + Some(WinSock::WSAEINTR) => Some(Errno::Intr.into()), + Some(WinSock::WSAEISCONN) => Some(Errno::Isconn.into()), + Some(WinSock::WSAEMSGSIZE) => Some(Errno::Msgsize.into()), + Some(WinSock::WSAENETDOWN) => Some(Errno::Netdown.into()), + Some(WinSock::WSAENETRESET) => Some(Errno::Netreset.into()), + Some(WinSock::WSAENETUNREACH) => Some(Errno::Netunreach.into()), + Some(WinSock::WSAENOBUFS) => Some(Errno::Nobufs.into()), + Some(WinSock::WSAENOPROTOOPT) => Some(Errno::Noprotoopt.into()), + Some(WinSock::WSAENOTCONN) => Some(Errno::Notconn.into()), + Some(WinSock::WSAENOTSOCK) => Some(Errno::Notsock.into()), + Some(WinSock::WSAEPROTONOSUPPORT) => Some(Errno::Protonosupport.into()), + Some(WinSock::WSAEPROTOTYPE) => Some(Errno::Prototype.into()), + Some(WinSock::WSAESTALE) => Some(Errno::Stale.into()), + Some(WinSock::WSAETIMEDOUT) => Some(Errno::Timedout.into()), + _ => None, + } +} + +impl From for Error { + fn from(err: std::io::Error) -> Error { + match from_raw_os_error(err.raw_os_error()) { + Some(errno) => errno, + None => match err.kind() { + std::io::ErrorKind::NotFound => Errno::Noent.into(), + std::io::ErrorKind::PermissionDenied => Errno::Perm.into(), + std::io::ErrorKind::AlreadyExists => Errno::Exist.into(), + std::io::ErrorKind::InvalidInput => Errno::Inval.into(), + _ => Error::trap(anyhow::anyhow!(err).context("Unknown OS error")), + }, + } + } +} + +impl From for Error { + fn from(err: cap_rand::Error) -> Error { + // I picked Error::Io as a 'reasonable default', FIXME dan is this ok? + from_raw_os_error(err.raw_os_error()).unwrap_or_else(|| Error::from(Errno::Io)) + } +} + +impl From for Error { + fn from(err: wiggle::GuestError) -> Error { + use wiggle::GuestError::*; + match err { + InvalidFlagValue { .. } => Errno::Inval.into(), + InvalidEnumValue { .. } => Errno::Inval.into(), + PtrOverflow { .. } => Errno::Fault.into(), + PtrOutOfBounds { .. } => Errno::Fault.into(), + PtrNotAligned { .. } => Errno::Inval.into(), + PtrBorrowed { .. } => Errno::Fault.into(), + InvalidUtf8 { .. } => Errno::Ilseq.into(), + TryFromIntError { .. } => Errno::Overflow.into(), + SliceLengthsDiffer { .. } => Errno::Fault.into(), + BorrowCheckerOutOfHandles { .. } => Errno::Fault.into(), + InFunc { err, .. } => Error::from(*err), + } + } +} + +impl From for Error { + fn from(_err: std::num::TryFromIntError) -> Error { + Errno::Overflow.into() + } +} diff --git a/crates/wasi-common/src/table.rs b/crates/wasi-common/src/table.rs index 0549ecabbc..195d8babb6 100644 --- a/crates/wasi-common/src/table.rs +++ b/crates/wasi-common/src/table.rs @@ -33,7 +33,7 @@ impl Table { // NOTE: The performance of this new key calculation could be very bad once keys wrap // around. if self.map.len() == u32::MAX as usize { - return Err(Error::trap("table has no free keys")); + return Err(Error::trap(anyhow::Error::msg("table has no free keys"))); } loop { let key = self.next_key; diff --git a/crates/wasi-common/tokio/src/sched/unix.rs b/crates/wasi-common/tokio/src/sched/unix.rs index cd4a3f8027..5ca3b1200d 100644 --- a/crates/wasi-common/tokio/src/sched/unix.rs +++ b/crates/wasi-common/tokio/src/sched/unix.rs @@ -6,7 +6,7 @@ use wasi_common::{ subscription::{RwEventFlags, Subscription}, Poll, }, - Context as _, Error, + Error, }; struct FirstReady<'a, T>(Vec + Send + 'a>>>); @@ -56,12 +56,15 @@ pub async fn poll_oneoff<'a>(poll: &mut Poll<'a>) -> Result<(), Error> { match s { Subscription::Read(f) => { futures.push(async move { - f.file.readable().await.context("readable future")?; + f.file + .readable() + .await + .map_err(|e| e.context("readable future"))?; f.complete( f.file .num_ready_bytes() .await - .context("read num_ready_bytes")?, + .map_err(|e| e.context("read num_ready_bytes"))?, RwEventFlags::empty(), ); Ok::<(), Error>(()) @@ -70,7 +73,10 @@ pub async fn poll_oneoff<'a>(poll: &mut Poll<'a>) -> Result<(), Error> { Subscription::Write(f) => { futures.push(async move { - f.file.writable().await.context("writable future")?; + f.file + .writable() + .await + .map_err(|e| e.context("writable future"))?; f.complete(0, RwEventFlags::empty()); Ok(()) }); diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index 308fc2d0d4..a865577695 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -48,9 +48,7 @@ pub fn add_to_linker( ) -> anyhow::Result<()> where U: Send + wasi_common::snapshots::preview_0::wasi_unstable::WasiUnstable - + wasi_common::snapshots::preview_0::types::UserErrorConversion - + wasi_common::snapshots::preview_1::wasi_snapshot_preview1::WasiSnapshotPreview1 - + wasi_common::snapshots::preview_1::types::UserErrorConversion, + + wasi_common::snapshots::preview_1::wasi_snapshot_preview1::WasiSnapshotPreview1, $($bounds)* { snapshots::preview_1::add_wasi_snapshot_preview1_to_linker(linker, get_cx)?; @@ -66,7 +64,7 @@ pub mod snapshots { // This must be the same witx document as used above. This should be ensured by // the `WASI_ROOT` env variable, which is set in wasi-common's `build.rs`. witx: ["$WASI_ROOT/phases/snapshot/witx/wasi_snapshot_preview1.witx"], - errors: { errno => Error }, + errors: { errno => trappable Error }, $async_mode: * }); } @@ -77,7 +75,7 @@ pub mod snapshots { // This must be the same witx document as used above. This should be ensured by // the `WASI_ROOT` env variable, which is set in wasi-common's `build.rs`. witx: ["$WASI_ROOT/phases/old/snapshot_0/witx/wasi_unstable.witx"], - errors: { errno => Error }, + errors: { errno => trappable Error }, $async_mode: * }); }