Extract common interface from lucet-wasi

This commit is contained in:
Jakub Konka
2019-05-04 14:28:22 +02:00
commit cd39c1dfd8
13 changed files with 4574 additions and 0 deletions

257
src/ctx.rs Normal file
View File

@@ -0,0 +1,257 @@
use crate::fdentry::FdEntry;
use crate::host;
use crate::wasm32;
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 trait VmContext {
fn as_wasi_ctx(&self) -> &WasiCtx;
fn as_wasi_ctx_mut(&mut self) -> &mut WasiCtx;
unsafe fn dec_ptr(
&mut self,
ptr: wasm32::uintptr_t,
len: usize,
) -> Result<*mut u8, host::__wasi_errno_t>;
}
pub struct WasiCtxBuilder {
fds: HashMap<host::__wasi_fd_t, FdEntry>,
preopens: HashMap<PathBuf, File>,
args: Vec<CString>,
env: HashMap<CString, CString>,
}
impl WasiCtxBuilder {
/// Builder for a new `WasiCtx`.
pub fn new() -> Self {
let null = dev_null();
WasiCtxBuilder {
fds: HashMap::new(),
preopens: HashMap::new(),
args: vec![],
env: HashMap::new(),
}
.fd_dup(0, &null)
.fd_dup(1, &null)
.fd_dup(2, &null)
}
pub fn args(mut self, args: &[&str]) -> Self {
self.args = args
.into_iter()
.map(|arg| CString::new(*arg).expect("argument can be converted to a CString"))
.collect();
self
}
pub fn arg(mut self, arg: &str) -> Self {
self.args
.push(CString::new(arg).expect("argument can be converted to a CString"));
self
}
pub fn c_args<S: AsRef<CStr>>(mut self, args: &[S]) -> Self {
self.args = args
.into_iter()
.map(|arg| arg.as_ref().to_owned())
.collect();
self
}
pub fn c_arg<S: AsRef<CStr>>(mut self, arg: S) -> Self {
self.args.push(arg.as_ref().to_owned());
self
}
pub fn inherit_stdio(self) -> Self {
self.fd_dup(0, &stdin())
.fd_dup(1, &stdout())
.fd_dup(2, &stderr())
}
pub fn inherit_env(mut self) -> Self {
self.env = std::env::vars()
.map(|(k, v)| {
// TODO: handle errors, and possibly assert that the key is valid per POSIX
(
CString::new(k).expect("environment key can be converted to a CString"),
CString::new(v).expect("environment value can be converted to a CString"),
)
})
.collect();
self
}
pub fn env(mut self, k: &str, v: &str) -> Self {
self.env.insert(
// TODO: handle errors, and possibly assert that the key is valid per POSIX
CString::new(k).expect("environment key can be converted to a CString"),
CString::new(v).expect("environment value can be converted to a CString"),
);
self
}
pub fn c_env<S, T>(mut self, k: S, v: T) -> Self
where
S: AsRef<CStr>,
T: AsRef<CStr>,
{
self.env
.insert(k.as_ref().to_owned(), v.as_ref().to_owned());
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<F: IntoRawFd>(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<F: AsRawFd>(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<P: AsRef<Path>>(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<WasiCtx, Error> {
// 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;
}
let env = self
.env
.into_iter()
.map(|(k, v)| {
let mut pair = k.into_bytes();
pair.extend_from_slice(b"=");
pair.extend_from_slice(v.to_bytes_with_nul());
// constructing a new CString from existing CStrings is safe
unsafe { CString::from_vec_unchecked(pair) }
})
.collect();
Ok(WasiCtx {
fds: self.fds,
args: self.args,
env,
})
}
}
#[derive(Debug)]
pub struct WasiCtx {
pub fds: HashMap<host::__wasi_fd_t, FdEntry>,
pub args: Vec<CString>,
pub env: Vec<CString>,
}
impl WasiCtx {
/// Make a new `WasiCtx` with some default settings.
///
/// - File descriptors 0, 1, and 2 inherit stdin, stdout, and stderr from the host process.
///
/// - Environment variables are inherited from the host process.
///
/// To override these behaviors, use `WasiCtxBuilder`.
pub fn new(args: &[&str]) -> WasiCtx {
WasiCtxBuilder::new()
.args(args)
.inherit_stdio()
.inherit_env()
.build()
.expect("default options don't fail")
}
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.fds.get(&fd) {
// validate rights
if !fe.rights_base & rights_base != 0 || !fe.rights_inheriting & rights_inheriting != 0
{
Err(host::__WASI_ENOTCAPABLE as host::__wasi_errno_t)
} else {
Ok(fe)
}
} else {
Err(host::__WASI_EBADF as host::__wasi_errno_t)
}
}
pub fn insert_fd_entry(
&mut self,
fe: FdEntry,
) -> Result<host::__wasi_fd_t, host::__wasi_errno_t> {
// 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 as host::__wasi_errno_t);
}
}
self.fds.insert(fd, fe);
Ok(fd)
}
}
fn dev_null() -> File {
File::open("/dev/null").expect("failed to open /dev/null")
}

