diff --git a/crates/wasi-c2/src/dir.rs b/crates/wasi-c2/src/dir.rs index bdc0811865..a0b59c3647 100644 --- a/crates/wasi-c2/src/dir.rs +++ b/crates/wasi-c2/src/dir.rs @@ -1,5 +1,5 @@ use crate::file::{FdFlags, FileCaps, FileType, Filestat, OFlags, WasiFile}; -use crate::{Error, SystemTimeSpec}; +use crate::{Error, ErrorExt, SystemTimeSpec}; use bitflags::bitflags; use std::any::Any; use std::cell::Ref; @@ -69,20 +69,15 @@ impl DirEntry { if self.caps.contains(caps) { Ok(()) } else { - Err(Error::DirNotCapable { - desired: caps, - has: self.caps, - }) + Err(Error::not_capable().context(format!("desired {:?}, has {:?}", caps, self.caps,))) } } pub fn capable_of_file(&self, caps: FileCaps) -> Result<(), Error> { if self.file_caps.contains(caps) { Ok(()) } else { - Err(Error::FileNotCapable { - desired: caps, - has: self.file_caps, - }) + Err(Error::not_capable() + .context(format!("desired {:?}, has {:?}", caps, self.file_caps))) } } pub fn drop_caps_to(&mut self, caps: DirCaps, file_caps: FileCaps) -> Result<(), Error> { diff --git a/crates/wasi-c2/src/error.rs b/crates/wasi-c2/src/error.rs index edd35b00c8..9ff237ce6c 100644 --- a/crates/wasi-c2/src/error.rs +++ b/crates/wasi-c2/src/error.rs @@ -1,171 +1,99 @@ -use crate::dir::DirCaps; -use crate::file::FileCaps; -use thiserror::Error; +pub use anyhow::Error; /// Internal error type for the `wasi-common` crate. /// Contains variants of the WASI `$errno` type are added according to what is actually used internally by /// the crate. Not all values are represented presently. -#[derive(Debug, Error)] -pub enum Error { - #[error("Wiggle GuestError: {0}")] - Guest(#[from] wiggle::GuestError), - #[error("TryFromIntError: {0}")] - TryFromInt(#[from] std::num::TryFromIntError), - #[error("Utf8Error: {0}")] - Utf8(#[from] std::str::Utf8Error), - #[error("cap_rand Error: {0}")] - CapRand(#[from] cap_rand::Error), - /// Errno::Notcapable: Extension: Capabilities insufficient - #[error("File not capable: desired {desired:?}, has {has:?}")] - FileNotCapable { desired: FileCaps, has: FileCaps }, - - /// Errno::Notcapable: Extension: Capabilities insufficient - #[error("Directory not capable: desired {desired:?}, has {has:?}")] - DirNotCapable { desired: DirCaps, has: DirCaps }, - - /// Idk what the deal with this guy is yet - #[error("Table overflow")] - TableOverflow, - - /// The host OS may return an io error that doesn't match one of the - /// wasi errno variants we expect. We do not expose the details of this - /// error to the user. - #[error("Unexpected IoError: {0}")] - UnexpectedIo(#[source] std::io::Error), - - /// An unsupported feature of Wasi was used. This error will trap. - #[error("Unsupported feature: {0}")] - Unsupported(&'static str), - - // Below this, all variants are from the `$errno` type: +#[derive(Debug, thiserror::Error)] +pub enum ErrorKind { /// Errno::TooBig: Argument list too long #[error("TooBig: Argument list too long")] TooBig, - /// Errno::Acces: Permission denied - #[error("Acces: Permission denied")] - Acces, /// Errno::Badf: Bad file descriptor #[error("Badf: Bad file descriptor")] Badf, - /// Errno::Busy: Device or resource busy - #[error("Busy: Device or resource busy")] - Busy, /// Errno::Exist: File exists #[error("Exist: File exists")] Exist, - /// Errno::Fault: Bad address - #[error("Fault: Bad address")] - Fault, - /// Errno::Fbig: File too large - #[error("Fbig: File too large")] - Fbig, /// Errno::Ilseq: Illegal byte sequence #[error("Ilseq: Illegal byte sequence")] Ilseq, /// Errno::Inval: Invalid argument #[error("Inval: Invalid argument")] Inval, - /// Errno::Io: I/O error - #[error("Io: I/o error")] - Io, - /// Errno::Isdir: Is a directory - #[error("Isdir: Is a directory")] - Isdir, - /// Errno::Loop: Too many levels of symbolic links - #[error("Loop: Too many levels of symbolic links")] - Loop, - /// Errno::Mfile: File descriptor value too large - #[error("Mfile: File descriptor value too large")] - Mfile, - /// Errno::Mlink: Too many links - #[error("Mlink: Too many links")] - Mlink, /// Errno::Nametoolong: Filename too long #[error("Nametoolong: Filename too long")] Nametoolong, - /// Errno::Nfile: Too many files open in system - #[error("Nfile: Too many files open in system")] - Nfile, - /// Errno::Noent: No such file or directory - #[error("Noent: No such file or directory")] - Noent, - /// Errno::Nomem: Not enough space - #[error("Nomem: Not enough space")] - Nomem, - /// Errno::Nospc: No space left on device - #[error("Nospc: No space left on device")] - Nospc, /// 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::Notempty: Directory not empty. - #[error("Notempty: Directory not empty")] - Notempty, /// 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::Pipe: Broken pipe - #[error("Pipe: Broken pipe")] - Pipe, - /// Errno::Perm: Operation not permitted - #[error("Perm: Operation not permitted")] - Perm, /// Errno::Range: Result too large #[error("Range: Result too large")] Range, /// Errno::Spipe: Invalid seek #[error("Spipe: Invalid seek")] Spipe, - /// Errno::NotCapable: Not capable #[error("Not capable")] NotCapable, } -impl From for Error { - fn from(_err: std::convert::Infallible) -> Self { - unreachable!("should be impossible: From") - } +pub trait ErrorExt { + fn too_big() -> Self; + fn badf() -> Self; + fn exist() -> Self; + fn illegal_byte_sequence() -> Self; + fn invalid_argument() -> 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 not_capable() -> Self; } -use std::io; -impl From for Error { - fn from(err: io::Error) -> Self { - match err.raw_os_error() { - Some(code) => match code { - libc::EPIPE => Self::Pipe, - libc::EPERM => Self::Perm, - libc::ENOENT => Self::Noent, - libc::ENOMEM => Self::Nomem, - libc::E2BIG => Self::TooBig, - libc::EIO => Self::Io, - libc::EBADF => Self::Badf, - libc::EBUSY => Self::Busy, - libc::EACCES => Self::Acces, - libc::EFAULT => Self::Fault, - libc::ENOTDIR => Self::Notdir, - libc::EISDIR => Self::Isdir, - libc::EINVAL => Self::Inval, - libc::EEXIST => Self::Exist, - libc::EFBIG => Self::Fbig, - libc::ENOSPC => Self::Nospc, - libc::ESPIPE => Self::Spipe, - libc::EMFILE => Self::Mfile, - libc::EMLINK => Self::Mlink, - libc::ENAMETOOLONG => Self::Nametoolong, - libc::ENFILE => Self::Nfile, - libc::ENOTEMPTY => Self::Notempty, - libc::ELOOP => Self::Loop, - libc::EOVERFLOW => Self::Overflow, - libc::EILSEQ => Self::Ilseq, - libc::ENOTSUP => Self::Notsup, - _ => Self::UnexpectedIo(err), - }, - None => Self::UnexpectedIo(err), - } +impl ErrorExt for Error { + fn too_big() -> Self { + ErrorKind::TooBig.into() + } + fn badf() -> Self { + ErrorKind::Badf.into() + } + fn exist() -> Self { + ErrorKind::Exist.into() + } + fn illegal_byte_sequence() -> Self { + ErrorKind::Ilseq.into() + } + fn invalid_argument() -> Self { + ErrorKind::Inval.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 not_capable() -> Self { + ErrorKind::NotCapable.into() } } diff --git a/crates/wasi-c2/src/file.rs b/crates/wasi-c2/src/file.rs index 3f1e81395a..c69c795b80 100644 --- a/crates/wasi-c2/src/file.rs +++ b/crates/wasi-c2/src/file.rs @@ -1,4 +1,4 @@ -use crate::{Error, SystemTimeSpec}; +use crate::{Error, ErrorExt, SystemTimeSpec}; use bitflags::bitflags; use std::any::Any; use std::cell::Ref; @@ -116,10 +116,7 @@ impl FileEntry { if self.caps.contains(caps) { Ok(()) } else { - Err(Error::FileNotCapable { - desired: caps, - has: self.caps, - }) + Err(Error::not_capable().context(format!("desired {:?}, has {:?}", caps, self.caps,))) } } diff --git a/crates/wasi-c2/src/lib.rs b/crates/wasi-c2/src/lib.rs index 7cd5c8325f..9b84849fae 100644 --- a/crates/wasi-c2/src/lib.rs +++ b/crates/wasi-c2/src/lib.rs @@ -15,6 +15,6 @@ pub mod table; pub use clocks::SystemTimeSpec; pub use ctx::{WasiCtx, WasiCtxBuilder}; pub use dir::{DirCaps, ReaddirCursor, ReaddirEntity, WasiDir}; -pub use error::Error; +pub use error::{Error, ErrorExt, ErrorKind}; pub use file::{FdFlags, FileCaps, Filestat, OFlags, WasiFile}; pub use string_array::StringArrayError; diff --git a/crates/wasi-c2/src/pipe.rs b/crates/wasi-c2/src/pipe.rs index b0fb6f41c2..4705c8bdba 100644 --- a/crates/wasi-c2/src/pipe.rs +++ b/crates/wasi-c2/src/pipe.rs @@ -11,7 +11,7 @@ //! use crate::{ file::{FdFlags, FileType, Filestat, WasiFile}, - Error, SystemTimeSpec, + Error, ErrorExt, SystemTimeSpec, }; use std::any::Any; use std::convert::TryInto; @@ -115,7 +115,7 @@ impl WasiFile for ReadPipe { Ok(FdFlags::empty()) } unsafe fn reopen_with_fdflags(&self, _fdflags: FdFlags) -> Result, Error> { - Err(Error::Badf) + Err(Error::badf()) } fn get_filestat(&self) -> Result { Ok(Filestat { @@ -130,39 +130,39 @@ impl WasiFile for ReadPipe { }) } fn set_filestat_size(&self, _size: u64) -> Result<(), Error> { - Err(Error::Perm) + Err(Error::badf()) } fn advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error> { - Err(Error::Badf) + Err(Error::badf()) } fn allocate(&self, offset: u64, len: u64) -> Result<(), Error> { - Err(Error::Badf) + Err(Error::badf()) } fn read_vectored(&self, bufs: &mut [io::IoSliceMut]) -> Result { let n = self.borrow().read_vectored(bufs)?; - Ok(n.try_into().map_err(|_| Error::Overflow)?) + Ok(n.try_into()?) } fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut], offset: u64) -> Result { - Err(Error::Badf) + Err(Error::badf()) } fn write_vectored(&self, bufs: &[io::IoSlice]) -> Result { - Err(Error::Badf) + Err(Error::badf()) } fn write_vectored_at(&self, bufs: &[io::IoSlice], offset: u64) -> Result { - Err(Error::Badf) + Err(Error::badf()) } fn seek(&self, pos: std::io::SeekFrom) -> Result { - Err(Error::Badf) + Err(Error::badf()) } fn peek(&self, buf: &mut [u8]) -> Result { - Err(Error::Badf) + Err(Error::badf()) } fn set_times( &self, atime: Option, mtime: Option, ) -> Result<(), Error> { - Err(Error::Badf) + Err(Error::badf()) } fn num_ready_bytes(&self) -> Result { Ok(0) @@ -251,7 +251,7 @@ impl WasiFile for WritePipe { Ok(FdFlags::APPEND) } unsafe fn reopen_with_fdflags(&self, _fdflags: FdFlags) -> Result, Error> { - Err(Error::Badf) + Err(Error::badf()) } fn get_filestat(&self) -> Result { Ok(Filestat { @@ -266,39 +266,39 @@ impl WasiFile for WritePipe { }) } fn set_filestat_size(&self, _size: u64) -> Result<(), Error> { - Err(Error::Perm) + Err(Error::badf()) } fn advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error> { - Err(Error::Badf) + Err(Error::badf()) } fn allocate(&self, offset: u64, len: u64) -> Result<(), Error> { - Err(Error::Badf) + Err(Error::badf()) } fn read_vectored(&self, bufs: &mut [io::IoSliceMut]) -> Result { - Err(Error::Badf) + Err(Error::badf()) } fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut], offset: u64) -> Result { - Err(Error::Badf) + Err(Error::badf()) } fn write_vectored(&self, bufs: &[io::IoSlice]) -> Result { let n = self.borrow().write_vectored(bufs)?; - Ok(n.try_into().map_err(|_| Error::Overflow)?) + Ok(n.try_into()?) } fn write_vectored_at(&self, bufs: &[io::IoSlice], offset: u64) -> Result { - Err(Error::Badf) + Err(Error::badf()) } fn seek(&self, pos: std::io::SeekFrom) -> Result { - Err(Error::Badf) + Err(Error::badf()) } fn peek(&self, buf: &mut [u8]) -> Result { - Err(Error::Badf) + Err(Error::badf()) } fn set_times( &self, atime: Option, mtime: Option, ) -> Result<(), Error> { - Err(Error::Badf) + Err(Error::badf()) } fn num_ready_bytes(&self) -> Result { Ok(0) diff --git a/crates/wasi-c2/src/snapshots/preview_1.rs b/crates/wasi-c2/src/snapshots/preview_1.rs index d1656875b7..73f539de9a 100644 --- a/crates/wasi-c2/src/snapshots/preview_1.rs +++ b/crates/wasi-c2/src/snapshots/preview_1.rs @@ -9,8 +9,9 @@ use crate::{ subscription::{RwEventFlags, SubscriptionResult}, Poll, }, - Error, SystemTimeSpec, WasiCtx, + Error, ErrorExt, ErrorKind, SystemTimeSpec, WasiCtx, }; +use anyhow::{anyhow, Context}; use cap_std::time::{Duration, SystemClock}; use std::cell::{Ref, RefMut}; use std::convert::{TryFrom, TryInto}; @@ -42,74 +43,49 @@ impl types::UserErrorConversion for WasiCtx { fn errno_from_error(&self, e: Error) -> Result { debug!("Error: {:?}", e); e.try_into() + .map_err(|e| wiggle::Trap::String(format!("{:?}", e))) } } impl TryFrom for types::Errno { - type Error = wiggle::Trap; - fn try_from(e: Error) -> Result { - use std::io::ErrorKind; + 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 { - Error::Unsupported(feat) => { - Err(wiggle::Trap::String(format!("Unsupported: {0}", feat))) - } - Error::Guest(e) => Ok(e.into()), - Error::TryFromInt(_) => Ok(Errno::Overflow), - Error::Utf8(_) => Ok(Errno::Ilseq), - Error::UnexpectedIo(e) => match e.kind() { - ErrorKind::NotFound => Ok(Errno::Noent), - ErrorKind::PermissionDenied => Ok(Errno::Perm), - ErrorKind::AlreadyExists => Ok(Errno::Exist), - ErrorKind::InvalidInput => Ok(Errno::Ilseq), - ErrorKind::ConnectionRefused - | ErrorKind::ConnectionReset - | ErrorKind::ConnectionAborted - | ErrorKind::NotConnected - | ErrorKind::AddrInUse - | ErrorKind::AddrNotAvailable - | ErrorKind::BrokenPipe - | ErrorKind::WouldBlock - | ErrorKind::InvalidData - | ErrorKind::TimedOut - | ErrorKind::WriteZero - | ErrorKind::Interrupted - | ErrorKind::Other - | ErrorKind::UnexpectedEof - | _ => Ok(Errno::Io), - }, - Error::CapRand(_) => Ok(Errno::Io), - Error::TooBig => Ok(Errno::TooBig), - Error::Acces => Ok(Errno::Acces), - Error::Badf => Ok(Errno::Badf), - Error::Busy => Ok(Errno::Busy), - Error::Exist => Ok(Errno::Exist), - Error::Fault => Ok(Errno::Fault), - Error::Fbig => Ok(Errno::Fbig), - Error::Ilseq => Ok(Errno::Ilseq), - Error::Inval => Ok(Errno::Inval), - Error::Io => Ok(Errno::Io), - Error::Isdir => Ok(Errno::Isdir), - Error::Loop => Ok(Errno::Loop), - Error::Mfile => Ok(Errno::Mfile), - Error::Mlink => Ok(Errno::Mlink), - Error::Nametoolong => Ok(Errno::Nametoolong), - Error::Nfile => Ok(Errno::Nfile), - Error::Noent => Ok(Errno::Noent), - Error::Nomem => Ok(Errno::Nomem), - Error::Nospc => Ok(Errno::Nospc), - Error::Notdir => Ok(Errno::Notdir), - Error::Notempty => Ok(Errno::Notempty), - Error::Notsup => Ok(Errno::Notsup), - Error::Overflow => Ok(Errno::Overflow), - Error::Pipe => Ok(Errno::Pipe), - Error::Perm => Ok(Errno::Perm), - Error::Range => Ok(Errno::Range), - Error::Spipe => Ok(Errno::Spipe), - Error::FileNotCapable { .. } => Ok(Errno::Notcapable), - Error::DirNotCapable { .. } => Ok(Errno::Notcapable), - Error::NotCapable => Ok(Errno::Notcapable), - Error::TableOverflow => Ok(Errno::Overflow), + ErrorKind::TooBig => Errno::TooBig, + ErrorKind::Badf => Errno::Badf, + ErrorKind::Exist => Errno::Exist, + ErrorKind::Ilseq => Errno::Ilseq, + ErrorKind::Inval => Errno::Inval, + 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::NotCapable => Errno::Notcapable, } } } @@ -134,6 +110,44 @@ impl From for types::Errno { } } +impl TryFrom for types::Errno { + type Error = Error; + fn try_from(err: std::io::Error) -> Result { + match err.raw_os_error() { + Some(code) => match code { + libc::EPIPE => Ok(types::Errno::Pipe), + libc::EPERM => Ok(types::Errno::Perm), + libc::ENOENT => Ok(types::Errno::Noent), + libc::ENOMEM => Ok(types::Errno::Nomem), + libc::E2BIG => Ok(types::Errno::TooBig), + libc::EIO => Ok(types::Errno::Io), + libc::EBADF => Ok(types::Errno::Badf), + libc::EBUSY => Ok(types::Errno::Busy), + libc::EACCES => Ok(types::Errno::Acces), + libc::EFAULT => Ok(types::Errno::Fault), + libc::ENOTDIR => Ok(types::Errno::Notdir), + libc::EISDIR => Ok(types::Errno::Isdir), + libc::EINVAL => Ok(types::Errno::Inval), + libc::EEXIST => Ok(types::Errno::Exist), + libc::EFBIG => Ok(types::Errno::Fbig), + libc::ENOSPC => Ok(types::Errno::Nospc), + libc::ESPIPE => Ok(types::Errno::Spipe), + libc::EMFILE => Ok(types::Errno::Mfile), + libc::EMLINK => Ok(types::Errno::Mlink), + libc::ENAMETOOLONG => Ok(types::Errno::Nametoolong), + libc::ENFILE => Ok(types::Errno::Nfile), + libc::ENOTEMPTY => Ok(types::Errno::Notempty), + libc::ELOOP => Ok(types::Errno::Loop), + libc::EOVERFLOW => Ok(types::Errno::Overflow), + libc::EILSEQ => Ok(types::Errno::Ilseq), + libc::ENOTSUP => Ok(types::Errno::Notsup), + _ => Err(anyhow!(err).context("Unknown raw OS error")), + }, + None => Err(anyhow!(err).context("No raw OS error")), + } + } +} + impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { fn args_get<'b>( &self, @@ -164,7 +178,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { types::Clockid::Realtime => Ok(self.clocks.system.resolution()), types::Clockid::Monotonic => Ok(self.clocks.monotonic.resolution()), types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => { - Err(Error::NotCapable) + Err(Error::badf().context("process and thread clocks are not supported")) } }?; Ok(resolution.as_nanos().try_into()?) @@ -181,7 +195,7 @@ impl<'a> 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::NotCapable)?; // XXX wrong + .map_err(|_| anyhow!("current time before unix epoch"))?; Ok(d.as_nanos().try_into()?) } types::Clockid::Monotonic => { @@ -189,7 +203,9 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { let d = now.duration_since(self.clocks.creation_time); Ok(d.as_nanos().try_into()?) } - types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => Err(Error::Badf), + types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => { + Err(Error::badf().context("process and thread clocks are not supported")) + } } } @@ -226,7 +242,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { // Fail fast: If not present in table, Badf if !table.contains_key(fd) { - return Err(Error::Badf); + return Err(Error::badf().context("key not in table")); } // fd_close must close either a File or a Dir handle if table.is::(fd) { @@ -235,13 +251,12 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { // We cannot close preopened directories let dir_entry: Ref = table.get(fd).unwrap(); if dir_entry.preopen_path().is_some() { - return Err(Error::Notsup); + return Err(Error::not_supported().context("cannot close propened directory")); } drop(dir_entry); let _ = table.delete(fd); } else { - // XXX do we just table delete other entry types anyway? - return Err(Error::Badf); + return Err(Error::badf().context("key does not refer to file or directory")); } Ok(()) @@ -267,7 +282,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { let dir_fdstat = dir_entry.get_dir_fdstat(); Ok(types::Fdstat::from(&dir_fdstat)) } else { - Err(Error::Badf) + Err(Error::badf()) } } @@ -300,7 +315,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { let file_caps = FileCaps::from(&fs_rights_inheriting); dir_entry.drop_caps_to(dir_caps, file_caps) } else { - Err(Error::Badf) + Err(Error::badf()) } } @@ -320,7 +335,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { .get_filestat()?; Ok(filestat.into()) } else { - Err(Error::Badf) + Err(Error::badf()) } } @@ -346,27 +361,10 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { let set_atim_now = fst_flags.contains(types::Fstflags::ATIM_NOW); let set_mtim = fst_flags.contains(types::Fstflags::MTIM); let set_mtim_now = fst_flags.contains(types::Fstflags::MTIM_NOW); - if (set_atim && set_atim_now) || (set_mtim && set_mtim_now) { - return Err(Error::Inval); - } - let atim = if set_atim { - Some(SystemTimeSpec::Absolute( - SystemClock::UNIX_EPOCH + Duration::from_nanos(atim), - )) - } else if set_atim_now { - Some(SystemTimeSpec::SymbolicNow) - } else { - None - }; - let mtim = if set_mtim { - Some(SystemTimeSpec::Absolute( - SystemClock::UNIX_EPOCH + Duration::from_nanos(mtim), - )) - } else if set_mtim_now { - Some(SystemTimeSpec::SymbolicNow) - } else { - None - }; + + let atim = systimespec(set_atim, atim, set_atim_now).context("atim")?; + let mtim = systimespec(set_mtim, mtim, set_mtim_now).context("mtim")?; + if table.is::(fd) { table .get_file(fd) @@ -380,7 +378,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { .get_cap(DirCaps::FILESTAT_SET_TIMES)? .set_times(".", atim, mtim) } else { - Err(Error::Badf) + Err(Error::badf()) } } @@ -492,14 +490,13 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { fn fd_prestat_get(&self, fd: types::Fd) -> Result { let table = self.table(); - let dir_entry: Ref = table.get(u32::from(fd)).map_err(|_| Error::Badf)?; + let dir_entry: Ref = table.get(u32::from(fd)).map_err(|_| Error::badf())?; if let Some(ref preopen) = dir_entry.preopen_path() { - let path_str = preopen.to_str().ok_or(Error::Notsup)?; - let pr_name_len = - u32::try_from(path_str.as_bytes().len()).map_err(|_| Error::Overflow)?; + let path_str = preopen.to_str().ok_or_else(|| Error::not_supported())?; + let pr_name_len = u32::try_from(path_str.as_bytes().len())?; Ok(types::Prestat::Dir(types::PrestatDir { pr_name_len })) } else { - Err(Error::Notsup) + Err(Error::not_supported().context("file is not a preopen")) } } @@ -510,18 +507,21 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { path_max_len: types::Size, ) -> Result<(), Error> { let table = self.table(); - let dir_entry: Ref = table.get(u32::from(fd)).map_err(|_| Error::Notdir)?; + let dir_entry: Ref = table.get(u32::from(fd)).map_err(|_| Error::not_dir())?; if let Some(ref preopen) = dir_entry.preopen_path() { - let path_bytes = preopen.to_str().ok_or(Error::Notsup)?.as_bytes(); + let path_bytes = preopen + .to_str() + .ok_or_else(|| Error::not_supported())? + .as_bytes(); let path_len = path_bytes.len(); if path_len < path_max_len as usize { - return Err(Error::Nametoolong); + return Err(Error::name_too_long()); } let mut p_memory = path.as_array(path_len as u32).as_slice_mut()?; p_memory.copy_from_slice(path_bytes); Ok(()) } else { - Err(Error::Notsup) + Err(Error::not_supported()) } } fn fd_renumber(&self, from: types::Fd, to: types::Fd) -> Result<(), Error> { @@ -529,13 +529,10 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { let from = u32::from(from); let to = u32::from(to); if !table.contains_key(from) { - return Err(Error::Badf); + return Err(Error::badf()); } - if table.is_preopen(from) { - return Err(Error::Notsup); - } - if table.is_preopen(to) { - return Err(Error::Notsup); + if table.is_preopen(from) || table.is_preopen(to) { + return Err(Error::not_supported().context("cannot renumber a preopen")); } let from_entry = table .delete(from) @@ -609,7 +606,9 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { let dirent_len: types::Size = dirent_raw.len().try_into()?; let name_raw = name.as_bytes(); let name_len: types::Size = name_raw.len().try_into()?; - let offset = dirent_len.checked_add(name_len).ok_or(Error::Overflow)?; + let offset = dirent_len + .checked_add(name_len) + .ok_or_else(|| Error::overflow())?; // Copy as many bytes of the dirent as we can, up to the end of the buffer let dirent_copy_len = std::cmp::min(dirent_len, buf_len - bufused); @@ -680,22 +679,9 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { let set_atim_now = fst_flags.contains(types::Fstflags::ATIM_NOW); let set_mtim = fst_flags.contains(types::Fstflags::MTIM); let set_mtim_now = fst_flags.contains(types::Fstflags::MTIM_NOW); - if (set_atim && set_atim_now) || (set_mtim && set_mtim_now) { - return Err(Error::Inval); - } - fn systimespec(set: bool, ts: types::Timestamp, now: bool) -> Option { - if set { - Some(SystemTimeSpec::Absolute( - SystemClock::UNIX_EPOCH + Duration::from_nanos(ts), - )) - } else if now { - Some(SystemTimeSpec::SymbolicNow) - } else { - None - } - } - let atim = systimespec(set_atim, atim, set_atim_now); - let mtim = systimespec(set_mtim, mtim, set_mtim_now); + + let atim = systimespec(set_atim, atim, set_atim_now).context("atim")?; + let mtim = systimespec(set_mtim, mtim, set_mtim_now).context("mtim")?; self.table() .get_dir(u32::from(dirfd))? .get_cap(DirCaps::PATH_FILESTAT_SET_TIMES)? @@ -740,7 +726,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { let mut table = self.table(); let dirfd = u32::from(dirfd); if table.is::(dirfd) { - return Err(Error::Notdir); + return Err(Error::not_dir()); } let dir_entry = table.get_dir(dirfd)?; @@ -754,7 +740,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { || oflags.contains(OFlags::EXCLUSIVE) || oflags.contains(OFlags::TRUNCATE) { - return Err(Error::Inval); + return Err(Error::invalid_argument().context("directory oflags")); } let dir_caps = dir_entry.child_dir_caps(DirCaps::from(&fs_rights_base)); let file_caps = dir_entry.child_file_caps(FileCaps::from(&fs_rights_inheriting)); @@ -794,11 +780,11 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { .read_link(path.as_str()?.deref())? .into_os_string() .into_string() - .map_err(|_| Error::Ilseq)?; + .map_err(|_| Error::illegal_byte_sequence().context("link contents"))?; let link_bytes = link.as_bytes(); let link_len = link_bytes.len(); if link_len > buf_len as usize { - return Err(Error::Range); + return Err(Error::range()); } let mut buf = buf.as_array(link_len as u32).as_slice_mut()?; buf.copy_from_slice(link_bytes); @@ -863,7 +849,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { nsubscriptions: types::Size, ) -> Result { if nsubscriptions == 0 { - return Err(Error::Inval); + return Err(Error::invalid_argument().context("nsubscriptions must be nonzero")); } let table = self.table(); @@ -886,12 +872,12 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { self.clocks .creation_time .checked_add(duration) - .ok_or(Error::Overflow)? + .ok_or_else(|| Error::overflow().context("deadline"))? } else { clock .now(precision) .checked_add(duration) - .ok_or(Error::Overflow)? + .ok_or_else(|| Error::overflow().context("deadline"))? }; poll.subscribe_monotonic_clock( clock, @@ -900,7 +886,8 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { sub.userdata.into(), ) } - _ => Err(Error::Inval)?, + _ => Err(Error::invalid_argument() + .context("timer subscriptions only support monotonic timer"))?, }, types::SubscriptionU::FdRead(readsub) => { let fd = readsub.file_descriptor; @@ -970,7 +957,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { }, Err(e) => types::Event { userdata, - error: e.try_into().expect("non-trapping"), + error: e.try_into()?, type_, fd_readwrite: fd_readwrite_empty(), }, @@ -982,7 +969,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { userdata, error: match r { Ok(()) => types::Errno::Success, - Err(e) => e.try_into().expect("non-trapping"), + Err(e) => e.try_into()?, }, type_, fd_readwrite: fd_readwrite_empty(), @@ -1004,7 +991,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { } fn proc_raise(&self, _sig: types::Signal) -> Result<(), Error> { - Err(Error::Unsupported("proc_raise")) + Err(anyhow!("proc_raise unsupported")) } fn sched_yield(&self) -> Result<(), Error> { @@ -1023,7 +1010,7 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { _ri_data: &types::IovecArray<'_>, _ri_flags: types::Riflags, ) -> Result<(types::Size, types::Roflags), Error> { - Err(Error::Unsupported("sock_recv")) + Err(anyhow!("sock_recv unsupported")) } fn sock_send( @@ -1032,11 +1019,11 @@ impl<'a> wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { _si_data: &types::CiovecArray<'_>, _si_flags: types::Siflags, ) -> Result { - Err(Error::Unsupported("sock_send")) + Err(anyhow!("sock_send unsupported")) } fn sock_shutdown(&self, _fd: types::Fd, _how: types::Sdflags) -> Result<(), Error> { - Err(Error::Unsupported("sock_shutdown")) + Err(anyhow!("sock_shutdown unsupported")) } } @@ -1447,3 +1434,21 @@ fn fd_readwrite_empty() -> types::EventFdReadwrite { flags: types::Eventrwflags::empty(), } } + +fn systimespec( + set: bool, + ts: types::Timestamp, + now: bool, +) -> Result, Error> { + if set && now { + Err(Error::invalid_argument()) + } else if set { + Ok(Some(SystemTimeSpec::Absolute( + SystemClock::UNIX_EPOCH + Duration::from_nanos(ts), + ))) + } else if now { + Ok(Some(SystemTimeSpec::SymbolicNow)) + } else { + Ok(None) + } +} diff --git a/crates/wasi-c2/src/string_array.rs b/crates/wasi-c2/src/string_array.rs index 8c223e1726..557784a9ff 100644 --- a/crates/wasi-c2/src/string_array.rs +++ b/crates/wasi-c2/src/string_array.rs @@ -1,4 +1,4 @@ -use crate::Error; +use crate::{Error, ErrorExt}; use wiggle::GuestPtr; pub struct StringArray { @@ -59,10 +59,13 @@ impl StringArray { { let elem_buffer = buffer .get_range(cursor..(cursor + len)) - .ok_or(Error::Inval)?; // Elements don't fit in buffer provided + .ok_or(Error::invalid_argument())?; // Elements don't fit in buffer provided elem_buffer.copy_from_slice(bytes)?; } - buffer.get(cursor + len).ok_or(Error::Inval)?.write(0)?; // 0 terminate + buffer + .get(cursor + len) + .ok_or(Error::invalid_argument())? + .write(0)?; // 0 terminate head?.write(buffer.get(cursor).expect("already validated"))?; cursor += len + 1; } diff --git a/crates/wasi-c2/src/table.rs b/crates/wasi-c2/src/table.rs index 20ee87bcc7..828483033e 100644 --- a/crates/wasi-c2/src/table.rs +++ b/crates/wasi-c2/src/table.rs @@ -1,4 +1,5 @@ -use crate::Error; +use crate::{Error, ErrorExt}; +use anyhow::anyhow; use std::any::Any; use std::cell::{Ref, RefCell, RefMut}; use std::collections::HashMap; @@ -25,7 +26,10 @@ impl Table { let key = self.next_key; // XXX this is not correct. The table may still have empty entries, but our // linear search strategy is quite bad - self.next_key = self.next_key.checked_add(1).ok_or(Error::TableOverflow)?; + self.next_key = self + .next_key + .checked_add(1) + .ok_or_else(|| anyhow!("out of keys in table"))?; if self.map.contains_key(&key) { continue; } @@ -56,13 +60,13 @@ impl Table { if r.is::() { Ok(Ref::map(r, |r| r.downcast_ref::().unwrap())) } else { - Err(Error::Exist) // Exists at another type + Err(Error::exist().context("element is a different type")) } } else { - Err(Error::Exist) // Does exist, but borrowed + Err(Error::exist().context("element in table, but mutably borrowed")) } } else { - Err(Error::Badf) // Does not exist + Err(Error::badf().context("key not in table")) } } @@ -72,13 +76,13 @@ impl Table { if r.is::() { Ok(RefMut::map(r, |r| r.downcast_mut::().unwrap())) } else { - Err(Error::Exist) // Exists at another type + Err(Error::exist().context("element is a different type")) } } else { - Err(Error::Exist) // Does exist, but borrowed + Err(Error::exist().context("element in table, but borrowed")) } } else { - Err(Error::Badf) // Does not exist + Err(Error::badf().context("key not in table")) } } @@ -91,8 +95,10 @@ impl Table { T: Any + Sized, F: FnOnce(T) -> Result, { - let entry = self.delete(key).ok_or(Error::Badf)?; - let downcast = entry.downcast::().map_err(|_| Error::Exist)?; + let entry = self.delete(key).ok_or(Error::badf())?; + let downcast = entry + .downcast::() + .map_err(|_| Error::exist().context("element is a different type"))?; let new = f(*downcast)?; self.insert_at(key, Box::new(new)); Ok(())