diff --git a/crates/test-programs/tests/wasm_tests/runtime.rs b/crates/test-programs/tests/wasm_tests/runtime.rs index 1c0018b16c..40adf18e6e 100644 --- a/crates/test-programs/tests/wasm_tests/runtime.rs +++ b/crates/test-programs/tests/wasm_tests/runtime.rs @@ -33,7 +33,7 @@ pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> any // Create our wasi context with pretty standard arguments/inheritance/etc. // Additionally register andy preopened directories if we have them. - let mut builder = wasi_common::WasiCtxBuilder::new() + let mut builder = wasi_common::old::snapshot_0::WasiCtxBuilder::new() .arg(bin_name) .arg(".") .inherit_stdio(); @@ -48,11 +48,13 @@ pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> any let (reader, _writer) = os_pipe::pipe()?; builder = builder.stdin(reader_to_file(reader)); + // The current stable Rust toolchain uses the old `wasi_unstable` ABI, + // aka `snapshot_0`. module_registry.insert( "wasi_unstable".to_owned(), Instance::from_handle( &store, - wasmtime_wasi::instantiate_wasi_with_context( + wasmtime_wasi::old::snapshot_0::instantiate_wasi_with_context( global_exports.clone(), builder.build().context("failed to build wasi context")?, ) diff --git a/crates/wasi-common/WASI b/crates/wasi-common/WASI index 8a968f2667..9b298d7911 160000 --- a/crates/wasi-common/WASI +++ b/crates/wasi-common/WASI @@ -1 +1 @@ -Subproject commit 8a968f26677e78c0bfbae7de8673702f71b7b39b +Subproject commit 9b298d7911986788bdfac662ed115ea6701b2c9a diff --git a/crates/wasi-common/src/error.rs b/crates/wasi-common/src/error.rs index 7dcac689e7..e1728e504a 100644 --- a/crates/wasi-common/src/error.rs +++ b/crates/wasi-common/src/error.rs @@ -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 } } } diff --git a/crates/wasi-common/src/fs/dir.rs b/crates/wasi-common/src/fs/dir.rs index b670ad7ead..f50fcfc06b 100644 --- a/crates/wasi-common/src/fs/dir.rs +++ b/crates/wasi-common/src/fs/dir.rs @@ -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, diff --git a/crates/wasi-common/src/fs/error.rs b/crates/wasi-common/src/fs/error.rs index a887c93668..4ffc2dc182 100644 --- a/crates/wasi-common/src/fs/error.rs +++ b/crates/wasi-common/src/fs/error.rs @@ -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"), } } diff --git a/crates/wasi-common/src/helpers.rs b/crates/wasi-common/src/helpers.rs index 9762a7f4f9..057540261e 100644 --- a/crates/wasi-common/src/helpers.rs +++ b/crates/wasi-common/src/helpers.rs @@ -14,7 +14,7 @@ pub(crate) fn systemtime_to_timestamp(st: SystemTime) -> Result { /// 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) } diff --git a/crates/wasi-common/src/host.rs b/crates/wasi-common/src/host.rs index d2378e1a48..4e37d12c40 100644 --- a/crates/wasi-common/src/host.rs +++ b/crates/wasi-common/src/host.rs @@ -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) ) diff --git a/crates/wasi-common/src/hostcalls/fs.rs b/crates/wasi-common/src/hostcalls/fs.rs index fd60c59574..dd83a2c12a 100644 --- a/crates/wasi-common/src/hostcalls/fs.rs +++ b/crates/wasi-common/src/hostcalls/fs.rs @@ -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; diff --git a/crates/wasi-common/src/hostcalls/misc.rs b/crates/wasi-common/src/hostcalls/misc.rs index f3a6cdac8c..592942d58e 100644 --- a/crates/wasi-common/src/hostcalls/misc.rs +++ b/crates/wasi-common/src/hostcalls/misc.rs @@ -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; diff --git a/crates/wasi-common/src/hostcalls_impl/fs.rs b/crates/wasi-common/src/hostcalls_impl/fs.rs index 382e00c99d..ca9b52e21a 100644 --- a/crates/wasi-common/src/hostcalls_impl/fs.rs +++ b/crates/wasi-common/src/hostcalls_impl/fs.rs @@ -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(), }, }, diff --git a/crates/wasi-common/src/hostcalls_impl/fs_helpers.rs b/crates/wasi-common/src/hostcalls_impl/fs_helpers.rs index 7aaca0930b..5fd939b167 100644 --- a/crates/wasi-common/src/hostcalls_impl/fs_helpers.rs +++ b/crates/wasi-common/src/hostcalls_impl/fs_helpers.rs @@ -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); } diff --git a/crates/wasi-common/src/hostcalls_impl/misc.rs b/crates/wasi-common/src/hostcalls_impl/misc.rs index 5ad1f73b0e..f5203f0b7a 100644 --- a/crates/wasi-common/src/hostcalls_impl/misc.rs +++ b/crates/wasi-common/src/hostcalls_impl/misc.rs @@ -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 { 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() diff --git a/crates/wasi-common/src/lib.rs b/crates/wasi-common/src/lib.rs index 47aa8b9fc5..bad4770b1e 100644 --- a/crates/wasi-common/src/lib.rs +++ b/crates/wasi-common/src/lib.rs @@ -33,6 +33,7 @@ pub mod fs; mod host; pub mod hostcalls; mod memory; +pub mod old; pub mod wasi; pub mod wasi32; diff --git a/crates/wasi-common/src/macros.rs b/crates/wasi-common/src/macros.rs index 56476d3d4e..b3da97c215 100644 --- a/crates/wasi-common/src/macros.rs +++ b/crates/wasi-common/src/macros.rs @@ -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(), }; diff --git a/crates/wasi-common/src/memory.rs b/crates/wasi-common/src/memory.rs index 6f8823a2fe..4171631d42 100644 --- a/crates/wasi-common/src/memory.rs +++ b/crates/wasi-common/src/memory.rs @@ -252,14 +252,14 @@ pub(crate) fn dec_filestat_byref( let raw = dec_raw_byref::(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::(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), diff --git a/crates/wasi-common/src/old/mod.rs b/crates/wasi-common/src/old/mod.rs new file mode 100644 index 0000000000..5d4d33030a --- /dev/null +++ b/crates/wasi-common/src/old/mod.rs @@ -0,0 +1 @@ +pub mod snapshot_0; diff --git a/crates/wasi-common/src/old/snapshot_0/ctx.rs b/crates/wasi-common/src/old/snapshot_0/ctx.rs new file mode 100644 index 0000000000..d6da885165 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/ctx.rs @@ -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), + 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 + ), + Self::File(f) => write!(fmt, "PendingFdEntry::File({:?})", f), + } + } +} + +#[derive(Debug, Eq, Hash, PartialEq)] +enum PendingCString { + Bytes(Vec), + OsString(OsString), +} + +impl From> for PendingCString { + fn from(bytes: Vec) -> Self { + Self::Bytes(bytes) + } +} + +impl From for PendingCString { + fn from(s: OsString) -> Self { + Self::OsString(s) + } +} + +impl PendingCString { + fn into_string(self) -> Result { + 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 { + 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, + preopens: Vec<(PathBuf, File)>, + args: Vec, + env: HashMap, +} + +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>(mut self, args: impl IntoIterator) -> 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>(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>(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, T: Borrow<(S, S)>>( + mut self, + envs: impl IntoIterator, + ) -> 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>(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 { + // 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::>>()?; + + 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::>>()?; + + let mut fds: HashMap = 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, + pub(crate) args: Vec, + pub(crate) env: Vec, +} + +impl WasiCtx { + /// Make a new `WasiCtx` with some default settings. + /// + /// - File descriptors 0, 1, and 2 inherit stdin, stdout, and stderr from the host process. + /// + /// - Environment variables are inherited from the host process. + /// + /// To override these behaviors, use `WasiCtxBuilder`. + pub fn new>(args: impl IntoIterator) -> Result { + 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 { + // 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 { + 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 { + self.fds.remove(&fd).ok_or(Error::EBADF) + } +} diff --git a/crates/wasi-common/src/old/snapshot_0/error.rs b/crates/wasi-common/src/old/snapshot_0/error.rs new file mode 100644 index 0000000000..70566892c3 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/error.rs @@ -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 for Error { + fn from(err: WasiError) -> Self { + Self::Wasi(err) + } +} + +#[cfg(unix)] +impl From for Error { + fn from(err: nix::Error) -> Self { + Self::Nix(err) + } +} + +impl From for Error { + fn from(err: std::io::Error) -> Self { + Self::Io(err) + } +} + +impl From for Error { + fn from(_: TryFromIntError) -> Self { + Self::Wasi(WasiError::EOVERFLOW) + } +} + +impl From for Error { + fn from(_: Infallible) -> Self { + unreachable!() + } +} + +impl From for Error { + fn from(_: str::Utf8Error) -> Self { + Self::Wasi(WasiError::EILSEQ) + } +} + +#[cfg(windows)] +impl From 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 + } + } +} diff --git a/crates/wasi-common/src/old/snapshot_0/fdentry.rs b/crates/wasi-common/src/old/snapshot_0/fdentry.rs new file mode 100644 index 0000000000..25cda13dc0 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/fdentry.rs @@ -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, + // TODO: directories +} + +impl FdEntry { + pub(crate) fn from(file: fs::File) -> Result { + 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::from(file.try_clone()?) + } + + pub(crate) fn duplicate_stdin() -> Result { + 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 { + 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 { + 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::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(()) + } + } +} diff --git a/crates/wasi-common/src/old/snapshot_0/helpers.rs b/crates/wasi-common/src/old/snapshot_0/helpers.rs new file mode 100644 index 0000000000..48bd5fd3a2 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/helpers.rs @@ -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 { + 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) +} diff --git a/crates/wasi-common/src/old/snapshot_0/host.rs b/crates/wasi-common/src/old/snapshot_0/host.rs new file mode 100644 index 0000000000..ae6f156b40 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/host.rs @@ -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) + ) + ); + } +} diff --git a/crates/wasi-common/src/old/snapshot_0/hostcalls/fs.rs b/crates/wasi-common/src/old/snapshot_0/hostcalls/fs.rs new file mode 100644 index 0000000000..4836da4e76 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/hostcalls/fs.rs @@ -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; +} diff --git a/crates/wasi-common/src/old/snapshot_0/hostcalls/misc.rs b/crates/wasi-common/src/old/snapshot_0/hostcalls/misc.rs new file mode 100644 index 0000000000..42712cb4d2 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/hostcalls/misc.rs @@ -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; +} diff --git a/crates/wasi-common/src/old/snapshot_0/hostcalls/mod.rs b/crates/wasi-common/src/old/snapshot_0/hostcalls/mod.rs new file mode 100644 index 0000000000..5832ce7dba --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/hostcalls/mod.rs @@ -0,0 +1,7 @@ +mod fs; +mod misc; +mod sock; + +pub use self::fs::*; +pub use self::misc::*; +pub use self::sock::*; diff --git a/crates/wasi-common/src/old/snapshot_0/hostcalls/sock.rs b/crates/wasi-common/src/old/snapshot_0/hostcalls/sock.rs new file mode 100644 index 0000000000..51ff5d7404 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/hostcalls/sock.rs @@ -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") +} diff --git a/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/fs.rs b/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/fs.rs new file mode 100644 index 0000000000..4dd4d1c579 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/fs.rs @@ -0,0 +1,1094 @@ +#![allow(non_camel_case_types)] +use super::fs_helpers::path_get; +use crate::old::snapshot_0::ctx::WasiCtx; +use crate::old::snapshot_0::fdentry::{Descriptor, FdEntry}; +use crate::old::snapshot_0::helpers::*; +use crate::old::snapshot_0::memory::*; +use crate::old::snapshot_0::sys::fdentry_impl::determine_type_rights; +use crate::old::snapshot_0::sys::hostcalls_impl::fs_helpers::path_open_rights; +use crate::old::snapshot_0::sys::{host_impl, hostcalls_impl}; +use crate::old::snapshot_0::{helpers, host, wasi, wasi32, Error, Result}; +use filetime::{set_file_handle_times, FileTime}; +use log::trace; +use std::convert::TryInto; +use std::fs::File; +use std::io::{self, Read, Seek, SeekFrom, Write}; +use std::mem; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + +pub(crate) unsafe fn fd_close(wasi_ctx: &mut WasiCtx, fd: wasi::__wasi_fd_t) -> Result<()> { + trace!("fd_close(fd={:?})", fd); + + if let Ok(fe) = wasi_ctx.get_fd_entry(fd) { + // can't close preopened files + if fe.preopen_path.is_some() { + return Err(Error::ENOTSUP); + } + } + + wasi_ctx.remove_fd_entry(fd)?; + Ok(()) +} + +pub(crate) unsafe fn fd_datasync(wasi_ctx: &WasiCtx, fd: wasi::__wasi_fd_t) -> Result<()> { + trace!("fd_datasync(fd={:?})", fd); + + let fd = wasi_ctx + .get_fd_entry(fd)? + .as_descriptor(wasi::__WASI_RIGHTS_FD_DATASYNC, 0)? + .as_file()?; + + fd.sync_data().map_err(Into::into) +} + +pub(crate) 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, +) -> Result<()> { + trace!( + "fd_pread(fd={:?}, iovs_ptr={:#x?}, iovs_len={:?}, offset={}, nread={:#x?})", + fd, + iovs_ptr, + iovs_len, + offset, + nread + ); + + let fd = wasi_ctx + .get_fd_entry(fd)? + .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)?; + + if offset > i64::max_value() as u64 { + return Err(Error::EIO); + } + let buf_size = iovs.iter().map(|v| v.buf_len).sum(); + let mut buf = vec![0; buf_size]; + let host_nread = hostcalls_impl::fd_pread(fd, &mut buf, offset)?; + let mut buf_offset = 0; + let mut left = host_nread; + for iov in &iovs { + if left == 0 { + break; + } + let vec_len = std::cmp::min(iov.buf_len, left); + std::slice::from_raw_parts_mut(iov.buf as *mut u8, vec_len) + .copy_from_slice(&buf[buf_offset..buf_offset + vec_len]); + buf_offset += vec_len; + left -= vec_len; + } + + trace!(" | *nread={:?}", host_nread); + + enc_usize_byref(memory, nread, host_nread) +} + +pub(crate) 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, +) -> Result<()> { + trace!( + "fd_pwrite(fd={:?}, iovs_ptr={:#x?}, iovs_len={:?}, offset={}, nwritten={:#x?})", + fd, + iovs_ptr, + iovs_len, + offset, + nwritten + ); + + let fd = wasi_ctx + .get_fd_entry(fd)? + .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)?; + + if offset > i64::max_value() as u64 { + return Err(Error::EIO); + } + let buf_size = iovs.iter().map(|v| v.buf_len).sum(); + let mut buf = Vec::with_capacity(buf_size); + for iov in &iovs { + buf.extend_from_slice(std::slice::from_raw_parts( + iov.buf as *const u8, + iov.buf_len, + )); + } + let host_nwritten = hostcalls_impl::fd_pwrite(fd, &buf, offset)?; + + trace!(" | *nwritten={:?}", host_nwritten); + + enc_usize_byref(memory, nwritten, host_nwritten) +} + +pub(crate) 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, +) -> Result<()> { + trace!( + "fd_read(fd={:?}, iovs_ptr={:#x?}, iovs_len={:?}, nread={:#x?})", + fd, + iovs_ptr, + iovs_len, + nread + ); + + let mut iovs = dec_iovec_slice(memory, iovs_ptr, iovs_len)?; + let mut iovs: Vec = iovs + .iter_mut() + .map(|vec| host::iovec_to_host_mut(vec)) + .collect(); + + let maybe_host_nread = match wasi_ctx + .get_fd_entry_mut(fd)? + .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), + _ => return Err(Error::EBADF), + }; + + let host_nread = maybe_host_nread?; + + trace!(" | *nread={:?}", host_nread); + + enc_usize_byref(memory, nread, host_nread) +} + +pub(crate) unsafe fn fd_renumber( + wasi_ctx: &mut WasiCtx, + from: wasi::__wasi_fd_t, + to: wasi::__wasi_fd_t, +) -> Result<()> { + trace!("fd_renumber(from={:?}, to={:?})", from, to); + + if !wasi_ctx.contains_fd_entry(from) || !wasi_ctx.contains_fd_entry(to) { + return Err(Error::EBADF); + } + + let from_fe = wasi_ctx.get_fd_entry(from)?; + let to_fe = wasi_ctx.get_fd_entry(to)?; + + // Don't allow renumbering over a pre-opened resource. + // TODO: Eventually, we do want to permit this, once libpreopen in + // userspace is capable of removing entries from its tables as well. + if from_fe.preopen_path.is_some() || to_fe.preopen_path.is_some() { + return Err(Error::ENOTSUP); + } + + // check if stdio fds + // TODO should we renumber stdio fds? + if !from_fe.as_descriptor(0, 0)?.is_file() || !to_fe.as_descriptor(0, 0)?.is_file() { + return Err(Error::EBADF); + } + + let fe_from_dup = from_fe + .as_descriptor(0, 0)? + .as_file() + .and_then(|file| FdEntry::duplicate(file))?; + + wasi_ctx.insert_fd_entry_at(to, fe_from_dup); + wasi_ctx.remove_fd_entry(from)?; + + Ok(()) +} + +pub(crate) 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, +) -> Result<()> { + trace!( + "fd_seek(fd={:?}, offset={:?}, whence={}, newoffset={:#x?})", + fd, + offset, + wasi::whence_to_str(whence), + newoffset + ); + + let rights = if offset == 0 && whence == wasi::__WASI_WHENCE_CUR { + wasi::__WASI_RIGHTS_FD_TELL + } else { + wasi::__WASI_RIGHTS_FD_SEEK | wasi::__WASI_RIGHTS_FD_TELL + }; + let fd = wasi_ctx + .get_fd_entry_mut(fd)? + .as_descriptor_mut(rights, 0)? + .as_file_mut()?; + + let pos = match whence { + wasi::__WASI_WHENCE_CUR => SeekFrom::Current(offset), + wasi::__WASI_WHENCE_END => SeekFrom::End(offset), + wasi::__WASI_WHENCE_SET => SeekFrom::Start(offset as u64), + _ => return Err(Error::EINVAL), + }; + let host_newoffset = fd.seek(pos)?; + + trace!(" | *newoffset={:?}", host_newoffset); + + enc_filesize_byref(memory, newoffset, host_newoffset) +} + +pub(crate) unsafe fn fd_tell( + wasi_ctx: &mut WasiCtx, + memory: &mut [u8], + fd: wasi::__wasi_fd_t, + newoffset: wasi32::uintptr_t, +) -> Result<()> { + trace!("fd_tell(fd={:?}, newoffset={:#x?})", fd, newoffset); + + let fd = wasi_ctx + .get_fd_entry_mut(fd)? + .as_descriptor_mut(wasi::__WASI_RIGHTS_FD_TELL, 0)? + .as_file_mut()?; + + let host_offset = fd.seek(SeekFrom::Current(0))?; + + trace!(" | *newoffset={:?}", host_offset); + + enc_filesize_byref(memory, newoffset, host_offset) +} + +pub(crate) unsafe fn fd_fdstat_get( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasi::__wasi_fd_t, + fdstat_ptr: wasi32::uintptr_t, // *mut wasi::__wasi_fdstat_t +) -> Result<()> { + trace!("fd_fdstat_get(fd={:?}, fdstat_ptr={:#x?})", fd, fdstat_ptr); + + let mut fdstat = dec_fdstat_byref(memory, fdstat_ptr)?; + let wasi_fd = wasi_ctx.get_fd_entry(fd)?.as_descriptor(0, 0)?.as_file()?; + + let fs_flags = hostcalls_impl::fd_fdstat_get(wasi_fd)?; + + let fe = wasi_ctx.get_fd_entry(fd)?; + fdstat.fs_filetype = fe.file_type; + fdstat.fs_rights_base = fe.rights_base; + fdstat.fs_rights_inheriting = fe.rights_inheriting; + fdstat.fs_flags = fs_flags; + + trace!(" | *buf={:?}", fdstat); + + enc_fdstat_byref(memory, fdstat_ptr, fdstat) +} + +pub(crate) unsafe fn fd_fdstat_set_flags( + wasi_ctx: &WasiCtx, + fd: wasi::__wasi_fd_t, + fdflags: wasi::__wasi_fdflags_t, +) -> Result<()> { + trace!("fd_fdstat_set_flags(fd={:?}, fdflags={:#x?})", fd, fdflags); + + let fd = wasi_ctx.get_fd_entry(fd)?.as_descriptor(0, 0)?.as_file()?; + + hostcalls_impl::fd_fdstat_set_flags(fd, fdflags) +} + +pub(crate) 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, +) -> Result<()> { + trace!( + "fd_fdstat_set_rights(fd={:?}, fs_rights_base={:#x?}, fs_rights_inheriting={:#x?})", + fd, + fs_rights_base, + fs_rights_inheriting + ); + + let fe = wasi_ctx.get_fd_entry_mut(fd)?; + if fe.rights_base & fs_rights_base != fs_rights_base + || fe.rights_inheriting & fs_rights_inheriting != fs_rights_inheriting + { + return Err(Error::ENOTCAPABLE); + } + fe.rights_base = fs_rights_base; + fe.rights_inheriting = fs_rights_inheriting; + + Ok(()) +} + +pub(crate) unsafe fn fd_sync(wasi_ctx: &WasiCtx, fd: wasi::__wasi_fd_t) -> Result<()> { + trace!("fd_sync(fd={:?})", fd); + + let fd = wasi_ctx + .get_fd_entry(fd)? + .as_descriptor(wasi::__WASI_RIGHTS_FD_SYNC, 0)? + .as_file()?; + fd.sync_all().map_err(Into::into) +} + +pub(crate) 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, +) -> Result<()> { + trace!( + "fd_write(fd={:?}, iovs_ptr={:#x?}, iovs_len={:?}, nwritten={:#x?})", + fd, + iovs_ptr, + iovs_len, + nwritten + ); + + let iovs = dec_ciovec_slice(memory, iovs_ptr, iovs_len)?; + let iovs: Vec = iovs.iter().map(|vec| host::ciovec_to_host(vec)).collect(); + + // perform unbuffered writes + let host_nwritten = match wasi_ctx + .get_fd_entry_mut(fd)? + .as_descriptor_mut(wasi::__WASI_RIGHTS_FD_WRITE, 0)? + { + Descriptor::OsFile(file) => file.write_vectored(&iovs)?, + Descriptor::Stdin => return Err(Error::EBADF), + Descriptor::Stdout => { + // lock for the duration of the scope + let stdout = io::stdout(); + let mut stdout = stdout.lock(); + let nwritten = stdout.write_vectored(&iovs)?; + stdout.flush()?; + nwritten + } + Descriptor::Stderr => io::stderr().lock().write_vectored(&iovs)?, + }; + + trace!(" | *nwritten={:?}", host_nwritten); + + enc_usize_byref(memory, nwritten, host_nwritten) +} + +pub(crate) 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, +) -> Result<()> { + trace!( + "fd_advise(fd={:?}, offset={}, len={}, advice={:?})", + fd, + offset, + len, + advice + ); + + let fd = wasi_ctx + .get_fd_entry(fd)? + .as_descriptor(wasi::__WASI_RIGHTS_FD_ADVISE, 0)? + .as_file()?; + + hostcalls_impl::fd_advise(fd, advice, offset, len) +} + +pub(crate) unsafe fn fd_allocate( + wasi_ctx: &WasiCtx, + fd: wasi::__wasi_fd_t, + offset: wasi::__wasi_filesize_t, + len: wasi::__wasi_filesize_t, +) -> Result<()> { + trace!("fd_allocate(fd={:?}, offset={}, len={})", fd, offset, len); + + let fd = wasi_ctx + .get_fd_entry(fd)? + .as_descriptor(wasi::__WASI_RIGHTS_FD_ALLOCATE, 0)? + .as_file()?; + + let metadata = fd.metadata()?; + + let current_size = metadata.len(); + let wanted_size = offset.checked_add(len).ok_or(Error::E2BIG)?; + // This check will be unnecessary when rust-lang/rust#63326 is fixed + if wanted_size > i64::max_value() as u64 { + return Err(Error::E2BIG); + } + + if wanted_size > current_size { + fd.set_len(wanted_size).map_err(Into::into) + } else { + Ok(()) + } +} + +pub(crate) 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, +) -> Result<()> { + trace!( + "path_create_directory(dirfd={:?}, path_ptr={:#x?}, path_len={})", + dirfd, + path_ptr, + path_len, + ); + + let path = dec_slice_of_u8(memory, path_ptr, path_len).and_then(helpers::path_from_slice)?; + + trace!(" | (path_ptr,path_len)='{}'", path); + + 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)?; + + hostcalls_impl::path_create_directory(resolved) +} + +pub(crate) 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, +) -> Result<()> { + trace!( + "path_link(old_dirfd={:?}, old_flags={:?}, old_path_ptr={:#x?}, old_path_len={}, new_dirfd={:?}, new_path_ptr={:#x?}, new_path_len={})", + old_dirfd, + old_flags, + old_path_ptr, + old_path_len, + new_dirfd, + new_path_ptr, + new_path_len, + ); + + let old_path = dec_slice_of_u8(memory, old_path_ptr, old_path_len).and_then(path_from_slice)?; + let new_path = dec_slice_of_u8(memory, new_path_ptr, new_path_len).and_then(path_from_slice)?; + + trace!(" | (old_path_ptr,old_path_len)='{}'", old_path); + trace!(" | (new_path_ptr,new_path_len)='{}'", new_path); + + let old_fe = wasi_ctx.get_fd_entry(old_dirfd)?; + let new_fe = wasi_ctx.get_fd_entry(new_dirfd)?; + let resolved_old = path_get( + old_fe, + wasi::__WASI_RIGHTS_PATH_LINK_SOURCE, + 0, + 0, + old_path, + false, + )?; + let resolved_new = path_get( + new_fe, + wasi::__WASI_RIGHTS_PATH_LINK_TARGET, + 0, + 0, + new_path, + false, + )?; + + hostcalls_impl::path_link(resolved_old, resolved_new) +} + +pub(crate) 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, +) -> Result<()> { + trace!( + "path_open(dirfd={:?}, dirflags={:?}, path_ptr={:#x?}, path_len={:?}, oflags={:#x?}, fs_rights_base={:#x?}, fs_rights_inheriting={:#x?}, fs_flags={:#x?}, fd_out_ptr={:#x?})", + dirfd, + dirflags, + path_ptr, + path_len, + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + fd_out_ptr + ); + + // pre-encode fd_out_ptr to -1 in case of error in opening a path + enc_fd_byref(memory, fd_out_ptr, wasi::__wasi_fd_t::max_value())?; + + let path = dec_slice_of_u8(memory, path_ptr, path_len).and_then(path_from_slice)?; + + trace!(" | (path_ptr,path_len)='{}'", path); + + let (needed_base, needed_inheriting) = + path_open_rights(fs_rights_base, fs_rights_inheriting, oflags, fs_flags); + let fe = wasi_ctx.get_fd_entry(dirfd)?; + let resolved = path_get( + fe, + needed_base, + needed_inheriting, + dirflags, + path, + oflags & wasi::__WASI_OFLAGS_CREAT != 0, + )?; + + // which open mode do we need? + let read = fs_rights_base & (wasi::__WASI_RIGHTS_FD_READ | wasi::__WASI_RIGHTS_FD_READDIR) != 0; + let write = fs_rights_base + & (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)?; + + // Determine the type of the new file descriptor and which rights contradict with this type + let (_ty, max_base, max_inheriting) = determine_type_rights(&fd)?; + let mut fe = FdEntry::from(fd)?; + fe.rights_base &= max_base; + fe.rights_inheriting &= max_inheriting; + let guest_fd = wasi_ctx.insert_fd_entry(fe)?; + + trace!(" | *fd={:?}", guest_fd); + + enc_fd_byref(memory, fd_out_ptr, guest_fd) +} + +pub(crate) 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, +) -> Result<()> { + trace!( + "fd_readdir(fd={:?}, buf={:#x?}, buf_len={}, cookie={:#x?}, buf_used={:#x?})", + fd, + buf, + buf_len, + cookie, + buf_used, + ); + + enc_usize_byref(memory, buf_used, 0)?; + + let file = wasi_ctx + .get_fd_entry_mut(fd)? + .as_descriptor_mut(wasi::__WASI_RIGHTS_FD_READDIR, 0)? + .as_file_mut()?; + let host_buf = dec_slice_of_mut_u8(memory, buf, buf_len)?; + + trace!(" | (buf,buf_len)={:?}", host_buf); + + let host_bufused = hostcalls_impl::fd_readdir(file, host_buf, cookie)?; + + trace!(" | *buf_used={:?}", host_bufused); + + enc_usize_byref(memory, buf_used, host_bufused) +} + +pub(crate) 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, +) -> Result<()> { + trace!( + "path_readlink(dirfd={:?}, path_ptr={:#x?}, path_len={:?}, buf_ptr={:#x?}, buf_len={}, buf_used={:#x?})", + dirfd, + path_ptr, + path_len, + buf_ptr, + buf_len, + buf_used, + ); + + enc_usize_byref(memory, buf_used, 0)?; + + let path = dec_slice_of_u8(memory, path_ptr, path_len).and_then(helpers::path_from_slice)?; + + trace!(" | (path_ptr,path_len)='{}'", &path); + + let fe = wasi_ctx.get_fd_entry(dirfd)?; + 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)?; + + let host_bufused = hostcalls_impl::path_readlink(resolved, &mut buf)?; + + trace!(" | (buf_ptr,*buf_used)={:?}", buf); + trace!(" | *buf_used={:?}", host_bufused); + + enc_usize_byref(memory, buf_used, host_bufused) +} + +pub(crate) 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, +) -> Result<()> { + trace!( + "path_rename(old_dirfd={:?}, old_path_ptr={:#x?}, old_path_len={:?}, new_dirfd={:?}, new_path_ptr={:#x?}, new_path_len={:?})", + old_dirfd, + old_path_ptr, + old_path_len, + new_dirfd, + new_path_ptr, + new_path_len, + ); + + let old_path = dec_slice_of_u8(memory, old_path_ptr, old_path_len).and_then(path_from_slice)?; + let new_path = dec_slice_of_u8(memory, new_path_ptr, new_path_len).and_then(path_from_slice)?; + + trace!(" | (old_path_ptr,old_path_len)='{}'", old_path); + trace!(" | (new_path_ptr,new_path_len)='{}'", new_path); + + let old_fe = wasi_ctx.get_fd_entry(old_dirfd)?; + let new_fe = wasi_ctx.get_fd_entry(new_dirfd)?; + let resolved_old = path_get( + old_fe, + wasi::__WASI_RIGHTS_PATH_RENAME_SOURCE, + 0, + 0, + old_path, + true, + )?; + let resolved_new = path_get( + new_fe, + wasi::__WASI_RIGHTS_PATH_RENAME_TARGET, + 0, + 0, + new_path, + true, + )?; + + log::debug!("path_rename resolved_old={:?}", resolved_old); + log::debug!("path_rename resolved_new={:?}", resolved_new); + + hostcalls_impl::path_rename(resolved_old, resolved_new) +} + +pub(crate) unsafe fn fd_filestat_get( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasi::__wasi_fd_t, + filestat_ptr: wasi32::uintptr_t, +) -> Result<()> { + trace!( + "fd_filestat_get(fd={:?}, filestat_ptr={:#x?})", + fd, + filestat_ptr + ); + + let fd = wasi_ctx.get_fd_entry(fd)?.as_descriptor(0, 0)?.as_file()?; + + let host_filestat = hostcalls_impl::fd_filestat_get_impl(fd)?; + + trace!(" | *filestat_ptr={:?}", host_filestat); + + enc_filestat_byref(memory, filestat_ptr, host_filestat) +} + +pub(crate) 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, +) -> Result<()> { + trace!( + "fd_filestat_set_times(fd={:?}, st_atim={}, st_mtim={}, fst_flags={:#x?})", + fd, + st_atim, + st_mtim, + fst_flags + ); + + let fd = wasi_ctx + .get_fd_entry(fd)? + .as_descriptor(wasi::__WASI_RIGHTS_FD_FILESTAT_SET_TIMES, 0)? + .as_file()?; + + fd_filestat_set_times_impl(fd, st_atim, st_mtim, fst_flags) +} + +pub(crate) fn fd_filestat_set_times_impl( + fd: &File, + st_atim: wasi::__wasi_timestamp_t, + st_mtim: wasi::__wasi_timestamp_t, + fst_flags: wasi::__wasi_fstflags_t, +) -> Result<()> { + 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 atim = if set_atim { + let time = UNIX_EPOCH + Duration::from_nanos(st_atim); + Some(FileTime::from_system_time(time)) + } else if set_atim_now { + let time = SystemTime::now(); + Some(FileTime::from_system_time(time)) + } else { + None + }; + + let mtim = if set_mtim { + let time = UNIX_EPOCH + Duration::from_nanos(st_mtim); + Some(FileTime::from_system_time(time)) + } else if set_mtim_now { + let time = SystemTime::now(); + Some(FileTime::from_system_time(time)) + } else { + None + }; + set_file_handle_times(fd, atim, mtim).map_err(Into::into) +} + +pub(crate) unsafe fn fd_filestat_set_size( + wasi_ctx: &WasiCtx, + fd: wasi::__wasi_fd_t, + st_size: wasi::__wasi_filesize_t, +) -> Result<()> { + trace!("fd_filestat_set_size(fd={:?}, st_size={})", fd, st_size); + + let fd = wasi_ctx + .get_fd_entry(fd)? + .as_descriptor(wasi::__WASI_RIGHTS_FD_FILESTAT_SET_SIZE, 0)? + .as_file()?; + + // This check will be unnecessary when rust-lang/rust#63326 is fixed + if st_size > i64::max_value() as u64 { + return Err(Error::E2BIG); + } + fd.set_len(st_size).map_err(Into::into) +} + +pub(crate) 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, +) -> Result<()> { + trace!( + "path_filestat_get(dirfd={:?}, dirflags={:?}, path_ptr={:#x?}, path_len={}, filestat_ptr={:#x?})", + dirfd, + dirflags, + path_ptr, + path_len, + filestat_ptr + ); + + let path = dec_slice_of_u8(memory, path_ptr, path_len).and_then(path_from_slice)?; + + trace!(" | (path_ptr,path_len)='{}'", path); + + let fe = wasi_ctx.get_fd_entry(dirfd)?; + let resolved = path_get( + fe, + wasi::__WASI_RIGHTS_PATH_FILESTAT_GET, + 0, + dirflags, + path, + false, + )?; + let host_filestat = hostcalls_impl::path_filestat_get(resolved, dirflags)?; + + trace!(" | *filestat_ptr={:?}", host_filestat); + + enc_filestat_byref(memory, filestat_ptr, host_filestat) +} + +pub(crate) 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, +) -> Result<()> { + trace!( + "path_filestat_set_times(dirfd={:?}, dirflags={:?}, path_ptr={:#x?}, path_len={}, st_atim={}, st_mtim={}, fst_flags={:#x?})", + dirfd, + dirflags, + path_ptr, + path_len, + st_atim, st_mtim, + fst_flags + ); + + let path = dec_slice_of_u8(memory, path_ptr, path_len).and_then(path_from_slice)?; + + trace!(" | (path_ptr,path_len)='{}'", path); + + let fe = wasi_ctx.get_fd_entry(dirfd)?; + let resolved = path_get( + fe, + wasi::__WASI_RIGHTS_PATH_FILESTAT_SET_TIMES, + 0, + dirflags, + path, + false, + )?; + + hostcalls_impl::path_filestat_set_times(resolved, dirflags, st_atim, st_mtim, fst_flags) +} + +pub(crate) 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, +) -> Result<()> { + trace!( + "path_symlink(old_path_ptr={:#x?}, old_path_len={}, dirfd={:?}, new_path_ptr={:#x?}, new_path_len={})", + old_path_ptr, + old_path_len, + dirfd, + new_path_ptr, + new_path_len + ); + + let old_path = dec_slice_of_u8(memory, old_path_ptr, old_path_len).and_then(path_from_slice)?; + let new_path = dec_slice_of_u8(memory, new_path_ptr, new_path_len).and_then(path_from_slice)?; + + trace!(" | (old_path_ptr,old_path_len)='{}'", old_path); + 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_RIGHTS_PATH_SYMLINK, 0, 0, new_path, true)?; + + hostcalls_impl::path_symlink(old_path, resolved_new) +} + +pub(crate) 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, +) -> Result<()> { + trace!( + "path_unlink_file(dirfd={:?}, path_ptr={:#x?}, path_len={})", + dirfd, + path_ptr, + path_len + ); + + let path = dec_slice_of_u8(memory, path_ptr, path_len).and_then(path_from_slice)?; + + trace!(" | (path_ptr,path_len)='{}'", path); + + let fe = wasi_ctx.get_fd_entry(dirfd)?; + let resolved = path_get(fe, wasi::__WASI_RIGHTS_PATH_UNLINK_FILE, 0, 0, path, false)?; + + hostcalls_impl::path_unlink_file(resolved) +} + +pub(crate) 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, +) -> Result<()> { + trace!( + "path_remove_directory(dirfd={:?}, path_ptr={:#x?}, path_len={})", + dirfd, + path_ptr, + path_len + ); + + let path = dec_slice_of_u8(memory, path_ptr, path_len).and_then(path_from_slice)?; + + trace!(" | (path_ptr,path_len)='{}'", path); + + let fe = wasi_ctx.get_fd_entry(dirfd)?; + let resolved = path_get( + fe, + wasi::__WASI_RIGHTS_PATH_REMOVE_DIRECTORY, + 0, + 0, + path, + true, + )?; + + log::debug!("path_remove_directory resolved={:?}", resolved); + + hostcalls_impl::path_remove_directory(resolved) +} + +pub(crate) unsafe fn fd_prestat_get( + wasi_ctx: &WasiCtx, + memory: &mut [u8], + fd: wasi::__wasi_fd_t, + prestat_ptr: wasi32::uintptr_t, +) -> Result<()> { + trace!( + "fd_prestat_get(fd={:?}, prestat_ptr={:#x?})", + fd, + prestat_ptr + ); + + // TODO: should we validate any rights here? + let fe = wasi_ctx.get_fd_entry(fd)?; + let po_path = fe.preopen_path.as_ref().ok_or(Error::ENOTSUP)?; + if fe.file_type != wasi::__WASI_FILETYPE_DIRECTORY { + return Err(Error::ENOTDIR); + } + + let path = host_impl::path_from_host(po_path.as_os_str())?; + + enc_prestat_byref( + memory, + prestat_ptr, + 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: path.len(), + }, + }, + }, + ) +} + +pub(crate) 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, +) -> Result<()> { + trace!( + "fd_prestat_dir_name(fd={:?}, path_ptr={:#x?}, path_len={})", + fd, + path_ptr, + path_len + ); + + // TODO: should we validate any rights here? + let fe = wasi_ctx.get_fd_entry(fd)?; + let po_path = fe.preopen_path.as_ref().ok_or(Error::ENOTSUP)?; + if fe.file_type != wasi::__WASI_FILETYPE_DIRECTORY { + return Err(Error::ENOTDIR); + } + + let path = host_impl::path_from_host(po_path.as_os_str())?; + + if path.len() > dec_usize(path_len) { + return Err(Error::ENAMETOOLONG); + } + + trace!(" | (path_ptr,path_len)='{}'", path); + + enc_slice_of_u8(memory, path.as_bytes(), path_ptr) +} + +#[allow(dead_code)] // trouble with sockets +#[derive(Clone, Copy, Debug)] +#[repr(u8)] +pub(crate) enum FileType { + Unknown = wasi::__WASI_FILETYPE_UNKNOWN, + BlockDevice = wasi::__WASI_FILETYPE_BLOCK_DEVICE, + CharacterDevice = wasi::__WASI_FILETYPE_CHARACTER_DEVICE, + Directory = wasi::__WASI_FILETYPE_DIRECTORY, + RegularFile = wasi::__WASI_FILETYPE_REGULAR_FILE, + SocketDgram = wasi::__WASI_FILETYPE_SOCKET_DGRAM, + SocketStream = wasi::__WASI_FILETYPE_SOCKET_STREAM, + Symlink = wasi::__WASI_FILETYPE_SYMBOLIC_LINK, +} + +impl FileType { + pub(crate) fn to_wasi(&self) -> wasi::__wasi_filetype_t { + *self as wasi::__wasi_filetype_t + } +} + +#[derive(Debug, Clone)] +pub(crate) struct Dirent { + pub name: String, + pub ftype: FileType, + pub ino: u64, + pub cookie: wasi::__wasi_dircookie_t, +} + +impl Dirent { + #![allow(unused)] // temporarily, until BSD catches up with this change + /// Serialize the directory entry to the format define by `__wasi_fd_readdir`, + /// so that the serialized entries can be concatenated by the implementation. + pub fn to_wasi_raw(&self) -> Result> { + use std::slice; + + let name = self.name.as_bytes(); + let namlen = name.len(); + let dirent_size = mem::size_of::(); + let offset = dirent_size.checked_add(namlen).ok_or(Error::EOVERFLOW)?; + + let mut raw = Vec::::with_capacity(offset); + raw.resize(offset, 0); + + let sys_dirent = raw.as_mut_ptr() as *mut wasi::__wasi_dirent_t; + unsafe { + *sys_dirent = wasi::__wasi_dirent_t { + d_namlen: namlen.try_into()?, + d_ino: self.ino, + d_next: self.cookie, + d_type: self.ftype.to_wasi(), + }; + } + + let sys_name = unsafe { sys_dirent.offset(1) as *mut u8 }; + let sys_name = unsafe { slice::from_raw_parts_mut(sys_name, namlen) }; + sys_name.copy_from_slice(&name); + + Ok(raw) + } +} diff --git a/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/fs_helpers.rs b/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/fs_helpers.rs new file mode 100644 index 0000000000..3a26bfc54b --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/fs_helpers.rs @@ -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 { + 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("."), + }); + } + } + } +} diff --git a/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/misc.rs b/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/misc.rs new file mode 100644 index 0000000000..83b193cb43 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/misc.rs @@ -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 = 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 { + 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, +} diff --git a/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/mod.rs b/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/mod.rs new file mode 100644 index 0000000000..f74be814b0 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/hostcalls_impl/mod.rs @@ -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::*; diff --git a/crates/wasi-common/src/old/snapshot_0/memory.rs b/crates/wasi-common/src/old/snapshot_0/memory.rs new file mode 100644 index 0000000000..5854c3ee93 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/memory.rs @@ -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::() != 0 { + return Err(Error::EINVAL); + } + + dec_ptr(memory, ptr, size_of::()).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::() != 0 { + return Err(Error::EINVAL); + } + + dec_ptr_mut(memory, ptr, size_of::()).map(|p| unsafe { &mut *(p as *mut T) }) +} + +/// This function does not perform endianness conversions! +fn dec_raw_byref(memory: &[u8], ptr: wasi32::uintptr_t) -> Result { + dec_ptr_to::(memory, ptr).map(|p| unsafe { ptr::read(p) }) +} + +/// This function does not perform endianness conversions! +fn enc_raw_byref(memory: &mut [u8], ptr: wasi32::uintptr_t, t: T) -> Result<()> { + dec_ptr_to_mut::(memory, ptr).map(|p| unsafe { ptr::write(p, t) }) +} + +pub(crate) fn dec_int_byref(memory: &[u8], ptr: wasi32::uintptr_t) -> Result +where + T: PrimInt, +{ + dec_raw_byref::(memory, ptr).map(|i| PrimInt::from_le(i)) +} + +pub(crate) fn enc_int_byref(memory: &mut [u8], ptr: wasi32::uintptr_t, t: T) -> Result<()> +where + T: PrimInt, +{ + enc_raw_byref::(memory, ptr, PrimInt::to_le(t)) +} + +fn check_slice_of(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::() != 0 { + return Err(Error::EINVAL); + } + let len = dec_usize(len); + let len_bytes = if let Some(len) = size_of::().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::(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::(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::() != 0 { + return Err(Error::EINVAL); + } + // check that length doesn't overflow + let len_bytes = if let Some(len) = size_of::().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::(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::(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::(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::(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 { + dec_int_byref::(memory, ptr) + } + + pub(crate) fn $enc_byref( + memory: &mut [u8], + ptr: wasi32::uintptr_t, + x: wasi::$ty, + ) -> Result<()> { + enc_int_byref::(memory, ptr, x) + } + }; +} + +pub(crate) fn dec_ciovec_slice( + memory: &[u8], + ptr: wasi32::uintptr_t, + len: wasi32::size_t, +) -> Result> { + let raw_slice = dec_raw_slice_of::(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> { + let raw_slice = dec_raw_slice_of::(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 { + let raw = dec_raw_byref::(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::(memory, filestat_ptr, raw) +} + +pub(crate) fn dec_fdstat_byref( + memory: &mut [u8], + fdstat_ptr: wasi32::uintptr_t, +) -> Result { + let raw = dec_raw_byref::(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::(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 { + let raw = dec_raw_byref::(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::(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::(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> { + let raw_input_slice = + dec_raw_slice_of::(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::>>() +} + +pub(crate) fn enc_events( + memory: &mut [u8], + output: wasi32::uintptr_t, + nsubscriptions: wasi32::size_t, + events: Vec, +) -> Result<()> { + let mut raw_output_iter = + dec_raw_slice_of_mut::(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); diff --git a/crates/wasi-common/src/old/snapshot_0/mod.rs b/crates/wasi-common/src/old/snapshot_0/mod.rs new file mode 100644 index 0000000000..5d4e7f01b0 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/mod.rs @@ -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 = std::result::Result; diff --git a/crates/wasi-common/src/old/snapshot_0/sys/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/mod.rs new file mode 100644 index 0000000000..468996239f --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/mod.rs @@ -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"); + } +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/filetime.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/filetime.rs new file mode 100644 index 0000000000..8825e97a66 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/filetime.rs @@ -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 { + 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; +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/hostcalls_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/hostcalls_impl.rs new file mode 100644 index 0000000000..f42467198a --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/hostcalls_impl.rs @@ -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 { + 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(()) +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/mod.rs new file mode 100644 index 0000000000..933048af34 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/mod.rs @@ -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 { + 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 { + let mut entry = unsafe { std::mem::zeroed::() }; + 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) + } +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/osfile.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/osfile.rs new file mode 100644 index 0000000000..41340149aa --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/bsd/osfile.rs @@ -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, + 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>, +} + +impl From 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 + } +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/dir.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/dir.rs new file mode 100644 index 0000000000..26f2ddc414 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/dir.rs @@ -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); + +impl Dir { + /// Converts from a descriptor-based object, closing the descriptor on success or failure. + #[inline] + pub(crate) fn from(fd: F) -> Result { + 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 { + 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; + fn next(&mut self) -> Option { + 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::::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; + + 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() + } +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/fdentry_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/fdentry_impl.rs new file mode 100644 index 0000000000..b63a850df5 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/fdentry_impl.rs @@ -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: &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: &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)) +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/filetime.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/filetime.rs new file mode 100644 index 0000000000..061537b3b8 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/filetime.rs @@ -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, +) -> 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 _, + }, + } +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/host_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/host_impl.rs new file mode 100644 index 0000000000..510a8c4b59 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/host_impl.rs @@ -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 { + use std::convert::TryFrom; + fn filestat_to_timestamp(secs: u64, nsecs: u64) -> Result { + 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 { + 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: S) -> Result { + helpers::path_from_slice(s.as_ref().as_bytes()).map(String::from) +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs.rs new file mode 100644 index 0000000000..cafce8ba0f --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs.rs @@ -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 { + file.read_at(buf, offset).map_err(Into::into) +} + +pub(crate) fn fd_pwrite(file: &File, buf: &[u8], offset: wasi::__wasi_filesize_t) -> Result { + file.write_at(buf, offset).map_err(Into::into) +} + +pub(crate) fn fd_fdstat_get(fd: &File) -> Result { + 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 { + 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 { + 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 { + 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 { + 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 { + 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())), + } +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs_helpers.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs_helpers.rs new file mode 100644 index 0000000000..eb83287599 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/fs_helpers.rs @@ -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 { + 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 { + 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) +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/misc.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/misc.rs new file mode 100644 index 0000000000..43acdf1fd9 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/misc.rs @@ -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 { + // 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 { + 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::::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 { + 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::::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, + fd_events: Vec, + events: &mut Vec, +) -> 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, +) { + 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, nix::poll::PollFd)>, + events: &mut Vec, +) -> 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(()) +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/mod.rs new file mode 100644 index 0000000000..ba18086104 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/hostcalls_impl/mod.rs @@ -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::*; diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/filetime.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/filetime.rs new file mode 100644 index 0000000000..9fb0516b52 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/filetime.rs @@ -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) +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/hostcalls_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/hostcalls_impl.rs new file mode 100644 index 0000000000..deebe005ff --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/hostcalls_impl.rs @@ -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>> { + // 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 { + 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(()) +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/mod.rs new file mode 100644 index 0000000000..b6c94efb54 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/mod.rs @@ -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 { + 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; +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/osfile.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/osfile.rs new file mode 100644 index 0000000000..b92a9793a6 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/linux/osfile.rs @@ -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 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 + } +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/unix/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/unix/mod.rs new file mode 100644 index 0000000000..b6288512dd --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/unix/mod.rs @@ -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 { + OpenOptions::new() + .read(true) + .write(true) + .open("/dev/null") + .map_err(Into::into) +} + +pub(crate) fn str_to_cstring(s: &str) -> Result { + CString::new(s.as_bytes()).map_err(|_| Error::EILSEQ) +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/windows/fdentry_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/windows/fdentry_impl.rs new file mode 100644 index 0000000000..7bb2c90c50 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/windows/fdentry_impl.rs @@ -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 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: &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: &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)) +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/windows/host_impl.rs b/crates/wasi-common/src/old/snapshot_0/sys/windows/host_impl.rs new file mode 100644 index 0000000000..ddb1f35f5d --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/windows/host_impl.rs @@ -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: S) -> Result { + let vec: Vec = s.as_ref().encode_wide().collect(); + String::from_utf16(&vec).map_err(|_| Error::EILSEQ) +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/fs.rs b/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/fs.rs new file mode 100644 index 0000000000..e53f52fe44 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/fs.rs @@ -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 { + // 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 { + // 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 { + 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 { + write_at(file, buf, offset).map_err(Into::into) +} + +pub(crate) fn fd_fdstat_get(fd: &File) -> Result { + 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 { + 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>( + path: P, + name: &str, + cookie: wasi::__wasi_dircookie_t, +) -> Result { + 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>> { + 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 { + 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 { + 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> { + 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 { + Ok(winx::file::get_fileinfo(file)?.nNumberOfLinks.into()) +} + +pub(crate) fn device_id(file: &File, _metadata: &Metadata) -> io::Result { + Ok(winx::file::get_fileinfo(file)?.dwVolumeSerialNumber.into()) +} + +pub(crate) fn file_serial_no(file: &File) -> io::Result { + 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 { + winx::file::change_time(file) +} + +pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result { + 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 { + 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) +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/fs_helpers.rs b/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/fs_helpers.rs new file mode 100644 index 0000000000..f05721a8dd --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/fs_helpers.rs @@ -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; +} + +impl PathGetExt for PathGet { + fn concatenate(&self) -> Result { + 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 { + 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 { + 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>(path: P) -> OsString { + let path: Vec = 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>(dirfd: &File, path: P) -> Result { + 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) +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/misc.rs b/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/misc.rs new file mode 100644 index 0000000000..b6ed4df11e --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/misc.rs @@ -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 { + 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 { + 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, + fd_events: Vec, + events: &mut Vec, +) -> Result> { + 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 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(|_| Error::EFAULT) +} + +fn get_proc_cputime() -> Result { + Ok(ProcessTime::try_now()?.as_duration()) +} + +fn get_thread_cputime() -> Result { + 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 +} diff --git a/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/mod.rs new file mode 100644 index 0000000000..fdbf448f80 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/windows/hostcalls_impl/mod.rs @@ -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::*; diff --git a/crates/wasi-common/src/old/snapshot_0/sys/windows/mod.rs b/crates/wasi-common/src/old/snapshot_0/sys/windows/mod.rs new file mode 100644 index 0000000000..b733f14920 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/sys/windows/mod.rs @@ -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 { + OpenOptions::new() + .read(true) + .write(true) + .open("NUL") + .map_err(Into::into) +} + +pub fn preopen_dir>(path: P) -> Result { + 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) +} diff --git a/crates/wasi-common/src/old/snapshot_0/wasi.rs b/crates/wasi-common/src/old/snapshot_0/wasi.rs new file mode 100644 index 0000000000..5f690fc03a --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/wasi.rs @@ -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) + ) + ); + } +} diff --git a/crates/wasi-common/src/old/snapshot_0/wasi32.rs b/crates/wasi-common/src/old/snapshot_0/wasi32.rs new file mode 100644 index 0000000000..9a102f9b48 --- /dev/null +++ b/crates/wasi-common/src/old/snapshot_0/wasi32.rs @@ -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) + ) + ); + } +} diff --git a/crates/wasi-common/src/sys/unix/fdentry_impl.rs b/crates/wasi-common/src/sys/unix/fdentry_impl.rs index 0a342acbd9..fcbe499999 100644 --- a/crates/wasi-common/src/sys/unix/fdentry_impl.rs +++ b/crates/wasi-common/src/sys/unix/fdentry_impl.rs @@ -46,9 +46,9 @@ pub(crate) unsafe fn determine_type_and_access_rights( 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)) diff --git a/crates/wasi-common/src/sys/unix/host_impl.rs b/crates/wasi-common/src/sys/unix/host_impl.rs index 8a3116b388..5c64fadd24 100644 --- a/crates/wasi-common/src/sys/unix/host_impl.rs +++ b/crates/wasi-common/src/sys/unix/host_impl.rs @@ -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: S) -> Result { helpers::path_from_slice(s.as_ref().as_bytes()).map(String::from) } diff --git a/crates/wasi-common/src/sys/unix/hostcalls_impl/fs.rs b/crates/wasi-common/src/sys/unix/hostcalls_impl/fs.rs index 3bdd8eefd7..8a853d510c 100644 --- a/crates/wasi-common/src/sys/unix/hostcalls_impl/fs.rs +++ b/crates/wasi-common/src/sys/unix/hostcalls_impl/fs.rs @@ -217,14 +217,14 @@ pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result Result { // 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, diff --git a/crates/wasi-common/src/sys/windows/fdentry_impl.rs b/crates/wasi-common/src/sys/windows/fdentry_impl.rs index 7e8bda0567..88eda6ae57 100644 --- a/crates/wasi-common/src/sys/windows/fdentry_impl.rs +++ b/crates/wasi-common/src/sys/windows/fdentry_impl.rs @@ -61,10 +61,10 @@ pub(crate) unsafe fn determine_type_and_access_rights( 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; } } _ => { diff --git a/crates/wasi-common/src/sys/windows/host_impl.rs b/crates/wasi-common/src/sys/windows/host_impl.rs index 18cf4a4adb..e2eea28497 100644 --- a/crates/wasi-common/src/sys/windows/host_impl.rs +++ b/crates/wasi-common/src/sys/windows/host_impl.rs @@ -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: S) -> Result { let vec: Vec = s.as_ref().encode_wide().collect(); String::from_utf16(&vec).map_err(|_| Error::EILSEQ) diff --git a/crates/wasi-common/src/sys/windows/hostcalls_impl/fs.rs b/crates/wasi-common/src/sys/windows/hostcalls_impl/fs.rs index cba0edc623..0279a257cf 100644 --- a/crates/wasi-common/src/sys/windows/hostcalls_impl/fs.rs +++ b/crates/wasi-common/src/sys/windows/hostcalls_impl/fs.rs @@ -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 pub(crate) fn fd_filestat_get_impl(file: &std::fs::File) -> Result { 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(), }) } diff --git a/crates/wasi-common/src/sys/windows/hostcalls_impl/fs_helpers.rs b/crates/wasi-common/src/sys/windows/hostcalls_impl/fs_helpers.rs index ddbfed5e20..8fe392ccba 100644 --- a/crates/wasi-common/src/sys/windows/hostcalls_impl/fs_helpers.rs +++ b/crates/wasi-common/src/sys/windows/hostcalls_impl/fs_helpers.rs @@ -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) diff --git a/crates/wasi-common/src/sys/windows/hostcalls_impl/misc.rs b/crates/wasi-common/src/sys/windows/hostcalls_impl/misc.rs index b77503f1f3..28d1408de1 100644 --- a/crates/wasi-common/src/sys/windows/hostcalls_impl/misc.rs +++ b/crates/wasi-common/src/sys/windows/hostcalls_impl/misc.rs @@ -53,25 +53,25 @@ pub(crate) fn clock_res_get(clock_id: wasi::__wasi_clockid_t) -> Result 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 { 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 diff --git a/crates/wasi-common/src/wasi.rs b/crates/wasi-common/src/wasi.rs index 21e230fcfd..5bcc4d3b27 100644 --- a/crates/wasi-common/src/wasi.rs +++ b/crates/wasi-common/src/wasi.rs @@ -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), diff --git a/crates/wasi-common/src/wasi32.rs b/crates/wasi-common/src/wasi32.rs index 2d194fca07..974ba53b74 100644 --- a/crates/wasi-common/src/wasi32.rs +++ b/crates/wasi-common/src/wasi32.rs @@ -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) ) diff --git a/crates/wasi-common/wasi-common-cbindgen/Cargo.toml b/crates/wasi-common/wasi-common-cbindgen/Cargo.toml index f9ede49db7..dadae6a915 100644 --- a/crates/wasi-common/wasi-common-cbindgen/Cargo.toml +++ b/crates/wasi-common/wasi-common-cbindgen/Cargo.toml @@ -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" diff --git a/crates/wasi-common/wasi-common-cbindgen/src/lib.rs b/crates/wasi-common/wasi-common-cbindgen/src/lib.rs index dcb7f0730d..66530b99a8 100644 --- a/crates/wasi-common/wasi-common-cbindgen/src/lib.rs +++ b/crates/wasi-common/wasi-common-cbindgen/src/lib.rs @@ -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, + Vec, + Vec, +) { 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() +} diff --git a/crates/wasi-common/wig/Cargo.toml b/crates/wasi-common/wig/Cargo.toml index 046ceb6063..c5a537d815 100644 --- a/crates/wasi-common/wig/Cargo.toml +++ b/crates/wasi-common/wig/Cargo.toml @@ -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. diff --git a/crates/wasi-common/wig/src/raw_types.rs b/crates/wasi-common/wig/src/raw_types.rs index 8068054794..c03b03471e 100644 --- a/crates/wasi-common/wig/src/raw_types.rs +++ b/crates/wasi-common/wig/src/raw_types.rs @@ -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, } } diff --git a/crates/wasi/js-polyfill/README.md b/crates/wasi/js-polyfill/README.md deleted file mode 100644 index 12e3fdc171..0000000000 --- a/crates/wasi/js-polyfill/README.md +++ /dev/null @@ -1,2 +0,0 @@ -For the current polyfill sources, please checkout this same directory in -[the polyfill branch](https://github.com/bytecodealliance/wasmtime/tree/polyfill/). diff --git a/crates/wasi/js-polyfill/WASI-small.png b/crates/wasi/js-polyfill/WASI-small.png deleted file mode 100644 index ef55a0bf6d..0000000000 Binary files a/crates/wasi/js-polyfill/WASI-small.png and /dev/null differ diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs index 3820b49ca3..b8452d3b64 100644 --- a/crates/wasi/src/lib.rs +++ b/crates/wasi/src/lib.rs @@ -1,6 +1,7 @@ #![allow(improper_ctypes)] mod instantiate; +pub mod old; mod syscalls; pub use instantiate::{create_wasi_instance, instantiate_wasi, instantiate_wasi_with_context}; diff --git a/crates/wasi/src/old/mod.rs b/crates/wasi/src/old/mod.rs new file mode 100644 index 0000000000..5d4d33030a --- /dev/null +++ b/crates/wasi/src/old/mod.rs @@ -0,0 +1 @@ +pub mod snapshot_0; diff --git a/crates/wasi/src/old/snapshot_0/instantiate.rs b/crates/wasi/src/old/snapshot_0/instantiate.rs new file mode 100644 index 0000000000..4a7c5b6fc6 --- /dev/null +++ b/crates/wasi/src/old/snapshot_0/instantiate.rs @@ -0,0 +1,156 @@ +use super::syscalls; +use alloc::rc::Rc; +use core::cell::RefCell; +use cranelift_codegen::ir::types; +use cranelift_codegen::{ir, isa}; +use cranelift_entity::PrimaryMap; +use cranelift_wasm::DefinedFuncIndex; +use std::collections::HashMap; +use std::fs::File; +use target_lexicon::HOST; +use wasi_common::old::snapshot_0::{WasiCtx, WasiCtxBuilder}; +use wasmtime_api as api; +use wasmtime_environ::{translate_signature, Export, Module}; +use wasmtime_runtime::{Imports, InstanceHandle, InstantiationError, VMFunctionBody}; + +/// Creates `api::Instance` object implementing the "wasi" interface. +pub fn create_wasi_instance( + store: &api::HostRef, + preopened_dirs: &[(String, File)], + argv: &[String], + environ: &[(String, String)], +) -> Result { + let global_exports = store.borrow().global_exports().clone(); + let wasi = instantiate_wasi(global_exports, preopened_dirs, argv, environ)?; + let instance = api::Instance::from_handle(&store, wasi); + Ok(instance) +} + +/// Return an instance implementing the "wasi" interface. +pub fn instantiate_wasi( + global_exports: Rc>>>, + preopened_dirs: &[(String, File)], + argv: &[String], + environ: &[(String, String)], +) -> Result { + let mut wasi_ctx_builder = WasiCtxBuilder::new() + .inherit_stdio() + .args(argv) + .envs(environ); + + for (dir, f) in preopened_dirs { + wasi_ctx_builder = wasi_ctx_builder.preopened_dir( + f.try_clone().map_err(|err| { + InstantiationError::Resource(format!( + "couldn't clone an instance handle to pre-opened dir: {}", + err + )) + })?, + dir, + ); + } + + let wasi_ctx = wasi_ctx_builder.build().map_err(|err| { + InstantiationError::Resource(format!("couldn't assemble WASI context object: {}", err)) + })?; + instantiate_wasi_with_context(global_exports, wasi_ctx) +} + +/// Return an instance implementing the "wasi" interface. +/// +/// The wasi context is configured by +pub fn instantiate_wasi_with_context( + global_exports: Rc>>>, + wasi_ctx: WasiCtx, +) -> Result { + let pointer_type = types::Type::triple_pointer_type(&HOST); + let mut module = Module::new(); + let mut finished_functions: PrimaryMap = + PrimaryMap::new(); + let call_conv = isa::CallConv::triple_default(&HOST); + + macro_rules! signature { + ($name:ident) => {{ + let sig = module.signatures.push(translate_signature( + ir::Signature { + params: syscalls::$name::params() + .into_iter() + .map(ir::AbiParam::new) + .collect(), + returns: syscalls::$name::results() + .into_iter() + .map(ir::AbiParam::new) + .collect(), + call_conv, + }, + pointer_type, + )); + let func = module.functions.push(sig); + module + .exports + .insert(stringify!($name).to_owned(), Export::Function(func)); + finished_functions.push(syscalls::$name::SHIM as *const VMFunctionBody); + }}; + } + + signature!(args_get); + signature!(args_sizes_get); + signature!(clock_res_get); + signature!(clock_time_get); + signature!(environ_get); + signature!(environ_sizes_get); + signature!(fd_prestat_get); + signature!(fd_prestat_dir_name); + signature!(fd_close); + signature!(fd_datasync); + signature!(fd_pread); + signature!(fd_pwrite); + signature!(fd_read); + signature!(fd_renumber); + signature!(fd_seek); + signature!(fd_tell); + signature!(fd_fdstat_get); + signature!(fd_fdstat_set_flags); + signature!(fd_fdstat_set_rights); + signature!(fd_sync); + signature!(fd_write); + signature!(fd_advise); + signature!(fd_allocate); + signature!(path_create_directory); + signature!(path_link); + signature!(path_open); + signature!(fd_readdir); + signature!(path_readlink); + signature!(path_rename); + signature!(fd_filestat_get); + signature!(fd_filestat_set_times); + signature!(fd_filestat_set_size); + signature!(path_filestat_get); + signature!(path_filestat_set_times); + signature!(path_symlink); + signature!(path_unlink_file); + signature!(path_remove_directory); + signature!(poll_oneoff); + signature!(proc_exit); + signature!(proc_raise); + signature!(random_get); + signature!(sched_yield); + signature!(sock_recv); + signature!(sock_send); + signature!(sock_shutdown); + + let imports = Imports::none(); + let data_initializers = Vec::new(); + let signatures = PrimaryMap::new(); + + InstanceHandle::new( + Rc::new(module), + global_exports, + finished_functions.into_boxed_slice(), + imports, + &data_initializers, + signatures.into_boxed_slice(), + None, + Box::new(wasi_ctx), + ) +} diff --git a/crates/wasi/src/old/snapshot_0/mod.rs b/crates/wasi/src/old/snapshot_0/mod.rs new file mode 100644 index 0000000000..ef699ce0b6 --- /dev/null +++ b/crates/wasi/src/old/snapshot_0/mod.rs @@ -0,0 +1,15 @@ +#![allow(improper_ctypes)] + +extern crate alloc; + +mod instantiate; +mod syscalls; + +pub use instantiate::{create_wasi_instance, instantiate_wasi, instantiate_wasi_with_context}; + +pub fn is_wasi_module(name: &str) -> bool { + // FIXME: this should be more conservative, but while WASI is in flux and + // we're figuring out how to support multiple revisions, this should do the + // trick. + name.starts_with("wasi") +} diff --git a/crates/wasi/src/old/snapshot_0/syscalls.rs b/crates/wasi/src/old/snapshot_0/syscalls.rs new file mode 100644 index 0000000000..d62b46a78e --- /dev/null +++ b/crates/wasi/src/old/snapshot_0/syscalls.rs @@ -0,0 +1,977 @@ +use cranelift_codegen::ir::types::{Type, I32, I64}; +use log::{error, trace}; +use wasi_common::old::snapshot_0::{hostcalls, wasi, wasi32, WasiCtx}; +use wasmtime_runtime::{Export, VMContext}; + +pub trait AbiRet { + type Abi; + fn convert(self) -> Self::Abi; + fn codegen_tys() -> Vec; +} + +pub trait AbiParam { + type Abi; + fn convert(arg: Self::Abi) -> Self; + fn codegen_ty() -> Type; +} + +macro_rules! cast32 { + ($($i:ident)*) => ($( + impl AbiRet for $i { + type Abi = i32; + + fn convert(self) -> Self::Abi { + self as i32 + } + + fn codegen_tys() -> Vec { vec![I32] } + } + + impl AbiParam for $i { + type Abi = i32; + + fn convert(param: i32) -> Self { + param as $i + } + + fn codegen_ty() -> Type { I32 } + } + )*) +} + +macro_rules! cast64 { + ($($i:ident)*) => ($( + impl AbiRet for $i { + type Abi = i64; + + fn convert(self) -> Self::Abi { + self as i64 + } + + fn codegen_tys() -> Vec { vec![I64] } + } + + impl AbiParam for $i { + type Abi = i64; + + fn convert(param: i64) -> Self { + param as $i + } + + fn codegen_ty() -> Type { I64 } + } + )*) +} + +cast32!(i8 i16 i32 u8 u16 u32); +cast64!(i64 u64); + +impl AbiRet for () { + type Abi = (); + fn convert(self) {} + fn codegen_tys() -> Vec { + Vec::new() + } +} + +fn get_wasi_ctx(vmctx: &mut VMContext) -> Result<&mut WasiCtx, wasi::__wasi_errno_t> { + unsafe { + vmctx + .host_state() + .downcast_mut::() + .ok_or_else(|| panic!("no host state named WasiCtx available")) + } +} + +fn get_memory(vmctx: &mut VMContext) -> Result<&mut [u8], wasi::__wasi_errno_t> { + unsafe { + match vmctx.lookup_global_export("memory") { + Some(Export::Memory { + definition, + vmctx: _, + memory: _, + }) => Ok(std::slice::from_raw_parts_mut( + (*definition).base, + (*definition).current_length, + )), + x => { + error!( + "no export named \"memory\", or the export isn't a mem: {:?}", + x + ); + Err(wasi::__WASI_ERRNO_INVAL) + } + } + } +} + +macro_rules! ok_or_errno { + ($expr:expr) => { + match $expr { + Ok(v) => v, + Err(e) => { + trace!(" -> errno={}", wasi::strerror(e)); + return e; + } + } + }; +} + +macro_rules! syscalls { + ($(pub unsafe extern "C" fn $name:ident($ctx:ident: *mut VMContext $(, $arg:ident: $ty:ty)*,) -> $ret:ty { + $($body:tt)* + })*) => ($( + pub mod $name { + use super::*; + + /// Returns the codegen types of all the parameters to the shim + /// generated + pub fn params() -> Vec { + vec![$(<$ty as AbiParam>::codegen_ty()),*] + } + + /// Returns the codegen types of all the results of the shim + /// generated + pub fn results() -> Vec { + <$ret as AbiRet>::codegen_tys() + } + + /// The actual function pointer to the shim for a syscall. + /// + /// NB: ideally we'd expose `shim` below, but it seems like there's + /// a compiler bug which prvents that from being cast to a `usize`. + pub static SHIM: unsafe extern "C" fn( + *mut VMContext, + $(<$ty as AbiParam>::Abi),* + ) -> <$ret as AbiRet>::Abi = shim; + + unsafe extern "C" fn shim( + $ctx: *mut VMContext, + $($arg: <$ty as AbiParam>::Abi,)* + ) -> <$ret as AbiRet>::Abi { + let r = super::$name($ctx, $(<$ty as AbiParam>::convert($arg),)*); + <$ret as AbiRet>::convert(r) + } + } + + pub unsafe extern "C" fn $name($ctx: *mut VMContext, $($arg: $ty,)*) -> $ret { + $($body)* + } + )*) +} + +syscalls! { + pub unsafe extern "C" fn args_get( + vmctx: *mut VMContext, + argv: wasi32::uintptr_t, + argv_buf: wasi32::uintptr_t, + ) -> wasi::__wasi_errno_t { + trace!( + "args_get(argv={:#x?}, argv_buf={:#x?})", + argv, + argv_buf, + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::args_get(wasi_ctx, memory, argv, argv_buf) + } + + pub unsafe extern "C" fn args_sizes_get( + vmctx: *mut VMContext, + argc: wasi32::uintptr_t, + argv_buf_size: wasi32::uintptr_t, + ) -> wasi::__wasi_errno_t { + trace!( + "args_sizes_get(argc={:#x?}, argv_buf_size={:#x?})", + argc, + argv_buf_size, + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::args_sizes_get(wasi_ctx, memory, argc, argv_buf_size) + } + + pub unsafe extern "C" fn clock_res_get( + vmctx: *mut VMContext, + clock_id: wasi::__wasi_clockid_t, + resolution: wasi32::uintptr_t, + ) -> wasi::__wasi_errno_t { + trace!( + "clock_res_get(clock_id={:?}, resolution={:#x?})", + clock_id, + resolution, + ); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::clock_res_get(memory, clock_id, resolution) + } + + pub unsafe extern "C" fn clock_time_get( + vmctx: *mut VMContext, + clock_id: wasi::__wasi_clockid_t, + precision: wasi::__wasi_timestamp_t, + time: wasi32::uintptr_t, + ) -> wasi::__wasi_errno_t { + trace!( + "clock_time_get(clock_id={:?}, precision={:?}, time={:#x?})", + clock_id, + precision, + time, + ); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::clock_time_get(memory, clock_id, precision, time) + } + + pub unsafe extern "C" fn environ_get( + vmctx: *mut VMContext, + environ: wasi32::uintptr_t, + environ_buf: wasi32::uintptr_t, + ) -> wasi::__wasi_errno_t { + trace!( + "environ_get(environ={:#x?}, environ_buf={:#x?})", + environ, + environ_buf, + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::environ_get(wasi_ctx, memory, environ, environ_buf) + } + + pub unsafe extern "C" fn environ_sizes_get( + vmctx: *mut VMContext, + environ_count: wasi32::uintptr_t, + environ_buf_size: wasi32::uintptr_t, + ) -> wasi::__wasi_errno_t { + trace!( + "environ_sizes_get(environ_count={:#x?}, environ_buf_size={:#x?})", + environ_count, + environ_buf_size, + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::environ_sizes_get(wasi_ctx, memory, environ_count, environ_buf_size) + } + + pub unsafe extern "C" fn fd_prestat_get( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + buf: wasi32::uintptr_t, + ) -> wasi::__wasi_errno_t { + trace!("fd_prestat_get(fd={:?}, buf={:#x?})", fd, buf); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::fd_prestat_get(wasi_ctx, memory, fd, buf) + } + + pub unsafe extern "C" fn fd_prestat_dir_name( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + path: wasi32::uintptr_t, + path_len: wasi32::size_t, + ) -> wasi::__wasi_errno_t { + trace!("fd_prestat_dir_name(fd={:?}, path={:#x?}, path_len={})", fd, path, path_len); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::fd_prestat_dir_name(wasi_ctx, memory, fd, path, path_len) + } + + pub unsafe extern "C" fn fd_close( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + ) -> wasi::__wasi_errno_t { + trace!("fd_close(fd={:?})", fd); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + hostcalls::fd_close(wasi_ctx, fd) + } + + pub unsafe extern "C" fn fd_datasync( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + ) -> wasi::__wasi_errno_t { + trace!("fd_datasync(fd={:?})", fd); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + hostcalls::fd_datasync(wasi_ctx, fd) + } + + pub unsafe extern "C" fn fd_pread( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + iovs: wasi32::uintptr_t, + iovs_len: wasi32::size_t, + offset: wasi::__wasi_filesize_t, + nread: wasi32::uintptr_t, + ) -> wasi::__wasi_errno_t { + trace!( + "fd_pread(fd={:?}, iovs={:#x?}, iovs_len={:?}, offset={}, nread={:#x?})", + fd, + iovs, + iovs_len, + offset, + nread + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::fd_pread( + wasi_ctx, + memory, + fd, + iovs, + iovs_len, + offset, + nread + ) + } + + pub unsafe extern "C" fn fd_pwrite( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + iovs: wasi32::uintptr_t, + iovs_len: wasi32::size_t, + offset: wasi::__wasi_filesize_t, + nwritten: wasi32::uintptr_t, + ) -> wasi::__wasi_errno_t { + trace!( + "fd_pwrite(fd={:?}, iovs={:#x?}, iovs_len={:?}, offset={}, nwritten={:#x?})", + fd, + iovs, + iovs_len, + offset, + nwritten + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::fd_pwrite( + wasi_ctx, + memory, + fd, + iovs, + iovs_len, + offset, + nwritten + ) + } + + pub unsafe extern "C" fn fd_read( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + iovs: wasi32::uintptr_t, + iovs_len: wasi32::size_t, + nread: wasi32::uintptr_t, + ) -> wasi::__wasi_errno_t { + trace!( + "fd_read(fd={:?}, iovs={:#x?}, iovs_len={:?}, nread={:#x?})", + fd, + iovs, + iovs_len, + nread + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::fd_read(wasi_ctx, memory, fd, iovs, iovs_len, nread) + } + + pub unsafe extern "C" fn fd_renumber( + vmctx: *mut VMContext, + from: wasi::__wasi_fd_t, + to: wasi::__wasi_fd_t, + ) -> wasi::__wasi_errno_t { + trace!("fd_renumber(from={:?}, to={:?})", from, to); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + hostcalls::fd_renumber(wasi_ctx, from, to) + } + + pub unsafe extern "C" fn fd_seek( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + offset: wasi::__wasi_filedelta_t, + whence: wasi::__wasi_whence_t, + newoffset: wasi32::uintptr_t, + ) -> wasi::__wasi_errno_t { + trace!( + "fd_seek(fd={:?}, offset={:?}, whence={}, newoffset={:#x?})", + fd, + offset, + wasi::whence_to_str(whence), + newoffset + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::fd_seek(wasi_ctx, memory, fd, offset, whence, newoffset) + } + + pub unsafe extern "C" fn fd_tell( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + newoffset: wasi32::uintptr_t, + ) -> wasi::__wasi_errno_t { + trace!("fd_tell(fd={:?}, newoffset={:#x?})", fd, newoffset); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::fd_tell(wasi_ctx, memory, fd, newoffset) + } + + pub unsafe extern "C" fn fd_fdstat_get( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + buf: wasi32::uintptr_t, + ) -> wasi::__wasi_errno_t { + trace!("fd_fdstat_get(fd={:?}, buf={:#x?})", fd, buf); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::fd_fdstat_get(wasi_ctx, memory, fd, buf) + } + + pub unsafe extern "C" fn fd_fdstat_set_flags( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + flags: wasi::__wasi_fdflags_t, + ) -> wasi::__wasi_errno_t { + trace!( + "fd_fdstat_set_flags(fd={:?}, flags={:#x?})", + fd, + flags + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + hostcalls::fd_fdstat_set_flags(wasi_ctx, fd, flags) + } + + pub unsafe extern "C" fn fd_fdstat_set_rights( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + fs_rights_base: wasi::__wasi_rights_t, + fs_rights_inheriting: wasi::__wasi_rights_t, + ) -> wasi::__wasi_errno_t { + trace!( + "fd_fdstat_set_rights(fd={:?}, fs_rights_base={:#x?}, fs_rights_inheriting={:#x?})", + fd, + fs_rights_base, + fs_rights_inheriting + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + hostcalls::fd_fdstat_set_rights( + wasi_ctx, + fd, + fs_rights_base, + fs_rights_inheriting + ) + } + + pub unsafe extern "C" fn fd_sync( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + ) -> wasi::__wasi_errno_t { + trace!("fd_sync(fd={:?})", fd); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + hostcalls::fd_sync(wasi_ctx, fd) + } + + pub unsafe extern "C" fn fd_write( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + iovs: wasi32::uintptr_t, + iovs_len: wasi32::size_t, + nwritten: wasi32::uintptr_t, + ) -> wasi::__wasi_errno_t { + trace!( + "fd_write(fd={:?}, iovs={:#x?}, iovs_len={:?}, nwritten={:#x?})", + fd, + iovs, + iovs_len, + nwritten + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::fd_write(wasi_ctx, memory, fd, iovs, iovs_len, nwritten) + } + + pub unsafe extern "C" fn fd_advise( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + offset: wasi::__wasi_filesize_t, + len: wasi::__wasi_filesize_t, + advice: wasi::__wasi_advice_t, + ) -> wasi::__wasi_errno_t { + trace!( + "fd_advise(fd={:?}, offset={}, len={}, advice={:?})", + fd, + offset, + len, + advice + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + hostcalls::fd_advise(wasi_ctx, fd, offset, len, advice) + } + + pub unsafe extern "C" fn fd_allocate( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + offset: wasi::__wasi_filesize_t, + len: wasi::__wasi_filesize_t, + ) -> wasi::__wasi_errno_t { + trace!("fd_allocate(fd={:?}, offset={}, len={})", fd, offset, len); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + hostcalls::fd_allocate(wasi_ctx, fd, offset, len) + } + + pub unsafe extern "C" fn path_create_directory( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + path: wasi32::uintptr_t, + path_len: wasi32::size_t, + ) -> wasi::__wasi_errno_t { + trace!( + "path_create_directory(fd={:?}, path={:#x?}, path_len={})", + fd, + path, + path_len, + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::path_create_directory(wasi_ctx, memory, fd, path, path_len) + } + + pub unsafe extern "C" fn path_link( + vmctx: *mut VMContext, + fd0: wasi::__wasi_fd_t, + flags0: wasi::__wasi_lookupflags_t, + path0: wasi32::uintptr_t, + path_len0: wasi32::size_t, + fd1: wasi::__wasi_fd_t, + path1: wasi32::uintptr_t, + path_len1: wasi32::size_t, + ) -> wasi::__wasi_errno_t { + trace!( + "path_link(fd0={:?}, flags0={:?}, path0={:#x?}, path_len0={}, fd1={:?}, path1={:#x?}, path_len1={})", + fd0, + flags0, + path0, + path_len0, + fd1, + path1, + path_len1 + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::path_link( + wasi_ctx, + memory, + fd0, + flags0, + path0, + path_len0, + fd1, + path1, + path_len1 + ) + } + + // TODO: When multi-value happens, switch to that instead of passing + // the `fd` by reference? + pub unsafe extern "C" fn path_open( + vmctx: *mut VMContext, + dirfd: wasi::__wasi_fd_t, + dirflags: wasi::__wasi_lookupflags_t, + path: 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: wasi32::uintptr_t, + ) -> wasi::__wasi_errno_t { + trace!( + "path_open(dirfd={:?}, dirflags={:?}, path={:#x?}, path_len={:?}, oflags={:#x?}, fs_rights_base={:#x?}, fs_rights_inheriting={:#x?}, fs_flags={:#x?}, fd={:#x?})", + dirfd, + dirflags, + path, + path_len, + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + fd + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::path_open( + wasi_ctx, + memory, + dirfd, + dirflags, + path, + path_len, + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + fd + ) + } + + pub unsafe extern "C" fn fd_readdir( + vmctx: *mut VMContext, + 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 { + trace!( + "fd_readdir(fd={:?}, buf={:#x?}, buf_len={}, cookie={:#x?}, buf_used={:#x?})", + fd, + buf, + buf_len, + cookie, + buf_used, + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::fd_readdir( + wasi_ctx, + memory, + fd, + buf, + buf_len, + cookie, + buf_used + ) + } + + pub unsafe extern "C" fn path_readlink( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + path: wasi32::uintptr_t, + path_len: wasi32::size_t, + buf: wasi32::uintptr_t, + buf_len: wasi32::size_t, + buf_used: wasi32::uintptr_t, + ) -> wasi::__wasi_errno_t { + trace!( + "path_readlink(fd={:?}, path={:#x?}, path_len={:?}, buf={:#x?}, buf_len={}, buf_used={:#x?})", + fd, + path, + path_len, + buf, + buf_len, + buf_used, + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::path_readlink( + wasi_ctx, + memory, + fd, + path, + path_len, + buf, + buf_len, + buf_used + ) + } + + pub unsafe extern "C" fn path_rename( + vmctx: *mut VMContext, + fd0: wasi::__wasi_fd_t, + path0: wasi32::uintptr_t, + path_len0: wasi32::size_t, + fd1: wasi::__wasi_fd_t, + path1: wasi32::uintptr_t, + path_len1: wasi32::size_t, + ) -> wasi::__wasi_errno_t { + trace!( + "path_rename(fd0={:?}, path0={:#x?}, path_len0={:?}, fd1={:?}, path1={:#x?}, path_len1={:?})", + fd0, + path0, + path_len0, + fd1, + path1, + path_len1, + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::path_rename( + wasi_ctx, + memory, + fd0, + path0, + path_len0, + fd1, + path1, + path_len1 + ) + } + + pub unsafe extern "C" fn fd_filestat_get( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + buf: wasi32::uintptr_t, + ) -> wasi::__wasi_errno_t { + trace!("fd_filestat_get(fd={:?}, buf={:#x?})", fd, buf); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::fd_filestat_get(wasi_ctx, memory, fd, buf) + } + + pub unsafe extern "C" fn fd_filestat_set_times( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + st_atim: wasi::__wasi_timestamp_t, + st_mtim: wasi::__wasi_timestamp_t, + fstflags: wasi::__wasi_fstflags_t, + ) -> wasi::__wasi_errno_t { + trace!( + "fd_filestat_set_times(fd={:?}, st_atim={}, st_mtim={}, fstflags={:#x?})", + fd, + st_atim, st_mtim, + fstflags + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + hostcalls::fd_filestat_set_times(wasi_ctx, fd, st_atim, st_mtim, fstflags) + } + + pub unsafe extern "C" fn fd_filestat_set_size( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + size: wasi::__wasi_filesize_t, + ) -> wasi::__wasi_errno_t { + trace!( + "fd_filestat_set_size(fd={:?}, size={})", + fd, + size + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + hostcalls::fd_filestat_set_size(wasi_ctx, fd, size) + } + + pub unsafe extern "C" fn path_filestat_get( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + flags: wasi::__wasi_lookupflags_t, + path: wasi32::uintptr_t, + path_len: wasi32::size_t, + buf: wasi32::uintptr_t, + ) -> wasi::__wasi_errno_t { + trace!( + "path_filestat_get(fd={:?}, flags={:?}, path={:#x?}, path_len={}, buf={:#x?})", + fd, + flags, + path, + path_len, + buf + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::path_filestat_get(wasi_ctx, memory, fd, flags, path, path_len, buf) + } + + pub unsafe extern "C" fn path_filestat_set_times( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + flags: wasi::__wasi_lookupflags_t, + path: wasi32::uintptr_t, + path_len: wasi32::size_t, + st_atim: wasi::__wasi_timestamp_t, + st_mtim: wasi::__wasi_timestamp_t, + fstflags: wasi::__wasi_fstflags_t, + ) -> wasi::__wasi_errno_t { + trace!( + "path_filestat_set_times(fd={:?}, flags={:?}, path={:#x?}, path_len={}, st_atim={}, st_mtim={}, fstflags={:#x?})", + fd, + flags, + path, + path_len, + st_atim, st_mtim, + fstflags + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::path_filestat_set_times( + wasi_ctx, + memory, + fd, + flags, + path, + path_len, + st_atim, + st_mtim, + fstflags + ) + } + + pub unsafe extern "C" fn path_symlink( + vmctx: *mut VMContext, + path0: wasi32::uintptr_t, + path_len0: wasi32::size_t, + fd: wasi::__wasi_fd_t, + path1: wasi32::uintptr_t, + path_len1: wasi32::size_t, + ) -> wasi::__wasi_errno_t { + trace!( + "path_symlink(path0={:#x?}, path_len0={}, fd={:?}, path1={:#x?}, path_len1={})", + path0, + path_len0, + fd, + path1, + path_len1 + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::path_symlink( + wasi_ctx, + memory, + path0, + path_len0, + fd, + path1, + path_len1 + ) + } + + pub unsafe extern "C" fn path_unlink_file( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + path: wasi32::uintptr_t, + path_len: wasi32::size_t, + ) -> wasi::__wasi_errno_t { + trace!( + "path_unlink_file(fd={:?}, path={:#x?}, path_len={})", + fd, + path, + path_len + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::path_unlink_file(wasi_ctx, memory, fd, path, path_len) + } + + pub unsafe extern "C" fn path_remove_directory( + vmctx: *mut VMContext, + fd: wasi::__wasi_fd_t, + path: wasi32::uintptr_t, + path_len: wasi32::size_t, + ) -> wasi::__wasi_errno_t { + trace!( + "path_remove_directory(fd={:?}, path={:#x?}, path_len={})", + fd, + path, + path_len + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::path_remove_directory(wasi_ctx, memory, fd, path, path_len) + } + + pub unsafe extern "C" fn poll_oneoff( + vmctx: *mut VMContext, + in_: wasi32::uintptr_t, + out: wasi32::uintptr_t, + nsubscriptions: wasi32::size_t, + nevents: wasi32::uintptr_t, + ) -> wasi::__wasi_errno_t { + trace!( + "poll_oneoff(in={:#x?}, out={:#x?}, nsubscriptions={}, nevents={:#x?})", + in_, + out, + nsubscriptions, + nevents, + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::poll_oneoff(wasi_ctx, memory, in_, out, nsubscriptions, nevents) + } + + pub unsafe extern "C" fn proc_exit(_vmctx: *mut VMContext, rval: u32,) -> () { + trace!("proc_exit(rval={:?})", rval); + hostcalls::proc_exit(rval) + } + + pub unsafe extern "C" fn proc_raise( + vmctx: *mut VMContext, + sig: wasi::__wasi_signal_t, + ) -> wasi::__wasi_errno_t { + trace!("proc_raise(sig={:?})", sig); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::proc_raise(wasi_ctx, memory, sig) + } + + pub unsafe extern "C" fn random_get( + vmctx: *mut VMContext, + buf: wasi32::uintptr_t, + buf_len: wasi32::size_t, + ) -> wasi::__wasi_errno_t { + trace!("random_get(buf={:#x?}, buf_len={:?})", buf, buf_len); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::random_get(memory, buf, buf_len) + } + + pub unsafe extern "C" fn sched_yield(_vmctx: *mut VMContext,) -> wasi::__wasi_errno_t { + trace!("sched_yield(void)"); + hostcalls::sched_yield() + } + + pub unsafe extern "C" fn sock_recv( + vmctx: *mut VMContext, + 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 { + trace!( + "sock_recv(sock={:?}, ri_data={:#x?}, ri_data_len={}, ri_flags={:#x?}, ro_datalen={:#x?}, ro_flags={:#x?})", + sock, + ri_data, ri_data_len, ri_flags, + ro_datalen, ro_flags + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::sock_recv( + wasi_ctx, + memory, + sock, + ri_data, + ri_data_len, + ri_flags, + ro_datalen, + ro_flags + ) + } + + pub unsafe extern "C" fn sock_send( + vmctx: *mut VMContext, + 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 { + trace!( + "sock_send(sock={:?}, si_data={:#x?}, si_data_len={}, si_flags={:#x?}, so_datalen={:#x?})", + sock, + si_data, si_data_len, si_flags, so_datalen, + ); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::sock_send( + wasi_ctx, + memory, + sock, + si_data, + si_data_len, + si_flags, + so_datalen + ) + } + + pub unsafe extern "C" fn sock_shutdown( + vmctx: *mut VMContext, + sock: wasi::__wasi_fd_t, + how: wasi::__wasi_sdflags_t, + ) -> wasi::__wasi_errno_t { + trace!("sock_shutdown(sock={:?}, how={:?})", sock, how); + let wasi_ctx = ok_or_errno!(get_wasi_ctx(&mut *vmctx)); + let memory = ok_or_errno!(get_memory(&mut *vmctx)); + hostcalls::sock_shutdown(wasi_ctx, memory, sock, how) + } +} diff --git a/crates/wasi/src/syscalls.rs b/crates/wasi/src/syscalls.rs index 5d6737ae9e..e8f80be244 100644 --- a/crates/wasi/src/syscalls.rs +++ b/crates/wasi/src/syscalls.rs @@ -99,7 +99,7 @@ fn get_memory(vmctx: &mut VMContext) -> Result<&mut [u8], wasi::__wasi_errno_t> "no export named \"memory\", or the export isn't a mem: {:?}", x ); - Err(wasi::__WASI_EINVAL) + Err(wasi::__WASI_ERRNO_INVAL) } } } diff --git a/src/bin/wasmtime.rs b/src/bin/wasmtime.rs index fb0836541e..cc36011f98 100644 --- a/src/bin/wasmtime.rs +++ b/src/bin/wasmtime.rs @@ -43,6 +43,7 @@ use wasmtime_environ::{cache_create_new_config, cache_init}; use wasmtime_interface_types::ModuleData; use wasmtime_jit::Features; use wasmtime_wasi::create_wasi_instance; +use wasmtime_wasi::old::snapshot_0::create_wasi_instance as create_wasi_instance_snapshot_0; #[cfg(feature = "wasi-c")] use wasmtime_wasi_c::instantiate_wasi_c; use wasmtime_wast::instantiate_spectest; @@ -80,7 +81,7 @@ Options: --lightbeam use Lightbeam for all compilation --cranelift use Cranelift for all compilation --enable-simd enable proposed SIMD instructions - --wasi-c enable the wasi-c implementation of WASI + --wasi-c enable the wasi-c implementation of `wasi_unstable` --preload= load an additional wasm module before loading the main module --env= pass an environment variable (\"key=value\") to the program --dir= grant access to the given host directory @@ -285,7 +286,7 @@ fn main() -> Result<()> { let argv = compute_argv(&args.arg_file, &args.arg_arg); let environ = compute_environ(&args.flag_env); - let wasi = HostRef::new(if args.flag_wasi_c { + let wasi_unstable = HostRef::new(if args.flag_wasi_c { #[cfg(feature = "wasi-c")] { let global_exports = store.borrow().global_exports().clone(); @@ -297,11 +298,18 @@ fn main() -> Result<()> { bail!("wasi-c feature not enabled at build time") } } else { - create_wasi_instance(&store, &preopen_dirs, &argv, &environ)? + create_wasi_instance_snapshot_0(&store, &preopen_dirs, &argv, &environ)? }); - module_registry.insert("wasi_unstable".to_owned(), wasi.clone()); - module_registry.insert("wasi_unstable_preview0".to_owned(), wasi); + let wasi_snapshot_preview1 = HostRef::new(create_wasi_instance( + &store, + &preopen_dirs, + &argv, + &environ, + )?); + + module_registry.insert("wasi_unstable".to_owned(), wasi_unstable); + module_registry.insert("wasi_snapshot_preview1".to_owned(), wasi_snapshot_preview1); // Load the preload wasm modules. for filename in &args.flag_preload {