Add support for wasi_snapshot_preview1. (#592)

* Add support for wasi_snapshot_preview1.

This adds support for the new ABI, while preserving compatibility
support for the old ABI.

* Fix compilation on platforms where nlink_t isn't 64-bit.

* rustfmt

* Fix Windows build errors.
This commit is contained in:
Dan Gohman
2019-11-18 22:07:16 -08:00
committed by GitHub
parent 39e57e3e9a
commit d645902620
83 changed files with 9928 additions and 803 deletions

View File

@@ -9,83 +9,83 @@ use thiserror::Error;
#[derive(Clone, Copy, Debug, Error, Eq, PartialEq)]
#[repr(u16)]
pub enum WasiError {
ESUCCESS = wasi::__WASI_ESUCCESS,
E2BIG = wasi::__WASI_E2BIG,
EACCES = wasi::__WASI_EACCES,
EADDRINUSE = wasi::__WASI_EADDRINUSE,
EADDRNOTAVAIL = wasi::__WASI_EADDRNOTAVAIL,
EAFNOSUPPORT = wasi::__WASI_EAFNOSUPPORT,
EAGAIN = wasi::__WASI_EAGAIN,
EALREADY = wasi::__WASI_EALREADY,
EBADF = wasi::__WASI_EBADF,
EBADMSG = wasi::__WASI_EBADMSG,
EBUSY = wasi::__WASI_EBUSY,
ECANCELED = wasi::__WASI_ECANCELED,
ECHILD = wasi::__WASI_ECHILD,
ECONNABORTED = wasi::__WASI_ECONNABORTED,
ECONNREFUSED = wasi::__WASI_ECONNREFUSED,
ECONNRESET = wasi::__WASI_ECONNRESET,
EDEADLK = wasi::__WASI_EDEADLK,
EDESTADDRREQ = wasi::__WASI_EDESTADDRREQ,
EDOM = wasi::__WASI_EDOM,
EDQUOT = wasi::__WASI_EDQUOT,
EEXIST = wasi::__WASI_EEXIST,
EFAULT = wasi::__WASI_EFAULT,
EFBIG = wasi::__WASI_EFBIG,
EHOSTUNREACH = wasi::__WASI_EHOSTUNREACH,
EIDRM = wasi::__WASI_EIDRM,
EILSEQ = wasi::__WASI_EILSEQ,
EINPROGRESS = wasi::__WASI_EINPROGRESS,
EINTR = wasi::__WASI_EINTR,
EINVAL = wasi::__WASI_EINVAL,
EIO = wasi::__WASI_EIO,
EISCONN = wasi::__WASI_EISCONN,
EISDIR = wasi::__WASI_EISDIR,
ELOOP = wasi::__WASI_ELOOP,
EMFILE = wasi::__WASI_EMFILE,
EMLINK = wasi::__WASI_EMLINK,
EMSGSIZE = wasi::__WASI_EMSGSIZE,
EMULTIHOP = wasi::__WASI_EMULTIHOP,
ENAMETOOLONG = wasi::__WASI_ENAMETOOLONG,
ENETDOWN = wasi::__WASI_ENETDOWN,
ENETRESET = wasi::__WASI_ENETRESET,
ENETUNREACH = wasi::__WASI_ENETUNREACH,
ENFILE = wasi::__WASI_ENFILE,
ENOBUFS = wasi::__WASI_ENOBUFS,
ENODEV = wasi::__WASI_ENODEV,
ENOENT = wasi::__WASI_ENOENT,
ENOEXEC = wasi::__WASI_ENOEXEC,
ENOLCK = wasi::__WASI_ENOLCK,
ENOLINK = wasi::__WASI_ENOLINK,
ENOMEM = wasi::__WASI_ENOMEM,
ENOMSG = wasi::__WASI_ENOMSG,
ENOPROTOOPT = wasi::__WASI_ENOPROTOOPT,
ENOSPC = wasi::__WASI_ENOSPC,
ENOSYS = wasi::__WASI_ENOSYS,
ENOTCONN = wasi::__WASI_ENOTCONN,
ENOTDIR = wasi::__WASI_ENOTDIR,
ENOTEMPTY = wasi::__WASI_ENOTEMPTY,
ENOTRECOVERABLE = wasi::__WASI_ENOTRECOVERABLE,
ENOTSOCK = wasi::__WASI_ENOTSOCK,
ENOTSUP = wasi::__WASI_ENOTSUP,
ENOTTY = wasi::__WASI_ENOTTY,
ENXIO = wasi::__WASI_ENXIO,
EOVERFLOW = wasi::__WASI_EOVERFLOW,
EOWNERDEAD = wasi::__WASI_EOWNERDEAD,
EPERM = wasi::__WASI_EPERM,
EPIPE = wasi::__WASI_EPIPE,
EPROTO = wasi::__WASI_EPROTO,
EPROTONOSUPPORT = wasi::__WASI_EPROTONOSUPPORT,
EPROTOTYPE = wasi::__WASI_EPROTOTYPE,
ERANGE = wasi::__WASI_ERANGE,
EROFS = wasi::__WASI_EROFS,
ESPIPE = wasi::__WASI_ESPIPE,
ESRCH = wasi::__WASI_ESRCH,
ESTALE = wasi::__WASI_ESTALE,
ETIMEDOUT = wasi::__WASI_ETIMEDOUT,
ETXTBSY = wasi::__WASI_ETXTBSY,
EXDEV = wasi::__WASI_EXDEV,
ENOTCAPABLE = wasi::__WASI_ENOTCAPABLE,
ESUCCESS = wasi::__WASI_ERRNO_SUCCESS,
E2BIG = wasi::__WASI_ERRNO_2BIG,
EACCES = wasi::__WASI_ERRNO_ACCES,
EADDRINUSE = wasi::__WASI_ERRNO_ADDRINUSE,
EADDRNOTAVAIL = wasi::__WASI_ERRNO_ADDRNOTAVAIL,
EAFNOSUPPORT = wasi::__WASI_ERRNO_AFNOSUPPORT,
EAGAIN = wasi::__WASI_ERRNO_AGAIN,
EALREADY = wasi::__WASI_ERRNO_ALREADY,
EBADF = wasi::__WASI_ERRNO_BADF,
EBADMSG = wasi::__WASI_ERRNO_BADMSG,
EBUSY = wasi::__WASI_ERRNO_BUSY,
ECANCELED = wasi::__WASI_ERRNO_CANCELED,
ECHILD = wasi::__WASI_ERRNO_CHILD,
ECONNABORTED = wasi::__WASI_ERRNO_CONNABORTED,
ECONNREFUSED = wasi::__WASI_ERRNO_CONNREFUSED,
ECONNRESET = wasi::__WASI_ERRNO_CONNRESET,
EDEADLK = wasi::__WASI_ERRNO_DEADLK,
EDESTADDRREQ = wasi::__WASI_ERRNO_DESTADDRREQ,
EDOM = wasi::__WASI_ERRNO_DOM,
EDQUOT = wasi::__WASI_ERRNO_DQUOT,
EEXIST = wasi::__WASI_ERRNO_EXIST,
EFAULT = wasi::__WASI_ERRNO_FAULT,
EFBIG = wasi::__WASI_ERRNO_FBIG,
EHOSTUNREACH = wasi::__WASI_ERRNO_HOSTUNREACH,
EIDRM = wasi::__WASI_ERRNO_IDRM,
EILSEQ = wasi::__WASI_ERRNO_ILSEQ,
EINPROGRESS = wasi::__WASI_ERRNO_INPROGRESS,
EINTR = wasi::__WASI_ERRNO_INTR,
EINVAL = wasi::__WASI_ERRNO_INVAL,
EIO = wasi::__WASI_ERRNO_IO,
EISCONN = wasi::__WASI_ERRNO_ISCONN,
EISDIR = wasi::__WASI_ERRNO_ISDIR,
ELOOP = wasi::__WASI_ERRNO_LOOP,
EMFILE = wasi::__WASI_ERRNO_MFILE,
EMLINK = wasi::__WASI_ERRNO_MLINK,
EMSGSIZE = wasi::__WASI_ERRNO_MSGSIZE,
EMULTIHOP = wasi::__WASI_ERRNO_MULTIHOP,
ENAMETOOLONG = wasi::__WASI_ERRNO_NAMETOOLONG,
ENETDOWN = wasi::__WASI_ERRNO_NETDOWN,
ENETRESET = wasi::__WASI_ERRNO_NETRESET,
ENETUNREACH = wasi::__WASI_ERRNO_NETUNREACH,
ENFILE = wasi::__WASI_ERRNO_NFILE,
ENOBUFS = wasi::__WASI_ERRNO_NOBUFS,
ENODEV = wasi::__WASI_ERRNO_NODEV,
ENOENT = wasi::__WASI_ERRNO_NOENT,
ENOEXEC = wasi::__WASI_ERRNO_NOEXEC,
ENOLCK = wasi::__WASI_ERRNO_NOLCK,
ENOLINK = wasi::__WASI_ERRNO_NOLINK,
ENOMEM = wasi::__WASI_ERRNO_NOMEM,
ENOMSG = wasi::__WASI_ERRNO_NOMSG,
ENOPROTOOPT = wasi::__WASI_ERRNO_NOPROTOOPT,
ENOSPC = wasi::__WASI_ERRNO_NOSPC,
ENOSYS = wasi::__WASI_ERRNO_NOSYS,
ENOTCONN = wasi::__WASI_ERRNO_NOTCONN,
ENOTDIR = wasi::__WASI_ERRNO_NOTDIR,
ENOTEMPTY = wasi::__WASI_ERRNO_NOTEMPTY,
ENOTRECOVERABLE = wasi::__WASI_ERRNO_NOTRECOVERABLE,
ENOTSOCK = wasi::__WASI_ERRNO_NOTSOCK,
ENOTSUP = wasi::__WASI_ERRNO_NOTSUP,
ENOTTY = wasi::__WASI_ERRNO_NOTTY,
ENXIO = wasi::__WASI_ERRNO_NXIO,
EOVERFLOW = wasi::__WASI_ERRNO_OVERFLOW,
EOWNERDEAD = wasi::__WASI_ERRNO_OWNERDEAD,
EPERM = wasi::__WASI_ERRNO_PERM,
EPIPE = wasi::__WASI_ERRNO_PIPE,
EPROTO = wasi::__WASI_ERRNO_PROTO,
EPROTONOSUPPORT = wasi::__WASI_ERRNO_PROTONOSUPPORT,
EPROTOTYPE = wasi::__WASI_ERRNO_PROTOTYPE,
ERANGE = wasi::__WASI_ERRNO_RANGE,
EROFS = wasi::__WASI_ERRNO_ROFS,
ESPIPE = wasi::__WASI_ERRNO_SPIPE,
ESRCH = wasi::__WASI_ERRNO_SRCH,
ESTALE = wasi::__WASI_ERRNO_STALE,
ETIMEDOUT = wasi::__WASI_ERRNO_TIMEDOUT,
ETXTBSY = wasi::__WASI_ERRNO_TXTBSY,
EXDEV = wasi::__WASI_ERRNO_XDEV,
ENOTCAPABLE = wasi::__WASI_ERRNO_NOTCAPABLE,
}
impl WasiError {
@@ -273,7 +273,7 @@ fn errno_from_ioerror(e: &std::io::Error) -> wasi::__wasi_errno_t {
Some(code) => crate::sys::errno_from_host(code),
None => {
log::debug!("Inconvertible OS error: {}", e);
wasi::__WASI_EIO
wasi::__WASI_ERRNO_IO
}
}
}

View File

@@ -52,7 +52,7 @@ impl<'ctx> Dir<'ctx> {
wasi_errno_to_io_error(hostcalls::path_open(
self.ctx,
self.fd,
wasi::__WASI_LOOKUP_SYMLINK_FOLLOW,
wasi::__WASI_LOOKUPFLAGS_SYMLINK_FOLLOW,
path.as_os_str().as_bytes(),
path.as_os_str().len(),
0,
@@ -98,9 +98,9 @@ impl<'ctx> Dir<'ctx> {
wasi_errno_to_io_error(hostcalls::path_open(
self.ctx,
self.fd,
wasi::__WASI_LOOKUP_SYMLINK_FOLLOW,
wasi::__WASI_LOOKUPFLAGS_SYMLINK_FOLLOW,
path.as_os_str().as_bytes(),
wasi::__WASI_O_DIRECTORY,
wasi::__WASI_OFLAGS_DIRECTORY,
!0,
!0,
0,
@@ -132,10 +132,10 @@ impl<'ctx> Dir<'ctx> {
wasi_errno_to_io_error(hostcalls::path_open(
self.ctx,
self.fd,
wasi::__WASI_LOOKUP_SYMLINK_FOLLOW,
wasi::__WASI_LOOKUPFLAGS_SYMLINK_FOLLOW,
path.as_os_str().as_bytes(),
path.as_os_str().len(),
wasi::__WASI_O_CREAT | wasi::__WASI_O_TRUNC,
wasi::__WASI_OFLAGS_CREAT | wasi::__WASI_OFLAGS_TRUNC,
!0,
!0,
0,

View File

@@ -9,86 +9,86 @@ use std::io;
pub(crate) fn wasi_errno_to_io_error(errno: wasi::__wasi_errno_t) -> io::Result<()> {
#[cfg(unix)]
let raw_os_error = match errno {
wasi::__WASI_ESUCCESS => return Ok(()),
wasi::__WASI_EIO => libc::EIO,
wasi::__WASI_EPERM => libc::EPERM,
wasi::__WASI_EINVAL => libc::EINVAL,
wasi::__WASI_EPIPE => libc::EPIPE,
wasi::__WASI_ENOTCONN => libc::ENOTCONN,
wasi::__WASI_E2BIG => libc::E2BIG,
wasi::__WASI_EACCES => libc::EACCES,
wasi::__WASI_EADDRINUSE => libc::EADDRINUSE,
wasi::__WASI_EADDRNOTAVAIL => libc::EADDRNOTAVAIL,
wasi::__WASI_EAFNOSUPPORT => libc::EAFNOSUPPORT,
wasi::__WASI_EAGAIN => libc::EAGAIN,
wasi::__WASI_EALREADY => libc::EALREADY,
wasi::__WASI_EBADF => libc::EBADF,
wasi::__WASI_EBADMSG => libc::EBADMSG,
wasi::__WASI_EBUSY => libc::EBUSY,
wasi::__WASI_ECANCELED => libc::ECANCELED,
wasi::__WASI_ECHILD => libc::ECHILD,
wasi::__WASI_ECONNABORTED => libc::ECONNABORTED,
wasi::__WASI_ECONNREFUSED => libc::ECONNREFUSED,
wasi::__WASI_ECONNRESET => libc::ECONNRESET,
wasi::__WASI_EDEADLK => libc::EDEADLK,
wasi::__WASI_EDESTADDRREQ => libc::EDESTADDRREQ,
wasi::__WASI_EDOM => libc::EDOM,
wasi::__WASI_EDQUOT => libc::EDQUOT,
wasi::__WASI_EEXIST => libc::EEXIST,
wasi::__WASI_EFAULT => libc::EFAULT,
wasi::__WASI_EFBIG => libc::EFBIG,
wasi::__WASI_EHOSTUNREACH => libc::EHOSTUNREACH,
wasi::__WASI_EIDRM => libc::EIDRM,
wasi::__WASI_EILSEQ => libc::EILSEQ,
wasi::__WASI_EINPROGRESS => libc::EINPROGRESS,
wasi::__WASI_EINTR => libc::EINTR,
wasi::__WASI_EISCONN => libc::EISCONN,
wasi::__WASI_EISDIR => libc::EISDIR,
wasi::__WASI_ELOOP => libc::ELOOP,
wasi::__WASI_EMFILE => libc::EMFILE,
wasi::__WASI_EMLINK => libc::EMLINK,
wasi::__WASI_EMSGSIZE => libc::EMSGSIZE,
wasi::__WASI_EMULTIHOP => libc::EMULTIHOP,
wasi::__WASI_ENAMETOOLONG => libc::ENAMETOOLONG,
wasi::__WASI_ENETDOWN => libc::ENETDOWN,
wasi::__WASI_ENETRESET => libc::ENETRESET,
wasi::__WASI_ENETUNREACH => libc::ENETUNREACH,
wasi::__WASI_ENFILE => libc::ENFILE,
wasi::__WASI_ENOBUFS => libc::ENOBUFS,
wasi::__WASI_ENODEV => libc::ENODEV,
wasi::__WASI_ENOENT => libc::ENOENT,
wasi::__WASI_ENOEXEC => libc::ENOEXEC,
wasi::__WASI_ENOLCK => libc::ENOLCK,
wasi::__WASI_ENOLINK => libc::ENOLINK,
wasi::__WASI_ENOMEM => libc::ENOMEM,
wasi::__WASI_ENOMSG => libc::ENOMSG,
wasi::__WASI_ENOPROTOOPT => libc::ENOPROTOOPT,
wasi::__WASI_ENOSPC => libc::ENOSPC,
wasi::__WASI_ENOSYS => libc::ENOSYS,
wasi::__WASI_ENOTDIR => libc::ENOTDIR,
wasi::__WASI_ENOTEMPTY => libc::ENOTEMPTY,
wasi::__WASI_ENOTRECOVERABLE => libc::ENOTRECOVERABLE,
wasi::__WASI_ENOTSOCK => libc::ENOTSOCK,
wasi::__WASI_ENOTSUP => libc::ENOTSUP,
wasi::__WASI_ENOTTY => libc::ENOTTY,
wasi::__WASI_ENXIO => libc::ENXIO,
wasi::__WASI_EOVERFLOW => libc::EOVERFLOW,
wasi::__WASI_EOWNERDEAD => libc::EOWNERDEAD,
wasi::__WASI_EPROTO => libc::EPROTO,
wasi::__WASI_EPROTONOSUPPORT => libc::EPROTONOSUPPORT,
wasi::__WASI_EPROTOTYPE => libc::EPROTOTYPE,
wasi::__WASI_ERANGE => libc::ERANGE,
wasi::__WASI_EROFS => libc::EROFS,
wasi::__WASI_ESPIPE => libc::ESPIPE,
wasi::__WASI_ESRCH => libc::ESRCH,
wasi::__WASI_ESTALE => libc::ESTALE,
wasi::__WASI_ETIMEDOUT => libc::ETIMEDOUT,
wasi::__WASI_ETXTBSY => libc::ETXTBSY,
wasi::__WASI_EXDEV => libc::EXDEV,
wasi::__WASI_ERRNO_SUCCESS => return Ok(()),
wasi::__WASI_ERRNO_IO => libc::EIO,
wasi::__WASI_ERRNO_PERM => libc::EPERM,
wasi::__WASI_ERRNO_INVAL => libc::EINVAL,
wasi::__WASI_ERRNO_PIPE => libc::EPIPE,
wasi::__WASI_ERRNO_NOTCONN => libc::ENOTCONN,
wasi::__WASI_ERRNO_2BIG => libc::E2BIG,
wasi::__WASI_ERRNO_ACCES => libc::EACCES,
wasi::__WASI_ERRNO_ADDRINUSE => libc::EADDRINUSE,
wasi::__WASI_ERRNO_ADDRNOTAVAIL => libc::EADDRNOTAVAIL,
wasi::__WASI_ERRNO_AFNOSUPPORT => libc::EAFNOSUPPORT,
wasi::__WASI_ERRNO_AGAIN => libc::EAGAIN,
wasi::__WASI_ERRNO_ALREADY => libc::EALREADY,
wasi::__WASI_ERRNO_BADF => libc::EBADF,
wasi::__WASI_ERRNO_BADMSG => libc::EBADMSG,
wasi::__WASI_ERRNO_BUSY => libc::EBUSY,
wasi::__WASI_ERRNO_CANCELED => libc::ECANCELED,
wasi::__WASI_ERRNO_CHILD => libc::ECHILD,
wasi::__WASI_ERRNO_CONNABORTED => libc::ECONNABORTED,
wasi::__WASI_ERRNO_CONNREFUSED => libc::ECONNREFUSED,
wasi::__WASI_ERRNO_CONNRESET => libc::ECONNRESET,
wasi::__WASI_ERRNO_DEADLK => libc::EDEADLK,
wasi::__WASI_ERRNO_DESTADDRREQ => libc::EDESTADDRREQ,
wasi::__WASI_ERRNO_DOM => libc::EDOM,
wasi::__WASI_ERRNO_DQUOT => libc::EDQUOT,
wasi::__WASI_ERRNO_EXIST => libc::EEXIST,
wasi::__WASI_ERRNO_FAULT => libc::EFAULT,
wasi::__WASI_ERRNO_FBIG => libc::EFBIG,
wasi::__WASI_ERRNO_HOSTUNREACH => libc::EHOSTUNREACH,
wasi::__WASI_ERRNO_IDRM => libc::EIDRM,
wasi::__WASI_ERRNO_ILSEQ => libc::EILSEQ,
wasi::__WASI_ERRNO_INPROGRESS => libc::EINPROGRESS,
wasi::__WASI_ERRNO_INTR => libc::EINTR,
wasi::__WASI_ERRNO_ISCONN => libc::EISCONN,
wasi::__WASI_ERRNO_ISDIR => libc::EISDIR,
wasi::__WASI_ERRNO_LOOP => libc::ELOOP,
wasi::__WASI_ERRNO_MFILE => libc::EMFILE,
wasi::__WASI_ERRNO_MLINK => libc::EMLINK,
wasi::__WASI_ERRNO_MSGSIZE => libc::EMSGSIZE,
wasi::__WASI_ERRNO_MULTIHOP => libc::EMULTIHOP,
wasi::__WASI_ERRNO_NAMETOOLONG => libc::ENAMETOOLONG,
wasi::__WASI_ERRNO_NETDOWN => libc::ENETDOWN,
wasi::__WASI_ERRNO_NETRESET => libc::ENETRESET,
wasi::__WASI_ERRNO_NETUNREACH => libc::ENETUNREACH,
wasi::__WASI_ERRNO_NFILE => libc::ENFILE,
wasi::__WASI_ERRNO_NOBUFS => libc::ENOBUFS,
wasi::__WASI_ERRNO_NODEV => libc::ENODEV,
wasi::__WASI_ERRNO_NOENT => libc::ENOENT,
wasi::__WASI_ERRNO_NOEXEC => libc::ENOEXEC,
wasi::__WASI_ERRNO_NOLCK => libc::ENOLCK,
wasi::__WASI_ERRNO_NOLINK => libc::ENOLINK,
wasi::__WASI_ERRNO_NOMEM => libc::ENOMEM,
wasi::__WASI_ERRNO_NOMSG => libc::ENOMSG,
wasi::__WASI_ERRNO_NOPROTOOPT => libc::ENOPROTOOPT,
wasi::__WASI_ERRNO_NOSPC => libc::ENOSPC,
wasi::__WASI_ERRNO_NOSYS => libc::ENOSYS,
wasi::__WASI_ERRNO_NOTDIR => libc::ENOTDIR,
wasi::__WASI_ERRNO_NOTEMPTY => libc::ENOTEMPTY,
wasi::__WASI_ERRNO_NOTRECOVERABLE => libc::ENOTRECOVERABLE,
wasi::__WASI_ERRNO_NOTSOCK => libc::ENOTSOCK,
wasi::__WASI_ERRNO_NOTSUP => libc::ENOTSUP,
wasi::__WASI_ERRNO_NOTTY => libc::ENOTTY,
wasi::__WASI_ERRNO_NXIO => libc::ENXIO,
wasi::__WASI_ERRNO_OVERFLOW => libc::EOVERFLOW,
wasi::__WASI_ERRNO_OWNERDEAD => libc::EOWNERDEAD,
wasi::__WASI_ERRNO_PROTO => libc::EPROTO,
wasi::__WASI_ERRNO_PROTONOSUPPORT => libc::EPROTONOSUPPORT,
wasi::__WASI_ERRNO_PROTOTYPE => libc::EPROTOTYPE,
wasi::__WASI_ERRNO_RANGE => libc::ERANGE,
wasi::__WASI_ERRNO_ROFS => libc::EROFS,
wasi::__WASI_ERRNO_SPIPE => libc::ESPIPE,
wasi::__WASI_ERRNO_SRCH => libc::ESRCH,
wasi::__WASI_ERRNO_STALE => libc::ESTALE,
wasi::__WASI_ERRNO_TIMEDOUT => libc::ETIMEDOUT,
wasi::__WASI_ERRNO_TXTBSY => libc::ETXTBSY,
wasi::__WASI_ERRNO_XDEV => libc::EXDEV,
#[cfg(target_os = "wasi")]
wasi::__WASI_ENOTCAPABLE => libc::ENOTCAPABLE,
wasi::__WASI_ERRNO_NOTCAPABLE => libc::ENOTCAPABLE,
#[cfg(not(target_os = "wasi"))]
wasi::__WASI_ENOTCAPABLE => libc::EIO,
wasi::__WASI_ERRNO_NOTCAPABLE => libc::EIO,
_ => panic!("unexpected wasi errno value"),
};
@@ -97,82 +97,82 @@ pub(crate) fn wasi_errno_to_io_error(errno: wasi::__wasi_errno_t) -> io::Result<
#[cfg(windows)]
let raw_os_error = match errno {
wasi::__WASI_ESUCCESS => return Ok(()),
wasi::__WASI_EINVAL => WSAEINVAL,
wasi::__WASI_EPIPE => ERROR_BROKEN_PIPE,
wasi::__WASI_ENOTCONN => WSAENOTCONN,
wasi::__WASI_EPERM | wasi::__WASI_EACCES => ERROR_ACCESS_DENIED,
wasi::__WASI_EADDRINUSE => WSAEADDRINUSE,
wasi::__WASI_EADDRNOTAVAIL => WSAEADDRNOTAVAIL,
wasi::__WASI_EAGAIN => WSAEWOULDBLOCK,
wasi::__WASI_ECONNABORTED => WSAECONNABORTED,
wasi::__WASI_ECONNREFUSED => WSAECONNREFUSED,
wasi::__WASI_ECONNRESET => WSAECONNRESET,
wasi::__WASI_EEXIST => ERROR_ALREADY_EXISTS,
wasi::__WASI_ENOENT => ERROR_FILE_NOT_FOUND,
wasi::__WASI_ETIMEDOUT => WSAETIMEDOUT,
wasi::__WASI_EAFNOSUPPORT => WSAEAFNOSUPPORT,
wasi::__WASI_EALREADY => WSAEALREADY,
wasi::__WASI_EBADF => WSAEBADF,
wasi::__WASI_EDESTADDRREQ => WSAEDESTADDRREQ,
wasi::__WASI_EDQUOT => WSAEDQUOT,
wasi::__WASI_EFAULT => WSAEFAULT,
wasi::__WASI_EHOSTUNREACH => WSAEHOSTUNREACH,
wasi::__WASI_EINPROGRESS => WSAEINPROGRESS,
wasi::__WASI_EINTR => WSAEINTR,
wasi::__WASI_EISCONN => WSAEISCONN,
wasi::__WASI_ELOOP => WSAELOOP,
wasi::__WASI_EMFILE => WSAEMFILE,
wasi::__WASI_EMSGSIZE => WSAEMSGSIZE,
wasi::__WASI_ENAMETOOLONG => WSAENAMETOOLONG,
wasi::__WASI_ENETDOWN => WSAENETDOWN,
wasi::__WASI_ENETRESET => WSAENETRESET,
wasi::__WASI_ENETUNREACH => WSAENETUNREACH,
wasi::__WASI_ENOBUFS => WSAENOBUFS,
wasi::__WASI_ENOPROTOOPT => WSAENOPROTOOPT,
wasi::__WASI_ENOTEMPTY => WSAENOTEMPTY,
wasi::__WASI_ENOTSOCK => WSAENOTSOCK,
wasi::__WASI_EPROTONOSUPPORT => WSAEPROTONOSUPPORT,
wasi::__WASI_EPROTOTYPE => WSAEPROTOTYPE,
wasi::__WASI_ESTALE => WSAESTALE,
wasi::__WASI_EIO
| wasi::__WASI_EISDIR
| wasi::__WASI_E2BIG
| wasi::__WASI_EBADMSG
| wasi::__WASI_EBUSY
| wasi::__WASI_ECANCELED
| wasi::__WASI_ECHILD
| wasi::__WASI_EDEADLK
| wasi::__WASI_EDOM
| wasi::__WASI_EFBIG
| wasi::__WASI_EIDRM
| wasi::__WASI_EILSEQ
| wasi::__WASI_EMLINK
| wasi::__WASI_EMULTIHOP
| wasi::__WASI_ENFILE
| wasi::__WASI_ENODEV
| wasi::__WASI_ENOEXEC
| wasi::__WASI_ENOLCK
| wasi::__WASI_ENOLINK
| wasi::__WASI_ENOMEM
| wasi::__WASI_ENOMSG
| wasi::__WASI_ENOSPC
| wasi::__WASI_ENOSYS
| wasi::__WASI_ENOTDIR
| wasi::__WASI_ENOTRECOVERABLE
| wasi::__WASI_ENOTSUP
| wasi::__WASI_ENOTTY
| wasi::__WASI_ENXIO
| wasi::__WASI_EOVERFLOW
| wasi::__WASI_EOWNERDEAD
| wasi::__WASI_EPROTO
| wasi::__WASI_ERANGE
| wasi::__WASI_EROFS
| wasi::__WASI_ESPIPE
| wasi::__WASI_ESRCH
| wasi::__WASI_ETXTBSY
| wasi::__WASI_EXDEV
| wasi::__WASI_ENOTCAPABLE => {
wasi::__WASI_ERRNO_SUCCESS => return Ok(()),
wasi::__WASI_ERRNO_INVAL => WSAEINVAL,
wasi::__WASI_ERRNO_PIPE => ERROR_BROKEN_PIPE,
wasi::__WASI_ERRNO_NOTCONN => WSAENOTCONN,
wasi::__WASI_ERRNO_PERM | wasi::__WASI_ERRNO_ACCES => ERROR_ACCESS_DENIED,
wasi::__WASI_ERRNO_ADDRINUSE => WSAEADDRINUSE,
wasi::__WASI_ERRNO_ADDRNOTAVAIL => WSAEADDRNOTAVAIL,
wasi::__WASI_ERRNO_AGAIN => WSAEWOULDBLOCK,
wasi::__WASI_ERRNO_CONNABORTED => WSAECONNABORTED,
wasi::__WASI_ERRNO_CONNREFUSED => WSAECONNREFUSED,
wasi::__WASI_ERRNO_CONNRESET => WSAECONNRESET,
wasi::__WASI_ERRNO_EXIST => ERROR_ALREADY_EXISTS,
wasi::__WASI_ERRNO_NOENT => ERROR_FILE_NOT_FOUND,
wasi::__WASI_ERRNO_TIMEDOUT => WSAETIMEDOUT,
wasi::__WASI_ERRNO_AFNOSUPPORT => WSAEAFNOSUPPORT,
wasi::__WASI_ERRNO_ALREADY => WSAEALREADY,
wasi::__WASI_ERRNO_BADF => WSAEBADF,
wasi::__WASI_ERRNO_DESTADDRREQ => WSAEDESTADDRREQ,
wasi::__WASI_ERRNO_DQUOT => WSAEDQUOT,
wasi::__WASI_ERRNO_FAULT => WSAEFAULT,
wasi::__WASI_ERRNO_HOSTUNREACH => WSAEHOSTUNREACH,
wasi::__WASI_ERRNO_INPROGRESS => WSAEINPROGRESS,
wasi::__WASI_ERRNO_INTR => WSAEINTR,
wasi::__WASI_ERRNO_ISCONN => WSAEISCONN,
wasi::__WASI_ERRNO_LOOP => WSAELOOP,
wasi::__WASI_ERRNO_MFILE => WSAEMFILE,
wasi::__WASI_ERRNO_MSGSIZE => WSAEMSGSIZE,
wasi::__WASI_ERRNO_NAMETOOLONG => WSAENAMETOOLONG,
wasi::__WASI_ERRNO_NETDOWN => WSAENETDOWN,
wasi::__WASI_ERRNO_NETRESET => WSAENETRESET,
wasi::__WASI_ERRNO_NETUNREACH => WSAENETUNREACH,
wasi::__WASI_ERRNO_NOBUFS => WSAENOBUFS,
wasi::__WASI_ERRNO_NOPROTOOPT => WSAENOPROTOOPT,
wasi::__WASI_ERRNO_NOTEMPTY => WSAENOTEMPTY,
wasi::__WASI_ERRNO_NOTSOCK => WSAENOTSOCK,
wasi::__WASI_ERRNO_PROTONOSUPPORT => WSAEPROTONOSUPPORT,
wasi::__WASI_ERRNO_PROTOTYPE => WSAEPROTOTYPE,
wasi::__WASI_ERRNO_STALE => WSAESTALE,
wasi::__WASI_ERRNO_IO
| wasi::__WASI_ERRNO_ISDIR
| wasi::__WASI_ERRNO_2BIG
| wasi::__WASI_ERRNO_BADMSG
| wasi::__WASI_ERRNO_BUSY
| wasi::__WASI_ERRNO_CANCELED
| wasi::__WASI_ERRNO_CHILD
| wasi::__WASI_ERRNO_DEADLK
| wasi::__WASI_ERRNO_DOM
| wasi::__WASI_ERRNO_FBIG
| wasi::__WASI_ERRNO_IDRM
| wasi::__WASI_ERRNO_ILSEQ
| wasi::__WASI_ERRNO_MLINK
| wasi::__WASI_ERRNO_MULTIHOP
| wasi::__WASI_ERRNO_NFILE
| wasi::__WASI_ERRNO_NODEV
| wasi::__WASI_ERRNO_NOEXEC
| wasi::__WASI_ERRNO_NOLCK
| wasi::__WASI_ERRNO_NOLINK
| wasi::__WASI_ERRNO_NOMEM
| wasi::__WASI_ERRNO_NOMSG
| wasi::__WASI_ERRNO_NOSPC
| wasi::__WASI_ERRNO_NOSYS
| wasi::__WASI_ERRNO_NOTDIR
| wasi::__WASI_ERRNO_NOTRECOVERABLE
| wasi::__WASI_ERRNO_NOTSUP
| wasi::__WASI_ERRNO_NOTTY
| wasi::__WASI_ERRNO_NXIO
| wasi::__WASI_ERRNO_OVERFLOW
| wasi::__WASI_ERRNO_OWNERDEAD
| wasi::__WASI_ERRNO_PROTO
| wasi::__WASI_ERRNO_RANGE
| wasi::__WASI_ERRNO_ROFS
| wasi::__WASI_ERRNO_SPIPE
| wasi::__WASI_ERRNO_SRCH
| wasi::__WASI_ERRNO_TXTBSY
| wasi::__WASI_ERRNO_XDEV
| wasi::__WASI_ERRNO_NOTCAPABLE => {
return Err(io::Error::new(io::ErrorKind::Other, error_str(errno)))
}
_ => panic!("unrecognized WASI errno value"),
@@ -184,82 +184,82 @@ pub(crate) fn wasi_errno_to_io_error(errno: wasi::__wasi_errno_t) -> io::Result<
#[cfg(windows)]
fn error_str(errno: wasi::__wasi_errno_t) -> &'static str {
match errno {
wasi::__WASI_E2BIG => "Argument list too long",
wasi::__WASI_EACCES => "Permission denied",
wasi::__WASI_EADDRINUSE => "Address in use",
wasi::__WASI_EADDRNOTAVAIL => "Address not available",
wasi::__WASI_EAFNOSUPPORT => "Address family not supported by protocol",
wasi::__WASI_EAGAIN => "Resource temporarily unavailable",
wasi::__WASI_EALREADY => "Operation already in progress",
wasi::__WASI_EBADF => "Bad file descriptor",
wasi::__WASI_EBADMSG => "Bad message",
wasi::__WASI_EBUSY => "Resource busy",
wasi::__WASI_ECANCELED => "Operation canceled",
wasi::__WASI_ECHILD => "No child process",
wasi::__WASI_ECONNABORTED => "Connection aborted",
wasi::__WASI_ECONNREFUSED => "Connection refused",
wasi::__WASI_ECONNRESET => "Connection reset by peer",
wasi::__WASI_EDEADLK => "Resource deadlock would occur",
wasi::__WASI_EDESTADDRREQ => "Destination address required",
wasi::__WASI_EDOM => "Domain error",
wasi::__WASI_EDQUOT => "Quota exceeded",
wasi::__WASI_EEXIST => "File exists",
wasi::__WASI_EFAULT => "Bad address",
wasi::__WASI_EFBIG => "File too large",
wasi::__WASI_EHOSTUNREACH => "Host is unreachable",
wasi::__WASI_EIDRM => "Identifier removed",
wasi::__WASI_EILSEQ => "Illegal byte sequence",
wasi::__WASI_EINPROGRESS => "Operation in progress",
wasi::__WASI_EINTR => "Interrupted system call",
wasi::__WASI_EINVAL => "Invalid argument",
wasi::__WASI_EIO => "Remote I/O error",
wasi::__WASI_EISCONN => "Socket is connected",
wasi::__WASI_EISDIR => "Is a directory",
wasi::__WASI_ELOOP => "Symbolic link loop",
wasi::__WASI_EMFILE => "No file descriptors available",
wasi::__WASI_EMLINK => "Too many links",
wasi::__WASI_EMSGSIZE => "Message too large",
wasi::__WASI_EMULTIHOP => "Multihop attempted",
wasi::__WASI_ENAMETOOLONG => "Filename too long",
wasi::__WASI_ENETDOWN => "Network is down",
wasi::__WASI_ENETRESET => "Connection reset by network",
wasi::__WASI_ENETUNREACH => "Network unreachable",
wasi::__WASI_ENFILE => "Too many open files in system",
wasi::__WASI_ENOBUFS => "No buffer space available",
wasi::__WASI_ENODEV => "No such device",
wasi::__WASI_ENOENT => "No such file or directory",
wasi::__WASI_ENOEXEC => "Exec format error",
wasi::__WASI_ENOLCK => "No locks available",
wasi::__WASI_ENOLINK => "Link has been severed",
wasi::__WASI_ENOMEM => "Out of memory",
wasi::__WASI_ENOMSG => "No message of desired type",
wasi::__WASI_ENOPROTOOPT => "Protocol not available",
wasi::__WASI_ENOSPC => "No space left on device",
wasi::__WASI_ENOSYS => "Function not implemented",
wasi::__WASI_ENOTCONN => "Socket not connected",
wasi::__WASI_ENOTDIR => "Not a directory",
wasi::__WASI_ENOTEMPTY => "Directory not empty",
wasi::__WASI_ENOTRECOVERABLE => "State not recoverable",
wasi::__WASI_ENOTSOCK => "Not a socket",
wasi::__WASI_ENOTSUP => "Not supported",
wasi::__WASI_ENOTTY => "Not a tty",
wasi::__WASI_ENXIO => "No such device or address",
wasi::__WASI_EOVERFLOW => "Value too large for data type",
wasi::__WASI_EOWNERDEAD => "Previous owner died",
wasi::__WASI_EPERM => "Operation not permitted",
wasi::__WASI_EPIPE => "Broken pipe",
wasi::__WASI_EPROTO => "Protocol error",
wasi::__WASI_EPROTONOSUPPORT => "Protocol not supported",
wasi::__WASI_EPROTOTYPE => "Protocol wrong type for socket",
wasi::__WASI_ERANGE => "Result not representable",
wasi::__WASI_EROFS => "Read-only file system",
wasi::__WASI_ESPIPE => "Invalid seek",
wasi::__WASI_ESRCH => "No such process",
wasi::__WASI_ESTALE => "Stale file handle",
wasi::__WASI_ETIMEDOUT => "Operation timed out",
wasi::__WASI_ETXTBSY => "Text file busy",
wasi::__WASI_EXDEV => "Cross-device link",
wasi::__WASI_ENOTCAPABLE => "Capabilities insufficient",
wasi::__WASI_ERRNO_2BIG => "Argument list too long",
wasi::__WASI_ERRNO_ACCES => "Permission denied",
wasi::__WASI_ERRNO_ADDRINUSE => "Address in use",
wasi::__WASI_ERRNO_ADDRNOTAVAIL => "Address not available",
wasi::__WASI_ERRNO_AFNOSUPPORT => "Address family not supported by protocol",
wasi::__WASI_ERRNO_AGAIN => "Resource temporarily unavailable",
wasi::__WASI_ERRNO_ALREADY => "Operation already in progress",
wasi::__WASI_ERRNO_BADF => "Bad file descriptor",
wasi::__WASI_ERRNO_BADMSG => "Bad message",
wasi::__WASI_ERRNO_BUSY => "Resource busy",
wasi::__WASI_ERRNO_CANCELED => "Operation canceled",
wasi::__WASI_ERRNO_CHILD => "No child process",
wasi::__WASI_ERRNO_CONNABORTED => "Connection aborted",
wasi::__WASI_ERRNO_CONNREFUSED => "Connection refused",
wasi::__WASI_ERRNO_CONNRESET => "Connection reset by peer",
wasi::__WASI_ERRNO_DEADLK => "Resource deadlock would occur",
wasi::__WASI_ERRNO_DESTADDRREQ => "Destination address required",
wasi::__WASI_ERRNO_DOM => "Domain error",
wasi::__WASI_ERRNO_DQUOT => "Quota exceeded",
wasi::__WASI_ERRNO_EXIST => "File exists",
wasi::__WASI_ERRNO_FAULT => "Bad address",
wasi::__WASI_ERRNO_FBIG => "File too large",
wasi::__WASI_ERRNO_HOSTUNREACH => "Host is unreachable",
wasi::__WASI_ERRNO_IDRM => "Identifier removed",
wasi::__WASI_ERRNO_ILSEQ => "Illegal byte sequence",
wasi::__WASI_ERRNO_INPROGRESS => "Operation in progress",
wasi::__WASI_ERRNO_INTR => "Interrupted system call",
wasi::__WASI_ERRNO_INVAL => "Invalid argument",
wasi::__WASI_ERRNO_IO => "Remote I/O error",
wasi::__WASI_ERRNO_ISCONN => "Socket is connected",
wasi::__WASI_ERRNO_ISDIR => "Is a directory",
wasi::__WASI_ERRNO_LOOP => "Symbolic link loop",
wasi::__WASI_ERRNO_MFILE => "No file descriptors available",
wasi::__WASI_ERRNO_MLINK => "Too many links",
wasi::__WASI_ERRNO_MSGSIZE => "Message too large",
wasi::__WASI_ERRNO_MULTIHOP => "Multihop attempted",
wasi::__WASI_ERRNO_NAMETOOLONG => "Filename too long",
wasi::__WASI_ERRNO_NETDOWN => "Network is down",
wasi::__WASI_ERRNO_NETRESET => "Connection reset by network",
wasi::__WASI_ERRNO_NETUNREACH => "Network unreachable",
wasi::__WASI_ERRNO_NFILE => "Too many open files in system",
wasi::__WASI_ERRNO_NOBUFS => "No buffer space available",
wasi::__WASI_ERRNO_NODEV => "No such device",
wasi::__WASI_ERRNO_NOENT => "No such file or directory",
wasi::__WASI_ERRNO_NOEXEC => "Exec format error",
wasi::__WASI_ERRNO_NOLCK => "No locks available",
wasi::__WASI_ERRNO_NOLINK => "Link has been severed",
wasi::__WASI_ERRNO_NOMEM => "Out of memory",
wasi::__WASI_ERRNO_NOMSG => "No message of desired type",
wasi::__WASI_ERRNO_NOPROTOOPT => "Protocol not available",
wasi::__WASI_ERRNO_NOSPC => "No space left on device",
wasi::__WASI_ERRNO_NOSYS => "Function not implemented",
wasi::__WASI_ERRNO_NOTCONN => "Socket not connected",
wasi::__WASI_ERRNO_NOTDIR => "Not a directory",
wasi::__WASI_ERRNO_NOTEMPTY => "Directory not empty",
wasi::__WASI_ERRNO_NOTRECOVERABLE => "State not recoverable",
wasi::__WASI_ERRNO_NOTSOCK => "Not a socket",
wasi::__WASI_ERRNO_NOTSUP => "Not supported",
wasi::__WASI_ERRNO_NOTTY => "Not a tty",
wasi::__WASI_ERRNO_NXIO => "No such device or address",
wasi::__WASI_ERRNO_OVERFLOW => "Value too large for data type",
wasi::__WASI_ERRNO_OWNERDEAD => "Previous owner died",
wasi::__WASI_ERRNO_PERM => "Operation not permitted",
wasi::__WASI_ERRNO_PIPE => "Broken pipe",
wasi::__WASI_ERRNO_PROTO => "Protocol error",
wasi::__WASI_ERRNO_PROTONOSUPPORT => "Protocol not supported",
wasi::__WASI_ERRNO_PROTOTYPE => "Protocol wrong type for socket",
wasi::__WASI_ERRNO_RANGE => "Result not representable",
wasi::__WASI_ERRNO_ROFS => "Read-only file system",
wasi::__WASI_ERRNO_SPIPE => "Invalid seek",
wasi::__WASI_ERRNO_SRCH => "No such process",
wasi::__WASI_ERRNO_STALE => "Stale file handle",
wasi::__WASI_ERRNO_TIMEDOUT => "Operation timed out",
wasi::__WASI_ERRNO_TXTBSY => "Text file busy",
wasi::__WASI_ERRNO_XDEV => "Cross-device link",
wasi::__WASI_ERRNO_NOTCAPABLE => "Capabilities insufficient",
_ => panic!("unrecognized WASI errno value"),
}
}

View File

@@ -14,7 +14,7 @@ pub(crate) fn systemtime_to_timestamp(st: SystemTime) -> Result<u64> {
/// Creates not-owned WASI path from byte slice.
///
/// NB WASI spec requires bytes to be valid UTF-8. Otherwise,
/// `__WASI_EILSEQ` error is returned.
/// `__WASI_ERRNO_ILSEQ` error is returned.
pub(crate) fn path_from_slice<'a>(s: &'a [u8]) -> Result<&'a str> {
str::from_utf8(s).map_err(|_| Error::EILSEQ)
}

View File

@@ -8,7 +8,7 @@ use crate::wasi::*;
use std::{io, slice};
use wig::witx_host_types;
witx_host_types!("unstable" "wasi_unstable_preview0");
witx_host_types!("snapshot" "wasi_snapshot_preview1");
pub(crate) unsafe fn ciovec_to_host(ciovec: &__wasi_ciovec_t) -> io::IoSlice {
let slice = slice::from_raw_parts(ciovec.buf as *const u8, ciovec.buf_len);
@@ -60,23 +60,23 @@ mod test {
#[test]
fn bindgen_test_layout___wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t() {
assert_eq!(
::std::mem::size_of::<__wasi_prestat_dir>(),
::std::mem::size_of::<__wasi_prestat_dir_t>(),
8usize,
concat!("Size of: ", stringify!(__wasi_prestat_dir))
concat!("Size of: ", stringify!(__wasi_prestat_dir_t))
);
assert_eq!(
::std::mem::align_of::<__wasi_prestat_dir>(),
::std::mem::align_of::<__wasi_prestat_dir_t>(),
8usize,
concat!("Alignment of ", stringify!(__wasi_prestat_dir))
concat!("Alignment of ", stringify!(__wasi_prestat_dir_t))
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<__wasi_prestat_dir>())).pr_name_len as *const _ as usize
&(*(::std::ptr::null::<__wasi_prestat_dir_t>())).pr_name_len as *const _ as usize
},
0usize,
concat!(
"Offset of field: ",
stringify!(__wasi_prestat_dir),
stringify!(__wasi_prestat_dir_t),
"::",
stringify!(pr_name_len)
)
@@ -86,21 +86,21 @@ mod test {
#[test]
fn bindgen_test_layout___wasi_prestat_t___wasi_prestat_u() {
assert_eq!(
::std::mem::size_of::<__wasi_prestat_u>(),
::std::mem::size_of::<__wasi_prestat_u_t>(),
8usize,
concat!("Size of: ", stringify!(__wasi_prestat_u))
);
assert_eq!(
::std::mem::align_of::<__wasi_prestat_u>(),
::std::mem::align_of::<__wasi_prestat_u_t>(),
8usize,
concat!("Alignment of ", stringify!(__wasi_prestat_u))
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<__wasi_prestat_u>())).dir as *const _ as usize },
unsafe { &(*(::std::ptr::null::<__wasi_prestat_u_t>())).dir as *const _ as usize },
0usize,
concat!(
"Offset of field: ",
stringify!(__wasi_prestat_u),
stringify!(__wasi_prestat_u_t),
"::",
stringify!(dir)
)

View File

@@ -1,6 +1,6 @@
#![allow(non_camel_case_types)]
use crate::ctx::WasiCtx;
use crate::{wasi, wasi32};
use crate::{hostcalls_impl, wasi, wasi32};
hostcalls! {
pub unsafe fn fd_close(wasi_ctx: &mut WasiCtx, fd: wasi::__wasi_fd_t,) -> wasi::__wasi_errno_t;

View File

@@ -1,6 +1,6 @@
#![allow(non_camel_case_types)]
use crate::ctx::WasiCtx;
use crate::{wasi, wasi32};
use crate::{hostcalls_impl, wasi, wasi32};
use log::trace;
use wasi_common_cbindgen::wasi_common_cbindgen;

View File

@@ -35,7 +35,7 @@ pub(crate) unsafe fn fd_datasync(wasi_ctx: &WasiCtx, fd: wasi::__wasi_fd_t) -> R
let fd = wasi_ctx
.get_fd_entry(fd)?
.as_descriptor(wasi::__WASI_RIGHT_FD_DATASYNC, 0)?
.as_descriptor(wasi::__WASI_RIGHTS_FD_DATASYNC, 0)?
.as_file()?;
fd.sync_data().map_err(Into::into)
@@ -61,7 +61,7 @@ pub(crate) unsafe fn fd_pread(
let fd = wasi_ctx
.get_fd_entry(fd)?
.as_descriptor(wasi::__WASI_RIGHT_FD_READ | wasi::__WASI_RIGHT_FD_SEEK, 0)?
.as_descriptor(wasi::__WASI_RIGHTS_FD_READ | wasi::__WASI_RIGHTS_FD_SEEK, 0)?
.as_file()?;
let iovs = dec_iovec_slice(memory, iovs_ptr, iovs_len)?;
@@ -110,7 +110,10 @@ pub(crate) unsafe fn fd_pwrite(
let fd = wasi_ctx
.get_fd_entry(fd)?
.as_descriptor(wasi::__WASI_RIGHT_FD_WRITE | wasi::__WASI_RIGHT_FD_SEEK, 0)?
.as_descriptor(
wasi::__WASI_RIGHTS_FD_WRITE | wasi::__WASI_RIGHTS_FD_SEEK,
0,
)?
.as_file()?;
let iovs = dec_ciovec_slice(memory, iovs_ptr, iovs_len)?;
@@ -156,7 +159,7 @@ pub(crate) unsafe fn fd_read(
let maybe_host_nread = match wasi_ctx
.get_fd_entry_mut(fd)?
.as_descriptor_mut(wasi::__WASI_RIGHT_FD_READ, 0)?
.as_descriptor_mut(wasi::__WASI_RIGHTS_FD_READ, 0)?
{
Descriptor::OsFile(file) => file.read_vectored(&mut iovs),
Descriptor::Stdin => io::stdin().lock().read_vectored(&mut iovs),
@@ -225,9 +228,9 @@ pub(crate) unsafe fn fd_seek(
);
let rights = if offset == 0 && whence == wasi::__WASI_WHENCE_CUR {
wasi::__WASI_RIGHT_FD_TELL
wasi::__WASI_RIGHTS_FD_TELL
} else {
wasi::__WASI_RIGHT_FD_SEEK | wasi::__WASI_RIGHT_FD_TELL
wasi::__WASI_RIGHTS_FD_SEEK | wasi::__WASI_RIGHTS_FD_TELL
};
let fd = wasi_ctx
.get_fd_entry_mut(fd)?
@@ -257,7 +260,7 @@ pub(crate) unsafe fn fd_tell(
let fd = wasi_ctx
.get_fd_entry_mut(fd)?
.as_descriptor_mut(wasi::__WASI_RIGHT_FD_TELL, 0)?
.as_descriptor_mut(wasi::__WASI_RIGHTS_FD_TELL, 0)?
.as_file_mut()?;
let host_offset = fd.seek(SeekFrom::Current(0))?;
@@ -333,7 +336,7 @@ pub(crate) unsafe fn fd_sync(wasi_ctx: &WasiCtx, fd: wasi::__wasi_fd_t) -> Resul
let fd = wasi_ctx
.get_fd_entry(fd)?
.as_descriptor(wasi::__WASI_RIGHT_FD_SYNC, 0)?
.as_descriptor(wasi::__WASI_RIGHTS_FD_SYNC, 0)?
.as_file()?;
fd.sync_all().map_err(Into::into)
}
@@ -360,7 +363,7 @@ pub(crate) unsafe fn fd_write(
// perform unbuffered writes
let host_nwritten = match wasi_ctx
.get_fd_entry_mut(fd)?
.as_descriptor_mut(wasi::__WASI_RIGHT_FD_WRITE, 0)?
.as_descriptor_mut(wasi::__WASI_RIGHTS_FD_WRITE, 0)?
{
Descriptor::OsFile(file) => file.write_vectored(&iovs)?,
Descriptor::Stdin => return Err(Error::EBADF),
@@ -397,7 +400,7 @@ pub(crate) unsafe fn fd_advise(
let fd = wasi_ctx
.get_fd_entry(fd)?
.as_descriptor(wasi::__WASI_RIGHT_FD_ADVISE, 0)?
.as_descriptor(wasi::__WASI_RIGHTS_FD_ADVISE, 0)?
.as_file()?;
hostcalls_impl::fd_advise(fd, advice, offset, len)
@@ -413,7 +416,7 @@ pub(crate) unsafe fn fd_allocate(
let fd = wasi_ctx
.get_fd_entry(fd)?
.as_descriptor(wasi::__WASI_RIGHT_FD_ALLOCATE, 0)?
.as_descriptor(wasi::__WASI_RIGHTS_FD_ALLOCATE, 0)?
.as_file()?;
let metadata = fd.metadata()?;
@@ -450,7 +453,7 @@ pub(crate) unsafe fn path_create_directory(
trace!(" | (path_ptr,path_len)='{}'", path);
let rights = wasi::__WASI_RIGHT_PATH_OPEN | wasi::__WASI_RIGHT_PATH_CREATE_DIRECTORY;
let rights = wasi::__WASI_RIGHTS_PATH_OPEN | wasi::__WASI_RIGHTS_PATH_CREATE_DIRECTORY;
let fe = wasi_ctx.get_fd_entry(dirfd)?;
let resolved = path_get(fe, rights, 0, 0, path, false)?;
@@ -489,7 +492,7 @@ pub(crate) unsafe fn path_link(
let new_fe = wasi_ctx.get_fd_entry(new_dirfd)?;
let resolved_old = path_get(
old_fe,
wasi::__WASI_RIGHT_PATH_LINK_SOURCE,
wasi::__WASI_RIGHTS_PATH_LINK_SOURCE,
0,
0,
old_path,
@@ -497,7 +500,7 @@ pub(crate) unsafe fn path_link(
)?;
let resolved_new = path_get(
new_fe,
wasi::__WASI_RIGHT_PATH_LINK_TARGET,
wasi::__WASI_RIGHTS_PATH_LINK_TARGET,
0,
0,
new_path,
@@ -549,16 +552,16 @@ pub(crate) unsafe fn path_open(
needed_inheriting,
dirflags,
path,
oflags & wasi::__WASI_O_CREAT != 0,
oflags & wasi::__WASI_OFLAGS_CREAT != 0,
)?;
// which open mode do we need?
let read = fs_rights_base & (wasi::__WASI_RIGHT_FD_READ | wasi::__WASI_RIGHT_FD_READDIR) != 0;
let read = fs_rights_base & (wasi::__WASI_RIGHTS_FD_READ | wasi::__WASI_RIGHTS_FD_READDIR) != 0;
let write = fs_rights_base
& (wasi::__WASI_RIGHT_FD_DATASYNC
| wasi::__WASI_RIGHT_FD_WRITE
| wasi::__WASI_RIGHT_FD_ALLOCATE
| wasi::__WASI_RIGHT_FD_FILESTAT_SET_SIZE)
& (wasi::__WASI_RIGHTS_FD_DATASYNC
| wasi::__WASI_RIGHTS_FD_WRITE
| wasi::__WASI_RIGHTS_FD_ALLOCATE
| wasi::__WASI_RIGHTS_FD_FILESTAT_SET_SIZE)
!= 0;
let fd = hostcalls_impl::path_open(resolved, read, write, oflags, fs_flags)?;
@@ -597,7 +600,7 @@ pub(crate) unsafe fn fd_readdir(
let file = wasi_ctx
.get_fd_entry_mut(fd)?
.as_descriptor_mut(wasi::__WASI_RIGHT_FD_READDIR, 0)?
.as_descriptor_mut(wasi::__WASI_RIGHTS_FD_READDIR, 0)?
.as_file_mut()?;
let host_buf = dec_slice_of_mut_u8(memory, buf, buf_len)?;
@@ -637,7 +640,7 @@ pub(crate) unsafe fn path_readlink(
trace!(" | (path_ptr,path_len)='{}'", &path);
let fe = wasi_ctx.get_fd_entry(dirfd)?;
let resolved = path_get(fe, wasi::__WASI_RIGHT_PATH_READLINK, 0, 0, &path, false)?;
let resolved = path_get(fe, wasi::__WASI_RIGHTS_PATH_READLINK, 0, 0, &path, false)?;
let mut buf = dec_slice_of_mut_u8(memory, buf_ptr, buf_len)?;
@@ -679,7 +682,7 @@ pub(crate) unsafe fn path_rename(
let new_fe = wasi_ctx.get_fd_entry(new_dirfd)?;
let resolved_old = path_get(
old_fe,
wasi::__WASI_RIGHT_PATH_RENAME_SOURCE,
wasi::__WASI_RIGHTS_PATH_RENAME_SOURCE,
0,
0,
old_path,
@@ -687,7 +690,7 @@ pub(crate) unsafe fn path_rename(
)?;
let resolved_new = path_get(
new_fe,
wasi::__WASI_RIGHT_PATH_RENAME_TARGET,
wasi::__WASI_RIGHTS_PATH_RENAME_TARGET,
0,
0,
new_path,
@@ -738,7 +741,7 @@ pub(crate) unsafe fn fd_filestat_set_times(
let fd = wasi_ctx
.get_fd_entry(fd)?
.as_descriptor(wasi::__WASI_RIGHT_FD_FILESTAT_SET_TIMES, 0)?
.as_descriptor(wasi::__WASI_RIGHTS_FD_FILESTAT_SET_TIMES, 0)?
.as_file()?;
fd_filestat_set_times_impl(fd, st_atim, st_mtim, fst_flags)
@@ -750,10 +753,10 @@ pub(crate) fn fd_filestat_set_times_impl(
st_mtim: wasi::__wasi_timestamp_t,
fst_flags: wasi::__wasi_fstflags_t,
) -> Result<()> {
let set_atim = fst_flags & wasi::__WASI_FILESTAT_SET_ATIM != 0;
let set_atim_now = fst_flags & wasi::__WASI_FILESTAT_SET_ATIM_NOW != 0;
let set_mtim = fst_flags & wasi::__WASI_FILESTAT_SET_MTIM != 0;
let set_mtim_now = fst_flags & wasi::__WASI_FILESTAT_SET_MTIM_NOW != 0;
let set_atim = fst_flags & wasi::__WASI_FSTFLAGS_ATIM != 0;
let set_atim_now = fst_flags & wasi::__WASI_FSTFLAGS_ATIM_NOW != 0;
let set_mtim = fst_flags & wasi::__WASI_FSTFLAGS_MTIM != 0;
let set_mtim_now = fst_flags & wasi::__WASI_FSTFLAGS_MTIM_NOW != 0;
if (set_atim && set_atim_now) || (set_mtim && set_mtim_now) {
return Err(Error::EINVAL);
@@ -789,7 +792,7 @@ pub(crate) unsafe fn fd_filestat_set_size(
let fd = wasi_ctx
.get_fd_entry(fd)?
.as_descriptor(wasi::__WASI_RIGHT_FD_FILESTAT_SET_SIZE, 0)?
.as_descriptor(wasi::__WASI_RIGHTS_FD_FILESTAT_SET_SIZE, 0)?
.as_file()?;
// This check will be unnecessary when rust-lang/rust#63326 is fixed
@@ -824,7 +827,7 @@ pub(crate) unsafe fn path_filestat_get(
let fe = wasi_ctx.get_fd_entry(dirfd)?;
let resolved = path_get(
fe,
wasi::__WASI_RIGHT_PATH_FILESTAT_GET,
wasi::__WASI_RIGHTS_PATH_FILESTAT_GET,
0,
dirflags,
path,
@@ -865,7 +868,7 @@ pub(crate) unsafe fn path_filestat_set_times(
let fe = wasi_ctx.get_fd_entry(dirfd)?;
let resolved = path_get(
fe,
wasi::__WASI_RIGHT_PATH_FILESTAT_SET_TIMES,
wasi::__WASI_RIGHTS_PATH_FILESTAT_SET_TIMES,
0,
dirflags,
path,
@@ -900,7 +903,7 @@ pub(crate) unsafe fn path_symlink(
trace!(" | (new_path_ptr,new_path_len)='{}'", new_path);
let fe = wasi_ctx.get_fd_entry(dirfd)?;
let resolved_new = path_get(fe, wasi::__WASI_RIGHT_PATH_SYMLINK, 0, 0, new_path, true)?;
let resolved_new = path_get(fe, wasi::__WASI_RIGHTS_PATH_SYMLINK, 0, 0, new_path, true)?;
hostcalls_impl::path_symlink(old_path, resolved_new)
}
@@ -924,7 +927,7 @@ pub(crate) unsafe fn path_unlink_file(
trace!(" | (path_ptr,path_len)='{}'", path);
let fe = wasi_ctx.get_fd_entry(dirfd)?;
let resolved = path_get(fe, wasi::__WASI_RIGHT_PATH_UNLINK_FILE, 0, 0, path, false)?;
let resolved = path_get(fe, wasi::__WASI_RIGHTS_PATH_UNLINK_FILE, 0, 0, path, false)?;
hostcalls_impl::path_unlink_file(resolved)
}
@@ -950,7 +953,7 @@ pub(crate) unsafe fn path_remove_directory(
let fe = wasi_ctx.get_fd_entry(dirfd)?;
let resolved = path_get(
fe,
wasi::__WASI_RIGHT_PATH_REMOVE_DIRECTORY,
wasi::__WASI_RIGHTS_PATH_REMOVE_DIRECTORY,
0,
0,
path,
@@ -988,8 +991,8 @@ pub(crate) unsafe fn fd_prestat_get(
prestat_ptr,
host::__wasi_prestat_t {
pr_type: wasi::__WASI_PREOPENTYPE_DIR,
u: host::__wasi_prestat_u {
dir: host::__wasi_prestat_dir {
u: host::__wasi_prestat_u_t {
dir: host::__wasi_prestat_dir_t {
pr_name_len: path.len(),
},
},

View File

@@ -118,9 +118,9 @@ pub(crate) fn path_get(
}
Err(e) => {
match e.as_wasi_errno() {
wasi::__WASI_ELOOP
| wasi::__WASI_EMLINK
| wasi::__WASI_ENOTDIR =>
wasi::__WASI_ERRNO_LOOP
| wasi::__WASI_ERRNO_MLINK
| wasi::__WASI_ERRNO_NOTDIR =>
// Check to see if it was a symlink. Linux indicates
// this with ENOTDIR because of the O_DIRECTORY flag.
{
@@ -155,7 +155,7 @@ pub(crate) fn path_get(
continue;
} else if ends_with_slash
|| (dirflags & wasi::__WASI_LOOKUP_SYMLINK_FOLLOW) != 0
|| (dirflags & wasi::__WASI_LOOKUPFLAGS_SYMLINK_FOLLOW) != 0
{
// if there's a trailing slash, or if `LOOKUP_SYMLINK_FOLLOW` is set, attempt
// symlink expansion
@@ -179,12 +179,12 @@ pub(crate) fn path_get(
continue;
}
Err(e) => {
if e.as_wasi_errno() != wasi::__WASI_EINVAL
&& e.as_wasi_errno() != wasi::__WASI_ENOENT
if e.as_wasi_errno() != wasi::__WASI_ERRNO_INVAL
&& e.as_wasi_errno() != wasi::__WASI_ERRNO_NOENT
// this handles the cases when trying to link to
// a destination that already exists, and the target
// path contains a slash
&& e.as_wasi_errno() != wasi::__WASI_ENOTDIR
&& e.as_wasi_errno() != wasi::__WASI_ERRNO_NOTDIR
{
return Err(e);
}

View File

@@ -240,9 +240,9 @@ pub(crate) fn poll_oneoff(
{
let wasi_fd = unsafe { subscription.u.fd_readwrite.file_descriptor };
let rights = if r#type == wasi::__WASI_EVENTTYPE_FD_READ {
wasi::__WASI_RIGHT_FD_READ
wasi::__WASI_RIGHTS_FD_READ
} else {
wasi::__WASI_RIGHT_FD_WRITE
wasi::__WASI_RIGHTS_FD_WRITE
};
match unsafe {
@@ -260,7 +260,7 @@ pub(crate) fn poll_oneoff(
userdata: subscription.userdata,
r#type,
error: err.as_wasi_errno(),
u: wasi::__wasi_event_u {
u: wasi::__wasi_event_u_t {
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
nbytes: 0,
flags: 0,
@@ -292,7 +292,7 @@ pub(crate) fn poll_oneoff(
fn wasi_clock_to_relative_ns_delay(wasi_clock: wasi::__wasi_subscription_clock_t) -> Result<u128> {
use std::time::SystemTime;
if wasi_clock.flags != wasi::__WASI_SUBSCRIPTION_CLOCK_ABSTIME {
if wasi_clock.flags != wasi::__WASI_SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME {
return Ok(u128::from(wasi_clock.timeout));
}
let now: u128 = SystemTime::now()

View File

@@ -33,6 +33,7 @@ pub mod fs;
mod host;
pub mod hostcalls;
mod memory;
pub mod old;
pub mod wasi;
pub mod wasi32;

View File

@@ -2,8 +2,24 @@ macro_rules! hostcalls {
($(pub unsafe fn $name:ident($($arg:ident: $ty:ty,)*) -> $ret:ty;)*) => ($(
#[wasi_common_cbindgen::wasi_common_cbindgen]
pub unsafe fn $name($($arg: $ty,)*) -> $ret {
let ret = match crate::hostcalls_impl::$name($($arg,)*) {
Ok(()) => crate::wasi::__WASI_ESUCCESS,
let ret = match hostcalls_impl::$name($($arg,)*) {
Ok(()) => wasi::__WASI_ERRNO_SUCCESS,
Err(e) => e.as_wasi_errno(),
};
ret
}
)*)
}
// Like `hostcalls`, but uses `wasi_common_cbindgen_old`, which means
// it doesn't declare a non-mangled function name.
macro_rules! hostcalls_old {
($(pub unsafe fn $name:ident($($arg:ident: $ty:ty,)*) -> $ret:ty;)*) => ($(
#[wasi_common_cbindgen::wasi_common_cbindgen_old]
pub unsafe fn $name($($arg: $ty,)*) -> $ret {
let ret = match hostcalls_impl::$name($($arg,)*) {
Ok(()) => wasi::__WASI_ERRNO_SUCCESS,
Err(e) => e.as_wasi_errno(),
};

View File

@@ -252,14 +252,14 @@ pub(crate) fn dec_filestat_byref(
let raw = dec_raw_byref::<wasi::__wasi_filestat_t>(memory, filestat_ptr)?;
Ok(wasi::__wasi_filestat_t {
st_dev: PrimInt::from_le(raw.st_dev),
st_ino: PrimInt::from_le(raw.st_ino),
st_filetype: PrimInt::from_le(raw.st_filetype),
st_nlink: PrimInt::from_le(raw.st_nlink),
st_size: PrimInt::from_le(raw.st_size),
st_atim: PrimInt::from_le(raw.st_atim),
st_mtim: PrimInt::from_le(raw.st_mtim),
st_ctim: PrimInt::from_le(raw.st_ctim),
dev: PrimInt::from_le(raw.dev),
ino: PrimInt::from_le(raw.ino),
filetype: PrimInt::from_le(raw.filetype),
nlink: PrimInt::from_le(raw.nlink),
size: PrimInt::from_le(raw.size),
atim: PrimInt::from_le(raw.atim),
mtim: PrimInt::from_le(raw.mtim),
ctim: PrimInt::from_le(raw.ctim),
})
}
@@ -269,14 +269,14 @@ pub(crate) fn enc_filestat_byref(
filestat: wasi::__wasi_filestat_t,
) -> Result<()> {
let raw = wasi::__wasi_filestat_t {
st_dev: PrimInt::to_le(filestat.st_dev),
st_ino: PrimInt::to_le(filestat.st_ino),
st_filetype: PrimInt::to_le(filestat.st_filetype),
st_nlink: PrimInt::to_le(filestat.st_nlink),
st_size: PrimInt::to_le(filestat.st_size),
st_atim: PrimInt::to_le(filestat.st_atim),
st_mtim: PrimInt::to_le(filestat.st_mtim),
st_ctim: PrimInt::to_le(filestat.st_ctim),
dev: PrimInt::to_le(filestat.dev),
ino: PrimInt::to_le(filestat.ino),
filetype: PrimInt::to_le(filestat.filetype),
nlink: PrimInt::to_le(filestat.nlink),
size: PrimInt::to_le(filestat.size),
atim: PrimInt::to_le(filestat.atim),
mtim: PrimInt::to_le(filestat.mtim),
ctim: PrimInt::to_le(filestat.ctim),
};
enc_raw_byref::<wasi::__wasi_filestat_t>(memory, filestat_ptr, raw)
@@ -332,8 +332,8 @@ pub(crate) fn dec_prestat_byref(
match PrimInt::from_le(raw.pr_type) {
wasi::__WASI_PREOPENTYPE_DIR => Ok(host::__wasi_prestat_t {
pr_type: wasi::__WASI_PREOPENTYPE_DIR,
u: host::__wasi_prestat_u {
dir: host::__wasi_prestat_dir {
u: host::__wasi_prestat_u_t {
dir: host::__wasi_prestat_dir_t {
pr_name_len: dec_usize(PrimInt::from_le(unsafe { raw.u.dir.pr_name_len })),
},
},
@@ -350,8 +350,8 @@ pub(crate) fn enc_prestat_byref(
let raw = match prestat.pr_type {
wasi::__WASI_PREOPENTYPE_DIR => Ok(wasi32::__wasi_prestat_t {
pr_type: PrimInt::to_le(wasi::__WASI_PREOPENTYPE_DIR),
u: wasi32::__wasi_prestat_u {
dir: wasi32::__wasi_prestat_dir {
u: wasi32::__wasi_prestat_u_t {
dir: wasi32::__wasi_prestat_dir_t {
pr_name_len: enc_usize(unsafe { prestat.u.dir.pr_name_len }),
},
},
@@ -413,11 +413,10 @@ pub(crate) fn dec_subscriptions(
let r#type = PrimInt::from_le(raw_subscription.r#type);
let raw_u = raw_subscription.u;
let u = match r#type {
wasi::__WASI_EVENTTYPE_CLOCK => wasi::__wasi_subscription_u {
wasi::__WASI_EVENTTYPE_CLOCK => wasi::__wasi_subscription_u_t {
clock: unsafe {
wasi::__wasi_subscription_clock_t {
identifier: PrimInt::from_le(raw_u.clock.identifier),
clock_id: PrimInt::from_le(raw_u.clock.clock_id),
id: PrimInt::from_le(raw_u.clock.id),
timeout: PrimInt::from_le(raw_u.clock.timeout),
precision: PrimInt::from_le(raw_u.clock.precision),
flags: PrimInt::from_le(raw_u.clock.flags),
@@ -425,7 +424,7 @@ pub(crate) fn dec_subscriptions(
},
},
wasi::__WASI_EVENTTYPE_FD_READ | wasi::__WASI_EVENTTYPE_FD_WRITE => {
wasi::__wasi_subscription_u {
wasi::__wasi_subscription_u_t {
fd_readwrite: wasi::__wasi_subscription_fd_readwrite_t {
file_descriptor: PrimInt::from_le(unsafe {
raw_u.fd_readwrite.file_descriptor
@@ -462,7 +461,7 @@ pub(crate) fn enc_events(
userdata: PrimInt::to_le(event.userdata),
r#type: PrimInt::to_le(event.r#type),
error: PrimInt::to_le(event.error),
u: wasi::__wasi_event_u {
u: wasi::__wasi_event_u_t {
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
nbytes: PrimInt::to_le(fd_readwrite.nbytes),
flags: PrimInt::to_le(fd_readwrite.flags),

View File

@@ -0,0 +1 @@
pub mod snapshot_0;

View File

@@ -0,0 +1,335 @@
use crate::old::snapshot_0::fdentry::FdEntry;
use crate::old::snapshot_0::{wasi, Error, Result};
use std::borrow::Borrow;
use std::collections::HashMap;
use std::env;
use std::ffi::{CString, OsString};
use std::fs::File;
use std::path::{Path, PathBuf};
enum PendingFdEntry {
Thunk(fn() -> Result<FdEntry>),
File(File),
}
impl std::fmt::Debug for PendingFdEntry {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Thunk(f) => write!(
fmt,
"PendingFdEntry::Thunk({:p})",
f as *const fn() -> Result<FdEntry>
),
Self::File(f) => write!(fmt, "PendingFdEntry::File({:?})", f),
}
}
}
#[derive(Debug, Eq, Hash, PartialEq)]
enum PendingCString {
Bytes(Vec<u8>),
OsString(OsString),
}
impl From<Vec<u8>> for PendingCString {
fn from(bytes: Vec<u8>) -> Self {
Self::Bytes(bytes)
}
}
impl From<OsString> for PendingCString {
fn from(s: OsString) -> Self {
Self::OsString(s)
}
}
impl PendingCString {
fn into_string(self) -> Result<String> {
match self {
Self::Bytes(v) => String::from_utf8(v).map_err(|_| Error::EILSEQ),
Self::OsString(s) => s.into_string().map_err(|_| Error::EILSEQ),
}
}
/// Create a `CString` containing valid UTF-8, or fail with `Error::EILSEQ`.
fn into_utf8_cstring(self) -> Result<CString> {
self.into_string()
.and_then(|s| CString::new(s).map_err(|_| Error::EILSEQ))
}
}
/// A builder allowing customizable construction of `WasiCtx` instances.
pub struct WasiCtxBuilder {
fds: HashMap<wasi::__wasi_fd_t, PendingFdEntry>,
preopens: Vec<(PathBuf, File)>,
args: Vec<PendingCString>,
env: HashMap<PendingCString, PendingCString>,
}
impl WasiCtxBuilder {
/// Builder for a new `WasiCtx`.
pub fn new() -> Self {
let mut builder = Self {
fds: HashMap::new(),
preopens: Vec::new(),
args: vec![],
env: HashMap::new(),
};
builder.fds.insert(0, PendingFdEntry::Thunk(FdEntry::null));
builder.fds.insert(1, PendingFdEntry::Thunk(FdEntry::null));
builder.fds.insert(2, PendingFdEntry::Thunk(FdEntry::null));
builder
}
/// Add arguments to the command-line arguments list.
///
/// Arguments must be valid UTF-8 with no NUL bytes, or else `WasiCtxBuilder::build()` will fail
/// with `Error::EILSEQ`.
pub fn args<S: AsRef<[u8]>>(mut self, args: impl IntoIterator<Item = S>) -> Self {
self.args = args
.into_iter()
.map(|arg| arg.as_ref().to_vec().into())
.collect();
self
}
/// Add an argument to the command-line arguments list.
///
/// Arguments must be valid UTF-8 with no NUL bytes, or else `WasiCtxBuilder::build()` will fail
/// with `Error::EILSEQ`.
pub fn arg<S: AsRef<[u8]>>(mut self, arg: S) -> Self {
self.args.push(arg.as_ref().to_vec().into());
self
}
/// Inherit the command-line arguments from the host process.
///
/// If any arguments from the host process contain invalid UTF-8, `WasiCtxBuilder::build()` will
/// fail with `Error::EILSEQ`.
pub fn inherit_args(mut self) -> Self {
self.args = env::args_os().map(PendingCString::OsString).collect();
self
}
/// Inherit the stdin, stdout, and stderr streams from the host process.
pub fn inherit_stdio(mut self) -> Self {
self.fds
.insert(0, PendingFdEntry::Thunk(FdEntry::duplicate_stdin));
self.fds
.insert(1, PendingFdEntry::Thunk(FdEntry::duplicate_stdout));
self.fds
.insert(2, PendingFdEntry::Thunk(FdEntry::duplicate_stderr));
self
}
/// Inherit the environment variables from the host process.
///
/// If any environment variables from the host process contain invalid Unicode (UTF-16 for
/// Windows, UTF-8 for other platforms), `WasiCtxBuilder::build()` will fail with
/// `Error::EILSEQ`.
pub fn inherit_env(mut self) -> Self {
self.env = std::env::vars_os()
.map(|(k, v)| (k.into(), v.into()))
.collect();
self
}
/// Add an entry to the environment.
///
/// Environment variable keys and values must be valid UTF-8 with no NUL bytes, or else
/// `WasiCtxBuilder::build()` will fail with `Error::EILSEQ`.
pub fn env<S: AsRef<[u8]>>(mut self, k: S, v: S) -> Self {
self.env
.insert(k.as_ref().to_vec().into(), v.as_ref().to_vec().into());
self
}
/// Add entries to the environment.
///
/// Environment variable keys and values must be valid UTF-8 with no NUL bytes, or else
/// `WasiCtxBuilder::build()` will fail with `Error::EILSEQ`.
pub fn envs<S: AsRef<[u8]>, T: Borrow<(S, S)>>(
mut self,
envs: impl IntoIterator<Item = T>,
) -> Self {
self.env = envs
.into_iter()
.map(|t| {
let (k, v) = t.borrow();
(k.as_ref().to_vec().into(), v.as_ref().to_vec().into())
})
.collect();
self
}
/// Provide a File to use as stdin
pub fn stdin(mut self, file: File) -> Self {
self.fds.insert(0, PendingFdEntry::File(file));
self
}
/// Provide a File to use as stdout
pub fn stdout(mut self, file: File) -> Self {
self.fds.insert(1, PendingFdEntry::File(file));
self
}
/// Provide a File to use as stderr
pub fn stderr(mut self, file: File) -> Self {
self.fds.insert(2, PendingFdEntry::File(file));
self
}
/// Add a preopened directory.
pub fn preopened_dir<P: AsRef<Path>>(mut self, dir: File, guest_path: P) -> Self {
self.preopens.push((guest_path.as_ref().to_owned(), dir));
self
}
/// Build a `WasiCtx`, consuming this `WasiCtxBuilder`.
///
/// If any of the arguments or environment variables in this builder cannot be converted into
/// `CString`s, either due to NUL bytes or Unicode conversions, this returns `Error::EILSEQ`.
pub fn build(self) -> Result<WasiCtx> {
// Process arguments and environment variables into `CString`s, failing quickly if they
// contain any NUL bytes, or if conversion from `OsString` fails.
let args = self
.args
.into_iter()
.map(|arg| arg.into_utf8_cstring())
.collect::<Result<Vec<CString>>>()?;
let env = self
.env
.into_iter()
.map(|(k, v)| {
k.into_string().and_then(|mut pair| {
v.into_string().and_then(|v| {
pair.push('=');
pair.push_str(v.as_str());
// We have valid UTF-8, but the keys and values have not yet been checked
// for NULs, so we do a final check here.
CString::new(pair).map_err(|_| Error::EILSEQ)
})
})
})
.collect::<Result<Vec<CString>>>()?;
let mut fds: HashMap<wasi::__wasi_fd_t, FdEntry> = HashMap::new();
// Populate the non-preopen fds.
for (fd, pending) in self.fds {
log::debug!("WasiCtx inserting ({:?}, {:?})", fd, pending);
match pending {
PendingFdEntry::Thunk(f) => {
fds.insert(fd, f()?);
}
PendingFdEntry::File(f) => {
fds.insert(fd, FdEntry::from(f)?);
}
}
}
// Then add the preopen fds. Startup code in the guest starts looking at fd 3 for preopens,
// so we start from there. This variable is initially 2, though, because the loop
// immediately does the increment and check for overflow.
let mut preopen_fd: wasi::__wasi_fd_t = 2;
for (guest_path, dir) in self.preopens {
// We do the increment at the beginning of the loop body, so that we don't overflow
// unnecessarily if we have exactly the maximum number of file descriptors.
preopen_fd = preopen_fd.checked_add(1).ok_or(Error::ENFILE)?;
if !dir.metadata()?.is_dir() {
return Err(Error::EBADF);
}
// We don't currently allow setting file descriptors other than 0-2, but this will avoid
// collisions if we restore that functionality in the future.
while fds.contains_key(&preopen_fd) {
preopen_fd = preopen_fd.checked_add(1).ok_or(Error::ENFILE)?;
}
let mut fe = FdEntry::from(dir)?;
fe.preopen_path = Some(guest_path);
log::debug!("WasiCtx inserting ({:?}, {:?})", preopen_fd, fe);
fds.insert(preopen_fd, fe);
log::debug!("WasiCtx fds = {:?}", fds);
}
Ok(WasiCtx { args, env, fds })
}
}
#[derive(Debug)]
pub struct WasiCtx {
fds: HashMap<wasi::__wasi_fd_t, FdEntry>,
pub(crate) args: Vec<CString>,
pub(crate) 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<S: AsRef<[u8]>>(args: impl IntoIterator<Item = S>) -> Result<Self> {
WasiCtxBuilder::new()
.args(args)
.inherit_stdio()
.inherit_env()
.build()
}
/// Check if `WasiCtx` contains the specified raw WASI `fd`.
pub(crate) unsafe fn contains_fd_entry(&self, fd: wasi::__wasi_fd_t) -> bool {
self.fds.contains_key(&fd)
}
/// Get an immutable `FdEntry` corresponding to the specified raw WASI `fd`.
pub(crate) unsafe fn get_fd_entry(&self, fd: wasi::__wasi_fd_t) -> Result<&FdEntry> {
self.fds.get(&fd).ok_or(Error::EBADF)
}
/// Get a mutable `FdEntry` corresponding to the specified raw WASI `fd`.
pub(crate) unsafe fn get_fd_entry_mut(
&mut self,
fd: wasi::__wasi_fd_t,
) -> Result<&mut FdEntry> {
self.fds.get_mut(&fd).ok_or(Error::EBADF)
}
/// Insert the specified `FdEntry` into the `WasiCtx` object.
///
/// The `FdEntry` will automatically get another free raw WASI `fd` assigned. Note that
/// the two subsequent free raw WASI `fd`s do not have to be stored contiguously.
pub(crate) fn insert_fd_entry(&mut self, fe: FdEntry) -> Result<wasi::__wasi_fd_t> {
// Never insert where stdio handles are expected to be.
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(Error::EMFILE);
}
}
self.fds.insert(fd, fe);
Ok(fd)
}
/// Insert the specified `FdEntry` with the specified raw WASI `fd` key into the `WasiCtx`
/// object.
pub(crate) fn insert_fd_entry_at(
&mut self,
fd: wasi::__wasi_fd_t,
fe: FdEntry,
) -> Option<FdEntry> {
self.fds.insert(fd, fe)
}
/// Remove `FdEntry` corresponding to the specified raw WASI `fd` from the `WasiCtx` object.
pub(crate) fn remove_fd_entry(&mut self, fd: wasi::__wasi_fd_t) -> Result<FdEntry> {
self.fds.remove(&fd).ok_or(Error::EBADF)
}
}

View File

@@ -0,0 +1,279 @@
// Due to https://github.com/rust-lang/rust/issues/64247
#![allow(clippy::use_self)]
use crate::old::snapshot_0::wasi;
use std::convert::Infallible;
use std::num::TryFromIntError;
use std::{fmt, str};
use thiserror::Error;
#[derive(Clone, Copy, Debug, Error, Eq, PartialEq)]
#[repr(u16)]
pub enum WasiError {
ESUCCESS = wasi::__WASI_ERRNO_SUCCESS,
E2BIG = wasi::__WASI_ERRNO_2BIG,
EACCES = wasi::__WASI_ERRNO_ACCES,
EADDRINUSE = wasi::__WASI_ERRNO_ADDRINUSE,
EADDRNOTAVAIL = wasi::__WASI_ERRNO_ADDRNOTAVAIL,
EAFNOSUPPORT = wasi::__WASI_ERRNO_AFNOSUPPORT,
EAGAIN = wasi::__WASI_ERRNO_AGAIN,
EALREADY = wasi::__WASI_ERRNO_ALREADY,
EBADF = wasi::__WASI_ERRNO_BADF,
EBADMSG = wasi::__WASI_ERRNO_BADMSG,
EBUSY = wasi::__WASI_ERRNO_BUSY,
ECANCELED = wasi::__WASI_ERRNO_CANCELED,
ECHILD = wasi::__WASI_ERRNO_CHILD,
ECONNABORTED = wasi::__WASI_ERRNO_CONNABORTED,
ECONNREFUSED = wasi::__WASI_ERRNO_CONNREFUSED,
ECONNRESET = wasi::__WASI_ERRNO_CONNRESET,
EDEADLK = wasi::__WASI_ERRNO_DEADLK,
EDESTADDRREQ = wasi::__WASI_ERRNO_DESTADDRREQ,
EDOM = wasi::__WASI_ERRNO_DOM,
EDQUOT = wasi::__WASI_ERRNO_DQUOT,
EEXIST = wasi::__WASI_ERRNO_EXIST,
EFAULT = wasi::__WASI_ERRNO_FAULT,
EFBIG = wasi::__WASI_ERRNO_FBIG,
EHOSTUNREACH = wasi::__WASI_ERRNO_HOSTUNREACH,
EIDRM = wasi::__WASI_ERRNO_IDRM,
EILSEQ = wasi::__WASI_ERRNO_ILSEQ,
EINPROGRESS = wasi::__WASI_ERRNO_INPROGRESS,
EINTR = wasi::__WASI_ERRNO_INTR,
EINVAL = wasi::__WASI_ERRNO_INVAL,
EIO = wasi::__WASI_ERRNO_IO,
EISCONN = wasi::__WASI_ERRNO_ISCONN,
EISDIR = wasi::__WASI_ERRNO_ISDIR,
ELOOP = wasi::__WASI_ERRNO_LOOP,
EMFILE = wasi::__WASI_ERRNO_MFILE,
EMLINK = wasi::__WASI_ERRNO_MLINK,
EMSGSIZE = wasi::__WASI_ERRNO_MSGSIZE,
EMULTIHOP = wasi::__WASI_ERRNO_MULTIHOP,
ENAMETOOLONG = wasi::__WASI_ERRNO_NAMETOOLONG,
ENETDOWN = wasi::__WASI_ERRNO_NETDOWN,
ENETRESET = wasi::__WASI_ERRNO_NETRESET,
ENETUNREACH = wasi::__WASI_ERRNO_NETUNREACH,
ENFILE = wasi::__WASI_ERRNO_NFILE,
ENOBUFS = wasi::__WASI_ERRNO_NOBUFS,
ENODEV = wasi::__WASI_ERRNO_NODEV,
ENOENT = wasi::__WASI_ERRNO_NOENT,
ENOEXEC = wasi::__WASI_ERRNO_NOEXEC,
ENOLCK = wasi::__WASI_ERRNO_NOLCK,
ENOLINK = wasi::__WASI_ERRNO_NOLINK,
ENOMEM = wasi::__WASI_ERRNO_NOMEM,
ENOMSG = wasi::__WASI_ERRNO_NOMSG,
ENOPROTOOPT = wasi::__WASI_ERRNO_NOPROTOOPT,
ENOSPC = wasi::__WASI_ERRNO_NOSPC,
ENOSYS = wasi::__WASI_ERRNO_NOSYS,
ENOTCONN = wasi::__WASI_ERRNO_NOTCONN,
ENOTDIR = wasi::__WASI_ERRNO_NOTDIR,
ENOTEMPTY = wasi::__WASI_ERRNO_NOTEMPTY,
ENOTRECOVERABLE = wasi::__WASI_ERRNO_NOTRECOVERABLE,
ENOTSOCK = wasi::__WASI_ERRNO_NOTSOCK,
ENOTSUP = wasi::__WASI_ERRNO_NOTSUP,
ENOTTY = wasi::__WASI_ERRNO_NOTTY,
ENXIO = wasi::__WASI_ERRNO_NXIO,
EOVERFLOW = wasi::__WASI_ERRNO_OVERFLOW,
EOWNERDEAD = wasi::__WASI_ERRNO_OWNERDEAD,
EPERM = wasi::__WASI_ERRNO_PERM,
EPIPE = wasi::__WASI_ERRNO_PIPE,
EPROTO = wasi::__WASI_ERRNO_PROTO,
EPROTONOSUPPORT = wasi::__WASI_ERRNO_PROTONOSUPPORT,
EPROTOTYPE = wasi::__WASI_ERRNO_PROTOTYPE,
ERANGE = wasi::__WASI_ERRNO_RANGE,
EROFS = wasi::__WASI_ERRNO_ROFS,
ESPIPE = wasi::__WASI_ERRNO_SPIPE,
ESRCH = wasi::__WASI_ERRNO_SRCH,
ESTALE = wasi::__WASI_ERRNO_STALE,
ETIMEDOUT = wasi::__WASI_ERRNO_TIMEDOUT,
ETXTBSY = wasi::__WASI_ERRNO_TXTBSY,
EXDEV = wasi::__WASI_ERRNO_XDEV,
ENOTCAPABLE = wasi::__WASI_ERRNO_NOTCAPABLE,
}
impl WasiError {
pub fn as_raw_errno(self) -> wasi::__wasi_errno_t {
self as wasi::__wasi_errno_t
}
}
impl fmt::Display for WasiError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
_ => write!(f, "{:?}", self),
}
}
}
#[derive(Debug, Error)]
pub enum Error {
Wasi(WasiError),
Io(std::io::Error),
#[cfg(unix)]
Nix(nix::Error),
#[cfg(windows)]
Win(winx::winerror::WinError),
}
impl From<WasiError> for Error {
fn from(err: WasiError) -> Self {
Self::Wasi(err)
}
}
#[cfg(unix)]
impl From<nix::Error> for Error {
fn from(err: nix::Error) -> Self {
Self::Nix(err)
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Self::Io(err)
}
}
impl From<TryFromIntError> for Error {
fn from(_: TryFromIntError) -> Self {
Self::Wasi(WasiError::EOVERFLOW)
}
}
impl From<Infallible> for Error {
fn from(_: Infallible) -> Self {
unreachable!()
}
}
impl From<str::Utf8Error> for Error {
fn from(_: str::Utf8Error) -> Self {
Self::Wasi(WasiError::EILSEQ)
}
}
#[cfg(windows)]
impl From<winx::winerror::WinError> for Error {
fn from(err: winx::winerror::WinError) -> Self {
Self::Win(err)
}
}
impl Error {
pub(crate) fn as_wasi_errno(&self) -> wasi::__wasi_errno_t {
match self {
Self::Wasi(no) => no.as_raw_errno(),
Self::Io(e) => errno_from_ioerror(e.to_owned()),
#[cfg(unix)]
Self::Nix(err) => err
.as_errno()
.map_or_else(
|| {
log::debug!("Unknown nix errno: {}", err);
Self::ENOSYS
},
crate::old::snapshot_0::sys::host_impl::errno_from_nix,
)
.as_wasi_errno(),
#[cfg(windows)]
Self::Win(err) => crate::old::snapshot_0::sys::host_impl::errno_from_win(*err),
}
}
pub const ESUCCESS: Self = Error::Wasi(WasiError::ESUCCESS);
pub const E2BIG: Self = Error::Wasi(WasiError::E2BIG);
pub const EACCES: Self = Error::Wasi(WasiError::EACCES);
pub const EADDRINUSE: Self = Error::Wasi(WasiError::EADDRINUSE);
pub const EADDRNOTAVAIL: Self = Error::Wasi(WasiError::EADDRNOTAVAIL);
pub const EAFNOSUPPORT: Self = Error::Wasi(WasiError::EAFNOSUPPORT);
pub const EAGAIN: Self = Error::Wasi(WasiError::EAGAIN);
pub const EALREADY: Self = Error::Wasi(WasiError::EALREADY);
pub const EBADF: Self = Error::Wasi(WasiError::EBADF);
pub const EBADMSG: Self = Error::Wasi(WasiError::EBADMSG);
pub const EBUSY: Self = Error::Wasi(WasiError::EBUSY);
pub const ECANCELED: Self = Error::Wasi(WasiError::ECANCELED);
pub const ECHILD: Self = Error::Wasi(WasiError::ECHILD);
pub const ECONNABORTED: Self = Error::Wasi(WasiError::ECONNABORTED);
pub const ECONNREFUSED: Self = Error::Wasi(WasiError::ECONNREFUSED);
pub const ECONNRESET: Self = Error::Wasi(WasiError::ECONNRESET);
pub const EDEADLK: Self = Error::Wasi(WasiError::EDEADLK);
pub const EDESTADDRREQ: Self = Error::Wasi(WasiError::EDESTADDRREQ);
pub const EDOM: Self = Error::Wasi(WasiError::EDOM);
pub const EDQUOT: Self = Error::Wasi(WasiError::EDQUOT);
pub const EEXIST: Self = Error::Wasi(WasiError::EEXIST);
pub const EFAULT: Self = Error::Wasi(WasiError::EFAULT);
pub const EFBIG: Self = Error::Wasi(WasiError::EFBIG);
pub const EHOSTUNREACH: Self = Error::Wasi(WasiError::EHOSTUNREACH);
pub const EIDRM: Self = Error::Wasi(WasiError::EIDRM);
pub const EILSEQ: Self = Error::Wasi(WasiError::EILSEQ);
pub const EINPROGRESS: Self = Error::Wasi(WasiError::EINPROGRESS);
pub const EINTR: Self = Error::Wasi(WasiError::EINTR);
pub const EINVAL: Self = Error::Wasi(WasiError::EINVAL);
pub const EIO: Self = Error::Wasi(WasiError::EIO);
pub const EISCONN: Self = Error::Wasi(WasiError::EISCONN);
pub const EISDIR: Self = Error::Wasi(WasiError::EISDIR);
pub const ELOOP: Self = Error::Wasi(WasiError::ELOOP);
pub const EMFILE: Self = Error::Wasi(WasiError::EMFILE);
pub const EMLINK: Self = Error::Wasi(WasiError::EMLINK);
pub const EMSGSIZE: Self = Error::Wasi(WasiError::EMSGSIZE);
pub const EMULTIHOP: Self = Error::Wasi(WasiError::EMULTIHOP);
pub const ENAMETOOLONG: Self = Error::Wasi(WasiError::ENAMETOOLONG);
pub const ENETDOWN: Self = Error::Wasi(WasiError::ENETDOWN);
pub const ENETRESET: Self = Error::Wasi(WasiError::ENETRESET);
pub const ENETUNREACH: Self = Error::Wasi(WasiError::ENETUNREACH);
pub const ENFILE: Self = Error::Wasi(WasiError::ENFILE);
pub const ENOBUFS: Self = Error::Wasi(WasiError::ENOBUFS);
pub const ENODEV: Self = Error::Wasi(WasiError::ENODEV);
pub const ENOENT: Self = Error::Wasi(WasiError::ENOENT);
pub const ENOEXEC: Self = Error::Wasi(WasiError::ENOEXEC);
pub const ENOLCK: Self = Error::Wasi(WasiError::ENOLCK);
pub const ENOLINK: Self = Error::Wasi(WasiError::ENOLINK);
pub const ENOMEM: Self = Error::Wasi(WasiError::ENOMEM);
pub const ENOMSG: Self = Error::Wasi(WasiError::ENOMSG);
pub const ENOPROTOOPT: Self = Error::Wasi(WasiError::ENOPROTOOPT);
pub const ENOSPC: Self = Error::Wasi(WasiError::ENOSPC);
pub const ENOSYS: Self = Error::Wasi(WasiError::ENOSYS);
pub const ENOTCONN: Self = Error::Wasi(WasiError::ENOTCONN);
pub const ENOTDIR: Self = Error::Wasi(WasiError::ENOTDIR);
pub const ENOTEMPTY: Self = Error::Wasi(WasiError::ENOTEMPTY);
pub const ENOTRECOVERABLE: Self = Error::Wasi(WasiError::ENOTRECOVERABLE);
pub const ENOTSOCK: Self = Error::Wasi(WasiError::ENOTSOCK);
pub const ENOTSUP: Self = Error::Wasi(WasiError::ENOTSUP);
pub const ENOTTY: Self = Error::Wasi(WasiError::ENOTTY);
pub const ENXIO: Self = Error::Wasi(WasiError::ENXIO);
pub const EOVERFLOW: Self = Error::Wasi(WasiError::EOVERFLOW);
pub const EOWNERDEAD: Self = Error::Wasi(WasiError::EOWNERDEAD);
pub const EPERM: Self = Error::Wasi(WasiError::EPERM);
pub const EPIPE: Self = Error::Wasi(WasiError::EPIPE);
pub const EPROTO: Self = Error::Wasi(WasiError::EPROTO);
pub const EPROTONOSUPPORT: Self = Error::Wasi(WasiError::EPROTONOSUPPORT);
pub const EPROTOTYPE: Self = Error::Wasi(WasiError::EPROTOTYPE);
pub const ERANGE: Self = Error::Wasi(WasiError::ERANGE);
pub const EROFS: Self = Error::Wasi(WasiError::EROFS);
pub const ESPIPE: Self = Error::Wasi(WasiError::ESPIPE);
pub const ESRCH: Self = Error::Wasi(WasiError::ESRCH);
pub const ESTALE: Self = Error::Wasi(WasiError::ESTALE);
pub const ETIMEDOUT: Self = Error::Wasi(WasiError::ETIMEDOUT);
pub const ETXTBSY: Self = Error::Wasi(WasiError::ETXTBSY);
pub const EXDEV: Self = Error::Wasi(WasiError::EXDEV);
pub const ENOTCAPABLE: Self = Error::Wasi(WasiError::ENOTCAPABLE);
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Io(e) => e.fmt(f),
Self::Wasi(e) => e.fmt(f),
#[cfg(unix)]
Self::Nix(e) => e.fmt(f),
#[cfg(windows)]
Self::Win(e) => e.fmt(f),
}
}
}
fn errno_from_ioerror(e: &std::io::Error) -> wasi::__wasi_errno_t {
match e.raw_os_error() {
Some(code) => crate::old::snapshot_0::sys::errno_from_host(code),
None => {
log::debug!("Inconvertible OS error: {}", e);
wasi::__WASI_ERRNO_IO
}
}
}

View File

@@ -0,0 +1,186 @@
use crate::old::snapshot_0::sys::dev_null;
use crate::old::snapshot_0::sys::fdentry_impl::{determine_type_and_access_rights, OsFile};
use crate::old::snapshot_0::{wasi, Error, Result};
use std::path::PathBuf;
use std::{fs, io};
#[derive(Debug)]
pub(crate) enum Descriptor {
OsFile(OsFile),
Stdin,
Stdout,
Stderr,
}
impl Descriptor {
pub(crate) fn as_file(&self) -> Result<&OsFile> {
match self {
Self::OsFile(file) => Ok(file),
_ => Err(Error::EBADF),
}
}
pub(crate) fn as_file_mut(&mut self) -> Result<&mut OsFile> {
match self {
Self::OsFile(file) => Ok(file),
_ => Err(Error::EBADF),
}
}
pub(crate) fn is_file(&self) -> bool {
match self {
Self::OsFile(_) => true,
_ => false,
}
}
#[allow(unused)]
pub(crate) fn is_stdin(&self) -> bool {
match self {
Self::Stdin => true,
_ => false,
}
}
#[allow(unused)]
pub(crate) fn is_stdout(&self) -> bool {
match self {
Self::Stdout => true,
_ => false,
}
}
#[allow(unused)]
pub(crate) fn is_stderr(&self) -> bool {
match self {
Self::Stderr => true,
_ => false,
}
}
}
/// An abstraction struct serving as a wrapper for a host `Descriptor` object which requires
/// certain base rights `rights_base` and inheriting rights `rights_inheriting` in order to be
/// accessed correctly.
///
/// Here, the `descriptor` field stores the host `Descriptor` object (such as a file descriptor, or
/// stdin handle), and accessing it can only be done via the provided `FdEntry::as_descriptor` and
/// `FdEntry::as_descriptor_mut` methods which require a set of base and inheriting rights to be
/// specified, verifying whether the stored `Descriptor` object is valid for the rights specified.
#[derive(Debug)]
pub(crate) struct FdEntry {
pub(crate) file_type: wasi::__wasi_filetype_t,
descriptor: Descriptor,
pub(crate) rights_base: wasi::__wasi_rights_t,
pub(crate) rights_inheriting: wasi::__wasi_rights_t,
pub(crate) preopen_path: Option<PathBuf>,
// TODO: directories
}
impl FdEntry {
pub(crate) fn from(file: fs::File) -> Result<Self> {
unsafe { determine_type_and_access_rights(&file) }.map(
|(file_type, rights_base, rights_inheriting)| Self {
file_type,
descriptor: Descriptor::OsFile(OsFile::from(file)),
rights_base,
rights_inheriting,
preopen_path: None,
},
)
}
pub(crate) fn duplicate(file: &fs::File) -> Result<Self> {
Self::from(file.try_clone()?)
}
pub(crate) fn duplicate_stdin() -> Result<Self> {
unsafe { determine_type_and_access_rights(&io::stdin()) }.map(
|(file_type, rights_base, rights_inheriting)| Self {
file_type,
descriptor: Descriptor::Stdin,
rights_base,
rights_inheriting,
preopen_path: None,
},
)
}
pub(crate) fn duplicate_stdout() -> Result<Self> {
unsafe { determine_type_and_access_rights(&io::stdout()) }.map(
|(file_type, rights_base, rights_inheriting)| Self {
file_type,
descriptor: Descriptor::Stdout,
rights_base,
rights_inheriting,
preopen_path: None,
},
)
}
pub(crate) fn duplicate_stderr() -> Result<Self> {
unsafe { determine_type_and_access_rights(&io::stderr()) }.map(
|(file_type, rights_base, rights_inheriting)| Self {
file_type,
descriptor: Descriptor::Stderr,
rights_base,
rights_inheriting,
preopen_path: None,
},
)
}
pub(crate) fn null() -> Result<Self> {
Self::from(dev_null()?)
}
/// Convert this `FdEntry` into a host `Descriptor` object provided the specified
/// `rights_base` and `rights_inheriting` rights are set on this `FdEntry` object.
///
/// The `FdEntry` can only be converted into a valid `Descriptor` object if
/// the specified set of base rights `rights_base`, and inheriting rights `rights_inheriting`
/// is a subset of rights attached to this `FdEntry`. The check is performed using
/// `FdEntry::validate_rights` method. If the check fails, `Error::ENOTCAPABLE` is returned.
pub(crate) fn as_descriptor(
&self,
rights_base: wasi::__wasi_rights_t,
rights_inheriting: wasi::__wasi_rights_t,
) -> Result<&Descriptor> {
self.validate_rights(rights_base, rights_inheriting)?;
Ok(&self.descriptor)
}
/// Convert this `FdEntry` into a mutable host `Descriptor` object provided the specified
/// `rights_base` and `rights_inheriting` rights are set on this `FdEntry` object.
///
/// The `FdEntry` can only be converted into a valid `Descriptor` object if
/// the specified set of base rights `rights_base`, and inheriting rights `rights_inheriting`
/// is a subset of rights attached to this `FdEntry`. The check is performed using
/// `FdEntry::validate_rights` method. If the check fails, `Error::ENOTCAPABLE` is returned.
pub(crate) fn as_descriptor_mut(
&mut self,
rights_base: wasi::__wasi_rights_t,
rights_inheriting: wasi::__wasi_rights_t,
) -> Result<&mut Descriptor> {
self.validate_rights(rights_base, rights_inheriting)?;
Ok(&mut self.descriptor)
}
/// Check if this `FdEntry` object satisfies the specified base rights `rights_base`, and
/// inheriting rights `rights_inheriting`; i.e., if rights attached to this `FdEntry` object
/// are a superset.
///
/// Upon unsuccessful check, `Error::ENOTCAPABLE` is returned.
fn validate_rights(
&self,
rights_base: wasi::__wasi_rights_t,
rights_inheriting: wasi::__wasi_rights_t,
) -> Result<()> {
if !self.rights_base & rights_base != 0 || !self.rights_inheriting & rights_inheriting != 0
{
Err(Error::ENOTCAPABLE)
} else {
Ok(())
}
}
}

View File

@@ -0,0 +1,20 @@
use crate::old::snapshot_0::{Error, Result};
use std::convert::TryInto;
use std::str;
use std::time::{SystemTime, UNIX_EPOCH};
pub(crate) fn systemtime_to_timestamp(st: SystemTime) -> Result<u64> {
st.duration_since(UNIX_EPOCH)
.map_err(|_| Error::EINVAL)? // date earlier than UNIX_EPOCH
.as_nanos()
.try_into()
.map_err(Into::into) // u128 doesn't fit into u64
}
/// Creates not-owned WASI path from byte slice.
///
/// NB WASI spec requires bytes to be valid UTF-8. Otherwise,
/// `__WASI_ERRNO_ILSEQ` error is returned.
pub(crate) fn path_from_slice<'a>(s: &'a [u8]) -> Result<&'a str> {
str::from_utf8(s).map_err(|_| Error::EILSEQ)
}

View File

@@ -0,0 +1,109 @@
//! WASI host types. These are types that contain raw pointers and `usize`
//! values, and so are platform-specific.
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use crate::old::snapshot_0::wasi::*;
use std::{io, slice};
use wig::witx_host_types;
witx_host_types!("old/snapshot_0" "wasi_unstable");
pub(crate) unsafe fn ciovec_to_host(ciovec: &__wasi_ciovec_t) -> io::IoSlice {
let slice = slice::from_raw_parts(ciovec.buf as *const u8, ciovec.buf_len);
io::IoSlice::new(slice)
}
pub(crate) unsafe fn iovec_to_host_mut(iovec: &mut __wasi_iovec_t) -> io::IoSliceMut {
let slice = slice::from_raw_parts_mut(iovec.buf as *mut u8, iovec.buf_len);
io::IoSliceMut::new(slice)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn bindgen_test_layout___wasi_prestat_t() {
assert_eq!(
::std::mem::size_of::<__wasi_prestat_t>(),
16usize,
concat!("Size of: ", stringify!(__wasi_prestat_t))
);
assert_eq!(
::std::mem::align_of::<__wasi_prestat_t>(),
8usize,
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 },
8usize,
concat!(
"Offset of field: ",
stringify!(__wasi_prestat_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_dir_t>(),
8usize,
concat!("Size of: ", stringify!(__wasi_prestat_dir_t))
);
assert_eq!(
::std::mem::align_of::<__wasi_prestat_dir_t>(),
8usize,
concat!("Alignment of ", stringify!(__wasi_prestat_dir_t))
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<__wasi_prestat_dir_t>())).pr_name_len as *const _ as usize
},
0usize,
concat!(
"Offset of field: ",
stringify!(__wasi_prestat_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_u_t>(),
8usize,
concat!("Size of: ", stringify!(__wasi_prestat_u_t))
);
assert_eq!(
::std::mem::align_of::<__wasi_prestat_u_t>(),
8usize,
concat!("Alignment of ", stringify!(__wasi_prestat_u_t))
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<__wasi_prestat_u_t>())).dir as *const _ as usize },
0usize,
concat!(
"Offset of field: ",
stringify!(__wasi_prestat_u_t),
"::",
stringify!(dir)
)
);
}
}

View File

@@ -0,0 +1,256 @@
#![allow(non_camel_case_types)]
use crate::old::snapshot_0::ctx::WasiCtx;
use crate::old::snapshot_0::{hostcalls_impl, wasi, wasi32};
hostcalls_old! {
pub unsafe fn fd_close(wasi_ctx: &mut WasiCtx, fd: wasi::__wasi_fd_t,) -> wasi::__wasi_errno_t;
pub unsafe fn fd_datasync(wasi_ctx: &WasiCtx, fd: wasi::__wasi_fd_t,) -> wasi::__wasi_errno_t;
pub unsafe fn fd_pread(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
fd: wasi::__wasi_fd_t,
iovs_ptr: wasi32::uintptr_t,
iovs_len: wasi32::size_t,
offset: wasi::__wasi_filesize_t,
nread: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn fd_pwrite(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
fd: wasi::__wasi_fd_t,
iovs_ptr: wasi32::uintptr_t,
iovs_len: wasi32::size_t,
offset: wasi::__wasi_filesize_t,
nwritten: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn fd_read(
wasi_ctx: &mut WasiCtx,
memory: &mut [u8],
fd: wasi::__wasi_fd_t,
iovs_ptr: wasi32::uintptr_t,
iovs_len: wasi32::size_t,
nread: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn fd_renumber(
wasi_ctx: &mut WasiCtx,
from: wasi::__wasi_fd_t,
to: wasi::__wasi_fd_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn fd_seek(
wasi_ctx: &mut WasiCtx,
memory: &mut [u8],
fd: wasi::__wasi_fd_t,
offset: wasi::__wasi_filedelta_t,
whence: wasi::__wasi_whence_t,
newoffset: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn fd_tell(
wasi_ctx: &mut WasiCtx,
memory: &mut [u8],
fd: wasi::__wasi_fd_t,
newoffset: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn fd_fdstat_get(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
fd: wasi::__wasi_fd_t,
fdstat_ptr: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn fd_fdstat_set_flags(
wasi_ctx: &WasiCtx,
fd: wasi::__wasi_fd_t,
fdflags: wasi::__wasi_fdflags_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn fd_fdstat_set_rights(
wasi_ctx: &mut WasiCtx,
fd: wasi::__wasi_fd_t,
fs_rights_base: wasi::__wasi_rights_t,
fs_rights_inheriting: wasi::__wasi_rights_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn fd_sync(wasi_ctx: &WasiCtx, fd: wasi::__wasi_fd_t,) -> wasi::__wasi_errno_t;
pub unsafe fn fd_write(
wasi_ctx: &mut WasiCtx,
memory: &mut [u8],
fd: wasi::__wasi_fd_t,
iovs_ptr: wasi32::uintptr_t,
iovs_len: wasi32::size_t,
nwritten: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn fd_advise(
wasi_ctx: &WasiCtx,
fd: wasi::__wasi_fd_t,
offset: wasi::__wasi_filesize_t,
len: wasi::__wasi_filesize_t,
advice: wasi::__wasi_advice_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn fd_allocate(
wasi_ctx: &WasiCtx,
fd: wasi::__wasi_fd_t,
offset: wasi::__wasi_filesize_t,
len: wasi::__wasi_filesize_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn path_create_directory(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
dirfd: wasi::__wasi_fd_t,
path_ptr: wasi32::uintptr_t,
path_len: wasi32::size_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn path_link(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
old_dirfd: wasi::__wasi_fd_t,
old_flags: wasi::__wasi_lookupflags_t,
old_path_ptr: wasi32::uintptr_t,
old_path_len: wasi32::size_t,
new_dirfd: wasi::__wasi_fd_t,
new_path_ptr: wasi32::uintptr_t,
new_path_len: wasi32::size_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn path_open(
wasi_ctx: &mut WasiCtx,
memory: &mut [u8],
dirfd: wasi::__wasi_fd_t,
dirflags: wasi::__wasi_lookupflags_t,
path_ptr: wasi32::uintptr_t,
path_len: wasi32::size_t,
oflags: wasi::__wasi_oflags_t,
fs_rights_base: wasi::__wasi_rights_t,
fs_rights_inheriting: wasi::__wasi_rights_t,
fs_flags: wasi::__wasi_fdflags_t,
fd_out_ptr: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn fd_readdir(
wasi_ctx: &mut WasiCtx,
memory: &mut [u8],
fd: wasi::__wasi_fd_t,
buf: wasi32::uintptr_t,
buf_len: wasi32::size_t,
cookie: wasi::__wasi_dircookie_t,
buf_used: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn path_readlink(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
dirfd: wasi::__wasi_fd_t,
path_ptr: wasi32::uintptr_t,
path_len: wasi32::size_t,
buf_ptr: wasi32::uintptr_t,
buf_len: wasi32::size_t,
buf_used: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn path_rename(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
old_dirfd: wasi::__wasi_fd_t,
old_path_ptr: wasi32::uintptr_t,
old_path_len: wasi32::size_t,
new_dirfd: wasi::__wasi_fd_t,
new_path_ptr: wasi32::uintptr_t,
new_path_len: wasi32::size_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn fd_filestat_get(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
fd: wasi::__wasi_fd_t,
filestat_ptr: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn fd_filestat_set_times(
wasi_ctx: &WasiCtx,
fd: wasi::__wasi_fd_t,
st_atim: wasi::__wasi_timestamp_t,
st_mtim: wasi::__wasi_timestamp_t,
fst_flags: wasi::__wasi_fstflags_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn fd_filestat_set_size(
wasi_ctx: &WasiCtx,
fd: wasi::__wasi_fd_t,
st_size: wasi::__wasi_filesize_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn path_filestat_get(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
dirfd: wasi::__wasi_fd_t,
dirflags: wasi::__wasi_lookupflags_t,
path_ptr: wasi32::uintptr_t,
path_len: wasi32::size_t,
filestat_ptr: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn path_filestat_set_times(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
dirfd: wasi::__wasi_fd_t,
dirflags: wasi::__wasi_lookupflags_t,
path_ptr: wasi32::uintptr_t,
path_len: wasi32::size_t,
st_atim: wasi::__wasi_timestamp_t,
st_mtim: wasi::__wasi_timestamp_t,
fst_flags: wasi::__wasi_fstflags_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn path_symlink(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
old_path_ptr: wasi32::uintptr_t,
old_path_len: wasi32::size_t,
dirfd: wasi::__wasi_fd_t,
new_path_ptr: wasi32::uintptr_t,
new_path_len: wasi32::size_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn path_unlink_file(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
dirfd: wasi::__wasi_fd_t,
path_ptr: wasi32::uintptr_t,
path_len: wasi32::size_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn path_remove_directory(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
dirfd: wasi::__wasi_fd_t,
path_ptr: wasi32::uintptr_t,
path_len: wasi32::size_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn fd_prestat_get(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
fd: wasi::__wasi_fd_t,
prestat_ptr: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn fd_prestat_dir_name(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
fd: wasi::__wasi_fd_t,
path_ptr: wasi32::uintptr_t,
path_len: wasi32::size_t,
) -> wasi::__wasi_errno_t;
}

View File

@@ -0,0 +1,82 @@
#![allow(non_camel_case_types)]
use crate::old::snapshot_0::ctx::WasiCtx;
use crate::old::snapshot_0::{hostcalls_impl, wasi, wasi32};
use log::trace;
use wasi_common_cbindgen::wasi_common_cbindgen_old;
#[wasi_common_cbindgen_old]
pub unsafe fn proc_exit(rval: wasi::__wasi_exitcode_t) {
trace!("proc_exit(rval={:?})", rval);
// TODO: Rather than call std::process::exit here, we should trigger a
// stack unwind similar to a trap.
std::process::exit(rval as i32);
}
#[wasi_common_cbindgen_old]
pub unsafe fn proc_raise(
_wasi_ctx: &WasiCtx,
_memory: &mut [u8],
_sig: wasi::__wasi_signal_t,
) -> wasi::__wasi_errno_t {
unimplemented!("proc_raise")
}
hostcalls_old! {
pub unsafe fn args_get(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
argv_ptr: wasi32::uintptr_t,
argv_buf: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn args_sizes_get(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
argc_ptr: wasi32::uintptr_t,
argv_buf_size_ptr: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn environ_get(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
environ_ptr: wasi32::uintptr_t,
environ_buf: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn environ_sizes_get(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
environ_count_ptr: wasi32::uintptr_t,
environ_size_ptr: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn random_get(
memory: &mut [u8],
buf_ptr: wasi32::uintptr_t,
buf_len: wasi32::size_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn clock_res_get(
memory: &mut [u8],
clock_id: wasi::__wasi_clockid_t,
resolution_ptr: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn clock_time_get(
memory: &mut [u8],
clock_id: wasi::__wasi_clockid_t,
precision: wasi::__wasi_timestamp_t,
time_ptr: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn poll_oneoff(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
input: wasi32::uintptr_t,
output: wasi32::uintptr_t,
nsubscriptions: wasi32::size_t,
nevents: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t;
pub unsafe fn sched_yield() -> wasi::__wasi_errno_t;
}

View File

@@ -0,0 +1,7 @@
mod fs;
mod misc;
mod sock;
pub use self::fs::*;
pub use self::misc::*;
pub use self::sock::*;

View File

@@ -0,0 +1,43 @@
#![allow(non_camel_case_types)]
#![allow(unused_unsafe)]
#![allow(unused)]
use crate::old::snapshot_0::ctx::WasiCtx;
use crate::old::snapshot_0::{wasi, wasi32};
use wasi_common_cbindgen::wasi_common_cbindgen_old;
#[wasi_common_cbindgen_old]
pub unsafe fn sock_recv(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
sock: wasi::__wasi_fd_t,
ri_data: wasi32::uintptr_t,
ri_data_len: wasi32::size_t,
ri_flags: wasi::__wasi_riflags_t,
ro_datalen: wasi32::uintptr_t,
ro_flags: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t {
unimplemented!("sock_recv")
}
#[wasi_common_cbindgen_old]
pub unsafe fn sock_send(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
sock: wasi::__wasi_fd_t,
si_data: wasi32::uintptr_t,
si_data_len: wasi32::size_t,
si_flags: wasi::__wasi_siflags_t,
so_datalen: wasi32::uintptr_t,
) -> wasi::__wasi_errno_t {
unimplemented!("sock_send")
}
#[wasi_common_cbindgen_old]
pub unsafe fn sock_shutdown(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
sock: wasi::__wasi_fd_t,
how: wasi::__wasi_sdflags_t,
) -> wasi::__wasi_errno_t {
unimplemented!("sock_shutdown")
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,213 @@
#![allow(non_camel_case_types)]
use crate::old::snapshot_0::sys::host_impl;
use crate::old::snapshot_0::sys::hostcalls_impl::fs_helpers::*;
use crate::old::snapshot_0::{fdentry::FdEntry, wasi, Error, Result};
use std::fs::File;
use std::path::{Component, Path};
#[derive(Debug)]
pub(crate) struct PathGet {
dirfd: File,
path: String,
}
impl PathGet {
pub(crate) fn dirfd(&self) -> &File {
&self.dirfd
}
pub(crate) fn path(&self) -> &str {
&self.path
}
}
/// Normalizes a path to ensure that the target path is located under the directory provided.
///
/// This is a workaround for not having Capsicum support in the OS.
pub(crate) fn path_get(
fe: &FdEntry,
rights_base: wasi::__wasi_rights_t,
rights_inheriting: wasi::__wasi_rights_t,
dirflags: wasi::__wasi_lookupflags_t,
path: &str,
needs_final_component: bool,
) -> Result<PathGet> {
const MAX_SYMLINK_EXPANSIONS: usize = 128;
if path.contains('\0') {
// if contains NUL, return EILSEQ
return Err(Error::EILSEQ);
}
if fe.file_type != wasi::__WASI_FILETYPE_DIRECTORY {
// if `dirfd` doesn't refer to a directory, return `ENOTDIR`.
return Err(Error::ENOTDIR);
}
let dirfd = fe
.as_descriptor(rights_base, rights_inheriting)?
.as_file()?
.try_clone()?;
// Stack of directory file descriptors. Index 0 always corresponds with the directory provided
// to this function. Entering a directory causes a file descriptor to be pushed, while handling
// ".." entries causes an entry to be popped. Index 0 cannot be popped, as this would imply
// escaping the base directory.
let mut dir_stack = vec![dirfd];
// Stack of paths left to process. This is initially the `path` argument to this function, but
// any symlinks we encounter are processed by pushing them on the stack.
let mut path_stack = vec![path.to_owned()];
// Track the number of symlinks we've expanded, so we can return `ELOOP` after too many.
let mut symlink_expansions = 0;
// TODO: rewrite this using a custom posix path type, with a component iterator that respects
// trailing slashes. This version does way too much allocation, and is way too fiddly.
loop {
match path_stack.pop() {
Some(cur_path) => {
log::debug!("path_get cur_path = {:?}", cur_path);
let ends_with_slash = cur_path.ends_with('/');
let mut components = Path::new(&cur_path).components();
let head = match components.next() {
None => return Err(Error::ENOENT),
Some(p) => p,
};
let tail = components.as_path();
if tail.components().next().is_some() {
let mut tail = host_impl::path_from_host(tail.as_os_str())?;
if ends_with_slash {
tail.push('/');
}
path_stack.push(tail);
}
log::debug!("path_get path_stack = {:?}", path_stack);
match head {
Component::Prefix(_) | Component::RootDir => {
// path is absolute!
return Err(Error::ENOTCAPABLE);
}
Component::CurDir => {
// "." so skip
}
Component::ParentDir => {
// ".." so pop a dir
let _ = dir_stack.pop().ok_or(Error::ENOTCAPABLE)?;
// we're not allowed to pop past the original directory
if dir_stack.is_empty() {
return Err(Error::ENOTCAPABLE);
}
}
Component::Normal(head) => {
let mut head = host_impl::path_from_host(head)?;
if ends_with_slash {
// preserve trailing slash
head.push('/');
}
if !path_stack.is_empty() || (ends_with_slash && !needs_final_component) {
match openat(dir_stack.last().ok_or(Error::ENOTCAPABLE)?, &head) {
Ok(new_dir) => {
dir_stack.push(new_dir);
}
Err(e) => {
match e.as_wasi_errno() {
wasi::__WASI_ERRNO_LOOP
| wasi::__WASI_ERRNO_MLINK
| wasi::__WASI_ERRNO_NOTDIR =>
// Check to see if it was a symlink. Linux indicates
// this with ENOTDIR because of the O_DIRECTORY flag.
{
// attempt symlink expansion
let mut link_path = readlinkat(
dir_stack.last().ok_or(Error::ENOTCAPABLE)?,
&head,
)?;
symlink_expansions += 1;
if symlink_expansions > MAX_SYMLINK_EXPANSIONS {
return Err(Error::ELOOP);
}
if head.ends_with('/') {
link_path.push('/');
}
log::debug!(
"attempted symlink expansion link_path={:?}",
link_path
);
path_stack.push(link_path);
}
_ => {
return Err(e);
}
}
}
}
continue;
} else if ends_with_slash
|| (dirflags & wasi::__WASI_LOOKUPFLAGS_SYMLINK_FOLLOW) != 0
{
// if there's a trailing slash, or if `LOOKUP_SYMLINK_FOLLOW` is set, attempt
// symlink expansion
match readlinkat(dir_stack.last().ok_or(Error::ENOTCAPABLE)?, &head) {
Ok(mut link_path) => {
symlink_expansions += 1;
if symlink_expansions > MAX_SYMLINK_EXPANSIONS {
return Err(Error::ELOOP);
}
if head.ends_with('/') {
link_path.push('/');
}
log::debug!(
"attempted symlink expansion link_path={:?}",
link_path
);
path_stack.push(link_path);
continue;
}
Err(e) => {
if e.as_wasi_errno() != wasi::__WASI_ERRNO_INVAL
&& e.as_wasi_errno() != wasi::__WASI_ERRNO_NOENT
// this handles the cases when trying to link to
// a destination that already exists, and the target
// path contains a slash
&& e.as_wasi_errno() != wasi::__WASI_ERRNO_NOTDIR
{
return Err(e);
}
}
}
}
// not a symlink, so we're done;
return Ok(PathGet {
dirfd: dir_stack.pop().ok_or(Error::ENOTCAPABLE)?,
path: head,
});
}
}
}
None => {
// no further components to process. means we've hit a case like "." or "a/..", or if the
// input path has trailing slashes and `needs_final_component` is not set
return Ok(PathGet {
dirfd: dir_stack.pop().ok_or(Error::ENOTCAPABLE)?,
path: String::from("."),
});
}
}
}
}

View File

@@ -0,0 +1,317 @@
#![allow(non_camel_case_types)]
use crate::old::snapshot_0::ctx::WasiCtx;
use crate::old::snapshot_0::fdentry::Descriptor;
use crate::old::snapshot_0::memory::*;
use crate::old::snapshot_0::sys::hostcalls_impl;
use crate::old::snapshot_0::{wasi, wasi32, Error, Result};
use log::trace;
use std::convert::TryFrom;
pub(crate) fn args_get(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
argv_ptr: wasi32::uintptr_t,
argv_buf: wasi32::uintptr_t,
) -> Result<()> {
trace!(
"args_get(argv_ptr={:#x?}, argv_buf={:#x?})",
argv_ptr,
argv_buf,
);
let mut argv_buf_offset = 0;
let mut argv = vec![];
for arg in &wasi_ctx.args {
let arg_bytes = arg.as_bytes_with_nul();
let arg_ptr = argv_buf + argv_buf_offset;
enc_slice_of_u8(memory, arg_bytes, arg_ptr)?;
argv.push(arg_ptr);
let len = wasi32::uintptr_t::try_from(arg_bytes.len())?;
argv_buf_offset = argv_buf_offset.checked_add(len).ok_or(Error::EOVERFLOW)?;
}
enc_slice_of_wasi32_uintptr(memory, argv.as_slice(), argv_ptr)
}
pub(crate) fn args_sizes_get(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
argc_ptr: wasi32::uintptr_t,
argv_buf_size_ptr: wasi32::uintptr_t,
) -> Result<()> {
trace!(
"args_sizes_get(argc_ptr={:#x?}, argv_buf_size_ptr={:#x?})",
argc_ptr,
argv_buf_size_ptr,
);
let argc = wasi_ctx.args.len();
let argv_size = wasi_ctx
.args
.iter()
.map(|arg| arg.as_bytes_with_nul().len())
.sum();
trace!(" | *argc_ptr={:?}", argc);
enc_usize_byref(memory, argc_ptr, argc)?;
trace!(" | *argv_buf_size_ptr={:?}", argv_size);
enc_usize_byref(memory, argv_buf_size_ptr, argv_size)
}
pub(crate) fn environ_get(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
environ_ptr: wasi32::uintptr_t,
environ_buf: wasi32::uintptr_t,
) -> Result<()> {
trace!(
"environ_get(environ_ptr={:#x?}, environ_buf={:#x?})",
environ_ptr,
environ_buf,
);
let mut environ_buf_offset = 0;
let mut environ = vec![];
for pair in &wasi_ctx.env {
let env_bytes = pair.as_bytes_with_nul();
let env_ptr = environ_buf + environ_buf_offset;
enc_slice_of_u8(memory, env_bytes, env_ptr)?;
environ.push(env_ptr);
let len = wasi32::uintptr_t::try_from(env_bytes.len())?;
environ_buf_offset = environ_buf_offset
.checked_add(len)
.ok_or(Error::EOVERFLOW)?;
}
enc_slice_of_wasi32_uintptr(memory, environ.as_slice(), environ_ptr)
}
pub(crate) fn environ_sizes_get(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
environ_count_ptr: wasi32::uintptr_t,
environ_size_ptr: wasi32::uintptr_t,
) -> Result<()> {
trace!(
"environ_sizes_get(environ_count_ptr={:#x?}, environ_size_ptr={:#x?})",
environ_count_ptr,
environ_size_ptr,
);
let environ_count = wasi_ctx.env.len();
let environ_size = wasi_ctx
.env
.iter()
.try_fold(0, |acc: u32, pair| {
acc.checked_add(pair.as_bytes_with_nul().len() as u32)
})
.ok_or(Error::EOVERFLOW)?;
trace!(" | *environ_count_ptr={:?}", environ_count);
enc_usize_byref(memory, environ_count_ptr, environ_count)?;
trace!(" | *environ_size_ptr={:?}", environ_size);
enc_usize_byref(memory, environ_size_ptr, environ_size as usize)
}
pub(crate) fn random_get(
memory: &mut [u8],
buf_ptr: wasi32::uintptr_t,
buf_len: wasi32::size_t,
) -> Result<()> {
use rand::{thread_rng, RngCore};
trace!("random_get(buf_ptr={:#x?}, buf_len={:?})", buf_ptr, buf_len);
let buf = dec_slice_of_mut_u8(memory, buf_ptr, buf_len)?;
thread_rng().fill_bytes(buf);
Ok(())
}
pub(crate) fn clock_res_get(
memory: &mut [u8],
clock_id: wasi::__wasi_clockid_t,
resolution_ptr: wasi32::uintptr_t,
) -> Result<()> {
trace!(
"clock_res_get(clock_id={:?}, resolution_ptr={:#x?})",
clock_id,
resolution_ptr,
);
let resolution = hostcalls_impl::clock_res_get(clock_id)?;
trace!(" | *resolution_ptr={:?}", resolution);
enc_timestamp_byref(memory, resolution_ptr, resolution)
}
pub(crate) fn clock_time_get(
memory: &mut [u8],
clock_id: wasi::__wasi_clockid_t,
precision: wasi::__wasi_timestamp_t,
time_ptr: wasi32::uintptr_t,
) -> Result<()> {
trace!(
"clock_time_get(clock_id={:?}, precision={:?}, time_ptr={:#x?})",
clock_id,
precision,
time_ptr,
);
let time = hostcalls_impl::clock_time_get(clock_id)?;
trace!(" | *time_ptr={:?}", time);
enc_timestamp_byref(memory, time_ptr, time)
}
pub(crate) fn sched_yield() -> Result<()> {
trace!("sched_yield()");
std::thread::yield_now();
Ok(())
}
pub(crate) fn poll_oneoff(
wasi_ctx: &WasiCtx,
memory: &mut [u8],
input: wasi32::uintptr_t,
output: wasi32::uintptr_t,
nsubscriptions: wasi32::size_t,
nevents: wasi32::uintptr_t,
) -> Result<()> {
trace!(
"poll_oneoff(input={:#x?}, output={:#x?}, nsubscriptions={}, nevents={:#x?})",
input,
output,
nsubscriptions,
nevents,
);
if u64::from(nsubscriptions) > wasi::__wasi_filesize_t::max_value() {
return Err(Error::EINVAL);
}
enc_int_byref(memory, nevents, 0)?;
let subscriptions = dec_subscriptions(memory, input, nsubscriptions)?;
let mut events = Vec::new();
let mut timeout: Option<ClockEventData> = None;
let mut fd_events = Vec::new();
for subscription in subscriptions {
match subscription.r#type {
wasi::__WASI_EVENTTYPE_CLOCK => {
let clock = unsafe { subscription.u.clock };
let delay = wasi_clock_to_relative_ns_delay(clock)?;
log::debug!("poll_oneoff event.u.clock = {:?}", clock);
log::debug!("poll_oneoff delay = {:?}ns", delay);
let current = ClockEventData {
delay,
userdata: subscription.userdata,
};
let timeout = timeout.get_or_insert(current);
if current.delay < timeout.delay {
*timeout = current;
}
}
r#type
if r#type == wasi::__WASI_EVENTTYPE_FD_READ
|| r#type == wasi::__WASI_EVENTTYPE_FD_WRITE =>
{
let wasi_fd = unsafe { subscription.u.fd_readwrite.file_descriptor };
let rights = if r#type == wasi::__WASI_EVENTTYPE_FD_READ {
wasi::__WASI_RIGHTS_FD_READ
} else {
wasi::__WASI_RIGHTS_FD_WRITE
};
match unsafe {
wasi_ctx
.get_fd_entry(wasi_fd)
.and_then(|fe| fe.as_descriptor(rights, 0))
} {
Ok(descriptor) => fd_events.push(FdEventData {
descriptor,
r#type: subscription.r#type,
userdata: subscription.userdata,
}),
Err(err) => {
let event = wasi::__wasi_event_t {
userdata: subscription.userdata,
r#type,
error: err.as_wasi_errno(),
u: wasi::__wasi_event_u_t {
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
nbytes: 0,
flags: 0,
},
},
};
events.push(event);
}
};
}
_ => unreachable!(),
}
}
log::debug!("poll_oneoff timeout = {:?}", timeout);
log::debug!("poll_oneoff fd_events = {:?}", fd_events);
hostcalls_impl::poll_oneoff(timeout, fd_events, &mut events)?;
let events_count = u32::try_from(events.len()).map_err(|_| Error::EOVERFLOW)?;
enc_events(memory, output, nsubscriptions, events)?;
trace!(" | *nevents={:?}", events_count);
enc_int_byref(memory, nevents, events_count)
}
fn wasi_clock_to_relative_ns_delay(wasi_clock: wasi::__wasi_subscription_clock_t) -> Result<u128> {
use std::time::SystemTime;
if wasi_clock.flags != wasi::__WASI_SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME {
return Ok(u128::from(wasi_clock.timeout));
}
let now: u128 = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.map_err(|_| Error::ENOTCAPABLE)?
.as_nanos();
let deadline = u128::from(wasi_clock.timeout);
Ok(deadline.saturating_sub(now))
}
#[derive(Debug, Copy, Clone)]
pub(crate) struct ClockEventData {
pub(crate) delay: u128, // delay is expressed in nanoseconds
pub(crate) userdata: wasi::__wasi_userdata_t,
}
#[derive(Debug)]
pub(crate) struct FdEventData<'a> {
pub(crate) descriptor: &'a Descriptor,
pub(crate) r#type: wasi::__wasi_eventtype_t,
pub(crate) userdata: wasi::__wasi_userdata_t,
}

View File

@@ -0,0 +1,7 @@
mod fs;
mod fs_helpers;
mod misc;
pub(crate) use self::fs::*;
pub(crate) use self::fs_helpers::PathGet;
pub(crate) use self::misc::*;

View File

@@ -0,0 +1,480 @@
//! Functions to store and load data to and from wasm linear memory,
//! transforming them from and to host data types.
//!
//! Endianness concerns are completely encapsulated in this file, so
//! that users outside this file holding a `wasi::*` value never need
//! to consider what endianness it's in. Inside this file,
//! wasm linear-memory-ordered values are called "raw" values, and
//! are not held for long durations.
#![allow(unused)]
use crate::old::snapshot_0::{host, wasi, wasi32, Error, Result};
use num::PrimInt;
use std::convert::TryFrom;
use std::mem::{align_of, size_of};
use std::{ptr, slice};
fn dec_ptr(memory: &[u8], ptr: wasi32::uintptr_t, len: usize) -> Result<*const u8> {
// check for overflow
let checked_len = (ptr as usize).checked_add(len).ok_or(Error::EFAULT)?;
// translate the pointer
memory
.get(ptr as usize..checked_len)
.ok_or(Error::EFAULT)
.map(|mem| mem.as_ptr())
}
fn dec_ptr_mut(memory: &mut [u8], ptr: wasi32::uintptr_t, len: usize) -> Result<*mut u8> {
// check for overflow
let checked_len = (ptr as usize).checked_add(len).ok_or(Error::EFAULT)?;
// translate the pointer
memory
.get_mut(ptr as usize..checked_len)
.ok_or(Error::EFAULT)
.map(|mem| mem.as_mut_ptr())
}
fn dec_ptr_to<'memory, T>(memory: &'memory [u8], ptr: wasi32::uintptr_t) -> Result<&'memory T> {
// check that the ptr is aligned
if ptr as usize % align_of::<T>() != 0 {
return Err(Error::EINVAL);
}
dec_ptr(memory, ptr, size_of::<T>()).map(|p| unsafe { &*(p as *const T) })
}
fn dec_ptr_to_mut<'memory, T>(
memory: &'memory mut [u8],
ptr: wasi32::uintptr_t,
) -> Result<&'memory mut T> {
// check that the ptr is aligned
if ptr as usize % align_of::<T>() != 0 {
return Err(Error::EINVAL);
}
dec_ptr_mut(memory, ptr, size_of::<T>()).map(|p| unsafe { &mut *(p as *mut T) })
}
/// This function does not perform endianness conversions!
fn dec_raw_byref<T>(memory: &[u8], ptr: wasi32::uintptr_t) -> Result<T> {
dec_ptr_to::<T>(memory, ptr).map(|p| unsafe { ptr::read(p) })
}
/// This function does not perform endianness conversions!
fn enc_raw_byref<T>(memory: &mut [u8], ptr: wasi32::uintptr_t, t: T) -> Result<()> {
dec_ptr_to_mut::<T>(memory, ptr).map(|p| unsafe { ptr::write(p, t) })
}
pub(crate) fn dec_int_byref<T>(memory: &[u8], ptr: wasi32::uintptr_t) -> Result<T>
where
T: PrimInt,
{
dec_raw_byref::<T>(memory, ptr).map(|i| PrimInt::from_le(i))
}
pub(crate) fn enc_int_byref<T>(memory: &mut [u8], ptr: wasi32::uintptr_t, t: T) -> Result<()>
where
T: PrimInt,
{
enc_raw_byref::<T>(memory, ptr, PrimInt::to_le(t))
}
fn check_slice_of<T>(ptr: wasi32::uintptr_t, len: wasi32::size_t) -> Result<(usize, usize)> {
// check alignment, and that length doesn't overflow
if ptr as usize % align_of::<T>() != 0 {
return Err(Error::EINVAL);
}
let len = dec_usize(len);
let len_bytes = if let Some(len) = size_of::<T>().checked_mul(len) {
len
} else {
return Err(Error::EOVERFLOW);
};
Ok((len, len_bytes))
}
fn dec_raw_slice_of<'memory, T>(
memory: &'memory [u8],
ptr: wasi32::uintptr_t,
len: wasi32::size_t,
) -> Result<&'memory [T]> {
let (len, len_bytes) = check_slice_of::<T>(ptr, len)?;
let ptr = dec_ptr(memory, ptr, len_bytes)? as *const T;
Ok(unsafe { slice::from_raw_parts(ptr, len) })
}
fn dec_raw_slice_of_mut<'memory, T>(
memory: &'memory mut [u8],
ptr: wasi32::uintptr_t,
len: wasi32::size_t,
) -> Result<&'memory mut [T]> {
let (len, len_bytes) = check_slice_of::<T>(ptr, len)?;
let ptr = dec_ptr_mut(memory, ptr, len_bytes)? as *mut T;
Ok(unsafe { slice::from_raw_parts_mut(ptr, len) })
}
fn raw_slice_for_enc<'memory, T>(
memory: &'memory mut [u8],
slice: &[T],
ptr: wasi32::uintptr_t,
) -> Result<&'memory mut [T]> {
// check alignment
if ptr as usize % align_of::<T>() != 0 {
return Err(Error::EINVAL);
}
// check that length doesn't overflow
let len_bytes = if let Some(len) = size_of::<T>().checked_mul(slice.len()) {
len
} else {
return Err(Error::EOVERFLOW);
};
// get the pointer into guest memory
let ptr = dec_ptr_mut(memory, ptr, len_bytes)? as *mut T;
Ok(unsafe { slice::from_raw_parts_mut(ptr, slice.len()) })
}
pub(crate) fn dec_slice_of_u8<'memory>(
memory: &'memory [u8],
ptr: wasi32::uintptr_t,
len: wasi32::size_t,
) -> Result<&'memory [u8]> {
dec_raw_slice_of::<u8>(memory, ptr, len)
}
pub(crate) fn dec_slice_of_mut_u8<'memory>(
memory: &'memory mut [u8],
ptr: wasi32::uintptr_t,
len: wasi32::size_t,
) -> Result<&'memory mut [u8]> {
dec_raw_slice_of_mut::<u8>(memory, ptr, len)
}
pub(crate) fn enc_slice_of_u8(
memory: &mut [u8],
slice: &[u8],
ptr: wasi32::uintptr_t,
) -> Result<()> {
let output = raw_slice_for_enc::<u8>(memory, slice, ptr)?;
output.copy_from_slice(slice);
Ok(())
}
pub(crate) fn enc_slice_of_wasi32_uintptr(
memory: &mut [u8],
slice: &[wasi32::uintptr_t],
ptr: wasi32::uintptr_t,
) -> Result<()> {
let mut output_iter = raw_slice_for_enc::<wasi32::uintptr_t>(memory, slice, ptr)?.into_iter();
for p in slice {
*output_iter.next().unwrap() = PrimInt::to_le(*p);
}
Ok(())
}
macro_rules! dec_enc_scalar {
($ty:ident, $dec_byref:ident, $enc_byref:ident) => {
pub(crate) fn $dec_byref(memory: &mut [u8], ptr: wasi32::uintptr_t) -> Result<wasi::$ty> {
dec_int_byref::<wasi::$ty>(memory, ptr)
}
pub(crate) fn $enc_byref(
memory: &mut [u8],
ptr: wasi32::uintptr_t,
x: wasi::$ty,
) -> Result<()> {
enc_int_byref::<wasi::$ty>(memory, ptr, x)
}
};
}
pub(crate) fn dec_ciovec_slice(
memory: &[u8],
ptr: wasi32::uintptr_t,
len: wasi32::size_t,
) -> Result<Vec<host::__wasi_ciovec_t>> {
let raw_slice = dec_raw_slice_of::<wasi32::__wasi_ciovec_t>(memory, ptr, len)?;
raw_slice
.iter()
.map(|raw_iov| {
let len = dec_usize(PrimInt::from_le(raw_iov.buf_len));
let buf = PrimInt::from_le(raw_iov.buf);
Ok(host::__wasi_ciovec_t {
buf: dec_ptr(memory, buf, len)? as *const u8,
buf_len: len,
})
})
.collect()
}
pub(crate) fn dec_iovec_slice(
memory: &[u8],
ptr: wasi32::uintptr_t,
len: wasi32::size_t,
) -> Result<Vec<host::__wasi_iovec_t>> {
let raw_slice = dec_raw_slice_of::<wasi32::__wasi_iovec_t>(memory, ptr, len)?;
raw_slice
.iter()
.map(|raw_iov| {
let len = dec_usize(PrimInt::from_le(raw_iov.buf_len));
let buf = PrimInt::from_le(raw_iov.buf);
Ok(host::__wasi_iovec_t {
buf: dec_ptr(memory, buf, len)? as *mut u8,
buf_len: len,
})
})
.collect()
}
dec_enc_scalar!(__wasi_clockid_t, dec_clockid_byref, enc_clockid_byref);
dec_enc_scalar!(__wasi_errno_t, dec_errno_byref, enc_errno_byref);
dec_enc_scalar!(__wasi_exitcode_t, dec_exitcode_byref, enc_exitcode_byref);
dec_enc_scalar!(__wasi_fd_t, dec_fd_byref, enc_fd_byref);
dec_enc_scalar!(__wasi_fdflags_t, dec_fdflags_byref, enc_fdflags_byref);
dec_enc_scalar!(__wasi_device_t, dev_device_byref, enc_device_byref);
dec_enc_scalar!(__wasi_inode_t, dev_inode_byref, enc_inode_byref);
dec_enc_scalar!(__wasi_linkcount_t, dev_linkcount_byref, enc_linkcount_byref);
pub(crate) fn dec_filestat_byref(
memory: &mut [u8],
filestat_ptr: wasi32::uintptr_t,
) -> Result<wasi::__wasi_filestat_t> {
let raw = dec_raw_byref::<wasi::__wasi_filestat_t>(memory, filestat_ptr)?;
Ok(wasi::__wasi_filestat_t {
dev: PrimInt::from_le(raw.dev),
ino: PrimInt::from_le(raw.ino),
filetype: PrimInt::from_le(raw.filetype),
nlink: PrimInt::from_le(raw.nlink),
size: PrimInt::from_le(raw.size),
atim: PrimInt::from_le(raw.atim),
mtim: PrimInt::from_le(raw.mtim),
ctim: PrimInt::from_le(raw.ctim),
})
}
pub(crate) fn enc_filestat_byref(
memory: &mut [u8],
filestat_ptr: wasi32::uintptr_t,
filestat: wasi::__wasi_filestat_t,
) -> Result<()> {
let raw = wasi::__wasi_filestat_t {
dev: PrimInt::to_le(filestat.dev),
ino: PrimInt::to_le(filestat.ino),
filetype: PrimInt::to_le(filestat.filetype),
nlink: PrimInt::to_le(filestat.nlink),
size: PrimInt::to_le(filestat.size),
atim: PrimInt::to_le(filestat.atim),
mtim: PrimInt::to_le(filestat.mtim),
ctim: PrimInt::to_le(filestat.ctim),
};
enc_raw_byref::<wasi::__wasi_filestat_t>(memory, filestat_ptr, raw)
}
pub(crate) fn dec_fdstat_byref(
memory: &mut [u8],
fdstat_ptr: wasi32::uintptr_t,
) -> Result<wasi::__wasi_fdstat_t> {
let raw = dec_raw_byref::<wasi::__wasi_fdstat_t>(memory, fdstat_ptr)?;
Ok(wasi::__wasi_fdstat_t {
fs_filetype: PrimInt::from_le(raw.fs_filetype),
fs_flags: PrimInt::from_le(raw.fs_flags),
fs_rights_base: PrimInt::from_le(raw.fs_rights_base),
fs_rights_inheriting: PrimInt::from_le(raw.fs_rights_inheriting),
})
}
pub(crate) fn enc_fdstat_byref(
memory: &mut [u8],
fdstat_ptr: wasi32::uintptr_t,
fdstat: wasi::__wasi_fdstat_t,
) -> Result<()> {
let raw = wasi::__wasi_fdstat_t {
fs_filetype: PrimInt::to_le(fdstat.fs_filetype),
fs_flags: PrimInt::to_le(fdstat.fs_flags),
fs_rights_base: PrimInt::to_le(fdstat.fs_rights_base),
fs_rights_inheriting: PrimInt::to_le(fdstat.fs_rights_inheriting),
};
enc_raw_byref::<wasi::__wasi_fdstat_t>(memory, fdstat_ptr, raw)
}
dec_enc_scalar!(__wasi_filedelta_t, dec_filedelta_byref, enc_filedelta_byref);
dec_enc_scalar!(__wasi_filesize_t, dec_filesize_byref, enc_filesize_byref);
dec_enc_scalar!(__wasi_filetype_t, dec_filetype_byref, enc_filetype_byref);
dec_enc_scalar!(
__wasi_lookupflags_t,
dec_lookupflags_byref,
enc_lookupflags_byref
);
dec_enc_scalar!(__wasi_oflags_t, dec_oflags_byref, enc_oflags_byref);
pub(crate) fn dec_prestat_byref(
memory: &mut [u8],
prestat_ptr: wasi32::uintptr_t,
) -> Result<host::__wasi_prestat_t> {
let raw = dec_raw_byref::<wasi32::__wasi_prestat_t>(memory, prestat_ptr)?;
match PrimInt::from_le(raw.pr_type) {
wasi::__WASI_PREOPENTYPE_DIR => Ok(host::__wasi_prestat_t {
pr_type: wasi::__WASI_PREOPENTYPE_DIR,
u: host::__wasi_prestat_u_t {
dir: host::__wasi_prestat_dir_t {
pr_name_len: dec_usize(PrimInt::from_le(unsafe { raw.u.dir.pr_name_len })),
},
},
}),
_ => Err(Error::EINVAL),
}
}
pub(crate) fn enc_prestat_byref(
memory: &mut [u8],
prestat_ptr: wasi32::uintptr_t,
prestat: host::__wasi_prestat_t,
) -> Result<()> {
let raw = match prestat.pr_type {
wasi::__WASI_PREOPENTYPE_DIR => Ok(wasi32::__wasi_prestat_t {
pr_type: PrimInt::to_le(wasi::__WASI_PREOPENTYPE_DIR),
u: wasi32::__wasi_prestat_u_t {
dir: wasi32::__wasi_prestat_dir_t {
pr_name_len: enc_usize(unsafe { prestat.u.dir.pr_name_len }),
},
},
}),
_ => Err(Error::EINVAL),
}?;
enc_raw_byref::<wasi32::__wasi_prestat_t>(memory, prestat_ptr, raw)
}
dec_enc_scalar!(__wasi_rights_t, dec_rights_byref, enc_rights_byref);
dec_enc_scalar!(__wasi_timestamp_t, dec_timestamp_byref, enc_timestamp_byref);
pub(crate) fn dec_usize(size: wasi32::size_t) -> usize {
usize::try_from(size).unwrap()
}
pub(crate) fn enc_usize(size: usize) -> wasi32::size_t {
wasi32::size_t::try_from(size).unwrap()
}
pub(crate) fn enc_usize_byref(
memory: &mut [u8],
usize_ptr: wasi32::uintptr_t,
host_usize: usize,
) -> Result<()> {
enc_int_byref::<wasi32::size_t>(memory, usize_ptr, enc_usize(host_usize))
}
dec_enc_scalar!(__wasi_whence_t, dec_whence_byref, enc_whence_byref);
dec_enc_scalar!(
__wasi_subclockflags_t,
dec_subclockflags_byref,
enc_subclockflags_byref
);
dec_enc_scalar!(
__wasi_eventrwflags_t,
dec_eventrwflags_byref,
enc_eventrwflags_byref
);
dec_enc_scalar!(__wasi_eventtype_t, dec_eventtype_byref, enc_eventtype_byref);
dec_enc_scalar!(__wasi_userdata_t, dec_userdata_byref, enc_userdata_byref);
pub(crate) fn dec_subscriptions(
memory: &mut [u8],
input: wasi32::uintptr_t,
nsubscriptions: wasi32::size_t,
) -> Result<Vec<wasi::__wasi_subscription_t>> {
let raw_input_slice =
dec_raw_slice_of::<wasi::__wasi_subscription_t>(memory, input, nsubscriptions)?;
raw_input_slice
.into_iter()
.map(|raw_subscription| {
let userdata = PrimInt::from_le(raw_subscription.userdata);
let r#type = PrimInt::from_le(raw_subscription.r#type);
let raw_u = raw_subscription.u;
let u = match r#type {
wasi::__WASI_EVENTTYPE_CLOCK => wasi::__wasi_subscription_u_t {
clock: unsafe {
wasi::__wasi_subscription_clock_t {
identifier: PrimInt::from_le(raw_u.clock.identifier),
id: PrimInt::from_le(raw_u.clock.id),
timeout: PrimInt::from_le(raw_u.clock.timeout),
precision: PrimInt::from_le(raw_u.clock.precision),
flags: PrimInt::from_le(raw_u.clock.flags),
}
},
},
wasi::__WASI_EVENTTYPE_FD_READ | wasi::__WASI_EVENTTYPE_FD_WRITE => {
wasi::__wasi_subscription_u_t {
fd_readwrite: wasi::__wasi_subscription_fd_readwrite_t {
file_descriptor: PrimInt::from_le(unsafe {
raw_u.fd_readwrite.file_descriptor
}),
},
}
}
_ => return Err(Error::EINVAL),
};
Ok(wasi::__wasi_subscription_t {
userdata,
r#type,
u,
})
})
.collect::<Result<Vec<_>>>()
}
pub(crate) fn enc_events(
memory: &mut [u8],
output: wasi32::uintptr_t,
nsubscriptions: wasi32::size_t,
events: Vec<wasi::__wasi_event_t>,
) -> Result<()> {
let mut raw_output_iter =
dec_raw_slice_of_mut::<wasi::__wasi_event_t>(memory, output, nsubscriptions)?.into_iter();
for event in events.iter() {
*raw_output_iter
.next()
.expect("the number of events cannot exceed the number of subscriptions") = {
let fd_readwrite = unsafe { event.u.fd_readwrite };
wasi::__wasi_event_t {
userdata: PrimInt::to_le(event.userdata),
r#type: PrimInt::to_le(event.r#type),
error: PrimInt::to_le(event.error),
u: wasi::__wasi_event_u_t {
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
nbytes: PrimInt::to_le(fd_readwrite.nbytes),
flags: PrimInt::to_le(fd_readwrite.flags),
},
},
}
};
}
Ok(())
}
dec_enc_scalar!(__wasi_advice_t, dec_advice_byref, enc_advice_byref);
dec_enc_scalar!(__wasi_fstflags_t, dec_fstflags_byref, enc_fstflags_byref);
dec_enc_scalar!(__wasi_dircookie_t, dec_dircookie_byref, enc_dircookie_byref);

View File

@@ -0,0 +1,16 @@
mod ctx;
mod error;
mod fdentry;
mod helpers;
mod host;
pub mod hostcalls;
mod hostcalls_impl;
mod memory;
mod sys;
pub mod wasi;
pub mod wasi32;
pub use ctx::{WasiCtx, WasiCtxBuilder};
pub type Error = error::Error;
pub type Result<T> = std::result::Result<T, Error>;

View File

@@ -0,0 +1,23 @@
use crate::old::snapshot_0::wasi;
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(unix)] {
mod unix;
pub(crate) use self::unix::*;
pub(crate) fn errno_from_host(err: i32) -> wasi::__wasi_errno_t {
host_impl::errno_from_nix(nix::errno::from_i32(err)).as_wasi_errno()
}
} else if #[cfg(windows)] {
mod windows;
pub(crate) use self::windows::*;
pub use self::windows::preopen_dir;
pub(crate) fn errno_from_host(err: i32) -> wasi::__wasi_errno_t {
host_impl::errno_from_win(winx::winerror::WinError::from_u32(err as u32))
}
} else {
compile_error!("wasi-common doesn't compile for this platform yet");
}
}

View File

@@ -0,0 +1,103 @@
//! This internal module consists of helper types and functions for dealing
//! with setting the file times specific to BSD-style *nixes.
use super::super::filetime::FileTime;
use cfg_if::cfg_if;
use std::ffi::CStr;
use std::fs::File;
use std::io;
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
cfg_if! {
if #[cfg(any(
target_os = "macos",
target_os = "freebsd",
target_os = "ios",
target_os = "dragonfly"
))] {
pub(crate) const UTIME_NOW: i64 = -1;
pub(crate) const UTIME_OMIT: i64 = -2;
} else if #[cfg(target_os = "openbsd")] {
// These are swapped compared to macos, freebsd, ios, and dragonfly.
// https://github.com/openbsd/src/blob/master/sys/sys/stat.h#L187
pub(crate) const UTIME_NOW: i64 = -2;
pub(crate) const UTIME_OMIT: i64 = -1;
} else if #[cfg(target_os = "netbsd" )] {
// These are the same as for Linux.
// http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/sys/stat.h?rev=1.69&content-type=text/x-cvsweb-markup&only_with_tag=MAIN
pub(crate) const UTIME_NOW: i64 = 1_073_741_823;
pub(crate) const UTIME_OMIT: i64 = 1_073_741_822;
}
}
/// Wrapper for `utimensat` syscall, however, with an added twist such that `utimensat` symbol
/// is firstly resolved (i.e., we check whether it exists on the host), and only used if that is
/// the case. Otherwise, the syscall resorts to a less accurate `utimesat` emulated syscall.
/// The original implementation can be found here: [filetime::unix::macos::set_times]
///
/// [filetime::unix::macos::set_times]: https://github.com/alexcrichton/filetime/blob/master/src/unix/macos.rs#L49
pub(crate) fn utimensat(
dirfd: &File,
path: &str,
atime: FileTime,
mtime: FileTime,
symlink_nofollow: bool,
) -> io::Result<()> {
use super::super::filetime::{to_timespec, utimesat};
use std::ffi::CString;
use std::os::unix::prelude::*;
// Attempt to use the `utimensat` syscall, but if it's not supported by the
// current kernel then fall back to an older syscall.
if let Some(func) = fetch_utimensat() {
let flags = if symlink_nofollow {
libc::AT_SYMLINK_NOFOLLOW
} else {
0
};
let p = CString::new(path.as_bytes())?;
let times = [to_timespec(&atime), to_timespec(&mtime)];
let rc = unsafe { func(dirfd.as_raw_fd(), p.as_ptr(), times.as_ptr(), flags) };
if rc == 0 {
return Ok(());
} else {
return Err(io::Error::last_os_error());
}
}
utimesat(dirfd, path, atime, mtime, symlink_nofollow)
}
/// Wraps `fetch` specifically targetting `utimensat` symbol. If the symbol exists
/// on the host, then returns an `Some(unsafe fn)`.
fn fetch_utimensat() -> Option<
unsafe extern "C" fn(
libc::c_int,
*const libc::c_char,
*const libc::timespec,
libc::c_int,
) -> libc::c_int,
> {
static ADDR: AtomicUsize = AtomicUsize::new(0);
unsafe {
fetch(&ADDR, CStr::from_bytes_with_nul_unchecked(b"utimensat\0"))
.map(|sym| std::mem::transmute(sym))
}
}
/// Fetches a symbol by `name` and stores it in `cache`.
fn fetch(cache: &AtomicUsize, name: &CStr) -> Option<usize> {
match cache.load(SeqCst) {
0 => {}
1 => return None,
n => return Some(n),
}
let sym = unsafe { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) };
let (val, ret) = if sym.is_null() {
(1, None)
} else {
(sym as usize, Some(sym as usize))
};
cache.store(val, SeqCst);
return ret;
}

View File

@@ -0,0 +1,298 @@
use super::osfile::OsFile;
use crate::old::snapshot_0::hostcalls_impl::PathGet;
use crate::old::snapshot_0::sys::host_impl;
use crate::old::snapshot_0::sys::unix::str_to_cstring;
use crate::old::snapshot_0::{wasi, Error, Result};
use nix::libc::{self, c_long, c_void};
use std::convert::TryInto;
use std::fs::File;
use std::os::unix::prelude::AsRawFd;
pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> {
use nix::errno;
use nix::libc::unlinkat;
let path_cstr = str_to_cstring(resolved.path())?;
// nix doesn't expose unlinkat() yet
match unsafe { unlinkat(resolved.dirfd().as_raw_fd(), path_cstr.as_ptr(), 0) } {
0 => Ok(()),
_ => {
let mut e = errno::Errno::last();
// Non-Linux implementations may return EPERM when attempting to remove a
// directory without REMOVEDIR. While that's what POSIX specifies, it's
// less useful. Adjust this to EISDIR. It doesn't matter that this is not
// atomic with the unlinkat, because if the file is removed and a directory
// is created before fstatat sees it, we're racing with that change anyway
// and unlinkat could have legitimately seen the directory if the race had
// turned out differently.
use nix::fcntl::AtFlags;
use nix::sys::stat::{fstatat, SFlag};
if e == errno::Errno::EPERM {
if let Ok(stat) = fstatat(
resolved.dirfd().as_raw_fd(),
resolved.path(),
AtFlags::AT_SYMLINK_NOFOLLOW,
) {
if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFDIR) {
e = errno::Errno::EISDIR;
}
} else {
e = errno::Errno::last();
}
}
Err(host_impl::errno_from_nix(e))
}
}
}
pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
use nix::{errno::Errno, fcntl::AtFlags, libc::symlinkat, sys::stat::fstatat};
let old_path_cstr = str_to_cstring(old_path)?;
let new_path_cstr = str_to_cstring(resolved.path())?;
log::debug!("path_symlink old_path = {:?}", old_path);
log::debug!("path_symlink resolved = {:?}", resolved);
let res = unsafe {
symlinkat(
old_path_cstr.as_ptr(),
resolved.dirfd().as_raw_fd(),
new_path_cstr.as_ptr(),
)
};
if res != 0 {
match Errno::last() {
Errno::ENOTDIR => {
// On BSD, symlinkat returns ENOTDIR when it should in fact
// return a EEXIST. It seems that it gets confused with by
// the trailing slash in the target path. Thus, we strip
// the trailing slash and check if the path exists, and
// adjust the error code appropriately.
let new_path = resolved.path().trim_end_matches('/');
if let Ok(_) = fstatat(
resolved.dirfd().as_raw_fd(),
new_path,
AtFlags::AT_SYMLINK_NOFOLLOW,
) {
Err(Error::EEXIST)
} else {
Err(Error::ENOTDIR)
}
}
x => Err(host_impl::errno_from_nix(x)),
}
} else {
Ok(())
}
}
pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> {
use nix::{errno::Errno, fcntl::AtFlags, libc::renameat, sys::stat::fstatat};
let old_path_cstr = str_to_cstring(resolved_old.path())?;
let new_path_cstr = str_to_cstring(resolved_new.path())?;
let res = unsafe {
renameat(
resolved_old.dirfd().as_raw_fd(),
old_path_cstr.as_ptr(),
resolved_new.dirfd().as_raw_fd(),
new_path_cstr.as_ptr(),
)
};
if res != 0 {
// Currently, this is verified to be correct on macOS, where
// ENOENT can be returned in case when we try to rename a file
// into a name with a trailing slash. On macOS, if the latter does
// not exist, an ENOENT is thrown, whereas on Linux we observe the
// correct behaviour of throwing an ENOTDIR since the destination is
// indeed not a directory.
//
// TODO
// Verify on other BSD-based OSes.
match Errno::last() {
Errno::ENOENT => {
// check if the source path exists
if let Ok(_) = fstatat(
resolved_old.dirfd().as_raw_fd(),
resolved_old.path(),
AtFlags::AT_SYMLINK_NOFOLLOW,
) {
// check if destination contains a trailing slash
if resolved_new.path().contains('/') {
Err(Error::ENOTDIR)
} else {
Err(Error::ENOENT)
}
} else {
Err(Error::ENOENT)
}
}
x => Err(host_impl::errno_from_nix(x)),
}
} else {
Ok(())
}
}
pub(crate) fn fd_readdir(
os_file: &mut OsFile,
host_buf: &mut [u8],
cookie: wasi::__wasi_dircookie_t,
) -> Result<usize> {
use crate::old::snapshot_0::sys::unix::bsd::osfile::DirStream;
use libc::{fdopendir, readdir, rewinddir, seekdir, telldir};
use nix::errno::Errno;
use std::ffi::CStr;
use std::mem::ManuallyDrop;
use std::sync::Mutex;
let dir_stream = match os_file.dir_stream {
Some(ref mut dir_stream) => dir_stream,
None => {
let file = os_file.file.try_clone()?;
let dir_ptr = unsafe { fdopendir(file.as_raw_fd()) };
os_file.dir_stream.get_or_insert(Mutex::new(DirStream {
file: ManuallyDrop::new(file),
dir_ptr,
}))
}
};
let dir_stream = dir_stream.lock().unwrap();
let host_buf_ptr = host_buf.as_mut_ptr();
let host_buf_len = host_buf.len();
if cookie != wasi::__WASI_DIRCOOKIE_START {
unsafe { seekdir(dir_stream.dir_ptr, cookie as c_long) };
} else {
unsafe { rewinddir(dir_stream.dir_ptr) };
}
let mut left = host_buf_len;
let mut host_buf_offset: usize = 0;
loop {
let errno = Errno::last();
let host_entry_ptr = unsafe { readdir(dir_stream.dir_ptr) };
if host_entry_ptr.is_null() {
if errno != Errno::last() {
// TODO Is this correct?
// According to POSIX man (for Linux though!), there was an error
// if the errno value has changed at some point during the sequence
// of readdir calls
return Err(host_impl::errno_from_nix(Errno::last()));
} else {
// Not an error
break;
}
}
let host_entry = unsafe { *host_entry_ptr };
let mut wasi_entry: wasi::__wasi_dirent_t = host_impl::dirent_from_host(&host_entry)?;
// Set d_next manually:
// * on macOS d_seekoff is not set for some reason
// * on FreeBSD d_seekoff doesn't exist; there is d_off but it is
// not equivalent to the value read from telldir call
wasi_entry.d_next = unsafe { telldir(dir_stream.dir_ptr) } as wasi::__wasi_dircookie_t;
log::debug!("fd_readdir host_entry = {:?}", host_entry);
log::debug!("fd_readdir wasi_entry = {:?}", wasi_entry);
let name_len = host_entry.d_namlen.try_into()?;
let required_space = std::mem::size_of_val(&wasi_entry) + name_len;
if required_space > left {
break;
}
let name = unsafe { CStr::from_ptr(host_entry.d_name.as_ptr()) }.to_str()?;
log::debug!("fd_readdir entry name = {}", name);
unsafe {
let ptr = host_buf_ptr.offset(host_buf_offset.try_into()?) as *mut c_void
as *mut wasi::__wasi_dirent_t;
*ptr = wasi_entry;
}
host_buf_offset += std::mem::size_of_val(&wasi_entry);
unsafe {
std::ptr::copy_nonoverlapping(
name.as_ptr(),
host_buf_ptr.offset(host_buf_offset.try_into()?),
name_len,
)
};
host_buf_offset += name_len;
left -= required_space;
}
Ok(host_buf_len - left)
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub(crate) fn fd_advise(
file: &File,
advice: wasi::__wasi_advice_t,
offset: wasi::__wasi_filesize_t,
len: wasi::__wasi_filesize_t,
) -> Result<()> {
use nix::errno::Errno;
match advice {
wasi::__WASI_ADVICE_DONTNEED => return Ok(()),
// unfortunately, the advisory syscall in macOS doesn't take any flags of this
// sort (unlike on Linux), hence, they are left here as a noop
wasi::__WASI_ADVICE_SEQUENTIAL
| wasi::__WASI_ADVICE_WILLNEED
| wasi::__WASI_ADVICE_NOREUSE
| wasi::__WASI_ADVICE_RANDOM
| wasi::__WASI_ADVICE_NORMAL => {}
_ => return Err(Error::EINVAL),
}
// From macOS man pages:
// F_RDADVISE Issue an advisory read async with no copy to user.
//
// The F_RDADVISE command operates on the following structure which holds information passed from
// the user to the system:
//
// struct radvisory {
// off_t ra_offset; /* offset into the file */
// int ra_count; /* size of the read */
// };
let advisory = libc::radvisory {
ra_offset: offset.try_into()?,
ra_count: len.try_into()?,
};
let res = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_RDADVISE, &advisory) };
Errno::result(res).map(|_| ()).map_err(Error::from)
}
// TODO
// It seems that at least some BSDs do support `posix_fadvise`,
// so we should investigate further.
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub(crate) fn fd_advise(
_file: &File,
advice: wasi::__wasi_advice_t,
_offset: wasi::__wasi_filesize_t,
_len: wasi::__wasi_filesize_t,
) -> Result<()> {
match advice {
wasi::__WASI_ADVICE_DONTNEED
| wasi::__WASI_ADVICE_SEQUENTIAL
| wasi::__WASI_ADVICE_WILLNEED
| wasi::__WASI_ADVICE_NOREUSE
| wasi::__WASI_ADVICE_RANDOM
| wasi::__WASI_ADVICE_NORMAL => {}
_ => return Err(Error::EINVAL),
}
Ok(())
}

View File

@@ -0,0 +1,41 @@
pub(crate) mod filetime;
pub(crate) mod hostcalls_impl;
pub(crate) mod osfile;
pub(crate) mod fdentry_impl {
use crate::old::snapshot_0::{sys::host_impl, Result};
use std::os::unix::prelude::AsRawFd;
pub(crate) unsafe fn isatty(fd: &impl AsRawFd) -> Result<bool> {
let res = libc::isatty(fd.as_raw_fd());
if res == 1 {
// isatty() returns 1 if fd is an open file descriptor referring to a terminal...
Ok(true)
} else {
// ... otherwise 0 is returned, and errno is set to indicate the error.
match nix::errno::Errno::last() {
nix::errno::Errno::ENOTTY => Ok(false),
x => Err(host_impl::errno_from_nix(x)),
}
}
}
}
pub(crate) mod host_impl {
use super::super::host_impl::dirent_filetype_from_host;
use crate::old::snapshot_0::{wasi, Result};
pub(crate) const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_SYNC;
pub(crate) fn dirent_from_host(
host_entry: &nix::libc::dirent,
) -> Result<wasi::__wasi_dirent_t> {
let mut entry = unsafe { std::mem::zeroed::<wasi::__wasi_dirent_t>() };
let d_type = dirent_filetype_from_host(host_entry)?;
entry.d_ino = host_entry.d_ino;
entry.d_next = host_entry.d_seekoff;
entry.d_namlen = u32::from(host_entry.d_namlen);
entry.d_type = d_type;
Ok(entry)
}
}

View File

@@ -0,0 +1,52 @@
use std::fs;
use std::mem::ManuallyDrop;
use std::ops::{Deref, DerefMut};
use std::os::unix::prelude::{AsRawFd, RawFd};
use std::sync::Mutex;
#[derive(Debug)]
pub(crate) struct DirStream {
pub(crate) file: ManuallyDrop<fs::File>,
pub(crate) dir_ptr: *mut libc::DIR,
}
impl Drop for DirStream {
fn drop(&mut self) {
unsafe { libc::closedir(self.dir_ptr) };
}
}
#[derive(Debug)]
pub(crate) struct OsFile {
pub(crate) file: fs::File,
pub(crate) dir_stream: Option<Mutex<DirStream>>,
}
impl From<fs::File> for OsFile {
fn from(file: fs::File) -> Self {
Self {
file,
dir_stream: None,
}
}
}
impl AsRawFd for OsFile {
fn as_raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
}
impl Deref for OsFile {
type Target = fs::File;
fn deref(&self) -> &Self::Target {
&self.file
}
}
impl DerefMut for OsFile {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.file
}
}

View File

@@ -0,0 +1,216 @@
// Based on src/dir.rs from nix
#![allow(unused)] // temporarily, until BSD catches up with this change
use crate::old::snapshot_0::hostcalls_impl::FileType;
use libc;
use nix::{errno::Errno, Error, Result};
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
use std::{ffi, ptr};
#[cfg(target_os = "linux")]
use libc::{dirent64 as dirent, readdir64_r as readdir_r};
#[cfg(not(target_os = "linux"))]
use libc::{dirent, readdir_r};
/// An open directory.
///
/// This is a lower-level interface than `std::fs::ReadDir`. Notable differences:
/// * can be opened from a file descriptor (as returned by `openat`, perhaps before knowing
/// if the path represents a file or directory).
/// * implements `AsRawFd`, so it can be passed to `fstat`, `openat`, etc.
/// The file descriptor continues to be owned by the `Dir`, so callers must not keep a `RawFd`
/// after the `Dir` is dropped.
/// * can be iterated through multiple times without closing and reopening the file
/// descriptor. Each iteration rewinds when finished.
/// * returns entries for `.` (current directory) and `..` (parent directory).
/// * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc
/// does).
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub(crate) struct Dir(ptr::NonNull<libc::DIR>);
impl Dir {
/// Converts from a descriptor-based object, closing the descriptor on success or failure.
#[inline]
pub(crate) fn from<F: IntoRawFd>(fd: F) -> Result<Self> {
unsafe { Self::from_fd(fd.into_raw_fd()) }
}
/// Converts from a file descriptor, closing it on success or failure.
unsafe fn from_fd(fd: RawFd) -> Result<Self> {
let d = libc::fdopendir(fd);
if d.is_null() {
let e = Error::last();
libc::close(fd);
return Err(e);
};
// Always guaranteed to be non-null by the previous check
Ok(Self(ptr::NonNull::new(d).unwrap()))
}
/// Set the position of the directory stream, see `seekdir(3)`.
#[cfg(not(target_os = "android"))]
pub(crate) fn seek(&mut self, loc: SeekLoc) {
unsafe { libc::seekdir(self.0.as_ptr(), loc.0) }
}
/// Reset directory stream, see `rewinddir(3)`.
pub(crate) fn rewind(&mut self) {
unsafe { libc::rewinddir(self.0.as_ptr()) }
}
/// Get the current position in the directory stream.
///
/// If this location is given to `Dir::seek`, the entries up to the previously returned
/// will be omitted and the iteration will start from the currently pending directory entry.
#[cfg(not(target_os = "android"))]
#[allow(dead_code)]
pub(crate) fn tell(&self) -> SeekLoc {
let loc = unsafe { libc::telldir(self.0.as_ptr()) };
SeekLoc(loc)
}
}
// `Dir` is not `Sync`. With the current implementation, it could be, but according to
// https://www.gnu.org/software/libc/manual/html_node/Reading_002fClosing-Directory.html,
// future versions of POSIX are likely to obsolete `readdir_r` and specify that it's unsafe to
// call `readdir` simultaneously from multiple threads.
//
// `Dir` is safe to pass from one thread to another, as it's not reference-counted.
unsafe impl Send for Dir {}
impl AsRawFd for Dir {
fn as_raw_fd(&self) -> RawFd {
unsafe { libc::dirfd(self.0.as_ptr()) }
}
}
impl Drop for Dir {
fn drop(&mut self) {
unsafe { libc::closedir(self.0.as_ptr()) };
}
}
pub(crate) struct IntoIter(Dir);
impl Iterator for IntoIter {
type Item = Result<Entry>;
fn next(&mut self) -> Option<Self::Item> {
unsafe {
// Note: POSIX specifies that portable applications should dynamically allocate a
// buffer with room for a `d_name` field of size `pathconf(..., _PC_NAME_MAX)` plus 1
// for the NUL byte. It doesn't look like the std library does this; it just uses
// fixed-sized buffers (and libc's dirent seems to be sized so this is appropriate).
// Probably fine here too then.
//
// See `impl Iterator for ReadDir` [1] for more details.
// [1] https://github.com/rust-lang/rust/blob/master/src/libstd/sys/unix/fs.rs
let mut ent = std::mem::MaybeUninit::<dirent>::uninit();
let mut result = ptr::null_mut();
if let Err(e) = Errno::result(readdir_r(
(self.0).0.as_ptr(),
ent.as_mut_ptr(),
&mut result,
)) {
return Some(Err(e));
}
if result.is_null() {
None
} else {
assert_eq!(result, ent.as_mut_ptr(), "readdir_r specification violated");
Some(Ok(Entry(ent.assume_init())))
}
}
}
}
impl IntoIterator for Dir {
type IntoIter = IntoIter;
type Item = Result<Entry>;
fn into_iter(self) -> IntoIter {
IntoIter(self)
}
}
/// A directory entry, similar to `std::fs::DirEntry`.
///
/// Note that unlike the std version, this may represent the `.` or `..` entries.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub(crate) struct Entry(dirent);
pub(crate) type Type = FileType;
impl Entry {
/// Returns the inode number (`d_ino`) of the underlying `dirent`.
#[cfg(any(
target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "ios",
target_os = "l4re",
target_os = "linux",
target_os = "macos",
target_os = "solaris"
))]
pub(crate) fn ino(&self) -> u64 {
self.0.d_ino.into()
}
/// Returns the inode number (`d_fileno`) of the underlying `dirent`.
#[cfg(not(any(
target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "ios",
target_os = "l4re",
target_os = "linux",
target_os = "macos",
target_os = "solaris"
)))]
pub(crate) fn ino(&self) -> u64 {
u64::from(self.0.d_fileno)
}
/// Returns the bare file name of this directory entry without any other leading path component.
pub(crate) fn file_name(&self) -> &ffi::CStr {
unsafe { ::std::ffi::CStr::from_ptr(self.0.d_name.as_ptr()) }
}
/// Returns the type of this directory entry, if known.
///
/// See platform `readdir(3)` or `dirent(5)` manpage for when the file type is known;
/// notably, some Linux filesystems don't implement this. The caller should use `stat` or
/// `fstat` if this returns `None`.
pub(crate) fn file_type(&self) -> FileType {
match self.0.d_type {
libc::DT_CHR => Type::CharacterDevice,
libc::DT_DIR => Type::Directory,
libc::DT_BLK => Type::BlockDevice,
libc::DT_REG => Type::RegularFile,
libc::DT_LNK => Type::Symlink,
/* libc::DT_UNKNOWN | libc::DT_SOCK | libc::DT_FIFO */ _ => Type::Unknown,
}
}
#[cfg(target_os = "linux")]
pub(crate) fn seek_loc(&self) -> SeekLoc {
unsafe { SeekLoc::from_raw(self.0.d_off) }
}
}
#[cfg(not(target_os = "android"))]
#[derive(Clone, Copy, Debug)]
pub(crate) struct SeekLoc(libc::c_long);
#[cfg(not(target_os = "android"))]
impl SeekLoc {
pub(crate) unsafe fn from_raw(loc: i64) -> Self {
Self(loc.into())
}
pub(crate) fn to_raw(&self) -> i64 {
self.0.into()
}
}

View File

@@ -0,0 +1,135 @@
use crate::old::snapshot_0::fdentry::Descriptor;
use crate::old::snapshot_0::{wasi, Error, Result};
use std::io;
use std::os::unix::prelude::{AsRawFd, FileTypeExt, FromRawFd, RawFd};
cfg_if::cfg_if! {
if #[cfg(target_os = "linux")] {
pub(crate) use super::linux::osfile::*;
pub(crate) use super::linux::fdentry_impl::*;
} else if #[cfg(any(
target_os = "macos",
target_os = "netbsd",
target_os = "freebsd",
target_os = "openbsd",
target_os = "ios",
target_os = "dragonfly"
))] {
pub(crate) use super::bsd::osfile::*;
pub(crate) use super::bsd::fdentry_impl::*;
}
}
impl AsRawFd for Descriptor {
fn as_raw_fd(&self) -> RawFd {
match self {
Self::OsFile(file) => file.as_raw_fd(),
Self::Stdin => io::stdin().as_raw_fd(),
Self::Stdout => io::stdout().as_raw_fd(),
Self::Stderr => io::stderr().as_raw_fd(),
}
}
}
/// This function is unsafe because it operates on a raw file descriptor.
pub(crate) unsafe fn determine_type_and_access_rights<Fd: AsRawFd>(
fd: &Fd,
) -> Result<(
wasi::__wasi_filetype_t,
wasi::__wasi_rights_t,
wasi::__wasi_rights_t,
)> {
let (file_type, mut rights_base, rights_inheriting) = determine_type_rights(fd)?;
use nix::fcntl::{fcntl, OFlag, F_GETFL};
let flags_bits = fcntl(fd.as_raw_fd(), F_GETFL)?;
let flags = OFlag::from_bits_truncate(flags_bits);
let accmode = flags & OFlag::O_ACCMODE;
if accmode == OFlag::O_RDONLY {
rights_base &= !wasi::__WASI_RIGHTS_FD_WRITE;
} else if accmode == OFlag::O_WRONLY {
rights_base &= !wasi::__WASI_RIGHTS_FD_READ;
}
Ok((file_type, rights_base, rights_inheriting))
}
/// This function is unsafe because it operates on a raw file descriptor.
pub(crate) unsafe fn determine_type_rights<Fd: AsRawFd>(
fd: &Fd,
) -> Result<(
wasi::__wasi_filetype_t,
wasi::__wasi_rights_t,
wasi::__wasi_rights_t,
)> {
let (file_type, rights_base, rights_inheriting) = {
// we just make a `File` here for convenience; we don't want it to close when it drops
let file = std::mem::ManuallyDrop::new(std::fs::File::from_raw_fd(fd.as_raw_fd()));
let ft = file.metadata()?.file_type();
if ft.is_block_device() {
log::debug!("Host fd {:?} is a block device", fd.as_raw_fd());
(
wasi::__WASI_FILETYPE_BLOCK_DEVICE,
wasi::RIGHTS_BLOCK_DEVICE_BASE,
wasi::RIGHTS_BLOCK_DEVICE_INHERITING,
)
} else if ft.is_char_device() {
log::debug!("Host fd {:?} is a char device", fd.as_raw_fd());
if isatty(fd)? {
(
wasi::__WASI_FILETYPE_CHARACTER_DEVICE,
wasi::RIGHTS_TTY_BASE,
wasi::RIGHTS_TTY_BASE,
)
} else {
(
wasi::__WASI_FILETYPE_CHARACTER_DEVICE,
wasi::RIGHTS_CHARACTER_DEVICE_BASE,
wasi::RIGHTS_CHARACTER_DEVICE_INHERITING,
)
}
} else if ft.is_dir() {
log::debug!("Host fd {:?} is a directory", fd.as_raw_fd());
(
wasi::__WASI_FILETYPE_DIRECTORY,
wasi::RIGHTS_DIRECTORY_BASE,
wasi::RIGHTS_DIRECTORY_INHERITING,
)
} else if ft.is_file() {
log::debug!("Host fd {:?} is a file", fd.as_raw_fd());
(
wasi::__WASI_FILETYPE_REGULAR_FILE,
wasi::RIGHTS_REGULAR_FILE_BASE,
wasi::RIGHTS_REGULAR_FILE_INHERITING,
)
} else if ft.is_socket() {
log::debug!("Host fd {:?} is a socket", fd.as_raw_fd());
use nix::sys::socket;
match socket::getsockopt(fd.as_raw_fd(), socket::sockopt::SockType)? {
socket::SockType::Datagram => (
wasi::__WASI_FILETYPE_SOCKET_DGRAM,
wasi::RIGHTS_SOCKET_BASE,
wasi::RIGHTS_SOCKET_INHERITING,
),
socket::SockType::Stream => (
wasi::__WASI_FILETYPE_SOCKET_STREAM,
wasi::RIGHTS_SOCKET_BASE,
wasi::RIGHTS_SOCKET_INHERITING,
),
_ => return Err(Error::EINVAL),
}
} else if ft.is_fifo() {
log::debug!("Host fd {:?} is a fifo", fd.as_raw_fd());
(
wasi::__WASI_FILETYPE_UNKNOWN,
wasi::RIGHTS_REGULAR_FILE_BASE,
wasi::RIGHTS_REGULAR_FILE_INHERITING,
)
} else {
log::debug!("Host fd {:?} is unknown", fd.as_raw_fd());
return Err(Error::EINVAL);
}
};
Ok((file_type, rights_base, rights_inheriting))
}

View File

@@ -0,0 +1,149 @@
//! This internal module consists of helper types and functions for dealing
//! with setting the file times (mainly in `path_filestat_set_times` syscall for now).
//!
//! The vast majority of the code contained within and in platform-specific implementations
//! (`super::linux::filetime` and `super::bsd::filetime`) is based on the [filetime] crate.
//! Kudos @alexcrichton!
//!
//! [filetime]: https://github.com/alexcrichton/filetime
use std::fs::{self, File};
use std::io;
cfg_if::cfg_if! {
if #[cfg(target_os = "linux")] {
pub(crate) use super::linux::filetime::*;
} else if #[cfg(any(
target_os = "macos",
target_os = "netbsd",
target_os = "freebsd",
target_os = "openbsd",
target_os = "ios",
target_os = "dragonfly"
))] {
pub(crate) use super::bsd::filetime::*;
}
}
/// A wrapper `enum` around `filetime::FileTime` struct, but unlike the original, this
/// type allows the possibility of specifying `FileTime::Now` as a valid enumeration which,
/// in turn, if `utimensat` is available on the host, will use a special const setting
/// `UTIME_NOW`.
#[derive(Debug, Copy, Clone)]
pub(crate) enum FileTime {
Now,
Omit,
FileTime(filetime::FileTime),
}
/// For a provided pair of access and modified `FileTime`s, converts the input to
/// `filetime::FileTime` used later in `utimensat` function. For variants `FileTime::Now`
/// and `FileTime::Omit`, this function will make two syscalls: either accessing current
/// system time, or accessing the file's metadata.
///
/// The original implementation can be found here: [filetime::unix::get_times].
///
/// [filetime::unix::get_times]: https://github.com/alexcrichton/filetime/blob/master/src/unix/utimes.rs#L42
fn get_times(
atime: FileTime,
mtime: FileTime,
current: impl Fn() -> io::Result<fs::Metadata>,
) -> io::Result<(filetime::FileTime, filetime::FileTime)> {
use std::time::SystemTime;
let atime = match atime {
FileTime::Now => {
let time = SystemTime::now();
filetime::FileTime::from_system_time(time)
}
FileTime::Omit => {
let meta = current()?;
filetime::FileTime::from_last_access_time(&meta)
}
FileTime::FileTime(ft) => ft,
};
let mtime = match mtime {
FileTime::Now => {
let time = SystemTime::now();
filetime::FileTime::from_system_time(time)
}
FileTime::Omit => {
let meta = current()?;
filetime::FileTime::from_last_modification_time(&meta)
}
FileTime::FileTime(ft) => ft,
};
Ok((atime, mtime))
}
/// Combines `openat` with `utimes` to emulate `utimensat` on platforms where it is
/// not available. The logic for setting file times is based on [filetime::unix::set_file_handles_times].
///
/// [filetime::unix::set_file_handles_times]: https://github.com/alexcrichton/filetime/blob/master/src/unix/utimes.rs#L24
pub(crate) fn utimesat(
dirfd: &File,
path: &str,
atime: FileTime,
mtime: FileTime,
symlink_nofollow: bool,
) -> io::Result<()> {
use std::ffi::CString;
use std::os::unix::prelude::*;
// emulate *at syscall by reading the path from a combination of
// (fd, path)
let p = CString::new(path.as_bytes())?;
let mut flags = libc::O_RDWR;
if symlink_nofollow {
flags |= libc::O_NOFOLLOW;
}
let fd = unsafe { libc::openat(dirfd.as_raw_fd(), p.as_ptr(), flags) };
let f = unsafe { File::from_raw_fd(fd) };
let (atime, mtime) = get_times(atime, mtime, || f.metadata())?;
let times = [to_timeval(atime), to_timeval(mtime)];
let rc = unsafe { libc::futimes(f.as_raw_fd(), times.as_ptr()) };
return if rc == 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
};
}
/// Converts `filetime::FileTime` to `libc::timeval`. This function was taken directly from
/// [filetime] crate.
///
/// [filetime]: https://github.com/alexcrichton/filetime/blob/master/src/unix/utimes.rs#L93
fn to_timeval(ft: filetime::FileTime) -> libc::timeval {
libc::timeval {
tv_sec: ft.seconds(),
tv_usec: (ft.nanoseconds() / 1000) as libc::suseconds_t,
}
}
/// Converts `FileTime` to `libc::timespec`. If `FileTime::Now` variant is specified, this
/// resolves to `UTIME_NOW` special const, `FileTime::Omit` variant resolves to `UTIME_OMIT`, and
/// `FileTime::FileTime(ft)` where `ft := filetime::FileTime` uses [filetime] crate's original
/// implementation which can be found here: [filetime::unix::to_timespec].
///
/// [filetime]: https://github.com/alexcrichton/filetime
/// [filetime::unix::to_timespec]: https://github.com/alexcrichton/filetime/blob/master/src/unix/mod.rs#L30
pub(crate) fn to_timespec(ft: &FileTime) -> libc::timespec {
match ft {
FileTime::Now => libc::timespec {
tv_sec: 0,
tv_nsec: UTIME_NOW,
},
FileTime::Omit => libc::timespec {
tv_sec: 0,
tv_nsec: UTIME_OMIT,
},
// `filetime::FileTime`'s fields are normalised by definition. `ft.seconds()` return the number
// of whole seconds, while `ft.nanoseconds()` returns only fractional part expressed in
// nanoseconds, as underneath it uses `std::time::Duration::subsec_nanos` to populate the
// `filetime::FileTime::nanoseconds` field. It is, therefore, OK to do an `as` cast here.
FileTime::FileTime(ft) => libc::timespec {
tv_sec: ft.seconds(),
tv_nsec: ft.nanoseconds() as _,
},
}
}

View File

@@ -0,0 +1,246 @@
//! WASI host types specific to *nix host.
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(dead_code)]
use crate::old::snapshot_0::hostcalls_impl::FileType;
use crate::old::snapshot_0::{helpers, wasi, Error, Result};
use log::warn;
use std::ffi::OsStr;
use std::os::unix::prelude::OsStrExt;
cfg_if::cfg_if! {
if #[cfg(target_os = "linux")] {
pub(crate) use super::linux::host_impl::*;
} else if #[cfg(any(
target_os = "macos",
target_os = "netbsd",
target_os = "freebsd",
target_os = "openbsd",
target_os = "ios",
target_os = "dragonfly"
))] {
pub(crate) use super::bsd::host_impl::*;
}
}
pub(crate) fn errno_from_nix(errno: nix::errno::Errno) -> Error {
match errno {
nix::errno::Errno::EPERM => Error::EPERM,
nix::errno::Errno::ENOENT => Error::ENOENT,
nix::errno::Errno::ESRCH => Error::ESRCH,
nix::errno::Errno::EINTR => Error::EINTR,
nix::errno::Errno::EIO => Error::EIO,
nix::errno::Errno::ENXIO => Error::ENXIO,
nix::errno::Errno::E2BIG => Error::E2BIG,
nix::errno::Errno::ENOEXEC => Error::ENOEXEC,
nix::errno::Errno::EBADF => Error::EBADF,
nix::errno::Errno::ECHILD => Error::ECHILD,
nix::errno::Errno::EAGAIN => Error::EAGAIN,
nix::errno::Errno::ENOMEM => Error::ENOMEM,
nix::errno::Errno::EACCES => Error::EACCES,
nix::errno::Errno::EFAULT => Error::EFAULT,
nix::errno::Errno::EBUSY => Error::EBUSY,
nix::errno::Errno::EEXIST => Error::EEXIST,
nix::errno::Errno::EXDEV => Error::EXDEV,
nix::errno::Errno::ENODEV => Error::ENODEV,
nix::errno::Errno::ENOTDIR => Error::ENOTDIR,
nix::errno::Errno::EISDIR => Error::EISDIR,
nix::errno::Errno::EINVAL => Error::EINVAL,
nix::errno::Errno::ENFILE => Error::ENFILE,
nix::errno::Errno::EMFILE => Error::EMFILE,
nix::errno::Errno::ENOTTY => Error::ENOTTY,
nix::errno::Errno::ETXTBSY => Error::ETXTBSY,
nix::errno::Errno::EFBIG => Error::EFBIG,
nix::errno::Errno::ENOSPC => Error::ENOSPC,
nix::errno::Errno::ESPIPE => Error::ESPIPE,
nix::errno::Errno::EROFS => Error::EROFS,
nix::errno::Errno::EMLINK => Error::EMLINK,
nix::errno::Errno::EPIPE => Error::EPIPE,
nix::errno::Errno::EDOM => Error::EDOM,
nix::errno::Errno::ERANGE => Error::ERANGE,
nix::errno::Errno::EDEADLK => Error::EDEADLK,
nix::errno::Errno::ENAMETOOLONG => Error::ENAMETOOLONG,
nix::errno::Errno::ENOLCK => Error::ENOLCK,
nix::errno::Errno::ENOSYS => Error::ENOSYS,
nix::errno::Errno::ENOTEMPTY => Error::ENOTEMPTY,
nix::errno::Errno::ELOOP => Error::ELOOP,
nix::errno::Errno::ENOMSG => Error::ENOMSG,
nix::errno::Errno::EIDRM => Error::EIDRM,
nix::errno::Errno::ENOLINK => Error::ENOLINK,
nix::errno::Errno::EPROTO => Error::EPROTO,
nix::errno::Errno::EMULTIHOP => Error::EMULTIHOP,
nix::errno::Errno::EBADMSG => Error::EBADMSG,
nix::errno::Errno::EOVERFLOW => Error::EOVERFLOW,
nix::errno::Errno::EILSEQ => Error::EILSEQ,
nix::errno::Errno::ENOTSOCK => Error::ENOTSOCK,
nix::errno::Errno::EDESTADDRREQ => Error::EDESTADDRREQ,
nix::errno::Errno::EMSGSIZE => Error::EMSGSIZE,
nix::errno::Errno::EPROTOTYPE => Error::EPROTOTYPE,
nix::errno::Errno::ENOPROTOOPT => Error::ENOPROTOOPT,
nix::errno::Errno::EPROTONOSUPPORT => Error::EPROTONOSUPPORT,
nix::errno::Errno::EAFNOSUPPORT => Error::EAFNOSUPPORT,
nix::errno::Errno::EADDRINUSE => Error::EADDRINUSE,
nix::errno::Errno::EADDRNOTAVAIL => Error::EADDRNOTAVAIL,
nix::errno::Errno::ENETDOWN => Error::ENETDOWN,
nix::errno::Errno::ENETUNREACH => Error::ENETUNREACH,
nix::errno::Errno::ENETRESET => Error::ENETRESET,
nix::errno::Errno::ECONNABORTED => Error::ECONNABORTED,
nix::errno::Errno::ECONNRESET => Error::ECONNRESET,
nix::errno::Errno::ENOBUFS => Error::ENOBUFS,
nix::errno::Errno::EISCONN => Error::EISCONN,
nix::errno::Errno::ENOTCONN => Error::ENOTCONN,
nix::errno::Errno::ETIMEDOUT => Error::ETIMEDOUT,
nix::errno::Errno::ECONNREFUSED => Error::ECONNREFUSED,
nix::errno::Errno::EHOSTUNREACH => Error::EHOSTUNREACH,
nix::errno::Errno::EALREADY => Error::EALREADY,
nix::errno::Errno::EINPROGRESS => Error::EINPROGRESS,
nix::errno::Errno::ESTALE => Error::ESTALE,
nix::errno::Errno::EDQUOT => Error::EDQUOT,
nix::errno::Errno::ECANCELED => Error::ECANCELED,
nix::errno::Errno::EOWNERDEAD => Error::EOWNERDEAD,
nix::errno::Errno::ENOTRECOVERABLE => Error::ENOTRECOVERABLE,
other => {
warn!("Unknown error from nix: {}", other);
Error::ENOSYS
}
}
}
pub(crate) fn nix_from_fdflags(fdflags: wasi::__wasi_fdflags_t) -> nix::fcntl::OFlag {
use nix::fcntl::OFlag;
let mut nix_flags = OFlag::empty();
if fdflags & wasi::__WASI_FDFLAGS_APPEND != 0 {
nix_flags.insert(OFlag::O_APPEND);
}
if fdflags & wasi::__WASI_FDFLAGS_DSYNC != 0 {
nix_flags.insert(OFlag::O_DSYNC);
}
if fdflags & wasi::__WASI_FDFLAGS_NONBLOCK != 0 {
nix_flags.insert(OFlag::O_NONBLOCK);
}
if fdflags & wasi::__WASI_FDFLAGS_RSYNC != 0 {
nix_flags.insert(O_RSYNC);
}
if fdflags & wasi::__WASI_FDFLAGS_SYNC != 0 {
nix_flags.insert(OFlag::O_SYNC);
}
nix_flags
}
pub(crate) fn fdflags_from_nix(oflags: nix::fcntl::OFlag) -> wasi::__wasi_fdflags_t {
use nix::fcntl::OFlag;
let mut fdflags = 0;
if oflags.contains(OFlag::O_APPEND) {
fdflags |= wasi::__WASI_FDFLAGS_APPEND;
}
if oflags.contains(OFlag::O_DSYNC) {
fdflags |= wasi::__WASI_FDFLAGS_DSYNC;
}
if oflags.contains(OFlag::O_NONBLOCK) {
fdflags |= wasi::__WASI_FDFLAGS_NONBLOCK;
}
if oflags.contains(O_RSYNC) {
fdflags |= wasi::__WASI_FDFLAGS_RSYNC;
}
if oflags.contains(OFlag::O_SYNC) {
fdflags |= wasi::__WASI_FDFLAGS_SYNC;
}
fdflags
}
pub(crate) fn nix_from_oflags(oflags: wasi::__wasi_oflags_t) -> nix::fcntl::OFlag {
use nix::fcntl::OFlag;
let mut nix_flags = OFlag::empty();
if oflags & wasi::__WASI_OFLAGS_CREAT != 0 {
nix_flags.insert(OFlag::O_CREAT);
}
if oflags & wasi::__WASI_OFLAGS_DIRECTORY != 0 {
nix_flags.insert(OFlag::O_DIRECTORY);
}
if oflags & wasi::__WASI_OFLAGS_EXCL != 0 {
nix_flags.insert(OFlag::O_EXCL);
}
if oflags & wasi::__WASI_OFLAGS_TRUNC != 0 {
nix_flags.insert(OFlag::O_TRUNC);
}
nix_flags
}
pub(crate) fn filetype_from_nix(sflags: nix::sys::stat::SFlag) -> FileType {
use nix::sys::stat::SFlag;
if sflags.contains(SFlag::S_IFCHR) {
FileType::CharacterDevice
} else if sflags.contains(SFlag::S_IFBLK) {
FileType::BlockDevice
} else if sflags.contains(SFlag::S_IFSOCK) {
FileType::SocketStream
} else if sflags.contains(SFlag::S_IFDIR) {
FileType::Directory
} else if sflags.contains(SFlag::S_IFREG) {
FileType::RegularFile
} else if sflags.contains(SFlag::S_IFLNK) {
FileType::Symlink
} else {
FileType::Unknown
}
}
pub(crate) fn filestat_from_nix(
filestat: nix::sys::stat::FileStat,
) -> Result<wasi::__wasi_filestat_t> {
use std::convert::TryFrom;
fn filestat_to_timestamp(secs: u64, nsecs: u64) -> Result<wasi::__wasi_timestamp_t> {
secs.checked_mul(1_000_000_000)
.and_then(|sec_nsec| sec_nsec.checked_add(nsecs))
.ok_or(Error::EOVERFLOW)
}
let filetype = nix::sys::stat::SFlag::from_bits_truncate(filestat.st_mode);
let dev = wasi::__wasi_device_t::try_from(filestat.st_dev)?;
let ino = wasi::__wasi_inode_t::try_from(filestat.st_ino)?;
let atim = filestat_to_timestamp(filestat.st_atime as u64, filestat.st_atime_nsec as u64)?;
let ctim = filestat_to_timestamp(filestat.st_ctime as u64, filestat.st_ctime_nsec as u64)?;
let mtim = filestat_to_timestamp(filestat.st_mtime as u64, filestat.st_mtime_nsec as u64)?;
Ok(wasi::__wasi_filestat_t {
dev,
ino,
nlink: filestat.st_nlink as wasi::__wasi_linkcount_t,
size: filestat.st_size as wasi::__wasi_filesize_t,
atim,
ctim,
mtim,
filetype: filetype_from_nix(filetype).to_wasi(),
})
}
pub(crate) fn dirent_filetype_from_host(
host_entry: &nix::libc::dirent,
) -> Result<wasi::__wasi_filetype_t> {
match host_entry.d_type {
libc::DT_FIFO => Ok(wasi::__WASI_FILETYPE_UNKNOWN),
libc::DT_CHR => Ok(wasi::__WASI_FILETYPE_CHARACTER_DEVICE),
libc::DT_DIR => Ok(wasi::__WASI_FILETYPE_DIRECTORY),
libc::DT_BLK => Ok(wasi::__WASI_FILETYPE_BLOCK_DEVICE),
libc::DT_REG => Ok(wasi::__WASI_FILETYPE_REGULAR_FILE),
libc::DT_LNK => Ok(wasi::__WASI_FILETYPE_SYMBOLIC_LINK),
libc::DT_SOCK => {
// TODO how to discriminate between STREAM and DGRAM?
// Perhaps, we should create a more general WASI filetype
// such as __WASI_FILETYPE_SOCKET, and then it would be
// up to the client to check whether it's actually
// STREAM or DGRAM?
Ok(wasi::__WASI_FILETYPE_UNKNOWN)
}
libc::DT_UNKNOWN => Ok(wasi::__WASI_FILETYPE_UNKNOWN),
_ => Err(Error::EINVAL),
}
}
/// Creates owned WASI path from OS string.
///
/// NB WASI spec requires OS string to be valid UTF-8. Otherwise,
/// `__WASI_ERRNO_ILSEQ` error is returned.
pub(crate) fn path_from_host<S: AsRef<OsStr>>(s: S) -> Result<String> {
helpers::path_from_slice(s.as_ref().as_bytes()).map(String::from)
}

View File

@@ -0,0 +1,340 @@
#![allow(non_camel_case_types)]
#![allow(unused_unsafe)]
use crate::old::snapshot_0::helpers::systemtime_to_timestamp;
use crate::old::snapshot_0::hostcalls_impl::{FileType, PathGet};
use crate::old::snapshot_0::sys::host_impl;
use crate::old::snapshot_0::sys::unix::str_to_cstring;
use crate::old::snapshot_0::{wasi, Error, Result};
use nix::libc;
use std::convert::TryInto;
use std::fs::{File, Metadata};
use std::os::unix::fs::FileExt;
use std::os::unix::prelude::{AsRawFd, FromRawFd};
cfg_if::cfg_if! {
if #[cfg(target_os = "linux")] {
pub(crate) use super::super::linux::hostcalls_impl::*;
} else if #[cfg(any(
target_os = "macos",
target_os = "netbsd",
target_os = "freebsd",
target_os = "openbsd",
target_os = "ios",
target_os = "dragonfly"
))] {
pub(crate) use super::super::bsd::hostcalls_impl::*;
}
}
pub(crate) fn fd_pread(
file: &File,
buf: &mut [u8],
offset: wasi::__wasi_filesize_t,
) -> Result<usize> {
file.read_at(buf, offset).map_err(Into::into)
}
pub(crate) fn fd_pwrite(file: &File, buf: &[u8], offset: wasi::__wasi_filesize_t) -> Result<usize> {
file.write_at(buf, offset).map_err(Into::into)
}
pub(crate) fn fd_fdstat_get(fd: &File) -> Result<wasi::__wasi_fdflags_t> {
use nix::fcntl::{fcntl, OFlag, F_GETFL};
match fcntl(fd.as_raw_fd(), F_GETFL).map(OFlag::from_bits_truncate) {
Ok(flags) => Ok(host_impl::fdflags_from_nix(flags)),
Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())),
}
}
pub(crate) fn fd_fdstat_set_flags(fd: &File, fdflags: wasi::__wasi_fdflags_t) -> Result<()> {
use nix::fcntl::{fcntl, F_SETFL};
let nix_flags = host_impl::nix_from_fdflags(fdflags);
match fcntl(fd.as_raw_fd(), F_SETFL(nix_flags)) {
Ok(_) => Ok(()),
Err(e) => Err(host_impl::errno_from_nix(e.as_errno().unwrap())),
}
}
pub(crate) fn path_create_directory(resolved: PathGet) -> Result<()> {
use nix::libc::mkdirat;
let path_cstr = str_to_cstring(resolved.path())?;
// nix doesn't expose mkdirat() yet
match unsafe { mkdirat(resolved.dirfd().as_raw_fd(), path_cstr.as_ptr(), 0o777) } {
0 => Ok(()),
_ => Err(host_impl::errno_from_nix(nix::errno::Errno::last())),
}
}
pub(crate) fn path_link(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> {
use nix::libc::linkat;
let old_path_cstr = str_to_cstring(resolved_old.path())?;
let new_path_cstr = str_to_cstring(resolved_new.path())?;
// Not setting AT_SYMLINK_FOLLOW fails on most filesystems
let atflags = libc::AT_SYMLINK_FOLLOW;
let res = unsafe {
linkat(
resolved_old.dirfd().as_raw_fd(),
old_path_cstr.as_ptr(),
resolved_new.dirfd().as_raw_fd(),
new_path_cstr.as_ptr(),
atflags,
)
};
if res != 0 {
Err(host_impl::errno_from_nix(nix::errno::Errno::last()))
} else {
Ok(())
}
}
pub(crate) fn path_open(
resolved: PathGet,
read: bool,
write: bool,
oflags: wasi::__wasi_oflags_t,
fs_flags: wasi::__wasi_fdflags_t,
) -> Result<File> {
use nix::errno::Errno;
use nix::fcntl::{openat, AtFlags, OFlag};
use nix::sys::stat::{fstatat, Mode, SFlag};
let mut nix_all_oflags = if read && write {
OFlag::O_RDWR
} else if write {
OFlag::O_WRONLY
} else {
OFlag::O_RDONLY
};
// on non-Capsicum systems, we always want nofollow
nix_all_oflags.insert(OFlag::O_NOFOLLOW);
// convert open flags
nix_all_oflags.insert(host_impl::nix_from_oflags(oflags));
// convert file descriptor flags
nix_all_oflags.insert(host_impl::nix_from_fdflags(fs_flags));
// 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.
log::debug!("path_open resolved = {:?}", resolved);
log::debug!("path_open oflags = {:?}", nix_all_oflags);
let new_fd = match openat(
resolved.dirfd().as_raw_fd(),
resolved.path(),
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(
resolved.dirfd().as_raw_fd(),
resolved.path(),
AtFlags::AT_SYMLINK_NOFOLLOW,
) {
if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFSOCK) {
return Err(Error::ENOTSUP);
} else {
return Err(Error::ENXIO);
}
} else {
return Err(Error::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(
resolved.dirfd().as_raw_fd(),
resolved.path(),
AtFlags::AT_SYMLINK_NOFOLLOW,
) {
if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFLNK) {
return Err(Error::ELOOP);
}
}
return Err(Error::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 Err(Error::ELOOP);
}
Some(e) => return Err(host_impl::errno_from_nix(e)),
None => return Err(Error::ENOSYS),
}
}
};
log::debug!("path_open (host) new_fd = {:?}", new_fd);
// Determine the type of the new file descriptor and which rights contradict with this type
Ok(unsafe { File::from_raw_fd(new_fd) })
}
pub(crate) fn path_readlink(resolved: PathGet, buf: &mut [u8]) -> Result<usize> {
use nix::errno::Errno;
let path_cstr = str_to_cstring(resolved.path())?;
// Linux requires that the buffer size is positive, whereas POSIX does not.
// Use a fake buffer to store the results if the size is zero.
// TODO: instead of using raw libc::readlinkat call here, this should really
// be fixed in `nix` crate
let fakebuf: &mut [u8] = &mut [0];
let buf_len = buf.len();
let len = unsafe {
libc::readlinkat(
resolved.dirfd().as_raw_fd(),
path_cstr.as_ptr() as *const libc::c_char,
if buf_len == 0 {
fakebuf.as_mut_ptr()
} else {
buf.as_mut_ptr()
} as *mut libc::c_char,
if buf_len == 0 { fakebuf.len() } else { buf_len },
)
};
if len < 0 {
Err(host_impl::errno_from_nix(Errno::last()))
} else {
let len = len as usize;
Ok(if len < buf_len { len } else { buf_len })
}
}
pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result<wasi::__wasi_filestat_t> {
use std::os::unix::fs::MetadataExt;
let metadata = file.metadata()?;
Ok(wasi::__wasi_filestat_t {
dev: metadata.dev(),
ino: metadata.ino(),
nlink: metadata.nlink().try_into()?, // u64 doesn't fit into u32
size: metadata.len(),
atim: systemtime_to_timestamp(metadata.accessed()?)?,
ctim: metadata.ctime().try_into()?, // i64 doesn't fit into u64
mtim: systemtime_to_timestamp(metadata.modified()?)?,
filetype: filetype(file, &metadata)?.to_wasi(),
})
}
fn filetype(file: &File, metadata: &Metadata) -> Result<FileType> {
use nix::sys::socket::{self, SockType};
use std::os::unix::fs::FileTypeExt;
let ftype = metadata.file_type();
if ftype.is_file() {
Ok(FileType::RegularFile)
} else if ftype.is_dir() {
Ok(FileType::Directory)
} else if ftype.is_symlink() {
Ok(FileType::Symlink)
} else if ftype.is_char_device() {
Ok(FileType::CharacterDevice)
} else if ftype.is_block_device() {
Ok(FileType::BlockDevice)
} else if ftype.is_socket() {
match socket::getsockopt(file.as_raw_fd(), socket::sockopt::SockType)
.map_err(|err| err.as_errno().unwrap())
.map_err(host_impl::errno_from_nix)?
{
SockType::Datagram => Ok(FileType::SocketDgram),
SockType::Stream => Ok(FileType::SocketStream),
_ => Ok(FileType::Unknown),
}
} else {
Ok(FileType::Unknown)
}
}
pub(crate) fn path_filestat_get(
resolved: PathGet,
dirflags: wasi::__wasi_lookupflags_t,
) -> Result<wasi::__wasi_filestat_t> {
use nix::fcntl::AtFlags;
use nix::sys::stat::fstatat;
let atflags = match dirflags {
0 => AtFlags::empty(),
_ => AtFlags::AT_SYMLINK_NOFOLLOW,
};
let filestat = fstatat(resolved.dirfd().as_raw_fd(), resolved.path(), atflags)
.map_err(|err| host_impl::errno_from_nix(err.as_errno().unwrap()))?;
host_impl::filestat_from_nix(filestat)
}
pub(crate) fn path_filestat_set_times(
resolved: PathGet,
dirflags: wasi::__wasi_lookupflags_t,
st_atim: wasi::__wasi_timestamp_t,
st_mtim: wasi::__wasi_timestamp_t,
fst_flags: wasi::__wasi_fstflags_t,
) -> Result<()> {
use super::super::filetime::*;
use std::time::{Duration, UNIX_EPOCH};
let set_atim = fst_flags & wasi::__WASI_FSTFLAGS_ATIM != 0;
let set_atim_now = fst_flags & wasi::__WASI_FSTFLAGS_ATIM_NOW != 0;
let set_mtim = fst_flags & wasi::__WASI_FSTFLAGS_MTIM != 0;
let set_mtim_now = fst_flags & wasi::__WASI_FSTFLAGS_MTIM_NOW != 0;
if (set_atim && set_atim_now) || (set_mtim && set_mtim_now) {
return Err(Error::EINVAL);
}
let symlink_nofollow = wasi::__WASI_LOOKUPFLAGS_SYMLINK_FOLLOW != dirflags;
let atim = if set_atim {
let time = UNIX_EPOCH + Duration::from_nanos(st_atim);
FileTime::FileTime(filetime::FileTime::from_system_time(time))
} else if set_atim_now {
FileTime::Now
} else {
FileTime::Omit
};
let mtim = if set_mtim {
let time = UNIX_EPOCH + Duration::from_nanos(st_mtim);
FileTime::FileTime(filetime::FileTime::from_system_time(time))
} else if set_mtim_now {
FileTime::Now
} else {
FileTime::Omit
};
utimensat(
resolved.dirfd(),
resolved.path(),
atim,
mtim,
symlink_nofollow,
)
.map_err(Into::into)
}
pub(crate) fn path_remove_directory(resolved: PathGet) -> Result<()> {
use nix::errno;
use nix::libc::{unlinkat, AT_REMOVEDIR};
let path_cstr = str_to_cstring(resolved.path())?;
// nix doesn't expose unlinkat() yet
match unsafe {
unlinkat(
resolved.dirfd().as_raw_fd(),
path_cstr.as_ptr(),
AT_REMOVEDIR,
)
} {
0 => Ok(()),
_ => Err(host_impl::errno_from_nix(errno::Errno::last())),
}
}

View File

@@ -0,0 +1,68 @@
#![allow(non_camel_case_types)]
#![allow(unused_unsafe)]
use crate::old::snapshot_0::sys::host_impl;
use crate::old::snapshot_0::{wasi, Result};
use std::fs::File;
pub(crate) fn path_open_rights(
rights_base: wasi::__wasi_rights_t,
rights_inheriting: wasi::__wasi_rights_t,
oflags: wasi::__wasi_oflags_t,
fs_flags: wasi::__wasi_fdflags_t,
) -> (wasi::__wasi_rights_t, wasi::__wasi_rights_t) {
use nix::fcntl::OFlag;
// which rights are needed on the dirfd?
let mut needed_base = wasi::__WASI_RIGHTS_PATH_OPEN;
let mut needed_inheriting = rights_base | rights_inheriting;
// convert open flags
let oflags = host_impl::nix_from_oflags(oflags);
if oflags.contains(OFlag::O_CREAT) {
needed_base |= wasi::__WASI_RIGHTS_PATH_CREATE_FILE;
}
if oflags.contains(OFlag::O_TRUNC) {
needed_base |= wasi::__WASI_RIGHTS_PATH_FILESTAT_SET_SIZE;
}
// convert file descriptor flags
let fdflags = host_impl::nix_from_fdflags(fs_flags);
if fdflags.contains(OFlag::O_DSYNC) {
needed_inheriting |= wasi::__WASI_RIGHTS_FD_DATASYNC;
}
if fdflags.intersects(host_impl::O_RSYNC | OFlag::O_SYNC) {
needed_inheriting |= wasi::__WASI_RIGHTS_FD_SYNC;
}
(needed_base, needed_inheriting)
}
pub(crate) fn openat(dirfd: &File, path: &str) -> Result<File> {
use nix::fcntl::{self, OFlag};
use nix::sys::stat::Mode;
use std::os::unix::prelude::{AsRawFd, FromRawFd};
log::debug!("path_get openat path = {:?}", path);
fcntl::openat(
dirfd.as_raw_fd(),
path,
OFlag::O_RDONLY | OFlag::O_DIRECTORY | OFlag::O_NOFOLLOW,
Mode::empty(),
)
.map(|new_fd| unsafe { File::from_raw_fd(new_fd) })
.map_err(Into::into)
}
pub(crate) fn readlinkat(dirfd: &File, path: &str) -> Result<String> {
use nix::fcntl;
use std::os::unix::prelude::AsRawFd;
log::debug!("path_get readlinkat path = {:?}", path);
let readlink_buf = &mut [0u8; libc::PATH_MAX as usize + 1];
fcntl::readlinkat(dirfd.as_raw_fd(), path, readlink_buf)
.map_err(Into::into)
.and_then(host_impl::path_from_host)
}

View File

@@ -0,0 +1,221 @@
#![allow(non_camel_case_types)]
#![allow(unused_unsafe)]
use crate::old::snapshot_0::hostcalls_impl::{ClockEventData, FdEventData};
use crate::old::snapshot_0::sys::host_impl;
use crate::old::snapshot_0::{wasi, Error, Result};
use nix::libc::{self, c_int};
use std::mem::MaybeUninit;
fn wasi_clock_id_to_unix(clock_id: wasi::__wasi_clockid_t) -> Result<libc::clockid_t> {
// convert the supported clocks to the libc types, or return EINVAL
match clock_id {
wasi::__WASI_CLOCKID_REALTIME => Ok(libc::CLOCK_REALTIME),
wasi::__WASI_CLOCKID_MONOTONIC => Ok(libc::CLOCK_MONOTONIC),
wasi::__WASI_CLOCKID_PROCESS_CPUTIME_ID => Ok(libc::CLOCK_PROCESS_CPUTIME_ID),
wasi::__WASI_CLOCKID_THREAD_CPUTIME_ID => Ok(libc::CLOCK_THREAD_CPUTIME_ID),
_ => Err(Error::EINVAL),
}
}
pub(crate) fn clock_res_get(clock_id: wasi::__wasi_clockid_t) -> Result<wasi::__wasi_timestamp_t> {
let clock_id = wasi_clock_id_to_unix(clock_id)?;
// no `nix` wrapper for clock_getres, so we do it ourselves
let mut timespec = MaybeUninit::<libc::timespec>::uninit();
let res = unsafe { libc::clock_getres(clock_id, timespec.as_mut_ptr()) };
if res != 0 {
return Err(host_impl::errno_from_nix(nix::errno::Errno::last()));
}
let timespec = unsafe { timespec.assume_init() };
// 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 wasi::__wasi_timestamp_t)
.checked_mul(1_000_000_000)
.and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as wasi::__wasi_timestamp_t))
.map_or(Err(Error::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 {
Err(Error::EINVAL)
} else {
Ok(resolution)
}
})
}
pub(crate) fn clock_time_get(clock_id: wasi::__wasi_clockid_t) -> Result<wasi::__wasi_timestamp_t> {
let clock_id = wasi_clock_id_to_unix(clock_id)?;
// no `nix` wrapper for clock_getres, so we do it ourselves
let mut timespec = MaybeUninit::<libc::timespec>::uninit();
let res = unsafe { libc::clock_gettime(clock_id, timespec.as_mut_ptr()) };
if res != 0 {
return Err(host_impl::errno_from_nix(nix::errno::Errno::last()));
}
let timespec = unsafe { timespec.assume_init() };
// 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 wasi::__wasi_timestamp_t)
.checked_mul(1_000_000_000)
.and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as wasi::__wasi_timestamp_t))
.map_or(Err(Error::EOVERFLOW), Ok)
}
pub(crate) fn poll_oneoff(
timeout: Option<ClockEventData>,
fd_events: Vec<FdEventData>,
events: &mut Vec<wasi::__wasi_event_t>,
) -> Result<()> {
use nix::{
errno::Errno,
poll::{poll, PollFd, PollFlags},
};
use std::{convert::TryInto, os::unix::prelude::AsRawFd};
if fd_events.is_empty() && timeout.is_none() {
return Ok(());
}
let mut poll_fds: Vec<_> = fd_events
.iter()
.map(|event| {
let mut flags = PollFlags::empty();
match event.r#type {
wasi::__WASI_EVENTTYPE_FD_READ => flags.insert(PollFlags::POLLIN),
wasi::__WASI_EVENTTYPE_FD_WRITE => flags.insert(PollFlags::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!(),
};
PollFd::new(event.descriptor.as_raw_fd(), flags)
})
.collect();
let poll_timeout = timeout.map_or(-1, |timeout| {
let delay = timeout.delay / 1_000_000; // poll syscall requires delay to expressed in milliseconds
delay.try_into().unwrap_or(c_int::max_value())
});
log::debug!("poll_oneoff poll_timeout = {:?}", poll_timeout);
let ready = loop {
match poll(&mut poll_fds, poll_timeout) {
Err(_) => {
if Errno::last() == Errno::EINTR {
continue;
}
return Err(host_impl::errno_from_nix(Errno::last()));
}
Ok(ready) => break ready as usize,
}
};
Ok(if ready == 0 {
poll_oneoff_handle_timeout_event(timeout.expect("timeout should not be None"), events)
} else {
let ready_events = fd_events.into_iter().zip(poll_fds.into_iter()).take(ready);
poll_oneoff_handle_fd_event(ready_events, events)?
})
}
// define the `fionread()` function, equivalent to `ioctl(fd, FIONREAD, *bytes)`
nix::ioctl_read_bad!(fionread, nix::libc::FIONREAD, c_int);
fn poll_oneoff_handle_timeout_event(
timeout: ClockEventData,
events: &mut Vec<wasi::__wasi_event_t>,
) {
events.push(wasi::__wasi_event_t {
userdata: timeout.userdata,
r#type: wasi::__WASI_EVENTTYPE_CLOCK,
error: wasi::__WASI_ERRNO_SUCCESS,
u: wasi::__wasi_event_u_t {
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
nbytes: 0,
flags: 0,
},
},
});
}
fn poll_oneoff_handle_fd_event<'a>(
ready_events: impl Iterator<Item = (FdEventData<'a>, nix::poll::PollFd)>,
events: &mut Vec<wasi::__wasi_event_t>,
) -> Result<()> {
use nix::poll::PollFlags;
use std::{convert::TryInto, os::unix::prelude::AsRawFd};
for (fd_event, poll_fd) in ready_events {
log::debug!("poll_oneoff_handle_fd_event fd_event = {:?}", fd_event);
log::debug!("poll_oneoff_handle_fd_event poll_fd = {:?}", poll_fd);
let revents = match poll_fd.revents() {
Some(revents) => revents,
None => continue,
};
log::debug!("poll_oneoff_handle_fd_event revents = {:?}", revents);
let mut nbytes = 0;
if fd_event.r#type == wasi::__WASI_EVENTTYPE_FD_READ {
let _ = unsafe { fionread(fd_event.descriptor.as_raw_fd(), &mut nbytes) };
}
let output_event = if revents.contains(PollFlags::POLLNVAL) {
wasi::__wasi_event_t {
userdata: fd_event.userdata,
r#type: fd_event.r#type,
error: wasi::__WASI_ERRNO_BADF,
u: wasi::__wasi_event_u_t {
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
nbytes: 0,
flags: wasi::__WASI_EVENTRWFLAGS_FD_READWRITE_HANGUP,
},
},
}
} else if revents.contains(PollFlags::POLLERR) {
wasi::__wasi_event_t {
userdata: fd_event.userdata,
r#type: fd_event.r#type,
error: wasi::__WASI_ERRNO_IO,
u: wasi::__wasi_event_u_t {
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
nbytes: 0,
flags: wasi::__WASI_EVENTRWFLAGS_FD_READWRITE_HANGUP,
},
},
}
} else if revents.contains(PollFlags::POLLHUP) {
wasi::__wasi_event_t {
userdata: fd_event.userdata,
r#type: fd_event.r#type,
error: wasi::__WASI_ERRNO_SUCCESS,
u: wasi::__wasi_event_u_t {
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
nbytes: 0,
flags: wasi::__WASI_EVENTRWFLAGS_FD_READWRITE_HANGUP,
},
},
}
} else if revents.contains(PollFlags::POLLIN) | revents.contains(PollFlags::POLLOUT) {
wasi::__wasi_event_t {
userdata: fd_event.userdata,
r#type: fd_event.r#type,
error: wasi::__WASI_ERRNO_SUCCESS,
u: wasi::__wasi_event_u_t {
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
nbytes: nbytes.try_into()?,
flags: 0,
},
},
}
} else {
continue;
};
events.push(output_event);
}
Ok(())
}

View File

@@ -0,0 +1,8 @@
//! Unix-specific hostcalls that implement
//! [WASI](https://github.com/bytecodealliance/wasmtime-wasi/blob/wasi/docs/WASI-overview.md).
mod fs;
pub(crate) mod fs_helpers;
mod misc;
pub(crate) use self::fs::*;
pub(crate) use self::misc::*;

View File

@@ -0,0 +1,61 @@
//! This internal module consists of helper types and functions for dealing
//! with setting the file times specific to Linux.
use super::super::filetime::FileTime;
use std::fs::File;
use std::io;
use std::sync::atomic::{AtomicBool, Ordering::Relaxed};
pub(crate) const UTIME_NOW: i64 = 1_073_741_823;
pub(crate) const UTIME_OMIT: i64 = 1_073_741_822;
/// Wrapper for `utimensat` syscall, however, with an added twist such that `utimensat` symbol
/// is firstly resolved (i.e., we check whether it exists on the host), and only used if that is
/// the case. Otherwise, the syscall resorts to a less accurate `utimesat` emulated syscall.
/// The original implementation can be found here: [filetime::unix::linux::set_times]
///
/// [filetime::unix::linux::set_times]: https://github.com/alexcrichton/filetime/blob/master/src/unix/linux.rs#L64
pub(crate) fn utimensat(
dirfd: &File,
path: &str,
atime: FileTime,
mtime: FileTime,
symlink_nofollow: bool,
) -> io::Result<()> {
use super::super::filetime::{to_timespec, utimesat};
use std::ffi::CString;
use std::os::unix::prelude::*;
let flags = if symlink_nofollow {
libc::AT_SYMLINK_NOFOLLOW
} else {
0
};
// Attempt to use the `utimensat` syscall, but if it's not supported by the
// current kernel then fall back to an older syscall.
static INVALID: AtomicBool = AtomicBool::new(false);
if !INVALID.load(Relaxed) {
let p = CString::new(path.as_bytes())?;
let times = [to_timespec(&atime), to_timespec(&mtime)];
let rc = unsafe {
libc::syscall(
libc::SYS_utimensat,
dirfd.as_raw_fd(),
p.as_ptr(),
times.as_ptr(),
flags,
)
};
if rc == 0 {
return Ok(());
}
let err = io::Error::last_os_error();
if err.raw_os_error() == Some(libc::ENOSYS) {
INVALID.store(true, Relaxed);
} else {
return Err(err);
}
}
utimesat(dirfd, path, atime, mtime, symlink_nofollow)
}

View File

@@ -0,0 +1,165 @@
use super::super::dir::{Dir, Entry, SeekLoc};
use super::osfile::OsFile;
use crate::old::snapshot_0::hostcalls_impl::{Dirent, PathGet};
use crate::old::snapshot_0::sys::host_impl;
use crate::old::snapshot_0::sys::unix::str_to_cstring;
use crate::old::snapshot_0::{wasi, Error, Result};
use log::trace;
use std::convert::TryInto;
use std::fs::File;
use std::os::unix::prelude::AsRawFd;
pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> {
use nix::errno;
use nix::libc::unlinkat;
let path_cstr = str_to_cstring(resolved.path())?;
// nix doesn't expose unlinkat() yet
let res = unsafe { unlinkat(resolved.dirfd().as_raw_fd(), path_cstr.as_ptr(), 0) };
if res == 0 {
Ok(())
} else {
Err(host_impl::errno_from_nix(errno::Errno::last()))
}
}
pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
use nix::{errno::Errno, libc::symlinkat};
let old_path_cstr = str_to_cstring(old_path)?;
let new_path_cstr = str_to_cstring(resolved.path())?;
log::debug!("path_symlink old_path = {:?}", old_path);
log::debug!("path_symlink resolved = {:?}", resolved);
let res = unsafe {
symlinkat(
old_path_cstr.as_ptr(),
resolved.dirfd().as_raw_fd(),
new_path_cstr.as_ptr(),
)
};
if res != 0 {
Err(host_impl::errno_from_nix(Errno::last()))
} else {
Ok(())
}
}
pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> {
use nix::libc::renameat;
let old_path_cstr = str_to_cstring(resolved_old.path())?;
let new_path_cstr = str_to_cstring(resolved_new.path())?;
let res = unsafe {
renameat(
resolved_old.dirfd().as_raw_fd(),
old_path_cstr.as_ptr(),
resolved_new.dirfd().as_raw_fd(),
new_path_cstr.as_ptr(),
)
};
if res != 0 {
Err(host_impl::errno_from_nix(nix::errno::Errno::last()))
} else {
Ok(())
}
}
pub(crate) fn fd_readdir_impl(
fd: &File,
cookie: wasi::__wasi_dircookie_t,
) -> Result<impl Iterator<Item = Result<Dirent>>> {
// We need to duplicate the fd, because `opendir(3)`:
// After a successful call to fdopendir(), fd is used internally by the implementation,
// and should not otherwise be used by the application.
// `opendir(3p)` also says that it's undefined behavior to
// modify the state of the fd in a different way than by accessing DIR*.
//
// Still, rewinddir will be needed because the two file descriptors
// share progress. But we can safely execute closedir now.
let fd = fd.try_clone()?;
let mut dir = Dir::from(fd)?;
// Seek if needed. Unless cookie is wasi::__WASI_DIRCOOKIE_START,
// new items may not be returned to the caller.
//
// According to `opendir(3p)`:
// If a file is removed from or added to the directory after the most recent call
// to opendir() or rewinddir(), whether a subsequent call to readdir() returns an entry
// for that file is unspecified.
if cookie == wasi::__WASI_DIRCOOKIE_START {
trace!(" | fd_readdir: doing rewinddir");
dir.rewind();
} else {
trace!(" | fd_readdir: doing seekdir to {}", cookie);
let loc = unsafe { SeekLoc::from_raw(cookie as i64) };
dir.seek(loc);
}
Ok(dir.into_iter().map(|entry| {
let entry: Entry = entry?;
Ok(Dirent {
name: entry // TODO can we reuse path_from_host for CStr?
.file_name()
.to_str()?
.to_owned(),
ino: entry.ino(),
ftype: entry.file_type().into(),
cookie: entry.seek_loc().to_raw().try_into()?,
})
}))
}
// This should actually be common code with Windows,
// but there's BSD stuff remaining
pub(crate) fn fd_readdir(
os_file: &mut OsFile,
mut host_buf: &mut [u8],
cookie: wasi::__wasi_dircookie_t,
) -> Result<usize> {
let iter = fd_readdir_impl(os_file, cookie)?;
let mut used = 0;
for dirent in iter {
let dirent_raw = dirent?.to_wasi_raw()?;
let offset = dirent_raw.len();
if host_buf.len() < offset {
break;
} else {
host_buf[0..offset].copy_from_slice(&dirent_raw);
used += offset;
host_buf = &mut host_buf[offset..];
}
}
trace!(" | *buf_used={:?}", used);
Ok(used)
}
pub(crate) fn fd_advise(
file: &File,
advice: wasi::__wasi_advice_t,
offset: wasi::__wasi_filesize_t,
len: wasi::__wasi_filesize_t,
) -> Result<()> {
{
use nix::fcntl::{posix_fadvise, PosixFadviseAdvice};
let offset = offset.try_into()?;
let len = len.try_into()?;
let host_advice = match advice {
wasi::__WASI_ADVICE_DONTNEED => PosixFadviseAdvice::POSIX_FADV_DONTNEED,
wasi::__WASI_ADVICE_SEQUENTIAL => PosixFadviseAdvice::POSIX_FADV_SEQUENTIAL,
wasi::__WASI_ADVICE_WILLNEED => PosixFadviseAdvice::POSIX_FADV_WILLNEED,
wasi::__WASI_ADVICE_NOREUSE => PosixFadviseAdvice::POSIX_FADV_NOREUSE,
wasi::__WASI_ADVICE_RANDOM => PosixFadviseAdvice::POSIX_FADV_RANDOM,
wasi::__WASI_ADVICE_NORMAL => PosixFadviseAdvice::POSIX_FADV_NORMAL,
_ => return Err(Error::EINVAL),
};
posix_fadvise(file.as_raw_fd(), offset, len, host_advice)?;
}
Ok(())
}

View File

@@ -0,0 +1,33 @@
pub(crate) mod filetime;
pub(crate) mod hostcalls_impl;
pub(crate) mod osfile;
pub(crate) mod fdentry_impl {
use crate::old::snapshot_0::{sys::host_impl, Result};
use std::os::unix::prelude::AsRawFd;
pub(crate) unsafe fn isatty(fd: &impl AsRawFd) -> Result<bool> {
use nix::errno::Errno;
let res = libc::isatty(fd.as_raw_fd());
if res == 1 {
// isatty() returns 1 if fd is an open file descriptor referring to a terminal...
Ok(true)
} else {
// ... otherwise 0 is returned, and errno is set to indicate the error.
match Errno::last() {
// While POSIX specifies ENOTTY if the passed
// fd is *not* a tty, on Linux, some implementations
// may return EINVAL instead.
//
// https://linux.die.net/man/3/isatty
Errno::ENOTTY | Errno::EINVAL => Ok(false),
x => Err(host_impl::errno_from_nix(x)),
}
}
}
}
pub(crate) mod host_impl {
pub(crate) const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_RSYNC;
}

View File

@@ -0,0 +1,32 @@
use std::fs;
use std::ops::{Deref, DerefMut};
use std::os::unix::prelude::{AsRawFd, RawFd};
#[derive(Debug)]
pub(crate) struct OsFile(fs::File);
impl From<fs::File> for OsFile {
fn from(file: fs::File) -> Self {
Self(file)
}
}
impl AsRawFd for OsFile {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl Deref for OsFile {
type Target = fs::File;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for OsFile {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

View File

@@ -0,0 +1,34 @@
pub(crate) mod fdentry_impl;
pub(crate) mod host_impl;
pub(crate) mod hostcalls_impl;
mod dir;
mod filetime;
#[cfg(any(
target_os = "macos",
target_os = "netbsd",
target_os = "freebsd",
target_os = "openbsd",
target_os = "ios",
target_os = "dragonfly"
))]
mod bsd;
#[cfg(target_os = "linux")]
mod linux;
use crate::old::snapshot_0::{Error, Result};
use std::ffi::CString;
use std::fs::{File, OpenOptions};
pub(crate) fn dev_null() -> Result<File> {
OpenOptions::new()
.read(true)
.write(true)
.open("/dev/null")
.map_err(Into::into)
}
pub(crate) fn str_to_cstring(s: &str) -> Result<CString> {
CString::new(s.as_bytes()).map_err(|_| Error::EILSEQ)
}

View File

@@ -0,0 +1,129 @@
use crate::old::snapshot_0::fdentry::Descriptor;
use crate::old::snapshot_0::{wasi, Error, Result};
use std::fs::File;
use std::io;
use std::ops::{Deref, DerefMut};
use std::os::windows::prelude::{AsRawHandle, FromRawHandle, RawHandle};
#[derive(Debug)]
pub(crate) struct OsFile(File);
impl From<File> for OsFile {
fn from(file: File) -> Self {
Self(file)
}
}
impl AsRawHandle for OsFile {
fn as_raw_handle(&self) -> RawHandle {
self.0.as_raw_handle()
}
}
impl Deref for OsFile {
type Target = File;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for OsFile {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl AsRawHandle for Descriptor {
fn as_raw_handle(&self) -> RawHandle {
match self {
Self::OsFile(file) => file.as_raw_handle(),
Self::Stdin => io::stdin().as_raw_handle(),
Self::Stdout => io::stdout().as_raw_handle(),
Self::Stderr => io::stderr().as_raw_handle(),
}
}
}
/// This function is unsafe because it operates on a raw file handle.
pub(crate) unsafe fn determine_type_and_access_rights<Handle: AsRawHandle>(
handle: &Handle,
) -> Result<(
wasi::__wasi_filetype_t,
wasi::__wasi_rights_t,
wasi::__wasi_rights_t,
)> {
use winx::file::{get_file_access_mode, AccessMode};
let (file_type, mut rights_base, rights_inheriting) = determine_type_rights(handle)?;
match file_type {
wasi::__WASI_FILETYPE_DIRECTORY | wasi::__WASI_FILETYPE_REGULAR_FILE => {
let mode = get_file_access_mode(handle.as_raw_handle())?;
if mode.contains(AccessMode::FILE_GENERIC_READ) {
rights_base |= wasi::__WASI_RIGHTS_FD_READ;
}
if mode.contains(AccessMode::FILE_GENERIC_WRITE) {
rights_base |= wasi::__WASI_RIGHTS_FD_WRITE;
}
}
_ => {
// TODO: is there a way around this? On windows, it seems
// we cannot check access rights for anything but dirs and regular files
}
}
Ok((file_type, rights_base, rights_inheriting))
}
/// This function is unsafe because it operates on a raw file handle.
pub(crate) unsafe fn determine_type_rights<Handle: AsRawHandle>(
handle: &Handle,
) -> Result<(
wasi::__wasi_filetype_t,
wasi::__wasi_rights_t,
wasi::__wasi_rights_t,
)> {
let (file_type, rights_base, rights_inheriting) = {
let file_type = winx::file::get_file_type(handle.as_raw_handle())?;
if file_type.is_char() {
// character file: LPT device or console
// TODO: rule out LPT device
(
wasi::__WASI_FILETYPE_CHARACTER_DEVICE,
wasi::RIGHTS_TTY_BASE,
wasi::RIGHTS_TTY_BASE,
)
} else if file_type.is_disk() {
// disk file: file, dir or disk device
let file = std::mem::ManuallyDrop::new(File::from_raw_handle(handle.as_raw_handle()));
let meta = file.metadata().map_err(|_| Error::EINVAL)?;
if meta.is_dir() {
(
wasi::__WASI_FILETYPE_DIRECTORY,
wasi::RIGHTS_DIRECTORY_BASE,
wasi::RIGHTS_DIRECTORY_INHERITING,
)
} else if meta.is_file() {
(
wasi::__WASI_FILETYPE_REGULAR_FILE,
wasi::RIGHTS_REGULAR_FILE_BASE,
wasi::RIGHTS_REGULAR_FILE_INHERITING,
)
} else {
return Err(Error::EINVAL);
}
} else if file_type.is_pipe() {
// pipe object: socket, named pipe or anonymous pipe
// TODO: what about pipes, etc?
(
wasi::__WASI_FILETYPE_SOCKET_STREAM,
wasi::RIGHTS_SOCKET_BASE,
wasi::RIGHTS_SOCKET_INHERITING,
)
} else {
return Err(Error::EINVAL);
}
};
Ok((file_type, rights_base, rights_inheriting))
}

View File

@@ -0,0 +1,108 @@
//! WASI host types specific to Windows host.
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(unused)]
use crate::old::snapshot_0::{wasi, Error, Result};
use std::ffi::OsStr;
use std::fs::OpenOptions;
use std::os::windows::ffi::OsStrExt;
use std::os::windows::fs::OpenOptionsExt;
use winx::file::{AccessMode, Attributes, CreationDisposition, Flags};
pub(crate) fn errno_from_win(error: winx::winerror::WinError) -> wasi::__wasi_errno_t {
// TODO: implement error mapping between Windows and WASI
use winx::winerror::WinError::*;
match error {
ERROR_SUCCESS => wasi::__WASI_ERRNO_SUCCESS,
ERROR_BAD_ENVIRONMENT => wasi::__WASI_ERRNO_2BIG,
ERROR_FILE_NOT_FOUND => wasi::__WASI_ERRNO_NOENT,
ERROR_PATH_NOT_FOUND => wasi::__WASI_ERRNO_NOENT,
ERROR_TOO_MANY_OPEN_FILES => wasi::__WASI_ERRNO_NFILE,
ERROR_ACCESS_DENIED => wasi::__WASI_ERRNO_ACCES,
ERROR_SHARING_VIOLATION => wasi::__WASI_ERRNO_ACCES,
ERROR_PRIVILEGE_NOT_HELD => wasi::__WASI_ERRNO_NOTCAPABLE, // TODO is this the correct mapping?
ERROR_INVALID_HANDLE => wasi::__WASI_ERRNO_BADF,
ERROR_INVALID_NAME => wasi::__WASI_ERRNO_NOENT,
ERROR_NOT_ENOUGH_MEMORY => wasi::__WASI_ERRNO_NOMEM,
ERROR_OUTOFMEMORY => wasi::__WASI_ERRNO_NOMEM,
ERROR_DIR_NOT_EMPTY => wasi::__WASI_ERRNO_NOTEMPTY,
ERROR_NOT_READY => wasi::__WASI_ERRNO_BUSY,
ERROR_BUSY => wasi::__WASI_ERRNO_BUSY,
ERROR_NOT_SUPPORTED => wasi::__WASI_ERRNO_NOTSUP,
ERROR_FILE_EXISTS => wasi::__WASI_ERRNO_EXIST,
ERROR_BROKEN_PIPE => wasi::__WASI_ERRNO_PIPE,
ERROR_BUFFER_OVERFLOW => wasi::__WASI_ERRNO_NAMETOOLONG,
ERROR_NOT_A_REPARSE_POINT => wasi::__WASI_ERRNO_INVAL,
ERROR_NEGATIVE_SEEK => wasi::__WASI_ERRNO_INVAL,
ERROR_DIRECTORY => wasi::__WASI_ERRNO_NOTDIR,
ERROR_ALREADY_EXISTS => wasi::__WASI_ERRNO_EXIST,
_ => wasi::__WASI_ERRNO_NOTSUP,
}
}
pub(crate) fn fdflags_from_win(mode: AccessMode) -> wasi::__wasi_fdflags_t {
let mut fdflags = 0;
// TODO verify this!
if mode.contains(AccessMode::FILE_APPEND_DATA) {
fdflags |= wasi::__WASI_FDFLAGS_APPEND;
}
if mode.contains(AccessMode::SYNCHRONIZE) {
fdflags |= wasi::__WASI_FDFLAGS_DSYNC;
fdflags |= wasi::__WASI_FDFLAGS_RSYNC;
fdflags |= wasi::__WASI_FDFLAGS_SYNC;
}
// The NONBLOCK equivalent is FILE_FLAG_OVERLAPPED
// but it seems winapi doesn't provide a mechanism
// for checking whether the handle supports async IO.
// On the contrary, I've found some dicsussion online
// which suggests that on Windows all handles should
// generally be assumed to be opened with async support
// and then the program should fallback should that **not**
// be the case at the time of the operation.
// TODO: this requires further investigation
fdflags
}
pub(crate) fn win_from_fdflags(fdflags: wasi::__wasi_fdflags_t) -> (AccessMode, Flags) {
let mut access_mode = AccessMode::empty();
let mut flags = Flags::empty();
// TODO verify this!
if fdflags & wasi::__WASI_FDFLAGS_NONBLOCK != 0 {
flags.insert(Flags::FILE_FLAG_OVERLAPPED);
}
if fdflags & wasi::__WASI_FDFLAGS_APPEND != 0 {
access_mode.insert(AccessMode::FILE_APPEND_DATA);
}
if fdflags & wasi::__WASI_FDFLAGS_DSYNC != 0
|| fdflags & wasi::__WASI_FDFLAGS_RSYNC != 0
|| fdflags & wasi::__WASI_FDFLAGS_SYNC != 0
{
access_mode.insert(AccessMode::SYNCHRONIZE);
}
(access_mode, flags)
}
pub(crate) fn win_from_oflags(oflags: wasi::__wasi_oflags_t) -> CreationDisposition {
if oflags & wasi::__WASI_OFLAGS_CREAT != 0 {
if oflags & wasi::__WASI_OFLAGS_EXCL != 0 {
CreationDisposition::CREATE_NEW
} else {
CreationDisposition::CREATE_ALWAYS
}
} else if oflags & wasi::__WASI_OFLAGS_TRUNC != 0 {
CreationDisposition::TRUNCATE_EXISTING
} else {
CreationDisposition::OPEN_EXISTING
}
}
/// Creates owned WASI path from OS string.
///
/// NB WASI spec requires OS string to be valid UTF-8. Otherwise,
/// `__WASI_ERRNO_ILSEQ` error is returned.
pub(crate) fn path_from_host<S: AsRef<OsStr>>(s: S) -> Result<String> {
let vec: Vec<u16> = s.as_ref().encode_wide().collect();
String::from_utf16(&vec).map_err(|_| Error::EILSEQ)
}

View File

@@ -0,0 +1,552 @@
#![allow(non_camel_case_types)]
#![allow(unused)]
use super::fs_helpers::*;
use crate::old::snapshot_0::ctx::WasiCtx;
use crate::old::snapshot_0::fdentry::FdEntry;
use crate::old::snapshot_0::helpers::systemtime_to_timestamp;
use crate::old::snapshot_0::hostcalls_impl::{
fd_filestat_set_times_impl, Dirent, FileType, PathGet,
};
use crate::old::snapshot_0::sys::fdentry_impl::{determine_type_rights, OsFile};
use crate::old::snapshot_0::sys::host_impl::{self, path_from_host};
use crate::old::snapshot_0::sys::hostcalls_impl::fs_helpers::PathGetExt;
use crate::old::snapshot_0::{wasi, Error, Result};
use log::{debug, trace};
use std::convert::TryInto;
use std::fs::{File, Metadata, OpenOptions};
use std::io::{self, Seek, SeekFrom};
use std::os::windows::fs::{FileExt, OpenOptionsExt};
use std::os::windows::prelude::{AsRawHandle, FromRawHandle};
use std::path::{Path, PathBuf};
use winx::file::{AccessMode, Flags};
fn read_at(mut file: &File, buf: &mut [u8], offset: u64) -> io::Result<usize> {
// get current cursor position
let cur_pos = file.seek(SeekFrom::Current(0))?;
// perform a seek read by a specified offset
let nread = file.seek_read(buf, offset)?;
// rewind the cursor back to the original position
file.seek(SeekFrom::Start(cur_pos))?;
Ok(nread)
}
fn write_at(mut file: &File, buf: &[u8], offset: u64) -> io::Result<usize> {
// get current cursor position
let cur_pos = file.seek(SeekFrom::Current(0))?;
// perform a seek write by a specified offset
let nwritten = file.seek_write(buf, offset)?;
// rewind the cursor back to the original position
file.seek(SeekFrom::Start(cur_pos))?;
Ok(nwritten)
}
// TODO refactor common code with unix
pub(crate) fn fd_pread(
file: &File,
buf: &mut [u8],
offset: wasi::__wasi_filesize_t,
) -> Result<usize> {
read_at(file, buf, offset).map_err(Into::into)
}
// TODO refactor common code with unix
pub(crate) fn fd_pwrite(file: &File, buf: &[u8], offset: wasi::__wasi_filesize_t) -> Result<usize> {
write_at(file, buf, offset).map_err(Into::into)
}
pub(crate) fn fd_fdstat_get(fd: &File) -> Result<wasi::__wasi_fdflags_t> {
use winx::file::AccessMode;
unsafe { winx::file::get_file_access_mode(fd.as_raw_handle()) }
.map(host_impl::fdflags_from_win)
.map_err(Into::into)
}
pub(crate) fn fd_fdstat_set_flags(fd: &File, fdflags: wasi::__wasi_fdflags_t) -> Result<()> {
unimplemented!("fd_fdstat_set_flags")
}
pub(crate) fn fd_advise(
_file: &File,
advice: wasi::__wasi_advice_t,
_offset: wasi::__wasi_filesize_t,
_len: wasi::__wasi_filesize_t,
) -> Result<()> {
match advice {
wasi::__WASI_ADVICE_DONTNEED
| wasi::__WASI_ADVICE_SEQUENTIAL
| wasi::__WASI_ADVICE_WILLNEED
| wasi::__WASI_ADVICE_NOREUSE
| wasi::__WASI_ADVICE_RANDOM
| wasi::__WASI_ADVICE_NORMAL => {}
_ => return Err(Error::EINVAL),
}
Ok(())
}
pub(crate) fn path_create_directory(resolved: PathGet) -> Result<()> {
let path = resolved.concatenate()?;
std::fs::create_dir(&path).map_err(Into::into)
}
pub(crate) fn path_link(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> {
unimplemented!("path_link")
}
pub(crate) fn path_open(
resolved: PathGet,
read: bool,
write: bool,
oflags: wasi::__wasi_oflags_t,
fdflags: wasi::__wasi_fdflags_t,
) -> Result<File> {
use winx::file::{AccessMode, CreationDisposition, Flags};
let mut access_mode = AccessMode::READ_CONTROL;
if read {
access_mode.insert(AccessMode::FILE_GENERIC_READ);
}
if write {
access_mode.insert(AccessMode::FILE_GENERIC_WRITE);
}
let mut flags = Flags::FILE_FLAG_BACKUP_SEMANTICS;
// convert open flags
let mut opts = OpenOptions::new();
match host_impl::win_from_oflags(oflags) {
CreationDisposition::CREATE_ALWAYS => {
opts.create(true).append(true);
}
CreationDisposition::CREATE_NEW => {
opts.create_new(true).write(true);
}
CreationDisposition::TRUNCATE_EXISTING => {
opts.truncate(true);
}
_ => {}
}
// convert file descriptor flags
let (add_access_mode, add_flags) = host_impl::win_from_fdflags(fdflags);
access_mode.insert(add_access_mode);
flags.insert(add_flags);
let path = resolved.concatenate()?;
match path.symlink_metadata().map(|metadata| metadata.file_type()) {
Ok(file_type) => {
// check if we are trying to open a symlink
if file_type.is_symlink() {
return Err(Error::ELOOP);
}
// check if we are trying to open a file as a dir
if file_type.is_file() && oflags & wasi::__WASI_OFLAGS_DIRECTORY != 0 {
return Err(Error::ENOTDIR);
}
}
Err(e) => match e.raw_os_error() {
Some(e) => {
use winx::winerror::WinError;
log::debug!("path_open at symlink_metadata error code={:?}", e);
let e = WinError::from_u32(e as u32);
if e != WinError::ERROR_FILE_NOT_FOUND {
return Err(e.into());
}
// file not found, let it proceed to actually
// trying to open it
}
None => {
log::debug!("Inconvertible OS error: {}", e);
return Err(Error::EIO);
}
},
}
opts.access_mode(access_mode.bits())
.custom_flags(flags.bits())
.open(&path)
.map_err(Into::into)
}
fn dirent_from_path<P: AsRef<Path>>(
path: P,
name: &str,
cookie: wasi::__wasi_dircookie_t,
) -> Result<Dirent> {
let path = path.as_ref();
trace!("dirent_from_path: opening {}", path.to_string_lossy());
// To open a directory on Windows, FILE_FLAG_BACKUP_SEMANTICS flag must be used
let file = OpenOptions::new()
.custom_flags(Flags::FILE_FLAG_BACKUP_SEMANTICS.bits())
.read(true)
.open(path)?;
let ty = file.metadata()?.file_type();
Ok(Dirent {
ftype: filetype_from_std(&ty),
name: name.to_owned(),
cookie,
ino: file_serial_no(&file)?,
})
}
// On Windows there is apparently no support for seeking the directory stream in the OS.
// cf. https://github.com/WebAssembly/WASI/issues/61
//
// The implementation here may perform in O(n^2) if the host buffer is O(1)
// and the number of directory entries is O(n).
// TODO: Add a heuristic optimization to achieve O(n) time in the most common case
// where fd_readdir is resumed where it previously finished
//
// Correctness of this approach relies upon one assumption: that the order of entries
// returned by `FindNextFileW` is stable, i.e. doesn't change if the directory
// contents stay the same. This invariant is crucial to be able to implement
// any kind of seeking whatsoever without having to read the whole directory at once
// and then return the data from cache. (which leaks memory)
//
// The MSDN documentation explicitly says that the order in which the search returns the files
// is not guaranteed, and is dependent on the file system.
// cf. https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findnextfilew
//
// This stackoverflow post suggests that `FindNextFileW` is indeed stable and that
// the order of directory entries depends **only** on the filesystem used, but the
// MSDN documentation is not clear about this.
// cf. https://stackoverflow.com/questions/47380739/is-findfirstfile-and-findnextfile-order-random-even-for-dvd
//
// Implementation details:
// Cookies for the directory entries start from 1. (0 is reserved by wasi::__WASI_DIRCOOKIE_START)
// . gets cookie = 1
// .. gets cookie = 2
// other entries, in order they were returned by FindNextFileW get subsequent integers as their cookies
pub(crate) fn fd_readdir_impl(
fd: &File,
cookie: wasi::__wasi_dircookie_t,
) -> Result<impl Iterator<Item = Result<Dirent>>> {
use winx::file::get_file_path;
let cookie = cookie.try_into()?;
let path = get_file_path(fd)?;
// std::fs::ReadDir doesn't return . and .., so we need to emulate it
let path = Path::new(&path);
// The directory /.. is the same as / on Unix (at least on ext4), so emulate this behavior too
let parent = path.parent().unwrap_or(path);
let dot = dirent_from_path(path, ".", 1)?;
let dotdot = dirent_from_path(parent, "..", 2)?;
trace!(" | fd_readdir impl: executing std::fs::ReadDir");
let iter = path.read_dir()?.zip(3..).map(|(dir, no)| {
let dir: std::fs::DirEntry = dir?;
Ok(Dirent {
name: path_from_host(dir.file_name())?,
ftype: filetype_from_std(&dir.file_type()?),
ino: File::open(dir.path()).and_then(|f| file_serial_no(&f))?,
cookie: no,
})
});
// into_iter for arrays is broken and returns references instead of values,
// so we need to use vec![...] and do heap allocation
// See https://github.com/rust-lang/rust/issues/25725
let iter = vec![dot, dotdot].into_iter().map(Ok).chain(iter);
// Emulate seekdir(). This may give O(n^2) complexity if used with a
// small host_buf, but this is difficult to implement efficiently.
//
// See https://github.com/WebAssembly/WASI/issues/61 for more details.
Ok(iter.skip(cookie))
}
// This should actually be common code with Linux
pub(crate) fn fd_readdir(
os_file: &mut OsFile,
mut host_buf: &mut [u8],
cookie: wasi::__wasi_dircookie_t,
) -> Result<usize> {
let iter = fd_readdir_impl(os_file, cookie)?;
let mut used = 0;
for dirent in iter {
let dirent_raw = dirent?.to_wasi_raw()?;
let offset = dirent_raw.len();
if host_buf.len() < offset {
break;
} else {
host_buf[0..offset].copy_from_slice(&dirent_raw);
used += offset;
host_buf = &mut host_buf[offset..];
}
}
trace!(" | *buf_used={:?}", used);
Ok(used)
}
pub(crate) fn path_readlink(resolved: PathGet, buf: &mut [u8]) -> Result<usize> {
use winx::file::get_file_path;
let path = resolved.concatenate()?;
let target_path = path.read_link()?;
// since on Windows we are effectively emulating 'at' syscalls
// we need to strip the prefix from the absolute path
// as otherwise we will error out since WASI is not capable
// of dealing with absolute paths
let dir_path = get_file_path(resolved.dirfd())?;
let dir_path = PathBuf::from(strip_extended_prefix(dir_path));
let target_path = target_path
.strip_prefix(dir_path)
.map_err(|_| Error::ENOTCAPABLE)
.and_then(|path| path.to_str().map(String::from).ok_or(Error::EILSEQ))?;
if buf.len() > 0 {
let mut chars = target_path.chars();
let mut nread = 0usize;
for i in 0..buf.len() {
match chars.next() {
Some(ch) => {
buf[i] = ch as u8;
nread += 1;
}
None => break,
}
}
Ok(nread)
} else {
Ok(0)
}
}
fn strip_trailing_slashes_and_concatenate(resolved: &PathGet) -> Result<Option<PathBuf>> {
if resolved.path().ends_with('/') {
let suffix = resolved.path().trim_end_matches('/');
concatenate(resolved.dirfd(), Path::new(suffix)).map(Some)
} else {
Ok(None)
}
}
pub(crate) fn path_rename(resolved_old: PathGet, resolved_new: PathGet) -> Result<()> {
use std::fs;
let old_path = resolved_old.concatenate()?;
let new_path = resolved_new.concatenate()?;
// First sanity check: check we're not trying to rename dir to file or vice versa.
// NB on Windows, the former is actually permitted [std::fs::rename].
//
// [std::fs::rename]: https://doc.rust-lang.org/std/fs/fn.rename.html
if old_path.is_dir() && new_path.is_file() {
return Err(Error::ENOTDIR);
}
// Second sanity check: check we're not trying to rename a file into a path
// ending in a trailing slash.
if old_path.is_file() && resolved_new.path().ends_with('/') {
return Err(Error::ENOTDIR);
}
// TODO handle symlinks
fs::rename(&old_path, &new_path).or_else(|e| match e.raw_os_error() {
Some(e) => {
use winx::winerror::WinError;
log::debug!("path_rename at rename error code={:?}", e);
match WinError::from_u32(e as u32) {
WinError::ERROR_ACCESS_DENIED => {
// So most likely dealing with new_path == dir.
// Eliminate case old_path == file first.
if old_path.is_file() {
Err(Error::EISDIR)
} else {
// Ok, let's try removing an empty dir at new_path if it exists
// and is a nonempty dir.
fs::remove_dir(&new_path)
.and_then(|()| fs::rename(old_path, new_path))
.map_err(Into::into)
}
}
WinError::ERROR_INVALID_NAME => {
// If source contains trailing slashes, check if we are dealing with
// a file instead of a dir, and if so, throw ENOTDIR.
if let Some(path) = strip_trailing_slashes_and_concatenate(&resolved_old)? {
if path.is_file() {
return Err(Error::ENOTDIR);
}
}
Err(WinError::ERROR_INVALID_NAME.into())
}
e => Err(e.into()),
}
}
None => {
log::debug!("Inconvertible OS error: {}", e);
Err(Error::EIO)
}
})
}
pub(crate) fn num_hardlinks(file: &File, _metadata: &Metadata) -> io::Result<u64> {
Ok(winx::file::get_fileinfo(file)?.nNumberOfLinks.into())
}
pub(crate) fn device_id(file: &File, _metadata: &Metadata) -> io::Result<u64> {
Ok(winx::file::get_fileinfo(file)?.dwVolumeSerialNumber.into())
}
pub(crate) fn file_serial_no(file: &File) -> io::Result<u64> {
let info = winx::file::get_fileinfo(file)?;
let high = info.nFileIndexHigh;
let low = info.nFileIndexLow;
let no = ((high as u64) << 32) | (low as u64);
Ok(no)
}
pub(crate) fn change_time(file: &File, _metadata: &Metadata) -> io::Result<i64> {
winx::file::change_time(file)
}
pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result<wasi::__wasi_filestat_t> {
let metadata = file.metadata()?;
Ok(wasi::__wasi_filestat_t {
dev: device_id(file, &metadata)?,
ino: file_serial_no(file)?,
nlink: num_hardlinks(file, &metadata)?.try_into()?, // u64 doesn't fit into u32
size: metadata.len(),
atim: systemtime_to_timestamp(metadata.accessed()?)?,
ctim: change_time(file, &metadata)?.try_into()?, // i64 doesn't fit into u64
mtim: systemtime_to_timestamp(metadata.modified()?)?,
filetype: filetype_from_std(&metadata.file_type()).to_wasi(),
})
}
pub(crate) fn filetype_from_std(ftype: &std::fs::FileType) -> FileType {
if ftype.is_file() {
FileType::RegularFile
} else if ftype.is_dir() {
FileType::Directory
} else if ftype.is_symlink() {
FileType::Symlink
} else {
FileType::Unknown
}
}
pub(crate) fn path_filestat_get(
resolved: PathGet,
dirflags: wasi::__wasi_lookupflags_t,
) -> Result<wasi::__wasi_filestat_t> {
let path = resolved.concatenate()?;
let file = File::open(path)?;
fd_filestat_get_impl(&file)
}
pub(crate) fn path_filestat_set_times(
resolved: PathGet,
dirflags: wasi::__wasi_lookupflags_t,
st_atim: wasi::__wasi_timestamp_t,
mut st_mtim: wasi::__wasi_timestamp_t,
fst_flags: wasi::__wasi_fstflags_t,
) -> Result<()> {
use winx::file::AccessMode;
let path = resolved.concatenate()?;
let file = OpenOptions::new()
.access_mode(AccessMode::FILE_WRITE_ATTRIBUTES.bits())
.open(path)?;
fd_filestat_set_times_impl(&file, st_atim, st_mtim, fst_flags)
}
pub(crate) fn path_symlink(old_path: &str, resolved: PathGet) -> Result<()> {
use std::os::windows::fs::{symlink_dir, symlink_file};
use winx::winerror::WinError;
let old_path = concatenate(resolved.dirfd(), Path::new(old_path))?;
let new_path = resolved.concatenate()?;
// try creating a file symlink
symlink_file(&old_path, &new_path).or_else(|e| {
match e.raw_os_error() {
Some(e) => {
log::debug!("path_symlink at symlink_file error code={:?}", e);
match WinError::from_u32(e as u32) {
WinError::ERROR_NOT_A_REPARSE_POINT => {
// try creating a dir symlink instead
symlink_dir(old_path, new_path).map_err(Into::into)
}
WinError::ERROR_ACCESS_DENIED => {
// does the target exist?
if new_path.exists() {
Err(Error::EEXIST)
} else {
Err(WinError::ERROR_ACCESS_DENIED.into())
}
}
WinError::ERROR_INVALID_NAME => {
// does the target without trailing slashes exist?
if let Some(path) = strip_trailing_slashes_and_concatenate(&resolved)? {
if path.exists() {
return Err(Error::EEXIST);
}
}
Err(WinError::ERROR_INVALID_NAME.into())
}
e => Err(e.into()),
}
}
None => {
log::debug!("Inconvertible OS error: {}", e);
Err(Error::EIO)
}
}
})
}
pub(crate) fn path_unlink_file(resolved: PathGet) -> Result<()> {
use std::fs;
use winx::winerror::WinError;
let path = resolved.concatenate()?;
let file_type = path
.symlink_metadata()
.map(|metadata| metadata.file_type())?;
// check if we're unlinking a symlink
// NB this will get cleaned up a lot when [std::os::windows::fs::FileTypeExt]
// stabilises
//
// [std::os::windows::fs::FileTypeExt]: https://doc.rust-lang.org/std/os/windows/fs/trait.FileTypeExt.html
if file_type.is_symlink() {
fs::remove_file(&path).or_else(|e| {
match e.raw_os_error() {
Some(e) => {
log::debug!("path_unlink_file at symlink_file error code={:?}", e);
match WinError::from_u32(e as u32) {
WinError::ERROR_ACCESS_DENIED => {
// try unlinking a dir symlink instead
fs::remove_dir(path).map_err(Into::into)
}
e => Err(e.into()),
}
}
None => {
log::debug!("Inconvertible OS error: {}", e);
Err(Error::EIO)
}
}
})
} else if file_type.is_dir() {
Err(Error::EISDIR)
} else if file_type.is_file() {
fs::remove_file(path).map_err(Into::into)
} else {
Err(Error::EINVAL)
}
}
pub(crate) fn path_remove_directory(resolved: PathGet) -> Result<()> {
let path = resolved.concatenate()?;
std::fs::remove_dir(&path).map_err(Into::into)
}

View File

@@ -0,0 +1,149 @@
#![allow(non_camel_case_types)]
use crate::old::snapshot_0::hostcalls_impl::PathGet;
use crate::old::snapshot_0::{wasi, Error, Result};
use std::ffi::{OsStr, OsString};
use std::fs::File;
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use std::path::{Path, PathBuf};
pub(crate) trait PathGetExt {
fn concatenate(&self) -> Result<PathBuf>;
}
impl PathGetExt for PathGet {
fn concatenate(&self) -> Result<PathBuf> {
concatenate(self.dirfd(), Path::new(self.path()))
}
}
pub(crate) fn path_open_rights(
rights_base: wasi::__wasi_rights_t,
rights_inheriting: wasi::__wasi_rights_t,
oflags: wasi::__wasi_oflags_t,
fdflags: wasi::__wasi_fdflags_t,
) -> (wasi::__wasi_rights_t, wasi::__wasi_rights_t) {
// which rights are needed on the dirfd?
let mut needed_base = wasi::__WASI_RIGHTS_PATH_OPEN;
let mut needed_inheriting = rights_base | rights_inheriting;
// convert open flags
if oflags & wasi::__WASI_OFLAGS_CREAT != 0 {
needed_base |= wasi::__WASI_RIGHTS_PATH_CREATE_FILE;
} else if oflags & wasi::__WASI_OFLAGS_TRUNC != 0 {
needed_base |= wasi::__WASI_RIGHTS_PATH_FILESTAT_SET_SIZE;
}
// convert file descriptor flags
if fdflags & wasi::__WASI_FDFLAGS_DSYNC != 0
|| fdflags & wasi::__WASI_FDFLAGS_RSYNC != 0
|| fdflags & wasi::__WASI_FDFLAGS_SYNC != 0
{
needed_inheriting |= wasi::__WASI_RIGHTS_FD_DATASYNC;
needed_inheriting |= wasi::__WASI_RIGHTS_FD_SYNC;
}
(needed_base, needed_inheriting)
}
pub(crate) fn openat(dirfd: &File, path: &str) -> Result<File> {
use std::fs::OpenOptions;
use std::os::windows::fs::OpenOptionsExt;
use winx::file::Flags;
use winx::winerror::WinError;
let path = concatenate(dirfd, Path::new(path))?;
OpenOptions::new()
.read(true)
.custom_flags(Flags::FILE_FLAG_BACKUP_SEMANTICS.bits())
.open(&path)
.map_err(|e| match e.raw_os_error() {
Some(e) => {
log::debug!("openat error={:?}", e);
match WinError::from_u32(e as u32) {
WinError::ERROR_INVALID_NAME => Error::ENOTDIR,
e => e.into(),
}
}
None => {
log::debug!("Inconvertible OS error: {}", e);
Error::EIO
}
})
}
pub(crate) fn readlinkat(dirfd: &File, s_path: &str) -> Result<String> {
use winx::file::get_file_path;
use winx::winerror::WinError;
let path = concatenate(dirfd, Path::new(s_path))?;
match path.read_link() {
Ok(target_path) => {
// since on Windows we are effectively emulating 'at' syscalls
// we need to strip the prefix from the absolute path
// as otherwise we will error out since WASI is not capable
// of dealing with absolute paths
let dir_path = get_file_path(dirfd)?;
let dir_path = PathBuf::from(strip_extended_prefix(dir_path));
target_path
.strip_prefix(dir_path)
.map_err(|_| Error::ENOTCAPABLE)
.and_then(|path| path.to_str().map(String::from).ok_or(Error::EILSEQ))
}
Err(e) => match e.raw_os_error() {
Some(e) => {
log::debug!("readlinkat error={:?}", e);
match WinError::from_u32(e as u32) {
WinError::ERROR_INVALID_NAME => {
if s_path.ends_with('/') {
// strip "/" and check if exists
let path = concatenate(dirfd, Path::new(s_path.trim_end_matches('/')))?;
if path.exists() && !path.is_dir() {
Err(Error::ENOTDIR)
} else {
Err(Error::ENOENT)
}
} else {
Err(Error::ENOENT)
}
}
e => Err(e.into()),
}
}
None => {
log::debug!("Inconvertible OS error: {}", e);
Err(Error::EIO)
}
},
}
}
pub(crate) fn strip_extended_prefix<P: AsRef<OsStr>>(path: P) -> OsString {
let path: Vec<u16> = path.as_ref().encode_wide().collect();
if &[92, 92, 63, 92] == &path[0..4] {
OsString::from_wide(&path[4..])
} else {
OsString::from_wide(&path)
}
}
pub(crate) fn concatenate<P: AsRef<Path>>(dirfd: &File, path: P) -> Result<PathBuf> {
use winx::file::get_file_path;
// WASI is not able to deal with absolute paths
// so error out if absolute
if path.as_ref().is_absolute() {
return Err(Error::ENOTCAPABLE);
}
let dir_path = get_file_path(dirfd)?;
// concatenate paths
let mut out_path = PathBuf::from(dir_path);
out_path.push(path.as_ref());
// strip extended prefix; otherwise we will error out on any relative
// components with `out_path`
let out_path = PathBuf::from(strip_extended_prefix(out_path));
log::debug!("out_path={:?}", out_path);
Ok(out_path)
}

View File

@@ -0,0 +1,119 @@
#![allow(non_camel_case_types)]
#![allow(unused_unsafe)]
#![allow(unused)]
use crate::old::snapshot_0::helpers::systemtime_to_timestamp;
use crate::old::snapshot_0::hostcalls_impl::{ClockEventData, FdEventData};
use crate::old::snapshot_0::memory::*;
use crate::old::snapshot_0::sys::host_impl;
use crate::old::snapshot_0::{wasi, wasi32, Error, Result};
use cpu_time::{ProcessTime, ThreadTime};
use lazy_static::lazy_static;
use std::convert::TryInto;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
lazy_static! {
static ref START_MONOTONIC: Instant = Instant::now();
static ref PERF_COUNTER_RES: u64 = get_perf_counter_resolution_ns();
}
// Timer resolution on Windows is really hard. We may consider exposing the resolution of the respective
// timers as an associated function in the future.
pub(crate) fn clock_res_get(clock_id: wasi::__wasi_clockid_t) -> Result<wasi::__wasi_timestamp_t> {
Ok(match clock_id {
// This is the best that we can do with std::time::SystemTime.
// Rust uses GetSystemTimeAsFileTime, which is said to have the resolution of
// 10ms or 55ms, [1] but MSDN doesn't confirm this in any way.
// Even the MSDN article on high resolution timestamps doesn't even mention the precision
// for this method. [3]
//
// The timer resolution can be queried using one of the functions: [2, 5]
// * NtQueryTimerResolution, which is undocumented and thus not exposed by the winapi crate
// * timeGetDevCaps, which returns the upper and lower bound for the precision, in ms.
// While the upper bound seems like something we could use, it's typically too high to be meaningful.
// For instance, the intervals return by the syscall are:
// * [1, 65536] on Wine
// * [1, 1000000] on Windows 10, which is up to (sic) 1000 seconds.
//
// It's possible to manually set the timer resolution, but this sounds like something which should
// only be done temporarily. [5]
//
// Alternatively, we could possibly use GetSystemTimePreciseAsFileTime in clock_time_get, but
// this syscall is only available starting from Windows 8.
// (we could possibly emulate it on earlier versions of Windows, see [4])
// The MSDN are not clear on the resolution of GetSystemTimePreciseAsFileTime either, but a
// Microsoft devblog entry [1] suggests that it kind of combines GetSystemTimeAsFileTime with
// QueryPeformanceCounter, which probably means that those two should have the same resolution.
//
// See also this discussion about the use of GetSystemTimePreciseAsFileTime in Python stdlib,
// which in particular contains some resolution benchmarks.
//
// [1] https://devblogs.microsoft.com/oldnewthing/20170921-00/?p=97057
// [2] http://www.windowstimestamp.com/description
// [3] https://docs.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps?redirectedfrom=MSDN
// [4] https://www.codeproject.com/Tips/1011902/High-Resolution-Time-For-Windows
// [5] https://stackoverflow.com/questions/7685762/windows-7-timing-functions-how-to-use-getsystemtimeadjustment-correctly
// [6] https://bugs.python.org/issue19007
wasi::__WASI_CLOCKID_REALTIME => 55_000_000,
// std::time::Instant uses QueryPerformanceCounter & QueryPerformanceFrequency internally
wasi::__WASI_CLOCKID_MONOTONIC => *PERF_COUNTER_RES,
// The best we can do is to hardcode the value from the docs.
// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getprocesstimes
wasi::__WASI_CLOCKID_PROCESS_CPUTIME_ID => 100,
// The best we can do is to hardcode the value from the docs.
// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getthreadtimes
wasi::__WASI_CLOCKID_THREAD_CPUTIME_ID => 100,
_ => return Err(Error::EINVAL),
})
}
pub(crate) fn clock_time_get(clock_id: wasi::__wasi_clockid_t) -> Result<wasi::__wasi_timestamp_t> {
let duration = match clock_id {
wasi::__WASI_CLOCKID_REALTIME => get_monotonic_time(),
wasi::__WASI_CLOCKID_MONOTONIC => get_realtime_time()?,
wasi::__WASI_CLOCKID_PROCESS_CPUTIME_ID => get_proc_cputime()?,
wasi::__WASI_CLOCKID_THREAD_CPUTIME_ID => get_thread_cputime()?,
_ => return Err(Error::EINVAL),
};
duration.as_nanos().try_into().map_err(Into::into)
}
pub(crate) fn poll_oneoff(
timeout: Option<ClockEventData>,
fd_events: Vec<FdEventData>,
events: &mut Vec<wasi::__wasi_event_t>,
) -> Result<Vec<wasi::__wasi_event_t>> {
unimplemented!("poll_oneoff")
}
fn get_monotonic_time() -> Duration {
// We're circumventing the fact that we can't get a Duration from an Instant
// The epoch of __WASI_CLOCKID_MONOTONIC is undefined, so we fix a time point once
// and count relative to this time point.
//
// The alternative would be to copy over the implementation of std::time::Instant
// to our source tree and add a conversion to std::time::Duration
START_MONOTONIC.elapsed()
}
fn get_realtime_time() -> Result<Duration> {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_err(|_| Error::EFAULT)
}
fn get_proc_cputime() -> Result<Duration> {
Ok(ProcessTime::try_now()?.as_duration())
}
fn get_thread_cputime() -> Result<Duration> {
Ok(ThreadTime::try_now()?.as_duration())
}
fn get_perf_counter_resolution_ns() -> u64 {
use winx::time::perf_counter_frequency;
const NANOS_PER_SEC: u64 = 1_000_000_000;
// This should always succeed starting from Windows XP, so it's fine to panic in case of an error.
let freq = perf_counter_frequency().expect("QueryPerformanceFrequency returned an error");
let epsilon = NANOS_PER_SEC / freq;
epsilon
}

View File

@@ -0,0 +1,8 @@
//! Windows-specific hostcalls that implement
//! [WASI](https://github.com/bytecodealliance/wasmtime-wasi/blob/wasi/docs/WASI-overview.md).
mod fs;
pub(crate) mod fs_helpers;
mod misc;
pub(crate) use self::fs::*;
pub(crate) use self::misc::*;

View File

@@ -0,0 +1,32 @@
pub(crate) mod fdentry_impl;
pub(crate) mod host_impl;
pub(crate) mod hostcalls_impl;
use crate::old::snapshot_0::Result;
use std::fs::{File, OpenOptions};
use std::path::Path;
pub(crate) fn dev_null() -> Result<File> {
OpenOptions::new()
.read(true)
.write(true)
.open("NUL")
.map_err(Into::into)
}
pub fn preopen_dir<P: AsRef<Path>>(path: P) -> Result<File> {
use std::fs::OpenOptions;
use std::os::windows::fs::OpenOptionsExt;
use winapi::um::winbase::FILE_FLAG_BACKUP_SEMANTICS;
// To open a directory using CreateFile, specify the
// FILE_FLAG_BACKUP_SEMANTICS flag as part of dwFileFlags...
// cf. https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfile2
OpenOptions::new()
.create(false)
.write(true)
.read(true)
.attributes(FILE_FLAG_BACKUP_SEMANTICS)
.open(path)
.map_err(Into::into)
}

View File

@@ -0,0 +1,957 @@
//! Types and constants shared between 32-bit and 64-bit wasi. Types involving
//! pointer or `usize`-sized data are excluded here, so this file only contains
//! fixed-size types, so it's host/target independent.
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(dead_code)]
use wig::witx_wasi_types;
witx_wasi_types!("old/snapshot_0" "wasi_unstable");
pub(crate) const RIGHTS_ALL: __wasi_rights_t = __WASI_RIGHTS_FD_DATASYNC
| __WASI_RIGHTS_FD_READ
| __WASI_RIGHTS_FD_SEEK
| __WASI_RIGHTS_FD_FDSTAT_SET_FLAGS
| __WASI_RIGHTS_FD_SYNC
| __WASI_RIGHTS_FD_TELL
| __WASI_RIGHTS_FD_WRITE
| __WASI_RIGHTS_FD_ADVISE
| __WASI_RIGHTS_FD_ALLOCATE
| __WASI_RIGHTS_PATH_CREATE_DIRECTORY
| __WASI_RIGHTS_PATH_CREATE_FILE
| __WASI_RIGHTS_PATH_LINK_SOURCE
| __WASI_RIGHTS_PATH_LINK_TARGET
| __WASI_RIGHTS_PATH_OPEN
| __WASI_RIGHTS_FD_READDIR
| __WASI_RIGHTS_PATH_READLINK
| __WASI_RIGHTS_PATH_RENAME_SOURCE
| __WASI_RIGHTS_PATH_RENAME_TARGET
| __WASI_RIGHTS_PATH_FILESTAT_GET
| __WASI_RIGHTS_PATH_FILESTAT_SET_SIZE
| __WASI_RIGHTS_PATH_FILESTAT_SET_TIMES
| __WASI_RIGHTS_FD_FILESTAT_GET
| __WASI_RIGHTS_FD_FILESTAT_SET_SIZE
| __WASI_RIGHTS_FD_FILESTAT_SET_TIMES
| __WASI_RIGHTS_PATH_SYMLINK
| __WASI_RIGHTS_PATH_UNLINK_FILE
| __WASI_RIGHTS_PATH_REMOVE_DIRECTORY
| __WASI_RIGHTS_POLL_FD_READWRITE
| __WASI_RIGHTS_SOCK_SHUTDOWN;
// Block and character device interaction is outside the scope of
// WASI. Simply allow everything.
pub(crate) const RIGHTS_BLOCK_DEVICE_BASE: __wasi_rights_t = RIGHTS_ALL;
pub(crate) const RIGHTS_BLOCK_DEVICE_INHERITING: __wasi_rights_t = RIGHTS_ALL;
pub(crate) const RIGHTS_CHARACTER_DEVICE_BASE: __wasi_rights_t = RIGHTS_ALL;
pub(crate) 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(crate) const RIGHTS_DIRECTORY_BASE: __wasi_rights_t = __WASI_RIGHTS_FD_FDSTAT_SET_FLAGS
| __WASI_RIGHTS_FD_SYNC
| __WASI_RIGHTS_FD_ADVISE
| __WASI_RIGHTS_PATH_CREATE_DIRECTORY
| __WASI_RIGHTS_PATH_CREATE_FILE
| __WASI_RIGHTS_PATH_LINK_SOURCE
| __WASI_RIGHTS_PATH_LINK_TARGET
| __WASI_RIGHTS_PATH_OPEN
| __WASI_RIGHTS_FD_READDIR
| __WASI_RIGHTS_PATH_READLINK
| __WASI_RIGHTS_PATH_RENAME_SOURCE
| __WASI_RIGHTS_PATH_RENAME_TARGET
| __WASI_RIGHTS_PATH_FILESTAT_GET
| __WASI_RIGHTS_PATH_FILESTAT_SET_SIZE
| __WASI_RIGHTS_PATH_FILESTAT_SET_TIMES
| __WASI_RIGHTS_FD_FILESTAT_GET
| __WASI_RIGHTS_FD_FILESTAT_SET_TIMES
| __WASI_RIGHTS_PATH_SYMLINK
| __WASI_RIGHTS_PATH_UNLINK_FILE
| __WASI_RIGHTS_PATH_REMOVE_DIRECTORY
| __WASI_RIGHTS_POLL_FD_READWRITE;
pub(crate) const RIGHTS_DIRECTORY_INHERITING: __wasi_rights_t =
RIGHTS_DIRECTORY_BASE | RIGHTS_REGULAR_FILE_BASE;
// Operations that apply to regular files.
pub(crate) const RIGHTS_REGULAR_FILE_BASE: __wasi_rights_t = __WASI_RIGHTS_FD_DATASYNC
| __WASI_RIGHTS_FD_READ
| __WASI_RIGHTS_FD_SEEK
| __WASI_RIGHTS_FD_FDSTAT_SET_FLAGS
| __WASI_RIGHTS_FD_SYNC
| __WASI_RIGHTS_FD_TELL
| __WASI_RIGHTS_FD_WRITE
| __WASI_RIGHTS_FD_ADVISE
| __WASI_RIGHTS_FD_ALLOCATE
| __WASI_RIGHTS_FD_FILESTAT_GET
| __WASI_RIGHTS_FD_FILESTAT_SET_SIZE
| __WASI_RIGHTS_FD_FILESTAT_SET_TIMES
| __WASI_RIGHTS_POLL_FD_READWRITE;
pub(crate) const RIGHTS_REGULAR_FILE_INHERITING: __wasi_rights_t = 0;
// Operations that apply to sockets and socket pairs.
pub(crate) const RIGHTS_SOCKET_BASE: __wasi_rights_t = __WASI_RIGHTS_FD_READ
| __WASI_RIGHTS_FD_FDSTAT_SET_FLAGS
| __WASI_RIGHTS_FD_WRITE
| __WASI_RIGHTS_FD_FILESTAT_GET
| __WASI_RIGHTS_POLL_FD_READWRITE
| __WASI_RIGHTS_SOCK_SHUTDOWN;
pub(crate) const RIGHTS_SOCKET_INHERITING: __wasi_rights_t = RIGHTS_ALL;
// Operations that apply to TTYs.
pub(crate) const RIGHTS_TTY_BASE: __wasi_rights_t = __WASI_RIGHTS_FD_READ
| __WASI_RIGHTS_FD_FDSTAT_SET_FLAGS
| __WASI_RIGHTS_FD_WRITE
| __WASI_RIGHTS_FD_FILESTAT_GET
| __WASI_RIGHTS_POLL_FD_READWRITE;
#[allow(unused)]
pub(crate) const RIGHTS_TTY_INHERITING: __wasi_rights_t = 0;
pub fn strerror(errno: __wasi_errno_t) -> &'static str {
match errno {
__WASI_ERRNO_SUCCESS => "__WASI_ERRNO_SUCCESS",
__WASI_ERRNO_2BIG => "__WASI_ERRNO_2BIG",
__WASI_ERRNO_ACCES => "__WASI_ERRNO_ACCES",
__WASI_ERRNO_ADDRINUSE => "__WASI_ERRNO_ADDRINUSE",
__WASI_ERRNO_ADDRNOTAVAIL => "__WASI_ERRNO_ADDRNOTAVAIL",
__WASI_ERRNO_AFNOSUPPORT => "__WASI_ERRNO_AFNOSUPPORT",
__WASI_ERRNO_AGAIN => "__WASI_ERRNO_AGAIN",
__WASI_ERRNO_ALREADY => "__WASI_ERRNO_ALREADY",
__WASI_ERRNO_BADF => "__WASI_ERRNO_BADF",
__WASI_ERRNO_BADMSG => "__WASI_ERRNO_BADMSG",
__WASI_ERRNO_BUSY => "__WASI_ERRNO_BUSY",
__WASI_ERRNO_CANCELED => "__WASI_ERRNO_CANCELED",
__WASI_ERRNO_CHILD => "__WASI_ERRNO_CHILD",
__WASI_ERRNO_CONNABORTED => "__WASI_ERRNO_CONNABORTED",
__WASI_ERRNO_CONNREFUSED => "__WASI_ERRNO_CONNREFUSED",
__WASI_ERRNO_CONNRESET => "__WASI_ERRNO_CONNRESET",
__WASI_ERRNO_DEADLK => "__WASI_ERRNO_DEADLK",
__WASI_ERRNO_DESTADDRREQ => "__WASI_ERRNO_DESTADDRREQ",
__WASI_ERRNO_DOM => "__WASI_ERRNO_DOM",
__WASI_ERRNO_DQUOT => "__WASI_ERRNO_DQUOT",
__WASI_ERRNO_EXIST => "__WASI_ERRNO_EXIST",
__WASI_ERRNO_FAULT => "__WASI_ERRNO_FAULT",
__WASI_ERRNO_FBIG => "__WASI_ERRNO_FBIG",
__WASI_ERRNO_HOSTUNREACH => "__WASI_ERRNO_HOSTUNREACH",
__WASI_ERRNO_IDRM => "__WASI_ERRNO_IDRM",
__WASI_ERRNO_ILSEQ => "__WASI_ERRNO_ILSEQ",
__WASI_ERRNO_INPROGRESS => "__WASI_ERRNO_INPROGRESS",
__WASI_ERRNO_INTR => "__WASI_ERRNO_INTR",
__WASI_ERRNO_INVAL => "__WASI_ERRNO_INVAL",
__WASI_ERRNO_IO => "__WASI_ERRNO_IO",
__WASI_ERRNO_ISCONN => "__WASI_ERRNO_ISCONN",
__WASI_ERRNO_ISDIR => "__WASI_ERRNO_ISDIR",
__WASI_ERRNO_LOOP => "__WASI_ERRNO_LOOP",
__WASI_ERRNO_MFILE => "__WASI_ERRNO_MFILE",
__WASI_ERRNO_MLINK => "__WASI_ERRNO_MLINK",
__WASI_ERRNO_MSGSIZE => "__WASI_ERRNO_MSGSIZE",
__WASI_ERRNO_MULTIHOP => "__WASI_ERRNO_MULTIHOP",
__WASI_ERRNO_NAMETOOLONG => "__WASI_ERRNO_NAMETOOLONG",
__WASI_ERRNO_NETDOWN => "__WASI_ERRNO_NETDOWN",
__WASI_ERRNO_NETRESET => "__WASI_ERRNO_NETRESET",
__WASI_ERRNO_NETUNREACH => "__WASI_ERRNO_NETUNREACH",
__WASI_ERRNO_NFILE => "__WASI_ERRNO_NFILE",
__WASI_ERRNO_NOBUFS => "__WASI_ERRNO_NOBUFS",
__WASI_ERRNO_NODEV => "__WASI_ERRNO_NODEV",
__WASI_ERRNO_NOENT => "__WASI_ERRNO_NOENT",
__WASI_ERRNO_NOEXEC => "__WASI_ERRNO_NOEXEC",
__WASI_ERRNO_NOLCK => "__WASI_ERRNO_NOLCK",
__WASI_ERRNO_NOLINK => "__WASI_ERRNO_NOLINK",
__WASI_ERRNO_NOMEM => "__WASI_ERRNO_NOMEM",
__WASI_ERRNO_NOMSG => "__WASI_ERRNO_NOMSG",
__WASI_ERRNO_NOPROTOOPT => "__WASI_ERRNO_NOPROTOOPT",
__WASI_ERRNO_NOSPC => "__WASI_ERRNO_NOSPC",
__WASI_ERRNO_NOSYS => "__WASI_ERRNO_NOSYS",
__WASI_ERRNO_NOTCONN => "__WASI_ERRNO_NOTCONN",
__WASI_ERRNO_NOTDIR => "__WASI_ERRNO_NOTDIR",
__WASI_ERRNO_NOTEMPTY => "__WASI_ERRNO_NOTEMPTY",
__WASI_ERRNO_NOTRECOVERABLE => "__WASI_ERRNO_NOTRECOVERABLE",
__WASI_ERRNO_NOTSOCK => "__WASI_ERRNO_NOTSOCK",
__WASI_ERRNO_NOTSUP => "__WASI_ERRNO_NOTSUP",
__WASI_ERRNO_NOTTY => "__WASI_ERRNO_NOTTY",
__WASI_ERRNO_NXIO => "__WASI_ERRNO_NXIO",
__WASI_ERRNO_OVERFLOW => "__WASI_ERRNO_OVERFLOW",
__WASI_ERRNO_OWNERDEAD => "__WASI_ERRNO_OWNERDEAD",
__WASI_ERRNO_PERM => "__WASI_ERRNO_PERM",
__WASI_ERRNO_PIPE => "__WASI_ERRNO_PIPE",
__WASI_ERRNO_PROTO => "__WASI_ERRNO_PROTO",
__WASI_ERRNO_PROTONOSUPPORT => "__WASI_ERRNO_PROTONOSUPPORT",
__WASI_ERRNO_PROTOTYPE => "__WASI_ERRNO_PROTOTYPE",
__WASI_ERRNO_RANGE => "__WASI_ERRNO_RANGE",
__WASI_ERRNO_ROFS => "__WASI_ERRNO_ROFS",
__WASI_ERRNO_SPIPE => "__WASI_ERRNO_SPIPE",
__WASI_ERRNO_SRCH => "__WASI_ERRNO_SRCH",
__WASI_ERRNO_STALE => "__WASI_ERRNO_STALE",
__WASI_ERRNO_TIMEDOUT => "__WASI_ERRNO_TIMEDOUT",
__WASI_ERRNO_TXTBSY => "__WASI_ERRNO_TXTBSY",
__WASI_ERRNO_XDEV => "__WASI_ERRNO_XDEV",
__WASI_ERRNO_NOTCAPABLE => "__WASI_ERRNO_NOTCAPABLE",
other => panic!("Undefined errno value {:?}", other),
}
}
pub fn whence_to_str(whence: __wasi_whence_t) -> &'static str {
match whence {
__WASI_WHENCE_CUR => "__WASI_WHENCE_CUR",
__WASI_WHENCE_END => "__WASI_WHENCE_END",
__WASI_WHENCE_SET => "__WASI_WHENCE_SET",
other => panic!("Undefined whence value {:?}", other),
}
}
pub const __WASI_DIRCOOKIE_START: __wasi_dircookie_t = 0;
#[cfg(test)]
mod test {
use super::*;
#[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_fd_readwrite_t>(),
16usize,
concat!("Size of: ", stringify!(__wasi_event_fd_readwrite_t))
);
assert_eq!(
::std::mem::align_of::<__wasi_event_fd_readwrite_t>(),
8usize,
concat!("Alignment of ", stringify!(__wasi_event_fd_readwrite_t))
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<__wasi_event_fd_readwrite_t>())).nbytes as *const _ as usize
},
0usize,
concat!(
"Offset of field: ",
stringify!(__wasi_event_fd_readwrite_t),
"::",
stringify!(nbytes)
)
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<__wasi_event_fd_readwrite_t>())).flags as *const _ as usize
},
8usize,
concat!(
"Offset of field: ",
stringify!(__wasi_event_fd_readwrite_t),
"::",
stringify!(flags)
)
);
}
#[test]
fn bindgen_test_layout___wasi_event_t___wasi_event_u() {
assert_eq!(
::std::mem::size_of::<__wasi_event_u_t>(),
16usize,
concat!("Size of: ", stringify!(__wasi_event_u_t))
);
assert_eq!(
::std::mem::align_of::<__wasi_event_u_t>(),
8usize,
concat!("Alignment of ", stringify!(__wasi_event_u_t))
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<__wasi_event_u_t>())).fd_readwrite as *const _ as usize
},
0usize,
concat!(
"Offset of field: ",
stringify!(__wasi_event_u_t),
"::",
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>())).r#type as *const _ as usize },
10usize,
concat!(
"Offset of field: ",
stringify!(__wasi_event_t),
"::",
stringify!(r#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_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>())).r#type as *const _ as usize },
10usize,
concat!(
"Offset of field: ",
stringify!(__wasi_event_t),
"::",
stringify!(r#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>())).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>())).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>())).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>())).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>())).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>())).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>())).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>())).ctim as *const _ as usize },
48usize,
concat!(
"Offset of field: ",
stringify!(__wasi_filestat_t),
"::",
stringify!(st_ctim)
)
);
}
#[test]
fn bindgen_test_layout___wasi_subscription_clock_t() {
assert_eq!(
::std::mem::size_of::<__wasi_subscription_clock_t>(),
40usize,
concat!("Size of: ", stringify!(__wasi_subscription_clock_t))
);
assert_eq!(
::std::mem::align_of::<__wasi_subscription_clock_t>(),
8usize,
concat!("Alignment of ", stringify!(__wasi_subscription_clock_t))
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<__wasi_subscription_clock_t>())).identifier as *const _
as usize
},
0usize,
concat!(
"Offset of field: ",
stringify!(__wasi_subscription_clock_t),
"::",
stringify!(identifier)
)
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<__wasi_subscription_clock_t>())).id as *const _ as usize
},
8usize,
concat!(
"Offset of field: ",
stringify!(__wasi_subscription_clock_t),
"::",
stringify!(clock_id)
)
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<__wasi_subscription_clock_t>())).timeout as *const _ as usize
},
16usize,
concat!(
"Offset of field: ",
stringify!(__wasi_subscription_clock_t),
"::",
stringify!(timeout)
)
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<__wasi_subscription_clock_t>())).precision as *const _
as usize
},
24usize,
concat!(
"Offset of field: ",
stringify!(__wasi_subscription_clock_t),
"::",
stringify!(precision)
)
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<__wasi_subscription_clock_t>())).flags as *const _ as usize
},
32usize,
concat!(
"Offset of field: ",
stringify!(__wasi_subscription_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_fd_readwrite_t>(),
4usize,
concat!("Size of: ", stringify!(__wasi_subscription_fd_readwrite_t))
);
assert_eq!(
::std::mem::align_of::<__wasi_subscription_fd_readwrite_t>(),
4usize,
concat!(
"Alignment of ",
stringify!(__wasi_subscription_fd_readwrite_t)
)
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<__wasi_subscription_fd_readwrite_t>())).file_descriptor
as *const _ as usize
},
0usize,
concat!(
"Offset of field: ",
stringify!(__wasi_subscription_fd_readwrite_t),
"::",
stringify!(fd)
)
);
}
#[test]
fn bindgen_test_layout___wasi_subscription_t___wasi_subscription_u() {
assert_eq!(
::std::mem::size_of::<__wasi_subscription_u_t>(),
40usize,
concat!("Size of: ", stringify!(__wasi_subscription_u_t))
);
assert_eq!(
::std::mem::align_of::<__wasi_subscription_u_t>(),
8usize,
concat!("Alignment of ", stringify!(__wasi_subscription_u_t))
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<__wasi_subscription_u_t>())).clock as *const _ as usize
},
0usize,
concat!(
"Offset of field: ",
stringify!(__wasi_subscription_u_t),
"::",
stringify!(clock)
)
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<__wasi_subscription_u_t>())).fd_readwrite as *const _
as usize
},
0usize,
concat!(
"Offset of field: ",
stringify!(__wasi_subscription_u_t),
"::",
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>())).r#type as *const _ as usize
},
8usize,
concat!(
"Offset of field: ",
stringify!(__wasi_subscription_t),
"::",
stringify!(r#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)
)
);
}
#[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!(
::std::mem::align_of::<__wasi_filestat_t>(),
8usize,
concat!("Alignment of ", stringify!(__wasi_filestat_t))
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).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>())).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>())).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>())).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>())).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>())).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>())).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>())).ctim as *const _ as usize },
48usize,
concat!(
"Offset of field: ",
stringify!(__wasi_filestat_t),
"::",
stringify!(st_ctim)
)
);
}
#[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!(
::std::mem::align_of::<__wasi_fdstat_t>(),
8usize,
concat!("Alignment 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_dirent_t() {
assert_eq!(
::std::mem::size_of::<__wasi_dirent_t>(),
24usize,
concat!("Size of: ", stringify!(__wasi_dirent_t))
);
assert_eq!(
::std::mem::align_of::<__wasi_dirent_t>(),
8usize,
concat!("Alignment 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)
)
);
}
}

View File

@@ -0,0 +1,172 @@
//! Types and constants specific to 32-bit wasi. These are similar to the types
//! in the `host` module, but pointers and `usize` values are replaced with
//! `u32`-sized types.
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(dead_code)]
use crate::old::snapshot_0::wasi::*;
use wig::witx_wasi32_types;
pub type uintptr_t = u32;
pub type size_t = u32;
witx_wasi32_types!("old/snapshot_0" "wasi_unstable");
#[cfg(test)]
mod test {
use super::*;
#[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_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t() {
assert_eq!(
::std::mem::size_of::<__wasi_prestat_dir_t>(),
4usize,
concat!("Size of: ", stringify!(__wasi_prestat_dir_t))
);
assert_eq!(
::std::mem::align_of::<__wasi_prestat_dir_t>(),
4usize,
concat!("Alignment of ", stringify!(__wasi_prestat_dir_t))
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<__wasi_prestat_dir_t>())).pr_name_len as *const _ as usize
},
0usize,
concat!(
"Offset of field: ",
stringify!(__wasi_prestat_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_u_t>(),
4usize,
concat!("Size of: ", stringify!(__wasi_prestat_u_t))
);
assert_eq!(
::std::mem::align_of::<__wasi_prestat_u_t>(),
4usize,
concat!("Alignment of ", stringify!(__wasi_prestat_u_t))
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<__wasi_prestat_u_t>())).dir as *const _ as usize },
0usize,
concat!(
"Offset of field: ",
stringify!(__wasi_prestat_u_t),
"::",
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)
)
);
}
}

View File

@@ -46,9 +46,9 @@ pub(crate) unsafe fn determine_type_and_access_rights<Fd: AsRawFd>(
let flags = OFlag::from_bits_truncate(flags_bits);
let accmode = flags & OFlag::O_ACCMODE;
if accmode == OFlag::O_RDONLY {
rights_base &= !wasi::__WASI_RIGHT_FD_WRITE;
rights_base &= !wasi::__WASI_RIGHTS_FD_WRITE;
} else if accmode == OFlag::O_WRONLY {
rights_base &= !wasi::__WASI_RIGHT_FD_READ;
rights_base &= !wasi::__WASI_RIGHTS_FD_READ;
}
Ok((file_type, rights_base, rights_inheriting))

View File

@@ -109,19 +109,19 @@ pub(crate) fn errno_from_nix(errno: nix::errno::Errno) -> Error {
pub(crate) fn nix_from_fdflags(fdflags: wasi::__wasi_fdflags_t) -> nix::fcntl::OFlag {
use nix::fcntl::OFlag;
let mut nix_flags = OFlag::empty();
if fdflags & wasi::__WASI_FDFLAG_APPEND != 0 {
if fdflags & wasi::__WASI_FDFLAGS_APPEND != 0 {
nix_flags.insert(OFlag::O_APPEND);
}
if fdflags & wasi::__WASI_FDFLAG_DSYNC != 0 {
if fdflags & wasi::__WASI_FDFLAGS_DSYNC != 0 {
nix_flags.insert(OFlag::O_DSYNC);
}
if fdflags & wasi::__WASI_FDFLAG_NONBLOCK != 0 {
if fdflags & wasi::__WASI_FDFLAGS_NONBLOCK != 0 {
nix_flags.insert(OFlag::O_NONBLOCK);
}
if fdflags & wasi::__WASI_FDFLAG_RSYNC != 0 {
if fdflags & wasi::__WASI_FDFLAGS_RSYNC != 0 {
nix_flags.insert(O_RSYNC);
}
if fdflags & wasi::__WASI_FDFLAG_SYNC != 0 {
if fdflags & wasi::__WASI_FDFLAGS_SYNC != 0 {
nix_flags.insert(OFlag::O_SYNC);
}
nix_flags
@@ -131,19 +131,19 @@ pub(crate) fn fdflags_from_nix(oflags: nix::fcntl::OFlag) -> wasi::__wasi_fdflag
use nix::fcntl::OFlag;
let mut fdflags = 0;
if oflags.contains(OFlag::O_APPEND) {
fdflags |= wasi::__WASI_FDFLAG_APPEND;
fdflags |= wasi::__WASI_FDFLAGS_APPEND;
}
if oflags.contains(OFlag::O_DSYNC) {
fdflags |= wasi::__WASI_FDFLAG_DSYNC;
fdflags |= wasi::__WASI_FDFLAGS_DSYNC;
}
if oflags.contains(OFlag::O_NONBLOCK) {
fdflags |= wasi::__WASI_FDFLAG_NONBLOCK;
fdflags |= wasi::__WASI_FDFLAGS_NONBLOCK;
}
if oflags.contains(O_RSYNC) {
fdflags |= wasi::__WASI_FDFLAG_RSYNC;
fdflags |= wasi::__WASI_FDFLAGS_RSYNC;
}
if oflags.contains(OFlag::O_SYNC) {
fdflags |= wasi::__WASI_FDFLAG_SYNC;
fdflags |= wasi::__WASI_FDFLAGS_SYNC;
}
fdflags
}
@@ -151,16 +151,16 @@ pub(crate) fn fdflags_from_nix(oflags: nix::fcntl::OFlag) -> wasi::__wasi_fdflag
pub(crate) fn nix_from_oflags(oflags: wasi::__wasi_oflags_t) -> nix::fcntl::OFlag {
use nix::fcntl::OFlag;
let mut nix_flags = OFlag::empty();
if oflags & wasi::__WASI_O_CREAT != 0 {
if oflags & wasi::__WASI_OFLAGS_CREAT != 0 {
nix_flags.insert(OFlag::O_CREAT);
}
if oflags & wasi::__WASI_O_DIRECTORY != 0 {
if oflags & wasi::__WASI_OFLAGS_DIRECTORY != 0 {
nix_flags.insert(OFlag::O_DIRECTORY);
}
if oflags & wasi::__WASI_O_EXCL != 0 {
if oflags & wasi::__WASI_OFLAGS_EXCL != 0 {
nix_flags.insert(OFlag::O_EXCL);
}
if oflags & wasi::__WASI_O_TRUNC != 0 {
if oflags & wasi::__WASI_OFLAGS_TRUNC != 0 {
nix_flags.insert(OFlag::O_TRUNC);
}
nix_flags
@@ -198,19 +198,19 @@ pub(crate) fn filestat_from_nix(
let filetype = nix::sys::stat::SFlag::from_bits_truncate(filestat.st_mode);
let dev = wasi::__wasi_device_t::try_from(filestat.st_dev)?;
let ino = wasi::__wasi_inode_t::try_from(filestat.st_ino)?;
let st_atim = filestat_to_timestamp(filestat.st_atime as u64, filestat.st_atime_nsec as u64)?;
let st_ctim = filestat_to_timestamp(filestat.st_ctime as u64, filestat.st_ctime_nsec as u64)?;
let st_mtim = filestat_to_timestamp(filestat.st_mtime as u64, filestat.st_mtime_nsec as u64)?;
let atim = filestat_to_timestamp(filestat.st_atime as u64, filestat.st_atime_nsec as u64)?;
let ctim = filestat_to_timestamp(filestat.st_ctime as u64, filestat.st_ctime_nsec as u64)?;
let mtim = filestat_to_timestamp(filestat.st_mtime as u64, filestat.st_mtime_nsec as u64)?;
Ok(wasi::__wasi_filestat_t {
st_dev: dev,
st_ino: ino,
st_nlink: filestat.st_nlink as wasi::__wasi_linkcount_t,
st_size: filestat.st_size as wasi::__wasi_filesize_t,
st_atim,
st_ctim,
st_mtim,
st_filetype: filetype_from_nix(filetype).to_wasi(),
dev,
ino,
nlink: wasi::__wasi_linkcount_t::from(filestat.st_nlink),
size: filestat.st_size as wasi::__wasi_filesize_t,
atim,
ctim,
mtim,
filetype: filetype_from_nix(filetype).to_wasi(),
})
}
@@ -240,7 +240,7 @@ pub(crate) fn dirent_filetype_from_host(
/// Creates owned WASI path from OS string.
///
/// NB WASI spec requires OS string to be valid UTF-8. Otherwise,
/// `__WASI_EILSEQ` error is returned.
/// `__WASI_ERRNO_ILSEQ` error is returned.
pub(crate) fn path_from_host<S: AsRef<OsStr>>(s: S) -> Result<String> {
helpers::path_from_slice(s.as_ref().as_bytes()).map(String::from)
}

View File

@@ -217,14 +217,14 @@ pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result<wasi::__wasi_
let metadata = file.metadata()?;
Ok(wasi::__wasi_filestat_t {
st_dev: metadata.dev(),
st_ino: metadata.ino(),
st_nlink: metadata.nlink().try_into()?, // u64 doesn't fit into u32
st_size: metadata.len(),
st_atim: systemtime_to_timestamp(metadata.accessed()?)?,
st_ctim: metadata.ctime().try_into()?, // i64 doesn't fit into u64
st_mtim: systemtime_to_timestamp(metadata.modified()?)?,
st_filetype: filetype(file, &metadata)?.to_wasi(),
dev: metadata.dev(),
ino: metadata.ino(),
nlink: metadata.nlink().try_into()?, // u64 doesn't fit into u32
size: metadata.len(),
atim: systemtime_to_timestamp(metadata.accessed()?)?,
ctim: metadata.ctime().try_into()?, // i64 doesn't fit into u64
mtim: systemtime_to_timestamp(metadata.modified()?)?,
filetype: filetype(file, &metadata)?.to_wasi(),
})
}
@@ -283,16 +283,16 @@ pub(crate) fn path_filestat_set_times(
use super::super::filetime::*;
use std::time::{Duration, UNIX_EPOCH};
let set_atim = fst_flags & wasi::__WASI_FILESTAT_SET_ATIM != 0;
let set_atim_now = fst_flags & wasi::__WASI_FILESTAT_SET_ATIM_NOW != 0;
let set_mtim = fst_flags & wasi::__WASI_FILESTAT_SET_MTIM != 0;
let set_mtim_now = fst_flags & wasi::__WASI_FILESTAT_SET_MTIM_NOW != 0;
let set_atim = fst_flags & wasi::__WASI_FSTFLAGS_ATIM != 0;
let set_atim_now = fst_flags & wasi::__WASI_FSTFLAGS_ATIM_NOW != 0;
let set_mtim = fst_flags & wasi::__WASI_FSTFLAGS_MTIM != 0;
let set_mtim_now = fst_flags & wasi::__WASI_FSTFLAGS_MTIM_NOW != 0;
if (set_atim && set_atim_now) || (set_mtim && set_mtim_now) {
return Err(Error::EINVAL);
}
let symlink_nofollow = wasi::__WASI_LOOKUP_SYMLINK_FOLLOW != dirflags;
let symlink_nofollow = wasi::__WASI_LOOKUPFLAGS_SYMLINK_FOLLOW != dirflags;
let atim = if set_atim {
let time = UNIX_EPOCH + Duration::from_nanos(st_atim);
FileTime::FileTime(filetime::FileTime::from_system_time(time))

View File

@@ -13,25 +13,25 @@ pub(crate) fn path_open_rights(
use nix::fcntl::OFlag;
// which rights are needed on the dirfd?
let mut needed_base = wasi::__WASI_RIGHT_PATH_OPEN;
let mut needed_base = wasi::__WASI_RIGHTS_PATH_OPEN;
let mut needed_inheriting = rights_base | rights_inheriting;
// convert open flags
let oflags = host_impl::nix_from_oflags(oflags);
if oflags.contains(OFlag::O_CREAT) {
needed_base |= wasi::__WASI_RIGHT_PATH_CREATE_FILE;
needed_base |= wasi::__WASI_RIGHTS_PATH_CREATE_FILE;
}
if oflags.contains(OFlag::O_TRUNC) {
needed_base |= wasi::__WASI_RIGHT_PATH_FILESTAT_SET_SIZE;
needed_base |= wasi::__WASI_RIGHTS_PATH_FILESTAT_SET_SIZE;
}
// convert file descriptor flags
let fdflags = host_impl::nix_from_fdflags(fs_flags);
if fdflags.contains(OFlag::O_DSYNC) {
needed_inheriting |= wasi::__WASI_RIGHT_FD_DATASYNC;
needed_inheriting |= wasi::__WASI_RIGHTS_FD_DATASYNC;
}
if fdflags.intersects(host_impl::O_RSYNC | OFlag::O_SYNC) {
needed_inheriting |= wasi::__WASI_RIGHT_FD_SYNC;
needed_inheriting |= wasi::__WASI_RIGHTS_FD_SYNC;
}
(needed_base, needed_inheriting)

View File

@@ -9,10 +9,10 @@ use std::mem::MaybeUninit;
fn wasi_clock_id_to_unix(clock_id: wasi::__wasi_clockid_t) -> Result<libc::clockid_t> {
// convert the supported clocks to the libc types, or return EINVAL
match clock_id {
wasi::__WASI_CLOCK_REALTIME => Ok(libc::CLOCK_REALTIME),
wasi::__WASI_CLOCK_MONOTONIC => Ok(libc::CLOCK_MONOTONIC),
wasi::__WASI_CLOCK_PROCESS_CPUTIME_ID => Ok(libc::CLOCK_PROCESS_CPUTIME_ID),
wasi::__WASI_CLOCK_THREAD_CPUTIME_ID => Ok(libc::CLOCK_THREAD_CPUTIME_ID),
wasi::__WASI_CLOCKID_REALTIME => Ok(libc::CLOCK_REALTIME),
wasi::__WASI_CLOCKID_MONOTONIC => Ok(libc::CLOCK_MONOTONIC),
wasi::__WASI_CLOCKID_PROCESS_CPUTIME_ID => Ok(libc::CLOCK_PROCESS_CPUTIME_ID),
wasi::__WASI_CLOCKID_THREAD_CPUTIME_ID => Ok(libc::CLOCK_THREAD_CPUTIME_ID),
_ => Err(Error::EINVAL),
}
}
@@ -129,8 +129,8 @@ fn poll_oneoff_handle_timeout_event(
events.push(wasi::__wasi_event_t {
userdata: timeout.userdata,
r#type: wasi::__WASI_EVENTTYPE_CLOCK,
error: wasi::__WASI_ESUCCESS,
u: wasi::__wasi_event_u {
error: wasi::__WASI_ERRNO_SUCCESS,
u: wasi::__wasi_event_u_t {
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
nbytes: 0,
flags: 0,
@@ -166,11 +166,11 @@ fn poll_oneoff_handle_fd_event<'a>(
wasi::__wasi_event_t {
userdata: fd_event.userdata,
r#type: fd_event.r#type,
error: wasi::__WASI_EBADF,
u: wasi::__wasi_event_u {
error: wasi::__WASI_ERRNO_BADF,
u: wasi::__wasi_event_u_t {
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
nbytes: 0,
flags: wasi::__WASI_EVENT_FD_READWRITE_HANGUP,
flags: wasi::__WASI_EVENTRWFLAGS_FD_READWRITE_HANGUP,
},
},
}
@@ -178,11 +178,11 @@ fn poll_oneoff_handle_fd_event<'a>(
wasi::__wasi_event_t {
userdata: fd_event.userdata,
r#type: fd_event.r#type,
error: wasi::__WASI_EIO,
u: wasi::__wasi_event_u {
error: wasi::__WASI_ERRNO_IO,
u: wasi::__wasi_event_u_t {
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
nbytes: 0,
flags: wasi::__WASI_EVENT_FD_READWRITE_HANGUP,
flags: wasi::__WASI_EVENTRWFLAGS_FD_READWRITE_HANGUP,
},
},
}
@@ -190,11 +190,11 @@ fn poll_oneoff_handle_fd_event<'a>(
wasi::__wasi_event_t {
userdata: fd_event.userdata,
r#type: fd_event.r#type,
error: wasi::__WASI_ESUCCESS,
u: wasi::__wasi_event_u {
error: wasi::__WASI_ERRNO_SUCCESS,
u: wasi::__wasi_event_u_t {
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
nbytes: 0,
flags: wasi::__WASI_EVENT_FD_READWRITE_HANGUP,
flags: wasi::__WASI_EVENTRWFLAGS_FD_READWRITE_HANGUP,
},
},
}
@@ -202,8 +202,8 @@ fn poll_oneoff_handle_fd_event<'a>(
wasi::__wasi_event_t {
userdata: fd_event.userdata,
r#type: fd_event.r#type,
error: wasi::__WASI_ESUCCESS,
u: wasi::__wasi_event_u {
error: wasi::__WASI_ERRNO_SUCCESS,
u: wasi::__wasi_event_u_t {
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
nbytes: nbytes.try_into()?,
flags: 0,

View File

@@ -61,10 +61,10 @@ pub(crate) unsafe fn determine_type_and_access_rights<Handle: AsRawHandle>(
wasi::__WASI_FILETYPE_DIRECTORY | wasi::__WASI_FILETYPE_REGULAR_FILE => {
let mode = get_file_access_mode(handle.as_raw_handle())?;
if mode.contains(AccessMode::FILE_GENERIC_READ) {
rights_base |= wasi::__WASI_RIGHT_FD_READ;
rights_base |= wasi::__WASI_RIGHTS_FD_READ;
}
if mode.contains(AccessMode::FILE_GENERIC_WRITE) {
rights_base |= wasi::__WASI_RIGHT_FD_WRITE;
rights_base |= wasi::__WASI_RIGHTS_FD_WRITE;
}
}
_ => {

View File

@@ -13,30 +13,30 @@ pub(crate) fn errno_from_win(error: winx::winerror::WinError) -> wasi::__wasi_er
// TODO: implement error mapping between Windows and WASI
use winx::winerror::WinError::*;
match error {
ERROR_SUCCESS => wasi::__WASI_ESUCCESS,
ERROR_BAD_ENVIRONMENT => wasi::__WASI_E2BIG,
ERROR_FILE_NOT_FOUND => wasi::__WASI_ENOENT,
ERROR_PATH_NOT_FOUND => wasi::__WASI_ENOENT,
ERROR_TOO_MANY_OPEN_FILES => wasi::__WASI_ENFILE,
ERROR_ACCESS_DENIED => wasi::__WASI_EACCES,
ERROR_SHARING_VIOLATION => wasi::__WASI_EACCES,
ERROR_PRIVILEGE_NOT_HELD => wasi::__WASI_ENOTCAPABLE, // TODO is this the correct mapping?
ERROR_INVALID_HANDLE => wasi::__WASI_EBADF,
ERROR_INVALID_NAME => wasi::__WASI_ENOENT,
ERROR_NOT_ENOUGH_MEMORY => wasi::__WASI_ENOMEM,
ERROR_OUTOFMEMORY => wasi::__WASI_ENOMEM,
ERROR_DIR_NOT_EMPTY => wasi::__WASI_ENOTEMPTY,
ERROR_NOT_READY => wasi::__WASI_EBUSY,
ERROR_BUSY => wasi::__WASI_EBUSY,
ERROR_NOT_SUPPORTED => wasi::__WASI_ENOTSUP,
ERROR_FILE_EXISTS => wasi::__WASI_EEXIST,
ERROR_BROKEN_PIPE => wasi::__WASI_EPIPE,
ERROR_BUFFER_OVERFLOW => wasi::__WASI_ENAMETOOLONG,
ERROR_NOT_A_REPARSE_POINT => wasi::__WASI_EINVAL,
ERROR_NEGATIVE_SEEK => wasi::__WASI_EINVAL,
ERROR_DIRECTORY => wasi::__WASI_ENOTDIR,
ERROR_ALREADY_EXISTS => wasi::__WASI_EEXIST,
_ => wasi::__WASI_ENOTSUP,
ERROR_SUCCESS => wasi::__WASI_ERRNO_SUCCESS,
ERROR_BAD_ENVIRONMENT => wasi::__WASI_ERRNO_2BIG,
ERROR_FILE_NOT_FOUND => wasi::__WASI_ERRNO_NOENT,
ERROR_PATH_NOT_FOUND => wasi::__WASI_ERRNO_NOENT,
ERROR_TOO_MANY_OPEN_FILES => wasi::__WASI_ERRNO_NFILE,
ERROR_ACCESS_DENIED => wasi::__WASI_ERRNO_ACCES,
ERROR_SHARING_VIOLATION => wasi::__WASI_ERRNO_ACCES,
ERROR_PRIVILEGE_NOT_HELD => wasi::__WASI_ERRNO_NOTCAPABLE, // TODO is this the correct mapping?
ERROR_INVALID_HANDLE => wasi::__WASI_ERRNO_BADF,
ERROR_INVALID_NAME => wasi::__WASI_ERRNO_NOENT,
ERROR_NOT_ENOUGH_MEMORY => wasi::__WASI_ERRNO_NOMEM,
ERROR_OUTOFMEMORY => wasi::__WASI_ERRNO_NOMEM,
ERROR_DIR_NOT_EMPTY => wasi::__WASI_ERRNO_NOTEMPTY,
ERROR_NOT_READY => wasi::__WASI_ERRNO_BUSY,
ERROR_BUSY => wasi::__WASI_ERRNO_BUSY,
ERROR_NOT_SUPPORTED => wasi::__WASI_ERRNO_NOTSUP,
ERROR_FILE_EXISTS => wasi::__WASI_ERRNO_EXIST,
ERROR_BROKEN_PIPE => wasi::__WASI_ERRNO_PIPE,
ERROR_BUFFER_OVERFLOW => wasi::__WASI_ERRNO_NAMETOOLONG,
ERROR_NOT_A_REPARSE_POINT => wasi::__WASI_ERRNO_INVAL,
ERROR_NEGATIVE_SEEK => wasi::__WASI_ERRNO_INVAL,
ERROR_DIRECTORY => wasi::__WASI_ERRNO_NOTDIR,
ERROR_ALREADY_EXISTS => wasi::__WASI_ERRNO_EXIST,
_ => wasi::__WASI_ERRNO_NOTSUP,
}
}
@@ -44,12 +44,12 @@ pub(crate) fn fdflags_from_win(mode: AccessMode) -> wasi::__wasi_fdflags_t {
let mut fdflags = 0;
// TODO verify this!
if mode.contains(AccessMode::FILE_APPEND_DATA) {
fdflags |= wasi::__WASI_FDFLAG_APPEND;
fdflags |= wasi::__WASI_FDFLAGS_APPEND;
}
if mode.contains(AccessMode::SYNCHRONIZE) {
fdflags |= wasi::__WASI_FDFLAG_DSYNC;
fdflags |= wasi::__WASI_FDFLAG_RSYNC;
fdflags |= wasi::__WASI_FDFLAG_SYNC;
fdflags |= wasi::__WASI_FDFLAGS_DSYNC;
fdflags |= wasi::__WASI_FDFLAGS_RSYNC;
fdflags |= wasi::__WASI_FDFLAGS_SYNC;
}
// The NONBLOCK equivalent is FILE_FLAG_OVERLAPPED
// but it seems winapi doesn't provide a mechanism
@@ -68,15 +68,15 @@ pub(crate) fn win_from_fdflags(fdflags: wasi::__wasi_fdflags_t) -> (AccessMode,
let mut flags = Flags::empty();
// TODO verify this!
if fdflags & wasi::__WASI_FDFLAG_NONBLOCK != 0 {
if fdflags & wasi::__WASI_FDFLAGS_NONBLOCK != 0 {
flags.insert(Flags::FILE_FLAG_OVERLAPPED);
}
if fdflags & wasi::__WASI_FDFLAG_APPEND != 0 {
if fdflags & wasi::__WASI_FDFLAGS_APPEND != 0 {
access_mode.insert(AccessMode::FILE_APPEND_DATA);
}
if fdflags & wasi::__WASI_FDFLAG_DSYNC != 0
|| fdflags & wasi::__WASI_FDFLAG_RSYNC != 0
|| fdflags & wasi::__WASI_FDFLAG_SYNC != 0
if fdflags & wasi::__WASI_FDFLAGS_DSYNC != 0
|| fdflags & wasi::__WASI_FDFLAGS_RSYNC != 0
|| fdflags & wasi::__WASI_FDFLAGS_SYNC != 0
{
access_mode.insert(AccessMode::SYNCHRONIZE);
}
@@ -85,13 +85,13 @@ pub(crate) fn win_from_fdflags(fdflags: wasi::__wasi_fdflags_t) -> (AccessMode,
}
pub(crate) fn win_from_oflags(oflags: wasi::__wasi_oflags_t) -> CreationDisposition {
if oflags & wasi::__WASI_O_CREAT != 0 {
if oflags & wasi::__WASI_O_EXCL != 0 {
if oflags & wasi::__WASI_OFLAGS_CREAT != 0 {
if oflags & wasi::__WASI_OFLAGS_EXCL != 0 {
CreationDisposition::CREATE_NEW
} else {
CreationDisposition::CREATE_ALWAYS
}
} else if oflags & wasi::__WASI_O_TRUNC != 0 {
} else if oflags & wasi::__WASI_OFLAGS_TRUNC != 0 {
CreationDisposition::TRUNCATE_EXISTING
} else {
CreationDisposition::OPEN_EXISTING
@@ -101,7 +101,7 @@ pub(crate) fn win_from_oflags(oflags: wasi::__wasi_oflags_t) -> CreationDisposit
/// Creates owned WASI path from OS string.
///
/// NB WASI spec requires OS string to be valid UTF-8. Otherwise,
/// `__WASI_EILSEQ` error is returned.
/// `__WASI_ERRNO_ILSEQ` error is returned.
pub(crate) fn path_from_host<S: AsRef<OsStr>>(s: S) -> Result<String> {
let vec: Vec<u16> = s.as_ref().encode_wide().collect();
String::from_utf16(&vec).map_err(|_| Error::EILSEQ)

View File

@@ -139,7 +139,7 @@ pub(crate) fn path_open(
return Err(Error::ELOOP);
}
// check if we are trying to open a file as a dir
if file_type.is_file() && oflags & wasi::__WASI_O_DIRECTORY != 0 {
if file_type.is_file() && oflags & wasi::__WASI_OFLAGS_DIRECTORY != 0 {
return Err(Error::ENOTDIR);
}
}
@@ -410,14 +410,14 @@ pub(crate) fn change_time(file: &File, _metadata: &Metadata) -> io::Result<i64>
pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result<wasi::__wasi_filestat_t> {
let metadata = file.metadata()?;
Ok(wasi::__wasi_filestat_t {
st_dev: device_id(file, &metadata)?,
st_ino: file_serial_no(file)?,
st_nlink: num_hardlinks(file, &metadata)?.try_into()?, // u64 doesn't fit into u32
st_size: metadata.len(),
st_atim: systemtime_to_timestamp(metadata.accessed()?)?,
st_ctim: change_time(file, &metadata)?.try_into()?, // i64 doesn't fit into u64
st_mtim: systemtime_to_timestamp(metadata.modified()?)?,
st_filetype: filetype_from_std(&metadata.file_type()).to_wasi(),
dev: device_id(file, &metadata)?,
ino: file_serial_no(file)?,
nlink: num_hardlinks(file, &metadata)?.try_into()?, // u64 doesn't fit into u32
size: metadata.len(),
atim: systemtime_to_timestamp(metadata.accessed()?)?,
ctim: change_time(file, &metadata)?.try_into()?, // i64 doesn't fit into u64
mtim: systemtime_to_timestamp(metadata.modified()?)?,
filetype: filetype_from_std(&metadata.file_type()).to_wasi(),
})
}

View File

@@ -23,23 +23,23 @@ pub(crate) fn path_open_rights(
fdflags: wasi::__wasi_fdflags_t,
) -> (wasi::__wasi_rights_t, wasi::__wasi_rights_t) {
// which rights are needed on the dirfd?
let mut needed_base = wasi::__WASI_RIGHT_PATH_OPEN;
let mut needed_base = wasi::__WASI_RIGHTS_PATH_OPEN;
let mut needed_inheriting = rights_base | rights_inheriting;
// convert open flags
if oflags & wasi::__WASI_O_CREAT != 0 {
needed_base |= wasi::__WASI_RIGHT_PATH_CREATE_FILE;
} else if oflags & wasi::__WASI_O_TRUNC != 0 {
needed_base |= wasi::__WASI_RIGHT_PATH_FILESTAT_SET_SIZE;
if oflags & wasi::__WASI_OFLAGS_CREAT != 0 {
needed_base |= wasi::__WASI_RIGHTS_PATH_CREATE_FILE;
} else if oflags & wasi::__WASI_OFLAGS_TRUNC != 0 {
needed_base |= wasi::__WASI_RIGHTS_PATH_FILESTAT_SET_SIZE;
}
// convert file descriptor flags
if fdflags & wasi::__WASI_FDFLAG_DSYNC != 0
|| fdflags & wasi::__WASI_FDFLAG_RSYNC != 0
|| fdflags & wasi::__WASI_FDFLAG_SYNC != 0
if fdflags & wasi::__WASI_FDFLAGS_DSYNC != 0
|| fdflags & wasi::__WASI_FDFLAGS_RSYNC != 0
|| fdflags & wasi::__WASI_FDFLAGS_SYNC != 0
{
needed_inheriting |= wasi::__WASI_RIGHT_FD_DATASYNC;
needed_inheriting |= wasi::__WASI_RIGHT_FD_SYNC;
needed_inheriting |= wasi::__WASI_RIGHTS_FD_DATASYNC;
needed_inheriting |= wasi::__WASI_RIGHTS_FD_SYNC;
}
(needed_base, needed_inheriting)

View File

@@ -53,25 +53,25 @@ pub(crate) fn clock_res_get(clock_id: wasi::__wasi_clockid_t) -> Result<wasi::__
// [4] https://www.codeproject.com/Tips/1011902/High-Resolution-Time-For-Windows
// [5] https://stackoverflow.com/questions/7685762/windows-7-timing-functions-how-to-use-getsystemtimeadjustment-correctly
// [6] https://bugs.python.org/issue19007
wasi::__WASI_CLOCK_REALTIME => 55_000_000,
wasi::__WASI_CLOCKID_REALTIME => 55_000_000,
// std::time::Instant uses QueryPerformanceCounter & QueryPerformanceFrequency internally
wasi::__WASI_CLOCK_MONOTONIC => *PERF_COUNTER_RES,
wasi::__WASI_CLOCKID_MONOTONIC => *PERF_COUNTER_RES,
// The best we can do is to hardcode the value from the docs.
// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getprocesstimes
wasi::__WASI_CLOCK_PROCESS_CPUTIME_ID => 100,
wasi::__WASI_CLOCKID_PROCESS_CPUTIME_ID => 100,
// The best we can do is to hardcode the value from the docs.
// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getthreadtimes
wasi::__WASI_CLOCK_THREAD_CPUTIME_ID => 100,
wasi::__WASI_CLOCKID_THREAD_CPUTIME_ID => 100,
_ => return Err(Error::EINVAL),
})
}
pub(crate) fn clock_time_get(clock_id: wasi::__wasi_clockid_t) -> Result<wasi::__wasi_timestamp_t> {
let duration = match clock_id {
wasi::__WASI_CLOCK_REALTIME => get_monotonic_time(),
wasi::__WASI_CLOCK_MONOTONIC => get_realtime_time()?,
wasi::__WASI_CLOCK_PROCESS_CPUTIME_ID => get_proc_cputime()?,
wasi::__WASI_CLOCK_THREAD_CPUTIME_ID => get_thread_cputime()?,
wasi::__WASI_CLOCKID_REALTIME => get_monotonic_time(),
wasi::__WASI_CLOCKID_MONOTONIC => get_realtime_time()?,
wasi::__WASI_CLOCKID_PROCESS_CPUTIME_ID => get_proc_cputime()?,
wasi::__WASI_CLOCKID_THREAD_CPUTIME_ID => get_thread_cputime()?,
_ => return Err(Error::EINVAL),
};
duration.as_nanos().try_into().map_err(Into::into)
@@ -87,7 +87,7 @@ pub(crate) fn poll_oneoff(
fn get_monotonic_time() -> Duration {
// We're circumventing the fact that we can't get a Duration from an Instant
// The epoch of __WASI_CLOCK_MONOTONIC is undefined, so we fix a time point once
// The epoch of __WASI_CLOCKID_MONOTONIC is undefined, so we fix a time point once
// and count relative to this time point.
//
// The alternative would be to copy over the implementation of std::time::Instant

View File

@@ -8,37 +8,37 @@
use wig::witx_wasi_types;
witx_wasi_types!("unstable" "wasi_unstable_preview0");
witx_wasi_types!("snapshot" "wasi_snapshot_preview1");
pub(crate) 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;
pub(crate) const RIGHTS_ALL: __wasi_rights_t = __WASI_RIGHTS_FD_DATASYNC
| __WASI_RIGHTS_FD_READ
| __WASI_RIGHTS_FD_SEEK
| __WASI_RIGHTS_FD_FDSTAT_SET_FLAGS
| __WASI_RIGHTS_FD_SYNC
| __WASI_RIGHTS_FD_TELL
| __WASI_RIGHTS_FD_WRITE
| __WASI_RIGHTS_FD_ADVISE
| __WASI_RIGHTS_FD_ALLOCATE
| __WASI_RIGHTS_PATH_CREATE_DIRECTORY
| __WASI_RIGHTS_PATH_CREATE_FILE
| __WASI_RIGHTS_PATH_LINK_SOURCE
| __WASI_RIGHTS_PATH_LINK_TARGET
| __WASI_RIGHTS_PATH_OPEN
| __WASI_RIGHTS_FD_READDIR
| __WASI_RIGHTS_PATH_READLINK
| __WASI_RIGHTS_PATH_RENAME_SOURCE
| __WASI_RIGHTS_PATH_RENAME_TARGET
| __WASI_RIGHTS_PATH_FILESTAT_GET
| __WASI_RIGHTS_PATH_FILESTAT_SET_SIZE
| __WASI_RIGHTS_PATH_FILESTAT_SET_TIMES
| __WASI_RIGHTS_FD_FILESTAT_GET
| __WASI_RIGHTS_FD_FILESTAT_SET_SIZE
| __WASI_RIGHTS_FD_FILESTAT_SET_TIMES
| __WASI_RIGHTS_PATH_SYMLINK
| __WASI_RIGHTS_PATH_UNLINK_FILE
| __WASI_RIGHTS_PATH_REMOVE_DIRECTORY
| __WASI_RIGHTS_POLL_FD_READWRITE
| __WASI_RIGHTS_SOCK_SHUTDOWN;
// Block and character device interaction is outside the scope of
// WASI. Simply allow everything.
@@ -49,143 +49,143 @@ pub(crate) const RIGHTS_CHARACTER_DEVICE_INHERITING: __wasi_rights_t = RIGHTS_AL
// Only allow directory operations on directories. Directories can only
// yield file descriptors to other directories and files.
pub(crate) 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_TIMES
| __WASI_RIGHT_PATH_SYMLINK
| __WASI_RIGHT_PATH_UNLINK_FILE
| __WASI_RIGHT_PATH_REMOVE_DIRECTORY
| __WASI_RIGHT_POLL_FD_READWRITE;
pub(crate) const RIGHTS_DIRECTORY_BASE: __wasi_rights_t = __WASI_RIGHTS_FD_FDSTAT_SET_FLAGS
| __WASI_RIGHTS_FD_SYNC
| __WASI_RIGHTS_FD_ADVISE
| __WASI_RIGHTS_PATH_CREATE_DIRECTORY
| __WASI_RIGHTS_PATH_CREATE_FILE
| __WASI_RIGHTS_PATH_LINK_SOURCE
| __WASI_RIGHTS_PATH_LINK_TARGET
| __WASI_RIGHTS_PATH_OPEN
| __WASI_RIGHTS_FD_READDIR
| __WASI_RIGHTS_PATH_READLINK
| __WASI_RIGHTS_PATH_RENAME_SOURCE
| __WASI_RIGHTS_PATH_RENAME_TARGET
| __WASI_RIGHTS_PATH_FILESTAT_GET
| __WASI_RIGHTS_PATH_FILESTAT_SET_SIZE
| __WASI_RIGHTS_PATH_FILESTAT_SET_TIMES
| __WASI_RIGHTS_FD_FILESTAT_GET
| __WASI_RIGHTS_FD_FILESTAT_SET_TIMES
| __WASI_RIGHTS_PATH_SYMLINK
| __WASI_RIGHTS_PATH_UNLINK_FILE
| __WASI_RIGHTS_PATH_REMOVE_DIRECTORY
| __WASI_RIGHTS_POLL_FD_READWRITE;
pub(crate) const RIGHTS_DIRECTORY_INHERITING: __wasi_rights_t =
RIGHTS_DIRECTORY_BASE | RIGHTS_REGULAR_FILE_BASE;
// Operations that apply to regular files.
pub(crate) 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;
pub(crate) const RIGHTS_REGULAR_FILE_BASE: __wasi_rights_t = __WASI_RIGHTS_FD_DATASYNC
| __WASI_RIGHTS_FD_READ
| __WASI_RIGHTS_FD_SEEK
| __WASI_RIGHTS_FD_FDSTAT_SET_FLAGS
| __WASI_RIGHTS_FD_SYNC
| __WASI_RIGHTS_FD_TELL
| __WASI_RIGHTS_FD_WRITE
| __WASI_RIGHTS_FD_ADVISE
| __WASI_RIGHTS_FD_ALLOCATE
| __WASI_RIGHTS_FD_FILESTAT_GET
| __WASI_RIGHTS_FD_FILESTAT_SET_SIZE
| __WASI_RIGHTS_FD_FILESTAT_SET_TIMES
| __WASI_RIGHTS_POLL_FD_READWRITE;
pub(crate) const RIGHTS_REGULAR_FILE_INHERITING: __wasi_rights_t = 0;
// Operations that apply to sockets and socket pairs.
pub(crate) 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;
pub(crate) const RIGHTS_SOCKET_BASE: __wasi_rights_t = __WASI_RIGHTS_FD_READ
| __WASI_RIGHTS_FD_FDSTAT_SET_FLAGS
| __WASI_RIGHTS_FD_WRITE
| __WASI_RIGHTS_FD_FILESTAT_GET
| __WASI_RIGHTS_POLL_FD_READWRITE
| __WASI_RIGHTS_SOCK_SHUTDOWN;
pub(crate) const RIGHTS_SOCKET_INHERITING: __wasi_rights_t = RIGHTS_ALL;
// Operations that apply to TTYs.
pub(crate) 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;
pub(crate) const RIGHTS_TTY_BASE: __wasi_rights_t = __WASI_RIGHTS_FD_READ
| __WASI_RIGHTS_FD_FDSTAT_SET_FLAGS
| __WASI_RIGHTS_FD_WRITE
| __WASI_RIGHTS_FD_FILESTAT_GET
| __WASI_RIGHTS_POLL_FD_READWRITE;
#[allow(unused)]
pub(crate) const RIGHTS_TTY_INHERITING: __wasi_rights_t = 0;
pub fn strerror(errno: __wasi_errno_t) -> &'static str {
match errno {
__WASI_ESUCCESS => "__WASI_ESUCCESS",
__WASI_E2BIG => "__WASI_E2BIG",
__WASI_EACCES => "__WASI_EACCES",
__WASI_EADDRINUSE => "__WASI_EADDRINUSE",
__WASI_EADDRNOTAVAIL => "__WASI_EADDRNOTAVAIL",
__WASI_EAFNOSUPPORT => "__WASI_EAFNOSUPPORT",
__WASI_EAGAIN => "__WASI_EAGAIN",
__WASI_EALREADY => "__WASI_EALREADY",
__WASI_EBADF => "__WASI_EBADF",
__WASI_EBADMSG => "__WASI_EBADMSG",
__WASI_EBUSY => "__WASI_EBUSY",
__WASI_ECANCELED => "__WASI_ECANCELED",
__WASI_ECHILD => "__WASI_ECHILD",
__WASI_ECONNABORTED => "__WASI_ECONNABORTED",
__WASI_ECONNREFUSED => "__WASI_ECONNREFUSED",
__WASI_ECONNRESET => "__WASI_ECONNRESET",
__WASI_EDEADLK => "__WASI_EDEADLK",
__WASI_EDESTADDRREQ => "__WASI_EDESTADDRREQ",
__WASI_EDOM => "__WASI_EDOM",
__WASI_EDQUOT => "__WASI_EDQUOT",
__WASI_EEXIST => "__WASI_EEXIST",
__WASI_EFAULT => "__WASI_EFAULT",
__WASI_EFBIG => "__WASI_EFBIG",
__WASI_EHOSTUNREACH => "__WASI_EHOSTUNREACH",
__WASI_EIDRM => "__WASI_EIDRM",
__WASI_EILSEQ => "__WASI_EILSEQ",
__WASI_EINPROGRESS => "__WASI_EINPROGRESS",
__WASI_EINTR => "__WASI_EINTR",
__WASI_EINVAL => "__WASI_EINVAL",
__WASI_EIO => "__WASI_EIO",
__WASI_EISCONN => "__WASI_EISCONN",
__WASI_EISDIR => "__WASI_EISDIR",
__WASI_ELOOP => "__WASI_ELOOP",
__WASI_EMFILE => "__WASI_EMFILE",
__WASI_EMLINK => "__WASI_EMLINK",
__WASI_EMSGSIZE => "__WASI_EMSGSIZE",
__WASI_EMULTIHOP => "__WASI_EMULTIHOP",
__WASI_ENAMETOOLONG => "__WASI_ENAMETOOLONG",
__WASI_ENETDOWN => "__WASI_ENETDOWN",
__WASI_ENETRESET => "__WASI_ENETRESET",
__WASI_ENETUNREACH => "__WASI_ENETUNREACH",
__WASI_ENFILE => "__WASI_ENFILE",
__WASI_ENOBUFS => "__WASI_ENOBUFS",
__WASI_ENODEV => "__WASI_ENODEV",
__WASI_ENOENT => "__WASI_ENOENT",
__WASI_ENOEXEC => "__WASI_ENOEXEC",
__WASI_ENOLCK => "__WASI_ENOLCK",
__WASI_ENOLINK => "__WASI_ENOLINK",
__WASI_ENOMEM => "__WASI_ENOMEM",
__WASI_ENOMSG => "__WASI_ENOMSG",
__WASI_ENOPROTOOPT => "__WASI_ENOPROTOOPT",
__WASI_ENOSPC => "__WASI_ENOSPC",
__WASI_ENOSYS => "__WASI_ENOSYS",
__WASI_ENOTCONN => "__WASI_ENOTCONN",
__WASI_ENOTDIR => "__WASI_ENOTDIR",
__WASI_ENOTEMPTY => "__WASI_ENOTEMPTY",
__WASI_ENOTRECOVERABLE => "__WASI_ENOTRECOVERABLE",
__WASI_ENOTSOCK => "__WASI_ENOTSOCK",
__WASI_ENOTSUP => "__WASI_ENOTSUP",
__WASI_ENOTTY => "__WASI_ENOTTY",
__WASI_ENXIO => "__WASI_ENXIO",
__WASI_EOVERFLOW => "__WASI_EOVERFLOW",
__WASI_EOWNERDEAD => "__WASI_EOWNERDEAD",
__WASI_EPERM => "__WASI_EPERM",
__WASI_EPIPE => "__WASI_EPIPE",
__WASI_EPROTO => "__WASI_EPROTO",
__WASI_EPROTONOSUPPORT => "__WASI_EPROTONOSUPPORT",
__WASI_EPROTOTYPE => "__WASI_EPROTOTYPE",
__WASI_ERANGE => "__WASI_ERANGE",
__WASI_EROFS => "__WASI_EROFS",
__WASI_ESPIPE => "__WASI_ESPIPE",
__WASI_ESRCH => "__WASI_ESRCH",
__WASI_ESTALE => "__WASI_ESTALE",
__WASI_ETIMEDOUT => "__WASI_ETIMEDOUT",
__WASI_ETXTBSY => "__WASI_ETXTBSY",
__WASI_EXDEV => "__WASI_EXDEV",
__WASI_ENOTCAPABLE => "__WASI_ENOTCAPABLE",
__WASI_ERRNO_SUCCESS => "__WASI_ERRNO_SUCCESS",
__WASI_ERRNO_2BIG => "__WASI_ERRNO_2BIG",
__WASI_ERRNO_ACCES => "__WASI_ERRNO_ACCES",
__WASI_ERRNO_ADDRINUSE => "__WASI_ERRNO_ADDRINUSE",
__WASI_ERRNO_ADDRNOTAVAIL => "__WASI_ERRNO_ADDRNOTAVAIL",
__WASI_ERRNO_AFNOSUPPORT => "__WASI_ERRNO_AFNOSUPPORT",
__WASI_ERRNO_AGAIN => "__WASI_ERRNO_AGAIN",
__WASI_ERRNO_ALREADY => "__WASI_ERRNO_ALREADY",
__WASI_ERRNO_BADF => "__WASI_ERRNO_BADF",
__WASI_ERRNO_BADMSG => "__WASI_ERRNO_BADMSG",
__WASI_ERRNO_BUSY => "__WASI_ERRNO_BUSY",
__WASI_ERRNO_CANCELED => "__WASI_ERRNO_CANCELED",
__WASI_ERRNO_CHILD => "__WASI_ERRNO_CHILD",
__WASI_ERRNO_CONNABORTED => "__WASI_ERRNO_CONNABORTED",
__WASI_ERRNO_CONNREFUSED => "__WASI_ERRNO_CONNREFUSED",
__WASI_ERRNO_CONNRESET => "__WASI_ERRNO_CONNRESET",
__WASI_ERRNO_DEADLK => "__WASI_ERRNO_DEADLK",
__WASI_ERRNO_DESTADDRREQ => "__WASI_ERRNO_DESTADDRREQ",
__WASI_ERRNO_DOM => "__WASI_ERRNO_DOM",
__WASI_ERRNO_DQUOT => "__WASI_ERRNO_DQUOT",
__WASI_ERRNO_EXIST => "__WASI_ERRNO_EXIST",
__WASI_ERRNO_FAULT => "__WASI_ERRNO_FAULT",
__WASI_ERRNO_FBIG => "__WASI_ERRNO_FBIG",
__WASI_ERRNO_HOSTUNREACH => "__WASI_ERRNO_HOSTUNREACH",
__WASI_ERRNO_IDRM => "__WASI_ERRNO_IDRM",
__WASI_ERRNO_ILSEQ => "__WASI_ERRNO_ILSEQ",
__WASI_ERRNO_INPROGRESS => "__WASI_ERRNO_INPROGRESS",
__WASI_ERRNO_INTR => "__WASI_ERRNO_INTR",
__WASI_ERRNO_INVAL => "__WASI_ERRNO_INVAL",
__WASI_ERRNO_IO => "__WASI_ERRNO_IO",
__WASI_ERRNO_ISCONN => "__WASI_ERRNO_ISCONN",
__WASI_ERRNO_ISDIR => "__WASI_ERRNO_ISDIR",
__WASI_ERRNO_LOOP => "__WASI_ERRNO_LOOP",
__WASI_ERRNO_MFILE => "__WASI_ERRNO_MFILE",
__WASI_ERRNO_MLINK => "__WASI_ERRNO_MLINK",
__WASI_ERRNO_MSGSIZE => "__WASI_ERRNO_MSGSIZE",
__WASI_ERRNO_MULTIHOP => "__WASI_ERRNO_MULTIHOP",
__WASI_ERRNO_NAMETOOLONG => "__WASI_ERRNO_NAMETOOLONG",
__WASI_ERRNO_NETDOWN => "__WASI_ERRNO_NETDOWN",
__WASI_ERRNO_NETRESET => "__WASI_ERRNO_NETRESET",
__WASI_ERRNO_NETUNREACH => "__WASI_ERRNO_NETUNREACH",
__WASI_ERRNO_NFILE => "__WASI_ERRNO_NFILE",
__WASI_ERRNO_NOBUFS => "__WASI_ERRNO_NOBUFS",
__WASI_ERRNO_NODEV => "__WASI_ERRNO_NODEV",
__WASI_ERRNO_NOENT => "__WASI_ERRNO_NOENT",
__WASI_ERRNO_NOEXEC => "__WASI_ERRNO_NOEXEC",
__WASI_ERRNO_NOLCK => "__WASI_ERRNO_NOLCK",
__WASI_ERRNO_NOLINK => "__WASI_ERRNO_NOLINK",
__WASI_ERRNO_NOMEM => "__WASI_ERRNO_NOMEM",
__WASI_ERRNO_NOMSG => "__WASI_ERRNO_NOMSG",
__WASI_ERRNO_NOPROTOOPT => "__WASI_ERRNO_NOPROTOOPT",
__WASI_ERRNO_NOSPC => "__WASI_ERRNO_NOSPC",
__WASI_ERRNO_NOSYS => "__WASI_ERRNO_NOSYS",
__WASI_ERRNO_NOTCONN => "__WASI_ERRNO_NOTCONN",
__WASI_ERRNO_NOTDIR => "__WASI_ERRNO_NOTDIR",
__WASI_ERRNO_NOTEMPTY => "__WASI_ERRNO_NOTEMPTY",
__WASI_ERRNO_NOTRECOVERABLE => "__WASI_ERRNO_NOTRECOVERABLE",
__WASI_ERRNO_NOTSOCK => "__WASI_ERRNO_NOTSOCK",
__WASI_ERRNO_NOTSUP => "__WASI_ERRNO_NOTSUP",
__WASI_ERRNO_NOTTY => "__WASI_ERRNO_NOTTY",
__WASI_ERRNO_NXIO => "__WASI_ERRNO_NXIO",
__WASI_ERRNO_OVERFLOW => "__WASI_ERRNO_OVERFLOW",
__WASI_ERRNO_OWNERDEAD => "__WASI_ERRNO_OWNERDEAD",
__WASI_ERRNO_PERM => "__WASI_ERRNO_PERM",
__WASI_ERRNO_PIPE => "__WASI_ERRNO_PIPE",
__WASI_ERRNO_PROTO => "__WASI_ERRNO_PROTO",
__WASI_ERRNO_PROTONOSUPPORT => "__WASI_ERRNO_PROTONOSUPPORT",
__WASI_ERRNO_PROTOTYPE => "__WASI_ERRNO_PROTOTYPE",
__WASI_ERRNO_RANGE => "__WASI_ERRNO_RANGE",
__WASI_ERRNO_ROFS => "__WASI_ERRNO_ROFS",
__WASI_ERRNO_SPIPE => "__WASI_ERRNO_SPIPE",
__WASI_ERRNO_SRCH => "__WASI_ERRNO_SRCH",
__WASI_ERRNO_STALE => "__WASI_ERRNO_STALE",
__WASI_ERRNO_TIMEDOUT => "__WASI_ERRNO_TIMEDOUT",
__WASI_ERRNO_TXTBSY => "__WASI_ERRNO_TXTBSY",
__WASI_ERRNO_XDEV => "__WASI_ERRNO_XDEV",
__WASI_ERRNO_NOTCAPABLE => "__WASI_ERRNO_NOTCAPABLE",
other => panic!("Undefined errno value {:?}", other),
}
}
@@ -295,21 +295,23 @@ mod test {
#[test]
fn bindgen_test_layout___wasi_event_t___wasi_event_u() {
assert_eq!(
::std::mem::size_of::<__wasi_event_u>(),
::std::mem::size_of::<__wasi_event_u_t>(),
16usize,
concat!("Size of: ", stringify!(__wasi_event_u))
concat!("Size of: ", stringify!(__wasi_event_u_t))
);
assert_eq!(
::std::mem::align_of::<__wasi_event_u>(),
::std::mem::align_of::<__wasi_event_u_t>(),
8usize,
concat!("Alignment of ", stringify!(__wasi_event_u))
concat!("Alignment of ", stringify!(__wasi_event_u_t))
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<__wasi_event_u>())).fd_readwrite as *const _ as usize },
unsafe {
&(*(::std::ptr::null::<__wasi_event_u_t>())).fd_readwrite as *const _ as usize
},
0usize,
concat!(
"Offset of field: ",
stringify!(__wasi_event_u),
stringify!(__wasi_event_u_t),
"::",
stringify!(fd_readwrite)
)
@@ -467,11 +469,11 @@ mod test {
fn bindgen_test_layout_wasi_filestat_t() {
assert_eq!(
::std::mem::size_of::<__wasi_filestat_t>(),
56usize,
64usize,
concat!("Size of: ", stringify!(__wasi_filestat_t))
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_dev as *const _ as usize },
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).dev as *const _ as usize },
0usize,
concat!(
"Offset of field: ",
@@ -481,7 +483,7 @@ mod test {
)
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_ino as *const _ as usize },
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).ino as *const _ as usize },
8usize,
concat!(
"Offset of field: ",
@@ -491,9 +493,7 @@ mod test {
)
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<__wasi_filestat_t>())).st_filetype as *const _ as usize
},
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).filetype as *const _ as usize },
16usize,
concat!(
"Offset of field: ",
@@ -503,8 +503,8 @@ mod test {
)
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_nlink as *const _ as usize },
20usize,
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).nlink as *const _ as usize },
24usize,
concat!(
"Offset of field: ",
stringify!(__wasi_filestat_t),
@@ -513,8 +513,8 @@ mod test {
)
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_size as *const _ as usize },
24usize,
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).size as *const _ as usize },
32usize,
concat!(
"Offset of field: ",
stringify!(__wasi_filestat_t),
@@ -523,8 +523,8 @@ mod test {
)
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_atim as *const _ as usize },
32usize,
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).atim as *const _ as usize },
40usize,
concat!(
"Offset of field: ",
stringify!(__wasi_filestat_t),
@@ -533,8 +533,8 @@ mod test {
)
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_mtim as *const _ as usize },
40usize,
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).mtim as *const _ as usize },
48usize,
concat!(
"Offset of field: ",
stringify!(__wasi_filestat_t),
@@ -543,8 +543,8 @@ mod test {
)
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_ctim as *const _ as usize },
48usize,
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).ctim as *const _ as usize },
56usize,
concat!(
"Offset of field: ",
stringify!(__wasi_filestat_t),
@@ -558,7 +558,7 @@ mod test {
fn bindgen_test_layout___wasi_subscription_clock_t() {
assert_eq!(
::std::mem::size_of::<__wasi_subscription_clock_t>(),
40usize,
32usize,
concat!("Size of: ", stringify!(__wasi_subscription_clock_t))
);
assert_eq!(
@@ -568,23 +568,9 @@ mod test {
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<__wasi_subscription_clock_t>())).identifier as *const _
as usize
&(*(::std::ptr::null::<__wasi_subscription_clock_t>())).id as *const _ as usize
},
0usize,
concat!(
"Offset of field: ",
stringify!(__wasi_subscription_clock_t),
"::",
stringify!(identifier)
)
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<__wasi_subscription_clock_t>())).clock_id as *const _
as usize
},
8usize,
concat!(
"Offset of field: ",
stringify!(__wasi_subscription_clock_t),
@@ -596,7 +582,7 @@ mod test {
unsafe {
&(*(::std::ptr::null::<__wasi_subscription_clock_t>())).timeout as *const _ as usize
},
16usize,
8usize,
concat!(
"Offset of field: ",
stringify!(__wasi_subscription_clock_t),
@@ -609,7 +595,7 @@ mod test {
&(*(::std::ptr::null::<__wasi_subscription_clock_t>())).precision as *const _
as usize
},
24usize,
16usize,
concat!(
"Offset of field: ",
stringify!(__wasi_subscription_clock_t),
@@ -621,7 +607,7 @@ mod test {
unsafe {
&(*(::std::ptr::null::<__wasi_subscription_clock_t>())).flags as *const _ as usize
},
32usize,
24usize,
concat!(
"Offset of field: ",
stringify!(__wasi_subscription_clock_t),
@@ -665,33 +651,36 @@ mod test {
#[test]
fn bindgen_test_layout___wasi_subscription_t___wasi_subscription_u() {
assert_eq!(
::std::mem::size_of::<__wasi_subscription_u>(),
40usize,
concat!("Size of: ", stringify!(__wasi_subscription_u))
::std::mem::size_of::<__wasi_subscription_u_t>(),
32usize,
concat!("Size of: ", stringify!(__wasi_subscription_u_t))
);
assert_eq!(
::std::mem::align_of::<__wasi_subscription_u>(),
::std::mem::align_of::<__wasi_subscription_u_t>(),
8usize,
concat!("Alignment of ", stringify!(__wasi_subscription_u))
concat!("Alignment of ", stringify!(__wasi_subscription_u_t))
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<__wasi_subscription_u>())).clock as *const _ as usize },
unsafe {
&(*(::std::ptr::null::<__wasi_subscription_u_t>())).clock as *const _ as usize
},
0usize,
concat!(
"Offset of field: ",
stringify!(__wasi_subscription_u),
stringify!(__wasi_subscription_u_t),
"::",
stringify!(clock)
)
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<__wasi_subscription_u>())).fd_readwrite as *const _ as usize
&(*(::std::ptr::null::<__wasi_subscription_u_t>())).fd_readwrite as *const _
as usize
},
0usize,
concat!(
"Offset of field: ",
stringify!(__wasi_subscription_u),
stringify!(__wasi_subscription_u_t),
"::",
stringify!(fd_readwrite)
)
@@ -702,7 +691,7 @@ mod test {
fn bindgen_test_layout___wasi_subscription_t() {
assert_eq!(
::std::mem::size_of::<__wasi_subscription_t>(),
56usize,
48usize,
concat!("Size of: ", stringify!(__wasi_subscription_t))
);
assert_eq!(
@@ -750,7 +739,7 @@ mod test {
fn bindgen_test_layout___wasi_filestat_t() {
assert_eq!(
::std::mem::size_of::<__wasi_filestat_t>(),
56usize,
64usize,
concat!("Size of: ", stringify!(__wasi_filestat_t))
);
assert_eq!(
@@ -759,7 +748,7 @@ mod test {
concat!("Alignment of ", stringify!(__wasi_filestat_t))
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_dev as *const _ as usize },
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).dev as *const _ as usize },
0usize,
concat!(
"Offset of field: ",
@@ -769,7 +758,7 @@ mod test {
)
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_ino as *const _ as usize },
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).ino as *const _ as usize },
8usize,
concat!(
"Offset of field: ",
@@ -779,9 +768,7 @@ mod test {
)
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<__wasi_filestat_t>())).st_filetype as *const _ as usize
},
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).filetype as *const _ as usize },
16usize,
concat!(
"Offset of field: ",
@@ -791,8 +778,8 @@ mod test {
)
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_nlink as *const _ as usize },
20usize,
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).nlink as *const _ as usize },
24usize,
concat!(
"Offset of field: ",
stringify!(__wasi_filestat_t),
@@ -801,8 +788,8 @@ mod test {
)
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_size as *const _ as usize },
24usize,
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).size as *const _ as usize },
32usize,
concat!(
"Offset of field: ",
stringify!(__wasi_filestat_t),
@@ -811,8 +798,8 @@ mod test {
)
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_atim as *const _ as usize },
32usize,
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).atim as *const _ as usize },
40usize,
concat!(
"Offset of field: ",
stringify!(__wasi_filestat_t),
@@ -821,8 +808,8 @@ mod test {
)
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_mtim as *const _ as usize },
40usize,
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).mtim as *const _ as usize },
48usize,
concat!(
"Offset of field: ",
stringify!(__wasi_filestat_t),
@@ -831,8 +818,8 @@ mod test {
)
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_ctim as *const _ as usize },
48usize,
unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).ctim as *const _ as usize },
56usize,
concat!(
"Offset of field: ",
stringify!(__wasi_filestat_t),

View File

@@ -12,7 +12,7 @@ use wig::witx_wasi32_types;
pub type uintptr_t = u32;
pub type size_t = u32;
witx_wasi32_types!("unstable" "wasi_unstable_preview0");
witx_wasi32_types!("snapshot" "wasi_snapshot_preview1");
#[cfg(test)]
mod test {
@@ -89,23 +89,23 @@ mod test {
#[test]
fn bindgen_test_layout___wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t() {
assert_eq!(
::std::mem::size_of::<__wasi_prestat_dir>(),
::std::mem::size_of::<__wasi_prestat_dir_t>(),
4usize,
concat!("Size of: ", stringify!(__wasi_prestat_dir))
concat!("Size of: ", stringify!(__wasi_prestat_dir_t))
);
assert_eq!(
::std::mem::align_of::<__wasi_prestat_dir>(),
::std::mem::align_of::<__wasi_prestat_dir_t>(),
4usize,
concat!("Alignment of ", stringify!(__wasi_prestat_dir))
concat!("Alignment of ", stringify!(__wasi_prestat_dir_t))
);
assert_eq!(
unsafe {
&(*(::std::ptr::null::<__wasi_prestat_dir>())).pr_name_len as *const _ as usize
&(*(::std::ptr::null::<__wasi_prestat_dir_t>())).pr_name_len as *const _ as usize
},
0usize,
concat!(
"Offset of field: ",
stringify!(__wasi_prestat_dir),
stringify!(__wasi_prestat_dir_t),
"::",
stringify!(pr_name_len)
)
@@ -115,21 +115,21 @@ mod test {
#[test]
fn bindgen_test_layout___wasi_prestat_t___wasi_prestat_u() {
assert_eq!(
::std::mem::size_of::<__wasi_prestat_u>(),
::std::mem::size_of::<__wasi_prestat_u_t>(),
4usize,
concat!("Size of: ", stringify!(__wasi_prestat_u))
concat!("Size of: ", stringify!(__wasi_prestat_u_t))
);
assert_eq!(
::std::mem::align_of::<__wasi_prestat_u>(),
::std::mem::align_of::<__wasi_prestat_u_t>(),
4usize,
concat!("Alignment of ", stringify!(__wasi_prestat_u))
concat!("Alignment of ", stringify!(__wasi_prestat_u_t))
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<__wasi_prestat_u>())).dir as *const _ as usize },
unsafe { &(*(::std::ptr::null::<__wasi_prestat_u_t>())).dir as *const _ as usize },
0usize,
concat!(
"Offset of field: ",
stringify!(__wasi_prestat_u),
stringify!(__wasi_prestat_u_t),
"::",
stringify!(dir)
)

View File

@@ -13,6 +13,7 @@ proc-macro = true
[dependencies]
syn = { version = "1.0.5", features = ["full"] }
quote = "1.0.2"
proc-macro2 = "1.0.6"
[dev-dependencies]
trybuild = "1.0.4"

View File

@@ -4,21 +4,13 @@ use proc_macro::TokenStream;
use quote::quote;
use syn::{FnArg, Pat, PatType, Type, TypeReference, TypeSlice};
#[proc_macro_attribute]
pub fn wasi_common_cbindgen(attr: TokenStream, function: TokenStream) -> TokenStream {
assert!(attr.is_empty());
let function = syn::parse_macro_input!(function as syn::ItemFn);
// capture visibility
let vis = &function.vis;
// generate C fn name prefixed with __wasi_
let fn_ident = &function.sig.ident;
let concatenated = format!("wasi_common_{}", fn_ident);
let c_fn_ident = syn::Ident::new(&concatenated, fn_ident.span());
// capture input args
fn capture_input_args(
function: &syn::ItemFn,
) -> (
Vec<proc_macro2::TokenStream>,
Vec<proc_macro2::TokenStream>,
Vec<proc_macro2::TokenStream>,
) {
let mut arg_ident = Vec::new();
let mut arg_type = Vec::new();
let mut call_arg_ident = Vec::new();
@@ -85,6 +77,26 @@ pub fn wasi_common_cbindgen(attr: TokenStream, function: TokenStream) -> TokenSt
}
}
(arg_ident, arg_type, call_arg_ident)
}
#[proc_macro_attribute]
pub fn wasi_common_cbindgen(attr: TokenStream, function: TokenStream) -> TokenStream {
assert!(attr.is_empty());
let function = syn::parse_macro_input!(function as syn::ItemFn);
// capture visibility
let vis = &function.vis;
// generate C fn name prefixed with wasi_common_
let fn_ident = &function.sig.ident;
let concatenated = format!("wasi_common_{}", fn_ident);
let c_fn_ident = syn::Ident::new(&concatenated, fn_ident.span());
// capture input args
let (arg_ident, arg_type, call_arg_ident) = capture_input_args(&function);
// capture output arg
let output = &function.sig.output;
@@ -105,3 +117,40 @@ pub fn wasi_common_cbindgen(attr: TokenStream, function: TokenStream) -> TokenSt
result.into()
}
#[proc_macro_attribute]
pub fn wasi_common_cbindgen_old(attr: TokenStream, function: TokenStream) -> TokenStream {
assert!(attr.is_empty());
let function = syn::parse_macro_input!(function as syn::ItemFn);
// capture visibility
let vis = &function.vis;
// generate C fn name prefixed with old_wasi_common_
let fn_ident = &function.sig.ident;
let concatenated = format!("old_wasi_common_{}", fn_ident);
let c_fn_ident = syn::Ident::new(&concatenated, fn_ident.span());
// capture input args
let (arg_ident, arg_type, call_arg_ident) = capture_input_args(&function);
// capture output arg
let output = &function.sig.output;
let result = quote! {
#function
#vis unsafe extern "C" fn #c_fn_ident(
#(
#arg_ident: #arg_type,
)*
) #output {
#fn_ident(#(
#call_arg_ident,
)*)
}
};
result.into()
}

View File

@@ -15,6 +15,7 @@ proc-macro = true
[dependencies]
quote = "1.0.2"
proc-macro2 = "1.0.6"
heck = "0.3.1"
# We include the WASI repo primarily for the witx files, but it's also useful
# to use the witx parser it contains, rather than the witx crate from
# crates.io, so that it always matches the version of the witx files.

View File

@@ -1,6 +1,7 @@
//! Translate witx types to Rust.
use crate::utils;
use heck::ShoutySnakeCase;
use proc_macro2::{Delimiter, Group, Literal, TokenStream, TokenTree};
use quote::{format_ident, quote};
use std::convert::TryFrom;
@@ -25,7 +26,7 @@ pub fn gen(args: TokenStream, mode: Mode) -> TokenStream {
let mut output = TokenStream::new();
let (path, _phase) = utils::witx_path_from_args(args);
let doc = match witx::load(&path) {
let doc = match witx::load(&[&path]) {
Ok(doc) => doc,
Err(e) => {
panic!("error opening file {}: {}", path, e);
@@ -55,35 +56,43 @@ fn gen_datatype(
) {
match &datatype.variant {
witx::DatatypeVariant::Alias(a) => {
if a.name.as_str() == "size_t" {
let wasi_name = format_ident!("__wasi_{}", a.name.as_str());
if a.name.as_str() == "size" {
let wasi_name = format_ident!("__wasi_{}_t", a.name.as_str());
match mode {
Mode::Host => output.extend(quote!(pub type #wasi_name = usize;)),
Mode::Wasi => panic!("size_t has target-specific size"),
Mode::Wasi => panic!("size has target-specific size"),
Mode::Wasi32 => output.extend(quote!(pub type #wasi_name = u32;)),
}
} else {
let wasi_name = format_ident!("__wasi_{}", a.name.as_str());
let wasi_name = format_ident!("__wasi_{}_t", a.name.as_str());
let to = ident_tokens(mode, &a.to);
output.extend(quote!(pub type #wasi_name = #to;));
}
}
witx::DatatypeVariant::Enum(e) => {
let wasi_name = format_ident!("__wasi_{}", e.name.as_str());
let wasi_name = format_ident!("__wasi_{}_t", e.name.as_str());
let repr = int_repr_tokens(e.repr);
output.extend(quote!(pub type #wasi_name = #repr;));
for (index, variant) in e.variants.iter().enumerate() {
let value_name = format_ident!("__WASI_{}", variant.as_str());
let value_name = format_ident!(
"__WASI_{}_{}",
e.name.as_str().to_shouty_snake_case(),
variant.name.as_str().to_shouty_snake_case()
);
let index_name = Literal::usize_unsuffixed(index);
output.extend(quote!(pub const #value_name: #wasi_name = #index_name;));
}
}
witx::DatatypeVariant::Flags(f) => {
let wasi_name = format_ident!("__wasi_{}", f.name.as_str());
let wasi_name = format_ident!("__wasi_{}_t", f.name.as_str());
let repr = int_repr_tokens(f.repr);
output.extend(quote!(pub type #wasi_name = #repr;));
for (index, flag) in f.flags.iter().enumerate() {
let value_name = format_ident!("__WASI_{}", flag.as_str());
let value_name = format_ident!(
"__WASI_{}_{}",
f.name.as_str().to_shouty_snake_case(),
flag.name.as_str().to_shouty_snake_case()
);
let flag_value = Literal::u128_unsuffixed(
1u128
.checked_shl(u32::try_from(index).expect("flag value overflow"))
@@ -105,7 +114,7 @@ fn gen_datatype(
output.extend(quote!(#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]));
}
let wasi_name = format_ident!("__wasi_{}", s.name.as_str());
let wasi_name = format_ident!("__wasi_{}_t", s.name.as_str());
output.extend(quote!(pub struct #wasi_name));
let mut inner = TokenStream::new();
@@ -122,7 +131,7 @@ fn gen_datatype(
output.extend(quote!(#[derive(Copy, Clone)]));
output.extend(quote!(#[allow(missing_debug_implementations)]));
let wasi_name = format_ident!("__wasi_{}", u.name.as_str());
let wasi_name = format_ident!("__wasi_{}_t", u.name.as_str());
output.extend(quote!(pub union #wasi_name));
let mut inner = TokenStream::new();
@@ -134,6 +143,10 @@ fn gen_datatype(
let braced = Group::new(Delimiter::Brace, inner);
output.extend(TokenStream::from(TokenTree::Group(braced)));
}
witx::DatatypeVariant::Handle(a) => {
let wasi_name = format_ident!("__wasi_{}_t", a.name.as_str());
output.extend(quote!(pub type #wasi_name = u32;));
}
}
}
@@ -170,7 +183,7 @@ fn ident_tokens(mode: Mode, ident: &witx::DatatypeIdent) -> TokenStream {
match ident {
witx::DatatypeIdent::Builtin(builtin) => builtin_tokens(mode, *builtin),
witx::DatatypeIdent::Ident(ident) => TokenStream::from(TokenTree::Ident(format_ident!(
"__wasi_{}",
"__wasi_{}_t",
ident.name.as_str()
))),
witx::DatatypeIdent::Pointer(pointee) => {
@@ -216,7 +229,7 @@ fn struct_has_union(doc: &witx::Document, s: &witx::StructDatatype) -> bool {
fn type_has_target_size(doc: &witx::Document, type_: &witx::Datatype) -> bool {
match &type_.variant {
witx::DatatypeVariant::Alias(a) => {
a.name.as_str() == "size_t" || ident_has_target_size(doc, &a.to)
a.name.as_str() == "size" || ident_has_target_size(doc, &a.to)
}
witx::DatatypeVariant::Enum(_) => false,
witx::DatatypeVariant::Flags(_) => false,
@@ -228,6 +241,7 @@ fn type_has_target_size(doc: &witx::Document, type_: &witx::Datatype) -> bool {
.variants
.iter()
.any(|v| ident_has_target_size(doc, &v.type_)),
witx::DatatypeVariant::Handle(_) => false,
}
}