diff --git a/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs index d785435b6c02..2c968f59e535 100644 --- a/crates/cli-flags/src/lib.rs +++ b/crates/cli-flags/src/lib.rs @@ -262,7 +262,8 @@ wasmtime_option_group! { pub inherit_network: Option, /// Indicates whether `wasi:sockets/ip-name-lookup` is enabled or not. pub allow_ip_name_lookup: Option, - + /// Allows imports from the `wasi_unstable` core wasm module. + pub preview0: Option, } enum Wasi { diff --git a/crates/wasi/foo.diff b/crates/wasi/foo.diff new file mode 100644 index 000000000000..c2267d65f146 --- /dev/null +++ b/crates/wasi/foo.diff @@ -0,0 +1,20 @@ +--- witx/preview0/typenames.witx 2023-11-15 13:25:21 ++++ witx/preview1/typenames.witx 2023-10-19 15:44:01 +@@ -270,6 +270,8 @@ + $poll_fd_readwrite + ;;; The right to invoke `sock_shutdown`. + $sock_shutdown ++ ;;; The right to invoke `sock_accept`. ++ $sock_accept + ) + ) + +@@ -545,8 +549,6 @@ + ;;; The contents of a `subscription` when type is `eventtype::clock`. + (typename $subscription_clock + (record +- ;;; The user-defined unique identifier of the clock. +- (field $identifier $userdata) + ;;; The clock against which to compare the timestamp. + (field $id $clockid) + ;;; The absolute or relative timestamp. diff --git a/crates/wasi/src/preview2/mod.rs b/crates/wasi/src/preview2/mod.rs index b01232ed6d12..481b588e706d 100644 --- a/crates/wasi/src/preview2/mod.rs +++ b/crates/wasi/src/preview2/mod.rs @@ -30,6 +30,8 @@ mod network; pub mod pipe; mod poll; #[cfg(feature = "preview1-on-preview2")] +pub mod preview0; +#[cfg(feature = "preview1-on-preview2")] pub mod preview1; mod random; mod stdio; diff --git a/crates/wasi/src/preview2/preview0.rs b/crates/wasi/src/preview2/preview0.rs new file mode 100644 index 000000000000..ca08eb6cc77e --- /dev/null +++ b/crates/wasi/src/preview2/preview0.rs @@ -0,0 +1,870 @@ +use crate::preview2::preview0::types::Error; +use crate::preview2::preview1::types as snapshot1_types; +use crate::preview2::preview1::wasi_snapshot_preview1::WasiSnapshotPreview1 as Snapshot1; +use crate::preview2::preview1::WasiPreview1View; +use wiggle::{GuestError, GuestPtr}; + +pub fn add_to_linker_async( + linker: &mut wasmtime::Linker, +) -> anyhow::Result<()> { + wasi_unstable::add_to_linker(linker, |t| t) +} + +pub fn add_to_linker_sync( + linker: &mut wasmtime::Linker, +) -> anyhow::Result<()> { + sync::add_wasi_unstable_to_linker(linker, |t| t) +} + +wiggle::from_witx!({ + witx: ["$CARGO_MANIFEST_DIR/witx/preview0/wasi_unstable.witx"], + async: { + wasi_unstable::{ + fd_advise, fd_close, fd_datasync, fd_fdstat_get, fd_filestat_get, fd_filestat_set_size, + fd_filestat_set_times, fd_read, fd_pread, fd_seek, fd_sync, fd_readdir, fd_write, + fd_pwrite, poll_oneoff, path_create_directory, path_filestat_get, + path_filestat_set_times, path_link, path_open, path_readlink, path_remove_directory, + path_rename, path_symlink, path_unlink_file + } + }, + errors: { errno => trappable Error }, +}); + +mod sync { + use anyhow::Result; + use std::future::Future; + + wiggle::wasmtime_integration!({ + witx: ["$CARGO_MANIFEST_DIR/witx/preview0/wasi_unstable.witx"], + target: super, + block_on[in_tokio]: { + wasi_unstable::{ + fd_advise, fd_close, fd_datasync, fd_fdstat_get, fd_filestat_get, fd_filestat_set_size, + fd_filestat_set_times, fd_read, fd_pread, fd_seek, fd_sync, fd_readdir, fd_write, + fd_pwrite, poll_oneoff, path_create_directory, path_filestat_get, + path_filestat_set_times, path_link, path_open, path_readlink, path_remove_directory, + path_rename, path_symlink, path_unlink_file + } + }, + errors: { errno => trappable Error }, + }); + + // Small wrapper around `in_tokio` to add a `Result` layer which is always + // `Ok` + fn in_tokio(future: F) -> Result { + Ok(crate::preview2::in_tokio(future)) + } +} + +impl wiggle::GuestErrorType for types::Errno { + fn success() -> Self { + Self::Success + } +} + +#[wiggle::async_trait] +impl wasi_unstable::WasiUnstable for T { + fn args_get<'a>( + &mut self, + argv: &GuestPtr<'a, GuestPtr<'a, u8>>, + argv_buf: &GuestPtr<'a, u8>, + ) -> Result<(), Error> { + Snapshot1::args_get(self, argv, argv_buf)?; + Ok(()) + } + + fn args_sizes_get(&mut self) -> Result<(types::Size, types::Size), Error> { + let s = Snapshot1::args_sizes_get(self)?; + Ok(s) + } + + fn environ_get<'a>( + &mut self, + environ: &GuestPtr<'a, GuestPtr<'a, u8>>, + environ_buf: &GuestPtr<'a, u8>, + ) -> Result<(), Error> { + Snapshot1::environ_get(self, environ, environ_buf)?; + Ok(()) + } + + fn environ_sizes_get(&mut self) -> Result<(types::Size, types::Size), Error> { + let s = Snapshot1::environ_sizes_get(self)?; + Ok(s) + } + + fn clock_res_get(&mut self, id: types::Clockid) -> Result { + let t = Snapshot1::clock_res_get(self, id.into())?; + Ok(t) + } + + fn clock_time_get( + &mut self, + id: types::Clockid, + precision: types::Timestamp, + ) -> Result { + let t = Snapshot1::clock_time_get(self, id.into(), precision)?; + Ok(t) + } + + async fn fd_advise( + &mut self, + fd: types::Fd, + offset: types::Filesize, + len: types::Filesize, + advice: types::Advice, + ) -> Result<(), Error> { + Snapshot1::fd_advise(self, fd.into(), offset, len, advice.into()).await?; + Ok(()) + } + + fn fd_allocate( + &mut self, + fd: types::Fd, + offset: types::Filesize, + len: types::Filesize, + ) -> Result<(), Error> { + Snapshot1::fd_allocate(self, fd.into(), offset, len)?; + Ok(()) + } + + async fn fd_close(&mut self, fd: types::Fd) -> Result<(), Error> { + 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?; + Ok(()) + } + + async fn fd_fdstat_get(&mut self, fd: types::Fd) -> Result { + Ok(Snapshot1::fd_fdstat_get(self, fd.into()).await?.into()) + } + + fn fd_fdstat_set_flags(&mut self, fd: types::Fd, flags: types::Fdflags) -> Result<(), Error> { + Snapshot1::fd_fdstat_set_flags(self, fd.into(), flags.into())?; + Ok(()) + } + + fn fd_fdstat_set_rights( + &mut self, + fd: types::Fd, + fs_rights_base: types::Rights, + fs_rights_inheriting: types::Rights, + ) -> Result<(), Error> { + Snapshot1::fd_fdstat_set_rights( + self, + fd.into(), + fs_rights_base.into(), + fs_rights_inheriting.into(), + )?; + Ok(()) + } + + async fn fd_filestat_get(&mut self, fd: types::Fd) -> Result { + Ok(Snapshot1::fd_filestat_get(self, fd.into()).await?.into()) + } + + async fn fd_filestat_set_size( + &mut self, + fd: types::Fd, + size: types::Filesize, + ) -> Result<(), Error> { + Snapshot1::fd_filestat_set_size(self, fd.into(), size).await?; + Ok(()) + } + + async fn fd_filestat_set_times( + &mut self, + fd: types::Fd, + atim: types::Timestamp, + mtim: types::Timestamp, + fst_flags: types::Fstflags, + ) -> Result<(), Error> { + Snapshot1::fd_filestat_set_times(self, fd.into(), atim, mtim, fst_flags.into()).await?; + Ok(()) + } + + async fn fd_read<'a>( + &mut self, + fd: types::Fd, + iovs: &types::IovecArray<'a>, + ) -> Result { + assert_iovec_array_same(); + let result = Snapshot1::fd_read(self, fd.into(), &iovs.cast()).await?; + Ok(result) + } + + async fn fd_pread<'a>( + &mut self, + fd: types::Fd, + iovs: &types::IovecArray<'a>, + offset: types::Filesize, + ) -> Result { + assert_iovec_array_same(); + let result = Snapshot1::fd_pread(self, fd.into(), &iovs.cast(), offset).await?; + Ok(result) + } + + async fn fd_write<'a>( + &mut self, + fd: types::Fd, + ciovs: &types::CiovecArray<'a>, + ) -> Result { + assert_ciovec_array_same(); + let result = Snapshot1::fd_write(self, fd.into(), &ciovs.cast()).await?; + Ok(result) + } + + async fn fd_pwrite<'a>( + &mut self, + fd: types::Fd, + ciovs: &types::CiovecArray<'a>, + offset: types::Filesize, + ) -> Result { + assert_ciovec_array_same(); + let result = Snapshot1::fd_pwrite(self, fd.into(), &ciovs.cast(), offset).await?; + Ok(result) + } + + fn fd_prestat_get(&mut self, fd: types::Fd) -> Result { + Ok(Snapshot1::fd_prestat_get(self, fd.into())?.into()) + } + + fn fd_prestat_dir_name( + &mut self, + fd: types::Fd, + path: &GuestPtr<'_, u8>, + path_max_len: types::Size, + ) -> Result<(), Error> { + Snapshot1::fd_prestat_dir_name(self, fd.into(), path, path_max_len)?; + Ok(()) + } + + fn fd_renumber(&mut self, from: types::Fd, to: types::Fd) -> Result<(), Error> { + Snapshot1::fd_renumber(self, from.into(), to.into())?; + Ok(()) + } + + async fn fd_seek( + &mut self, + fd: types::Fd, + offset: types::Filedelta, + whence: types::Whence, + ) -> Result { + 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?; + Ok(()) + } + + fn fd_tell(&mut self, fd: types::Fd) -> Result { + Ok(Snapshot1::fd_tell(self, fd.into())?) + } + + async fn fd_readdir<'a>( + &mut self, + fd: types::Fd, + buf: &GuestPtr<'a, u8>, + buf_len: types::Size, + cookie: types::Dircookie, + ) -> Result { + Ok(Snapshot1::fd_readdir(self, fd.into(), buf, buf_len, cookie).await?) + } + + async fn path_create_directory<'a>( + &mut self, + dirfd: types::Fd, + path: &GuestPtr<'a, str>, + ) -> Result<(), Error> { + Snapshot1::path_create_directory(self, dirfd.into(), path).await?; + Ok(()) + } + + async fn path_filestat_get<'a>( + &mut self, + dirfd: types::Fd, + flags: types::Lookupflags, + path: &GuestPtr<'a, str>, + ) -> Result { + Ok( + Snapshot1::path_filestat_get(self, dirfd.into(), flags.into(), path) + .await? + .into(), + ) + } + + async fn path_filestat_set_times<'a>( + &mut self, + dirfd: types::Fd, + flags: types::Lookupflags, + path: &GuestPtr<'a, str>, + atim: types::Timestamp, + mtim: types::Timestamp, + fst_flags: types::Fstflags, + ) -> Result<(), Error> { + Snapshot1::path_filestat_set_times( + self, + dirfd.into(), + flags.into(), + path, + atim, + mtim, + fst_flags.into(), + ) + .await?; + Ok(()) + } + + async fn path_link<'a>( + &mut self, + src_fd: types::Fd, + src_flags: types::Lookupflags, + src_path: &GuestPtr<'a, str>, + target_fd: types::Fd, + target_path: &GuestPtr<'a, str>, + ) -> Result<(), Error> { + Snapshot1::path_link( + self, + src_fd.into(), + src_flags.into(), + src_path, + target_fd.into(), + target_path, + ) + .await?; + Ok(()) + } + + async fn path_open<'a>( + &mut self, + dirfd: types::Fd, + dirflags: types::Lookupflags, + path: &GuestPtr<'a, str>, + oflags: types::Oflags, + fs_rights_base: types::Rights, + fs_rights_inheriting: types::Rights, + fdflags: types::Fdflags, + ) -> Result { + Ok(Snapshot1::path_open( + self, + dirfd.into(), + dirflags.into(), + path, + oflags.into(), + fs_rights_base.into(), + fs_rights_inheriting.into(), + fdflags.into(), + ) + .await? + .into()) + } + + async fn path_readlink<'a>( + &mut self, + dirfd: types::Fd, + path: &GuestPtr<'a, str>, + buf: &GuestPtr<'a, u8>, + buf_len: types::Size, + ) -> Result { + Ok(Snapshot1::path_readlink(self, dirfd.into(), path, buf, buf_len).await?) + } + + async fn path_remove_directory<'a>( + &mut self, + dirfd: types::Fd, + path: &GuestPtr<'a, str>, + ) -> Result<(), Error> { + Snapshot1::path_remove_directory(self, dirfd.into(), path).await?; + Ok(()) + } + + async fn path_rename<'a>( + &mut self, + src_fd: types::Fd, + src_path: &GuestPtr<'a, str>, + 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?; + Ok(()) + } + + async fn path_symlink<'a>( + &mut self, + src_path: &GuestPtr<'a, str>, + dirfd: types::Fd, + dest_path: &GuestPtr<'a, str>, + ) -> Result<(), Error> { + Snapshot1::path_symlink(self, src_path, dirfd.into(), dest_path).await?; + Ok(()) + } + + async fn path_unlink_file<'a>( + &mut self, + dirfd: types::Fd, + path: &GuestPtr<'a, str>, + ) -> Result<(), Error> { + Snapshot1::path_unlink_file(self, dirfd.into(), path).await?; + Ok(()) + } + + // The representation of `SubscriptionClock` is different in preview0 and + // preview1 so a bit of a hack is employed here. The change was to remove a + // field from `SubscriptionClock` so to implement this without copying too + // much the `subs` field is overwritten with preview1-compatible structures + // and then the preview1 implementation is used. Before returning though + // the old values are restored to pretend like we didn't overwrite them. + // + // Surely no one would pass overlapping pointers to this API right? + async fn poll_oneoff<'a>( + &mut self, + subs: &GuestPtr<'a, types::Subscription>, + events: &GuestPtr<'a, types::Event>, + nsubscriptions: types::Size, + ) -> Result { + let subs_array = subs.as_array(nsubscriptions); + let mut old_subs = Vec::new(); + for slot in subs_array.iter() { + let slot = slot?; + let sub = slot.read()?; + old_subs.push(sub.clone()); + slot.cast().write(snapshot1_types::Subscription { + userdata: sub.userdata, + u: match sub.u { + types::SubscriptionU::Clock(c) => { + snapshot1_types::SubscriptionU::Clock(c.into()) + } + types::SubscriptionU::FdRead(c) => { + snapshot1_types::SubscriptionU::FdRead(c.into()) + } + types::SubscriptionU::FdWrite(c) => { + snapshot1_types::SubscriptionU::FdWrite(c.into()) + } + }, + })?; + } + let ret = + Snapshot1::poll_oneoff(self, &subs.cast(), &events.cast(), nsubscriptions).await?; + for (sub, slot) in old_subs.into_iter().zip(subs_array.iter()) { + slot?.write(sub)?; + } + Ok(ret) + } + + fn proc_exit(&mut self, status: types::Exitcode) -> anyhow::Error { + Snapshot1::proc_exit(self, status) + } + + fn proc_raise(&mut self, sig: types::Signal) -> Result<(), Error> { + Snapshot1::proc_raise(self, sig.into())?; + Ok(()) + } + + fn sched_yield(&mut self) -> Result<(), Error> { + Snapshot1::sched_yield(self)?; + Ok(()) + } + + fn random_get(&mut self, buf: &GuestPtr<'_, u8>, buf_len: types::Size) -> Result<(), Error> { + Snapshot1::random_get(self, buf, buf_len)?; + Ok(()) + } + + fn sock_recv( + &mut self, + _fd: types::Fd, + _ri_data: &types::IovecArray<'_>, + _ri_flags: types::Riflags, + ) -> Result<(types::Size, types::Roflags), Error> { + Err(Error::trap(anyhow::Error::msg("sock_recv unsupported"))) + } + + fn sock_send( + &mut self, + _fd: types::Fd, + _si_data: &types::CiovecArray<'_>, + _si_flags: types::Siflags, + ) -> Result { + Err(Error::trap(anyhow::Error::msg("sock_send unsupported"))) + } + + fn sock_shutdown(&mut self, _fd: types::Fd, _how: types::Sdflags) -> Result<(), Error> { + Err(Error::trap(anyhow::Error::msg("sock_shutdown unsupported"))) + } +} + +fn assert_iovec_array_same() { + // NB: this isn't enough to assert the types are the same, but it's + // something. Additionally preview1 and preview0 aren't changing any more + // and it's been manually verified that these two types are the same, so + // it's ok to cast between them. + assert_eq!( + std::mem::size_of::>(), + std::mem::size_of::>() + ); +} + +fn assert_ciovec_array_same() { + // NB: see above too + assert_eq!( + std::mem::size_of::>(), + std::mem::size_of::>() + ); +} + +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), + } + } +} + +/// Fd is a newtype wrapper around u32. Unwrap and wrap it. +impl From for snapshot1_types::Fd { + fn from(fd: types::Fd) -> snapshot1_types::Fd { + u32::from(fd).into() + } +} + +/// Fd is a newtype wrapper around u32. Unwrap and wrap it. +impl From for types::Fd { + fn from(fd: snapshot1_types::Fd) -> types::Fd { + u32::from(fd).into() + } +} + +/// Trivial conversion between two c-style enums that have the exact same set of variants. +/// Could we do something unsafe and not list all these variants out? Probably, but doing +/// it this way doesn't bother me much. I copy-pasted the list of variants out of the +/// rendered rustdocs. +/// LLVM ought to compile these From impls into no-ops, inshallah +macro_rules! convert_enum { + ($from:ty, $to:ty, $($var:ident),+) => { + impl From<$from> for $to { + fn from(e: $from) -> $to { + match e { + $( <$from>::$var => <$to>::$var, )+ + } + } + } + } +} +convert_enum!( + snapshot1_types::Errno, + types::Errno, + Success, + TooBig, + Acces, + Addrinuse, + Addrnotavail, + Afnosupport, + Again, + Already, + Badf, + Badmsg, + Busy, + Canceled, + Child, + Connaborted, + Connrefused, + Connreset, + Deadlk, + Destaddrreq, + Dom, + Dquot, + Exist, + Fault, + Fbig, + Hostunreach, + Idrm, + Ilseq, + Inprogress, + Intr, + Inval, + Io, + Isconn, + Isdir, + Loop, + Mfile, + Mlink, + Msgsize, + Multihop, + Nametoolong, + Netdown, + Netreset, + Netunreach, + Nfile, + Nobufs, + Nodev, + Noent, + Noexec, + Nolck, + Nolink, + Nomem, + Nomsg, + Noprotoopt, + Nospc, + Nosys, + Notconn, + Notdir, + Notempty, + Notrecoverable, + Notsock, + Notsup, + Notty, + Nxio, + Overflow, + Ownerdead, + Perm, + Pipe, + Proto, + Protonosupport, + Prototype, + Range, + Rofs, + Spipe, + Srch, + Stale, + Timedout, + Txtbsy, + Xdev, + Notcapable +); +convert_enum!( + types::Clockid, + snapshot1_types::Clockid, + Realtime, + Monotonic, + ProcessCputimeId, + ThreadCputimeId +); + +convert_enum!( + types::Advice, + snapshot1_types::Advice, + Normal, + Sequential, + Random, + Willneed, + Dontneed, + Noreuse +); +convert_enum!( + snapshot1_types::Filetype, + types::Filetype, + Directory, + BlockDevice, + CharacterDevice, + RegularFile, + SocketDgram, + SocketStream, + SymbolicLink, + Unknown +); +convert_enum!(types::Whence, snapshot1_types::Whence, Cur, End, Set); + +convert_enum!( + types::Signal, + snapshot1_types::Signal, + None, + Hup, + Int, + Quit, + Ill, + Trap, + Abrt, + Bus, + Fpe, + Kill, + Usr1, + Segv, + Usr2, + Pipe, + Alrm, + Term, + Chld, + Cont, + Stop, + Tstp, + Ttin, + Ttou, + Urg, + Xcpu, + Xfsz, + Vtalrm, + Prof, + Winch, + Poll, + Pwr, + Sys +); + +/// Prestat isn't a c-style enum, its a union where the variant has a payload. Its the only one of +/// those we need to convert, so write it by hand. +impl From for types::Prestat { + fn from(p: snapshot1_types::Prestat) -> types::Prestat { + match p { + snapshot1_types::Prestat::Dir(d) => types::Prestat::Dir(d.into()), + } + } +} + +/// Trivial conversion between two structs that have the exact same set of fields, +/// with recursive descent into the field types. +macro_rules! convert_struct { + ($from:ty, $to:path, $($field:ident),+) => { + impl From<$from> for $to { + fn from(e: $from) -> $to { + $to { + $( $field: e.$field.into(), )+ + } + } + } + } +} + +convert_struct!(snapshot1_types::PrestatDir, types::PrestatDir, pr_name_len); +convert_struct!( + snapshot1_types::Fdstat, + types::Fdstat, + fs_filetype, + fs_rights_base, + fs_rights_inheriting, + fs_flags +); +convert_struct!( + types::SubscriptionClock, + snapshot1_types::SubscriptionClock, + id, + timeout, + precision, + flags +); +convert_struct!( + types::SubscriptionFdReadwrite, + snapshot1_types::SubscriptionFdReadwrite, + file_descriptor +); + +/// Snapshot1 Filestat is incompatible with Snapshot0 Filestat - the nlink +/// field is u32 on this Filestat, and u64 on theirs. If you've got more than +/// 2^32 links I don't know what to tell you +impl From for types::Filestat { + fn from(f: snapshot1_types::Filestat) -> types::Filestat { + types::Filestat { + dev: f.dev.into(), + ino: f.ino.into(), + filetype: f.filetype.into(), + nlink: f.nlink.try_into().unwrap_or(u32::MAX), + size: f.size.into(), + atim: f.atim.into(), + mtim: f.mtim.into(), + ctim: f.ctim.into(), + } + } +} + +/// Trivial conversion between two bitflags that have the exact same set of flags. +macro_rules! convert_flags { + ($from:ty, $to:ty, $($flag:ident),+) => { + impl From<$from> for $to { + fn from(f: $from) -> $to { + let mut out = <$to>::empty(); + $( + if f.contains(<$from>::$flag) { + out |= <$to>::$flag; + } + )+ + out + } + } + } +} + +/// Need to convert in both directions? This saves listing out the flags twice +macro_rules! convert_flags_bidirectional { + ($from:ty, $to:ty, $($flag:tt)*) => { + convert_flags!($from, $to, $($flag)*); + convert_flags!($to, $from, $($flag)*); + } +} + +convert_flags_bidirectional!( + snapshot1_types::Fdflags, + types::Fdflags, + APPEND, + DSYNC, + NONBLOCK, + RSYNC, + SYNC +); +convert_flags!( + types::Lookupflags, + snapshot1_types::Lookupflags, + SYMLINK_FOLLOW +); +convert_flags!( + types::Fstflags, + snapshot1_types::Fstflags, + ATIM, + ATIM_NOW, + MTIM, + MTIM_NOW +); +convert_flags!( + types::Oflags, + snapshot1_types::Oflags, + CREAT, + DIRECTORY, + EXCL, + TRUNC +); +convert_flags_bidirectional!( + types::Rights, + snapshot1_types::Rights, + FD_DATASYNC, + FD_READ, + FD_SEEK, + FD_FDSTAT_SET_FLAGS, + FD_SYNC, + FD_TELL, + FD_WRITE, + FD_ADVISE, + FD_ALLOCATE, + PATH_CREATE_DIRECTORY, + PATH_CREATE_FILE, + PATH_LINK_SOURCE, + PATH_LINK_TARGET, + PATH_OPEN, + FD_READDIR, + PATH_READLINK, + PATH_RENAME_SOURCE, + PATH_RENAME_TARGET, + PATH_FILESTAT_GET, + PATH_FILESTAT_SET_SIZE, + PATH_FILESTAT_SET_TIMES, + FD_FILESTAT_GET, + FD_FILESTAT_SET_SIZE, + FD_FILESTAT_SET_TIMES, + PATH_SYMLINK, + PATH_REMOVE_DIRECTORY, + PATH_UNLINK_FILE, + POLL_FD_READWRITE, + SOCK_SHUTDOWN +); +convert_flags!( + types::Subclockflags, + snapshot1_types::Subclockflags, + SUBSCRIPTION_CLOCK_ABSTIME +); + +impl From for types::Error { + fn from(err: GuestError) -> Self { + snapshot1_types::Error::from(err).into() + } +} diff --git a/crates/wasi/src/preview2/preview1.rs b/crates/wasi/src/preview2/preview1.rs index 7760bc418733..b47a571954bf 100644 --- a/crates/wasi/src/preview2/preview1.rs +++ b/crates/wasi/src/preview2/preview1.rs @@ -513,7 +513,7 @@ pub fn add_to_linker_sync( // None of the generated modules, traits, or types should be used externally // to this module. wiggle::from_witx!({ - witx: ["$CARGO_MANIFEST_DIR/witx/wasi_snapshot_preview1.witx"], + witx: ["$CARGO_MANIFEST_DIR/witx/preview1/wasi_snapshot_preview1.witx"], async: { wasi_snapshot_preview1::{ fd_advise, fd_close, fd_datasync, fd_fdstat_get, fd_filestat_get, fd_filestat_set_size, @@ -531,7 +531,7 @@ mod sync { use std::future::Future; wiggle::wasmtime_integration!({ - witx: ["$CARGO_MANIFEST_DIR/witx/wasi_snapshot_preview1.witx"], + witx: ["$CARGO_MANIFEST_DIR/witx/preview1/wasi_snapshot_preview1.witx"], target: super, block_on[in_tokio]: { wasi_snapshot_preview1::{ diff --git a/crates/wasi/witx/preview0/typenames.witx b/crates/wasi/witx/preview0/typenames.witx new file mode 100644 index 000000000000..f4ba7880242a --- /dev/null +++ b/crates/wasi/witx/preview0/typenames.witx @@ -0,0 +1,746 @@ +;; Type names used by low-level WASI interfaces. +;; +;; Some content here is derived from [CloudABI](https://github.com/NuxiNL/cloudabi). +;; +;; This is a `witx` file. See [here](https://github.com/WebAssembly/WASI/tree/main/docs/witx.md) +;; for an explanation of what that means. + +(typename $size u32) + +;;; Non-negative file size or length of a region within a file. +(typename $filesize u64) + +;;; Timestamp in nanoseconds. +(typename $timestamp u64) + +;;; Identifiers for clocks. +(typename $clockid + (enum (@witx tag u32) + ;;; The clock measuring real time. Time value zero corresponds with + ;;; 1970-01-01T00:00:00Z. + $realtime + ;;; The store-wide monotonic clock, which is defined as a clock measuring + ;;; real time, whose value cannot be adjusted and which cannot have negative + ;;; clock jumps. The epoch of this clock is undefined. The absolute time + ;;; value of this clock therefore has no meaning. + $monotonic + ;;; The CPU-time clock associated with the current process. + $process_cputime_id + ;;; The CPU-time clock associated with the current thread. + $thread_cputime_id + ) +) + +;;; Error codes returned by functions. +;;; Not all of these error codes are returned by the functions provided by this +;;; API; some are used in higher-level library layers, and others are provided +;;; merely for alignment with POSIX. +(typename $errno + (enum (@witx tag u16) + ;;; No error occurred. System call completed successfully. + $success + ;;; Argument list too long. + $2big + ;;; Permission denied. + $acces + ;;; Address in use. + $addrinuse + ;;; Address not available. + $addrnotavail + ;;; Address family not supported. + $afnosupport + ;;; Resource unavailable, or operation would block. + $again + ;;; Connection already in progress. + $already + ;;; Bad file descriptor. + $badf + ;;; Bad message. + $badmsg + ;;; Device or resource busy. + $busy + ;;; Operation canceled. + $canceled + ;;; No child processes. + $child + ;;; Connection aborted. + $connaborted + ;;; Connection refused. + $connrefused + ;;; Connection reset. + $connreset + ;;; Resource deadlock would occur. + $deadlk + ;;; Destination address required. + $destaddrreq + ;;; Mathematics argument out of domain of function. + $dom + ;;; Reserved. + $dquot + ;;; File exists. + $exist + ;;; Bad address. + $fault + ;;; File too large. + $fbig + ;;; Host is unreachable. + $hostunreach + ;;; Identifier removed. + $idrm + ;;; Illegal byte sequence. + $ilseq + ;;; Operation in progress. + $inprogress + ;;; Interrupted function. + $intr + ;;; Invalid argument. + $inval + ;;; I/O error. + $io + ;;; Socket is connected. + $isconn + ;;; Is a directory. + $isdir + ;;; Too many levels of symbolic links. + $loop + ;;; File descriptor value too large. + $mfile + ;;; Too many links. + $mlink + ;;; Message too large. + $msgsize + ;;; Reserved. + $multihop + ;;; Filename too long. + $nametoolong + ;;; Network is down. + $netdown + ;;; Connection aborted by network. + $netreset + ;;; Network unreachable. + $netunreach + ;;; Too many files open in system. + $nfile + ;;; No buffer space available. + $nobufs + ;;; No such device. + $nodev + ;;; No such file or directory. + $noent + ;;; Executable file format error. + $noexec + ;;; No locks available. + $nolck + ;;; Reserved. + $nolink + ;;; Not enough space. + $nomem + ;;; No message of the desired type. + $nomsg + ;;; Protocol not available. + $noprotoopt + ;;; No space left on device. + $nospc + ;;; Function not supported. + $nosys + ;;; The socket is not connected. + $notconn + ;;; Not a directory or a symbolic link to a directory. + $notdir + ;;; Directory not empty. + $notempty + ;;; State not recoverable. + $notrecoverable + ;;; Not a socket. + $notsock + ;;; Not supported, or operation not supported on socket. + $notsup + ;;; Inappropriate I/O control operation. + $notty + ;;; No such device or address. + $nxio + ;;; Value too large to be stored in data type. + $overflow + ;;; Previous owner died. + $ownerdead + ;;; Operation not permitted. + $perm + ;;; Broken pipe. + $pipe + ;;; Protocol error. + $proto + ;;; Protocol not supported. + $protonosupport + ;;; Protocol wrong type for socket. + $prototype + ;;; Result too large. + $range + ;;; Read-only file system. + $rofs + ;;; Invalid seek. + $spipe + ;;; No such process. + $srch + ;;; Reserved. + $stale + ;;; Connection timed out. + $timedout + ;;; Text file busy. + $txtbsy + ;;; Cross-device link. + $xdev + ;;; Extension: Capabilities insufficient. + $notcapable + ) +) + +;;; File descriptor rights, determining which actions may be performed. +(typename $rights + (flags (@witx repr u64) + ;;; The right to invoke `fd_datasync`. + ;; + ;;; If `rights::path_open` is set, includes the right to invoke + ;;; `path_open` with `fdflags::dsync`. + $fd_datasync + ;;; The right to invoke `fd_read` and `sock_recv`. + ;; + ;;; If `rights::fd_seek` is set, includes the right to invoke `fd_pread`. + $fd_read + ;;; The right to invoke `fd_seek`. This flag implies `rights::fd_tell`. + $fd_seek + ;;; The right to invoke `fd_fdstat_set_flags`. + $fd_fdstat_set_flags + ;;; The right to invoke `fd_sync`. + ;; + ;;; If `rights::path_open` is set, includes the right to invoke + ;;; `path_open` with `fdflags::rsync` and `fdflags::dsync`. + $fd_sync + ;;; The right to invoke `fd_seek` in such a way that the file offset + ;;; remains unaltered (i.e., `whence::cur` with offset zero), or to + ;;; invoke `fd_tell`. + $fd_tell + ;;; The right to invoke `fd_write` and `sock_send`. + ;;; If `rights::fd_seek` is set, includes the right to invoke `fd_pwrite`. + $fd_write + ;;; The right to invoke `fd_advise`. + $fd_advise + ;;; The right to invoke `fd_allocate`. + $fd_allocate + ;;; The right to invoke `path_create_directory`. + $path_create_directory + ;;; If `rights::path_open` is set, the right to invoke `path_open` with `oflags::creat`. + $path_create_file + ;;; The right to invoke `path_link` with the file descriptor as the + ;;; source directory. + $path_link_source + ;;; The right to invoke `path_link` with the file descriptor as the + ;;; target directory. + $path_link_target + ;;; The right to invoke `path_open`. + $path_open + ;;; The right to invoke `fd_readdir`. + $fd_readdir + ;;; The right to invoke `path_readlink`. + $path_readlink + ;;; The right to invoke `path_rename` with the file descriptor as the source directory. + $path_rename_source + ;;; The right to invoke `path_rename` with the file descriptor as the target directory. + $path_rename_target + ;;; The right to invoke `path_filestat_get`. + $path_filestat_get + ;;; The right to change a file's size (there is no `path_filestat_set_size`). + ;;; If `rights::path_open` is set, includes the right to invoke `path_open` with `oflags::trunc`. + $path_filestat_set_size + ;;; The right to invoke `path_filestat_set_times`. + $path_filestat_set_times + ;;; The right to invoke `fd_filestat_get`. + $fd_filestat_get + ;;; The right to invoke `fd_filestat_set_size`. + $fd_filestat_set_size + ;;; The right to invoke `fd_filestat_set_times`. + $fd_filestat_set_times + ;;; The right to invoke `path_symlink`. + $path_symlink + ;;; The right to invoke `path_remove_directory`. + $path_remove_directory + ;;; The right to invoke `path_unlink_file`. + $path_unlink_file + ;;; If `rights::fd_read` is set, includes the right to invoke `poll_oneoff` to subscribe to `eventtype::fd_read`. + ;;; If `rights::fd_write` is set, includes the right to invoke `poll_oneoff` to subscribe to `eventtype::fd_write`. + $poll_fd_readwrite + ;;; The right to invoke `sock_shutdown`. + $sock_shutdown + ) +) + +;;; A file descriptor handle. +(typename $fd (handle)) + +;;; A region of memory for scatter/gather reads. +(typename $iovec + (record + ;;; The address of the buffer to be filled. + (field $buf (@witx pointer u8)) + ;;; The length of the buffer to be filled. + (field $buf_len $size) + ) +) + +;;; A region of memory for scatter/gather writes. +(typename $ciovec + (record + ;;; The address of the buffer to be written. + (field $buf (@witx const_pointer u8)) + ;;; The length of the buffer to be written. + (field $buf_len $size) + ) +) + +(typename $iovec_array (list $iovec)) +(typename $ciovec_array (list $ciovec)) + +;;; Relative offset within a file. +(typename $filedelta s64) + +;;; The position relative to which to set the offset of the file descriptor. +(typename $whence + (enum (@witx tag u8) + ;;; Seek relative to current position. + $cur + ;;; Seek relative to end-of-file. + $end + ;;; Seek relative to start-of-file. + $set + ) +) + +;;; A reference to the offset of a directory entry. +(typename $dircookie u64) + +;;; The type for the `dirent::d_namlen` field of `dirent` struct. +(typename $dirnamlen u32) + +;;; File serial number that is unique within its file system. +(typename $inode u64) + +;;; The type of a file descriptor or file. +(typename $filetype + (enum (@witx tag u8) + ;;; The type of the file descriptor or file is unknown or is different from any of the other types specified. + $unknown + ;;; The file descriptor or file refers to a block device inode. + $block_device + ;;; The file descriptor or file refers to a character device inode. + $character_device + ;;; The file descriptor or file refers to a directory inode. + $directory + ;;; The file descriptor or file refers to a regular file inode. + $regular_file + ;;; The file descriptor or file refers to a datagram socket. + $socket_dgram + ;;; The file descriptor or file refers to a byte-stream socket. + $socket_stream + ;;; The file refers to a symbolic link inode. + $symbolic_link + ) +) + +;;; A directory entry. +(typename $dirent + (record + ;;; The offset of the next directory entry stored in this directory. + (field $d_next $dircookie) + ;;; The serial number of the file referred to by this directory entry. + (field $d_ino $inode) + ;;; The length of the name of the directory entry. + (field $d_namlen $dirnamlen) + ;;; The type of the file referred to by this directory entry. + (field $d_type $filetype) + ) +) + +;;; File or memory access pattern advisory information. +(typename $advice + (enum (@witx tag u8) + ;;; The application has no advice to give on its behavior with respect to the specified data. + $normal + ;;; The application expects to access the specified data sequentially from lower offsets to higher offsets. + $sequential + ;;; The application expects to access the specified data in a random order. + $random + ;;; The application expects to access the specified data in the near future. + $willneed + ;;; The application expects that it will not access the specified data in the near future. + $dontneed + ;;; The application expects to access the specified data once and then not reuse it thereafter. + $noreuse + ) +) + +;;; File descriptor flags. +(typename $fdflags + (flags (@witx repr u16) + ;;; Append mode: Data written to the file is always appended to the file's end. + $append + ;;; Write according to synchronized I/O data integrity completion. Only the data stored in the file is synchronized. + $dsync + ;;; Non-blocking mode. + $nonblock + ;;; Synchronized read I/O operations. + $rsync + ;;; Write according to synchronized I/O file integrity completion. In + ;;; addition to synchronizing the data stored in the file, the implementation + ;;; may also synchronously update the file's metadata. + $sync + ) +) + +;;; File descriptor attributes. +(typename $fdstat + (record + ;;; File type. + (field $fs_filetype $filetype) + ;;; File descriptor flags. + (field $fs_flags $fdflags) + ;;; Rights that apply to this file descriptor. + (field $fs_rights_base $rights) + ;;; Maximum set of rights that may be installed on new file descriptors that + ;;; are created through this file descriptor, e.g., through `path_open`. + (field $fs_rights_inheriting $rights) + ) +) + +;;; Identifier for a device containing a file system. Can be used in combination +;;; with `inode` to uniquely identify a file or directory in the filesystem. +(typename $device u64) + +;;; Which file time attributes to adjust. +(typename $fstflags + (flags (@witx repr u16) + ;;; Adjust the last data access timestamp to the value stored in `filestat::atim`. + $atim + ;;; Adjust the last data access timestamp to the time of clock `clockid::realtime`. + $atim_now + ;;; Adjust the last data modification timestamp to the value stored in `filestat::mtim`. + $mtim + ;;; Adjust the last data modification timestamp to the time of clock `clockid::realtime`. + $mtim_now + ) +) + +;;; Flags determining the method of how paths are resolved. +(typename $lookupflags + (flags (@witx repr u32) + ;;; As long as the resolved path corresponds to a symbolic link, it is expanded. + $symlink_follow + ) +) + +;;; Open flags used by `path_open`. +(typename $oflags + (flags (@witx repr u16) + ;;; Create file if it does not exist. + $creat + ;;; Fail if not a directory. + $directory + ;;; Fail if file already exists. + $excl + ;;; Truncate file to size 0. + $trunc + ) +) + +;;; Number of hard links to an inode. +(typename $linkcount u32) + +;;; File attributes. +(typename $filestat + (record + ;;; Device ID of device containing the file. + (field $dev $device) + ;;; File serial number. + (field $ino $inode) + ;;; File type. + (field $filetype $filetype) + ;;; Number of hard links to the file. + (field $nlink $linkcount) + ;;; For regular files, the file size in bytes. For symbolic links, the length in bytes of the pathname contained in the symbolic link. + (field $size $filesize) + ;;; Last data access timestamp. + (field $atim $timestamp) + ;;; Last data modification timestamp. + (field $mtim $timestamp) + ;;; Last file status change timestamp. + (field $ctim $timestamp) + ) +) + +;;; User-provided value that may be attached to objects that is retained when +;;; extracted from the implementation. +(typename $userdata u64) + +;;; Type of a subscription to an event or its occurrence. +(typename $eventtype + (enum (@witx tag u8) + ;;; The time value of clock `subscription_clock::id` has + ;;; reached timestamp `subscription_clock::timeout`. + $clock + ;;; File descriptor `subscription_fd_readwrite::file_descriptor` has data + ;;; available for reading. This event always triggers for regular files. + $fd_read + ;;; File descriptor `subscription_fd_readwrite::file_descriptor` has capacity + ;;; available for writing. This event always triggers for regular files. + $fd_write + ) +) + +;;; The state of the file descriptor subscribed to with +;;; `eventtype::fd_read` or `eventtype::fd_write`. +(typename $eventrwflags + (flags (@witx repr u16) + ;;; The peer of this socket has closed or disconnected. + $fd_readwrite_hangup + ) +) + +;;; The contents of an `event` for the `eventtype::fd_read` and +;;; `eventtype::fd_write` variants +(typename $event_fd_readwrite + (record + ;;; The number of bytes available for reading or writing. + (field $nbytes $filesize) + ;;; The state of the file descriptor. + (field $flags $eventrwflags) + ) +) + +;;; An event that occurred. +(typename $event + (record + ;;; User-provided value that got attached to `subscription::userdata`. + (field $userdata $userdata) + ;;; If non-zero, an error that occurred while processing the subscription request. + (field $error $errno) + ;;; The type of event that occured + (field $type $eventtype) + ;;; The contents of the event, if it is an `eventtype::fd_read` or + ;;; `eventtype::fd_write`. `eventtype::clock` events ignore this field. + (field $fd_readwrite $event_fd_readwrite) + ) +) + +;;; Flags determining how to interpret the timestamp provided in +;;; `subscription_clock::timeout`. +(typename $subclockflags + (flags (@witx repr u16) + ;;; If set, treat the timestamp provided in + ;;; `subscription_clock::timeout` as an absolute timestamp of clock + ;;; `subscription_clock::id`. If clear, treat the timestamp + ;;; provided in `subscription_clock::timeout` relative to the + ;;; current time value of clock `subscription_clock::id`. + $subscription_clock_abstime + ) +) + +;;; The contents of a `subscription` when type is `eventtype::clock`. +(typename $subscription_clock + (record + ;;; The user-defined unique identifier of the clock. + (field $identifier $userdata) + ;;; The clock against which to compare the timestamp. + (field $id $clockid) + ;;; The absolute or relative timestamp. + (field $timeout $timestamp) + ;;; The amount of time that the implementation may wait additionally + ;;; to coalesce with other events. + (field $precision $timestamp) + ;;; Flags specifying whether the timeout is absolute or relative + (field $flags $subclockflags) + ) +) + +;;; The contents of a `subscription` when the variant is +;;; `eventtype::fd_read` or `eventtype::fd_write`. +(typename $subscription_fd_readwrite + (record + ;;; The file descriptor on which to wait for it to become ready for reading or writing. + (field $file_descriptor $fd) + ) +) + +;;; The contents of a `subscription`. +(typename $subscription_u + (union (@witx tag $eventtype) + $subscription_clock + $subscription_fd_readwrite + $subscription_fd_readwrite + ) +) + +;;; Subscription to an event. +(typename $subscription + (record + ;;; User-provided value that is attached to the subscription in the + ;;; implementation and returned through `event::userdata`. + (field $userdata $userdata) + ;;; The type of the event to which to subscribe. + (field $u $subscription_u) + ) +) + +;;; Exit code generated by a process when exiting. +(typename $exitcode u32) + +;;; Signal condition. +(typename $signal + (enum (@witx tag u8) + ;;; No signal. Note that POSIX has special semantics for `kill(pid, 0)`, + ;;; so this value is reserved. + $none + ;;; Hangup. + ;;; Action: Terminates the process. + $hup + ;;; Terminate interrupt signal. + ;;; Action: Terminates the process. + $int + ;;; Terminal quit signal. + ;;; Action: Terminates the process. + $quit + ;;; Illegal instruction. + ;;; Action: Terminates the process. + $ill + ;;; Trace/breakpoint trap. + ;;; Action: Terminates the process. + $trap + ;;; Process abort signal. + ;;; Action: Terminates the process. + $abrt + ;;; Access to an undefined portion of a memory object. + ;;; Action: Terminates the process. + $bus + ;;; Erroneous arithmetic operation. + ;;; Action: Terminates the process. + $fpe + ;;; Kill. + ;;; Action: Terminates the process. + $kill + ;;; User-defined signal 1. + ;;; Action: Terminates the process. + $usr1 + ;;; Invalid memory reference. + ;;; Action: Terminates the process. + $segv + ;;; User-defined signal 2. + ;;; Action: Terminates the process. + $usr2 + ;;; Write on a pipe with no one to read it. + ;;; Action: Ignored. + $pipe + ;;; Alarm clock. + ;;; Action: Terminates the process. + $alrm + ;;; Termination signal. + ;;; Action: Terminates the process. + $term + ;;; Child process terminated, stopped, or continued. + ;;; Action: Ignored. + $chld + ;;; Continue executing, if stopped. + ;;; Action: Continues executing, if stopped. + $cont + ;;; Stop executing. + ;;; Action: Stops executing. + $stop + ;;; Terminal stop signal. + ;;; Action: Stops executing. + $tstp + ;;; Background process attempting read. + ;;; Action: Stops executing. + $ttin + ;;; Background process attempting write. + ;;; Action: Stops executing. + $ttou + ;;; High bandwidth data is available at a socket. + ;;; Action: Ignored. + $urg + ;;; CPU time limit exceeded. + ;;; Action: Terminates the process. + $xcpu + ;;; File size limit exceeded. + ;;; Action: Terminates the process. + $xfsz + ;;; Virtual timer expired. + ;;; Action: Terminates the process. + $vtalrm + ;;; Profiling timer expired. + ;;; Action: Terminates the process. + $prof + ;;; Window changed. + ;;; Action: Ignored. + $winch + ;;; I/O possible. + ;;; Action: Terminates the process. + $poll + ;;; Power failure. + ;;; Action: Terminates the process. + $pwr + ;;; Bad system call. + ;;; Action: Terminates the process. + $sys + ) +) + +;;; Flags provided to `sock_recv`. +(typename $riflags + (flags (@witx repr u16) + ;;; Returns the message without removing it from the socket's receive queue. + $recv_peek + ;;; On byte-stream sockets, block until the full amount of data can be returned. + $recv_waitall + ) +) + +;;; Flags returned by `sock_recv`. +(typename $roflags + (flags (@witx repr u16) + ;;; Returned by `sock_recv`: Message data has been truncated. + $recv_data_truncated + ) +) + +;;; Flags provided to `sock_send`. As there are currently no flags +;;; defined, it must be set to zero. +(typename $siflags u16) + +;;; Which channels on a socket to shut down. +(typename $sdflags + (flags (@witx repr u8) + ;;; Disables further receive operations. + $rd + ;;; Disables further send operations. + $wr + ) +) + +;;; Identifiers for preopened capabilities. +(typename $preopentype + (enum (@witx tag u8) + ;;; A pre-opened directory. + $dir + ) +) + +;;; The contents of a $prestat when type is `preopentype::dir`. +(typename $prestat_dir + (record + ;;; The length of the directory name for use with `fd_prestat_dir_name`. + (field $pr_name_len $size) + ) +) + +;;; Information about a pre-opened capability. +(typename $prestat + (union (@witx tag $preopentype) + $prestat_dir + ) +) diff --git a/crates/wasi/witx/preview0/wasi_unstable.witx b/crates/wasi/witx/preview0/wasi_unstable.witx new file mode 100644 index 000000000000..dbece2641c49 --- /dev/null +++ b/crates/wasi/witx/preview0/wasi_unstable.witx @@ -0,0 +1,513 @@ +;; WASI Preview. This is an evolution of the API that WASI initially +;; launched with. +;; +;; Some content here is derived from [CloudABI](https://github.com/NuxiNL/cloudabi). +;; +;; This is a `witx` file. See [here](https://github.com/WebAssembly/WASI/tree/master/docs/witx.md) +;; for an explanation of what that means. + +(use "typenames.witx") + +;;; This API predated the convention of naming modules with a `wasi_unstable_` +;;; prefix and a version number. It is preserved here for compatibility, but +;;; we shouldn't follow this pattern in new APIs. +(module $wasi_unstable + ;;; Linear memory to be accessed by WASI functions that need it. + (import "memory" (memory)) + + ;;; Read command-line argument data. + ;;; The size of the array should match that returned by `args_sizes_get`. + ;;; Each argument is expected to be `\0` terminated. + (@interface func (export "args_get") + (param $argv (@witx pointer (@witx pointer u8))) + (param $argv_buf (@witx pointer u8)) + (result $error (expected (error $errno))) + ) + ;;; Return command-line argument data sizes. + (@interface func (export "args_sizes_get") + ;;; Returns the number of arguments and the size of the argument string + ;;; data, or an error. + (result $error (expected (tuple $size $size) (error $errno))) + ) + + ;;; Read environment variable data. + ;;; The sizes of the buffers should match that returned by `environ_sizes_get`. + ;;; Key/value pairs are expected to be joined with `=`s, and terminated with `\0`s. + (@interface func (export "environ_get") + (param $environ (@witx pointer (@witx pointer u8))) + (param $environ_buf (@witx pointer u8)) + (result $error (expected (error $errno))) + ) + ;;; Return environment variable data sizes. + (@interface func (export "environ_sizes_get") + ;;; Returns the number of environment variable arguments and the size of the + ;;; environment variable data. + (result $error (expected (tuple $size $size) (error $errno))) + ) + + ;;; Return the resolution of a clock. + ;;; Implementations are required to provide a non-zero value for supported clocks. For unsupported clocks, return + ;;; `errno::inval`. + ;;; Note: This is similar to `clock_getres` in POSIX. + (@interface func (export "clock_res_get") + ;;; The clock for which to return the resolution. + (param $id $clockid) + ;;; The resolution of the clock, or an error if one happened. + (result $error (expected $timestamp (error $errno))) + ) + ;;; Return the time value of a clock. + ;;; Note: This is similar to `clock_gettime` in POSIX. + (@interface func (export "clock_time_get") + ;;; The clock for which to return the time. + (param $id $clockid) + ;;; The maximum lag (exclusive) that the returned time value may have, compared to its actual value. + (param $precision $timestamp) + ;;; The time value of the clock. + (result $error (expected $timestamp (error $errno))) + ) + + ;;; Provide file advisory information on a file descriptor. + ;;; Note: This is similar to `posix_fadvise` in POSIX. + (@interface func (export "fd_advise") + (param $fd $fd) + ;;; The offset within the file to which the advisory applies. + (param $offset $filesize) + ;;; The length of the region to which the advisory applies. + (param $len $filesize) + ;;; The advice. + (param $advice $advice) + (result $error (expected (error $errno))) + ) + + ;;; Force the allocation of space in a file. + ;;; Note: This is similar to `posix_fallocate` in POSIX. + (@interface func (export "fd_allocate") + (param $fd $fd) + ;;; The offset at which to start the allocation. + (param $offset $filesize) + ;;; The length of the area that is allocated. + (param $len $filesize) + (result $error (expected (error $errno))) + ) + + ;;; Close a file descriptor. + ;;; Note: This is similar to `close` in POSIX. + (@interface func (export "fd_close") + (param $fd $fd) + (result $error (expected (error $errno))) + ) + + ;;; Synchronize the data of a file to disk. + ;;; Note: This is similar to `fdatasync` in POSIX. + (@interface func (export "fd_datasync") + (param $fd $fd) + (result $error (expected (error $errno))) + ) + + ;;; Get the attributes of a file descriptor. + ;;; Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields. + (@interface func (export "fd_fdstat_get") + (param $fd $fd) + ;;; The buffer where the file descriptor's attributes are stored. + (result $error (expected $fdstat (error $errno))) + ) + + ;;; Adjust the flags associated with a file descriptor. + ;;; Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX. + (@interface func (export "fd_fdstat_set_flags") + (param $fd $fd) + ;;; The desired values of the file descriptor flags. + (param $flags $fdflags) + (result $error (expected (error $errno))) + ) + + ;;; Adjust the rights associated with a file descriptor. + ;;; This can only be used to remove rights, and returns `errno::notcapable` if called in a way that would attempt to add rights + (@interface func (export "fd_fdstat_set_rights") + (param $fd $fd) + ;;; The desired rights of the file descriptor. + (param $fs_rights_base $rights) + (param $fs_rights_inheriting $rights) + (result $error (expected (error $errno))) + ) + + ;;; Return the attributes of an open file. + (@interface func (export "fd_filestat_get") + (param $fd $fd) + ;;; The buffer where the file's attributes are stored. + (result $error (expected $filestat (error $errno))) + ) + + ;;; Adjust the size of an open file. If this increases the file's size, the extra bytes are filled with zeros. + ;;; Note: This is similar to `ftruncate` in POSIX. + (@interface func (export "fd_filestat_set_size") + (param $fd $fd) + ;;; The desired file size. + (param $size $filesize) + (result $error (expected (error $errno))) + ) + + ;;; Adjust the timestamps of an open file or directory. + ;;; Note: This is similar to `futimens` in POSIX. + (@interface func (export "fd_filestat_set_times") + (param $fd $fd) + ;;; The desired values of the data access timestamp. + (param $atim $timestamp) + ;;; The desired values of the data modification timestamp. + (param $mtim $timestamp) + ;;; A bitmask indicating which timestamps to adjust. + (param $fst_flags $fstflags) + (result $error (expected (error $errno))) + ) + + ;;; Read from a file descriptor, without using and updating the file descriptor's offset. + ;;; Note: This is similar to `preadv` in POSIX. + (@interface func (export "fd_pread") + (param $fd $fd) + ;;; List of scatter/gather vectors in which to store data. + (param $iovs $iovec_array) + ;;; The offset within the file at which to read. + (param $offset $filesize) + ;;; The number of bytes read. + (result $error (expected $size (error $errno))) + ) + + ;;; Return a description of the given preopened file descriptor. + (@interface func (export "fd_prestat_get") + (param $fd $fd) + ;;; The buffer where the description is stored. + (result $error (expected $prestat (error $errno))) + ) + + ;;; Return a description of the given preopened file descriptor. + (@interface func (export "fd_prestat_dir_name") + (param $fd $fd) + ;;; A buffer into which to write the preopened directory name. + (param $path (@witx pointer u8)) + (param $path_len $size) + (result $error (expected (error $errno))) + ) + + ;;; Write to a file descriptor, without using and updating the file descriptor's offset. + ;;; Note: This is similar to `pwritev` in POSIX. + (@interface func (export "fd_pwrite") + (param $fd $fd) + ;;; List of scatter/gather vectors from which to retrieve data. + (param $iovs $ciovec_array) + ;;; The offset within the file at which to write. + (param $offset $filesize) + ;;; The number of bytes written. + (result $error (expected $size (error $errno))) + ) + + ;;; Read from a file descriptor. + ;;; Note: This is similar to `readv` in POSIX. + (@interface func (export "fd_read") + (param $fd $fd) + ;;; List of scatter/gather vectors to which to store data. + (param $iovs $iovec_array) + ;;; The number of bytes read. + (result $error (expected $size (error $errno))) + ) + + ;;; Read directory entries from a directory. + ;;; When successful, the contents of the output buffer consist of a sequence of + ;;; directory entries. Each directory entry consists of a `dirent` object, + ;;; followed by `dirent::d_namlen` bytes holding the name of the directory + ;;; entry. + ;; + ;;; This function fills the output buffer as much as possible, potentially + ;;; truncating the last directory entry. This allows the caller to grow its + ;;; read buffer size in case it's too small to fit a single large directory + ;;; entry, or skip the oversized directory entry. + (@interface func (export "fd_readdir") + (param $fd $fd) + ;;; The buffer where directory entries are stored + (param $buf (@witx pointer u8)) + (param $buf_len $size) + ;;; The location within the directory to start reading + (param $cookie $dircookie) + ;;; The number of bytes stored in the read buffer. If less than the size of the read buffer, the end of the directory has been reached. + (result $error (expected $size (error $errno))) + ) + + ;;; Atomically replace a file descriptor by renumbering another file descriptor. + ;; + ;;; Due to the strong focus on thread safety, this environment does not provide + ;;; a mechanism to duplicate or renumber a file descriptor to an arbitrary + ;;; number, like `dup2()`. This would be prone to race conditions, as an actual + ;;; file descriptor with the same number could be allocated by a different + ;;; thread at the same time. + ;; + ;;; This function provides a way to atomically renumber file descriptors, which + ;;; would disappear if `dup2()` were to be removed entirely. + (@interface func (export "fd_renumber") + (param $fd $fd) + ;;; The file descriptor to overwrite. + (param $to $fd) + (result $error (expected (error $errno))) + ) + + ;;; Move the offset of a file descriptor. + ;;; Note: This is similar to `lseek` in POSIX. + (@interface func (export "fd_seek") + (param $fd $fd) + ;;; The number of bytes to move. + (param $offset $filedelta) + ;;; The base from which the offset is relative. + (param $whence $whence) + ;;; The new offset of the file descriptor, relative to the start of the file. + (result $error (expected $filesize (error $errno))) + ) + + ;;; Synchronize the data and metadata of a file to disk. + ;;; Note: This is similar to `fsync` in POSIX. + (@interface func (export "fd_sync") + (param $fd $fd) + (result $error (expected (error $errno))) + ) + + ;;; Return the current offset of a file descriptor. + ;;; Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX. + (@interface func (export "fd_tell") + (param $fd $fd) + ;;; The current offset of the file descriptor, relative to the start of the file. + (result $error (expected $filesize (error $errno))) + ) + + ;;; Write to a file descriptor. + ;;; Note: This is similar to `writev` in POSIX. + (@interface func (export "fd_write") + (param $fd $fd) + ;;; List of scatter/gather vectors from which to retrieve data. + (param $iovs $ciovec_array) + (result $error (expected $size (error $errno))) + ) + + ;;; Create a directory. + ;;; Note: This is similar to `mkdirat` in POSIX. + (@interface func (export "path_create_directory") + (param $fd $fd) + ;;; The path at which to create the directory. + (param $path string) + (result $error (expected (error $errno))) + ) + + ;;; Return the attributes of a file or directory. + ;;; Note: This is similar to `stat` in POSIX. + (@interface func (export "path_filestat_get") + (param $fd $fd) + ;;; Flags determining the method of how the path is resolved. + (param $flags $lookupflags) + ;;; The path of the file or directory to inspect. + (param $path string) + ;;; The buffer where the file's attributes are stored. + (result $error (expected $filestat (error $errno))) + ) + + ;;; Adjust the timestamps of a file or directory. + ;;; Note: This is similar to `utimensat` in POSIX. + (@interface func (export "path_filestat_set_times") + (param $fd $fd) + ;;; Flags determining the method of how the path is resolved. + (param $flags $lookupflags) + ;;; The path of the file or directory to operate on. + (param $path string) + ;;; The desired values of the data access timestamp. + (param $atim $timestamp) + ;;; The desired values of the data modification timestamp. + (param $mtim $timestamp) + ;;; A bitmask indicating which timestamps to adjust. + (param $fst_flags $fstflags) + (result $error (expected (error $errno))) + ) + + ;;; Create a hard link. + ;;; Note: This is similar to `linkat` in POSIX. + (@interface func (export "path_link") + (param $old_fd $fd) + ;;; Flags determining the method of how the path is resolved. + (param $old_flags $lookupflags) + ;;; The source path from which to link. + (param $old_path string) + ;;; The working directory at which the resolution of the new path starts. + (param $new_fd $fd) + ;;; The destination path at which to create the hard link. + (param $new_path string) + (result $error (expected (error $errno))) + ) + + ;;; Open a file or directory. + ;; + ;;; The returned file descriptor is not guaranteed to be the lowest-numbered + ;;; file descriptor not currently open; it is randomized to prevent + ;;; applications from depending on making assumptions about indexes, since this + ;;; is error-prone in multi-threaded contexts. The returned file descriptor is + ;;; guaranteed to be less than 2**31. + ;; + ;;; Note: This is similar to `openat` in POSIX. + (@interface func (export "path_open") + (param $fd $fd) + ;;; Flags determining the method of how the path is resolved. + (param $dirflags $lookupflags) + ;;; The relative path of the file or directory to open, relative to the + ;;; `path_open::fd` directory. + (param $path string) + ;;; The method by which to open the file. + (param $oflags $oflags) + ;;; The initial rights of the newly created file descriptor. The + ;;; implementation is allowed to return a file descriptor with fewer rights + ;;; than specified, if and only if those rights do not apply to the type of + ;;; file being opened. + ;; + ;;; The *base* rights are rights that will apply to operations using the file + ;;; descriptor itself, while the *inheriting* rights are rights that apply to + ;;; file descriptors derived from it. + (param $fs_rights_base $rights) + (param $fs_rights_inheriting $rights) + (param $fdflags $fdflags) + ;;; The file descriptor of the file that has been opened. + (result $error (expected $fd (error $errno))) + ) + + ;;; Read the contents of a symbolic link. + ;;; Note: This is similar to `readlinkat` in POSIX. + (@interface func (export "path_readlink") + (param $fd $fd) + ;;; The path of the symbolic link from which to read. + (param $path string) + ;;; The buffer to which to write the contents of the symbolic link. + (param $buf (@witx pointer u8)) + (param $buf_len $size) + ;;; The number of bytes placed in the buffer. + (result $error (expected $size (error $errno))) + ) + + ;;; Remove a directory. + ;;; Return `errno::notempty` if the directory is not empty. + ;;; Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + (@interface func (export "path_remove_directory") + (param $fd $fd) + ;;; The path to a directory to remove. + (param $path string) + (result $error (expected (error $errno))) + ) + + ;;; Rename a file or directory. + ;;; Note: This is similar to `renameat` in POSIX. + (@interface func (export "path_rename") + (param $fd $fd) + ;;; The source path of the file or directory to rename. + (param $old_path string) + ;;; The working directory at which the resolution of the new path starts. + (param $new_fd $fd) + ;;; The destination path to which to rename the file or directory. + (param $new_path string) + (result $error (expected (error $errno))) + ) + + ;;; Create a symbolic link. + ;;; Note: This is similar to `symlinkat` in POSIX. + (@interface func (export "path_symlink") + ;;; The contents of the symbolic link. + (param $old_path string) + (param $fd $fd) + ;;; The destination path at which to create the symbolic link. + (param $new_path string) + (result $error (expected (error $errno))) + ) + + + ;;; Unlink a file. + ;;; Return `errno::isdir` if the path refers to a directory. + ;;; Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. + (@interface func (export "path_unlink_file") + (param $fd $fd) + ;;; The path to a file to unlink. + (param $path string) + (result $error (expected (error $errno))) + ) + + ;;; Concurrently poll for the occurrence of a set of events. + (@interface func (export "poll_oneoff") + ;;; The events to which to subscribe. + (param $in (@witx const_pointer $subscription)) + ;;; The events that have occurred. + (param $out (@witx pointer $event)) + ;;; Both the number of subscriptions and events. + (param $nsubscriptions $size) + ;;; The number of events stored. + (result $error (expected $size (error $errno))) + ) + + ;;; Terminate the process normally. An exit code of 0 indicates successful + ;;; termination of the program. The meanings of other values is dependent on + ;;; the environment. + (@interface func (export "proc_exit") + ;;; The exit code returned by the process. + (param $rval $exitcode) + (@witx noreturn) + ) + + ;;; Send a signal to the process of the calling thread. + ;;; Note: This is similar to `raise` in POSIX. + (@interface func (export "proc_raise") + ;;; The signal condition to trigger. + (param $sig $signal) + (result $error (expected (error $errno))) + ) + + ;;; Temporarily yield execution of the calling thread. + ;;; Note: This is similar to `sched_yield` in POSIX. + (@interface func (export "sched_yield") + (result $error (expected (error $errno))) + ) + + ;;; Write high-quality random data into a buffer. + ;;; This function blocks when the implementation is unable to immediately + ;;; provide sufficient high-quality random data. + ;;; This function may execute slowly, so when large mounts of random data are + ;;; required, it's advisable to use this function to seed a pseudo-random + ;;; number generator, rather than to provide the random data directly. + (@interface func (export "random_get") + ;;; The buffer to fill with random data. + (param $buf (@witx pointer u8)) + (param $buf_len $size) + (result $error (expected (error $errno))) + ) + + ;;; Receive a message from a socket. + ;;; Note: This is similar to `recv` in POSIX, though it also supports reading + ;;; the data into multiple buffers in the manner of `readv`. + (@interface func (export "sock_recv") + (param $fd $fd) + ;;; List of scatter/gather vectors to which to store data. + (param $ri_data $iovec_array) + ;;; Message flags. + (param $ri_flags $riflags) + ;;; Number of bytes stored in ri_data and message flags. + (result $error (expected (tuple $size $roflags) (error $errno))) + ) + + ;;; Send a message on a socket. + ;;; Note: This is similar to `send` in POSIX, though it also supports writing + ;;; the data from multiple buffers in the manner of `writev`. + (@interface func (export "sock_send") + (param $fd $fd) + ;;; List of scatter/gather vectors to which to retrieve data + (param $si_data $ciovec_array) + ;;; Message flags. + (param $si_flags $siflags) + ;;; Number of bytes transmitted. + (result $error (expected $size (error $errno))) + ) + + ;;; Shut down socket send and receive channels. + ;;; Note: This is similar to `shutdown` in POSIX. + (@interface func (export "sock_shutdown") + (param $fd $fd) + ;;; Which channels on the socket to shut down. + (param $how $sdflags) + (result $error (expected (error $errno))) + ) +) diff --git a/crates/wasi/witx/typenames.witx b/crates/wasi/witx/preview1/typenames.witx similarity index 100% rename from crates/wasi/witx/typenames.witx rename to crates/wasi/witx/preview1/typenames.witx diff --git a/crates/wasi/witx/wasi_snapshot_preview1.witx b/crates/wasi/witx/preview1/wasi_snapshot_preview1.witx similarity index 100% rename from crates/wasi/witx/wasi_snapshot_preview1.witx rename to crates/wasi/witx/preview1/wasi_snapshot_preview1.witx diff --git a/crates/wiggle/src/lib.rs b/crates/wiggle/src/lib.rs index 7bad7f20ee07..27a20c94f02d 100644 --- a/crates/wiggle/src/lib.rs +++ b/crates/wiggle/src/lib.rs @@ -402,7 +402,7 @@ impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> { /// etc of the returned pointer. pub fn cast(&self) -> GuestPtr<'a, U> where - T: Pointee, + U: Pointee + ?Sized, { GuestPtr::new(self.mem, self.pointer) } diff --git a/src/commands/run.rs b/src/commands/run.rs index 6ca015dddf67..cb0912bf5e3e 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -598,6 +598,9 @@ impl RunCommand { // If preview2 was explicitly requested, always use it. // Otherwise use it so long as threads are disabled. (Some(true), _) | (None, Some(false) | None) => { + if self.run.common.wasi.preview0 != Some(false) { + preview2::preview0::add_to_linker_sync(linker)?; + } preview2::preview1::add_to_linker_sync(linker)?; self.set_preview2_ctx(store)?; } diff --git a/tests/all/cli_tests.rs b/tests/all/cli_tests.rs index ff336feb8daf..03783844b7b3 100644 --- a/tests/all/cli_tests.rs +++ b/tests/all/cli_tests.rs @@ -188,8 +188,10 @@ fn run_wasmtime_unreachable_wat() -> Result<()> { #[test] fn hello_wasi_snapshot0() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot0.wat")?; - let stdout = run_wasmtime(&["-Ccache=n", "-Spreview2=n", wasm.path().to_str().unwrap()])?; - assert_eq!(stdout, "Hello, world!\n"); + for preview2 in ["-Spreview2=n", "-Spreview2=y"] { + let stdout = run_wasmtime(&["-Ccache=n", preview2, wasm.path().to_str().unwrap()])?; + assert_eq!(stdout, "Hello, world!\n"); + } Ok(()) } @@ -252,11 +254,14 @@ fn timeout_in_invoke() -> Result<()> { #[test] fn exit2_wasi_snapshot0() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/exit2_wasi_snapshot0.wat")?; - let output = run_wasmtime_for_output( - &["-Ccache=n", "-Spreview2=n", wasm.path().to_str().unwrap()], - None, - )?; - assert_eq!(output.status.code().unwrap(), 2); + + for preview2 in ["-Spreview2=n", "-Spreview2=y"] { + let output = run_wasmtime_for_output( + &["-Ccache=n", preview2, wasm.path().to_str().unwrap()], + None, + )?; + assert_eq!(output.status.code().unwrap(), 2); + } Ok(()) } @@ -273,14 +278,17 @@ fn exit2_wasi_snapshot1() -> Result<()> { #[test] fn exit125_wasi_snapshot0() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/exit125_wasi_snapshot0.wat")?; - let output = run_wasmtime_for_output( - &["-Ccache=n", "-Spreview2=n", wasm.path().to_str().unwrap()], - None, - )?; - if cfg!(windows) { - assert_eq!(output.status.code().unwrap(), 1); - } else { - assert_eq!(output.status.code().unwrap(), 125); + for preview2 in ["-Spreview2=n", "-Spreview2=y"] { + let output = run_wasmtime_for_output( + &["-Ccache=n", preview2, wasm.path().to_str().unwrap()], + None, + )?; + dbg!(&output); + if cfg!(windows) { + assert_eq!(output.status.code().unwrap(), 1); + } else { + assert_eq!(output.status.code().unwrap(), 125); + } } Ok(()) } @@ -302,13 +310,16 @@ fn exit125_wasi_snapshot1() -> Result<()> { #[test] fn exit126_wasi_snapshot0() -> Result<()> { let wasm = build_wasm("tests/all/cli_tests/exit126_wasi_snapshot0.wat")?; - let output = run_wasmtime_for_output( - &["-Ccache=n", "-Spreview2=n", wasm.path().to_str().unwrap()], - None, - )?; - assert_eq!(output.status.code().unwrap(), 1); - assert!(output.stdout.is_empty()); - assert!(String::from_utf8_lossy(&output.stderr).contains("invalid exit status")); + + for preview2 in ["-Spreview2=n", "-Spreview2=y"] { + let output = run_wasmtime_for_output( + &["-Ccache=n", preview2, wasm.path().to_str().unwrap()], + None, + )?; + assert_eq!(output.status.code().unwrap(), 1); + assert!(output.stdout.is_empty()); + assert!(String::from_utf8_lossy(&output.stderr).contains("invalid exit status")); + } Ok(()) } @@ -450,20 +461,22 @@ fn hello_wasi_snapshot0_from_stdin() -> Result<()> { // Run a simple WASI hello world, snapshot0 edition. // The module is piped from standard input. let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot0.wat")?; - let stdout = { - let path = wasm.path(); - let args: &[&str] = &["-Ccache=n", "-Spreview2=n", "-"]; - let output = run_wasmtime_for_output(args, Some(path))?; - if !output.status.success() { - bail!( - "Failed to execute wasmtime with: {:?}\n{}", - args, - String::from_utf8_lossy(&output.stderr) - ); - } - Ok::<_, anyhow::Error>(String::from_utf8(output.stdout).unwrap()) - }?; - assert_eq!(stdout, "Hello, world!\n"); + for preview2 in ["-Spreview2=n", "-Spreview2=y"] { + let stdout = { + let path = wasm.path(); + let args: &[&str] = &["-Ccache=n", preview2, "-"]; + let output = run_wasmtime_for_output(args, Some(path))?; + if !output.status.success() { + bail!( + "Failed to execute wasmtime with: {:?}\n{}", + args, + String::from_utf8_lossy(&output.stderr) + ); + } + Ok::<_, anyhow::Error>(String::from_utf8(output.stdout).unwrap()) + }?; + assert_eq!(stdout, "Hello, world!\n"); + } Ok(()) }