143
src/fdentry.rs Normal file
View File

@@ -0,0 +1,143 @@
use crate::host;
use std::fs::File;
use std::os::unix::prelude::{FileTypeExt, FromRawFd, IntoRawFd, RawFd};
use std::path::PathBuf;
#[derive(Clone, Debug)]
pub struct FdEntry {
pub fd_object: FdObject,
pub rights_base: host::__wasi_rights_t,
pub rights_inheriting: host::__wasi_rights_t,
pub preopen_path: Option<PathBuf>,
}
impl FdEntry {
pub fn from_file(file: File) -> FdEntry {
unsafe { FdEntry::from_raw_fd(file.into_raw_fd()) }
}
}
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 {
let (ty, mut rights_base, rights_inheriting) =
determine_type_rights(rawfd).expect("can determine file rights");
use nix::fcntl::{fcntl, OFlag, F_GETFL};
let flags_bits = fcntl(rawfd, F_GETFL).expect("fcntl succeeds");
let flags = OFlag::from_bits_truncate(flags_bits);
let accmode = flags & OFlag::O_ACCMODE;
if accmode == OFlag::O_RDONLY {
rights_base &= !host::__WASI_RIGHT_FD_WRITE as host::__wasi_rights_t;
} else if accmode == OFlag::O_WRONLY {
rights_base &= !host::__WASI_RIGHT_FD_READ as host::__wasi_rights_t;
}
FdEntry {
fd_object: FdObject {
ty: ty as u8,
rawfd,
needs_close: true,
},
rights_base,
rights_inheriting,
preopen_path: None,
}
}
}
// TODO: can probably make this safe by using fcntl directly rather than going through `File`
pub unsafe fn determine_type_rights(
rawfd: RawFd,
) -> Result<
(
host::__wasi_filetype_t,
host::__wasi_rights_t,
host::__wasi_rights_t,
),
host::__wasi_errno_t,
> {
let (ty, rights_base, rights_inheriting) = {
let file = File::from_raw_fd(rawfd);
let ft = file.metadata().unwrap().file_type();
// we just make a `File` here for convenience; we don't want it to close when it drops
std::mem::forget(file);
if ft.is_block_device() {
(
host::__WASI_FILETYPE_BLOCK_DEVICE,
host::RIGHTS_BLOCK_DEVICE_BASE,
host::RIGHTS_BLOCK_DEVICE_INHERITING,
)
} else if ft.is_char_device() {
if nix::unistd::isatty(rawfd).unwrap() {
(
host::__WASI_FILETYPE_CHARACTER_DEVICE,
host::RIGHTS_TTY_BASE,
host::RIGHTS_TTY_BASE,
)
} else {
(
host::__WASI_FILETYPE_CHARACTER_DEVICE,
host::RIGHTS_CHARACTER_DEVICE_BASE,
host::RIGHTS_CHARACTER_DEVICE_INHERITING,
)
}
} else if ft.is_dir() {
(
host::__WASI_FILETYPE_DIRECTORY,
host::RIGHTS_DIRECTORY_BASE,
host::RIGHTS_DIRECTORY_INHERITING,
)
} else if ft.is_file() {
(
host::__WASI_FILETYPE_REGULAR_FILE,
host::RIGHTS_REGULAR_FILE_BASE,
host::RIGHTS_REGULAR_FILE_INHERITING,
)
} else if ft.is_socket() {
use nix::sys::socket;
match socket::getsockopt(rawfd, socket::sockopt::SockType).unwrap() {
socket::SockType::Datagram => (
host::__WASI_FILETYPE_SOCKET_DGRAM,
host::RIGHTS_SOCKET_BASE,
host::RIGHTS_SOCKET_INHERITING,
),
socket::SockType::Stream => (
host::__WASI_FILETYPE_SOCKET_STREAM,
host::RIGHTS_SOCKET_BASE,
host::RIGHTS_SOCKET_INHERITING,
),
_ => return Err(host::__WASI_EINVAL as host::__wasi_errno_t),
}
} else if ft.is_fifo() {
(
host::__WASI_FILETYPE_SOCKET_STREAM,
host::RIGHTS_SOCKET_BASE,
host::RIGHTS_SOCKET_INHERITING,
)
} else {
return Err(host::__WASI_EINVAL as host::__wasi_errno_t);
}
};
Ok((
ty as host::__wasi_filetype_t,
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));
}
}
}

