From 760558469154f17963e8a7c0fbb0bb3ec4cb2bbf Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 19 May 2019 14:52:41 +0200 Subject: [PATCH] Move *nix specific implementation to separate module --- .gitignore | 1 + Cargo.toml | 8 +- src/ctx.rs | 116 +- src/host.rs | 236 --- src/hostcalls/fs.rs | 1136 +----------- src/hostcalls/misc.rs | 299 +--- src/hostcalls/mod.rs | 3 - src/hostcalls/sock.rs | 29 +- src/lib.rs | 7 +- src/memory.rs | 41 +- src/sys/mod.rs | 13 + src/{fdentry.rs => sys/unix/fdmap.rs} | 107 +- src/sys/unix/host.rs | 240 +++ src/sys/unix/hostcalls/fs.rs | 1347 ++++++++++++++ src/{ => sys/unix}/hostcalls/fs_helpers.rs | 7 +- src/sys/unix/hostcalls/misc.rs | 313 ++++ src/sys/unix/hostcalls/mod.rs | 10 + src/sys/unix/hostcalls/sock.rs | 41 + src/sys/unix/mod.rs | 42 + src/sys/windows/mod.rs | 0 src/wasm32.rs | 1878 ++++++++++---------- 21 files changed, 3184 insertions(+), 2690 deletions(-) create mode 100644 src/sys/mod.rs rename src/{fdentry.rs => sys/unix/fdmap.rs} (67%) create mode 100644 src/sys/unix/host.rs create mode 100644 src/sys/unix/hostcalls/fs.rs rename src/{ => sys/unix}/hostcalls/fs_helpers.rs (97%) create mode 100644 src/sys/unix/hostcalls/misc.rs create mode 100644 src/sys/unix/hostcalls/mod.rs create mode 100644 src/sys/unix/hostcalls/sock.rs create mode 100644 src/sys/unix/mod.rs create mode 100644 src/sys/windows/mod.rs diff --git a/.gitignore b/.gitignore index a18277be79..b67a9a3878 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target Cargo.lock **/*.rs.bk +.DS_Store diff --git a/Cargo.toml b/Cargo.toml index 6ebec5be56..61fa18dd46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,14 @@ wasi-common-cbindgen = { path = "wasi-common-cbindgen" } cast = "0.2" failure = "0.1" libc = "0.2" -nix = "0.13" rand = "0.6" +cfg-if = "0.1.9" + +[target.'cfg(unix)'.dependencies] +nix = "0.13" + +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3", features=["std", "handleapi", "processthreadsapi", "winbase", "ws2def", "fileapi"] } [lib] name = "wasi_common" diff --git a/src/ctx.rs b/src/ctx.rs index 6b8d5d0e94..019239955f 100644 --- a/src/ctx.rs +++ b/src/ctx.rs @@ -1,17 +1,17 @@ -use crate::fdentry::FdEntry; use crate::host; +use crate::sys::dev_null; +use crate::sys::fdmap::{FdEntry, FdMap}; + use failure::{bail, format_err, Error}; -use nix::unistd::dup; use std::collections::HashMap; use std::ffi::{CStr, CString}; use std::fs::File; use std::io::{stderr, stdin, stdout}; -use std::os::unix::prelude::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use std::path::{Path, PathBuf}; pub struct WasiCtxBuilder { - fds: HashMap, + fds: FdMap, preopens: HashMap, args: Vec, env: HashMap, @@ -20,16 +20,18 @@ pub struct WasiCtxBuilder { impl WasiCtxBuilder { /// Builder for a new `WasiCtx`. pub fn new() -> Self { - let null = dev_null(); - WasiCtxBuilder { - fds: HashMap::new(), + let mut builder = Self { + fds: FdMap::new(), preopens: HashMap::new(), args: vec![], env: HashMap::new(), - } - .fd_dup(0, &null) - .fd_dup(1, &null) - .fd_dup(2, &null) + }; + + builder.fds.insert_fd_entry_at(0, FdEntry::from_file(dev_null())); + builder.fds.insert_fd_entry_at(1, FdEntry::from_file(dev_null())); + builder.fds.insert_fd_entry_at(2, FdEntry::from_file(dev_null())); + + builder } pub fn args(mut self, args: &[&str]) -> Self { @@ -59,10 +61,11 @@ impl WasiCtxBuilder { self } - pub fn inherit_stdio(self) -> Self { - self.fd_dup(0, &stdin()) - .fd_dup(1, &stdout()) - .fd_dup(2, &stderr()) + pub fn inherit_stdio(mut self) -> Self { + self.fds.insert_fd_entry_at(0, FdEntry::duplicate(&stdin())); + self.fds.insert_fd_entry_at(1, FdEntry::duplicate(&stdout())); + self.fds.insert_fd_entry_at(2, FdEntry::duplicate(&stderr())); + self } pub fn inherit_env(mut self) -> Self { @@ -97,66 +100,21 @@ impl WasiCtxBuilder { self } - /// Add an existing file-like object as a file descriptor in the context. - /// - /// When the `WasiCtx` is dropped, all of its associated file descriptors are `close`d. If you - /// do not want this to close the existing object, use `WasiCtxBuilder::fd_dup()`. - pub fn fd(self, wasm_fd: host::__wasi_fd_t, fd: F) -> Self { - // safe because we're getting a valid RawFd from the F directly - unsafe { self.raw_fd(wasm_fd, fd.into_raw_fd()) } - } - - /// Add an existing file-like object as a duplicate file descriptor in the context. - /// - /// The underlying file descriptor of this object will be duplicated before being added to the - /// context, so it will not be closed when the `WasiCtx` is dropped. - /// - /// TODO: handle `dup` errors - pub fn fd_dup(self, wasm_fd: host::__wasi_fd_t, fd: &F) -> Self { - // safe because we're getting a valid RawFd from the F directly - unsafe { self.raw_fd(wasm_fd, dup(fd.as_raw_fd()).unwrap()) } - } - - /// Add an existing file descriptor to the context. - /// - /// When the `WasiCtx` is dropped, this file descriptor will be `close`d. If you do not want to - /// close the existing descriptor, use `WasiCtxBuilder::raw_fd_dup()`. - pub unsafe fn raw_fd(mut self, wasm_fd: host::__wasi_fd_t, fd: RawFd) -> Self { - self.fds.insert(wasm_fd, FdEntry::from_raw_fd(fd)); - self - } - - /// Add a duplicate of an existing file descriptor to the context. - /// - /// The file descriptor will be duplicated before being added to the context, so it will not be - /// closed when the `WasiCtx` is dropped. - /// - /// TODO: handle `dup` errors - pub unsafe fn raw_fd_dup(self, wasm_fd: host::__wasi_fd_t, fd: RawFd) -> Self { - self.raw_fd(wasm_fd, dup(fd).unwrap()) - } - pub fn preopened_dir>(mut self, dir: File, guest_path: P) -> Self { self.preopens.insert(guest_path.as_ref().to_owned(), dir); self } pub fn build(mut self) -> Result { - // startup code starts looking at fd 3 for preopens - let mut preopen_fd = 3; for (guest_path, dir) in self.preopens { if !dir.metadata()?.is_dir() { bail!("preopened file is not a directory"); } - while self.fds.contains_key(&preopen_fd) { - preopen_fd = preopen_fd - .checked_add(1) - .ok_or(format_err!("not enough file handles"))?; - } let mut fe = FdEntry::from_file(dir); fe.preopen_path = Some(guest_path); - self.fds.insert(preopen_fd, fe); - preopen_fd += 1; + self.fds + .insert_fd_entry(fe) + .map_err(|_| format_err!("not enough file handles"))?; } let env = self @@ -181,7 +139,7 @@ impl WasiCtxBuilder { #[derive(Debug)] pub struct WasiCtx { - pub fds: HashMap, + pub fds: FdMap, pub args: Vec, pub env: Vec, } @@ -194,7 +152,7 @@ impl WasiCtx { /// - Environment variables are inherited from the host process. /// /// To override these behaviors, use `WasiCtxBuilder`. - pub fn new(args: &[&str]) -> WasiCtx { + pub fn new(args: &[&str]) -> Self { WasiCtxBuilder::new() .args(args) .inherit_stdio() @@ -209,37 +167,13 @@ impl WasiCtx { rights_base: host::__wasi_rights_t, rights_inheriting: host::__wasi_rights_t, ) -> Result<&FdEntry, host::__wasi_errno_t> { - if let Some(fe) = self.fds.get(&fd) { - // validate rights - if !fe.rights_base & rights_base != 0 || !fe.rights_inheriting & rights_inheriting != 0 - { - Err(host::__WASI_ENOTCAPABLE) - } else { - Ok(fe) - } - } else { - Err(host::__WASI_EBADF) - } + self.fds.get_fd_entry(fd, rights_base, rights_inheriting) } pub fn insert_fd_entry( &mut self, fe: FdEntry, ) -> Result { - // never insert where stdio handles usually are - let mut fd = 3; - while self.fds.contains_key(&fd) { - if let Some(next_fd) = fd.checked_add(1) { - fd = next_fd; - } else { - return Err(host::__WASI_EMFILE); - } - } - self.fds.insert(fd, fe); - Ok(fd) + self.fds.insert_fd_entry(fe) } } - -fn dev_null() -> File { - File::open("/dev/null").expect("failed to open /dev/null") -} diff --git a/src/host.rs b/src/host.rs index e2f19ab4cc..d5f981f5f7 100644 --- a/src/host.rs +++ b/src/host.rs @@ -4,7 +4,6 @@ #![allow(non_camel_case_types)] #![allow(non_snake_case)] - pub type void = ::std::os::raw::c_void; pub type __wasi_advice_t = u8; @@ -474,241 +473,6 @@ pub struct __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_fd_ pub fd: __wasi_fd_t, } -pub fn errno_from_nix(errno: nix::errno::Errno) -> __wasi_errno_t { - match errno { - nix::errno::Errno::EPERM => __WASI_EPERM, - nix::errno::Errno::ENOENT => __WASI_ENOENT, - nix::errno::Errno::ESRCH => __WASI_ESRCH, - nix::errno::Errno::EINTR => __WASI_EINTR, - nix::errno::Errno::EIO => __WASI_EIO, - nix::errno::Errno::ENXIO => __WASI_ENXIO, - nix::errno::Errno::E2BIG => __WASI_E2BIG, - nix::errno::Errno::ENOEXEC => __WASI_ENOEXEC, - nix::errno::Errno::EBADF => __WASI_EBADF, - nix::errno::Errno::ECHILD => __WASI_ECHILD, - nix::errno::Errno::EAGAIN => __WASI_EAGAIN, - nix::errno::Errno::ENOMEM => __WASI_ENOMEM, - nix::errno::Errno::EACCES => __WASI_EACCES, - nix::errno::Errno::EFAULT => __WASI_EFAULT, - nix::errno::Errno::EBUSY => __WASI_EBUSY, - nix::errno::Errno::EEXIST => __WASI_EEXIST, - nix::errno::Errno::EXDEV => __WASI_EXDEV, - nix::errno::Errno::ENODEV => __WASI_ENODEV, - nix::errno::Errno::ENOTDIR => __WASI_ENOTDIR, - nix::errno::Errno::EISDIR => __WASI_EISDIR, - nix::errno::Errno::EINVAL => __WASI_EINVAL, - nix::errno::Errno::ENFILE => __WASI_ENFILE, - nix::errno::Errno::EMFILE => __WASI_EMFILE, - nix::errno::Errno::ENOTTY => __WASI_ENOTTY, - nix::errno::Errno::ETXTBSY => __WASI_ETXTBSY, - nix::errno::Errno::EFBIG => __WASI_EFBIG, - nix::errno::Errno::ENOSPC => __WASI_ENOSPC, - nix::errno::Errno::ESPIPE => __WASI_ESPIPE, - nix::errno::Errno::EROFS => __WASI_EROFS, - nix::errno::Errno::EMLINK => __WASI_EMLINK, - nix::errno::Errno::EPIPE => __WASI_EPIPE, - nix::errno::Errno::EDOM => __WASI_EDOM, - nix::errno::Errno::ERANGE => __WASI_ERANGE, - nix::errno::Errno::EDEADLK => __WASI_EDEADLK, - nix::errno::Errno::ENAMETOOLONG => __WASI_ENAMETOOLONG, - nix::errno::Errno::ENOLCK => __WASI_ENOLCK, - nix::errno::Errno::ENOSYS => __WASI_ENOSYS, - nix::errno::Errno::ENOTEMPTY => __WASI_ENOTEMPTY, - nix::errno::Errno::ELOOP => __WASI_ELOOP, - nix::errno::Errno::ENOMSG => __WASI_ENOMSG, - nix::errno::Errno::EIDRM => __WASI_EIDRM, - nix::errno::Errno::ENOLINK => __WASI_ENOLINK, - nix::errno::Errno::EPROTO => __WASI_EPROTO, - nix::errno::Errno::EMULTIHOP => __WASI_EMULTIHOP, - nix::errno::Errno::EBADMSG => __WASI_EBADMSG, - nix::errno::Errno::EOVERFLOW => __WASI_EOVERFLOW, - nix::errno::Errno::EILSEQ => __WASI_EILSEQ, - nix::errno::Errno::ENOTSOCK => __WASI_ENOTSOCK, - nix::errno::Errno::EDESTADDRREQ => __WASI_EDESTADDRREQ, - nix::errno::Errno::EMSGSIZE => __WASI_EMSGSIZE, - nix::errno::Errno::EPROTOTYPE => __WASI_EPROTOTYPE, - nix::errno::Errno::ENOPROTOOPT => __WASI_ENOPROTOOPT, - nix::errno::Errno::EPROTONOSUPPORT => __WASI_EPROTONOSUPPORT, - nix::errno::Errno::EAFNOSUPPORT => __WASI_EAFNOSUPPORT, - nix::errno::Errno::EADDRINUSE => __WASI_EADDRINUSE, - nix::errno::Errno::EADDRNOTAVAIL => __WASI_EADDRNOTAVAIL, - nix::errno::Errno::ENETDOWN => __WASI_ENETDOWN, - nix::errno::Errno::ENETUNREACH => __WASI_ENETUNREACH, - nix::errno::Errno::ENETRESET => __WASI_ENETRESET, - nix::errno::Errno::ECONNABORTED => __WASI_ECONNABORTED, - nix::errno::Errno::ECONNRESET => __WASI_ECONNRESET, - nix::errno::Errno::ENOBUFS => __WASI_ENOBUFS, - nix::errno::Errno::EISCONN => __WASI_EISCONN, - nix::errno::Errno::ENOTCONN => __WASI_ENOTCONN, - nix::errno::Errno::ETIMEDOUT => __WASI_ETIMEDOUT, - nix::errno::Errno::ECONNREFUSED => __WASI_ECONNREFUSED, - nix::errno::Errno::EHOSTUNREACH => __WASI_EHOSTUNREACH, - nix::errno::Errno::EALREADY => __WASI_EALREADY, - nix::errno::Errno::EINPROGRESS => __WASI_EINPROGRESS, - nix::errno::Errno::ESTALE => __WASI_ESTALE, - nix::errno::Errno::EDQUOT => __WASI_EDQUOT, - nix::errno::Errno::ECANCELED => __WASI_ECANCELED, - nix::errno::Errno::EOWNERDEAD => __WASI_EOWNERDEAD, - nix::errno::Errno::ENOTRECOVERABLE => __WASI_ENOTRECOVERABLE, - _ => __WASI_ENOSYS, - } -} - -pub unsafe fn ciovec_to_nix<'a>(ciovec: &'a __wasi_ciovec_t) -> nix::sys::uio::IoVec<&'a [u8]> { - let slice = std::slice::from_raw_parts(ciovec.buf as *const u8, ciovec.buf_len); - nix::sys::uio::IoVec::from_slice(slice) -} - -pub unsafe fn ciovec_to_nix_mut<'a>( - ciovec: &'a mut __wasi_ciovec_t, -) -> nix::sys::uio::IoVec<&'a mut [u8]> { - let slice = std::slice::from_raw_parts_mut(ciovec.buf as *mut u8, ciovec.buf_len); - nix::sys::uio::IoVec::from_mut_slice(slice) -} - -pub unsafe fn iovec_to_nix<'a>(iovec: &'a __wasi_iovec_t) -> nix::sys::uio::IoVec<&'a [u8]> { - let slice = std::slice::from_raw_parts(iovec.buf as *const u8, iovec.buf_len); - nix::sys::uio::IoVec::from_slice(slice) -} - -pub unsafe fn iovec_to_nix_mut<'a>( - iovec: &'a mut __wasi_iovec_t, -) -> nix::sys::uio::IoVec<&'a mut [u8]> { - let slice = std::slice::from_raw_parts_mut(iovec.buf as *mut u8, iovec.buf_len); - nix::sys::uio::IoVec::from_mut_slice(slice) -} - -#[cfg(target_os = "linux")] -pub const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_RSYNC; - -#[cfg(not(target_os = "linux"))] -pub const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_SYNC; - -pub fn nix_from_fdflags(fdflags: __wasi_fdflags_t) -> nix::fcntl::OFlag { - use nix::fcntl::OFlag; - let mut nix_flags = OFlag::empty(); - if fdflags & __WASI_FDFLAG_APPEND != 0 { - nix_flags.insert(OFlag::O_APPEND); - } - if fdflags & __WASI_FDFLAG_DSYNC != 0 { - nix_flags.insert(OFlag::O_DSYNC); - } - if fdflags & __WASI_FDFLAG_NONBLOCK != 0 { - nix_flags.insert(OFlag::O_NONBLOCK); - } - if fdflags & __WASI_FDFLAG_RSYNC != 0 { - nix_flags.insert(O_RSYNC); - } - if fdflags & __WASI_FDFLAG_SYNC != 0 { - nix_flags.insert(OFlag::O_SYNC); - } - nix_flags -} - -pub fn fdflags_from_nix(oflags: nix::fcntl::OFlag) -> __wasi_fdflags_t { - use nix::fcntl::OFlag; - let mut fdflags = 0; - if oflags.contains(OFlag::O_APPEND) { - fdflags |= __WASI_FDFLAG_APPEND; - } - if oflags.contains(OFlag::O_DSYNC) { - fdflags |= __WASI_FDFLAG_DSYNC; - } - if oflags.contains(OFlag::O_NONBLOCK) { - fdflags |= __WASI_FDFLAG_NONBLOCK; - } - if oflags.contains(O_RSYNC) { - fdflags |= __WASI_FDFLAG_RSYNC; - } - if oflags.contains(OFlag::O_SYNC) { - fdflags |= __WASI_FDFLAG_SYNC; - } - fdflags -} - -pub fn nix_from_oflags(oflags: __wasi_oflags_t) -> nix::fcntl::OFlag { - use nix::fcntl::OFlag; - let mut nix_flags = OFlag::empty(); - if oflags & __WASI_O_CREAT != 0 { - nix_flags.insert(OFlag::O_CREAT); - } - if oflags & __WASI_O_DIRECTORY != 0 { - nix_flags.insert(OFlag::O_DIRECTORY); - } - if oflags & __WASI_O_EXCL != 0 { - nix_flags.insert(OFlag::O_EXCL); - } - if oflags & __WASI_O_TRUNC != 0 { - nix_flags.insert(OFlag::O_TRUNC); - } - nix_flags -} - -pub fn filetype_from_nix(sflags: nix::sys::stat::SFlag) -> __wasi_filetype_t { - use nix::sys::stat::SFlag; - if sflags.contains(SFlag::S_IFCHR) { - __WASI_FILETYPE_CHARACTER_DEVICE - } else if sflags.contains(SFlag::S_IFBLK) { - __WASI_FILETYPE_BLOCK_DEVICE - } else if sflags.contains(SFlag::S_IFIFO) | sflags.contains(SFlag::S_IFSOCK) { - __WASI_FILETYPE_SOCKET_STREAM - } else if sflags.contains(SFlag::S_IFDIR) { - __WASI_FILETYPE_DIRECTORY - } else if sflags.contains(SFlag::S_IFREG) { - __WASI_FILETYPE_REGULAR_FILE - } else if sflags.contains(SFlag::S_IFLNK) { - __WASI_FILETYPE_SYMBOLIC_LINK - } else { - __WASI_FILETYPE_UNKNOWN - } -} - -pub fn nix_from_filetype(sflags: __wasi_filetype_t) -> nix::sys::stat::SFlag { - use nix::sys::stat::SFlag; - let mut nix_sflags = SFlag::empty(); - if sflags & __WASI_FILETYPE_CHARACTER_DEVICE != 0 { - nix_sflags.insert(SFlag::S_IFCHR); - } - if sflags & __WASI_FILETYPE_BLOCK_DEVICE != 0 { - nix_sflags.insert(SFlag::S_IFBLK); - } - if sflags & __WASI_FILETYPE_SOCKET_STREAM != 0 { - nix_sflags.insert(SFlag::S_IFIFO); - nix_sflags.insert(SFlag::S_IFSOCK); - } - if sflags & __WASI_FILETYPE_DIRECTORY != 0 { - nix_sflags.insert(SFlag::S_IFDIR); - } - if sflags & __WASI_FILETYPE_REGULAR_FILE != 0 { - nix_sflags.insert(SFlag::S_IFREG); - } - if sflags & __WASI_FILETYPE_SYMBOLIC_LINK != 0 { - nix_sflags.insert(SFlag::S_IFLNK); - } - nix_sflags -} - -pub fn filestat_from_nix(filestat: nix::sys::stat::FileStat) -> __wasi_filestat_t { - use std::convert::TryFrom; - - let filetype = nix::sys::stat::SFlag::from_bits_truncate(filestat.st_mode); - let dev = __wasi_device_t::try_from(filestat.st_dev) - .expect("FileStat::st_dev is trivially convertible to __wasi_device_t"); - let ino = __wasi_inode_t::try_from(filestat.st_ino) - .expect("FileStat::st_ino is trivially convertible to __wasi_inode_t"); - - __wasi_filestat_t { - st_dev: dev, - st_ino: ino, - st_nlink: filestat.st_nlink as __wasi_linkcount_t, - st_size: filestat.st_size as __wasi_filesize_t, - st_atim: filestat.st_atime as __wasi_timestamp_t, - st_ctim: filestat.st_ctime as __wasi_timestamp_t, - st_mtim: filestat.st_mtime as __wasi_timestamp_t, - st_filetype: filetype_from_nix(filetype), - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/hostcalls/fs.rs b/src/hostcalls/fs.rs index 7257b9b959..9aa3c55660 100644 --- a/src/hostcalls/fs.rs +++ b/src/hostcalls/fs.rs @@ -1,62 +1,19 @@ #![allow(non_camel_case_types)] -#![allow(unused_unsafe)] - use crate::ctx::WasiCtx; -use crate::fdentry::{determine_type_rights, FdEntry}; -use crate::memory::*; -use crate::{host, wasm32}; +use crate::wasm32; -use super::fs_helpers::*; +use crate::sys::hostcalls as hostcalls_impl; -use nix::libc::{self, c_long, c_void, off_t}; -use std::ffi::OsStr; -use std::os::unix::prelude::{FromRawFd, OsStrExt}; use wasi_common_cbindgen::wasi_common_cbindgen; #[wasi_common_cbindgen] pub fn fd_close(wasi_ctx: &mut WasiCtx, fd: wasm32::__wasi_fd_t) -> wasm32::__wasi_errno_t { - let fd = dec_fd(fd); - if let Some(fdent) = wasi_ctx.fds.get(&fd) { - // can't close preopened files - if fdent.preopen_path.is_some() { - return wasm32::__WASI_ENOTSUP; - } - } - if let Some(mut fdent) = wasi_ctx.fds.remove(&fd) { - fdent.fd_object.needs_close = false; - match nix::unistd::close(fdent.fd_object.rawfd) { - Ok(_) => wasm32::__WASI_ESUCCESS, - Err(e) => wasm32::errno_from_nix(e.as_errno().unwrap()), - } - } else { - wasm32::__WASI_EBADF - } + hostcalls_impl::fd_close(wasi_ctx, fd) } #[wasi_common_cbindgen] pub fn fd_datasync(wasi_ctx: &WasiCtx, fd: wasm32::__wasi_fd_t) -> wasm32::__wasi_errno_t { - let host_fd = dec_fd(fd); - let rights = host::__WASI_RIGHT_FD_DATASYNC; - let fe = match wasi_ctx.get_fd_entry(host_fd, rights.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - let res; - - #[cfg(target_os = "linux")] - { - res = nix::unistd::fdatasync(fe.fd_object.rawfd); - } - - #[cfg(not(target_os = "linux"))] - { - res = nix::unistd::fsync(fe.fd_object.rawfd); - } - - if let Err(e) = res { - return wasm32::errno_from_nix(e.as_errno().unwrap()); - } - wasm32::__WASI_ESUCCESS + hostcalls_impl::fd_datasync(wasi_ctx, fd) } #[wasi_common_cbindgen] @@ -69,44 +26,7 @@ pub fn fd_pread( offset: wasm32::__wasi_filesize_t, nread: wasm32::uintptr_t, ) -> wasm32::__wasi_errno_t { - use nix::sys::uio::pread; - use std::cmp; - - let fd = dec_fd(fd); - let iovs = match dec_iovec_slice(memory, iovs_ptr, iovs_len) { - Ok(iovs) => iovs, - Err(e) => return enc_errno(e), - }; - let rights = host::__WASI_RIGHT_FD_READ; - let fe = match wasi_ctx.get_fd_entry(fd, rights.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - let offset = dec_filesize(offset); - if offset > i64::max_value() as u64 { - return wasm32::__WASI_EIO; - } - let buf_size = iovs.iter().map(|v| v.buf_len).sum(); - let mut buf = vec![0; buf_size]; - let host_nread = match pread(fe.fd_object.rawfd, &mut buf, offset as off_t) { - Ok(len) => len, - Err(e) => return wasm32::errno_from_nix(e.as_errno().unwrap()), - }; - let mut buf_offset = 0; - let mut left = host_nread; - for iov in &iovs { - if left == 0 { - break; - } - let vec_len = cmp::min(iov.buf_len, left); - unsafe { std::slice::from_raw_parts_mut(iov.buf as *mut u8, vec_len) } - .copy_from_slice(&buf[buf_offset..buf_offset + vec_len]); - buf_offset += vec_len; - left -= vec_len; - } - enc_usize_byref(memory, nread, host_nread) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) + hostcalls_impl::fd_pread(wasi_ctx, memory, fd, iovs_ptr, iovs_len, offset, nread) } #[wasi_common_cbindgen] @@ -119,36 +39,7 @@ pub fn fd_pwrite( offset: wasm32::__wasi_filesize_t, nwritten: wasm32::uintptr_t, ) -> wasm32::__wasi_errno_t { - use nix::sys::uio::pwrite; - - let fd = dec_fd(fd); - let iovs = match dec_iovec_slice(memory, iovs_ptr, iovs_len) { - Ok(iovs) => iovs, - Err(e) => return enc_errno(e), - }; - let rights = host::__WASI_RIGHT_FD_READ; - let fe = match wasi_ctx.get_fd_entry(fd, rights.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - let offset = dec_filesize(offset); - if offset > i64::max_value() as u64 { - return wasm32::__WASI_EIO; - } - let buf_size = iovs.iter().map(|v| v.buf_len).sum(); - let mut buf = Vec::with_capacity(buf_size); - for iov in &iovs { - buf.extend_from_slice(unsafe { - std::slice::from_raw_parts(iov.buf as *const u8, iov.buf_len) - }); - } - let host_nwritten = match pwrite(fe.fd_object.rawfd, &buf, offset as off_t) { - Ok(len) => len, - Err(e) => return wasm32::errno_from_nix(e.as_errno().unwrap()), - }; - enc_usize_byref(memory, nwritten, host_nwritten) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) + hostcalls_impl::fd_pwrite(wasi_ctx, memory, fd, iovs_ptr, iovs_len, offset, nwritten) } #[wasi_common_cbindgen] @@ -160,38 +51,7 @@ pub fn fd_read( iovs_len: wasm32::size_t, nread: wasm32::uintptr_t, ) -> wasm32::__wasi_errno_t { - use nix::sys::uio::{readv, IoVec}; - - let fd = dec_fd(fd); - let mut iovs = match dec_iovec_slice(memory, iovs_ptr, iovs_len) { - Ok(iovs) => iovs, - Err(e) => return enc_errno(e), - }; - - let fe = match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_FD_READ.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - - let mut iovs: Vec> = iovs - .iter_mut() - .map(|iov| unsafe { host::iovec_to_nix_mut(iov) }) - .collect(); - - let host_nread = match readv(fe.fd_object.rawfd, &mut iovs) { - Ok(len) => len, - Err(e) => return wasm32::errno_from_nix(e.as_errno().unwrap()), - }; - - if host_nread == 0 { - // we hit eof, so remove the fdentry from the context - let mut fe = wasi_ctx.fds.remove(&fd).expect("file entry is still there"); - fe.fd_object.needs_close = false; - } - - enc_usize_byref(memory, nread, host_nread) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) + hostcalls_impl::fd_read(wasi_ctx, memory, fd, iovs_ptr, iovs_len, nread) } #[wasi_common_cbindgen] @@ -200,23 +60,7 @@ pub fn fd_renumber( from: wasm32::__wasi_fd_t, to: wasm32::__wasi_fd_t, ) -> wasm32::__wasi_errno_t { - let from = dec_fd(from); - let to = dec_fd(to); - let fe_from = match wasi_ctx.fds.get(&from) { - Some(fe_from) => fe_from, - None => return wasm32::__WASI_EBADF, - }; - let fe_to = match wasi_ctx.fds.get(&to) { - Some(fe_to) => fe_to, - None => return wasm32::__WASI_EBADF, - }; - if let Err(e) = nix::unistd::dup2(fe_from.fd_object.rawfd, fe_to.fd_object.rawfd) { - return wasm32::errno_from_nix(e.as_errno().unwrap()); - } - let fe_from_rawfd = fe_from.fd_object.rawfd; - wasi_ctx.fds.remove(&(fe_from_rawfd as host::__wasi_fd_t)); - - wasm32::__WASI_ESUCCESS + hostcalls_impl::fd_renumber(wasi_ctx, from, to) } #[wasi_common_cbindgen] @@ -228,36 +72,7 @@ pub fn fd_seek( whence: wasm32::__wasi_whence_t, newoffset: wasm32::uintptr_t, ) -> wasm32::__wasi_errno_t { - let fd = dec_fd(fd); - let offset = dec_filedelta(offset); - let whence = dec_whence(whence); - - let host_newoffset = { - use nix::unistd::{lseek, Whence}; - let nwhence = match whence { - host::__WASI_WHENCE_CUR => Whence::SeekCur, - host::__WASI_WHENCE_END => Whence::SeekEnd, - host::__WASI_WHENCE_SET => Whence::SeekSet, - _ => return wasm32::__WASI_EINVAL, - }; - - let rights = if offset == 0 && whence == host::__WASI_WHENCE_CUR { - host::__WASI_RIGHT_FD_TELL - } else { - host::__WASI_RIGHT_FD_SEEK | host::__WASI_RIGHT_FD_TELL - }; - match wasi_ctx.get_fd_entry(fd, rights.into(), 0) { - Ok(fe) => match lseek(fe.fd_object.rawfd, offset, nwhence) { - Ok(newoffset) => newoffset, - Err(e) => return wasm32::errno_from_nix(e.as_errno().unwrap()), - }, - Err(e) => return enc_errno(e), - } - }; - - enc_filesize_byref(memory, newoffset, host_newoffset as u64) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) + hostcalls_impl::fd_seek(wasi_ctx, memory, fd, offset, whence, newoffset) } #[wasi_common_cbindgen] @@ -267,24 +82,7 @@ pub fn fd_tell( fd: wasm32::__wasi_fd_t, newoffset: wasm32::uintptr_t, ) -> wasm32::__wasi_errno_t { - let fd = dec_fd(fd); - - let host_offset = { - use nix::unistd::{lseek, Whence}; - - let rights = host::__WASI_RIGHT_FD_TELL; - match wasi_ctx.get_fd_entry(fd, rights.into(), 0) { - Ok(fe) => match lseek(fe.fd_object.rawfd, 0, Whence::SeekCur) { - Ok(newoffset) => newoffset, - Err(e) => return wasm32::errno_from_nix(e.as_errno().unwrap()), - }, - Err(e) => return enc_errno(e), - } - }; - - enc_filesize_byref(memory, newoffset, host_offset as u64) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) + hostcalls_impl::fd_tell(wasi_ctx, memory, fd, newoffset) } #[wasi_common_cbindgen] @@ -292,34 +90,9 @@ pub fn fd_fdstat_get( wasi_ctx: &WasiCtx, memory: &mut [u8], fd: wasm32::__wasi_fd_t, - fdstat_ptr: wasm32::uintptr_t, // *mut wasm32::__wasi_fdstat_t + fdstat_ptr: wasm32::uintptr_t, ) -> wasm32::__wasi_errno_t { - let host_fd = dec_fd(fd); - let mut host_fdstat = match dec_fdstat_byref(memory, fdstat_ptr) { - Ok(host_fdstat) => host_fdstat, - Err(e) => return enc_errno(e), - }; - - let errno = if let Some(fe) = wasi_ctx.fds.get(&host_fd) { - host_fdstat.fs_filetype = fe.fd_object.ty; - host_fdstat.fs_rights_base = fe.rights_base; - host_fdstat.fs_rights_inheriting = fe.rights_inheriting; - use nix::fcntl::{fcntl, OFlag, F_GETFL}; - match fcntl(fe.fd_object.rawfd, F_GETFL).map(OFlag::from_bits_truncate) { - Ok(flags) => { - host_fdstat.fs_flags = host::fdflags_from_nix(flags); - wasm32::__WASI_ESUCCESS - } - Err(e) => wasm32::errno_from_nix(e.as_errno().unwrap()), - } - } else { - wasm32::__WASI_EBADF - }; - - enc_fdstat_byref(memory, fdstat_ptr, host_fdstat) - .expect("can write back into the pointer we read from"); - - errno + hostcalls_impl::fd_fdstat_get(wasi_ctx, memory, fd, fdstat_ptr) } #[wasi_common_cbindgen] @@ -328,18 +101,7 @@ pub fn fd_fdstat_set_flags( fd: wasm32::__wasi_fd_t, fdflags: wasm32::__wasi_fdflags_t, ) -> wasm32::__wasi_errno_t { - let host_fd = dec_fd(fd); - let host_fdflags = dec_fdflags(fdflags); - let nix_flags = host::nix_from_fdflags(host_fdflags); - - if let Some(fe) = wasi_ctx.fds.get(&host_fd) { - match nix::fcntl::fcntl(fe.fd_object.rawfd, nix::fcntl::F_SETFL(nix_flags)) { - Ok(_) => wasm32::__WASI_ESUCCESS, - Err(e) => wasm32::errno_from_nix(e.as_errno().unwrap()), - } - } else { - wasm32::__WASI_EBADF - } + hostcalls_impl::fd_fdstat_set_flags(wasi_ctx, fd, fdflags) } #[wasi_common_cbindgen] @@ -349,35 +111,12 @@ pub fn fd_fdstat_set_rights( fs_rights_base: wasm32::__wasi_rights_t, fs_rights_inheriting: wasm32::__wasi_rights_t, ) -> wasm32::__wasi_errno_t { - let host_fd = dec_fd(fd); - let fe = match wasi_ctx.fds.get_mut(&host_fd) { - Some(fe) => fe, - None => return wasm32::__WASI_EBADF, - }; - if fe.rights_base & fs_rights_base != fs_rights_base - || fe.rights_inheriting & fs_rights_inheriting != fs_rights_inheriting - { - return wasm32::__WASI_ENOTCAPABLE; - } - - fe.rights_base = fs_rights_base; - fe.rights_inheriting = fs_rights_inheriting; - wasm32::__WASI_ESUCCESS + hostcalls_impl::fd_fdstat_set_rights(wasi_ctx, fd, fs_rights_base, fs_rights_inheriting) } #[wasi_common_cbindgen] pub fn fd_sync(wasi_ctx: &WasiCtx, fd: wasm32::__wasi_fd_t) -> wasm32::__wasi_errno_t { - let host_fd = dec_fd(fd); - let rights = host::__WASI_RIGHT_FD_SYNC; - let fe = match wasi_ctx.get_fd_entry(host_fd, rights.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - let res = nix::unistd::fsync(fe.fd_object.rawfd); - if let Err(e) = res { - return wasm32::errno_from_nix(e.as_errno().unwrap()); - } - wasm32::__WASI_ESUCCESS + hostcalls_impl::fd_sync(wasi_ctx, fd) } #[wasi_common_cbindgen] @@ -389,32 +128,7 @@ pub fn fd_write( iovs_len: wasm32::size_t, nwritten: wasm32::uintptr_t, ) -> wasm32::__wasi_errno_t { - use nix::sys::uio::{writev, IoVec}; - - let fd = dec_fd(fd); - let iovs = match dec_iovec_slice(memory, iovs_ptr, iovs_len) { - Ok(iovs) => iovs, - Err(e) => return enc_errno(e), - }; - - let fe = match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_FD_WRITE.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - - let iovs: Vec> = iovs - .iter() - .map(|iov| unsafe { host::iovec_to_nix(iov) }) - .collect(); - - let host_nwritten = match writev(fe.fd_object.rawfd, &iovs) { - Ok(len) => len, - Err(e) => return wasm32::errno_from_nix(e.as_errno().unwrap()), - }; - - enc_usize_byref(memory, nwritten, host_nwritten) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) + hostcalls_impl::fd_write(wasi_ctx, memory, fd, iovs_ptr, iovs_len, nwritten) } #[wasi_common_cbindgen] @@ -425,55 +139,7 @@ pub fn fd_advise( len: wasm32::__wasi_filesize_t, advice: wasm32::__wasi_advice_t, ) -> wasm32::__wasi_errno_t { - let host_fd = dec_fd(fd); - let rights = host::__WASI_RIGHT_FD_ADVISE; - let fe = match wasi_ctx.get_fd_entry(host_fd, rights.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - let advice = dec_advice(advice); - - #[cfg(target_os = "linux")] - { - let host_advice = match advice { - host::__WASI_ADVICE_DONTNEED => libc::POSIX_FADV_DONTNEED, - host::__WASI_ADVICE_SEQUENTIAL => libc::POSIX_FADV_SEQUENTIAL, - host::__WASI_ADVICE_WILLNEED => libc::POSIX_FADV_DONTNEED, - host::__WASI_ADVICE_NOREUSE => libc::POSIX_FADV_NOREUSE, - host::__WASI_ADVICE_RANDOM => libc::POSIX_FADV_RANDOM, - host::__WASI_ADVICE_NORMAL => libc::POSIX_FADV_NORMAL, - _ => return wasm32::__WASI_EINVAL, - }; - let offset = dec_filesize(offset); - let len = dec_filesize(len); - let res = unsafe { - libc::posix_fadvise( - fe.fd_object.rawfd, - offset as off_t, - len as off_t, - host_advice, - ) - }; - if res != 0 { - return wasm32::errno_from_nix(nix::errno::Errno::last()); - } - } - - #[cfg(not(target_os = "linux"))] - { - let _ = (fe, offset, len); - match advice { - host::__WASI_ADVICE_DONTNEED - | host::__WASI_ADVICE_SEQUENTIAL - | host::__WASI_ADVICE_WILLNEED - | host::__WASI_ADVICE_NOREUSE - | host::__WASI_ADVICE_RANDOM - | host::__WASI_ADVICE_NORMAL => {} - _ => return wasm32::__WASI_EINVAL, - } - } - - wasm32::__WASI_ESUCCESS + hostcalls_impl::fd_advise(wasi_ctx, fd, offset, len, advice) } #[wasi_common_cbindgen] @@ -483,50 +149,7 @@ pub fn fd_allocate( offset: wasm32::__wasi_filesize_t, len: wasm32::__wasi_filesize_t, ) -> wasm32::__wasi_errno_t { - let host_fd = dec_fd(fd); - let rights = host::__WASI_RIGHT_FD_ALLOCATE; - let fe = match wasi_ctx.get_fd_entry(host_fd, rights.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - let offset = dec_filesize(offset); - let len = dec_filesize(len); - - #[cfg(target_os = "linux")] - { - let res = - unsafe { libc::posix_fallocate(fe.fd_object.rawfd, offset as off_t, len as off_t) }; - if res != 0 { - return wasm32::errno_from_nix(nix::errno::Errno::last()); - } - } - - #[cfg(not(target_os = "linux"))] - { - use nix::sys::stat::fstat; - use nix::unistd::ftruncate; - - match fstat(fe.fd_object.rawfd) { - Err(e) => return wasm32::errno_from_nix(e.as_errno().unwrap()), - Ok(st) => { - let current_size = st.st_size as u64; - let wanted_size = match offset.checked_add(len) { - Some(wanted_size) => wanted_size, - None => return wasm32::__WASI_E2BIG, - }; - if wanted_size > i64::max_value() as u64 { - return wasm32::__WASI_E2BIG; - } - if wanted_size > current_size { - if let Err(e) = ftruncate(fe.fd_object.rawfd, wanted_size as off_t) { - return wasm32::errno_from_nix(e.as_errno().unwrap()); - } - } - } - } - } - - wasm32::__WASI_ESUCCESS + hostcalls_impl::fd_allocate(wasi_ctx, fd, offset, len) } #[wasi_common_cbindgen] @@ -537,35 +160,7 @@ pub fn path_create_directory( path_ptr: wasm32::uintptr_t, path_len: wasm32::size_t, ) -> wasm32::__wasi_errno_t { - use nix::errno; - use nix::libc::mkdirat; - - let dirfd = dec_fd(dirfd); - let path = match dec_slice_of::(memory, path_ptr, path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let (dir, path) = match path_get( - wasi_ctx, - dirfd, - 0, - path, - host::__WASI_RIGHT_PATH_OPEN | host::__WASI_RIGHT_PATH_CREATE_DIRECTORY, - 0, - false, - ) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { - Ok(path_cstr) => path_cstr, - Err(_) => return wasm32::__WASI_EINVAL, - }; - // nix doesn't expose mkdirat() yet - match unsafe { mkdirat(dir, path_cstr.as_ptr(), 0o777) } { - 0 => wasm32::__WASI_ESUCCESS, - _ => wasm32::errno_from_nix(errno::Errno::last()), - } + hostcalls_impl::path_create_directory(wasi_ctx, memory, dirfd, path_ptr, path_len) } #[wasi_common_cbindgen] @@ -573,61 +168,24 @@ pub fn path_link( wasi_ctx: &WasiCtx, memory: &mut [u8], old_dirfd: wasm32::__wasi_fd_t, - _old_flags: wasm32::__wasi_lookupflags_t, + old_flags: wasm32::__wasi_lookupflags_t, old_path_ptr: wasm32::uintptr_t, old_path_len: wasm32::size_t, new_dirfd: wasm32::__wasi_fd_t, new_path_ptr: wasm32::uintptr_t, new_path_len: wasm32::size_t, ) -> wasm32::__wasi_errno_t { - use nix::libc::linkat; - - let old_dirfd = dec_fd(old_dirfd); - let new_dirfd = dec_fd(new_dirfd); - let old_path = match dec_slice_of::(memory, old_path_ptr, old_path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let new_path = match dec_slice_of::(memory, new_path_ptr, new_path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let rights = host::__WASI_RIGHT_PATH_LINK_SOURCE; - let (old_dir, old_path) = - match path_get(wasi_ctx, old_dirfd, 0, old_path, rights.into(), 0, false) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let rights = host::__WASI_RIGHT_PATH_LINK_TARGET; - let (new_dir, new_path) = - match path_get(wasi_ctx, new_dirfd, 0, new_path, rights.into(), 0, false) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let old_path_cstr = match std::ffi::CString::new(old_path.as_bytes()) { - Ok(old_path_cstr) => old_path_cstr, - Err(_) => return wasm32::__WASI_EINVAL, - }; - let new_path_cstr = match std::ffi::CString::new(new_path.as_bytes()) { - Ok(new_path_cstr) => new_path_cstr, - Err(_) => return wasm32::__WASI_EINVAL, - }; - - // Not setting AT_SYMLINK_FOLLOW fails on most filesystems - let atflags = libc::AT_SYMLINK_FOLLOW; - let res = unsafe { - linkat( - old_dir, - old_path_cstr.as_ptr(), - new_dir, - new_path_cstr.as_ptr(), - atflags, - ) - }; - if res != 0 { - return wasm32::errno_from_nix(nix::errno::Errno::last()); - } - wasm32::__WASI_ESUCCESS + hostcalls_impl::path_link( + wasi_ctx, + memory, + old_dirfd, + old_flags, + old_path_ptr, + old_path_len, + new_dirfd, + new_path_ptr, + new_path_len, + ) } #[wasi_common_cbindgen] @@ -644,148 +202,19 @@ pub fn path_open( fs_flags: wasm32::__wasi_fdflags_t, fd_out_ptr: wasm32::uintptr_t, ) -> wasm32::__wasi_errno_t { - use nix::errno::Errno; - use nix::fcntl::{openat, AtFlags, OFlag}; - use nix::sys::stat::{fstatat, Mode, SFlag}; - - let dirfd = dec_fd(dirfd); - let dirflags = dec_lookupflags(dirflags); - let oflags = dec_oflags(oflags); - let fs_rights_base = dec_rights(fs_rights_base); - let fs_rights_inheriting = dec_rights(fs_rights_inheriting); - let fs_flags = dec_fdflags(fs_flags); - - // which open mode do we need? - let read = fs_rights_base & (host::__WASI_RIGHT_FD_READ | host::__WASI_RIGHT_FD_READDIR) != 0; - let write = fs_rights_base - & (host::__WASI_RIGHT_FD_DATASYNC - | host::__WASI_RIGHT_FD_WRITE - | host::__WASI_RIGHT_FD_ALLOCATE - | host::__WASI_RIGHT_FD_FILESTAT_SET_SIZE) - != 0; - - let mut nix_all_oflags = if read && write { - OFlag::O_RDWR - } else if read { - OFlag::O_RDONLY - } else { - OFlag::O_WRONLY - }; - - // on non-Capsicum systems, we always want nofollow - nix_all_oflags.insert(OFlag::O_NOFOLLOW); - - // which rights are needed on the dirfd? - let mut needed_base = host::__WASI_RIGHT_PATH_OPEN; - let mut needed_inheriting = fs_rights_base | fs_rights_inheriting; - - // convert open flags - let nix_oflags = host::nix_from_oflags(oflags); - nix_all_oflags.insert(nix_oflags); - if nix_all_oflags.contains(OFlag::O_CREAT) { - needed_base |= host::__WASI_RIGHT_PATH_CREATE_FILE; - } - if nix_all_oflags.contains(OFlag::O_TRUNC) { - needed_base |= host::__WASI_RIGHT_PATH_FILESTAT_SET_SIZE; - } - - // convert file descriptor flags - nix_all_oflags.insert(host::nix_from_fdflags(fs_flags)); - if nix_all_oflags.contains(OFlag::O_DSYNC) { - needed_inheriting |= host::__WASI_RIGHT_FD_DATASYNC; - } - if nix_all_oflags.intersects(host::O_RSYNC | OFlag::O_SYNC) { - needed_inheriting |= host::__WASI_RIGHT_FD_SYNC; - } - - let path = match dec_slice_of::(memory, path_ptr, path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - - let (dir, path) = match path_get( + hostcalls_impl::path_open( wasi_ctx, + memory, dirfd, dirflags, - path, - needed_base, - needed_inheriting, - nix_oflags.contains(OFlag::O_CREAT), - ) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - - // Call openat. Use mode 0o666 so that we follow whatever the user's - // umask is, but don't set the executable flag, because it isn't yet - // meaningful for WASI programs to create executable files. - let new_fd = match openat( - dir, - path.as_os_str(), - nix_all_oflags, - Mode::from_bits_truncate(0o666), - ) { - Ok(fd) => fd, - Err(e) => { - match e.as_errno() { - // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket - Some(Errno::ENXIO) => { - if let Ok(stat) = fstatat(dir, path.as_os_str(), AtFlags::AT_SYMLINK_NOFOLLOW) { - if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFSOCK) { - return wasm32::__WASI_ENOTSUP; - } else { - return wasm32::__WASI_ENXIO; - } - } else { - return wasm32::__WASI_ENXIO; - } - } - // Linux returns ENOTDIR instead of ELOOP when using O_NOFOLLOW|O_DIRECTORY - // on a symlink. - Some(Errno::ENOTDIR) - if !(nix_all_oflags & (OFlag::O_NOFOLLOW | OFlag::O_DIRECTORY)).is_empty() => - { - if let Ok(stat) = fstatat(dir, path.as_os_str(), AtFlags::AT_SYMLINK_NOFOLLOW) { - if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFLNK) { - return wasm32::__WASI_ELOOP; - } - } - return wasm32::__WASI_ENOTDIR; - } - // FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on - // a symlink. - Some(Errno::EMLINK) if !(nix_all_oflags & OFlag::O_NOFOLLOW).is_empty() => { - return wasm32::__WASI_ELOOP; - } - Some(e) => return wasm32::errno_from_nix(e), - None => return wasm32::__WASI_ENOSYS, - } - } - }; - - // Determine the type of the new file descriptor and which rights contradict with this type - let guest_fd = match unsafe { determine_type_rights(new_fd) } { - Err(e) => { - // if `close` fails, note it but do not override the underlying errno - nix::unistd::close(new_fd).unwrap_or_else(|e| { - dbg!(e); - }); - return enc_errno(e); - } - Ok((_ty, max_base, max_inheriting)) => { - let mut fe = unsafe { FdEntry::from_raw_fd(new_fd) }; - fe.rights_base &= max_base; - fe.rights_inheriting &= max_inheriting; - match wasi_ctx.insert_fd_entry(fe) { - Ok(fd) => fd, - Err(e) => return enc_errno(e), - } - } - }; - - enc_fd_byref(memory, fd_out_ptr, guest_fd) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) + path_ptr, + path_len, + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + fd_out_ptr, + ) } #[wasi_common_cbindgen] @@ -798,74 +227,7 @@ pub fn fd_readdir( cookie: wasm32::__wasi_dircookie_t, buf_used: wasm32::uintptr_t, ) -> wasm32::__wasi_errno_t { - use libc::{dirent, fdopendir, memcpy, readdir_r, seekdir}; - - match enc_usize_byref(memory, buf_used, 0) { - Ok(_) => {} - Err(e) => return enc_errno(e), - }; - let fd = dec_fd(fd); - let rights = host::__WASI_RIGHT_FD_READDIR; - let fe = match wasi_ctx.get_fd_entry(fd, rights.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - let host_buf = match dec_slice_of_mut::(memory, buf, buf_len) { - Ok(host_buf) => host_buf, - Err(e) => return enc_errno(e), - }; - let host_buf_ptr = host_buf.as_mut_ptr(); - let host_buf_len = host_buf.len(); - let dir = unsafe { fdopendir(fe.fd_object.rawfd) }; - if dir.is_null() { - return wasm32::errno_from_nix(nix::errno::Errno::last()); - } - let cookie = dec_dircookie(cookie); - if cookie != wasm32::__WASI_DIRCOOKIE_START { - unsafe { seekdir(dir, cookie as c_long) }; - } - let mut entry_buf = unsafe { std::mem::uninitialized::() }; - let mut left = host_buf_len; - let mut host_buf_offset: usize = 0; - while left > 0 { - let mut host_entry: *mut dirent = std::ptr::null_mut(); - let res = unsafe { readdir_r(dir, &mut entry_buf, &mut host_entry) }; - if res == -1 { - return wasm32::errno_from_nix(nix::errno::Errno::last()); - } - if host_entry.is_null() { - break; - } - let entry: wasm32::__wasi_dirent_t = match dirent_from_host(&unsafe { *host_entry }) { - Ok(entry) => entry, - Err(e) => return enc_errno(e), - }; - let name_len = entry.d_namlen as usize; - let required_space = std::mem::size_of_val(&entry) + name_len; - if required_space > left { - break; - } - unsafe { - let ptr = host_buf_ptr.offset(host_buf_offset as isize) as *mut c_void - as *mut wasm32::__wasi_dirent_t; - *ptr = entry; - } - host_buf_offset += std::mem::size_of_val(&entry); - let name_ptr = unsafe { *host_entry }.d_name.as_ptr(); - unsafe { - memcpy( - host_buf_ptr.offset(host_buf_offset as isize) as *mut _, - name_ptr as *const _, - name_len, - ) - }; - host_buf_offset += name_len; - left -= required_space; - } - let host_bufused = host_buf_len - left; - enc_usize_byref(memory, buf_used, host_bufused) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) + hostcalls_impl::fd_readdir(wasi_ctx, memory, fd, buf, buf_len, cookie, buf_used) } #[wasi_common_cbindgen] @@ -879,37 +241,9 @@ pub fn path_readlink( buf_len: wasm32::size_t, buf_used: wasm32::uintptr_t, ) -> wasm32::__wasi_errno_t { - use nix::fcntl::readlinkat; - - match enc_usize_byref(memory, buf_used, 0) { - Ok(_) => {} - Err(e) => return enc_errno(e), - }; - let dirfd = dec_fd(dirfd); - let path = match dec_slice_of::(memory, path_ptr, path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let rights = host::__WASI_RIGHT_PATH_READLINK; - let (dir, path) = match path_get(wasi_ctx, dirfd, 0, path, rights.into(), 0, false) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let mut buf = match dec_slice_of_mut::(memory, buf_ptr, buf_len) { - Ok(slice) => slice, - Err(e) => return enc_errno(e), - }; - let target_path = match readlinkat(dir, path.as_os_str(), &mut buf) { - Err(e) => return wasm32::errno_from_nix(e.as_errno().unwrap()), - Ok(target_path) => target_path, - }; - let host_bufused = target_path.len(); - match enc_usize_byref(memory, buf_used, host_bufused) { - Ok(_) => {} - Err(e) => return enc_errno(e), - }; - - wasm32::__WASI_ESUCCESS + hostcalls_impl::path_readlink( + wasi_ctx, memory, dirfd, path_ptr, path_len, buf_ptr, buf_len, buf_used, + ) } #[wasi_common_cbindgen] @@ -923,50 +257,16 @@ pub fn path_rename( new_path_ptr: wasm32::uintptr_t, new_path_len: wasm32::size_t, ) -> wasm32::__wasi_errno_t { - use nix::libc::renameat; - - let old_dirfd = dec_fd(old_dirfd); - let new_dirfd = dec_fd(new_dirfd); - let old_path = match dec_slice_of::(memory, old_path_ptr, old_path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let new_path = match dec_slice_of::(memory, new_path_ptr, new_path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let rights = host::__WASI_RIGHT_PATH_RENAME_SOURCE; - let (old_dir, old_path) = - match path_get(wasi_ctx, old_dirfd, 0, old_path, rights.into(), 0, false) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let rights = host::__WASI_RIGHT_PATH_RENAME_TARGET; - let (new_dir, new_path) = - match path_get(wasi_ctx, new_dirfd, 0, new_path, rights.into(), 0, false) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let old_path_cstr = match std::ffi::CString::new(old_path.as_bytes()) { - Ok(old_path_cstr) => old_path_cstr, - Err(_) => return wasm32::__WASI_EINVAL, - }; - let new_path_cstr = match std::ffi::CString::new(new_path.as_bytes()) { - Ok(new_path_cstr) => new_path_cstr, - Err(_) => return wasm32::__WASI_EINVAL, - }; - let res = unsafe { - renameat( - old_dir, - old_path_cstr.as_ptr(), - new_dir, - new_path_cstr.as_ptr(), - ) - }; - if res != 0 { - return wasm32::errno_from_nix(nix::errno::Errno::last()); - } - wasm32::__WASI_ESUCCESS + hostcalls_impl::path_rename( + wasi_ctx, + memory, + old_dirfd, + old_path_ptr, + old_path_len, + new_dirfd, + new_path_ptr, + new_path_len, + ) } #[wasi_common_cbindgen] @@ -976,24 +276,7 @@ pub fn fd_filestat_get( fd: wasm32::__wasi_fd_t, filestat_ptr: wasm32::uintptr_t, ) -> wasm32::__wasi_errno_t { - use nix::sys::stat::fstat; - - let host_fd = dec_fd(fd); - - let errno = if let Some(fe) = wasi_ctx.fds.get(&host_fd) { - match fstat(fe.fd_object.rawfd) { - Err(e) => wasm32::errno_from_nix(e.as_errno().unwrap()), - Ok(filestat) => { - let host_filestat = host::filestat_from_nix(filestat); - enc_filestat_byref(memory, filestat_ptr, host_filestat) - .expect("can write into the pointer"); - wasm32::__WASI_ESUCCESS - } - } - } else { - wasm32::__WASI_EBADF - }; - errno + hostcalls_impl::fd_filestat_get(wasi_ctx, memory, fd, filestat_ptr) } #[wasi_common_cbindgen] @@ -1004,53 +287,7 @@ pub fn fd_filestat_set_times( st_mtim: wasm32::__wasi_timestamp_t, fst_flags: wasm32::__wasi_fstflags_t, ) -> wasm32::__wasi_errno_t { - use nix::sys::time::{TimeSpec, TimeValLike}; - - let host_fd = dec_fd(fd); - let rights = host::__WASI_RIGHT_FD_FILESTAT_SET_TIMES; - let fe = match wasi_ctx.get_fd_entry(host_fd, rights.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - let st_atim = dec_timestamp(st_atim); - let mut st_mtim = dec_timestamp(st_mtim); - let fst_flags = dec_fstflags(fst_flags); - if fst_flags & host::__WASI_FILESTAT_SET_MTIM_NOW != 0 { - let clock_id = libc::CLOCK_REALTIME; - let mut timespec = unsafe { std::mem::uninitialized::() }; - let res = unsafe { libc::clock_gettime(clock_id, &mut timespec as *mut libc::timespec) }; - if res != 0 { - return wasm32::errno_from_nix(nix::errno::Errno::last()); - } - let time_ns = match (timespec.tv_sec as host::__wasi_timestamp_t) - .checked_mul(1_000_000_000) - .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as host::__wasi_timestamp_t)) - { - Some(time_ns) => time_ns, - None => return wasm32::__WASI_EOVERFLOW, - }; - st_mtim = time_ns; - } - let ts_atime = match fst_flags { - f if f & host::__WASI_FILESTAT_SET_ATIM_NOW != 0 => libc::timespec { - tv_sec: 0, - tv_nsec: utime_now(), - }, - f if f & host::__WASI_FILESTAT_SET_ATIM != 0 => { - *TimeSpec::nanoseconds(st_atim as i64).as_ref() - } - _ => libc::timespec { - tv_sec: 0, - tv_nsec: utime_omit(), - }, - }; - let ts_mtime = *TimeSpec::nanoseconds(st_mtim as i64).as_ref(); - let times = [ts_atime, ts_mtime]; - let res = unsafe { libc::futimens(fe.fd_object.rawfd, times.as_ptr()) }; - if res != 0 { - return wasm32::errno_from_nix(nix::errno::Errno::last()); - } - wasm32::__WASI_ESUCCESS + hostcalls_impl::fd_filestat_set_times(wasi_ctx, fd, st_atim, st_mtim, fst_flags) } #[wasi_common_cbindgen] @@ -1059,22 +296,7 @@ pub fn fd_filestat_set_size( fd: wasm32::__wasi_fd_t, st_size: wasm32::__wasi_filesize_t, ) -> wasm32::__wasi_errno_t { - use nix::unistd::ftruncate; - - let host_fd = dec_fd(fd); - let rights = host::__WASI_RIGHT_FD_FILESTAT_SET_SIZE; - let fe = match wasi_ctx.get_fd_entry(host_fd, rights.into(), 0) { - Ok(fe) => fe, - Err(e) => return enc_errno(e), - }; - let st_size = dec_filesize(st_size); - if st_size > i64::max_value() as u64 { - return wasm32::__WASI_E2BIG; - } - if let Err(e) = ftruncate(fe.fd_object.rawfd, st_size as off_t) { - return wasm32::errno_from_nix(e.as_errno().unwrap()); - } - wasm32::__WASI_ESUCCESS + hostcalls_impl::fd_filestat_set_size(wasi_ctx, fd, st_size) } #[wasi_common_cbindgen] @@ -1087,40 +309,15 @@ pub fn path_filestat_get( path_len: wasm32::size_t, filestat_ptr: wasm32::uintptr_t, ) -> wasm32::__wasi_errno_t { - use nix::fcntl::AtFlags; - use nix::sys::stat::fstatat; - - let dirfd = dec_fd(dirfd); - let dirflags = dec_lookupflags(dirflags); - let path = match dec_slice_of::(memory, path_ptr, path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let (dir, path) = match path_get( + hostcalls_impl::path_filestat_get( wasi_ctx, + memory, dirfd, dirflags, - path, - host::__WASI_RIGHT_PATH_FILESTAT_GET, - 0, - false, - ) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let atflags = match dirflags { - 0 => AtFlags::empty(), - _ => AtFlags::AT_SYMLINK_NOFOLLOW, - }; - match fstatat(dir, path.as_os_str(), atflags) { - Err(e) => wasm32::errno_from_nix(e.as_errno().unwrap()), - Ok(filestat) => { - let host_filestat = host::filestat_from_nix(filestat); - enc_filestat_byref(memory, filestat_ptr, host_filestat) - .expect("can write into the pointer"); - wasm32::__WASI_ESUCCESS - } - } + path_ptr, + path_len, + filestat_ptr, + ) } #[wasi_common_cbindgen] @@ -1135,66 +332,9 @@ pub fn path_filestat_set_times( st_mtim: wasm32::__wasi_timestamp_t, fst_flags: wasm32::__wasi_fstflags_t, ) -> wasm32::__wasi_errno_t { - use nix::sys::time::{TimeSpec, TimeValLike}; - - let dirfd = dec_fd(dirfd); - let dirflags = dec_lookupflags(dirflags); - let path = match dec_slice_of::(memory, path_ptr, path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let rights = host::__WASI_RIGHT_PATH_FILESTAT_SET_TIMES; - let (dir, path) = match path_get(wasi_ctx, dirfd, dirflags, path, rights.into(), 0, false) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let atflags = match dirflags { - wasm32::__WASI_LOOKUP_SYMLINK_FOLLOW => 0, - _ => libc::AT_SYMLINK_NOFOLLOW, - }; - let st_atim = dec_timestamp(st_atim); - let mut st_mtim = dec_timestamp(st_mtim); - let fst_flags = dec_fstflags(fst_flags); - if fst_flags & host::__WASI_FILESTAT_SET_MTIM_NOW != 0 { - let clock_id = libc::CLOCK_REALTIME; - let mut timespec = unsafe { std::mem::uninitialized::() }; - let res = unsafe { libc::clock_gettime(clock_id, &mut timespec as *mut libc::timespec) }; - if res != 0 { - return wasm32::errno_from_nix(nix::errno::Errno::last()); - } - let time_ns = match (timespec.tv_sec as host::__wasi_timestamp_t) - .checked_mul(1_000_000_000) - .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as host::__wasi_timestamp_t)) - { - Some(time_ns) => time_ns, - None => return wasm32::__WASI_EOVERFLOW, - }; - st_mtim = time_ns; - } - let ts_atime = match fst_flags { - f if f & host::__WASI_FILESTAT_SET_ATIM_NOW != 0 => libc::timespec { - tv_sec: 0, - tv_nsec: utime_now(), - }, - f if f & host::__WASI_FILESTAT_SET_ATIM != 0 => { - *TimeSpec::nanoseconds(st_atim as i64).as_ref() - } - _ => libc::timespec { - tv_sec: 0, - tv_nsec: utime_omit(), - }, - }; - let ts_mtime = *TimeSpec::nanoseconds(st_mtim as i64).as_ref(); - let times = [ts_atime, ts_mtime]; - let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { - Ok(path_cstr) => path_cstr, - Err(_) => return wasm32::__WASI_EINVAL, - }; - let res = unsafe { libc::utimensat(dir, path_cstr.as_ptr(), times.as_ptr(), atflags) }; - if res != 0 { - return wasm32::errno_from_nix(nix::errno::Errno::last()); - } - wasm32::__WASI_ESUCCESS + hostcalls_impl::path_filestat_set_times( + wasi_ctx, memory, dirfd, dirflags, path_ptr, path_len, st_atim, st_mtim, fst_flags, + ) } #[wasi_common_cbindgen] @@ -1207,35 +347,15 @@ pub fn path_symlink( new_path_ptr: wasm32::uintptr_t, new_path_len: wasm32::size_t, ) -> wasm32::__wasi_errno_t { - use nix::libc::symlinkat; - - let dirfd = dec_fd(dirfd); - let old_path = match dec_slice_of::(memory, old_path_ptr, old_path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let new_path = match dec_slice_of::(memory, new_path_ptr, new_path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let rights = host::__WASI_RIGHT_PATH_SYMLINK; - let (dir, new_path) = match path_get(wasi_ctx, dirfd, 0, new_path, rights.into(), 0, false) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let old_path_cstr = match std::ffi::CString::new(old_path.as_bytes()) { - Ok(old_path_cstr) => old_path_cstr, - Err(_) => return wasm32::__WASI_EINVAL, - }; - let new_path_cstr = match std::ffi::CString::new(new_path.as_bytes()) { - Ok(new_path_cstr) => new_path_cstr, - Err(_) => return wasm32::__WASI_EINVAL, - }; - let res = unsafe { symlinkat(old_path_cstr.as_ptr(), dir, new_path_cstr.as_ptr()) }; - if res != 0 { - return wasm32::errno_from_nix(nix::errno::Errno::last()); - } - wasm32::__WASI_ESUCCESS + hostcalls_impl::path_symlink( + wasi_ctx, + memory, + old_path_ptr, + old_path_len, + dirfd, + new_path_ptr, + new_path_len, + ) } #[wasi_common_cbindgen] @@ -1246,35 +366,7 @@ pub fn path_unlink_file( path_ptr: wasm32::uintptr_t, path_len: wasm32::size_t, ) -> wasm32::__wasi_errno_t { - use nix::errno; - use nix::libc::unlinkat; - - let dirfd = dec_fd(dirfd); - let path = match dec_slice_of::(memory, path_ptr, path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let (dir, path) = match path_get( - wasi_ctx, - dirfd, - 0, - path, - host::__WASI_RIGHT_PATH_UNLINK_FILE, - 0, - false, - ) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { - Ok(path_cstr) => path_cstr, - Err(_) => return wasm32::__WASI_EINVAL, - }; - // nix doesn't expose unlinkat() yet - match unsafe { unlinkat(dir, path_cstr.as_ptr(), 0) } { - 0 => wasm32::__WASI_ESUCCESS, - _ => wasm32::errno_from_nix(errno::Errno::last()), - } + hostcalls_impl::path_unlink_file(wasi_ctx, memory, dirfd, path_ptr, path_len) } #[wasi_common_cbindgen] @@ -1285,28 +377,7 @@ pub fn path_remove_directory( path_ptr: wasm32::uintptr_t, path_len: wasm32::size_t, ) -> wasm32::__wasi_errno_t { - use nix::errno; - use nix::libc::{unlinkat, AT_REMOVEDIR}; - - let dirfd = dec_fd(dirfd); - let path = match dec_slice_of::(memory, path_ptr, path_len) { - Ok(slice) => OsStr::from_bytes(slice), - Err(e) => return enc_errno(e), - }; - let rights = host::__WASI_RIGHT_PATH_REMOVE_DIRECTORY; - let (dir, path) = match path_get(wasi_ctx, dirfd, 0, path, rights.into(), 0, false) { - Ok((dir, path)) => (dir, path), - Err(e) => return enc_errno(e), - }; - let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { - Ok(path_cstr) => path_cstr, - Err(_) => return wasm32::__WASI_EINVAL, - }; - // nix doesn't expose unlinkat() yet - match unsafe { unlinkat(dir, path_cstr.as_ptr(), AT_REMOVEDIR) } { - 0 => wasm32::__WASI_ESUCCESS, - _ => wasm32::errno_from_nix(errno::Errno::last()), - } + hostcalls_impl::path_remove_directory(wasi_ctx, memory, dirfd, path_ptr, path_len) } #[wasi_common_cbindgen] @@ -1316,34 +387,7 @@ pub fn fd_prestat_get( fd: wasm32::__wasi_fd_t, prestat_ptr: wasm32::uintptr_t, ) -> wasm32::__wasi_errno_t { - let fd = dec_fd(fd); - // TODO: is this the correct right for this? - match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_PATH_OPEN.into(), 0) { - Ok(fe) => { - if let Some(po_path) = &fe.preopen_path { - if fe.fd_object.ty != host::__WASI_FILETYPE_DIRECTORY { - return wasm32::__WASI_ENOTDIR; - } - enc_prestat_byref( - memory, - prestat_ptr, - host::__wasi_prestat_t { - pr_type: host::__WASI_PREOPENTYPE_DIR, - u: host::__wasi_prestat_t___wasi_prestat_u { - dir: host::__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t { - pr_name_len: po_path.as_os_str().as_bytes().len(), - }, - }, - }, - ) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) - } else { - wasm32::__WASI_ENOTSUP - } - } - Err(e) => enc_errno(e), - } + hostcalls_impl::fd_prestat_get(wasi_ctx, memory, fd, prestat_ptr) } #[wasi_common_cbindgen] @@ -1354,25 +398,5 @@ pub fn fd_prestat_dir_name( path_ptr: wasm32::uintptr_t, path_len: wasm32::size_t, ) -> wasm32::__wasi_errno_t { - let fd = dec_fd(fd); - - match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_PATH_OPEN.into(), 0) { - Ok(fe) => { - if let Some(po_path) = &fe.preopen_path { - if fe.fd_object.ty != host::__WASI_FILETYPE_DIRECTORY { - return wasm32::__WASI_ENOTDIR; - } - let path_bytes = po_path.as_os_str().as_bytes(); - if path_bytes.len() > dec_usize(path_len) { - return wasm32::__WASI_ENAMETOOLONG; - } - enc_slice_of(memory, path_bytes, path_ptr) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) - } else { - wasm32::__WASI_ENOTSUP - } - } - Err(e) => enc_errno(e), - } + hostcalls_impl::fd_prestat_dir_name(wasi_ctx, memory, fd, path_ptr, path_len) } diff --git a/src/hostcalls/misc.rs b/src/hostcalls/misc.rs index 9c5e42d15a..e24350253d 100644 --- a/src/hostcalls/misc.rs +++ b/src/hostcalls/misc.rs @@ -1,16 +1,12 @@ #![allow(non_camel_case_types)] -#![allow(unused_unsafe)] - use crate::ctx::WasiCtx; use crate::memory::*; -use crate::{host, wasm32}; +use crate::wasm32; + +use crate::sys::hostcalls as hostcalls_impl; use cast::From as _0; -use nix::convert_ioctl_res; -use nix::libc::{self, c_int}; -use std::cmp; -use std::time::SystemTime; use wasi_common_cbindgen::wasi_common_cbindgen; #[wasi_common_cbindgen] @@ -77,75 +73,17 @@ pub fn clock_res_get( clock_id: wasm32::__wasi_clockid_t, resolution_ptr: wasm32::uintptr_t, ) -> wasm32::__wasi_errno_t { - // convert the supported clocks to the libc types, or return EINVAL - let clock_id = match dec_clockid(clock_id) { - host::__WASI_CLOCK_REALTIME => libc::CLOCK_REALTIME, - host::__WASI_CLOCK_MONOTONIC => libc::CLOCK_MONOTONIC, - host::__WASI_CLOCK_PROCESS_CPUTIME_ID => libc::CLOCK_PROCESS_CPUTIME_ID, - host::__WASI_CLOCK_THREAD_CPUTIME_ID => libc::CLOCK_THREAD_CPUTIME_ID, - _ => return wasm32::__WASI_EINVAL, - }; - - // no `nix` wrapper for clock_getres, so we do it ourselves - let mut timespec = unsafe { std::mem::uninitialized::() }; - let res = unsafe { libc::clock_getres(clock_id, &mut timespec as *mut libc::timespec) }; - if res != 0 { - return wasm32::errno_from_nix(nix::errno::Errno::last()); - } - - // convert to nanoseconds, returning EOVERFLOW in case of overflow; this is freelancing a bit - // from the spec but seems like it'll be an unusual situation to hit - (timespec.tv_sec as host::__wasi_timestamp_t) - .checked_mul(1_000_000_000) - .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as host::__wasi_timestamp_t)) - .map_or(wasm32::__WASI_EOVERFLOW, |resolution| { - // a supported clock can never return zero; this case will probably never get hit, but - // make sure we follow the spec - if resolution == 0 { - wasm32::__WASI_EINVAL - } else { - enc_timestamp_byref(memory, resolution_ptr, resolution) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) - } - }) + hostcalls_impl::clock_res_get(memory, clock_id, resolution_ptr) } #[wasi_common_cbindgen] pub fn clock_time_get( memory: &mut [u8], clock_id: wasm32::__wasi_clockid_t, - // ignored for now, but will be useful once we put optional limits on precision to reduce side - // channels - _precision: wasm32::__wasi_timestamp_t, + precision: wasm32::__wasi_timestamp_t, time_ptr: wasm32::uintptr_t, ) -> wasm32::__wasi_errno_t { - // convert the supported clocks to the libc types, or return EINVAL - let clock_id = match dec_clockid(clock_id) { - host::__WASI_CLOCK_REALTIME => libc::CLOCK_REALTIME, - host::__WASI_CLOCK_MONOTONIC => libc::CLOCK_MONOTONIC, - host::__WASI_CLOCK_PROCESS_CPUTIME_ID => libc::CLOCK_PROCESS_CPUTIME_ID, - host::__WASI_CLOCK_THREAD_CPUTIME_ID => libc::CLOCK_THREAD_CPUTIME_ID, - _ => return wasm32::__WASI_EINVAL, - }; - - // no `nix` wrapper for clock_getres, so we do it ourselves - let mut timespec = unsafe { std::mem::uninitialized::() }; - let res = unsafe { libc::clock_gettime(clock_id, &mut timespec as *mut libc::timespec) }; - if res != 0 { - return wasm32::errno_from_nix(nix::errno::Errno::last()); - } - - // convert to nanoseconds, returning EOVERFLOW in case of overflow; this is freelancing a bit - // from the spec but seems like it'll be an unusual situation to hit - (timespec.tv_sec as host::__wasi_timestamp_t) - .checked_mul(1_000_000_000) - .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as host::__wasi_timestamp_t)) - .map_or(wasm32::__WASI_EOVERFLOW, |time| { - enc_timestamp_byref(memory, time_ptr, time) - .map(|_| wasm32::__WASI_ESUCCESS) - .unwrap_or_else(|e| e) - }) + hostcalls_impl::clock_time_get(memory, clock_id, precision, time_ptr) } #[wasi_common_cbindgen] @@ -214,88 +152,7 @@ pub fn poll_oneoff( nsubscriptions: wasm32::size_t, nevents: wasm32::uintptr_t, ) -> wasm32::__wasi_errno_t { - if nsubscriptions as u64 > wasm32::__wasi_filesize_t::max_value() { - return wasm32::__WASI_EINVAL; - } - enc_pointee(memory, nevents, 0).unwrap(); - let input_slice = - dec_slice_of::(memory, input, nsubscriptions).unwrap(); - - let input: Vec<_> = input_slice.iter().map(|x| dec_subscription(x)).collect(); - - let output_slice = - dec_slice_of_mut::(memory, output, nsubscriptions).unwrap(); - - let timeout = input - .iter() - .filter_map(|event| match event { - Ok(event) if event.type_ == wasm32::__WASI_EVENTTYPE_CLOCK => Some(ClockEventData { - delay: wasi_clock_to_relative_ns_delay(unsafe { event.u.clock }) / 1_000_000, - userdata: event.userdata, - }), - _ => None, - }) - .min_by_key(|event| event.delay); - let fd_events: Vec<_> = input - .iter() - .filter_map(|event| match event { - Ok(event) - if event.type_ == wasm32::__WASI_EVENTTYPE_FD_READ - || event.type_ == wasm32::__WASI_EVENTTYPE_FD_WRITE => - { - Some(FdEventData { - fd: unsafe { event.u.fd_readwrite.fd } as c_int, - type_: event.type_, - userdata: event.userdata, - }) - } - _ => None, - }) - .collect(); - if fd_events.is_empty() && timeout.is_none() { - return wasm32::__WASI_ESUCCESS; - } - let mut poll_fds: Vec<_> = fd_events - .iter() - .map(|event| { - let mut flags = nix::poll::EventFlags::empty(); - match event.type_ { - wasm32::__WASI_EVENTTYPE_FD_READ => flags.insert(nix::poll::EventFlags::POLLIN), - wasm32::__WASI_EVENTTYPE_FD_WRITE => flags.insert(nix::poll::EventFlags::POLLOUT), - // An event on a file descriptor can currently only be of type FD_READ or FD_WRITE - // Nothing else has been defined in the specification, and these are also the only two - // events we filtered before. If we get something else here, the code has a serious bug. - _ => unreachable!(), - }; - nix::poll::PollFd::new(event.fd, flags) - }) - .collect(); - let timeout = timeout.map(|ClockEventData { delay, userdata }| ClockEventData { - delay: cmp::min(delay, c_int::max_value() as u128), - userdata, - }); - let poll_timeout = timeout.map_or(-1, |timeout| timeout.delay as c_int); - let ready = loop { - match nix::poll::poll(&mut poll_fds, poll_timeout) { - Err(_) => { - if nix::errno::Errno::last() == nix::errno::Errno::EINTR { - continue; - } - return wasm32::errno_from_nix(nix::errno::Errno::last()); - } - Ok(ready) => break ready as usize, - } - }; - let events_count = if ready == 0 { - poll_oneoff_handle_timeout_event(output_slice, timeout) - } else { - let events = fd_events.iter().zip(poll_fds.iter()).take(ready); - poll_oneoff_handle_fd_event(output_slice, events) - }; - if let Err(e) = enc_pointee(memory, nevents, events_count) { - return enc_errno(e); - } - wasm32::__WASI_ESUCCESS + hostcalls_impl::poll_oneoff(memory, input, output, nsubscriptions, nevents) } #[wasi_common_cbindgen] @@ -314,6 +171,11 @@ pub fn proc_raise( unimplemented!("proc_raise") } +#[wasi_common_cbindgen] +pub fn sched_yield() -> wasm32::__wasi_errno_t { + hostcalls_impl::sched_yield() +} + #[wasi_common_cbindgen] pub fn random_get( memory: &mut [u8], @@ -331,140 +193,3 @@ pub fn random_get( return wasm32::__WASI_ESUCCESS; } - -#[wasi_common_cbindgen] -pub fn sched_yield() -> wasm32::__wasi_errno_t { - unsafe { libc::sched_yield() }; - wasm32::__WASI_ESUCCESS -} - -// define the `fionread()` function, equivalent to `ioctl(fd, FIONREAD, *bytes)` -nix::ioctl_read_bad!(fionread, nix::libc::FIONREAD, c_int); - -fn wasi_clock_to_relative_ns_delay( - wasi_clock: host::__wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t, -) -> u128 { - if wasi_clock.flags != wasm32::__WASI_SUBSCRIPTION_CLOCK_ABSTIME { - return wasi_clock.timeout as u128; - } - let now: u128 = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .expect("Current date is before the epoch") - .as_nanos(); - let deadline = wasi_clock.timeout as u128; - deadline.saturating_sub(now) -} - -#[derive(Debug, Copy, Clone)] -struct ClockEventData { - delay: u128, - userdata: host::__wasi_userdata_t, -} -#[derive(Debug, Copy, Clone)] -struct FdEventData { - fd: c_int, - type_: host::__wasi_eventtype_t, - userdata: host::__wasi_userdata_t, -} - -fn poll_oneoff_handle_timeout_event( - output_slice: &mut [wasm32::__wasi_event_t], - timeout: Option, -) -> wasm32::size_t { - if let Some(ClockEventData { userdata, .. }) = timeout { - let output_event = host::__wasi_event_t { - userdata, - type_: wasm32::__WASI_EVENTTYPE_CLOCK, - error: wasm32::__WASI_ESUCCESS, - u: host::__wasi_event_t___wasi_event_u { - fd_readwrite: host::__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t { - nbytes: 0, - flags: 0, - }, - }, - }; - output_slice[0] = enc_event(output_event); - 1 - } else { - // shouldn't happen - 0 - } -} - -fn poll_oneoff_handle_fd_event<'t>( - output_slice: &mut [wasm32::__wasi_event_t], - events: impl Iterator, -) -> wasm32::size_t { - let mut output_slice_cur = output_slice.iter_mut(); - let mut revents_count = 0; - for (fd_event, poll_fd) in events { - let revents = match poll_fd.revents() { - Some(revents) => revents, - None => continue, - }; - let mut nbytes = 0; - if fd_event.type_ == wasm32::__WASI_EVENTTYPE_FD_READ { - let _ = unsafe { fionread(fd_event.fd, &mut nbytes) }; - } - let output_event = if revents.contains(nix::poll::EventFlags::POLLNVAL) { - host::__wasi_event_t { - userdata: fd_event.userdata, - type_: fd_event.type_, - error: wasm32::__WASI_EBADF, - u: host::__wasi_event_t___wasi_event_u { - fd_readwrite: - host::__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t { - nbytes: 0, - flags: wasm32::__WASI_EVENT_FD_READWRITE_HANGUP, - }, - }, - } - } else if revents.contains(nix::poll::EventFlags::POLLERR) { - host::__wasi_event_t { - userdata: fd_event.userdata, - type_: fd_event.type_, - error: wasm32::__WASI_EIO, - u: host::__wasi_event_t___wasi_event_u { - fd_readwrite: - host::__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t { - nbytes: 0, - flags: wasm32::__WASI_EVENT_FD_READWRITE_HANGUP, - }, - }, - } - } else if revents.contains(nix::poll::EventFlags::POLLHUP) { - host::__wasi_event_t { - userdata: fd_event.userdata, - type_: fd_event.type_, - error: wasm32::__WASI_ESUCCESS, - u: host::__wasi_event_t___wasi_event_u { - fd_readwrite: - host::__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t { - nbytes: 0, - flags: wasm32::__WASI_EVENT_FD_READWRITE_HANGUP, - }, - }, - } - } else if revents.contains(nix::poll::EventFlags::POLLIN) - | revents.contains(nix::poll::EventFlags::POLLOUT) - { - host::__wasi_event_t { - userdata: fd_event.userdata, - type_: fd_event.type_, - error: wasm32::__WASI_ESUCCESS, - u: host::__wasi_event_t___wasi_event_u { - fd_readwrite: - host::__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t { - nbytes: nbytes as host::__wasi_filesize_t, - flags: 0, - }, - }, - } - } else { - continue; - }; - *output_slice_cur.next().unwrap() = enc_event(output_event); - revents_count += 1; - } - revents_count -} diff --git a/src/hostcalls/mod.rs b/src/hostcalls/mod.rs index a0ce8f8ecc..5832ce7dba 100644 --- a/src/hostcalls/mod.rs +++ b/src/hostcalls/mod.rs @@ -1,7 +1,4 @@ -//! Hostcalls that implement -//! [WASI](https://github.com/CraneStation/wasmtime-wasi/blob/wasi/docs/WASI-overview.md). mod fs; -mod fs_helpers; mod misc; mod sock; diff --git a/src/hostcalls/sock.rs b/src/hostcalls/sock.rs index 0d38a3f611..e3bd80a9c3 100644 --- a/src/hostcalls/sock.rs +++ b/src/hostcalls/sock.rs @@ -1,9 +1,9 @@ #![allow(non_camel_case_types)] -#![allow(unused_unsafe)] -#![allow(unused)] - use crate::ctx::WasiCtx; use crate::wasm32; + +use crate::sys::hostcalls as hostcalls_impl; + use wasi_common_cbindgen::wasi_common_cbindgen; #[wasi_common_cbindgen] @@ -17,7 +17,16 @@ pub fn sock_recv( ro_datalen: wasm32::uintptr_t, ro_flags: wasm32::uintptr_t, ) -> wasm32::__wasi_errno_t { - unimplemented!("sock_recv") + hostcalls_impl::sock_recv( + wasi_ctx, + memory, + sock, + ri_data, + ri_data_len, + ri_flags, + ro_datalen, + ro_flags, + ) } #[wasi_common_cbindgen] @@ -30,7 +39,15 @@ pub fn sock_send( si_flags: wasm32::__wasi_siflags_t, so_datalen: wasm32::uintptr_t, ) -> wasm32::__wasi_errno_t { - unimplemented!("sock_send") + hostcalls_impl::sock_send( + wasi_ctx, + memory, + sock, + si_data, + si_data_len, + si_flags, + so_datalen, + ) } #[wasi_common_cbindgen] @@ -40,5 +57,5 @@ pub fn sock_shutdown( sock: wasm32::__wasi_fd_t, how: wasm32::__wasi_sdflags_t, ) -> wasm32::__wasi_errno_t { - unimplemented!("sock_shutdown") + hostcalls_impl::sock_shutdown(wasi_ctx, memory, sock, how) } diff --git a/src/lib.rs b/src/lib.rs index d98ff3a343..f58e918c24 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,11 +20,12 @@ ) )] -pub mod ctx; -pub mod fdentry; +mod ctx; +mod sys; + +pub mod memory; pub mod host; pub mod hostcalls; -pub mod memory; pub mod wasm32; pub use ctx::{WasiCtx, WasiCtxBuilder}; diff --git a/src/memory.rs b/src/memory.rs index a03db7c697..98085b780a 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,15 +1,6 @@ //! Functions to go back and forth between WASI types in host and wasm32 representations. -//! -//! This module is an adaptation of the `wasmtime-wasi` module -//! [`translate.rs`](https://github.com/CraneStation/wasmtime-wasi/blob/1a6ecf3a0378d71f3fc1ba25ce76a2b43e4166b8/lib/wasi/src/translate.rs); -//! its license file `LICENSE.wasmtime-wasi` is included in this project. -//! -//! Any of these functions that take a `Vmctx` argument are only meant to be called from within a -//! hostcall. -//! -//! This sort of manual encoding will hopefully be obsolete once the IDL is developed. - use crate::{host, wasm32}; +pub use crate::sys::memory::*; use cast; use cast::From as _0; @@ -587,33 +578,3 @@ dec_enc_scalar!( enc_dircookie, enc_dircookie_byref ); - -#[cfg(target_os = "linux")] -pub fn dirent_from_host( - host_entry: &nix::libc::dirent, -) -> Result { - let mut entry = unsafe { std::mem::zeroed::() }; - let d_namlen = unsafe { std::ffi::CStr::from_ptr(host_entry.d_name.as_ptr()) } - .to_bytes() - .len(); - if d_namlen > u32::max_value() as usize { - return Err(host::__WASI_EIO); - } - entry.d_ino = enc_inode(host_entry.d_ino); - entry.d_next = enc_dircookie(host_entry.d_off as u64); - entry.d_namlen = enc_u32(d_namlen as u32); - entry.d_type = enc_filetype(host_entry.d_type); - Ok(entry) -} - -#[cfg(not(target_os = "linux"))] -pub fn dirent_from_host( - host_entry: &nix::libc::dirent, -) -> Result { - let mut entry = unsafe { std::mem::zeroed::() }; - entry.d_ino = enc_inode(host_entry.d_ino); - entry.d_next = enc_dircookie(host_entry.d_seekoff); - entry.d_namlen = enc_u32(u32::from(host_entry.d_namlen)); - entry.d_type = enc_filetype(host_entry.d_type); - Ok(entry) -} diff --git a/src/sys/mod.rs b/src/sys/mod.rs new file mode 100644 index 0000000000..ad6c1de4c5 --- /dev/null +++ b/src/sys/mod.rs @@ -0,0 +1,13 @@ +use cfg_if::cfg_if; + +cfg_if! { + if #[cfg(unix)] { + mod unix; + pub use self::unix::*; + } else if #[cfg(windows)] { + mod windows; + pub use self::windows::*; + } else { + compile_error!("wasi-common doesn't compile for this platform yet"); + } +} diff --git a/src/fdentry.rs b/src/sys/unix/fdmap.rs similarity index 67% rename from src/fdentry.rs rename to src/sys/unix/fdmap.rs index 249e14c589..b1aa321486 100644 --- a/src/fdentry.rs +++ b/src/sys/unix/fdmap.rs @@ -1,7 +1,17 @@ use crate::host; + use std::fs::File; -use std::os::unix::prelude::{FileTypeExt, FromRawFd, IntoRawFd, RawFd}; +use std::os::unix::prelude::{FileTypeExt, FromRawFd, IntoRawFd, RawFd, AsRawFd}; use std::path::PathBuf; +use std::collections::HashMap; + +#[derive(Clone, Debug)] +pub struct FdObject { + pub ty: host::__wasi_filetype_t, + pub rawfd: RawFd, + pub needs_close: bool, + // TODO: directories +} #[derive(Clone, Debug)] pub struct FdEntry { @@ -11,15 +21,32 @@ pub struct FdEntry { pub preopen_path: Option, } +#[derive(Debug)] +pub struct FdMap { + entries: HashMap, +} + +impl Drop for FdObject { + fn drop(&mut self) { + if self.needs_close { + nix::unistd::close(self.rawfd).unwrap_or_else(|e| eprintln!("FdObject::drop(): {}", e)); + } + } +} + impl FdEntry { - pub fn from_file(file: File) -> FdEntry { - unsafe { FdEntry::from_raw_fd(file.into_raw_fd()) } + pub fn from_file(file: File) -> Self { + unsafe { Self::from_raw_fd(file.into_raw_fd()) } + } + + pub fn duplicate(fd: &F) -> Self { + unsafe { Self::from_raw_fd(nix::unistd::dup(fd.as_raw_fd()).unwrap()) } } } impl FromRawFd for FdEntry { // TODO: make this a different function with error handling, rather than using the trait method - unsafe fn from_raw_fd(rawfd: RawFd) -> FdEntry { + unsafe fn from_raw_fd(rawfd: RawFd) -> Self { let (ty, mut rights_base, rights_inheriting) = determine_type_rights(rawfd).expect("can determine file rights"); @@ -33,7 +60,7 @@ impl FromRawFd for FdEntry { rights_base &= !host::__WASI_RIGHT_FD_READ; } - FdEntry { + Self { fd_object: FdObject { ty: ty, rawfd, @@ -122,18 +149,62 @@ pub unsafe fn determine_type_rights( Ok((ty, rights_base, rights_inheriting)) } -#[derive(Clone, Debug)] -pub struct FdObject { - pub ty: host::__wasi_filetype_t, - pub rawfd: RawFd, - pub needs_close: bool, - // TODO: directories -} - -impl Drop for FdObject { - fn drop(&mut self) { - if self.needs_close { - nix::unistd::close(self.rawfd).unwrap_or_else(|e| eprintln!("FdObject::drop(): {}", e)); +impl FdMap { + pub fn new() -> Self { + Self { + entries: HashMap::new() } } -} + + pub(crate) fn insert_fd_entry_at(&mut self, fd: host::__wasi_fd_t, fe: FdEntry) { + self.entries.insert(fd, fe); + } + + pub(crate) fn get(&self, fd: &host::__wasi_fd_t) -> Option<&FdEntry> { + self.entries.get(fd) + } + + pub(crate) fn get_mut(&mut self, fd: &host::__wasi_fd_t) -> Option<&mut FdEntry> { + self.entries.get_mut(fd) + } + + pub(crate) fn remove(&mut self, fd: &host::__wasi_fd_t) -> Option { + self.entries.remove(fd) + } + + pub fn get_fd_entry( + &self, + fd: host::__wasi_fd_t, + rights_base: host::__wasi_rights_t, + rights_inheriting: host::__wasi_rights_t, + ) -> Result<&FdEntry, host::__wasi_errno_t> { + if let Some(fe) = self.entries.get(&fd) { + // validate rights + if !fe.rights_base & rights_base != 0 || !fe.rights_inheriting & rights_inheriting != 0 + { + Err(host::__WASI_ENOTCAPABLE) + } else { + Ok(fe) + } + } else { + Err(host::__WASI_EBADF) + } + } + + pub fn insert_fd_entry( + &mut self, + fe: FdEntry, + ) -> Result { + // never insert where stdio handles usually are + let mut fd = 3; + while self.entries.contains_key(&fd) { + if let Some(next_fd) = fd.checked_add(1) { + fd = next_fd; + } else { + return Err(host::__WASI_EMFILE); + } + } + self.entries.insert(fd, fe); + Ok(fd) + } +} \ No newline at end of file diff --git a/src/sys/unix/host.rs b/src/sys/unix/host.rs new file mode 100644 index 0000000000..6f6914e90d --- /dev/null +++ b/src/sys/unix/host.rs @@ -0,0 +1,240 @@ +//! WASI host types specific to *nix host. +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(dead_code)] +use crate::host; + +pub fn errno_from_nix(errno: nix::errno::Errno) -> host::__wasi_errno_t { + match errno { + nix::errno::Errno::EPERM => host::__WASI_EPERM, + nix::errno::Errno::ENOENT => host::__WASI_ENOENT, + nix::errno::Errno::ESRCH => host::__WASI_ESRCH, + nix::errno::Errno::EINTR => host::__WASI_EINTR, + nix::errno::Errno::EIO => host::__WASI_EIO, + nix::errno::Errno::ENXIO => host::__WASI_ENXIO, + nix::errno::Errno::E2BIG => host::__WASI_E2BIG, + nix::errno::Errno::ENOEXEC => host::__WASI_ENOEXEC, + nix::errno::Errno::EBADF => host::__WASI_EBADF, + nix::errno::Errno::ECHILD => host::__WASI_ECHILD, + nix::errno::Errno::EAGAIN => host::__WASI_EAGAIN, + nix::errno::Errno::ENOMEM => host::__WASI_ENOMEM, + nix::errno::Errno::EACCES => host::__WASI_EACCES, + nix::errno::Errno::EFAULT => host::__WASI_EFAULT, + nix::errno::Errno::EBUSY => host::__WASI_EBUSY, + nix::errno::Errno::EEXIST => host::__WASI_EEXIST, + nix::errno::Errno::EXDEV => host::__WASI_EXDEV, + nix::errno::Errno::ENODEV => host::__WASI_ENODEV, + nix::errno::Errno::ENOTDIR => host::__WASI_ENOTDIR, + nix::errno::Errno::EISDIR => host::__WASI_EISDIR, + nix::errno::Errno::EINVAL => host::__WASI_EINVAL, + nix::errno::Errno::ENFILE => host::__WASI_ENFILE, + nix::errno::Errno::EMFILE => host::__WASI_EMFILE, + nix::errno::Errno::ENOTTY => host::__WASI_ENOTTY, + nix::errno::Errno::ETXTBSY => host::__WASI_ETXTBSY, + nix::errno::Errno::EFBIG => host::__WASI_EFBIG, + nix::errno::Errno::ENOSPC => host::__WASI_ENOSPC, + nix::errno::Errno::ESPIPE => host::__WASI_ESPIPE, + nix::errno::Errno::EROFS => host::__WASI_EROFS, + nix::errno::Errno::EMLINK => host::__WASI_EMLINK, + nix::errno::Errno::EPIPE => host::__WASI_EPIPE, + nix::errno::Errno::EDOM => host::__WASI_EDOM, + nix::errno::Errno::ERANGE => host::__WASI_ERANGE, + nix::errno::Errno::EDEADLK => host::__WASI_EDEADLK, + nix::errno::Errno::ENAMETOOLONG => host::__WASI_ENAMETOOLONG, + nix::errno::Errno::ENOLCK => host::__WASI_ENOLCK, + nix::errno::Errno::ENOSYS => host::__WASI_ENOSYS, + nix::errno::Errno::ENOTEMPTY => host::__WASI_ENOTEMPTY, + nix::errno::Errno::ELOOP => host::__WASI_ELOOP, + nix::errno::Errno::ENOMSG => host::__WASI_ENOMSG, + nix::errno::Errno::EIDRM => host::__WASI_EIDRM, + nix::errno::Errno::ENOLINK => host::__WASI_ENOLINK, + nix::errno::Errno::EPROTO => host::__WASI_EPROTO, + nix::errno::Errno::EMULTIHOP => host::__WASI_EMULTIHOP, + nix::errno::Errno::EBADMSG => host::__WASI_EBADMSG, + nix::errno::Errno::EOVERFLOW => host::__WASI_EOVERFLOW, + nix::errno::Errno::EILSEQ => host::__WASI_EILSEQ, + nix::errno::Errno::ENOTSOCK => host::__WASI_ENOTSOCK, + nix::errno::Errno::EDESTADDRREQ => host::__WASI_EDESTADDRREQ, + nix::errno::Errno::EMSGSIZE => host::__WASI_EMSGSIZE, + nix::errno::Errno::EPROTOTYPE => host::__WASI_EPROTOTYPE, + nix::errno::Errno::ENOPROTOOPT => host::__WASI_ENOPROTOOPT, + nix::errno::Errno::EPROTONOSUPPORT => host::__WASI_EPROTONOSUPPORT, + nix::errno::Errno::EAFNOSUPPORT => host::__WASI_EAFNOSUPPORT, + nix::errno::Errno::EADDRINUSE => host::__WASI_EADDRINUSE, + nix::errno::Errno::EADDRNOTAVAIL => host::__WASI_EADDRNOTAVAIL, + nix::errno::Errno::ENETDOWN => host::__WASI_ENETDOWN, + nix::errno::Errno::ENETUNREACH => host::__WASI_ENETUNREACH, + nix::errno::Errno::ENETRESET => host::__WASI_ENETRESET, + nix::errno::Errno::ECONNABORTED => host::__WASI_ECONNABORTED, + nix::errno::Errno::ECONNRESET => host::__WASI_ECONNRESET, + nix::errno::Errno::ENOBUFS => host::__WASI_ENOBUFS, + nix::errno::Errno::EISCONN => host::__WASI_EISCONN, + nix::errno::Errno::ENOTCONN => host::__WASI_ENOTCONN, + nix::errno::Errno::ETIMEDOUT => host::__WASI_ETIMEDOUT, + nix::errno::Errno::ECONNREFUSED => host::__WASI_ECONNREFUSED, + nix::errno::Errno::EHOSTUNREACH => host::__WASI_EHOSTUNREACH, + nix::errno::Errno::EALREADY => host::__WASI_EALREADY, + nix::errno::Errno::EINPROGRESS => host::__WASI_EINPROGRESS, + nix::errno::Errno::ESTALE => host::__WASI_ESTALE, + nix::errno::Errno::EDQUOT => host::__WASI_EDQUOT, + nix::errno::Errno::ECANCELED => host::__WASI_ECANCELED, + nix::errno::Errno::EOWNERDEAD => host::__WASI_EOWNERDEAD, + nix::errno::Errno::ENOTRECOVERABLE => host::__WASI_ENOTRECOVERABLE, + _ => host::__WASI_ENOSYS, + } +} + +pub unsafe fn ciovec_to_nix<'a>(ciovec: &'a host::__wasi_ciovec_t) -> nix::sys::uio::IoVec<&'a [u8]> { + let slice = std::slice::from_raw_parts(ciovec.buf as *const u8, ciovec.buf_len); + nix::sys::uio::IoVec::from_slice(slice) +} + +pub unsafe fn ciovec_to_nix_mut<'a>( + ciovec: &'a mut host::__wasi_ciovec_t, +) -> nix::sys::uio::IoVec<&'a mut [u8]> { + let slice = std::slice::from_raw_parts_mut(ciovec.buf as *mut u8, ciovec.buf_len); + nix::sys::uio::IoVec::from_mut_slice(slice) +} + +pub unsafe fn iovec_to_nix<'a>(iovec: &'a host::__wasi_iovec_t) -> nix::sys::uio::IoVec<&'a [u8]> { + let slice = std::slice::from_raw_parts(iovec.buf as *const u8, iovec.buf_len); + nix::sys::uio::IoVec::from_slice(slice) +} + +pub unsafe fn iovec_to_nix_mut<'a>( + iovec: &'a mut host::__wasi_iovec_t, +) -> nix::sys::uio::IoVec<&'a mut [u8]> { + let slice = std::slice::from_raw_parts_mut(iovec.buf as *mut u8, iovec.buf_len); + nix::sys::uio::IoVec::from_mut_slice(slice) +} + +#[cfg(target_os = "linux")] +pub const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_RSYNC; + +#[cfg(not(target_os = "linux"))] +pub const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_SYNC; + +pub fn nix_from_fdflags(fdflags: host::__wasi_fdflags_t) -> nix::fcntl::OFlag { + use nix::fcntl::OFlag; + let mut nix_flags = OFlag::empty(); + if fdflags & host::__WASI_FDFLAG_APPEND != 0 { + nix_flags.insert(OFlag::O_APPEND); + } + if fdflags & host::__WASI_FDFLAG_DSYNC != 0 { + nix_flags.insert(OFlag::O_DSYNC); + } + if fdflags & host::__WASI_FDFLAG_NONBLOCK != 0 { + nix_flags.insert(OFlag::O_NONBLOCK); + } + if fdflags & host::__WASI_FDFLAG_RSYNC != 0 { + nix_flags.insert(O_RSYNC); + } + if fdflags & host::__WASI_FDFLAG_SYNC != 0 { + nix_flags.insert(OFlag::O_SYNC); + } + nix_flags +} + +pub fn fdflags_from_nix(oflags: nix::fcntl::OFlag) -> host::__wasi_fdflags_t { + use nix::fcntl::OFlag; + let mut fdflags = 0; + if oflags.contains(OFlag::O_APPEND) { + fdflags |= host::__WASI_FDFLAG_APPEND; + } + if oflags.contains(OFlag::O_DSYNC) { + fdflags |= host::__WASI_FDFLAG_DSYNC; + } + if oflags.contains(OFlag::O_NONBLOCK) { + fdflags |= host::__WASI_FDFLAG_NONBLOCK; + } + if oflags.contains(O_RSYNC) { + fdflags |= host::__WASI_FDFLAG_RSYNC; + } + if oflags.contains(OFlag::O_SYNC) { + fdflags |= host::__WASI_FDFLAG_SYNC; + } + fdflags +} + +pub fn nix_from_oflags(oflags: host::__wasi_oflags_t) -> nix::fcntl::OFlag { + use nix::fcntl::OFlag; + let mut nix_flags = OFlag::empty(); + if oflags & host::__WASI_O_CREAT != 0 { + nix_flags.insert(OFlag::O_CREAT); + } + if oflags & host::__WASI_O_DIRECTORY != 0 { + nix_flags.insert(OFlag::O_DIRECTORY); + } + if oflags & host::__WASI_O_EXCL != 0 { + nix_flags.insert(OFlag::O_EXCL); + } + if oflags & host::__WASI_O_TRUNC != 0 { + nix_flags.insert(OFlag::O_TRUNC); + } + nix_flags +} + +pub fn filetype_from_nix(sflags: nix::sys::stat::SFlag) -> host::__wasi_filetype_t { + use nix::sys::stat::SFlag; + if sflags.contains(SFlag::S_IFCHR) { + host::__WASI_FILETYPE_CHARACTER_DEVICE + } else if sflags.contains(SFlag::S_IFBLK) { + host::__WASI_FILETYPE_BLOCK_DEVICE + } else if sflags.contains(SFlag::S_IFIFO) | sflags.contains(SFlag::S_IFSOCK) { + host::__WASI_FILETYPE_SOCKET_STREAM + } else if sflags.contains(SFlag::S_IFDIR) { + host::__WASI_FILETYPE_DIRECTORY + } else if sflags.contains(SFlag::S_IFREG) { + host::__WASI_FILETYPE_REGULAR_FILE + } else if sflags.contains(SFlag::S_IFLNK) { + host::__WASI_FILETYPE_SYMBOLIC_LINK + } else { + host::__WASI_FILETYPE_UNKNOWN + } +} + +pub fn nix_from_filetype(sflags: host::__wasi_filetype_t) -> nix::sys::stat::SFlag { + use nix::sys::stat::SFlag; + let mut nix_sflags = SFlag::empty(); + if sflags & host::__WASI_FILETYPE_CHARACTER_DEVICE != 0 { + nix_sflags.insert(SFlag::S_IFCHR); + } + if sflags & host::__WASI_FILETYPE_BLOCK_DEVICE != 0 { + nix_sflags.insert(SFlag::S_IFBLK); + } + if sflags & host::__WASI_FILETYPE_SOCKET_STREAM != 0 { + nix_sflags.insert(SFlag::S_IFIFO); + nix_sflags.insert(SFlag::S_IFSOCK); + } + if sflags & host::__WASI_FILETYPE_DIRECTORY != 0 { + nix_sflags.insert(SFlag::S_IFDIR); + } + if sflags & host::__WASI_FILETYPE_REGULAR_FILE != 0 { + nix_sflags.insert(SFlag::S_IFREG); + } + if sflags & host::__WASI_FILETYPE_SYMBOLIC_LINK != 0 { + nix_sflags.insert(SFlag::S_IFLNK); + } + nix_sflags +} + +pub fn filestat_from_nix(filestat: nix::sys::stat::FileStat) -> host::__wasi_filestat_t { + use std::convert::TryFrom; + + let filetype = nix::sys::stat::SFlag::from_bits_truncate(filestat.st_mode); + let dev = host::__wasi_device_t::try_from(filestat.st_dev) + .expect("FileStat::st_dev is trivially convertible to __wasi_device_t"); + let ino = host::__wasi_inode_t::try_from(filestat.st_ino) + .expect("FileStat::st_ino is trivially convertible to __wasi_inode_t"); + + host::__wasi_filestat_t { + st_dev: dev, + st_ino: ino, + st_nlink: filestat.st_nlink as host::__wasi_linkcount_t, + st_size: filestat.st_size as host::__wasi_filesize_t, + st_atim: filestat.st_atime as host::__wasi_timestamp_t, + st_ctim: filestat.st_ctime as host::__wasi_timestamp_t, + st_mtim: filestat.st_mtime as host::__wasi_timestamp_t, + st_filetype: filetype_from_nix(filetype), + } +} diff --git a/src/sys/unix/hostcalls/fs.rs b/src/sys/unix/hostcalls/fs.rs new file mode 100644 index 0000000000..a922061914 --- /dev/null +++ b/src/sys/unix/hostcalls/fs.rs @@ -0,0 +1,1347 @@ +#![allow(non_camel_case_types)] +#![allow(unused_unsafe)] +use crate::sys::fdmap::{determine_type_rights, FdEntry}; +use crate::sys::host as host_impl; + +use crate::ctx::WasiCtx; +use crate::memory::*; +use crate::{host, wasm32}; + +use super::fs_helpers::*; + +use nix::libc::{self, c_long, c_void, off_t}; +use std::ffi::OsStr; +use std::os::unix::prelude::{FromRawFd, OsStrExt}; + +pub fn fd_close(wasi_ctx: &mut WasiCtx, fd: wasm32::__wasi_fd_t) -> wasm32::__wasi_errno_t { + let fd = dec_fd(fd); + if let Some(fdent) = wasi_ctx.fds.get(&fd) { + // can't close preopened files + if fdent.preopen_path.is_some() { + return wasm32::__WASI_ENOTSUP; + } + } + if let Some(mut fdent) = wasi_ctx.fds.remove(&fd) { + fdent.fd_object.needs_close = false; + match nix::unistd::close(fdent.fd_object.rawfd) { + Ok(_) => wasm32::__WASI_ESUCCESS, + Err(e) => host_impl::errno_from_nix(e.as_errno().unwrap()), + } + } else { + wasm32::__WASI_EBADF + } +} + +pub fn fd_datasync(wasi_ctx: &WasiCtx, fd: wasm32::__wasi_fd_t) -> wasm32::__wasi_errno_t { + let host_fd = dec_fd(fd); + let rights = host::__WASI_RIGHT_FD_DATASYNC; + let fe = match wasi_ctx.get_fd_entry(host_fd, rights.into(), 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + let res; + + #[cfg(target_os = "linux")] + { + res = nix::unistd::fdatasync(fe.fd_object.rawfd); + } + + #[cfg(not(target_os = "linux"))] + { + res = nix::unistd::fsync(fe.fd_object.rawfd); + } + + if let Err(e) = res { + return host_impl::errno_from_nix(e.as_errno().unwrap()); + } + wasm32::__WASI_ESUCCESS +} + +pub fn fd_pread( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + iovs_ptr: wasm32::uintptr_t, + iovs_len: wasm32::size_t, + offset: wasm32::__wasi_filesize_t, + nread: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + use nix::sys::uio::pread; + use std::cmp; + + let fd = dec_fd(fd); + let iovs = match dec_iovec_slice(memory, iovs_ptr, iovs_len) { + Ok(iovs) => iovs, + Err(e) => return enc_errno(e), + }; + let rights = host::__WASI_RIGHT_FD_READ; + let fe = match wasi_ctx.get_fd_entry(fd, rights.into(), 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + let offset = dec_filesize(offset); + if offset > i64::max_value() as u64 { + return wasm32::__WASI_EIO; + } + let buf_size = iovs.iter().map(|v| v.buf_len).sum(); + let mut buf = vec![0; buf_size]; + let host_nread = match pread(fe.fd_object.rawfd, &mut buf, offset as off_t) { + Ok(len) => len, + Err(e) => return host_impl::errno_from_nix(e.as_errno().unwrap()), + }; + let mut buf_offset = 0; + let mut left = host_nread; + for iov in &iovs { + if left == 0 { + break; + } + let vec_len = cmp::min(iov.buf_len, left); + unsafe { std::slice::from_raw_parts_mut(iov.buf as *mut u8, vec_len) } + .copy_from_slice(&buf[buf_offset..buf_offset + vec_len]); + buf_offset += vec_len; + left -= vec_len; + } + enc_usize_byref(memory, nread, host_nread) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) +} + +pub fn fd_pwrite( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + iovs_ptr: wasm32::uintptr_t, + iovs_len: wasm32::size_t, + offset: wasm32::__wasi_filesize_t, + nwritten: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + use nix::sys::uio::pwrite; + + let fd = dec_fd(fd); + let iovs = match dec_iovec_slice(memory, iovs_ptr, iovs_len) { + Ok(iovs) => iovs, + Err(e) => return enc_errno(e), + }; + let rights = host::__WASI_RIGHT_FD_READ; + let fe = match wasi_ctx.get_fd_entry(fd, rights.into(), 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + let offset = dec_filesize(offset); + if offset > i64::max_value() as u64 { + return wasm32::__WASI_EIO; + } + let buf_size = iovs.iter().map(|v| v.buf_len).sum(); + let mut buf = Vec::with_capacity(buf_size); + for iov in &iovs { + buf.extend_from_slice(unsafe { + std::slice::from_raw_parts(iov.buf as *const u8, iov.buf_len) + }); + } + let host_nwritten = match pwrite(fe.fd_object.rawfd, &buf, offset as off_t) { + Ok(len) => len, + Err(e) => return host_impl::errno_from_nix(e.as_errno().unwrap()), + }; + enc_usize_byref(memory, nwritten, host_nwritten) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) +} + +pub fn fd_read( + wasi_ctx: &mut WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + iovs_ptr: wasm32::uintptr_t, + iovs_len: wasm32::size_t, + nread: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + use nix::sys::uio::{readv, IoVec}; + + let fd = dec_fd(fd); + let mut iovs = match dec_iovec_slice(memory, iovs_ptr, iovs_len) { + Ok(iovs) => iovs, + Err(e) => return enc_errno(e), + }; + + let fe = match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_FD_READ.into(), 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + + let mut iovs: Vec> = iovs + .iter_mut() + .map(|iov| unsafe { host_impl::iovec_to_nix_mut(iov) }) + .collect(); + + let host_nread = match readv(fe.fd_object.rawfd, &mut iovs) { + Ok(len) => len, + Err(e) => return host_impl::errno_from_nix(e.as_errno().unwrap()), + }; + + if host_nread == 0 { + // we hit eof, so remove the fdentry from the context + let mut fe = wasi_ctx.fds.remove(&fd).expect("file entry is still there"); + fe.fd_object.needs_close = false; + } + + enc_usize_byref(memory, nread, host_nread) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) +} + +pub fn fd_renumber( + wasi_ctx: &mut WasiCtx, + from: wasm32::__wasi_fd_t, + to: wasm32::__wasi_fd_t, +) -> wasm32::__wasi_errno_t { + let from = dec_fd(from); + let to = dec_fd(to); + let fe_from = match wasi_ctx.fds.get(&from) { + Some(fe_from) => fe_from, + None => return wasm32::__WASI_EBADF, + }; + let fe_to = match wasi_ctx.fds.get(&to) { + Some(fe_to) => fe_to, + None => return wasm32::__WASI_EBADF, + }; + if let Err(e) = nix::unistd::dup2(fe_from.fd_object.rawfd, fe_to.fd_object.rawfd) { + return host_impl::errno_from_nix(e.as_errno().unwrap()); + } + let fe_from_rawfd = fe_from.fd_object.rawfd; + wasi_ctx.fds.remove(&(fe_from_rawfd as host::__wasi_fd_t)); + + wasm32::__WASI_ESUCCESS +} + +pub fn fd_seek( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + offset: wasm32::__wasi_filedelta_t, + whence: wasm32::__wasi_whence_t, + newoffset: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + let fd = dec_fd(fd); + let offset = dec_filedelta(offset); + let whence = dec_whence(whence); + + let host_newoffset = { + use nix::unistd::{lseek, Whence}; + let nwhence = match whence { + host::__WASI_WHENCE_CUR => Whence::SeekCur, + host::__WASI_WHENCE_END => Whence::SeekEnd, + host::__WASI_WHENCE_SET => Whence::SeekSet, + _ => return wasm32::__WASI_EINVAL, + }; + + let rights = if offset == 0 && whence == host::__WASI_WHENCE_CUR { + host::__WASI_RIGHT_FD_TELL + } else { + host::__WASI_RIGHT_FD_SEEK | host::__WASI_RIGHT_FD_TELL + }; + match wasi_ctx.get_fd_entry(fd, rights.into(), 0) { + Ok(fe) => match lseek(fe.fd_object.rawfd, offset, nwhence) { + Ok(newoffset) => newoffset, + Err(e) => return host_impl::errno_from_nix(e.as_errno().unwrap()), + }, + Err(e) => return enc_errno(e), + } + }; + + enc_filesize_byref(memory, newoffset, host_newoffset as u64) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) +} + +pub fn fd_tell( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + newoffset: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + let fd = dec_fd(fd); + + let host_offset = { + use nix::unistd::{lseek, Whence}; + + let rights = host::__WASI_RIGHT_FD_TELL; + match wasi_ctx.get_fd_entry(fd, rights.into(), 0) { + Ok(fe) => match lseek(fe.fd_object.rawfd, 0, Whence::SeekCur) { + Ok(newoffset) => newoffset, + Err(e) => return host_impl::errno_from_nix(e.as_errno().unwrap()), + }, + Err(e) => return enc_errno(e), + } + }; + + enc_filesize_byref(memory, newoffset, host_offset as u64) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) +} + +pub fn fd_fdstat_get( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + fdstat_ptr: wasm32::uintptr_t, // *mut wasm32::__wasi_fdstat_t +) -> wasm32::__wasi_errno_t { + let host_fd = dec_fd(fd); + let mut host_fdstat = match dec_fdstat_byref(memory, fdstat_ptr) { + Ok(host_fdstat) => host_fdstat, + Err(e) => return enc_errno(e), + }; + + let errno = if let Some(fe) = wasi_ctx.fds.get(&host_fd) { + host_fdstat.fs_filetype = fe.fd_object.ty; + host_fdstat.fs_rights_base = fe.rights_base; + host_fdstat.fs_rights_inheriting = fe.rights_inheriting; + use nix::fcntl::{fcntl, OFlag, F_GETFL}; + match fcntl(fe.fd_object.rawfd, F_GETFL).map(OFlag::from_bits_truncate) { + Ok(flags) => { + host_fdstat.fs_flags = host_impl::fdflags_from_nix(flags); + wasm32::__WASI_ESUCCESS + } + Err(e) => host_impl::errno_from_nix(e.as_errno().unwrap()), + } + } else { + wasm32::__WASI_EBADF + }; + + enc_fdstat_byref(memory, fdstat_ptr, host_fdstat) + .expect("can write back into the pointer we read from"); + + errno +} + +pub fn fd_fdstat_set_flags( + wasi_ctx: &WasiCtx, + fd: wasm32::__wasi_fd_t, + fdflags: wasm32::__wasi_fdflags_t, +) -> wasm32::__wasi_errno_t { + let host_fd = dec_fd(fd); + let host_fdflags = dec_fdflags(fdflags); + let nix_flags = host_impl::nix_from_fdflags(host_fdflags); + + if let Some(fe) = wasi_ctx.fds.get(&host_fd) { + match nix::fcntl::fcntl(fe.fd_object.rawfd, nix::fcntl::F_SETFL(nix_flags)) { + Ok(_) => wasm32::__WASI_ESUCCESS, + Err(e) => host_impl::errno_from_nix(e.as_errno().unwrap()), + } + } else { + wasm32::__WASI_EBADF + } +} + +pub fn fd_fdstat_set_rights( + wasi_ctx: &mut WasiCtx, + fd: wasm32::__wasi_fd_t, + fs_rights_base: wasm32::__wasi_rights_t, + fs_rights_inheriting: wasm32::__wasi_rights_t, +) -> wasm32::__wasi_errno_t { + let host_fd = dec_fd(fd); + let fe = match wasi_ctx.fds.get_mut(&host_fd) { + Some(fe) => fe, + None => return wasm32::__WASI_EBADF, + }; + if fe.rights_base & fs_rights_base != fs_rights_base + || fe.rights_inheriting & fs_rights_inheriting != fs_rights_inheriting + { + return wasm32::__WASI_ENOTCAPABLE; + } + + fe.rights_base = fs_rights_base; + fe.rights_inheriting = fs_rights_inheriting; + wasm32::__WASI_ESUCCESS +} + +pub fn fd_sync(wasi_ctx: &WasiCtx, fd: wasm32::__wasi_fd_t) -> wasm32::__wasi_errno_t { + let host_fd = dec_fd(fd); + let rights = host::__WASI_RIGHT_FD_SYNC; + let fe = match wasi_ctx.get_fd_entry(host_fd, rights.into(), 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + let res = nix::unistd::fsync(fe.fd_object.rawfd); + if let Err(e) = res { + return host_impl::errno_from_nix(e.as_errno().unwrap()); + } + wasm32::__WASI_ESUCCESS +} + +pub fn fd_write( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + iovs_ptr: wasm32::uintptr_t, + iovs_len: wasm32::size_t, + nwritten: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + use nix::sys::uio::{writev, IoVec}; + + let fd = dec_fd(fd); + let iovs = match dec_iovec_slice(memory, iovs_ptr, iovs_len) { + Ok(iovs) => iovs, + Err(e) => return enc_errno(e), + }; + + let fe = match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_FD_WRITE.into(), 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + + let iovs: Vec> = iovs + .iter() + .map(|iov| unsafe { host_impl::iovec_to_nix(iov) }) + .collect(); + + let host_nwritten = match writev(fe.fd_object.rawfd, &iovs) { + Ok(len) => len, + Err(e) => return host_impl::errno_from_nix(e.as_errno().unwrap()), + }; + + enc_usize_byref(memory, nwritten, host_nwritten) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) +} + +pub fn fd_advise( + wasi_ctx: &WasiCtx, + fd: wasm32::__wasi_fd_t, + offset: wasm32::__wasi_filesize_t, + len: wasm32::__wasi_filesize_t, + advice: wasm32::__wasi_advice_t, +) -> wasm32::__wasi_errno_t { + let host_fd = dec_fd(fd); + let rights = host::__WASI_RIGHT_FD_ADVISE; + let fe = match wasi_ctx.get_fd_entry(host_fd, rights.into(), 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + let advice = dec_advice(advice); + + #[cfg(target_os = "linux")] + { + let host_advice = match advice { + host::__WASI_ADVICE_DONTNEED => libc::POSIX_FADV_DONTNEED, + host::__WASI_ADVICE_SEQUENTIAL => libc::POSIX_FADV_SEQUENTIAL, + host::__WASI_ADVICE_WILLNEED => libc::POSIX_FADV_DONTNEED, + host::__WASI_ADVICE_NOREUSE => libc::POSIX_FADV_NOREUSE, + host::__WASI_ADVICE_RANDOM => libc::POSIX_FADV_RANDOM, + host::__WASI_ADVICE_NORMAL => libc::POSIX_FADV_NORMAL, + _ => return wasm32::__WASI_EINVAL, + }; + let offset = dec_filesize(offset); + let len = dec_filesize(len); + let res = unsafe { + libc::posix_fadvise( + fe.fd_object.rawfd, + offset as off_t, + len as off_t, + host_advice, + ) + }; + if res != 0 { + return host_impl::errno_from_nix(nix::errno::Errno::last()); + } + } + + #[cfg(not(target_os = "linux"))] + { + let _ = (fe, offset, len); + match advice { + host::__WASI_ADVICE_DONTNEED + | host::__WASI_ADVICE_SEQUENTIAL + | host::__WASI_ADVICE_WILLNEED + | host::__WASI_ADVICE_NOREUSE + | host::__WASI_ADVICE_RANDOM + | host::__WASI_ADVICE_NORMAL => {} + _ => return wasm32::__WASI_EINVAL, + } + } + + wasm32::__WASI_ESUCCESS +} + +pub fn fd_allocate( + wasi_ctx: &WasiCtx, + fd: wasm32::__wasi_fd_t, + offset: wasm32::__wasi_filesize_t, + len: wasm32::__wasi_filesize_t, +) -> wasm32::__wasi_errno_t { + let host_fd = dec_fd(fd); + let rights = host::__WASI_RIGHT_FD_ALLOCATE; + let fe = match wasi_ctx.get_fd_entry(host_fd, rights.into(), 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + let offset = dec_filesize(offset); + let len = dec_filesize(len); + + #[cfg(target_os = "linux")] + { + let res = + unsafe { libc::posix_fallocate(fe.fd_object.rawfd, offset as off_t, len as off_t) }; + if res != 0 { + return host_impl::errno_from_nix(nix::errno::Errno::last()); + } + } + + #[cfg(not(target_os = "linux"))] + { + use nix::sys::stat::fstat; + use nix::unistd::ftruncate; + + match fstat(fe.fd_object.rawfd) { + Err(e) => return host_impl::errno_from_nix(e.as_errno().unwrap()), + Ok(st) => { + let current_size = st.st_size as u64; + let wanted_size = match offset.checked_add(len) { + Some(wanted_size) => wanted_size, + None => return wasm32::__WASI_E2BIG, + }; + if wanted_size > i64::max_value() as u64 { + return wasm32::__WASI_E2BIG; + } + if wanted_size > current_size { + if let Err(e) = ftruncate(fe.fd_object.rawfd, wanted_size as off_t) { + return host_impl::errno_from_nix(e.as_errno().unwrap()); + } + } + } + } + } + + wasm32::__WASI_ESUCCESS +} + +pub fn path_create_directory( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + dirfd: wasm32::__wasi_fd_t, + path_ptr: wasm32::uintptr_t, + path_len: wasm32::size_t, +) -> wasm32::__wasi_errno_t { + use nix::errno; + use nix::libc::mkdirat; + + let dirfd = dec_fd(dirfd); + let path = match dec_slice_of::(memory, path_ptr, path_len) { + Ok(slice) => OsStr::from_bytes(slice), + Err(e) => return enc_errno(e), + }; + let (dir, path) = match path_get( + wasi_ctx, + dirfd, + 0, + path, + host::__WASI_RIGHT_PATH_OPEN | host::__WASI_RIGHT_PATH_CREATE_DIRECTORY, + 0, + false, + ) { + Ok((dir, path)) => (dir, path), + Err(e) => return enc_errno(e), + }; + let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { + Ok(path_cstr) => path_cstr, + Err(_) => return wasm32::__WASI_EINVAL, + }; + // nix doesn't expose mkdirat() yet + match unsafe { mkdirat(dir, path_cstr.as_ptr(), 0o777) } { + 0 => wasm32::__WASI_ESUCCESS, + _ => host_impl::errno_from_nix(errno::Errno::last()), + } +} + +pub fn path_link( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + old_dirfd: wasm32::__wasi_fd_t, + _old_flags: wasm32::__wasi_lookupflags_t, + old_path_ptr: wasm32::uintptr_t, + old_path_len: wasm32::size_t, + new_dirfd: wasm32::__wasi_fd_t, + new_path_ptr: wasm32::uintptr_t, + new_path_len: wasm32::size_t, +) -> wasm32::__wasi_errno_t { + use nix::libc::linkat; + + let old_dirfd = dec_fd(old_dirfd); + let new_dirfd = dec_fd(new_dirfd); + let old_path = match dec_slice_of::(memory, old_path_ptr, old_path_len) { + Ok(slice) => OsStr::from_bytes(slice), + Err(e) => return enc_errno(e), + }; + let new_path = match dec_slice_of::(memory, new_path_ptr, new_path_len) { + Ok(slice) => OsStr::from_bytes(slice), + Err(e) => return enc_errno(e), + }; + let rights = host::__WASI_RIGHT_PATH_LINK_SOURCE; + let (old_dir, old_path) = + match path_get(wasi_ctx, old_dirfd, 0, old_path, rights.into(), 0, false) { + Ok((dir, path)) => (dir, path), + Err(e) => return enc_errno(e), + }; + let rights = host::__WASI_RIGHT_PATH_LINK_TARGET; + let (new_dir, new_path) = + match path_get(wasi_ctx, new_dirfd, 0, new_path, rights.into(), 0, false) { + Ok((dir, path)) => (dir, path), + Err(e) => return enc_errno(e), + }; + let old_path_cstr = match std::ffi::CString::new(old_path.as_bytes()) { + Ok(old_path_cstr) => old_path_cstr, + Err(_) => return wasm32::__WASI_EINVAL, + }; + let new_path_cstr = match std::ffi::CString::new(new_path.as_bytes()) { + Ok(new_path_cstr) => new_path_cstr, + Err(_) => return wasm32::__WASI_EINVAL, + }; + + // Not setting AT_SYMLINK_FOLLOW fails on most filesystems + let atflags = libc::AT_SYMLINK_FOLLOW; + let res = unsafe { + linkat( + old_dir, + old_path_cstr.as_ptr(), + new_dir, + new_path_cstr.as_ptr(), + atflags, + ) + }; + if res != 0 { + return host_impl::errno_from_nix(nix::errno::Errno::last()); + } + wasm32::__WASI_ESUCCESS +} + +pub fn path_open( + wasi_ctx: &mut WasiCtx, + memory: &mut [u8], + dirfd: wasm32::__wasi_fd_t, + dirflags: wasm32::__wasi_lookupflags_t, + path_ptr: wasm32::uintptr_t, + path_len: wasm32::size_t, + oflags: wasm32::__wasi_oflags_t, + fs_rights_base: wasm32::__wasi_rights_t, + fs_rights_inheriting: wasm32::__wasi_rights_t, + fs_flags: wasm32::__wasi_fdflags_t, + fd_out_ptr: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + use nix::errno::Errno; + use nix::fcntl::{openat, AtFlags, OFlag}; + use nix::sys::stat::{fstatat, Mode, SFlag}; + + let dirfd = dec_fd(dirfd); + let dirflags = dec_lookupflags(dirflags); + let oflags = dec_oflags(oflags); + let fs_rights_base = dec_rights(fs_rights_base); + let fs_rights_inheriting = dec_rights(fs_rights_inheriting); + let fs_flags = dec_fdflags(fs_flags); + + // which open mode do we need? + let read = fs_rights_base & (host::__WASI_RIGHT_FD_READ | host::__WASI_RIGHT_FD_READDIR) != 0; + let write = fs_rights_base + & (host::__WASI_RIGHT_FD_DATASYNC + | host::__WASI_RIGHT_FD_WRITE + | host::__WASI_RIGHT_FD_ALLOCATE + | host::__WASI_RIGHT_FD_FILESTAT_SET_SIZE) + != 0; + + let mut nix_all_oflags = if read && write { + OFlag::O_RDWR + } else if read { + OFlag::O_RDONLY + } else { + OFlag::O_WRONLY + }; + + // on non-Capsicum systems, we always want nofollow + nix_all_oflags.insert(OFlag::O_NOFOLLOW); + + // which rights are needed on the dirfd? + let mut needed_base = host::__WASI_RIGHT_PATH_OPEN; + let mut needed_inheriting = fs_rights_base | fs_rights_inheriting; + + // convert open flags + let nix_oflags = host_impl::nix_from_oflags(oflags); + nix_all_oflags.insert(nix_oflags); + if nix_all_oflags.contains(OFlag::O_CREAT) { + needed_base |= host::__WASI_RIGHT_PATH_CREATE_FILE; + } + if nix_all_oflags.contains(OFlag::O_TRUNC) { + needed_inheriting |= host::__WASI_RIGHT_PATH_FILESTAT_SET_SIZE; + } + + // convert file descriptor flags + nix_all_oflags.insert(host_impl::nix_from_fdflags(fs_flags)); + if nix_all_oflags.contains(OFlag::O_DSYNC) { + needed_inheriting |= host::__WASI_RIGHT_FD_DATASYNC; + } + if nix_all_oflags.intersects(host_impl::O_RSYNC | OFlag::O_SYNC) { + needed_inheriting |= host::__WASI_RIGHT_FD_SYNC; + } + + let path = match dec_slice_of::(memory, path_ptr, path_len) { + Ok(slice) => OsStr::from_bytes(slice), + Err(e) => return enc_errno(e), + }; + + let (dir, path) = match path_get( + wasi_ctx, + dirfd, + dirflags, + path, + needed_base, + needed_inheriting, + nix_oflags.contains(OFlag::O_CREAT), + ) { + Ok((dir, path)) => (dir, path), + Err(e) => return enc_errno(e), + }; + + // Call openat. Use mode 0o666 so that we follow whatever the user's + // umask is, but don't set the executable flag, because it isn't yet + // meaningful for WASI programs to create executable files. + let new_fd = match openat( + dir, + path.as_os_str(), + nix_all_oflags, + Mode::from_bits_truncate(0o666), + ) { + Ok(fd) => fd, + Err(e) => { + match e.as_errno() { + // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket + Some(Errno::ENXIO) => { + if let Ok(stat) = fstatat(dir, path.as_os_str(), AtFlags::AT_SYMLINK_NOFOLLOW) { + if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFSOCK) { + return wasm32::__WASI_ENOTSUP; + } else { + return wasm32::__WASI_ENXIO; + } + } else { + return wasm32::__WASI_ENXIO; + } + } + // Linux returns ENOTDIR instead of ELOOP when using O_NOFOLLOW|O_DIRECTORY + // on a symlink. + Some(Errno::ENOTDIR) + if !(nix_all_oflags & (OFlag::O_NOFOLLOW | OFlag::O_DIRECTORY)).is_empty() => + { + if let Ok(stat) = fstatat(dir, path.as_os_str(), AtFlags::AT_SYMLINK_NOFOLLOW) { + if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFLNK) { + return wasm32::__WASI_ELOOP; + } + } + return wasm32::__WASI_ENOTDIR; + } + // FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on + // a symlink. + Some(Errno::EMLINK) if !(nix_all_oflags & OFlag::O_NOFOLLOW).is_empty() => { + return wasm32::__WASI_ELOOP; + } + Some(e) => return host_impl::errno_from_nix(e), + None => return wasm32::__WASI_ENOSYS, + } + } + }; + + // Determine the type of the new file descriptor and which rights contradict with this type + let guest_fd = match unsafe { determine_type_rights(new_fd) } { + Err(e) => { + // if `close` fails, note it but do not override the underlying errno + nix::unistd::close(new_fd).unwrap_or_else(|e| { + dbg!(e); + }); + return enc_errno(e); + } + Ok((_ty, max_base, max_inheriting)) => { + let mut fe = unsafe { FdEntry::from_raw_fd(new_fd) }; + fe.rights_base &= max_base; + fe.rights_inheriting &= max_inheriting; + match wasi_ctx.insert_fd_entry(fe) { + Ok(fd) => fd, + Err(e) => return enc_errno(e), + } + } + }; + + enc_fd_byref(memory, fd_out_ptr, guest_fd) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) +} + +pub fn fd_readdir( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + buf: wasm32::uintptr_t, + buf_len: wasm32::size_t, + cookie: wasm32::__wasi_dircookie_t, + buf_used: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + use libc::{dirent, fdopendir, memcpy, readdir_r, seekdir}; + + match enc_usize_byref(memory, buf_used, 0) { + Ok(_) => {} + Err(e) => return enc_errno(e), + }; + let fd = dec_fd(fd); + let rights = host::__WASI_RIGHT_FD_READDIR; + let fe = match wasi_ctx.get_fd_entry(fd, rights.into(), 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + let host_buf = match dec_slice_of_mut::(memory, buf, buf_len) { + Ok(host_buf) => host_buf, + Err(e) => return enc_errno(e), + }; + let host_buf_ptr = host_buf.as_mut_ptr(); + let host_buf_len = host_buf.len(); + let dir = unsafe { fdopendir(fe.fd_object.rawfd) }; + if dir.is_null() { + return host_impl::errno_from_nix(nix::errno::Errno::last()); + } + let cookie = dec_dircookie(cookie); + if cookie != wasm32::__WASI_DIRCOOKIE_START { + unsafe { seekdir(dir, cookie as c_long) }; + } + let mut entry_buf = unsafe { std::mem::uninitialized::() }; + let mut left = host_buf_len; + let mut host_buf_offset: usize = 0; + while left > 0 { + let mut host_entry: *mut dirent = std::ptr::null_mut(); + let res = unsafe { readdir_r(dir, &mut entry_buf, &mut host_entry) }; + if res == -1 { + return host_impl::errno_from_nix(nix::errno::Errno::last()); + } + if host_entry.is_null() { + break; + } + let entry: wasm32::__wasi_dirent_t = match dirent_from_host(&unsafe { *host_entry }) { + Ok(entry) => entry, + Err(e) => return enc_errno(e), + }; + let name_len = entry.d_namlen as usize; + let required_space = std::mem::size_of_val(&entry) + name_len; + if required_space > left { + break; + } + unsafe { + let ptr = host_buf_ptr.offset(host_buf_offset as isize) as *mut c_void + as *mut wasm32::__wasi_dirent_t; + *ptr = entry; + } + host_buf_offset += std::mem::size_of_val(&entry); + let name_ptr = unsafe { *host_entry }.d_name.as_ptr(); + unsafe { + memcpy( + host_buf_ptr.offset(host_buf_offset as isize) as *mut _, + name_ptr as *const _, + name_len, + ) + }; + host_buf_offset += name_len; + left -= required_space; + } + let host_bufused = host_buf_len - left; + enc_usize_byref(memory, buf_used, host_bufused) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) +} + +pub fn path_readlink( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + dirfd: wasm32::__wasi_fd_t, + path_ptr: wasm32::uintptr_t, + path_len: wasm32::size_t, + buf_ptr: wasm32::uintptr_t, + buf_len: wasm32::size_t, + buf_used: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + use nix::fcntl::readlinkat; + + match enc_usize_byref(memory, buf_used, 0) { + Ok(_) => {} + Err(e) => return enc_errno(e), + }; + let dirfd = dec_fd(dirfd); + let path = match dec_slice_of::(memory, path_ptr, path_len) { + Ok(slice) => OsStr::from_bytes(slice), + Err(e) => return enc_errno(e), + }; + let rights = host::__WASI_RIGHT_PATH_READLINK; + let (dir, path) = match path_get(wasi_ctx, dirfd, 0, path, rights.into(), 0, false) { + Ok((dir, path)) => (dir, path), + Err(e) => return enc_errno(e), + }; + let mut buf = match dec_slice_of_mut::(memory, buf_ptr, buf_len) { + Ok(slice) => slice, + Err(e) => return enc_errno(e), + }; + let target_path = match readlinkat(dir, path.as_os_str(), &mut buf) { + Err(e) => return host_impl::errno_from_nix(e.as_errno().unwrap()), + Ok(target_path) => target_path, + }; + let host_bufused = target_path.len(); + match enc_usize_byref(memory, buf_used, host_bufused) { + Ok(_) => {} + Err(e) => return enc_errno(e), + }; + + wasm32::__WASI_ESUCCESS +} + +pub fn path_rename( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + old_dirfd: wasm32::__wasi_fd_t, + old_path_ptr: wasm32::uintptr_t, + old_path_len: wasm32::size_t, + new_dirfd: wasm32::__wasi_fd_t, + new_path_ptr: wasm32::uintptr_t, + new_path_len: wasm32::size_t, +) -> wasm32::__wasi_errno_t { + use nix::libc::renameat; + + let old_dirfd = dec_fd(old_dirfd); + let new_dirfd = dec_fd(new_dirfd); + let old_path = match dec_slice_of::(memory, old_path_ptr, old_path_len) { + Ok(slice) => OsStr::from_bytes(slice), + Err(e) => return enc_errno(e), + }; + let new_path = match dec_slice_of::(memory, new_path_ptr, new_path_len) { + Ok(slice) => OsStr::from_bytes(slice), + Err(e) => return enc_errno(e), + }; + let rights = host::__WASI_RIGHT_PATH_RENAME_SOURCE; + let (old_dir, old_path) = + match path_get(wasi_ctx, old_dirfd, 0, old_path, rights.into(), 0, false) { + Ok((dir, path)) => (dir, path), + Err(e) => return enc_errno(e), + }; + let rights = host::__WASI_RIGHT_PATH_RENAME_TARGET; + let (new_dir, new_path) = + match path_get(wasi_ctx, new_dirfd, 0, new_path, rights.into(), 0, false) { + Ok((dir, path)) => (dir, path), + Err(e) => return enc_errno(e), + }; + let old_path_cstr = match std::ffi::CString::new(old_path.as_bytes()) { + Ok(old_path_cstr) => old_path_cstr, + Err(_) => return wasm32::__WASI_EINVAL, + }; + let new_path_cstr = match std::ffi::CString::new(new_path.as_bytes()) { + Ok(new_path_cstr) => new_path_cstr, + Err(_) => return wasm32::__WASI_EINVAL, + }; + let res = unsafe { + renameat( + old_dir, + old_path_cstr.as_ptr(), + new_dir, + new_path_cstr.as_ptr(), + ) + }; + if res != 0 { + return host_impl::errno_from_nix(nix::errno::Errno::last()); + } + wasm32::__WASI_ESUCCESS +} + +pub fn fd_filestat_get( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + filestat_ptr: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + use nix::sys::stat::fstat; + + let host_fd = dec_fd(fd); + + let errno = if let Some(fe) = wasi_ctx.fds.get(&host_fd) { + match fstat(fe.fd_object.rawfd) { + Err(e) => host_impl::errno_from_nix(e.as_errno().unwrap()), + Ok(filestat) => { + let host_filestat = host_impl::filestat_from_nix(filestat); + enc_filestat_byref(memory, filestat_ptr, host_filestat) + .expect("can write into the pointer"); + wasm32::__WASI_ESUCCESS + } + } + } else { + wasm32::__WASI_EBADF + }; + errno +} + +pub fn fd_filestat_set_times( + wasi_ctx: &WasiCtx, + fd: wasm32::__wasi_fd_t, + st_atim: wasm32::__wasi_timestamp_t, + st_mtim: wasm32::__wasi_timestamp_t, + fst_flags: wasm32::__wasi_fstflags_t, +) -> wasm32::__wasi_errno_t { + use nix::sys::time::{TimeSpec, TimeValLike}; + + let host_fd = dec_fd(fd); + let rights = host::__WASI_RIGHT_FD_FILESTAT_SET_TIMES; + let fe = match wasi_ctx.get_fd_entry(host_fd, rights.into(), 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + let st_atim = dec_timestamp(st_atim); + let mut st_mtim = dec_timestamp(st_mtim); + let fst_flags = dec_fstflags(fst_flags); + if fst_flags & host::__WASI_FILESTAT_SET_MTIM_NOW != 0 { + let clock_id = libc::CLOCK_REALTIME; + let mut timespec = unsafe { std::mem::uninitialized::() }; + let res = unsafe { libc::clock_gettime(clock_id, &mut timespec as *mut libc::timespec) }; + if res != 0 { + return host_impl::errno_from_nix(nix::errno::Errno::last()); + } + let time_ns = match (timespec.tv_sec as host::__wasi_timestamp_t) + .checked_mul(1_000_000_000) + .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as host::__wasi_timestamp_t)) + { + Some(time_ns) => time_ns, + None => return wasm32::__WASI_EOVERFLOW, + }; + st_mtim = time_ns; + } + let ts_atime = match fst_flags { + f if f & host::__WASI_FILESTAT_SET_ATIM_NOW != 0 => libc::timespec { + tv_sec: 0, + tv_nsec: utime_now(), + }, + f if f & host::__WASI_FILESTAT_SET_ATIM != 0 => { + *TimeSpec::nanoseconds(st_atim as i64).as_ref() + } + _ => libc::timespec { + tv_sec: 0, + tv_nsec: utime_omit(), + }, + }; + let ts_mtime = *TimeSpec::nanoseconds(st_mtim as i64).as_ref(); + let times = [ts_atime, ts_mtime]; + let res = unsafe { libc::futimens(fe.fd_object.rawfd, times.as_ptr()) }; + if res != 0 { + return host_impl::errno_from_nix(nix::errno::Errno::last()); + } + wasm32::__WASI_ESUCCESS +} + +pub fn fd_filestat_set_size( + wasi_ctx: &WasiCtx, + fd: wasm32::__wasi_fd_t, + st_size: wasm32::__wasi_filesize_t, +) -> wasm32::__wasi_errno_t { + use nix::unistd::ftruncate; + + let host_fd = dec_fd(fd); + let rights = host::__WASI_RIGHT_FD_FILESTAT_SET_SIZE; + let fe = match wasi_ctx.get_fd_entry(host_fd, rights.into(), 0) { + Ok(fe) => fe, + Err(e) => return enc_errno(e), + }; + let st_size = dec_filesize(st_size); + if st_size > i64::max_value() as u64 { + return wasm32::__WASI_E2BIG; + } + if let Err(e) = ftruncate(fe.fd_object.rawfd, st_size as off_t) { + return host_impl::errno_from_nix(e.as_errno().unwrap()); + } + wasm32::__WASI_ESUCCESS +} + +pub fn path_filestat_get( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + dirfd: wasm32::__wasi_fd_t, + dirflags: wasm32::__wasi_lookupflags_t, + path_ptr: wasm32::uintptr_t, + path_len: wasm32::size_t, + filestat_ptr: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + use nix::fcntl::AtFlags; + use nix::sys::stat::fstatat; + + let dirfd = dec_fd(dirfd); + let dirflags = dec_lookupflags(dirflags); + let path = match dec_slice_of::(memory, path_ptr, path_len) { + Ok(slice) => OsStr::from_bytes(slice), + Err(e) => return enc_errno(e), + }; + let (dir, path) = match path_get( + wasi_ctx, + dirfd, + dirflags, + path, + host::__WASI_RIGHT_PATH_FILESTAT_GET, + 0, + false, + ) { + Ok((dir, path)) => (dir, path), + Err(e) => return enc_errno(e), + }; + let atflags = match dirflags { + 0 => AtFlags::empty(), + _ => AtFlags::AT_SYMLINK_NOFOLLOW, + }; + match fstatat(dir, path.as_os_str(), atflags) { + Err(e) => host_impl::errno_from_nix(e.as_errno().unwrap()), + Ok(filestat) => { + let host_filestat = host_impl::filestat_from_nix(filestat); + enc_filestat_byref(memory, filestat_ptr, host_filestat) + .expect("can write into the pointer"); + wasm32::__WASI_ESUCCESS + } + } +} + +pub fn path_filestat_set_times( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + dirfd: wasm32::__wasi_fd_t, + dirflags: wasm32::__wasi_lookupflags_t, + path_ptr: wasm32::uintptr_t, + path_len: wasm32::size_t, + st_atim: wasm32::__wasi_timestamp_t, + st_mtim: wasm32::__wasi_timestamp_t, + fst_flags: wasm32::__wasi_fstflags_t, +) -> wasm32::__wasi_errno_t { + use nix::sys::time::{TimeSpec, TimeValLike}; + + let dirfd = dec_fd(dirfd); + let dirflags = dec_lookupflags(dirflags); + let path = match dec_slice_of::(memory, path_ptr, path_len) { + Ok(slice) => OsStr::from_bytes(slice), + Err(e) => return enc_errno(e), + }; + let rights = host::__WASI_RIGHT_PATH_FILESTAT_SET_TIMES; + let (dir, path) = match path_get(wasi_ctx, dirfd, dirflags, path, rights.into(), 0, false) { + Ok((dir, path)) => (dir, path), + Err(e) => return enc_errno(e), + }; + let atflags = match dirflags { + wasm32::__WASI_LOOKUP_SYMLINK_FOLLOW => 0, + _ => libc::AT_SYMLINK_NOFOLLOW, + }; + let st_atim = dec_timestamp(st_atim); + let mut st_mtim = dec_timestamp(st_mtim); + let fst_flags = dec_fstflags(fst_flags); + if fst_flags & host::__WASI_FILESTAT_SET_MTIM_NOW != 0 { + let clock_id = libc::CLOCK_REALTIME; + let mut timespec = unsafe { std::mem::uninitialized::() }; + let res = unsafe { libc::clock_gettime(clock_id, &mut timespec as *mut libc::timespec) }; + if res != 0 { + return host_impl::errno_from_nix(nix::errno::Errno::last()); + } + let time_ns = match (timespec.tv_sec as host::__wasi_timestamp_t) + .checked_mul(1_000_000_000) + .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as host::__wasi_timestamp_t)) + { + Some(time_ns) => time_ns, + None => return wasm32::__WASI_EOVERFLOW, + }; + st_mtim = time_ns; + } + let ts_atime = match fst_flags { + f if f & host::__WASI_FILESTAT_SET_ATIM_NOW != 0 => libc::timespec { + tv_sec: 0, + tv_nsec: utime_now(), + }, + f if f & host::__WASI_FILESTAT_SET_ATIM != 0 => { + *TimeSpec::nanoseconds(st_atim as i64).as_ref() + } + _ => libc::timespec { + tv_sec: 0, + tv_nsec: utime_omit(), + }, + }; + let ts_mtime = *TimeSpec::nanoseconds(st_mtim as i64).as_ref(); + let times = [ts_atime, ts_mtime]; + let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { + Ok(path_cstr) => path_cstr, + Err(_) => return wasm32::__WASI_EINVAL, + }; + let res = unsafe { libc::utimensat(dir, path_cstr.as_ptr(), times.as_ptr(), atflags) }; + if res != 0 { + return host_impl::errno_from_nix(nix::errno::Errno::last()); + } + wasm32::__WASI_ESUCCESS +} + +pub fn path_symlink( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + old_path_ptr: wasm32::uintptr_t, + old_path_len: wasm32::size_t, + dirfd: wasm32::__wasi_fd_t, + new_path_ptr: wasm32::uintptr_t, + new_path_len: wasm32::size_t, +) -> wasm32::__wasi_errno_t { + use nix::libc::symlinkat; + + let dirfd = dec_fd(dirfd); + let old_path = match dec_slice_of::(memory, old_path_ptr, old_path_len) { + Ok(slice) => OsStr::from_bytes(slice), + Err(e) => return enc_errno(e), + }; + let new_path = match dec_slice_of::(memory, new_path_ptr, new_path_len) { + Ok(slice) => OsStr::from_bytes(slice), + Err(e) => return enc_errno(e), + }; + let rights = host::__WASI_RIGHT_PATH_SYMLINK; + let (dir, new_path) = match path_get(wasi_ctx, dirfd, 0, new_path, rights.into(), 0, false) { + Ok((dir, path)) => (dir, path), + Err(e) => return enc_errno(e), + }; + let old_path_cstr = match std::ffi::CString::new(old_path.as_bytes()) { + Ok(old_path_cstr) => old_path_cstr, + Err(_) => return wasm32::__WASI_EINVAL, + }; + let new_path_cstr = match std::ffi::CString::new(new_path.as_bytes()) { + Ok(new_path_cstr) => new_path_cstr, + Err(_) => return wasm32::__WASI_EINVAL, + }; + let res = unsafe { symlinkat(old_path_cstr.as_ptr(), dir, new_path_cstr.as_ptr()) }; + if res != 0 { + return host_impl::errno_from_nix(nix::errno::Errno::last()); + } + wasm32::__WASI_ESUCCESS +} + +pub fn path_unlink_file( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + dirfd: wasm32::__wasi_fd_t, + path_ptr: wasm32::uintptr_t, + path_len: wasm32::size_t, +) -> wasm32::__wasi_errno_t { + use nix::errno; + use nix::libc::unlinkat; + + let dirfd = dec_fd(dirfd); + let path = match dec_slice_of::(memory, path_ptr, path_len) { + Ok(slice) => OsStr::from_bytes(slice), + Err(e) => return enc_errno(e), + }; + let (dir, path) = match path_get( + wasi_ctx, + dirfd, + 0, + path, + host::__WASI_RIGHT_PATH_UNLINK_FILE, + 0, + false, + ) { + Ok((dir, path)) => (dir, path), + Err(e) => return enc_errno(e), + }; + let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { + Ok(path_cstr) => path_cstr, + Err(_) => return wasm32::__WASI_EINVAL, + }; + // nix doesn't expose unlinkat() yet + match unsafe { unlinkat(dir, path_cstr.as_ptr(), 0) } { + 0 => wasm32::__WASI_ESUCCESS, + _ => host_impl::errno_from_nix(errno::Errno::last()), + } +} + +pub fn path_remove_directory( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + dirfd: wasm32::__wasi_fd_t, + path_ptr: wasm32::uintptr_t, + path_len: wasm32::size_t, +) -> wasm32::__wasi_errno_t { + use nix::errno; + use nix::libc::{unlinkat, AT_REMOVEDIR}; + + let dirfd = dec_fd(dirfd); + let path = match dec_slice_of::(memory, path_ptr, path_len) { + Ok(slice) => OsStr::from_bytes(slice), + Err(e) => return enc_errno(e), + }; + let rights = host::__WASI_RIGHT_PATH_REMOVE_DIRECTORY; + let (dir, path) = match path_get(wasi_ctx, dirfd, 0, path, rights.into(), 0, false) { + Ok((dir, path)) => (dir, path), + Err(e) => return enc_errno(e), + }; + let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) { + Ok(path_cstr) => path_cstr, + Err(_) => return wasm32::__WASI_EINVAL, + }; + // nix doesn't expose unlinkat() yet + match unsafe { unlinkat(dir, path_cstr.as_ptr(), AT_REMOVEDIR) } { + 0 => wasm32::__WASI_ESUCCESS, + _ => host_impl::errno_from_nix(errno::Errno::last()), + } +} + +pub fn fd_prestat_get( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + prestat_ptr: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + let fd = dec_fd(fd); + // TODO: is this the correct right for this? + match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_PATH_OPEN.into(), 0) { + Ok(fe) => { + if let Some(po_path) = &fe.preopen_path { + if fe.fd_object.ty != host::__WASI_FILETYPE_DIRECTORY { + return wasm32::__WASI_ENOTDIR; + } + enc_prestat_byref( + memory, + prestat_ptr, + host::__wasi_prestat_t { + pr_type: host::__WASI_PREOPENTYPE_DIR, + u: host::__wasi_prestat_t___wasi_prestat_u { + dir: host::__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t { + pr_name_len: po_path.as_os_str().as_bytes().len(), + }, + }, + }, + ) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) + } else { + wasm32::__WASI_ENOTSUP + } + } + Err(e) => enc_errno(e), + } +} + +pub fn fd_prestat_dir_name( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasm32::__wasi_fd_t, + path_ptr: wasm32::uintptr_t, + path_len: wasm32::size_t, +) -> wasm32::__wasi_errno_t { + let fd = dec_fd(fd); + + match wasi_ctx.get_fd_entry(fd, host::__WASI_RIGHT_PATH_OPEN.into(), 0) { + Ok(fe) => { + if let Some(po_path) = &fe.preopen_path { + if fe.fd_object.ty != host::__WASI_FILETYPE_DIRECTORY { + return wasm32::__WASI_ENOTDIR; + } + let path_bytes = po_path.as_os_str().as_bytes(); + if path_bytes.len() > dec_usize(path_len) { + return wasm32::__WASI_ENAMETOOLONG; + } + enc_slice_of(memory, path_bytes, path_ptr) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) + } else { + wasm32::__WASI_ENOTSUP + } + } + Err(e) => enc_errno(e), + } +} diff --git a/src/hostcalls/fs_helpers.rs b/src/sys/unix/hostcalls/fs_helpers.rs similarity index 97% rename from src/hostcalls/fs_helpers.rs rename to src/sys/unix/hostcalls/fs_helpers.rs index 5f5400ebb2..10f5b43e68 100644 --- a/src/hostcalls/fs_helpers.rs +++ b/src/sys/unix/hostcalls/fs_helpers.rs @@ -3,6 +3,7 @@ use crate::ctx::WasiCtx; use crate::host; +use crate::sys::host as host_impl; use nix::libc::{self, c_long}; use std::ffi::{OsStr, OsString}; @@ -191,7 +192,7 @@ pub fn path_get>( Err(e) => { return ret_error( &mut dir_stack, - host::errno_from_nix(e.as_errno().unwrap()), + host_impl::errno_from_nix(e.as_errno().unwrap()), ); } } @@ -199,7 +200,7 @@ pub fn path_get>( Err(e) => { return ret_error( &mut dir_stack, - host::errno_from_nix(e.as_errno().unwrap()), + host_impl::errno_from_nix(e.as_errno().unwrap()), ); } } @@ -237,7 +238,7 @@ pub fn path_get>( let errno = e.as_errno().unwrap(); if errno != Errno::EINVAL && errno != Errno::ENOENT { // only return an error if this path is not actually a symlink - return ret_error(&mut dir_stack, host::errno_from_nix(errno)); + return ret_error(&mut dir_stack, host_impl::errno_from_nix(errno)); } } } diff --git a/src/sys/unix/hostcalls/misc.rs b/src/sys/unix/hostcalls/misc.rs new file mode 100644 index 0000000000..bad79caca1 --- /dev/null +++ b/src/sys/unix/hostcalls/misc.rs @@ -0,0 +1,313 @@ +#![allow(non_camel_case_types)] +#![allow(unused_unsafe)] + +use crate::memory::*; +use crate::{host, wasm32}; +use crate::sys::host as host_impl; + +use nix::convert_ioctl_res; +use nix::libc::{self, c_int}; +use std::cmp; +use std::time::SystemTime; + +pub fn clock_res_get( + memory: &mut [u8], + clock_id: wasm32::__wasi_clockid_t, + resolution_ptr: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + // convert the supported clocks to the libc types, or return EINVAL + let clock_id = match dec_clockid(clock_id) { + host::__WASI_CLOCK_REALTIME => libc::CLOCK_REALTIME, + host::__WASI_CLOCK_MONOTONIC => libc::CLOCK_MONOTONIC, + host::__WASI_CLOCK_PROCESS_CPUTIME_ID => libc::CLOCK_PROCESS_CPUTIME_ID, + host::__WASI_CLOCK_THREAD_CPUTIME_ID => libc::CLOCK_THREAD_CPUTIME_ID, + _ => return wasm32::__WASI_EINVAL, + }; + + // no `nix` wrapper for clock_getres, so we do it ourselves + let mut timespec = unsafe { std::mem::uninitialized::() }; + let res = unsafe { libc::clock_getres(clock_id, &mut timespec as *mut libc::timespec) }; + if res != 0 { + return host_impl::errno_from_nix(nix::errno::Errno::last()); + } + + // convert to nanoseconds, returning EOVERFLOW in case of overflow; this is freelancing a bit + // from the spec but seems like it'll be an unusual situation to hit + (timespec.tv_sec as host::__wasi_timestamp_t) + .checked_mul(1_000_000_000) + .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as host::__wasi_timestamp_t)) + .map_or(wasm32::__WASI_EOVERFLOW, |resolution| { + // a supported clock can never return zero; this case will probably never get hit, but + // make sure we follow the spec + if resolution == 0 { + wasm32::__WASI_EINVAL + } else { + enc_timestamp_byref(memory, resolution_ptr, resolution) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) + } + }) +} + +pub fn clock_time_get( + memory: &mut [u8], + clock_id: wasm32::__wasi_clockid_t, + // ignored for now, but will be useful once we put optional limits on precision to reduce side + // channels + _precision: wasm32::__wasi_timestamp_t, + time_ptr: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + // convert the supported clocks to the libc types, or return EINVAL + let clock_id = match dec_clockid(clock_id) { + host::__WASI_CLOCK_REALTIME => libc::CLOCK_REALTIME, + host::__WASI_CLOCK_MONOTONIC => libc::CLOCK_MONOTONIC, + host::__WASI_CLOCK_PROCESS_CPUTIME_ID => libc::CLOCK_PROCESS_CPUTIME_ID, + host::__WASI_CLOCK_THREAD_CPUTIME_ID => libc::CLOCK_THREAD_CPUTIME_ID, + _ => return wasm32::__WASI_EINVAL, + }; + + // no `nix` wrapper for clock_getres, so we do it ourselves + let mut timespec = unsafe { std::mem::uninitialized::() }; + let res = unsafe { libc::clock_gettime(clock_id, &mut timespec as *mut libc::timespec) }; + if res != 0 { + return host_impl::errno_from_nix(nix::errno::Errno::last()); + } + + // convert to nanoseconds, returning EOVERFLOW in case of overflow; this is freelancing a bit + // from the spec but seems like it'll be an unusual situation to hit + (timespec.tv_sec as host::__wasi_timestamp_t) + .checked_mul(1_000_000_000) + .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as host::__wasi_timestamp_t)) + .map_or(wasm32::__WASI_EOVERFLOW, |time| { + enc_timestamp_byref(memory, time_ptr, time) + .map(|_| wasm32::__WASI_ESUCCESS) + .unwrap_or_else(|e| e) + }) +} + +pub fn poll_oneoff( + memory: &mut [u8], + input: wasm32::uintptr_t, + output: wasm32::uintptr_t, + nsubscriptions: wasm32::size_t, + nevents: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + if nsubscriptions as u64 > wasm32::__wasi_filesize_t::max_value() { + return wasm32::__WASI_EINVAL; + } + enc_pointee(memory, nevents, 0).unwrap(); + let input_slice = + dec_slice_of::(memory, input, nsubscriptions).unwrap(); + + let input: Vec<_> = input_slice.iter().map(|x| dec_subscription(x)).collect(); + + let output_slice = + dec_slice_of_mut::(memory, output, nsubscriptions).unwrap(); + + let timeout = input + .iter() + .filter_map(|event| match event { + Ok(event) if event.type_ == wasm32::__WASI_EVENTTYPE_CLOCK => Some(ClockEventData { + delay: wasi_clock_to_relative_ns_delay(unsafe { event.u.clock }) / 1_000_000, + userdata: event.userdata, + }), + _ => None, + }) + .min_by_key(|event| event.delay); + let fd_events: Vec<_> = input + .iter() + .filter_map(|event| match event { + Ok(event) + if event.type_ == wasm32::__WASI_EVENTTYPE_FD_READ + || event.type_ == wasm32::__WASI_EVENTTYPE_FD_WRITE => + { + Some(FdEventData { + fd: unsafe { event.u.fd_readwrite.fd } as c_int, + type_: event.type_, + userdata: event.userdata, + }) + } + _ => None, + }) + .collect(); + if fd_events.is_empty() && timeout.is_none() { + return wasm32::__WASI_ESUCCESS; + } + let mut poll_fds: Vec<_> = fd_events + .iter() + .map(|event| { + let mut flags = nix::poll::EventFlags::empty(); + match event.type_ { + wasm32::__WASI_EVENTTYPE_FD_READ => flags.insert(nix::poll::EventFlags::POLLIN), + wasm32::__WASI_EVENTTYPE_FD_WRITE => flags.insert(nix::poll::EventFlags::POLLOUT), + // An event on a file descriptor can currently only be of type FD_READ or FD_WRITE + // Nothing else has been defined in the specification, and these are also the only two + // events we filtered before. If we get something else here, the code has a serious bug. + _ => unreachable!(), + }; + nix::poll::PollFd::new(event.fd, flags) + }) + .collect(); + let timeout = timeout.map(|ClockEventData { delay, userdata }| ClockEventData { + delay: cmp::min(delay, c_int::max_value() as u128), + userdata, + }); + let poll_timeout = timeout.map_or(-1, |timeout| timeout.delay as c_int); + let ready = loop { + match nix::poll::poll(&mut poll_fds, poll_timeout) { + Err(_) => { + if nix::errno::Errno::last() == nix::errno::Errno::EINTR { + continue; + } + return host_impl::errno_from_nix(nix::errno::Errno::last()); + } + Ok(ready) => break ready as usize, + } + }; + let events_count = if ready == 0 { + poll_oneoff_handle_timeout_event(output_slice, timeout) + } else { + let events = fd_events.iter().zip(poll_fds.iter()).take(ready); + poll_oneoff_handle_fd_event(output_slice, events) + }; + if let Err(e) = enc_pointee(memory, nevents, events_count) { + return enc_errno(e); + } + wasm32::__WASI_ESUCCESS +} + +pub fn sched_yield() -> wasm32::__wasi_errno_t { + unsafe { libc::sched_yield() }; + wasm32::__WASI_ESUCCESS +} + +// define the `fionread()` function, equivalent to `ioctl(fd, FIONREAD, *bytes)` +nix::ioctl_read_bad!(fionread, nix::libc::FIONREAD, c_int); + +fn wasi_clock_to_relative_ns_delay( + wasi_clock: host::__wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t, +) -> u128 { + if wasi_clock.flags != wasm32::__WASI_SUBSCRIPTION_CLOCK_ABSTIME { + return wasi_clock.timeout as u128; + } + let now: u128 = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .expect("Current date is before the epoch") + .as_nanos(); + let deadline = wasi_clock.timeout as u128; + deadline.saturating_sub(now) +} + +#[derive(Debug, Copy, Clone)] +struct ClockEventData { + delay: u128, + userdata: host::__wasi_userdata_t, +} +#[derive(Debug, Copy, Clone)] +struct FdEventData { + fd: c_int, + type_: host::__wasi_eventtype_t, + userdata: host::__wasi_userdata_t, +} + +fn poll_oneoff_handle_timeout_event( + output_slice: &mut [wasm32::__wasi_event_t], + timeout: Option, +) -> wasm32::size_t { + if let Some(ClockEventData { userdata, .. }) = timeout { + let output_event = host::__wasi_event_t { + userdata, + type_: wasm32::__WASI_EVENTTYPE_CLOCK, + error: wasm32::__WASI_ESUCCESS, + u: host::__wasi_event_t___wasi_event_u { + fd_readwrite: host::__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t { + nbytes: 0, + flags: 0, + }, + }, + }; + output_slice[0] = enc_event(output_event); + 1 + } else { + // shouldn't happen + 0 + } +} + +fn poll_oneoff_handle_fd_event<'t>( + output_slice: &mut [wasm32::__wasi_event_t], + events: impl Iterator, +) -> wasm32::size_t { + let mut output_slice_cur = output_slice.iter_mut(); + let mut revents_count = 0; + for (fd_event, poll_fd) in events { + let revents = match poll_fd.revents() { + Some(revents) => revents, + None => continue, + }; + let mut nbytes = 0; + if fd_event.type_ == wasm32::__WASI_EVENTTYPE_FD_READ { + let _ = unsafe { fionread(fd_event.fd, &mut nbytes) }; + } + let output_event = if revents.contains(nix::poll::EventFlags::POLLNVAL) { + host::__wasi_event_t { + userdata: fd_event.userdata, + type_: fd_event.type_, + error: wasm32::__WASI_EBADF, + u: host::__wasi_event_t___wasi_event_u { + fd_readwrite: + host::__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t { + nbytes: 0, + flags: wasm32::__WASI_EVENT_FD_READWRITE_HANGUP, + }, + }, + } + } else if revents.contains(nix::poll::EventFlags::POLLERR) { + host::__wasi_event_t { + userdata: fd_event.userdata, + type_: fd_event.type_, + error: wasm32::__WASI_EIO, + u: host::__wasi_event_t___wasi_event_u { + fd_readwrite: + host::__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t { + nbytes: 0, + flags: wasm32::__WASI_EVENT_FD_READWRITE_HANGUP, + }, + }, + } + } else if revents.contains(nix::poll::EventFlags::POLLHUP) { + host::__wasi_event_t { + userdata: fd_event.userdata, + type_: fd_event.type_, + error: wasm32::__WASI_ESUCCESS, + u: host::__wasi_event_t___wasi_event_u { + fd_readwrite: + host::__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t { + nbytes: 0, + flags: wasm32::__WASI_EVENT_FD_READWRITE_HANGUP, + }, + }, + } + } else if revents.contains(nix::poll::EventFlags::POLLIN) + | revents.contains(nix::poll::EventFlags::POLLOUT) + { + host::__wasi_event_t { + userdata: fd_event.userdata, + type_: fd_event.type_, + error: wasm32::__WASI_ESUCCESS, + u: host::__wasi_event_t___wasi_event_u { + fd_readwrite: + host::__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t { + nbytes: nbytes as host::__wasi_filesize_t, + flags: 0, + }, + }, + } + } else { + continue; + }; + *output_slice_cur.next().unwrap() = enc_event(output_event); + revents_count += 1; + } + revents_count +} diff --git a/src/sys/unix/hostcalls/mod.rs b/src/sys/unix/hostcalls/mod.rs new file mode 100644 index 0000000000..a0ce8f8ecc --- /dev/null +++ b/src/sys/unix/hostcalls/mod.rs @@ -0,0 +1,10 @@ +//! Hostcalls that implement +//! [WASI](https://github.com/CraneStation/wasmtime-wasi/blob/wasi/docs/WASI-overview.md). +mod fs; +mod fs_helpers; +mod misc; +mod sock; + +pub use self::fs::*; +pub use self::misc::*; +pub use self::sock::*; diff --git a/src/sys/unix/hostcalls/sock.rs b/src/sys/unix/hostcalls/sock.rs new file mode 100644 index 0000000000..2dbef5a69f --- /dev/null +++ b/src/sys/unix/hostcalls/sock.rs @@ -0,0 +1,41 @@ +#![allow(non_camel_case_types)] +#![allow(unused_unsafe)] +#![allow(unused)] + +use crate::ctx::WasiCtx; +use crate::wasm32; +use wasi_common_cbindgen::wasi_common_cbindgen; + +pub fn sock_recv( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + sock: wasm32::__wasi_fd_t, + ri_data: wasm32::uintptr_t, + ri_data_len: wasm32::size_t, + ri_flags: wasm32::__wasi_riflags_t, + ro_datalen: wasm32::uintptr_t, + ro_flags: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + unimplemented!("sock_recv") +} + +pub fn sock_send( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + sock: wasm32::__wasi_fd_t, + si_data: wasm32::uintptr_t, + si_data_len: wasm32::size_t, + si_flags: wasm32::__wasi_siflags_t, + so_datalen: wasm32::uintptr_t, +) -> wasm32::__wasi_errno_t { + unimplemented!("sock_send") +} + +pub fn sock_shutdown( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + sock: wasm32::__wasi_fd_t, + how: wasm32::__wasi_sdflags_t, +) -> wasm32::__wasi_errno_t { + unimplemented!("sock_shutdown") +} \ No newline at end of file diff --git a/src/sys/unix/mod.rs b/src/sys/unix/mod.rs new file mode 100644 index 0000000000..5b200f9fc4 --- /dev/null +++ b/src/sys/unix/mod.rs @@ -0,0 +1,42 @@ +pub mod host; +pub mod hostcalls; +pub mod fdmap; + +pub mod memory { + use crate::{host, wasm32}; + use crate::memory::*; + + #[cfg(target_os = "linux")] + pub fn dirent_from_host( + host_entry: &nix::libc::dirent, + ) -> Result { + let mut entry = unsafe { std::mem::zeroed::() }; + let d_namlen = unsafe { std::ffi::CStr::from_ptr(host_entry.d_name.as_ptr()) } + .to_bytes() + .len(); + if d_namlen > u32::max_value() as usize { + return Err(host::__WASI_EIO); + } + entry.d_ino = enc_inode(host_entry.d_ino); + entry.d_next = enc_dircookie(host_entry.d_off as u64); + entry.d_namlen = enc_u32(d_namlen as u32); + entry.d_type = enc_filetype(host_entry.d_type); + Ok(entry) + } + + #[cfg(not(target_os = "linux"))] + pub fn dirent_from_host( + host_entry: &nix::libc::dirent, + ) -> Result { + let mut entry = unsafe { std::mem::zeroed::() }; + entry.d_ino = enc_inode(host_entry.d_ino); + entry.d_next = enc_dircookie(host_entry.d_seekoff); + entry.d_namlen = enc_u32(u32::from(host_entry.d_namlen)); + entry.d_type = enc_filetype(host_entry.d_type); + Ok(entry) + } +} + +pub fn dev_null() -> std::fs::File { + std::fs::File::open("/dev/null").expect("failed to open /dev/null") +} diff --git a/src/sys/windows/mod.rs b/src/sys/windows/mod.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/wasm32.rs b/src/wasm32.rs index 73bf090c71..b8c379afa6 100644 --- a/src/wasm32.rs +++ b/src/wasm32.rs @@ -89,35 +89,12 @@ pub type loff_t = off64_t; pub type caddr_t = uintptr_t; // *mut i8 pub type socklen_t = u32; pub type sig_atomic_t = i32; + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct fsid_t { pub __val: [i32; 2usize], } -#[allow(non_snake_case)] -#[test] -fn bindgen_test_layout_fsid_t() { - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(fsid_t)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(fsid_t)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).__val as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(fsid_t), - "::", - stringify!(__val) - ) - ); -} // WASI types pub type __wasi_advice_t = u8; @@ -150,6 +127,7 @@ pub type __wasi_subclockflags_t = u16; pub type __wasi_timestamp_t = u64; pub type __wasi_userdata_t = u64; pub type __wasi_whence_t = u8; + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct __wasi_dirent_t { @@ -159,54 +137,7 @@ pub struct __wasi_dirent_t { pub d_type: __wasi_filetype_t, pub __bindgen_padding_0: [u8; 3usize], } -#[test] -fn bindgen_test_layout_wasi_dirent_t() { - assert_eq!( - ::std::mem::size_of::<__wasi_dirent_t>(), - 24usize, - concat!("Size of: ", stringify!(__wasi_dirent_t)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_dirent_t>())).d_next as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__wasi_dirent_t), - "::", - stringify!(d_next) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_dirent_t>())).d_ino as *const _ as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(__wasi_dirent_t), - "::", - stringify!(d_ino) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_dirent_t>())).d_namlen as *const _ as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(__wasi_dirent_t), - "::", - stringify!(d_namlen) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_dirent_t>())).d_type as *const _ as usize }, - 20usize, - concat!( - "Offset of field: ", - stringify!(__wasi_dirent_t), - "::", - stringify!(d_type) - ) - ); -} + #[repr(C)] #[derive(Copy, Clone)] pub struct __wasi_event_t { @@ -216,12 +147,14 @@ pub struct __wasi_event_t { pub __bindgen_padding_0: u32, pub u: __wasi_event_t___wasi_event_u, } + #[repr(C)] #[derive(Copy, Clone)] pub union __wasi_event_t___wasi_event_u { pub fd_readwrite: __wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t, _bindgen_union_align: [u64; 2usize], } + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct __wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t { @@ -229,246 +162,33 @@ pub struct __wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t { pub flags: __wasi_eventrwflags_t, pub __bindgen_padding_0: [u16; 3usize], } -#[test] -fn bindgen_test_layout___wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t() { - assert_eq!( - ::std::mem::size_of::<__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t>(), - 16usize, - concat!( - "Size of: ", - stringify!(__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t) - ) - ); - assert_eq!( - ::std::mem::align_of::<__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t>(), - 8usize, - concat!( - "Alignment of ", - stringify!(__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t>())) - .nbytes as *const _ as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t), - "::", - stringify!(nbytes) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t>())) - .flags as *const _ as usize - }, - 8usize, - concat!( - "Offset of field: ", - stringify!(__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t), - "::", - stringify!(flags) - ) - ); -} -#[test] -fn bindgen_test_layout___wasi_event_t___wasi_event_u() { - assert_eq!( - ::std::mem::size_of::<__wasi_event_t___wasi_event_u>(), - 16usize, - concat!("Size of: ", stringify!(__wasi_event_t___wasi_event_u)) - ); - assert_eq!( - ::std::mem::align_of::<__wasi_event_t___wasi_event_u>(), - 8usize, - concat!("Alignment of ", stringify!(__wasi_event_t___wasi_event_u)) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<__wasi_event_t___wasi_event_u>())).fd_readwrite as *const _ - as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__wasi_event_t___wasi_event_u), - "::", - stringify!(fd_readwrite) - ) - ); -} -#[test] -fn bindgen_test_layout___wasi_event_t() { - assert_eq!( - ::std::mem::size_of::<__wasi_event_t>(), - 32usize, - concat!("Size of: ", stringify!(__wasi_event_t)) - ); - assert_eq!( - ::std::mem::align_of::<__wasi_event_t>(), - 8usize, - concat!("Alignment of ", stringify!(__wasi_event_t)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_event_t>())).userdata as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__wasi_event_t), - "::", - stringify!(userdata) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_event_t>())).error as *const _ as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(__wasi_event_t), - "::", - stringify!(error) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_event_t>())).type_ as *const _ as usize }, - 10usize, - concat!( - "Offset of field: ", - stringify!(__wasi_event_t), - "::", - stringify!(type_) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_event_t>())).u as *const _ as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(__wasi_event_t), - "::", - stringify!(u) - ) - ); -} + #[repr(C)] #[derive(Copy, Clone)] pub struct __wasi_prestat_t { pub pr_type: __wasi_preopentype_t, pub u: __wasi_prestat_t___wasi_prestat_u, } + #[repr(C)] #[derive(Copy, Clone)] pub union __wasi_prestat_t___wasi_prestat_u { pub dir: __wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t, } + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct __wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t { pub pr_name_len: size_t, } -#[test] -fn bindgen_test_layout___wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t() { - assert_eq!( - ::std::mem::size_of::<__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t>(), - 4usize, - concat!( - "Size of: ", - stringify!(__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t) - ) - ); - assert_eq!( - ::std::mem::align_of::<__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t>(), - 4usize, - concat!( - "Alignment of ", - stringify!(__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t>())) - .pr_name_len as *const _ as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t), - "::", - stringify!(pr_name_len) - ) - ); -} -#[test] -fn bindgen_test_layout___wasi_prestat_t___wasi_prestat_u() { - assert_eq!( - ::std::mem::size_of::<__wasi_prestat_t___wasi_prestat_u>(), - 4usize, - concat!("Size of: ", stringify!(__wasi_prestat_t___wasi_prestat_u)) - ); - assert_eq!( - ::std::mem::align_of::<__wasi_prestat_t___wasi_prestat_u>(), - 4usize, - concat!( - "Alignment of ", - stringify!(__wasi_prestat_t___wasi_prestat_u) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<__wasi_prestat_t___wasi_prestat_u>())).dir as *const _ as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__wasi_prestat_t___wasi_prestat_u), - "::", - stringify!(dir) - ) - ); -} -#[test] -fn bindgen_test_layout___wasi_prestat_t() { - assert_eq!( - ::std::mem::size_of::<__wasi_prestat_t>(), - 8usize, - concat!("Size of: ", stringify!(__wasi_prestat_t)) - ); - assert_eq!( - ::std::mem::align_of::<__wasi_prestat_t>(), - 4usize, - concat!("Alignment of ", stringify!(__wasi_prestat_t)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_prestat_t>())).pr_type as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__wasi_prestat_t), - "::", - stringify!(pr_type) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_prestat_t>())).u as *const _ as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(__wasi_prestat_t), - "::", - stringify!(u) - ) - ); -} -#[allow(non_snake_case)] + #[repr(C)] #[derive(Copy, Clone)] pub union __wasi_event_t__bindgen_ty_1 { pub fd_readwrite: __wasi_event_t__bindgen_ty_1__bindgen_ty_1, _bindgen_union_align: [u64; 2usize], } + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct __wasi_event_t__bindgen_ty_1__bindgen_ty_1 { @@ -476,156 +196,14 @@ pub struct __wasi_event_t__bindgen_ty_1__bindgen_ty_1 { pub flags: __wasi_eventrwflags_t, pub __bindgen_padding_0: [u16; 3usize], } -#[allow(non_snake_case)] -#[test] -fn bindgen_test_layout_wasi_event_t__bindgen_ty_1__bindgen_ty_1() { - assert_eq!( - ::std::mem::size_of::<__wasi_event_t__bindgen_ty_1__bindgen_ty_1>(), - 16usize, - concat!( - "Size of: ", - stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_1) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<__wasi_event_t__bindgen_ty_1__bindgen_ty_1>())).nbytes - as *const _ as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_1), - "::", - stringify!(nbytes) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<__wasi_event_t__bindgen_ty_1__bindgen_ty_1>())).flags as *const _ - as usize - }, - 8usize, - concat!( - "Offset of field: ", - stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_1), - "::", - stringify!(flags) - ) - ); -} + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct __wasi_event_t__bindgen_ty_1__bindgen_ty_2 { pub signal: __wasi_signal_t, pub exitcode: __wasi_exitcode_t, } -#[allow(non_snake_case)] -#[test] -fn bindgen_test_layout_wasi_event_t__bindgen_ty_1__bindgen_ty_2() { - assert_eq!( - ::std::mem::size_of::<__wasi_event_t__bindgen_ty_1__bindgen_ty_2>(), - 8usize, - concat!( - "Size of: ", - stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_2) - ) - ); - assert_eq!( - ::std::mem::align_of::<__wasi_event_t__bindgen_ty_1__bindgen_ty_2>(), - 4usize, - concat!( - "Alignment of ", - stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_2) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<__wasi_event_t__bindgen_ty_1__bindgen_ty_2>())).signal - as *const _ as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_2), - "::", - stringify!(signal) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<__wasi_event_t__bindgen_ty_1__bindgen_ty_2>())).exitcode - as *const _ as usize - }, - 4usize, - concat!( - "Offset of field: ", - stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_2), - "::", - stringify!(exitcode) - ) - ); -} -#[allow(non_snake_case)] -#[test] -fn bindgen_test_layout_wasi_event_t__bindgen_ty_1() { - assert_eq!( - ::std::mem::size_of::<__wasi_event_t__bindgen_ty_1>(), - 16usize, - concat!("Size of: ", stringify!(__wasi_event_t__bindgen_ty_1)) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<__wasi_event_t__bindgen_ty_1>())).fd_readwrite as *const _ - as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__wasi_event_t__bindgen_ty_1), - "::", - stringify!(fd_readwrite) - ) - ); -} -#[test] -fn bindgen_test_layout_wasi_event_t() { - assert_eq!( - ::std::mem::size_of::<__wasi_event_t>(), - 32usize, - concat!("Size of: ", stringify!(__wasi_event_t)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_event_t>())).userdata as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__wasi_event_t), - "::", - stringify!(userdata) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_event_t>())).error as *const _ as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(__wasi_event_t), - "::", - stringify!(error) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_event_t>())).type_ as *const _ as usize }, - 10usize, - concat!( - "Offset of field: ", - stringify!(__wasi_event_t), - "::", - stringify!(type_) - ) - ); -} + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct __wasi_fdstat_t { @@ -635,56 +213,7 @@ pub struct __wasi_fdstat_t { pub fs_rights_base: __wasi_rights_t, pub fs_rights_inheriting: __wasi_rights_t, } -#[test] -fn bindgen_test_layout_wasi_fdstat_t() { - assert_eq!( - ::std::mem::size_of::<__wasi_fdstat_t>(), - 24usize, - concat!("Size of: ", stringify!(__wasi_fdstat_t)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_fdstat_t>())).fs_filetype as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__wasi_fdstat_t), - "::", - stringify!(fs_filetype) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_fdstat_t>())).fs_flags as *const _ as usize }, - 2usize, - concat!( - "Offset of field: ", - stringify!(__wasi_fdstat_t), - "::", - stringify!(fs_flags) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_fdstat_t>())).fs_rights_base as *const _ as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(__wasi_fdstat_t), - "::", - stringify!(fs_rights_base) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<__wasi_fdstat_t>())).fs_rights_inheriting as *const _ as usize - }, - 16usize, - concat!( - "Offset of field: ", - stringify!(__wasi_fdstat_t), - "::", - stringify!(fs_rights_inheriting) - ) - ); -} + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct __wasi_filestat_t { @@ -697,172 +226,21 @@ pub struct __wasi_filestat_t { pub st_mtim: __wasi_timestamp_t, pub st_ctim: __wasi_timestamp_t, } -#[test] -fn bindgen_test_layout_wasi_filestat_t() { - assert_eq!( - ::std::mem::size_of::<__wasi_filestat_t>(), - 56usize, - concat!("Size of: ", stringify!(__wasi_filestat_t)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_dev as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__wasi_filestat_t), - "::", - stringify!(st_dev) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_ino as *const _ as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(__wasi_filestat_t), - "::", - stringify!(st_ino) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_filetype as *const _ as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(__wasi_filestat_t), - "::", - stringify!(st_filetype) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_nlink as *const _ as usize }, - 20usize, - concat!( - "Offset of field: ", - stringify!(__wasi_filestat_t), - "::", - stringify!(st_nlink) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_size as *const _ as usize }, - 24usize, - concat!( - "Offset of field: ", - stringify!(__wasi_filestat_t), - "::", - stringify!(st_size) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_atim as *const _ as usize }, - 32usize, - concat!( - "Offset of field: ", - stringify!(__wasi_filestat_t), - "::", - stringify!(st_atim) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_mtim as *const _ as usize }, - 40usize, - concat!( - "Offset of field: ", - stringify!(__wasi_filestat_t), - "::", - stringify!(st_mtim) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_ctim as *const _ as usize }, - 48usize, - concat!( - "Offset of field: ", - stringify!(__wasi_filestat_t), - "::", - stringify!(st_ctim) - ) - ); -} + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct __wasi_ciovec_t { pub buf: uintptr_t, // *const ::std::os::raw::c_void pub buf_len: size_t, } -#[test] -fn bindgen_test_layout_wasi_ciovec_t() { - assert_eq!( - ::std::mem::size_of::<__wasi_ciovec_t>(), - 8usize, - concat!("Size of: ", stringify!(__wasi_ciovec_t)) - ); - assert_eq!( - ::std::mem::align_of::<__wasi_ciovec_t>(), - 4usize, - concat!("Alignment of ", stringify!(__wasi_ciovec_t)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_ciovec_t>())).buf as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__wasi_ciovec_t), - "::", - stringify!(buf) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_ciovec_t>())).buf_len as *const _ as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(__wasi_ciovec_t), - "::", - stringify!(buf_len) - ) - ); -} + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct __wasi_iovec_t { pub buf: uintptr_t, // *mut ::std::os::raw::c_void pub buf_len: size_t, } -#[test] -fn bindgen_test_layout_wasi_iovec_t() { - assert_eq!( - ::std::mem::size_of::<__wasi_iovec_t>(), - 8usize, - concat!("Size of: ", stringify!(__wasi_iovec_t)) - ); - assert_eq!( - ::std::mem::align_of::<__wasi_iovec_t>(), - 4usize, - concat!("Alignment of ", stringify!(__wasi_iovec_t)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_iovec_t>())).buf as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__wasi_iovec_t), - "::", - stringify!(buf) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_iovec_t>())).buf_len as *const _ as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(__wasi_iovec_t), - "::", - stringify!(buf_len) - ) - ); -} + #[repr(C)] #[derive(Copy, Clone)] pub struct __wasi_subscription_t { @@ -871,6 +249,7 @@ pub struct __wasi_subscription_t { pub __bindgen_padding_0: u32, pub u: __wasi_subscription_t___wasi_subscription_u, } + #[repr(C)] #[derive(Copy, Clone)] pub union __wasi_subscription_t___wasi_subscription_u { @@ -879,6 +258,7 @@ pub union __wasi_subscription_t___wasi_subscription_u { __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_fd_readwrite_t, _bindgen_union_align: [u64; 5usize], } + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t { @@ -890,242 +270,13 @@ pub struct __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clo pub flags: __wasi_subclockflags_t, pub __bindgen_padding_1: [u16; 3usize], } -#[test] -fn bindgen_test_layout___wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t() { - assert_eq!( - ::std::mem::size_of::< - __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t, - >(), - 40usize, - concat!( - "Size of: ", - stringify!(__wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t) - ) - ); - assert_eq!( - ::std::mem::align_of::< - __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t, - >(), - 8usize, - concat!( - "Alignment of ", - stringify!(__wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::< - __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t, - >())) - .identifier as *const _ as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t), - "::", - stringify!(identifier) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::< - __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t, - >())) - .clock_id as *const _ as usize - }, - 8usize, - concat!( - "Offset of field: ", - stringify!(__wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t), - "::", - stringify!(clock_id) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::< - __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t, - >())) - .timeout as *const _ as usize - }, - 16usize, - concat!( - "Offset of field: ", - stringify!(__wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t), - "::", - stringify!(timeout) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::< - __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t, - >())) - .precision as *const _ as usize - }, - 24usize, - concat!( - "Offset of field: ", - stringify!(__wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t), - "::", - stringify!(precision) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::< - __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t, - >())) - .flags as *const _ as usize - }, - 32usize, - concat!( - "Offset of field: ", - stringify!(__wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t), - "::", - stringify!(flags) - ) - ); -} + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_fd_readwrite_t { pub fd: __wasi_fd_t, } -#[test] -fn bindgen_test_layout___wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_fd_readwrite_t( -) { - assert_eq!( - ::std::mem::size_of::< - __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_fd_readwrite_t, - >(), - 4usize, - concat!( - "Size of: ", - stringify!( - __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_fd_readwrite_t - ) - ) - ); - assert_eq!( - ::std::mem::align_of::< - __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_fd_readwrite_t, - >(), - 4usize, - concat!( - "Alignment of ", - stringify!( - __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_fd_readwrite_t - ) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::< - __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_fd_readwrite_t, - >())) - .fd as *const _ as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!( - __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_fd_readwrite_t - ), - "::", - stringify!(fd) - ) - ); -} -#[test] -fn bindgen_test_layout___wasi_subscription_t___wasi_subscription_u() { - assert_eq!( - ::std::mem::size_of::<__wasi_subscription_t___wasi_subscription_u>(), - 40usize, - concat!( - "Size of: ", - stringify!(__wasi_subscription_t___wasi_subscription_u) - ) - ); - assert_eq!( - ::std::mem::align_of::<__wasi_subscription_t___wasi_subscription_u>(), - 8usize, - concat!( - "Alignment of ", - stringify!(__wasi_subscription_t___wasi_subscription_u) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<__wasi_subscription_t___wasi_subscription_u>())).clock - as *const _ as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__wasi_subscription_t___wasi_subscription_u), - "::", - stringify!(clock) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<__wasi_subscription_t___wasi_subscription_u>())).fd_readwrite - as *const _ as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__wasi_subscription_t___wasi_subscription_u), - "::", - stringify!(fd_readwrite) - ) - ); -} -#[test] -fn bindgen_test_layout___wasi_subscription_t() { - assert_eq!( - ::std::mem::size_of::<__wasi_subscription_t>(), - 56usize, - concat!("Size of: ", stringify!(__wasi_subscription_t)) - ); - assert_eq!( - ::std::mem::align_of::<__wasi_subscription_t>(), - 8usize, - concat!("Alignment of ", stringify!(__wasi_subscription_t)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_subscription_t>())).userdata as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__wasi_subscription_t), - "::", - stringify!(userdata) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_subscription_t>())).type_ as *const _ as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(__wasi_subscription_t), - "::", - stringify!(type_) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__wasi_subscription_t>())).u as *const _ as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(__wasi_subscription_t), - "::", - stringify!(u) - ) - ); -} + pub fn strerror(errno: __wasi_errno_t) -> &'static str { match errno { __WASI_ESUCCESS => "__WASI_ESUCCESS", @@ -1442,82 +593,917 @@ pub const __WASI_WHENCE_CUR: __wasi_whence_t = 0; pub const __WASI_WHENCE_END: __wasi_whence_t = 1; pub const __WASI_WHENCE_SET: __wasi_whence_t = 2; -pub fn errno_from_nix(errno: nix::errno::Errno) -> __wasi_errno_t { - match errno { - nix::errno::Errno::EPERM => __WASI_EPERM, - nix::errno::Errno::ENOENT => __WASI_ENOENT, - nix::errno::Errno::ESRCH => __WASI_ESRCH, - nix::errno::Errno::EINTR => __WASI_EINTR, - nix::errno::Errno::EIO => __WASI_EIO, - nix::errno::Errno::ENXIO => __WASI_ENXIO, - nix::errno::Errno::E2BIG => __WASI_E2BIG, - nix::errno::Errno::ENOEXEC => __WASI_ENOEXEC, - nix::errno::Errno::EBADF => __WASI_EBADF, - nix::errno::Errno::ECHILD => __WASI_ECHILD, - nix::errno::Errno::EAGAIN => __WASI_EAGAIN, - nix::errno::Errno::ENOMEM => __WASI_ENOMEM, - nix::errno::Errno::EACCES => __WASI_EACCES, - nix::errno::Errno::EFAULT => __WASI_EFAULT, - nix::errno::Errno::EBUSY => __WASI_EBUSY, - nix::errno::Errno::EEXIST => __WASI_EEXIST, - nix::errno::Errno::EXDEV => __WASI_EXDEV, - nix::errno::Errno::ENODEV => __WASI_ENODEV, - nix::errno::Errno::ENOTDIR => __WASI_ENOTDIR, - nix::errno::Errno::EISDIR => __WASI_EISDIR, - nix::errno::Errno::EINVAL => __WASI_EINVAL, - nix::errno::Errno::ENFILE => __WASI_ENFILE, - nix::errno::Errno::EMFILE => __WASI_EMFILE, - nix::errno::Errno::ENOTTY => __WASI_ENOTTY, - nix::errno::Errno::ETXTBSY => __WASI_ETXTBSY, - nix::errno::Errno::EFBIG => __WASI_EFBIG, - nix::errno::Errno::ENOSPC => __WASI_ENOSPC, - nix::errno::Errno::ESPIPE => __WASI_ESPIPE, - nix::errno::Errno::EROFS => __WASI_EROFS, - nix::errno::Errno::EMLINK => __WASI_EMLINK, - nix::errno::Errno::EPIPE => __WASI_EPIPE, - nix::errno::Errno::EDOM => __WASI_EDOM, - nix::errno::Errno::ERANGE => __WASI_ERANGE, - nix::errno::Errno::EDEADLK => __WASI_EDEADLK, - nix::errno::Errno::ENAMETOOLONG => __WASI_ENAMETOOLONG, - nix::errno::Errno::ENOLCK => __WASI_ENOLCK, - nix::errno::Errno::ENOSYS => __WASI_ENOSYS, - nix::errno::Errno::ENOTEMPTY => __WASI_ENOTEMPTY, - nix::errno::Errno::ELOOP => __WASI_ELOOP, - nix::errno::Errno::ENOMSG => __WASI_ENOMSG, - nix::errno::Errno::EIDRM => __WASI_EIDRM, - nix::errno::Errno::ENOLINK => __WASI_ENOLINK, - nix::errno::Errno::EPROTO => __WASI_EPROTO, - nix::errno::Errno::EMULTIHOP => __WASI_EMULTIHOP, - nix::errno::Errno::EBADMSG => __WASI_EBADMSG, - nix::errno::Errno::EOVERFLOW => __WASI_EOVERFLOW, - nix::errno::Errno::EILSEQ => __WASI_EILSEQ, - nix::errno::Errno::ENOTSOCK => __WASI_ENOTSOCK, - nix::errno::Errno::EDESTADDRREQ => __WASI_EDESTADDRREQ, - nix::errno::Errno::EMSGSIZE => __WASI_EMSGSIZE, - nix::errno::Errno::EPROTOTYPE => __WASI_EPROTOTYPE, - nix::errno::Errno::ENOPROTOOPT => __WASI_ENOPROTOOPT, - nix::errno::Errno::EPROTONOSUPPORT => __WASI_EPROTONOSUPPORT, - nix::errno::Errno::EAFNOSUPPORT => __WASI_EAFNOSUPPORT, - nix::errno::Errno::EADDRINUSE => __WASI_EADDRINUSE, - nix::errno::Errno::EADDRNOTAVAIL => __WASI_EADDRNOTAVAIL, - nix::errno::Errno::ENETDOWN => __WASI_ENETDOWN, - nix::errno::Errno::ENETUNREACH => __WASI_ENETUNREACH, - nix::errno::Errno::ENETRESET => __WASI_ENETRESET, - nix::errno::Errno::ECONNABORTED => __WASI_ECONNABORTED, - nix::errno::Errno::ECONNRESET => __WASI_ECONNRESET, - nix::errno::Errno::ENOBUFS => __WASI_ENOBUFS, - nix::errno::Errno::EISCONN => __WASI_EISCONN, - nix::errno::Errno::ENOTCONN => __WASI_ENOTCONN, - nix::errno::Errno::ETIMEDOUT => __WASI_ETIMEDOUT, - nix::errno::Errno::ECONNREFUSED => __WASI_ECONNREFUSED, - nix::errno::Errno::EHOSTUNREACH => __WASI_EHOSTUNREACH, - nix::errno::Errno::EALREADY => __WASI_EALREADY, - nix::errno::Errno::EINPROGRESS => __WASI_EINPROGRESS, - nix::errno::Errno::ESTALE => __WASI_ESTALE, - nix::errno::Errno::EDQUOT => __WASI_EDQUOT, - nix::errno::Errno::ECANCELED => __WASI_ECANCELED, - nix::errno::Errno::EOWNERDEAD => __WASI_EOWNERDEAD, - nix::errno::Errno::ENOTRECOVERABLE => __WASI_ENOTRECOVERABLE, - _ => __WASI_ENOSYS, +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn bindgen_test_layout_fsid_t() { + assert_eq!( + ::std::mem::size_of::(), + 8usize, + concat!("Size of: ", stringify!(fsid_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(fsid_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__val as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(fsid_t), + "::", + stringify!(__val) + ) + ); } + + #[test] + fn bindgen_test_layout_wasi_dirent_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_dirent_t>(), + 24usize, + concat!("Size of: ", stringify!(__wasi_dirent_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_dirent_t>())).d_next as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_dirent_t), + "::", + stringify!(d_next) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_dirent_t>())).d_ino as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_dirent_t), + "::", + stringify!(d_ino) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_dirent_t>())).d_namlen as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(__wasi_dirent_t), + "::", + stringify!(d_namlen) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_dirent_t>())).d_type as *const _ as usize }, + 20usize, + concat!( + "Offset of field: ", + stringify!(__wasi_dirent_t), + "::", + stringify!(d_type) + ) + ); + } + + #[test] + fn bindgen_test_layout___wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t>(), + 16usize, + concat!( + "Size of: ", + stringify!(__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t) + ) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t>(), + 8usize, + concat!( + "Alignment of ", + stringify!(__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t>( + ))) + .nbytes as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t), + "::", + stringify!(nbytes) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t>( + ))) + .flags as *const _ as usize + }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t), + "::", + stringify!(flags) + ) + ); + } + + #[test] + fn bindgen_test_layout___wasi_event_t___wasi_event_u() { + assert_eq!( + ::std::mem::size_of::<__wasi_event_t___wasi_event_u>(), + 16usize, + concat!("Size of: ", stringify!(__wasi_event_t___wasi_event_u)) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_event_t___wasi_event_u>(), + 8usize, + concat!("Alignment of ", stringify!(__wasi_event_t___wasi_event_u)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_event_t___wasi_event_u>())).fd_readwrite as *const _ + as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t___wasi_event_u), + "::", + stringify!(fd_readwrite) + ) + ); + } + + #[test] + fn bindgen_test_layout___wasi_event_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_event_t>(), + 32usize, + concat!("Size of: ", stringify!(__wasi_event_t)) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_event_t>(), + 8usize, + concat!("Alignment of ", stringify!(__wasi_event_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_event_t>())).userdata as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t), + "::", + stringify!(userdata) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_event_t>())).error as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t), + "::", + stringify!(error) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_event_t>())).type_ as *const _ as usize }, + 10usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t), + "::", + stringify!(type_) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_event_t>())).u as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t), + "::", + stringify!(u) + ) + ); + } + + #[test] + fn bindgen_test_layout___wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t>(), + 4usize, + concat!( + "Size of: ", + stringify!(__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t) + ) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t>(), + 4usize, + concat!( + "Alignment of ", + stringify!(__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t>())) + .pr_name_len as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t), + "::", + stringify!(pr_name_len) + ) + ); + } + + #[test] + fn bindgen_test_layout___wasi_prestat_t___wasi_prestat_u() { + assert_eq!( + ::std::mem::size_of::<__wasi_prestat_t___wasi_prestat_u>(), + 4usize, + concat!("Size of: ", stringify!(__wasi_prestat_t___wasi_prestat_u)) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_prestat_t___wasi_prestat_u>(), + 4usize, + concat!( + "Alignment of ", + stringify!(__wasi_prestat_t___wasi_prestat_u) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_prestat_t___wasi_prestat_u>())).dir as *const _ + as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_prestat_t___wasi_prestat_u), + "::", + stringify!(dir) + ) + ); + } + + #[test] + fn bindgen_test_layout___wasi_prestat_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_prestat_t>(), + 8usize, + concat!("Size of: ", stringify!(__wasi_prestat_t)) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_prestat_t>(), + 4usize, + concat!("Alignment of ", stringify!(__wasi_prestat_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_prestat_t>())).pr_type as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_prestat_t), + "::", + stringify!(pr_type) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_prestat_t>())).u as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(__wasi_prestat_t), + "::", + stringify!(u) + ) + ); + } + + #[test] + fn bindgen_test_layout_wasi_event_t__bindgen_ty_1__bindgen_ty_1() { + assert_eq!( + ::std::mem::size_of::<__wasi_event_t__bindgen_ty_1__bindgen_ty_1>(), + 16usize, + concat!( + "Size of: ", + stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_1) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_event_t__bindgen_ty_1__bindgen_ty_1>())).nbytes + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(nbytes) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_event_t__bindgen_ty_1__bindgen_ty_1>())).flags + as *const _ as usize + }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(flags) + ) + ); + } + + #[test] + fn bindgen_test_layout_wasi_event_t__bindgen_ty_1__bindgen_ty_2() { + assert_eq!( + ::std::mem::size_of::<__wasi_event_t__bindgen_ty_1__bindgen_ty_2>(), + 8usize, + concat!( + "Size of: ", + stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_2) + ) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_event_t__bindgen_ty_1__bindgen_ty_2>(), + 4usize, + concat!( + "Alignment of ", + stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_2) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_event_t__bindgen_ty_1__bindgen_ty_2>())).signal + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_2), + "::", + stringify!(signal) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_event_t__bindgen_ty_1__bindgen_ty_2>())).exitcode + as *const _ as usize + }, + 4usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_2), + "::", + stringify!(exitcode) + ) + ); + } + + #[test] + fn bindgen_test_layout_wasi_event_t__bindgen_ty_1() { + assert_eq!( + ::std::mem::size_of::<__wasi_event_t__bindgen_ty_1>(), + 16usize, + concat!("Size of: ", stringify!(__wasi_event_t__bindgen_ty_1)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_event_t__bindgen_ty_1>())).fd_readwrite as *const _ + as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t__bindgen_ty_1), + "::", + stringify!(fd_readwrite) + ) + ); + } + + #[test] + fn bindgen_test_layout_wasi_event_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_event_t>(), + 32usize, + concat!("Size of: ", stringify!(__wasi_event_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_event_t>())).userdata as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t), + "::", + stringify!(userdata) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_event_t>())).error as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t), + "::", + stringify!(error) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_event_t>())).type_ as *const _ as usize }, + 10usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t), + "::", + stringify!(type_) + ) + ); + } + + #[test] + fn bindgen_test_layout_wasi_fdstat_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_fdstat_t>(), + 24usize, + concat!("Size of: ", stringify!(__wasi_fdstat_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_fdstat_t>())).fs_filetype as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_fdstat_t), + "::", + stringify!(fs_filetype) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_fdstat_t>())).fs_flags as *const _ as usize }, + 2usize, + concat!( + "Offset of field: ", + stringify!(__wasi_fdstat_t), + "::", + stringify!(fs_flags) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_fdstat_t>())).fs_rights_base as *const _ as usize + }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_fdstat_t), + "::", + stringify!(fs_rights_base) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_fdstat_t>())).fs_rights_inheriting as *const _ + as usize + }, + 16usize, + concat!( + "Offset of field: ", + stringify!(__wasi_fdstat_t), + "::", + stringify!(fs_rights_inheriting) + ) + ); + } + + #[test] + fn bindgen_test_layout_wasi_filestat_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_filestat_t>(), + 56usize, + concat!("Size of: ", stringify!(__wasi_filestat_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_dev as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_dev) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_ino as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_ino) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_filestat_t>())).st_filetype as *const _ as usize + }, + 16usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_filetype) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_nlink as *const _ as usize }, + 20usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_nlink) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_size as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_size) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_atim as *const _ as usize }, + 32usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_atim) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_mtim as *const _ as usize }, + 40usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_mtim) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_ctim as *const _ as usize }, + 48usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_ctim) + ) + ); + } + + #[test] + fn bindgen_test_layout_wasi_ciovec_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_ciovec_t>(), + 8usize, + concat!("Size of: ", stringify!(__wasi_ciovec_t)) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_ciovec_t>(), + 4usize, + concat!("Alignment of ", stringify!(__wasi_ciovec_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_ciovec_t>())).buf as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_ciovec_t), + "::", + stringify!(buf) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_ciovec_t>())).buf_len as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(__wasi_ciovec_t), + "::", + stringify!(buf_len) + ) + ); + } + + #[test] + fn bindgen_test_layout_wasi_iovec_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_iovec_t>(), + 8usize, + concat!("Size of: ", stringify!(__wasi_iovec_t)) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_iovec_t>(), + 4usize, + concat!("Alignment of ", stringify!(__wasi_iovec_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_iovec_t>())).buf as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_iovec_t), + "::", + stringify!(buf) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_iovec_t>())).buf_len as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(__wasi_iovec_t), + "::", + stringify!(buf_len) + ) + ); + } + + #[test] + fn bindgen_test_layout___wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t( + ) { + assert_eq!( + ::std::mem::size_of::< + __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t, + >(), + 40usize, + concat!( + "Size of: ", + stringify!( + __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t + ) + ) + ); + assert_eq!( + ::std::mem::align_of::< + __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t, + >(), + 8usize, + concat!( + "Alignment of ", + stringify!( + __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t + ) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::< + __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t, + >())) + .identifier as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!( + __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t + ), + "::", + stringify!(identifier) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::< + __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t, + >())) + .clock_id as *const _ as usize + }, + 8usize, + concat!( + "Offset of field: ", + stringify!( + __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t + ), + "::", + stringify!(clock_id) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::< + __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t, + >())) + .timeout as *const _ as usize + }, + 16usize, + concat!( + "Offset of field: ", + stringify!( + __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t + ), + "::", + stringify!(timeout) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::< + __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t, + >())) + .precision as *const _ as usize + }, + 24usize, + concat!( + "Offset of field: ", + stringify!( + __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t + ), + "::", + stringify!(precision) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::< + __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t, + >())) + .flags as *const _ as usize + }, + 32usize, + concat!( + "Offset of field: ", + stringify!( + __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t + ), + "::", + stringify!(flags) + ) + ); + } + + #[test] + fn bindgen_test_layout___wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_fd_readwrite_t( + ) { + assert_eq!( + ::std::mem::size_of::< + __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_fd_readwrite_t, + >(), + 4usize, + concat!( + "Size of: ", + stringify!( + __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_fd_readwrite_t + ) + ) + ); + assert_eq!( + ::std::mem::align_of::< + __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_fd_readwrite_t, + >(), + 4usize, + concat!( + "Alignment of ", + stringify!( + __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_fd_readwrite_t + ) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::< + __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_fd_readwrite_t, + >())) + .fd as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!( + __wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_fd_readwrite_t + ), + "::", + stringify!(fd) + ) + ); + } + + #[test] + fn bindgen_test_layout___wasi_subscription_t___wasi_subscription_u() { + assert_eq!( + ::std::mem::size_of::<__wasi_subscription_t___wasi_subscription_u>(), + 40usize, + concat!( + "Size of: ", + stringify!(__wasi_subscription_t___wasi_subscription_u) + ) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_subscription_t___wasi_subscription_u>(), + 8usize, + concat!( + "Alignment of ", + stringify!(__wasi_subscription_t___wasi_subscription_u) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_subscription_t___wasi_subscription_u>())).clock + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t___wasi_subscription_u), + "::", + stringify!(clock) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_subscription_t___wasi_subscription_u>())).fd_readwrite + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t___wasi_subscription_u), + "::", + stringify!(fd_readwrite) + ) + ); + } + + #[test] + fn bindgen_test_layout___wasi_subscription_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_subscription_t>(), + 56usize, + concat!("Size of: ", stringify!(__wasi_subscription_t)) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_subscription_t>(), + 8usize, + concat!("Alignment of ", stringify!(__wasi_subscription_t)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_subscription_t>())).userdata as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t), + "::", + stringify!(userdata) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_subscription_t>())).type_ as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t), + "::", + stringify!(type_) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_subscription_t>())).u as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t), + "::", + stringify!(u) + ) + ); + } + }