335
src/host.rs Normal file
View File

@@ -0,0 +1,335 @@
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/wasi_host.rs"));
pub type void = ::std::os::raw::c_void;
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 fn errno_from_nix(errno: nix::errno::Errno) -> __wasi_errno_t {
let e = 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,
};
e as __wasi_errno_t
}
#[cfg(target_os = "linux")]
const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_RSYNC;
#[cfg(not(target_os = "linux"))]
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 as __wasi_fdflags_t) != 0 {
nix_flags.insert(OFlag::O_APPEND);
}
if fdflags & (__WASI_FDFLAG_DSYNC as __wasi_fdflags_t) != 0 {
nix_flags.insert(OFlag::O_DSYNC);
}
if fdflags & (__WASI_FDFLAG_NONBLOCK as __wasi_fdflags_t) != 0 {
nix_flags.insert(OFlag::O_NONBLOCK);
}
if fdflags & (__WASI_FDFLAG_RSYNC as __wasi_fdflags_t) != 0 {
nix_flags.insert(O_RSYNC);
}
if fdflags & (__WASI_FDFLAG_SYNC as __wasi_fdflags_t) != 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 as __wasi_fdflags_t
}
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 as __wasi_oflags_t) != 0 {
nix_flags.insert(OFlag::O_CREAT);
}
if oflags & (__WASI_O_DIRECTORY as __wasi_oflags_t) != 0 {
nix_flags.insert(OFlag::O_DIRECTORY);
}
if oflags & (__WASI_O_EXCL as __wasi_oflags_t) != 0 {
nix_flags.insert(OFlag::O_EXCL);
}
if oflags & (__WASI_O_TRUNC as __wasi_oflags_t) != 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 as __wasi_filetype_t
} else if sflags.contains(SFlag::S_IFBLK) {
__WASI_FILETYPE_BLOCK_DEVICE as __wasi_filetype_t
} else if sflags.contains(SFlag::S_IFIFO) | sflags.contains(SFlag::S_IFSOCK) {
__WASI_FILETYPE_SOCKET_STREAM as __wasi_filetype_t
} else if sflags.contains(SFlag::S_IFDIR) {
__WASI_FILETYPE_DIRECTORY as __wasi_filetype_t
} else if sflags.contains(SFlag::S_IFREG) {
__WASI_FILETYPE_REGULAR_FILE as __wasi_filetype_t
} else if sflags.contains(SFlag::S_IFLNK) {
__WASI_FILETYPE_SYMBOLIC_LINK as __wasi_filetype_t
} else {
__WASI_FILETYPE_UNKNOWN as __wasi_filetype_t
}
}
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 as __wasi_filetype_t) != 0 {
nix_sflags.insert(SFlag::S_IFCHR);
}
if sflags & (__WASI_FILETYPE_BLOCK_DEVICE as __wasi_filetype_t) != 0 {
nix_sflags.insert(SFlag::S_IFBLK);
}
if sflags & (__WASI_FILETYPE_SOCKET_STREAM as __wasi_filetype_t) != 0 {
nix_sflags.insert(SFlag::S_IFIFO);
nix_sflags.insert(SFlag::S_IFSOCK);
}
if sflags & (__WASI_FILETYPE_DIRECTORY as __wasi_filetype_t) != 0 {
nix_sflags.insert(SFlag::S_IFDIR);
}
if sflags & (__WASI_FILETYPE_REGULAR_FILE as __wasi_filetype_t) != 0 {
nix_sflags.insert(SFlag::S_IFREG);
}
if sflags & (__WASI_FILETYPE_SYMBOLIC_LINK as __wasi_filetype_t) != 0 {
nix_sflags.insert(SFlag::S_IFLNK);
}
nix_sflags
}
pub fn filestat_from_nix(filestat: nix::sys::stat::FileStat) -> __wasi_filestat_t {
let filetype = nix::sys::stat::SFlag::from_bits_truncate(filestat.st_mode);
__wasi_filestat_t {
st_dev: filestat.st_dev as __wasi_device_t,
st_ino: filestat.st_ino as __wasi_inode_t,
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),
}
}
// Rights sets from wasmtime-wasi sandboxed system primitives. Transcribed because bindgen can't
// parse the #defines.
pub const RIGHTS_ALL: __wasi_rights_t = (__WASI_RIGHT_FD_DATASYNC
| __WASI_RIGHT_FD_READ
| __WASI_RIGHT_FD_SEEK
| __WASI_RIGHT_FD_FDSTAT_SET_FLAGS
| __WASI_RIGHT_FD_SYNC
| __WASI_RIGHT_FD_TELL
| __WASI_RIGHT_FD_WRITE
| __WASI_RIGHT_FD_ADVISE
| __WASI_RIGHT_FD_ALLOCATE
| __WASI_RIGHT_PATH_CREATE_DIRECTORY
| __WASI_RIGHT_PATH_CREATE_FILE
| __WASI_RIGHT_PATH_LINK_SOURCE
| __WASI_RIGHT_PATH_LINK_TARGET
| __WASI_RIGHT_PATH_OPEN
| __WASI_RIGHT_FD_READDIR
| __WASI_RIGHT_PATH_READLINK
| __WASI_RIGHT_PATH_RENAME_SOURCE
| __WASI_RIGHT_PATH_RENAME_TARGET
| __WASI_RIGHT_PATH_FILESTAT_GET
| __WASI_RIGHT_PATH_FILESTAT_SET_SIZE
| __WASI_RIGHT_PATH_FILESTAT_SET_TIMES
| __WASI_RIGHT_FD_FILESTAT_GET
| __WASI_RIGHT_FD_FILESTAT_SET_SIZE
| __WASI_RIGHT_FD_FILESTAT_SET_TIMES
| __WASI_RIGHT_PATH_SYMLINK
| __WASI_RIGHT_PATH_UNLINK_FILE
| __WASI_RIGHT_PATH_REMOVE_DIRECTORY
| __WASI_RIGHT_POLL_FD_READWRITE
| __WASI_RIGHT_SOCK_SHUTDOWN) as __wasi_rights_t;
// Block and character device interaction is outside the scope of
// CloudABI. Simply allow everything.
pub const RIGHTS_BLOCK_DEVICE_BASE: __wasi_rights_t = RIGHTS_ALL;
pub const RIGHTS_BLOCK_DEVICE_INHERITING: __wasi_rights_t = RIGHTS_ALL;
pub const RIGHTS_CHARACTER_DEVICE_BASE: __wasi_rights_t = RIGHTS_ALL;
pub const RIGHTS_CHARACTER_DEVICE_INHERITING: __wasi_rights_t = RIGHTS_ALL;
// Only allow directory operations on directories. Directories can only
// yield file descriptors to other directories and files.
pub const RIGHTS_DIRECTORY_BASE: __wasi_rights_t = (__WASI_RIGHT_FD_FDSTAT_SET_FLAGS
| __WASI_RIGHT_FD_SYNC
| __WASI_RIGHT_FD_ADVISE
| __WASI_RIGHT_PATH_CREATE_DIRECTORY
| __WASI_RIGHT_PATH_CREATE_FILE
| __WASI_RIGHT_PATH_LINK_SOURCE
| __WASI_RIGHT_PATH_LINK_TARGET
| __WASI_RIGHT_PATH_OPEN
| __WASI_RIGHT_FD_READDIR
| __WASI_RIGHT_PATH_READLINK
| __WASI_RIGHT_PATH_RENAME_SOURCE
| __WASI_RIGHT_PATH_RENAME_TARGET
| __WASI_RIGHT_PATH_FILESTAT_GET
| __WASI_RIGHT_PATH_FILESTAT_SET_SIZE
| __WASI_RIGHT_PATH_FILESTAT_SET_TIMES
| __WASI_RIGHT_FD_FILESTAT_GET
| __WASI_RIGHT_FD_FILESTAT_SET_SIZE
| __WASI_RIGHT_FD_FILESTAT_SET_TIMES
| __WASI_RIGHT_PATH_SYMLINK
| __WASI_RIGHT_PATH_UNLINK_FILE
| __WASI_RIGHT_PATH_REMOVE_DIRECTORY
| __WASI_RIGHT_POLL_FD_READWRITE)
as __wasi_rights_t;
pub const RIGHTS_DIRECTORY_INHERITING: __wasi_rights_t =
(RIGHTS_DIRECTORY_BASE | RIGHTS_REGULAR_FILE_BASE);
// Operations that apply to regular files.
pub const RIGHTS_REGULAR_FILE_BASE: __wasi_rights_t = (__WASI_RIGHT_FD_DATASYNC
| __WASI_RIGHT_FD_READ
| __WASI_RIGHT_FD_SEEK
| __WASI_RIGHT_FD_FDSTAT_SET_FLAGS
| __WASI_RIGHT_FD_SYNC
| __WASI_RIGHT_FD_TELL
| __WASI_RIGHT_FD_WRITE
| __WASI_RIGHT_FD_ADVISE
| __WASI_RIGHT_FD_ALLOCATE
| __WASI_RIGHT_FD_FILESTAT_GET
| __WASI_RIGHT_FD_FILESTAT_SET_SIZE
| __WASI_RIGHT_FD_FILESTAT_SET_TIMES
| __WASI_RIGHT_POLL_FD_READWRITE)
as __wasi_rights_t;
pub const RIGHTS_REGULAR_FILE_INHERITING: __wasi_rights_t = 0;
// Operations that apply to shared memory objects.
pub const RIGHTS_SHARED_MEMORY_BASE: __wasi_rights_t = (__WASI_RIGHT_FD_READ
| __WASI_RIGHT_FD_WRITE
| __WASI_RIGHT_FD_FILESTAT_GET
| __WASI_RIGHT_FD_FILESTAT_SET_SIZE)
as __wasi_rights_t;
pub const RIGHTS_SHARED_MEMORY_INHERITING: __wasi_rights_t = 0;
// Operations that apply to sockets and socket pairs.
pub const RIGHTS_SOCKET_BASE: __wasi_rights_t = (__WASI_RIGHT_FD_READ
| __WASI_RIGHT_FD_FDSTAT_SET_FLAGS
| __WASI_RIGHT_FD_WRITE
| __WASI_RIGHT_FD_FILESTAT_GET
| __WASI_RIGHT_POLL_FD_READWRITE
| __WASI_RIGHT_SOCK_SHUTDOWN)
as __wasi_rights_t;
pub const RIGHTS_SOCKET_INHERITING: __wasi_rights_t = RIGHTS_ALL;
// Operations that apply to TTYs.
pub const RIGHTS_TTY_BASE: __wasi_rights_t = (__WASI_RIGHT_FD_READ
| __WASI_RIGHT_FD_FDSTAT_SET_FLAGS
| __WASI_RIGHT_FD_WRITE
| __WASI_RIGHT_FD_FILESTAT_GET
| __WASI_RIGHT_POLL_FD_READWRITE)
as __wasi_rights_t;
pub const RIGHTS_TTY_INHERITING: __wasi_rights_t = 0;

1360
src/hostcalls.rs Normal file

File diff suppressed because it is too large Load Diff

8
src/lib.rs Normal file
View File

@@ -0,0 +1,8 @@
pub mod ctx;
pub mod fdentry;
pub mod host;
pub mod hostcalls;
pub mod memory;
pub mod wasm32;
pub use ctx::{WasiCtx, WasiCtxBuilder};

479
src/memory.rs Normal file
View File

@@ -0,0 +1,479 @@
//! 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::ctx::VmContext;
use crate::{host, wasm32};
use cast;
use cast::From as _0;
use std::mem::{align_of, size_of};
use std::slice;
macro_rules! bail_errno {
( $errno:ident ) => {
return Err(host::$errno as host::__wasi_errno_t);
};
}
pub unsafe fn dec_ptr_to<T>(
vmctx: *mut VmContext,
ptr: wasm32::uintptr_t,
) -> Result<*mut T, host::__wasi_errno_t> {
// check that the ptr is aligned
if ptr as usize % align_of::<T>() != 0 {
bail_errno!(__WASI_EINVAL);
}
(*vmctx).dec_ptr(ptr, size_of::<T>()).map(|p| p as *mut T)
}
pub unsafe fn dec_pointee<T>(
vmctx: *mut VmContext,
ptr: wasm32::uintptr_t,
) -> Result<T, host::__wasi_errno_t> {
dec_ptr_to::<T>(vmctx, ptr).map(|p| p.read())
}
pub unsafe fn enc_pointee<T>(
vmctx: *mut VmContext,
ptr: wasm32::uintptr_t,
t: T,
) -> Result<(), host::__wasi_errno_t> {
dec_ptr_to::<T>(vmctx, ptr).map(|p| p.write(t))
}
pub unsafe fn dec_slice_of<T>(
vmctx: *mut VmContext,
ptr: wasm32::uintptr_t,
len: wasm32::size_t,
) -> Result<(*mut T, usize), host::__wasi_errno_t> {
// check alignment, and that length doesn't overflow
if ptr as usize % align_of::<T>() != 0 {
return Err(host::__WASI_EINVAL as host::__wasi_errno_t);
}
let len = dec_usize(len);
let len_bytes = if let Some(len) = size_of::<T>().checked_mul(len) {
len
} else {
return Err(host::__WASI_EOVERFLOW as host::__wasi_errno_t);
};
let ptr = (*vmctx).dec_ptr(ptr, len_bytes)? as *mut T;
Ok((ptr, len))
}
pub unsafe fn enc_slice_of<T>(
vmctx: *mut VmContext,
slice: &[T],
ptr: wasm32::uintptr_t,
) -> Result<(), host::__wasi_errno_t> {
// check alignment
if ptr as usize % align_of::<T>() != 0 {
return Err(host::__WASI_EINVAL as host::__wasi_errno_t);
}
// check that length doesn't overflow
let len_bytes = if let Some(len) = size_of::<T>().checked_mul(slice.len()) {
len
} else {
return Err(host::__WASI_EOVERFLOW as host::__wasi_errno_t);
};
// get the pointer into guest memory, and copy the bytes
let ptr = (*vmctx).dec_ptr(ptr, len_bytes)? as *mut libc::c_void;
libc::memcpy(ptr, slice.as_ptr() as *const libc::c_void, len_bytes);
Ok(())
}
macro_rules! dec_enc_scalar {
( $ty:ident, $dec:ident, $dec_byref:ident, $enc:ident, $enc_byref:ident) => {
pub fn $dec(x: wasm32::$ty) -> host::$ty {
host::$ty::from_le(x)
}
pub unsafe fn $dec_byref(
vmctx: *mut VmContext,
ptr: wasm32::uintptr_t,
) -> Result<host::$ty, host::__wasi_errno_t> {
dec_pointee::<wasm32::$ty>(vmctx, ptr).map($dec)
}
pub fn $enc(x: host::$ty) -> wasm32::$ty {
x.to_le()
}
pub unsafe fn $enc_byref(
vmctx: *mut VmContext,
ptr: wasm32::uintptr_t,
x: host::$ty,
) -> Result<(), host::__wasi_errno_t> {
enc_pointee::<wasm32::$ty>(vmctx, ptr, $enc(x))
}
};
}
pub unsafe fn dec_ciovec(
vmctx: *mut VmContext,
ciovec: &wasm32::__wasi_ciovec_t,
) -> Result<host::__wasi_ciovec_t, host::__wasi_errno_t> {
let len = dec_usize(ciovec.buf_len);
Ok(host::__wasi_ciovec_t {
buf: (*vmctx).dec_ptr(ciovec.buf, len)? as *const host::void,
buf_len: len,
})
}
pub unsafe fn dec_ciovec_slice(
vmctx: *mut VmContext,
ptr: wasm32::uintptr_t,
len: wasm32::size_t,
) -> Result<Vec<host::__wasi_ciovec_t>, host::__wasi_errno_t> {
let slice = dec_slice_of::<wasm32::__wasi_ciovec_t>(vmctx, ptr, len)?;
let slice = slice::from_raw_parts(slice.0, slice.1);
slice.iter().map(|iov| dec_ciovec(vmctx, iov)).collect()
}
dec_enc_scalar!(
__wasi_clockid_t,
dec_clockid,
dec_clockid_byref,
enc_clockid,
enc_clockid_byref
);
dec_enc_scalar!(
__wasi_errno_t,
dec_errno,
dec_errno_byref,
enc_errno,
enc_errno_byref
);
dec_enc_scalar!(
__wasi_exitcode_t,
dec_exitcode,
dec_exitcode_byref,
enc_exitcode,
enc_exitcode_byref
);
dec_enc_scalar!(__wasi_fd_t, dec_fd, dec_fd_byref, enc_fd, enc_fd_byref);
dec_enc_scalar!(
__wasi_fdflags_t,
dec_fdflags,
dec_fdflags_byref,
enc_fdflags,
enc_fdflags_byref
);
dec_enc_scalar!(
__wasi_device_t,
dec_device,
dev_device_byref,
enc_device,
enc_device_byref
);
dec_enc_scalar!(
__wasi_inode_t,
dec_inode,
dev_inode_byref,
enc_inode,
enc_inode_byref
);
dec_enc_scalar!(
__wasi_linkcount_t,
dec_linkcount,
dev_linkcount_byref,
enc_linkcount,
enc_linkcount_byref
);
pub fn dec_filestat(filestat: wasm32::__wasi_filestat_t) -> host::__wasi_filestat_t {
host::__wasi_filestat_t {
st_dev: dec_device(filestat.st_dev),
st_ino: dec_inode(filestat.st_ino),
st_filetype: dec_filetype(filestat.st_filetype),
st_nlink: dec_linkcount(filestat.st_nlink),
st_size: dec_filesize(filestat.st_size),
st_atim: dec_timestamp(filestat.st_atim),
st_mtim: dec_timestamp(filestat.st_mtim),
st_ctim: dec_timestamp(filestat.st_ctim),
}
}
pub unsafe fn dec_filestat_byref(
vmctx: *mut VmContext,
filestat_ptr: wasm32::uintptr_t,
) -> Result<host::__wasi_filestat_t, host::__wasi_errno_t> {
dec_pointee::<wasm32::__wasi_filestat_t>(vmctx, filestat_ptr).map(dec_filestat)
}
pub fn enc_filestat(filestat: host::__wasi_filestat_t) -> wasm32::__wasi_filestat_t {
wasm32::__wasi_filestat_t {
st_dev: enc_device(filestat.st_dev),
st_ino: enc_inode(filestat.st_ino),
st_filetype: enc_filetype(filestat.st_filetype),
st_nlink: enc_linkcount(filestat.st_nlink),
st_size: enc_filesize(filestat.st_size),
st_atim: enc_timestamp(filestat.st_atim),
st_mtim: enc_timestamp(filestat.st_mtim),
st_ctim: enc_timestamp(filestat.st_ctim),
}
}
pub unsafe fn enc_filestat_byref(
vmctx: *mut VmContext,
filestat_ptr: wasm32::uintptr_t,
host_filestat: host::__wasi_filestat_t,
) -> Result<(), host::__wasi_errno_t> {
let filestat = enc_filestat(host_filestat);
enc_pointee::<wasm32::__wasi_filestat_t>(vmctx, filestat_ptr, filestat)
}
pub fn dec_fdstat(fdstat: wasm32::__wasi_fdstat_t) -> host::__wasi_fdstat_t {
host::__wasi_fdstat_t {
fs_filetype: dec_filetype(fdstat.fs_filetype),
fs_flags: dec_fdflags(fdstat.fs_flags),
fs_rights_base: dec_rights(fdstat.fs_rights_base),
fs_rights_inheriting: dec_rights(fdstat.fs_rights_inheriting),
}
}
pub unsafe fn dec_fdstat_byref(
vmctx: *mut VmContext,
fdstat_ptr: wasm32::uintptr_t,
) -> Result<host::__wasi_fdstat_t, host::__wasi_errno_t> {
dec_pointee::<wasm32::__wasi_fdstat_t>(vmctx, fdstat_ptr).map(dec_fdstat)
}
pub fn enc_fdstat(fdstat: host::__wasi_fdstat_t) -> wasm32::__wasi_fdstat_t {
wasm32::__wasi_fdstat_t {
fs_filetype: enc_filetype(fdstat.fs_filetype),
fs_flags: enc_fdflags(fdstat.fs_flags),
__bindgen_padding_0: 0,
fs_rights_base: enc_rights(fdstat.fs_rights_base),
fs_rights_inheriting: enc_rights(fdstat.fs_rights_inheriting),
}
}
pub unsafe fn enc_fdstat_byref(
vmctx: *mut VmContext,
fdstat_ptr: wasm32::uintptr_t,
host_fdstat: host::__wasi_fdstat_t,
) -> Result<(), host::__wasi_errno_t> {
let fdstat = enc_fdstat(host_fdstat);
enc_pointee::<wasm32::__wasi_fdstat_t>(vmctx, fdstat_ptr, fdstat)
}
dec_enc_scalar!(
__wasi_filedelta_t,
dec_filedelta,
dec_filedelta_byref,
enc_filedelta,
enc_filedelta_byref
);
dec_enc_scalar!(
__wasi_filesize_t,
dec_filesize,
dec_filesize_byref,
enc_filesize,
enc_filesize_byref
);
dec_enc_scalar!(
__wasi_filetype_t,
dec_filetype,
dec_filetype_byref,
enc_filetype,
enc_filetype_byref
);
dec_enc_scalar!(
__wasi_lookupflags_t,
dec_lookupflags,
dec_lookupflags_byref,
enc_lookupflags,
enc_lookupflags_byref
);
dec_enc_scalar!(
__wasi_oflags_t,
dec_oflags,
dec_oflags_byref,
enc_oflags,
enc_oflags_byref
);
pub fn dec_prestat(
prestat: wasm32::__wasi_prestat_t,
) -> Result<host::__wasi_prestat_t, host::__wasi_errno_t> {
match prestat.pr_type {
wasm32::__WASI_PREOPENTYPE_DIR => {
let u = host::__wasi_prestat_t___wasi_prestat_u {
dir: host::__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t {
pr_name_len: dec_usize(unsafe { prestat.u.dir.pr_name_len }),
},
};
Ok(host::__wasi_prestat_t {
pr_type: host::__WASI_PREOPENTYPE_DIR as host::__wasi_preopentype_t,
u,
})
}
_ => Err(host::__WASI_EINVAL as host::__wasi_errno_t),
}
}
pub unsafe fn dec_prestat_byref(
vmctx: *mut VmContext,
prestat_ptr: wasm32::uintptr_t,
) -> Result<host::__wasi_prestat_t, host::__wasi_errno_t> {
dec_pointee::<wasm32::__wasi_prestat_t>(vmctx, prestat_ptr).and_then(dec_prestat)
}
pub fn enc_prestat(
prestat: host::__wasi_prestat_t,
) -> Result<wasm32::__wasi_prestat_t, host::__wasi_errno_t> {
match prestat.pr_type as u32 {
host::__WASI_PREOPENTYPE_DIR => {
let u = wasm32::__wasi_prestat_t___wasi_prestat_u {
dir: wasm32::__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t {
pr_name_len: enc_usize(unsafe { prestat.u.dir.pr_name_len }),
},
};
Ok(wasm32::__wasi_prestat_t {
pr_type: wasm32::__WASI_PREOPENTYPE_DIR as wasm32::__wasi_preopentype_t,
u,
})
}
_ => Err(host::__WASI_EINVAL as host::__wasi_errno_t),
}
}
pub unsafe fn enc_prestat_byref(
vmctx: *mut VmContext,
prestat_ptr: wasm32::uintptr_t,
host_prestat: host::__wasi_prestat_t,
) -> Result<(), host::__wasi_errno_t> {
let prestat = enc_prestat(host_prestat)?;
enc_pointee::<wasm32::__wasi_prestat_t>(vmctx, prestat_ptr, prestat)
}
dec_enc_scalar!(
__wasi_rights_t,
dec_rights,
dec_rights_byref,
enc_rights,
enc_rights_byref
);
dec_enc_scalar!(
__wasi_timestamp_t,
dec_timestamp,
dec_timestamp_byref,
enc_timestamp,
enc_timestamp_byref
);
pub fn dec_usize(size: wasm32::size_t) -> usize {
cast::usize(u32::from_le(size))
}
pub fn enc_usize(size: usize) -> wasm32::size_t {
wasm32::size_t::cast(size).unwrap()
}
pub unsafe fn enc_usize_byref(
vmctx: *mut VmContext,
usize_ptr: wasm32::uintptr_t,
host_usize: usize,
) -> Result<(), host::__wasi_errno_t> {
enc_pointee::<wasm32::size_t>(vmctx, usize_ptr, enc_usize(host_usize))
}
dec_enc_scalar!(
__wasi_whence_t,
dec_whence,
dec_whence_byref,
enc_whence,
enc_whence_byref
);
dec_enc_scalar!(
__wasi_subclockflags_t,
dec_subclockflags,
dec_subclockflags_byref,
enc_subclockflags,
enc_subclockflags_byref
);
dec_enc_scalar!(
__wasi_eventrwflags_t,
dec_eventrwflags,
dec_eventrwflags_byref,
enc_eventrwflags,
enc_eventrwflags_byref
);
dec_enc_scalar!(
__wasi_eventtype_t,
dec_eventtype,
dec_eventtype_byref,
enc_eventtype,
enc_eventtype_byref
);
dec_enc_scalar!(
__wasi_userdata_t,
dec_userdata,
dec_userdata_byref,
enc_userdata,
enc_userdata_byref
);
pub fn dec_subscription(
subscription: &wasm32::__wasi_subscription_t,
) -> Result<host::__wasi_subscription_t, host::__wasi_errno_t> {
let userdata = dec_userdata(subscription.userdata);
let type_ = dec_eventtype(subscription.type_);
let u_orig = subscription.__bindgen_anon_1;
let u = match type_ {
wasm32::__WASI_EVENTTYPE_CLOCK => host::__wasi_subscription_t___wasi_subscription_u {
clock: unsafe {
host::__wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t {
identifier: dec_userdata(u_orig.clock.identifier),
clock_id: dec_clockid(u_orig.clock.clock_id),
timeout: dec_timestamp(u_orig.clock.timeout),
precision: dec_timestamp(u_orig.clock.precision),
flags: dec_subclockflags(u_orig.clock.flags),
}
},
},
wasm32::__WASI_EVENTTYPE_FD_READ | wasm32::__WASI_EVENTTYPE_FD_WRITE => host::__wasi_subscription_t___wasi_subscription_u {
fd_readwrite: host::__wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_fd_readwrite_t {
fd: dec_fd(unsafe{u_orig.fd_readwrite.fd})
}
},
_ => return Err(wasm32::__WASI_EINVAL)
};
Ok(host::__wasi_subscription_t { userdata, type_, u })
}
pub fn enc_event(event: host::__wasi_event_t) -> wasm32::__wasi_event_t {
let fd_readwrite = unsafe { event.u.fd_readwrite };
wasm32::__wasi_event_t {
userdata: enc_userdata(event.userdata),
type_: enc_eventtype(event.type_),
error: enc_errno(event.error),
__bindgen_anon_1: wasm32::__wasi_event_t__bindgen_ty_1 {
fd_readwrite: wasm32::__wasi_event_t__bindgen_ty_1__bindgen_ty_1 {
nbytes: enc_filesize(fd_readwrite.nbytes),
flags: enc_eventrwflags(fd_readwrite.flags),
__bindgen_padding_0: [0; 3],
},
},
__bindgen_padding_0: 0,
}
}

1367
src/wasm32.rs Normal file

File diff suppressed because it is too large Load Diff