summaryrefslogtreecommitdiffstats
path: root/third_party/rust/lucet-wasi-wasmsbx/src/hostcalls
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/lucet-wasi-wasmsbx/src/hostcalls
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/lucet-wasi-wasmsbx/src/hostcalls')
-rw-r--r--third_party/rust/lucet-wasi-wasmsbx/src/hostcalls/fs.rs1358
-rw-r--r--third_party/rust/lucet-wasi-wasmsbx/src/hostcalls/fs_helpers.rs306
-rw-r--r--third_party/rust/lucet-wasi-wasmsbx/src/hostcalls/misc.rs457
-rw-r--r--third_party/rust/lucet-wasi-wasmsbx/src/hostcalls/mod.rs448
-rw-r--r--third_party/rust/lucet-wasi-wasmsbx/src/hostcalls/timers.rs113
5 files changed, 2682 insertions, 0 deletions
diff --git a/third_party/rust/lucet-wasi-wasmsbx/src/hostcalls/fs.rs b/third_party/rust/lucet-wasi-wasmsbx/src/hostcalls/fs.rs
new file mode 100644
index 0000000000..db1a0c5d2d
--- /dev/null
+++ b/third_party/rust/lucet-wasi-wasmsbx/src/hostcalls/fs.rs
@@ -0,0 +1,1358 @@
+#![allow(non_camel_case_types)]
+#![allow(unused_unsafe)]
+
+use crate::ctx::WasiCtx;
+use crate::fdentry::{determine_type_rights, FdEntry};
+use crate::memory::*;
+use crate::{host, wasm32};
+
+use super::fs_helpers::*;
+use super::timers;
+use lucet_runtime::vmctx::Vmctx;
+
+use nix::libc::{self, c_long, c_void, off_t};
+use std::ffi::OsStr;
+use std::mem::MaybeUninit;
+use std::os::unix::prelude::{FromRawFd, OsStrExt};
+
+pub fn wasi_fd_close(vmctx: &mut Vmctx, fd: wasm32::__wasi_fd_t) -> wasm32::__wasi_errno_t {
+ let mut ctx = vmctx.get_embed_ctx_mut::<WasiCtx>();
+ let fd = dec_fd(fd);
+ if let Some(fdent) = ctx.fds.get(&fd) {
+ // can't close preopened files
+ if fdent.preopen_path.is_some() {
+ return wasm32::__WASI_ENOTSUP;
+ }
+ }
+ if let Some(mut fdent) = ctx.fds.remove(&fd) {
+ fdent.fd_object.needs_close = false;
+ match nix::unistd::close(fdent.fd_object.rawfd) {
+ Ok(_) => wasm32::__WASI_ESUCCESS,
+ Err(e) => wasm32::errno_from_nix(e.as_errno().unwrap()),
+ }
+ } else {
+ wasm32::__WASI_EBADF
+ }
+}
+
+pub fn wasi_fd_fdstat_get(
+ vmctx: &mut Vmctx,
+ fd: wasm32::__wasi_fd_t,
+ fdstat_ptr: wasm32::uintptr_t, // *mut wasm32::__wasi_fdstat_t
+) -> wasm32::__wasi_errno_t {
+ let host_fd = dec_fd(fd);
+ let mut host_fdstat = match dec_fdstat_byref(vmctx, fdstat_ptr) {
+ Ok(host_fdstat) => host_fdstat,
+ Err(e) => return enc_errno(e),
+ };
+
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+ let errno = if let Some(fe) = ctx.fds.get(&host_fd) {
+ host_fdstat.fs_filetype = fe.fd_object.ty;
+ host_fdstat.fs_rights_base = fe.rights_base;
+ host_fdstat.fs_rights_inheriting = fe.rights_inheriting;
+ use nix::fcntl::{fcntl, OFlag, F_GETFL};
+ match fcntl(fe.fd_object.rawfd, F_GETFL).map(OFlag::from_bits_truncate) {
+ Ok(flags) => {
+ host_fdstat.fs_flags = host::fdflags_from_nix(flags);
+ wasm32::__WASI_ESUCCESS
+ }
+ Err(e) => wasm32::errno_from_nix(e.as_errno().unwrap()),
+ }
+ } else {
+ wasm32::__WASI_EBADF
+ };
+ enc_fdstat_byref(vmctx, fdstat_ptr, host_fdstat)
+ .expect("can write back into the pointer we read from");
+ errno
+}
+
+pub fn wasi_fd_fdstat_set_flags(
+ vmctx: &mut Vmctx,
+ fd: wasm32::__wasi_fd_t,
+ fdflags: wasm32::__wasi_fdflags_t,
+) -> wasm32::__wasi_errno_t {
+ let host_fd = dec_fd(fd);
+ let host_fdflags = dec_fdflags(fdflags);
+ let nix_flags = host::nix_from_fdflags(host_fdflags);
+
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+ if let Some(fe) = ctx.fds.get(&host_fd) {
+ match nix::fcntl::fcntl(fe.fd_object.rawfd, nix::fcntl::F_SETFL(nix_flags)) {
+ Ok(_) => wasm32::__WASI_ESUCCESS,
+ Err(e) => wasm32::errno_from_nix(e.as_errno().unwrap()),
+ }
+ } else {
+ wasm32::__WASI_EBADF
+ }
+}
+
+pub fn wasi_fd_tell(
+ vmctx: &mut Vmctx,
+ fd: wasm32::__wasi_fd_t,
+ offset: wasm32::uintptr_t,
+) -> wasm32::__wasi_errno_t {
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+ let fd = dec_fd(fd);
+
+ let host_offset = {
+ use nix::unistd::{lseek, Whence};
+
+ let rights = host::__WASI_RIGHT_FD_TELL;
+ match ctx.get_fd_entry(fd, rights.into(), 0) {
+ Ok(fe) => match lseek(fe.fd_object.rawfd, 0, Whence::SeekCur) {
+ Ok(newoffset) => newoffset,
+ Err(e) => return wasm32::errno_from_nix(e.as_errno().unwrap()),
+ },
+ Err(e) => return enc_errno(e),
+ }
+ };
+ enc_filesize_byref(vmctx, offset, host_offset as u64)
+ .map(|_| wasm32::__WASI_ESUCCESS)
+ .unwrap_or_else(|e| e)
+}
+
+pub fn wasi_fd_seek(
+ vmctx: &mut Vmctx,
+ fd: wasm32::__wasi_fd_t,
+ offset: wasm32::__wasi_filedelta_t,
+ whence: wasm32::__wasi_whence_t,
+ newoffset: wasm32::uintptr_t,
+) -> wasm32::__wasi_errno_t {
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+ let fd = dec_fd(fd);
+ let offset = dec_filedelta(offset);
+ let whence = dec_whence(whence);
+
+ let host_newoffset = {
+ use nix::unistd::{lseek, Whence};
+ let nwhence = match u32::from(whence) {
+ host::__WASI_WHENCE_CUR => Whence::SeekCur,
+ host::__WASI_WHENCE_END => Whence::SeekEnd,
+ host::__WASI_WHENCE_SET => Whence::SeekSet,
+ _ => return wasm32::__WASI_EINVAL,
+ };
+
+ let rights = if offset == 0 && whence as u32 == host::__WASI_WHENCE_CUR {
+ host::__WASI_RIGHT_FD_TELL
+ } else {
+ host::__WASI_RIGHT_FD_SEEK | host::__WASI_RIGHT_FD_TELL
+ };
+ match ctx.get_fd_entry(fd, rights.into(), 0) {
+ Ok(fe) => match lseek(fe.fd_object.rawfd, offset, nwhence) {
+ Ok(newoffset) => newoffset,
+ Err(e) => return wasm32::errno_from_nix(e.as_errno().unwrap()),
+ },
+ Err(e) => return enc_errno(e),
+ }
+ };
+ enc_filesize_byref(vmctx, newoffset, host_newoffset as u64)
+ .map(|_| wasm32::__WASI_ESUCCESS)
+ .unwrap_or_else(|e| e)
+}
+
+pub fn wasi_fd_prestat_get(
+ vmctx: &mut Vmctx,
+ fd: wasm32::__wasi_fd_t,
+ prestat_ptr: wasm32::uintptr_t,
+) -> wasm32::__wasi_errno_t {
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+ let fd = dec_fd(fd);
+
+ let rights = host::__WASI_RIGHT_PATH_OPEN;
+ match ctx.get_fd_entry(fd, rights.into(), 0) {
+ Ok(fe) => {
+ if let Some(po_path) = &fe.preopen_path {
+ if fe.fd_object.ty != host::__WASI_FILETYPE_DIRECTORY as host::__wasi_filetype_t {
+ return wasm32::__WASI_ENOTDIR;
+ }
+ enc_prestat_byref(
+ vmctx,
+ prestat_ptr,
+ host::__wasi_prestat_t {
+ pr_type: host::__WASI_PREOPENTYPE_DIR as host::__wasi_preopentype_t,
+ u: host::__wasi_prestat_t___wasi_prestat_u {
+ dir: host::__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t {
+ pr_name_len: po_path.as_os_str().as_bytes().len(),
+ },
+ },
+ },
+ )
+ .map(|_| wasm32::__WASI_ESUCCESS)
+ .unwrap_or_else(|e| e)
+ } else {
+ wasm32::__WASI_ENOTSUP
+ }
+ }
+ Err(e) => enc_errno(e),
+ }
+}
+
+pub fn wasi_fd_prestat_dir_name(
+ vmctx: &mut Vmctx,
+ fd: wasm32::__wasi_fd_t,
+ path_ptr: wasm32::uintptr_t,
+ path_len: wasm32::size_t,
+) -> wasm32::__wasi_errno_t {
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+ let fd = dec_fd(fd);
+ let rights = host::__WASI_RIGHT_PATH_OPEN;
+ match ctx.get_fd_entry(fd, rights.into(), 0) {
+ Ok(fe) => {
+ if let Some(po_path) = &fe.preopen_path {
+ if fe.fd_object.ty != host::__WASI_FILETYPE_DIRECTORY as host::__wasi_filetype_t {
+ return wasm32::__WASI_ENOTDIR;
+ }
+ let path_bytes = po_path.as_os_str().as_bytes();
+ if path_bytes.len() > dec_usize(path_len) {
+ return wasm32::__WASI_ENAMETOOLONG;
+ }
+ enc_slice_of(vmctx, path_bytes, path_ptr)
+ .map(|_| wasm32::__WASI_ESUCCESS)
+ .unwrap_or_else(|e| e)
+ } else {
+ wasm32::__WASI_ENOTSUP
+ }
+ }
+ Err(e) => enc_errno(e),
+ }
+}
+
+pub fn wasi_fd_read(
+ vmctx: &mut Vmctx,
+ fd: wasm32::__wasi_fd_t,
+ iovs_ptr: wasm32::uintptr_t,
+ iovs_len: wasm32::size_t,
+ nread: wasm32::uintptr_t,
+) -> wasm32::__wasi_errno_t {
+ use nix::sys::uio::{readv, IoVec};
+
+ let fd = dec_fd(fd);
+ let mut iovs = match dec_iovec_slice(vmctx, iovs_ptr, iovs_len) {
+ Ok(iovs) => iovs,
+ Err(e) => return enc_errno(e),
+ };
+
+ let mut ctx = vmctx.get_embed_ctx_mut::<WasiCtx>();
+ let rights = host::__WASI_RIGHT_FD_READ;
+ let fe = match ctx.get_fd_entry(fd, rights.into(), 0) {
+ Ok(fe) => fe,
+ Err(e) => return enc_errno(e),
+ };
+
+ let mut iovs: Vec<IoVec<&mut [u8]>> = iovs
+ .iter_mut()
+ .map(|iov| unsafe { host::iovec_to_nix_mut(iov) })
+ .collect();
+
+ let host_nread = match readv(fe.fd_object.rawfd, &mut iovs) {
+ Ok(len) => len,
+ Err(e) => return wasm32::errno_from_nix(e.as_errno().unwrap()),
+ };
+
+ if host_nread == 0 {
+ // we hit eof, so remove the fdentry from the context
+ let mut fe = ctx.fds.remove(&fd).expect("file entry is still there");
+ fe.fd_object.needs_close = false;
+ }
+ enc_usize_byref(vmctx, nread, host_nread)
+ .map(|_| wasm32::__WASI_ESUCCESS)
+ .unwrap_or_else(|e| e)
+}
+
+pub fn wasi_fd_write(
+ vmctx: &mut Vmctx,
+ fd: wasm32::__wasi_fd_t,
+ iovs_ptr: wasm32::uintptr_t,
+ iovs_len: wasm32::size_t,
+ nwritten: wasm32::uintptr_t,
+) -> wasm32::__wasi_errno_t {
+ use nix::sys::uio::{writev, IoVec};
+
+ let fd = dec_fd(fd);
+ let iovs = match dec_ciovec_slice(vmctx, iovs_ptr, iovs_len) {
+ Ok(iovs) => iovs,
+ Err(e) => return enc_errno(e),
+ };
+
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+ let rights = host::__WASI_RIGHT_FD_WRITE;
+ let fe = match ctx.get_fd_entry(fd, rights.into(), 0) {
+ Ok(fe) => fe,
+ Err(e) => return enc_errno(e),
+ };
+
+ let iovs: Vec<IoVec<&[u8]>> = iovs
+ .iter()
+ .map(|iov| unsafe { host::ciovec_to_nix(iov) })
+ .collect();
+
+ let host_nwritten = match writev(fe.fd_object.rawfd, &iovs) {
+ Ok(len) => len,
+ Err(e) => return wasm32::errno_from_nix(e.as_errno().unwrap()),
+ };
+ enc_usize_byref(vmctx, nwritten, host_nwritten)
+ .map(|_| wasm32::__WASI_ESUCCESS)
+ .unwrap_or_else(|e| e)
+}
+
+pub fn wasi_path_open(
+ vmctx: &mut Vmctx,
+ dirfd: wasm32::__wasi_fd_t,
+ dirflags: wasm32::__wasi_lookupflags_t,
+ path_ptr: wasm32::uintptr_t,
+ path_len: wasm32::size_t,
+ oflags: wasm32::__wasi_oflags_t,
+ fs_rights_base: wasm32::__wasi_rights_t,
+ fs_rights_inheriting: wasm32::__wasi_rights_t,
+ fs_flags: wasm32::__wasi_fdflags_t,
+ fd_out_ptr: wasm32::uintptr_t,
+) -> wasm32::__wasi_errno_t {
+ use nix::errno::Errno;
+ use nix::fcntl::{openat, AtFlags, OFlag};
+ use nix::sys::stat::{fstatat, Mode, SFlag};
+
+ let dirfd = dec_fd(dirfd);
+ let dirflags = dec_lookupflags(dirflags);
+ let oflags = dec_oflags(oflags);
+ let fs_rights_base = dec_rights(fs_rights_base);
+ let fs_rights_inheriting = dec_rights(fs_rights_inheriting);
+ let fs_flags = dec_fdflags(fs_flags);
+
+ // which open mode do we need?
+ let read = fs_rights_base
+ & ((host::__WASI_RIGHT_FD_READ | host::__WASI_RIGHT_FD_READDIR) as host::__wasi_rights_t)
+ != 0;
+ let write = fs_rights_base
+ & ((host::__WASI_RIGHT_FD_DATASYNC
+ | host::__WASI_RIGHT_FD_WRITE
+ | host::__WASI_RIGHT_FD_ALLOCATE
+ | host::__WASI_RIGHT_FD_FILESTAT_SET_SIZE) as host::__wasi_rights_t)
+ != 0;
+
+ let mut nix_all_oflags = if read && write {
+ OFlag::O_RDWR
+ } else if read {
+ OFlag::O_RDONLY
+ } else {
+ OFlag::O_WRONLY
+ };
+
+ // on non-Capsicum systems, we always want nofollow
+ nix_all_oflags.insert(OFlag::O_NOFOLLOW);
+
+ // which rights are needed on the dirfd?
+ let mut needed_base = host::__WASI_RIGHT_PATH_OPEN as host::__wasi_rights_t;
+ let mut needed_inheriting = fs_rights_base | fs_rights_inheriting;
+
+ // convert open flags
+ let nix_oflags = host::nix_from_oflags(oflags);
+ nix_all_oflags.insert(nix_oflags);
+ if nix_all_oflags.contains(OFlag::O_CREAT) {
+ needed_base |= host::__WASI_RIGHT_PATH_CREATE_FILE as host::__wasi_rights_t;
+ }
+ if nix_all_oflags.contains(OFlag::O_TRUNC) {
+ needed_base |= host::__WASI_RIGHT_PATH_FILESTAT_SET_SIZE as host::__wasi_rights_t;
+ }
+
+ // convert file descriptor flags
+ nix_all_oflags.insert(host::nix_from_fdflags(fs_flags));
+ if nix_all_oflags.contains(OFlag::O_DSYNC) {
+ needed_inheriting |= host::__WASI_RIGHT_FD_DATASYNC as host::__wasi_rights_t;
+ }
+ if nix_all_oflags.intersects(O_RSYNC | OFlag::O_SYNC) {
+ needed_inheriting |= host::__WASI_RIGHT_FD_SYNC as host::__wasi_rights_t;
+ }
+ if nix_all_oflags.contains(OFlag::O_DIRECTORY) {
+ nix_all_oflags.remove(OFlag::O_RDWR);
+ nix_all_oflags.remove(OFlag::O_WRONLY);
+ nix_all_oflags.insert(OFlag::O_RDONLY);
+ }
+ let path = match dec_slice_of::<u8>(vmctx, path_ptr, path_len) {
+ Ok(path_bytes) => OsStr::from_bytes(path_bytes),
+ Err(e) => return enc_errno(e),
+ };
+
+ let (dir, path) = match path_get(
+ &vmctx,
+ dirfd,
+ dirflags,
+ path,
+ needed_base,
+ needed_inheriting,
+ nix_oflags.contains(OFlag::O_CREAT),
+ ) {
+ Ok((dir, path)) => (dir, path),
+ Err(e) => return enc_errno(e),
+ };
+
+ // Call openat. Use mode 0o666 so that we follow whatever the user's
+ // umask is, but don't set the executable flag, because it isn't yet
+ // meaningful for WASI programs to create executable files.
+ let new_fd = match openat(
+ dir,
+ path.as_os_str(),
+ nix_all_oflags,
+ Mode::from_bits_truncate(0o666),
+ ) {
+ Ok(fd) => fd,
+ Err(e) => {
+ match e.as_errno() {
+ // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket
+ Some(Errno::ENXIO) => {
+ if let Ok(stat) = fstatat(dir, path.as_os_str(), AtFlags::AT_SYMLINK_NOFOLLOW) {
+ if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFSOCK) {
+ return wasm32::__WASI_ENOTSUP;
+ } else {
+ return wasm32::__WASI_ENXIO;
+ }
+ } else {
+ return wasm32::__WASI_ENXIO;
+ }
+ }
+ // Linux returns ENOTDIR instead of ELOOP when using O_NOFOLLOW|O_DIRECTORY
+ // on a symlink.
+ Some(Errno::ENOTDIR)
+ if !(nix_all_oflags & (OFlag::O_NOFOLLOW | OFlag::O_DIRECTORY)).is_empty() =>
+ {
+ if let Ok(stat) = fstatat(dir, path.as_os_str(), AtFlags::AT_SYMLINK_NOFOLLOW) {
+ if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFLNK) {
+ return wasm32::__WASI_ELOOP;
+ }
+ }
+ return wasm32::__WASI_ENOTDIR;
+ }
+ // FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on
+ // a symlink.
+ Some(Errno::EMLINK) if !(nix_all_oflags & OFlag::O_NOFOLLOW).is_empty() => {
+ return wasm32::__WASI_ELOOP;
+ }
+ Some(e) => return wasm32::errno_from_nix(e),
+ None => return wasm32::__WASI_ENOSYS,
+ }
+ }
+ };
+
+ // Determine the type of the new file descriptor and which rights contradict with this type
+ let guest_fd = match unsafe { determine_type_rights(new_fd) } {
+ Err(e) => {
+ // if `close` fails, note it but do not override the underlying errno
+ nix::unistd::close(new_fd).unwrap_or_else(|e| {
+ dbg!(e);
+ });
+ if let Err(e) = enc_fd_byref(vmctx, fd_out_ptr, wasm32::__wasi_fd_t::max_value()) {
+ return enc_errno(e);
+ }
+ return enc_errno(e);
+ }
+ Ok((_ty, max_base, max_inheriting)) => {
+ let mut fe = unsafe { FdEntry::from_raw_fd(new_fd) };
+ fe.rights_base &= max_base;
+ fe.rights_inheriting &= max_inheriting;
+ match vmctx.get_embed_ctx_mut::<WasiCtx>().insert_fd_entry(fe) {
+ Ok(fd) => fd,
+ Err(e) => return enc_errno(e),
+ }
+ }
+ };
+ enc_fd_byref(vmctx, fd_out_ptr, guest_fd)
+ .map(|_| wasm32::__WASI_ESUCCESS)
+ .unwrap_or_else(|e| e)
+}
+
+pub fn wasi_fd_filestat_get(
+ vmctx: &mut Vmctx,
+ fd: wasm32::__wasi_fd_t,
+ filestat_ptr: wasm32::uintptr_t,
+) -> wasm32::__wasi_errno_t {
+ use nix::sys::stat::fstat;
+
+ let host_fd = dec_fd(fd);
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+
+ let rights = host::__WASI_RIGHT_FD_FILESTAT_GET;
+ match ctx.get_fd_entry(host_fd, rights.into(), 0) {
+ Ok(fe) => match fstat(fe.fd_object.rawfd) {
+ Err(e) => return wasm32::errno_from_nix(e.as_errno().unwrap()),
+ Ok(filestat) => {
+ let host_filestat = host::filestat_from_nix(filestat);
+ enc_filestat_byref(vmctx, filestat_ptr, host_filestat)
+ .expect("can write into the pointer");
+ }
+ },
+ Err(e) => return enc_errno(e),
+ }
+ wasm32::__WASI_ESUCCESS
+}
+
+pub fn wasi_path_filestat_get(
+ vmctx: &mut Vmctx,
+ dirfd: wasm32::__wasi_fd_t,
+ dirflags: wasm32::__wasi_lookupflags_t,
+ path_ptr: wasm32::uintptr_t,
+ path_len: wasm32::size_t,
+ filestat_ptr: wasm32::uintptr_t,
+) -> wasm32::__wasi_errno_t {
+ use nix::fcntl::AtFlags;
+ use nix::sys::stat::fstatat;
+
+ let dirfd = dec_fd(dirfd);
+ let dirflags = dec_lookupflags(dirflags);
+ let path = match dec_slice_of::<u8>(vmctx, path_ptr, path_len) {
+ Ok(path_bytes) => OsStr::from_bytes(path_bytes),
+ Err(e) => return enc_errno(e),
+ };
+ let rights = host::__WASI_RIGHT_PATH_FILESTAT_GET;
+ let (dir, path) = match path_get(&vmctx, dirfd, dirflags, path, rights.into(), 0, false) {
+ Ok((dir, path)) => (dir, path),
+ Err(e) => return enc_errno(e),
+ };
+ let atflags = match dirflags {
+ wasm32::__WASI_LOOKUP_SYMLINK_FOLLOW => AtFlags::empty(),
+ _ => AtFlags::AT_SYMLINK_NOFOLLOW,
+ };
+ match fstatat(dir, path.as_os_str(), atflags) {
+ Err(e) => wasm32::errno_from_nix(e.as_errno().unwrap()),
+ Ok(filestat) => {
+ let host_filestat = host::filestat_from_nix(filestat);
+ enc_filestat_byref(vmctx, filestat_ptr, host_filestat)
+ .expect("can write into the pointer");
+ wasm32::__WASI_ESUCCESS
+ }
+ }
+}
+
+pub fn wasi_path_create_directory(
+ vmctx: &mut Vmctx,
+ dirfd: wasm32::__wasi_fd_t,
+ path_ptr: wasm32::uintptr_t,
+ path_len: wasm32::size_t,
+) -> wasm32::__wasi_errno_t {
+ use nix::errno;
+ use nix::libc::mkdirat;
+
+ let dirfd = dec_fd(dirfd);
+ let path = match dec_slice_of::<u8>(vmctx, path_ptr, path_len) {
+ Ok(path_bytes) => OsStr::from_bytes(path_bytes),
+ Err(e) => return enc_errno(e),
+ };
+ let rights = host::__WASI_RIGHT_PATH_CREATE_DIRECTORY;
+ let (dir, path) = match path_get(&vmctx, dirfd, 0, path, rights.into(), 0, false) {
+ Ok((dir, path)) => (dir, path),
+ Err(e) => return enc_errno(e),
+ };
+ let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) {
+ Ok(path_cstr) => path_cstr,
+ Err(_) => return wasm32::__WASI_EINVAL,
+ };
+ // nix doesn't expose mkdirat() yet
+ match unsafe { mkdirat(dir, path_cstr.as_ptr(), 0o777) } {
+ 0 => wasm32::__WASI_ESUCCESS,
+ _ => wasm32::errno_from_nix(errno::Errno::last()),
+ }
+}
+
+pub fn wasi_path_unlink_file(
+ vmctx: &mut Vmctx,
+ dirfd: wasm32::__wasi_fd_t,
+ path_ptr: wasm32::uintptr_t,
+ path_len: wasm32::size_t,
+) -> wasm32::__wasi_errno_t {
+ use nix::errno;
+ use nix::libc::unlinkat;
+
+ let dirfd = dec_fd(dirfd);
+ let path = match dec_slice_of::<u8>(vmctx, path_ptr, path_len) {
+ Ok(path_bytes) => OsStr::from_bytes(path_bytes),
+ Err(e) => return enc_errno(e),
+ };
+ let rights = host::__WASI_RIGHT_PATH_UNLINK_FILE;
+ let (dir, path) = match path_get(&vmctx, dirfd, 0, path, rights.into(), 0, false) {
+ Ok((dir, path)) => (dir, path),
+ Err(e) => return enc_errno(e),
+ };
+ let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) {
+ Ok(path_cstr) => path_cstr,
+ Err(_) => return wasm32::__WASI_EINVAL,
+ };
+ // nix doesn't expose unlinkat() yet
+ match unsafe { unlinkat(dir, path_cstr.as_ptr(), 0) } {
+ 0 => wasm32::__WASI_ESUCCESS,
+ _ => {
+ let mut e = errno::Errno::last();
+ // Non-Linux implementations may return EPERM when attempting to remove a
+ // directory without `REMOVEDIR`. For WASI, adjust this to `EISDIR`.
+ #[cfg(not(linux))]
+ {
+ use nix::fcntl::AtFlags;
+ use nix::sys::stat::{fstatat, SFlag};
+ if e == errno::Errno::EPERM {
+ if let Ok(stat) = fstatat(dir, path.as_os_str(), AtFlags::AT_SYMLINK_NOFOLLOW) {
+ if SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFDIR) {
+ e = errno::Errno::EISDIR;
+ }
+ } else {
+ e = errno::Errno::last();
+ }
+ }
+ }
+ wasm32::errno_from_nix(e)
+ }
+ }
+}
+
+pub fn wasi_fd_allocate(
+ vmctx: &mut Vmctx,
+ fd: wasm32::__wasi_fd_t,
+ offset: wasm32::__wasi_filesize_t,
+ len: wasm32::__wasi_filesize_t,
+) -> wasm32::__wasi_errno_t {
+ let host_fd = dec_fd(fd);
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+ let rights = host::__WASI_RIGHT_FD_ALLOCATE;
+ let fe = match ctx.get_fd_entry(host_fd, rights.into(), 0) {
+ Ok(fe) => fe,
+ Err(e) => return enc_errno(e),
+ };
+ let offset = dec_filesize(offset);
+ let len = dec_filesize(len);
+
+ #[cfg(target_os = "linux")]
+ {
+ let res =
+ unsafe { libc::posix_fallocate(fe.fd_object.rawfd, offset as off_t, len as off_t) };
+ if res != 0 {
+ return wasm32::errno_from_nix(nix::errno::Errno::last());
+ }
+ }
+
+ #[cfg(not(target_os = "linux"))]
+ {
+ use nix::sys::stat::fstat;
+ use nix::unistd::ftruncate;
+
+ match fstat(fe.fd_object.rawfd) {
+ Err(e) => return wasm32::errno_from_nix(e.as_errno().unwrap()),
+ Ok(st) => {
+ let current_size = st.st_size as u64;
+ let wanted_size = match offset.checked_add(len) {
+ Some(wanted_size) => wanted_size,
+ None => return wasm32::__WASI_E2BIG,
+ };
+ if wanted_size > i64::max_value() as u64 {
+ return wasm32::__WASI_E2BIG;
+ }
+ if wanted_size > current_size {
+ if let Err(e) = ftruncate(fe.fd_object.rawfd, wanted_size as off_t) {
+ return wasm32::errno_from_nix(e.as_errno().unwrap());
+ }
+ }
+ }
+ }
+ }
+
+ wasm32::__WASI_ESUCCESS
+}
+
+pub fn wasi_fd_advise(
+ vmctx: &mut Vmctx,
+ fd: wasm32::__wasi_fd_t,
+ offset: wasm32::__wasi_filesize_t,
+ len: wasm32::__wasi_filesize_t,
+ advice: wasm32::__wasi_advice_t,
+) -> wasm32::__wasi_errno_t {
+ let host_fd = dec_fd(fd);
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+ let rights = host::__WASI_RIGHT_FD_ADVISE;
+ let fe = match ctx.get_fd_entry(host_fd, rights.into(), 0) {
+ Ok(fe) => fe,
+ Err(e) => return enc_errno(e),
+ };
+ let advice = dec_advice(advice);
+
+ #[cfg(target_os = "linux")]
+ {
+ let host_advice = match advice as u32 {
+ host::__WASI_ADVICE_DONTNEED => libc::POSIX_FADV_DONTNEED,
+ host::__WASI_ADVICE_SEQUENTIAL => libc::POSIX_FADV_SEQUENTIAL,
+ host::__WASI_ADVICE_WILLNEED => libc::POSIX_FADV_DONTNEED,
+ host::__WASI_ADVICE_NOREUSE => libc::POSIX_FADV_NOREUSE,
+ host::__WASI_ADVICE_RANDOM => libc::POSIX_FADV_RANDOM,
+ host::__WASI_ADVICE_NORMAL => libc::POSIX_FADV_NORMAL,
+ _ => return wasm32::__WASI_EINVAL,
+ };
+ let offset = dec_filesize(offset);
+ let len = dec_filesize(len);
+ let res = unsafe {
+ libc::posix_fadvise(
+ fe.fd_object.rawfd,
+ offset as off_t,
+ len as off_t,
+ host_advice,
+ )
+ };
+ if res != 0 {
+ return wasm32::errno_from_nix(nix::errno::Errno::last());
+ }
+ }
+
+ #[cfg(not(target_os = "linux"))]
+ {
+ let _ = (fe, offset, len);
+ match advice as u32 {
+ host::__WASI_ADVICE_DONTNEED
+ | host::__WASI_ADVICE_SEQUENTIAL
+ | host::__WASI_ADVICE_WILLNEED
+ | host::__WASI_ADVICE_NOREUSE
+ | host::__WASI_ADVICE_RANDOM
+ | host::__WASI_ADVICE_NORMAL => {}
+ _ => return wasm32::__WASI_EINVAL,
+ }
+ }
+
+ wasm32::__WASI_ESUCCESS
+}
+
+pub fn wasi_fd_datasync(vmctx: &mut Vmctx, fd: wasm32::__wasi_fd_t) -> wasm32::__wasi_errno_t {
+ let host_fd = dec_fd(fd);
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+ let rights = host::__WASI_RIGHT_FD_DATASYNC;
+ let fe = match ctx.get_fd_entry(host_fd, rights.into(), 0) {
+ Ok(fe) => fe,
+ Err(e) => return enc_errno(e),
+ };
+ let res;
+
+ #[cfg(target_os = "linux")]
+ {
+ res = nix::unistd::fdatasync(fe.fd_object.rawfd);
+ }
+
+ #[cfg(not(target_os = "linux"))]
+ {
+ res = nix::unistd::fsync(fe.fd_object.rawfd);
+ }
+
+ if let Err(e) = res {
+ return wasm32::errno_from_nix(e.as_errno().unwrap());
+ }
+ wasm32::__WASI_ESUCCESS
+}
+
+pub fn wasi_fd_sync(vmctx: &mut Vmctx, fd: wasm32::__wasi_fd_t) -> wasm32::__wasi_errno_t {
+ let host_fd = dec_fd(fd);
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+ let rights = host::__WASI_RIGHT_FD_SYNC;
+ let fe = match ctx.get_fd_entry(host_fd, rights.into(), 0) {
+ Ok(fe) => fe,
+ Err(e) => return enc_errno(e),
+ };
+ let res = nix::unistd::fsync(fe.fd_object.rawfd);
+ if let Err(e) = res {
+ return wasm32::errno_from_nix(e.as_errno().unwrap());
+ }
+ wasm32::__WASI_ESUCCESS
+}
+
+pub fn wasi_fd_fdstat_set_rights(
+ vmctx: &mut Vmctx,
+ fd: wasm32::__wasi_fd_t,
+ fs_rights_base: wasm32::__wasi_rights_t,
+ fs_rights_inheriting: wasm32::__wasi_rights_t,
+) -> wasm32::__wasi_errno_t {
+ let host_fd = dec_fd(fd);
+ let mut ctx = vmctx.get_embed_ctx_mut::<WasiCtx>();
+ let fe = match ctx.fds.get_mut(&host_fd) {
+ Some(fe) => fe,
+ None => return wasm32::__WASI_EBADF,
+ };
+ if fe.rights_base & fs_rights_base != fs_rights_base
+ || fe.rights_inheriting & fs_rights_inheriting != fs_rights_inheriting
+ {
+ return wasm32::__WASI_ENOTCAPABLE;
+ }
+ fe.rights_base = fs_rights_base;
+ fe.rights_inheriting = fs_rights_inheriting;
+ wasm32::__WASI_ESUCCESS
+}
+
+pub fn wasi_fd_filestat_set_size(
+ vmctx: &mut Vmctx,
+ fd: wasm32::__wasi_fd_t,
+ st_size: wasm32::__wasi_filesize_t,
+) -> wasm32::__wasi_errno_t {
+ use nix::unistd::ftruncate;
+
+ let host_fd = dec_fd(fd);
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+ let rights = host::__WASI_RIGHT_FD_FILESTAT_SET_SIZE;
+ let fe = match ctx.get_fd_entry(host_fd, rights.into(), 0) {
+ Ok(fe) => fe,
+ Err(e) => return enc_errno(e),
+ };
+ let st_size = dec_filesize(st_size);
+ if st_size > i64::max_value() as u64 {
+ return wasm32::__WASI_E2BIG;
+ }
+ if let Err(e) = ftruncate(fe.fd_object.rawfd, st_size as off_t) {
+ return wasm32::errno_from_nix(e.as_errno().unwrap());
+ }
+ wasm32::__WASI_ESUCCESS
+}
+
+pub fn wasi_fd_filestat_set_times(
+ vmctx: &mut Vmctx,
+ fd: wasm32::__wasi_fd_t,
+ st_atim: wasm32::__wasi_timestamp_t,
+ st_mtim: wasm32::__wasi_timestamp_t,
+ fst_flags: wasm32::__wasi_fstflags_t,
+) -> wasm32::__wasi_errno_t {
+ use nix::sys::time::{TimeSpec, TimeValLike};
+
+ let host_fd = dec_fd(fd);
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+ let rights = host::__WASI_RIGHT_FD_FILESTAT_SET_TIMES;
+ let fe = match ctx.get_fd_entry(host_fd, rights.into(), 0) {
+ Ok(fe) => fe,
+ Err(e) => return enc_errno(e),
+ };
+ let st_atim = dec_timestamp(st_atim);
+ let mut st_mtim = dec_timestamp(st_mtim);
+ let fst_flags = dec_fstflags(fst_flags);
+ if fst_flags & (host::__WASI_FILESTAT_SET_MTIM_NOW as host::__wasi_fstflags_t) != 0 {
+ let clock_id = libc::CLOCK_REALTIME;
+ let mut timespec = MaybeUninit::<libc::timespec>::uninit();
+ let res = unsafe { timers::clock_gettime_helper(clock_id, timespec.as_mut_ptr()) };
+ if res != 0 {
+ return wasm32::errno_from_nix(nix::errno::Errno::last());
+ }
+ let timespec = unsafe { timespec.assume_init() };
+ let time_ns = match (timespec.tv_sec as host::__wasi_timestamp_t)
+ .checked_mul(1_000_000_000)
+ .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as host::__wasi_timestamp_t))
+ {
+ Some(time_ns) => time_ns,
+ None => return wasm32::__WASI_EOVERFLOW,
+ };
+ st_mtim = time_ns;
+ }
+ let ts_atime = match fst_flags as u32 {
+ f if f & host::__WASI_FILESTAT_SET_ATIM_NOW != 0 => libc::timespec {
+ tv_sec: 0,
+ tv_nsec: utime_now(),
+ },
+ f if f & host::__WASI_FILESTAT_SET_ATIM != 0 => {
+ *TimeSpec::nanoseconds(st_atim as i64).as_ref()
+ }
+ _ => libc::timespec {
+ tv_sec: 0,
+ tv_nsec: utime_omit(),
+ },
+ };
+ let ts_mtime = *TimeSpec::nanoseconds(st_mtim as i64).as_ref();
+ let times = [ts_atime, ts_mtime];
+ let res = unsafe { timers::futimens_helper(fe.fd_object.rawfd, times.as_ptr()) };
+ if res != 0 {
+ return wasm32::errno_from_nix(nix::errno::Errno::last());
+ }
+ wasm32::__WASI_ESUCCESS
+}
+
+pub fn wasi_path_filestat_set_times(
+ vmctx: &mut Vmctx,
+ dirfd: wasm32::__wasi_fd_t,
+ dirflags: wasm32::__wasi_lookupflags_t,
+ path_ptr: wasm32::uintptr_t,
+ path_len: wasm32::size_t,
+ st_atim: wasm32::__wasi_timestamp_t,
+ st_mtim: wasm32::__wasi_timestamp_t,
+ fst_flags: wasm32::__wasi_fstflags_t,
+) -> wasm32::__wasi_errno_t {
+ use nix::sys::time::{TimeSpec, TimeValLike};
+
+ let dirfd = dec_fd(dirfd);
+ let dirflags = dec_lookupflags(dirflags);
+ let path = match dec_slice_of::<u8>(vmctx, path_ptr, path_len) {
+ Ok(path_bytes) => OsStr::from_bytes(path_bytes),
+ Err(e) => return enc_errno(e),
+ };
+ let rights = host::__WASI_RIGHT_PATH_FILESTAT_SET_TIMES;
+ let (dir, path) = match path_get(&vmctx, dirfd, dirflags, path, rights.into(), 0, false) {
+ Ok((dir, path)) => (dir, path),
+ Err(e) => return enc_errno(e),
+ };
+ let atflags = match dirflags {
+ wasm32::__WASI_LOOKUP_SYMLINK_FOLLOW => 0,
+ _ => libc::AT_SYMLINK_NOFOLLOW,
+ };
+ let st_atim = dec_timestamp(st_atim);
+ let mut st_mtim = dec_timestamp(st_mtim);
+ let fst_flags = dec_fstflags(fst_flags);
+ if fst_flags & (host::__WASI_FILESTAT_SET_MTIM_NOW as host::__wasi_fstflags_t) != 0 {
+ let clock_id = libc::CLOCK_REALTIME;
+ let mut timespec = MaybeUninit::<libc::timespec>::uninit();
+ let res = unsafe { timers::clock_gettime_helper(clock_id, timespec.as_mut_ptr()) };
+ if res != 0 {
+ return wasm32::errno_from_nix(nix::errno::Errno::last());
+ }
+ let timespec = unsafe { timespec.assume_init() };
+ let time_ns = match (timespec.tv_sec as host::__wasi_timestamp_t)
+ .checked_mul(1_000_000_000)
+ .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as host::__wasi_timestamp_t))
+ {
+ Some(time_ns) => time_ns,
+ None => return wasm32::__WASI_EOVERFLOW,
+ };
+ st_mtim = time_ns;
+ }
+ let ts_atime = match fst_flags as u32 {
+ f if f & host::__WASI_FILESTAT_SET_ATIM_NOW != 0 => libc::timespec {
+ tv_sec: 0,
+ tv_nsec: utime_now(),
+ },
+ f if f & host::__WASI_FILESTAT_SET_ATIM != 0 => {
+ *TimeSpec::nanoseconds(st_atim as i64).as_ref()
+ }
+ _ => libc::timespec {
+ tv_sec: 0,
+ tv_nsec: utime_omit(),
+ },
+ };
+ let ts_mtime = *TimeSpec::nanoseconds(st_mtim as i64).as_ref();
+ let times = [ts_atime, ts_mtime];
+ let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) {
+ Ok(path_cstr) => path_cstr,
+ Err(_) => return wasm32::__WASI_EINVAL,
+ };
+ let res = unsafe { timers::utimensat_helper(dir, path_cstr.as_ptr(), times.as_ptr(), atflags) };
+ if res != 0 {
+ return wasm32::errno_from_nix(nix::errno::Errno::last());
+ }
+ wasm32::__WASI_ESUCCESS
+}
+
+pub fn wasi_fd_pread(
+ vmctx: &mut Vmctx,
+ fd: wasm32::__wasi_fd_t,
+ iovs_ptr: wasm32::uintptr_t,
+ iovs_len: wasm32::size_t,
+ offset: wasm32::__wasi_filesize_t,
+ nread: wasm32::uintptr_t,
+) -> wasm32::__wasi_errno_t {
+ use nix::sys::uio::pread;
+ use std::cmp;
+
+ let fd = dec_fd(fd);
+ let iovs = match dec_iovec_slice(vmctx, iovs_ptr, iovs_len) {
+ Ok(iovs) => iovs,
+ Err(e) => return enc_errno(e),
+ };
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+ let rights = host::__WASI_RIGHT_FD_READ;
+ let fe = match ctx.get_fd_entry(fd, rights.into(), 0) {
+ Ok(fe) => fe,
+ Err(e) => return enc_errno(e),
+ };
+ let offset = dec_filesize(offset);
+ if offset > i64::max_value() as u64 {
+ return wasm32::__WASI_EIO;
+ }
+ let buf_size = iovs.iter().map(|v| v.buf_len).sum();
+ let mut buf = vec![0; buf_size];
+ let host_nread = match pread(fe.fd_object.rawfd, &mut buf, offset as off_t) {
+ Ok(len) => len,
+ Err(e) => return wasm32::errno_from_nix(e.as_errno().unwrap()),
+ };
+ let mut buf_offset = 0;
+ let mut left = host_nread;
+ for iov in &iovs {
+ if left == 0 {
+ break;
+ }
+ let vec_len = cmp::min(iov.buf_len, left);
+ unsafe { std::slice::from_raw_parts_mut(iov.buf as *mut u8, vec_len) }
+ .copy_from_slice(&buf[buf_offset..buf_offset + vec_len]);
+ buf_offset += vec_len;
+ left -= vec_len;
+ }
+ enc_usize_byref(vmctx, nread, host_nread)
+ .map(|_| wasm32::__WASI_ESUCCESS)
+ .unwrap_or_else(|e| e)
+}
+
+pub fn wasi_fd_pwrite(
+ vmctx: &mut Vmctx,
+ fd: wasm32::__wasi_fd_t,
+ iovs_ptr: wasm32::uintptr_t,
+ iovs_len: wasm32::size_t,
+ offset: wasm32::__wasi_filesize_t,
+ nwritten: wasm32::uintptr_t,
+) -> wasm32::__wasi_errno_t {
+ use nix::sys::uio::pwrite;
+
+ let fd = dec_fd(fd);
+ let iovs = match dec_iovec_slice(vmctx, iovs_ptr, iovs_len) {
+ Ok(iovs) => iovs,
+ Err(e) => return enc_errno(e),
+ };
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+ let rights = host::__WASI_RIGHT_FD_READ;
+ let fe = match ctx.get_fd_entry(fd, rights.into(), 0) {
+ Ok(fe) => fe,
+ Err(e) => return enc_errno(e),
+ };
+ let offset = dec_filesize(offset);
+ if offset > i64::max_value() as u64 {
+ return wasm32::__WASI_EIO;
+ }
+ let buf_size = iovs.iter().map(|v| v.buf_len).sum();
+ let mut buf = Vec::with_capacity(buf_size);
+ for iov in &iovs {
+ buf.extend_from_slice(unsafe {
+ std::slice::from_raw_parts(iov.buf as *const u8, iov.buf_len)
+ });
+ }
+ let host_nwritten = match pwrite(fe.fd_object.rawfd, &buf, offset as off_t) {
+ Ok(len) => len,
+ Err(e) => return wasm32::errno_from_nix(e.as_errno().unwrap()),
+ };
+ enc_usize_byref(vmctx, nwritten, host_nwritten)
+ .map(|_| wasm32::__WASI_ESUCCESS)
+ .unwrap_or_else(|e| e)
+}
+
+pub fn wasi_fd_readdir(
+ vmctx: &mut Vmctx,
+ fd: wasm32::__wasi_fd_t,
+ buf: wasm32::uintptr_t,
+ buf_len: wasm32::size_t,
+ cookie: wasm32::__wasi_dircookie_t,
+ bufused: wasm32::uintptr_t,
+) -> wasm32::__wasi_errno_t {
+ use libc::{dirent, fdopendir, readdir_r, seekdir};
+
+ match enc_usize_byref(vmctx, bufused, 0) {
+ Ok(_) => {}
+ Err(e) => return enc_errno(e),
+ };
+ let fd = dec_fd(fd);
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+ let rights = host::__WASI_RIGHT_FD_READDIR;
+ let fe = match ctx.get_fd_entry(fd, rights.into(), 0) {
+ Ok(fe) => fe,
+ Err(e) => return enc_errno(e),
+ };
+ let host_buf = match dec_slice_of::<u8>(vmctx, buf, buf_len) {
+ Ok(host_buf) => host_buf,
+ Err(e) => return enc_errno(e),
+ };
+ let host_buf_ptr = host_buf.as_ptr();
+ let host_buf_len = host_buf.len();
+ let dir = unsafe { fdopendir(fe.fd_object.rawfd) };
+ if dir.is_null() {
+ return wasm32::errno_from_nix(nix::errno::Errno::last());
+ }
+ let cookie = dec_dircookie(cookie);
+ if cookie != wasm32::__WASI_DIRCOOKIE_START {
+ unsafe { seekdir(dir, cookie as c_long) };
+ }
+ let mut entry_buf = MaybeUninit::<dirent>::uninit();
+ let mut left = host_buf_len;
+ let mut host_buf_offset: usize = 0;
+ while left > 0 {
+ let mut host_entry: *mut dirent = std::ptr::null_mut();
+ let res = unsafe { readdir_r(dir, entry_buf.as_mut_ptr(), &mut host_entry) };
+ if res == -1 {
+ return wasm32::errno_from_nix(nix::errno::Errno::last());
+ }
+ if host_entry.is_null() {
+ break;
+ }
+ unsafe { entry_buf.assume_init() };
+ let entry: wasm32::__wasi_dirent_t = match dirent_from_host(&unsafe { *host_entry }) {
+ Ok(entry) => entry,
+ Err(e) => return enc_errno(e),
+ };
+ let name_len = entry.d_namlen as usize;
+ let required_space = std::mem::size_of_val(&entry) + name_len;
+ if required_space > left {
+ break;
+ }
+ unsafe {
+ let ptr = host_buf_ptr.offset(host_buf_offset as isize) as *mut c_void
+ as *mut wasm32::__wasi_dirent_t;
+ *ptr = entry;
+ }
+ host_buf_offset += std::mem::size_of_val(&entry);
+ let name_ptr = unsafe { *host_entry }.d_name.as_ptr();
+ unsafe {
+ std::ptr::copy_nonoverlapping(
+ name_ptr as *const _,
+ host_buf_ptr.offset(host_buf_offset as isize) as *mut _,
+ name_len,
+ )
+ };
+ host_buf_offset += name_len;
+ left -= required_space;
+ }
+ let host_bufused = host_buf_len - left;
+ enc_usize_byref(vmctx, bufused, host_bufused)
+ .map(|_| wasm32::__WASI_ESUCCESS)
+ .unwrap_or_else(|e| e)
+}
+
+pub fn wasi_fd_renumber(
+ vmctx: &mut Vmctx,
+ from: wasm32::__wasi_fd_t,
+ to: wasm32::__wasi_fd_t,
+) -> wasm32::__wasi_errno_t {
+ let from = dec_fd(from);
+ let to = dec_fd(to);
+ let mut ctx = vmctx.get_embed_ctx_mut::<WasiCtx>();
+ let fe_from = match ctx.fds.get(&from) {
+ Some(fe_from) => fe_from,
+ None => return wasm32::__WASI_EBADF,
+ };
+ let fe_to = match ctx.fds.get(&to) {
+ Some(fe_to) => fe_to,
+ None => return wasm32::__WASI_EBADF,
+ };
+ if let Err(e) = nix::unistd::dup2(fe_from.fd_object.rawfd, fe_to.fd_object.rawfd) {
+ return wasm32::errno_from_nix(e.as_errno().unwrap());
+ }
+ let fe_from_rawfd = fe_from.fd_object.rawfd;
+ ctx.fds.remove(&(fe_from_rawfd as host::__wasi_fd_t));
+
+ wasm32::__WASI_ESUCCESS
+}
+
+pub fn wasi_path_link(
+ vmctx: &mut Vmctx,
+ old_dirfd: wasm32::__wasi_fd_t,
+ _old_flags: wasm32::__wasi_lookupflags_t,
+ old_path_ptr: wasm32::uintptr_t,
+ old_path_len: wasm32::size_t,
+ new_dirfd: wasm32::__wasi_fd_t,
+ new_path_ptr: wasm32::uintptr_t,
+ new_path_len: wasm32::size_t,
+) -> wasm32::__wasi_errno_t {
+ use nix::libc::linkat;
+
+ let old_dirfd = dec_fd(old_dirfd);
+ let new_dirfd = dec_fd(new_dirfd);
+ let old_path = match dec_slice_of::<u8>(vmctx, old_path_ptr, old_path_len) {
+ Ok(old_path_bytes) => OsStr::from_bytes(old_path_bytes),
+ Err(e) => return enc_errno(e),
+ };
+ let new_path = match dec_slice_of::<u8>(vmctx, new_path_ptr, new_path_len) {
+ Ok(new_path_bytes) => OsStr::from_bytes(new_path_bytes),
+ Err(e) => return enc_errno(e),
+ };
+ let rights = host::__WASI_RIGHT_PATH_LINK_SOURCE;
+ let (old_dir, old_path) =
+ match path_get(&vmctx, old_dirfd, 0, old_path, rights.into(), 0, false) {
+ Ok((dir, path)) => (dir, path),
+ Err(e) => return enc_errno(e),
+ };
+ let rights = host::__WASI_RIGHT_PATH_LINK_TARGET;
+ let (new_dir, new_path) =
+ match path_get(&vmctx, new_dirfd, 0, new_path, rights.into(), 0, false) {
+ Ok((dir, path)) => (dir, path),
+ Err(e) => return enc_errno(e),
+ };
+ let old_path_cstr = match std::ffi::CString::new(old_path.as_bytes()) {
+ Ok(old_path_cstr) => old_path_cstr,
+ Err(_) => return wasm32::__WASI_EINVAL,
+ };
+ let new_path_cstr = match std::ffi::CString::new(new_path.as_bytes()) {
+ Ok(new_path_cstr) => new_path_cstr,
+ Err(_) => return wasm32::__WASI_EINVAL,
+ };
+
+ // Not setting AT_SYMLINK_FOLLOW fails on most filesystems
+ let atflags = libc::AT_SYMLINK_FOLLOW;
+ let res = unsafe {
+ linkat(
+ old_dir,
+ old_path_cstr.as_ptr(),
+ new_dir,
+ new_path_cstr.as_ptr(),
+ atflags,
+ )
+ };
+ if res != 0 {
+ return wasm32::errno_from_nix(nix::errno::Errno::last());
+ }
+ wasm32::__WASI_ESUCCESS
+}
+
+pub fn wasi_path_readlink(
+ vmctx: &mut Vmctx,
+ dirfd: wasm32::__wasi_fd_t,
+ path_ptr: wasm32::uintptr_t,
+ path_len: wasm32::size_t,
+ buf_ptr: wasm32::uintptr_t,
+ buf_len: wasm32::size_t,
+ bufused: wasm32::uintptr_t,
+) -> wasm32::__wasi_errno_t {
+ use nix::fcntl::readlinkat;
+ use std::cmp;
+
+ match enc_usize_byref(vmctx, bufused, 0) {
+ Ok(_) => {}
+ Err(e) => return enc_errno(e),
+ };
+ let dirfd = dec_fd(dirfd);
+ let path = match dec_slice_of::<u8>(vmctx, path_ptr, path_len) {
+ Ok(path_bytes) => OsStr::from_bytes(path_bytes),
+ Err(e) => return enc_errno(e),
+ };
+ let rights = host::__WASI_RIGHT_PATH_READLINK;
+ let (dir, path) = match path_get(&vmctx, dirfd, 0, path, rights.into(), 0, false) {
+ Ok((dir, path)) => (dir, path),
+ Err(e) => return enc_errno(e),
+ };
+ let dummy_buf = &mut [0u8];
+ let mut buf = if buf_len > 0 {
+ match dec_slice_of_mut::<u8>(vmctx, buf_ptr, buf_len) {
+ Ok(buf) => buf,
+ Err(e) => return enc_errno(e),
+ }
+ } else {
+ dummy_buf
+ };
+ let target_path = match readlinkat(dir, path.as_os_str(), &mut buf) {
+ Err(e) => return wasm32::errno_from_nix(e.as_errno().unwrap()),
+ Ok(target_path) => target_path,
+ };
+ let host_bufused = cmp::min(buf_len as usize, target_path.len());
+ match enc_usize_byref(vmctx, bufused, host_bufused) {
+ Ok(_) => {}
+ Err(e) => return enc_errno(e),
+ };
+ wasm32::__WASI_ESUCCESS
+}
+
+pub fn wasi_path_remove_directory(
+ vmctx: &mut Vmctx,
+ dirfd: wasm32::__wasi_fd_t,
+ path_ptr: wasm32::uintptr_t,
+ path_len: wasm32::size_t,
+) -> wasm32::__wasi_errno_t {
+ use nix::errno;
+ use nix::libc::{unlinkat, AT_REMOVEDIR};
+
+ let dirfd = dec_fd(dirfd);
+ let path = match dec_slice_of::<u8>(vmctx, path_ptr, path_len) {
+ Ok(path_bytes) => OsStr::from_bytes(path_bytes),
+ Err(e) => return enc_errno(e),
+ };
+ let rights = host::__WASI_RIGHT_PATH_REMOVE_DIRECTORY;
+ let (dir, path) = match path_get(&vmctx, dirfd, 0, path, rights.into(), 0, false) {
+ Ok((dir, path)) => (dir, path),
+ Err(e) => return enc_errno(e),
+ };
+ let path_cstr = match std::ffi::CString::new(path.as_os_str().as_bytes()) {
+ Ok(path_cstr) => path_cstr,
+ Err(_) => return wasm32::__WASI_EINVAL,
+ };
+ // nix doesn't expose unlinkat() yet
+ match unsafe { unlinkat(dir, path_cstr.as_ptr(), AT_REMOVEDIR) } {
+ 0 => wasm32::__WASI_ESUCCESS,
+ _ => wasm32::errno_from_nix(errno::Errno::last()),
+ }
+}
+
+pub fn wasi_path_rename(
+ vmctx: &mut Vmctx,
+ old_dirfd: wasm32::__wasi_fd_t,
+ old_path_ptr: wasm32::uintptr_t,
+ old_path_len: wasm32::size_t,
+ new_dirfd: wasm32::__wasi_fd_t,
+ new_path_ptr: wasm32::uintptr_t,
+ new_path_len: wasm32::size_t,
+) -> wasm32::__wasi_errno_t {
+ use nix::libc::renameat;
+
+ let old_dirfd = dec_fd(old_dirfd);
+ let new_dirfd = dec_fd(new_dirfd);
+ let old_path = match dec_slice_of::<u8>(vmctx, old_path_ptr, old_path_len) {
+ Ok(old_path_bytes) => OsStr::from_bytes(old_path_bytes),
+ Err(e) => return enc_errno(e),
+ };
+ let new_path = match dec_slice_of::<u8>(vmctx, new_path_ptr, new_path_len) {
+ Ok(new_path_bytes) => OsStr::from_bytes(new_path_bytes),
+ Err(e) => return enc_errno(e),
+ };
+ let rights = host::__WASI_RIGHT_PATH_RENAME_SOURCE;
+ let (old_dir, old_path) =
+ match path_get(&vmctx, old_dirfd, 0, old_path, rights.into(), 0, false) {
+ Ok((dir, path)) => (dir, path),
+ Err(e) => return enc_errno(e),
+ };
+ let rights = host::__WASI_RIGHT_PATH_RENAME_TARGET;
+ let (new_dir, new_path) =
+ match path_get(&vmctx, new_dirfd, 0, new_path, rights.into(), 0, false) {
+ Ok((dir, path)) => (dir, path),
+ Err(e) => return enc_errno(e),
+ };
+ let old_path_cstr = match std::ffi::CString::new(old_path.as_bytes()) {
+ Ok(old_path_cstr) => old_path_cstr,
+ Err(_) => return wasm32::__WASI_EINVAL,
+ };
+ let new_path_cstr = match std::ffi::CString::new(new_path.as_bytes()) {
+ Ok(new_path_cstr) => new_path_cstr,
+ Err(_) => return wasm32::__WASI_EINVAL,
+ };
+ let res = unsafe {
+ renameat(
+ old_dir,
+ old_path_cstr.as_ptr(),
+ new_dir,
+ new_path_cstr.as_ptr(),
+ )
+ };
+ if res != 0 {
+ return wasm32::errno_from_nix(nix::errno::Errno::last());
+ }
+ wasm32::__WASI_ESUCCESS
+}
+
+pub fn wasi_path_symlink(
+ vmctx: &mut Vmctx,
+ old_path_ptr: wasm32::uintptr_t,
+ old_path_len: wasm32::size_t,
+ dirfd: wasm32::__wasi_fd_t,
+ new_path_ptr: wasm32::uintptr_t,
+ new_path_len: wasm32::size_t,
+) -> wasm32::__wasi_errno_t {
+ use nix::libc::symlinkat;
+
+ let dirfd = dec_fd(dirfd);
+ let old_path = match dec_slice_of::<u8>(vmctx, old_path_ptr, old_path_len) {
+ Ok(old_path_bytes) => OsStr::from_bytes(old_path_bytes),
+ Err(e) => return enc_errno(e),
+ };
+ let new_path = match dec_slice_of::<u8>(vmctx, new_path_ptr, new_path_len) {
+ Ok(new_path_bytes) => OsStr::from_bytes(new_path_bytes),
+ Err(e) => return enc_errno(e),
+ };
+ let rights = host::__WASI_RIGHT_PATH_SYMLINK;
+ let (dir, new_path) = match path_get(&vmctx, dirfd, 0, new_path, rights.into(), 0, false) {
+ Ok((dir, path)) => (dir, path),
+ Err(e) => return enc_errno(e),
+ };
+ let old_path_cstr = match std::ffi::CString::new(old_path.as_bytes()) {
+ Ok(old_path_cstr) => old_path_cstr,
+ Err(_) => return wasm32::__WASI_EINVAL,
+ };
+ let new_path_cstr = match std::ffi::CString::new(new_path.as_bytes()) {
+ Ok(new_path_cstr) => new_path_cstr,
+ Err(_) => return wasm32::__WASI_EINVAL,
+ };
+ let res = unsafe { symlinkat(old_path_cstr.as_ptr(), dir, new_path_cstr.as_ptr()) };
+ if res != 0 {
+ return wasm32::errno_from_nix(nix::errno::Errno::last());
+ }
+ wasm32::__WASI_ESUCCESS
+}
diff --git a/third_party/rust/lucet-wasi-wasmsbx/src/hostcalls/fs_helpers.rs b/third_party/rust/lucet-wasi-wasmsbx/src/hostcalls/fs_helpers.rs
new file mode 100644
index 0000000000..a82924e481
--- /dev/null
+++ b/third_party/rust/lucet-wasi-wasmsbx/src/hostcalls/fs_helpers.rs
@@ -0,0 +1,306 @@
+#![allow(non_camel_case_types)]
+#![allow(unused_unsafe)]
+use crate::ctx::WasiCtx;
+use crate::host;
+
+use lucet_runtime::vmctx::Vmctx;
+
+use nix::libc::{self, c_long};
+use std::ffi::{OsStr, OsString};
+use std::os::unix::prelude::{OsStrExt, OsStringExt, RawFd};
+
+#[cfg(target_os = "linux")]
+pub const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_RSYNC;
+
+#[cfg(not(target_os = "linux"))]
+pub const O_RSYNC: nix::fcntl::OFlag = nix::fcntl::OFlag::O_SYNC;
+
+/// 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 fn path_get<P: AsRef<OsStr>>(
+ vmctx: &Vmctx,
+ dirfd: host::__wasi_fd_t,
+ dirflags: host::__wasi_lookupflags_t,
+ path: P,
+ needed_base: host::__wasi_rights_t,
+ needed_inheriting: host::__wasi_rights_t,
+ needs_final_component: bool,
+) -> Result<(RawFd, OsString), host::__wasi_errno_t> {
+ use nix::errno::Errno;
+ use nix::fcntl::{openat, readlinkat, OFlag};
+ use nix::sys::stat::Mode;
+
+ const MAX_SYMLINK_EXPANSIONS: usize = 128;
+
+ /// close all the intermediate file descriptors, but make sure not to drop either the original
+ /// dirfd or the one we return (which may be the same dirfd)
+ fn ret_dir_success(dir_stack: &mut Vec<RawFd>) -> RawFd {
+ let ret_dir = dir_stack.pop().expect("there is always a dirfd to return");
+ if let Some(dirfds) = dir_stack.get(1..) {
+ for dirfd in dirfds {
+ nix::unistd::close(*dirfd).unwrap_or_else(|e| {
+ dbg!(e);
+ });
+ }
+ }
+ ret_dir
+ }
+
+ /// close all file descriptors other than the base directory, and return the errno for
+ /// convenience with `return`
+ fn ret_error(
+ dir_stack: &mut Vec<RawFd>,
+ errno: host::__wasi_errno_t,
+ ) -> Result<(RawFd, OsString), host::__wasi_errno_t> {
+ if let Some(dirfds) = dir_stack.get(1..) {
+ for dirfd in dirfds {
+ nix::unistd::close(*dirfd).unwrap_or_else(|e| {
+ dbg!(e);
+ });
+ }
+ }
+ Err(errno)
+ }
+
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+
+ let dirfe = ctx.get_fd_entry(dirfd, needed_base, needed_inheriting)?;
+
+ // 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![dirfe.fd_object.rawfd];
+
+ // 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.as_ref().to_owned().into_vec()];
+
+ // Track the number of symlinks we've expanded, so we can return `ELOOP` after too many.
+ let mut symlink_expansions = 0;
+
+ // Buffer to read links into; defined outside of the loop so we don't reallocate it constantly.
+ let mut readlink_buf = vec![0u8; libc::PATH_MAX as usize + 1];
+
+ // 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 {
+ let component = if let Some(cur_path) = path_stack.pop() {
+ // eprintln!(
+ // "cur_path = {:?}",
+ // std::str::from_utf8(cur_path.as_slice()).unwrap()
+ // );
+ let mut split = cur_path.splitn(2, |&c| c == b'/');
+ let head = split.next();
+ let tail = split.next();
+ match (head, tail) {
+ (None, _) => {
+ // split always returns at least a singleton iterator with an empty slice
+ panic!("unreachable");
+ }
+ // path is empty
+ (Some([]), None) => {
+ return ret_error(&mut dir_stack, host::__WASI_ENOENT as host::__wasi_errno_t);
+ }
+ // path starts with `/`, is absolute
+ (Some([]), Some(_)) => {
+ return ret_error(
+ &mut dir_stack,
+ host::__WASI_ENOTCAPABLE as host::__wasi_errno_t,
+ );
+ }
+ // the final component of the path with no trailing slash
+ (Some(component), None) => component.to_vec(),
+ (Some(component), Some(rest)) => {
+ if rest.iter().all(|&c| c == b'/') {
+ // the final component of the path with trailing slashes; put one trailing
+ // slash back on
+ let mut component = component.to_vec();
+ component.push('/' as u8);
+ component
+ } else {
+ // non-final component; push the rest back on the stack
+ path_stack.push(rest.to_vec());
+ component.to_vec()
+ }
+ }
+ }
+ } else {
+ // if the path stack is ever empty, we return rather than going through the loop again
+ panic!("unreachable");
+ };
+
+ // eprintln!(
+ // "component = {:?}",
+ // std::str::from_utf8(component.as_slice()).unwrap()
+ // );
+
+ match component.as_slice() {
+ b"." => {
+ // skip component
+ }
+ b".." => {
+ // pop a directory
+ let dirfd = dir_stack.pop().expect("dir_stack is never empty");
+
+ // we're not allowed to pop past the original directory
+ if dir_stack.is_empty() {
+ return ret_error(
+ &mut dir_stack,
+ host::__WASI_ENOTCAPABLE as host::__wasi_errno_t,
+ );
+ } else {
+ nix::unistd::close(dirfd).unwrap_or_else(|e| {
+ dbg!(e);
+ });
+ }
+ }
+ // should the component be a directory? it should if there is more path left to process, or
+ // if it has a trailing slash and `needs_final_component` is not set
+ component
+ if !path_stack.is_empty()
+ || (component.ends_with(b"/") && !needs_final_component) =>
+ {
+ match openat(
+ *dir_stack.last().expect("dir_stack is never empty"),
+ component,
+ OFlag::O_RDONLY | OFlag::O_DIRECTORY | OFlag::O_NOFOLLOW,
+ Mode::empty(),
+ ) {
+ Ok(new_dir) => {
+ dir_stack.push(new_dir);
+ continue;
+ }
+ Err(e)
+ // Check to see if it was a symlink. Linux indicates
+ // this with ENOTDIR because of the O_DIRECTORY flag.
+ if e.as_errno() == Some(Errno::ELOOP)
+ || e.as_errno() == Some(Errno::EMLINK)
+ || e.as_errno() == Some(Errno::ENOTDIR) =>
+ {
+ // attempt symlink expansion
+ match readlinkat(
+ *dir_stack.last().expect("dir_stack is never empty"),
+ component,
+ readlink_buf.as_mut_slice(),
+ ) {
+ Ok(link_path) => {
+ symlink_expansions += 1;
+ if symlink_expansions > MAX_SYMLINK_EXPANSIONS {
+ return ret_error(
+ &mut dir_stack,
+ host::__WASI_ELOOP as host::__wasi_errno_t,
+ );
+ }
+
+ let mut link_path = link_path.as_bytes().to_vec();
+
+ // append a trailing slash if the component leading to it has one, so
+ // that we preserve any ENOTDIR that might come from trying to open a
+ // non-directory
+ if component.ends_with(b"/") {
+ link_path.push(b'/');
+ }
+
+ path_stack.push(link_path);
+ continue;
+ }
+ Err(e) => {
+ return ret_error(
+ &mut dir_stack,
+ host::errno_from_nix(e.as_errno().unwrap()),
+ );
+ }
+ }
+ }
+ Err(e) => {
+ return ret_error(
+ &mut dir_stack,
+ host::errno_from_nix(e.as_errno().unwrap()),
+ );
+ }
+ }
+ }
+ // the final component
+ component => {
+ // if there's a trailing slash, or if `LOOKUP_SYMLINK_FOLLOW` is set, attempt
+ // symlink expansion
+ if component.ends_with(b"/") || (dirflags & host::__WASI_LOOKUP_SYMLINK_FOLLOW) != 0
+ {
+ match readlinkat(
+ *dir_stack.last().expect("dir_stack is never empty"),
+ component,
+ readlink_buf.as_mut_slice(),
+ ) {
+ Ok(link_path) => {
+ symlink_expansions += 1;
+ if symlink_expansions > MAX_SYMLINK_EXPANSIONS {
+ return ret_error(
+ &mut dir_stack,
+ host::__WASI_ELOOP as host::__wasi_errno_t,
+ );
+ }
+
+ let mut link_path = link_path.as_bytes().to_vec();
+
+ // append a trailing slash if the component leading to it has one, so
+ // that we preserve any ENOTDIR that might come from trying to open a
+ // non-directory
+ if component.ends_with(b"/") {
+ link_path.push(b'/');
+ }
+
+ path_stack.push(link_path);
+ continue;
+ }
+ Err(e) => {
+ let errno = e.as_errno().unwrap();
+ if errno != Errno::EINVAL && errno != Errno::ENOENT {
+ // only return an error if this path is not actually a symlink
+ return ret_error(&mut dir_stack, host::errno_from_nix(errno));
+ }
+ }
+ }
+ }
+
+ // not a symlink, so we're done;
+ return Ok((
+ ret_dir_success(&mut dir_stack),
+ OsStr::from_bytes(component).to_os_string(),
+ ));
+ }
+ }
+
+ if path_stack.is_empty() {
+ // 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((
+ ret_dir_success(&mut dir_stack),
+ OsStr::new(".").to_os_string(),
+ ));
+ } else {
+ continue;
+ }
+ }
+}
+
+#[cfg(not(target_os = "macos"))]
+pub fn utime_now() -> c_long {
+ libc::UTIME_NOW
+}
+
+#[cfg(target_os = "macos")]
+pub fn utime_now() -> c_long {
+ -1
+}
+
+#[cfg(not(target_os = "macos"))]
+pub fn utime_omit() -> c_long {
+ libc::UTIME_OMIT
+}
+
+#[cfg(target_os = "macos")]
+pub fn utime_omit() -> c_long {
+ -2
+}
diff --git a/third_party/rust/lucet-wasi-wasmsbx/src/hostcalls/misc.rs b/third_party/rust/lucet-wasi-wasmsbx/src/hostcalls/misc.rs
new file mode 100644
index 0000000000..39ab5109c1
--- /dev/null
+++ b/third_party/rust/lucet-wasi-wasmsbx/src/hostcalls/misc.rs
@@ -0,0 +1,457 @@
+#![allow(non_camel_case_types)]
+#![allow(unused_unsafe)]
+
+use crate::ctx::WasiCtx;
+use crate::memory::*;
+use crate::{host, wasm32};
+
+use super::timers;
+
+use cast::From as _0;
+use lucet_runtime::lucet_hostcall_terminate;
+use lucet_runtime::vmctx::Vmctx;
+
+use nix::convert_ioctl_res;
+use nix::libc::{self, c_int};
+use std::cmp;
+use std::mem::MaybeUninit;
+use std::time::SystemTime;
+
+// define the `fionread()` function, equivalent to `ioctl(fd, FIONREAD, *bytes)`
+nix::ioctl_read_bad!(fionread, nix::libc::FIONREAD, c_int);
+
+fn wasi_clock_to_relative_ns_delay(
+ wasi_clock: host::__wasi_subscription_t___wasi_subscription_u___wasi_subscription_u_clock_t,
+) -> u128 {
+ if wasi_clock.flags != wasm32::__WASI_SUBSCRIPTION_CLOCK_ABSTIME {
+ return wasi_clock.timeout as u128;
+ }
+ let now: u128 = SystemTime::now()
+ .duration_since(SystemTime::UNIX_EPOCH)
+ .expect("Current date is before the epoch")
+ .as_nanos();
+ let deadline = wasi_clock.timeout as u128;
+ deadline.saturating_sub(now)
+}
+
+#[derive(Debug, Copy, Clone)]
+struct ClockEventData {
+ delay: u128,
+ userdata: host::__wasi_userdata_t,
+}
+#[derive(Debug, Copy, Clone)]
+struct FdEventData {
+ fd: c_int,
+ type_: host::__wasi_eventtype_t,
+ userdata: host::__wasi_userdata_t,
+}
+
+pub fn wasi_proc_exit(_vmctx: &mut Vmctx, rval: wasm32::__wasi_exitcode_t) -> ! {
+ lucet_hostcall_terminate!(dec_exitcode(rval));
+}
+
+pub fn wasi_args_get(
+ vmctx: &mut Vmctx,
+ argv_ptr: wasm32::uintptr_t,
+ argv_buf: wasm32::uintptr_t,
+) -> wasm32::__wasi_errno_t {
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+
+ let mut argv_buf_offset = 0;
+ let mut argv = vec![];
+
+ for arg in ctx.args.iter() {
+ let arg_bytes = arg.as_bytes_with_nul();
+ let arg_ptr = argv_buf + argv_buf_offset;
+
+ if let Err(e) = enc_slice_of(vmctx, arg_bytes, arg_ptr) {
+ return enc_errno(e);
+ }
+
+ argv.push(arg_ptr);
+
+ argv_buf_offset = if let Some(new_offset) = argv_buf_offset.checked_add(
+ wasm32::uintptr_t::cast(arg_bytes.len())
+ .expect("cast overflow would have been caught by `enc_slice_of` above"),
+ ) {
+ new_offset
+ } else {
+ return wasm32::__WASI_EOVERFLOW;
+ }
+ }
+ enc_slice_of(vmctx, argv.as_slice(), argv_ptr)
+ .map(|_| wasm32::__WASI_ESUCCESS)
+ .unwrap_or_else(|e| e)
+}
+
+pub fn wasi_args_sizes_get(
+ vmctx: &mut Vmctx,
+ argc_ptr: wasm32::uintptr_t,
+ argv_buf_size_ptr: wasm32::uintptr_t,
+) -> wasm32::__wasi_errno_t {
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+
+ let argc = ctx.args.len();
+ let argv_size = ctx
+ .args
+ .iter()
+ .map(|arg| arg.as_bytes_with_nul().len())
+ .sum();
+ if let Err(e) = enc_usize_byref(vmctx, argc_ptr, argc) {
+ return enc_errno(e);
+ }
+ if let Err(e) = enc_usize_byref(vmctx, argv_buf_size_ptr, argv_size) {
+ return enc_errno(e);
+ }
+ wasm32::__WASI_ESUCCESS
+}
+
+pub fn wasi_sched_yield(_vmctx: &mut Vmctx) -> wasm32::__wasi_errno_t {
+ unsafe { libc::sched_yield() };
+ wasm32::__WASI_ESUCCESS
+}
+
+pub fn wasi_clock_res_get(
+ vmctx: &mut Vmctx,
+ clock_id: wasm32::__wasi_clockid_t,
+ resolution_ptr: wasm32::uintptr_t,
+) -> wasm32::__wasi_errno_t {
+ // convert the supported clocks to the libc types, or return EINVAL
+ let clock_id = match dec_clockid(clock_id) {
+ host::__WASI_CLOCK_REALTIME => libc::CLOCK_REALTIME,
+ host::__WASI_CLOCK_MONOTONIC => libc::CLOCK_MONOTONIC,
+ host::__WASI_CLOCK_PROCESS_CPUTIME_ID => libc::CLOCK_PROCESS_CPUTIME_ID,
+ host::__WASI_CLOCK_THREAD_CPUTIME_ID => libc::CLOCK_THREAD_CPUTIME_ID,
+ _ => return wasm32::__WASI_EINVAL,
+ };
+
+ // no `nix` wrapper for clock_getres, so we do it ourselves
+ let mut timespec = MaybeUninit::<libc::timespec>::uninit();
+ let res = unsafe { timers::clock_getres_helper(clock_id, timespec.as_mut_ptr()) };
+ if res != 0 {
+ return wasm32::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 host::__wasi_timestamp_t)
+ .checked_mul(1_000_000_000)
+ .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as host::__wasi_timestamp_t))
+ .map(|resolution| {
+ // a supported clock can never return zero; this case will probably never get hit, but
+ // make sure we follow the spec
+ if resolution == 0 {
+ wasm32::__WASI_EINVAL
+ } else {
+ enc_timestamp_byref(vmctx, resolution_ptr, resolution)
+ .map(|_| wasm32::__WASI_ESUCCESS)
+ .unwrap_or_else(|e| e)
+ }
+ })
+ .unwrap_or(wasm32::__WASI_EOVERFLOW)
+}
+
+pub fn wasi_clock_time_get(
+ vmctx: &mut Vmctx,
+ clock_id: wasm32::__wasi_clockid_t,
+ // ignored for now, but will be useful once we put optional limits on precision to reduce side
+ // channels
+ _precision: wasm32::__wasi_timestamp_t,
+ time_ptr: wasm32::uintptr_t,
+) -> wasm32::__wasi_errno_t {
+ // convert the supported clocks to the libc types, or return EINVAL
+ let clock_id = match dec_clockid(clock_id) {
+ host::__WASI_CLOCK_REALTIME => libc::CLOCK_REALTIME,
+ host::__WASI_CLOCK_MONOTONIC => libc::CLOCK_MONOTONIC,
+ host::__WASI_CLOCK_PROCESS_CPUTIME_ID => libc::CLOCK_PROCESS_CPUTIME_ID,
+ host::__WASI_CLOCK_THREAD_CPUTIME_ID => libc::CLOCK_THREAD_CPUTIME_ID,
+ _ => return wasm32::__WASI_EINVAL,
+ };
+
+ // no `nix` wrapper for clock_getres, so we do it ourselves
+ let mut timespec = MaybeUninit::<libc::timespec>::uninit();
+ let res = unsafe { timers::clock_gettime_helper(clock_id, timespec.as_mut_ptr()) };
+ if res != 0 {
+ return wasm32::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 host::__wasi_timestamp_t)
+ .checked_mul(1_000_000_000)
+ .and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as host::__wasi_timestamp_t))
+ .map(|time| {
+ enc_timestamp_byref(vmctx, time_ptr, time)
+ .map(|_| wasm32::__WASI_ESUCCESS)
+ .unwrap_or_else(|e| e)
+ })
+ .unwrap_or(wasm32::__WASI_EOVERFLOW)
+}
+
+pub fn wasi_environ_get(
+ vmctx: &mut Vmctx,
+ environ_ptr: wasm32::uintptr_t,
+ environ_buf: wasm32::uintptr_t,
+) -> wasm32::__wasi_errno_t {
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+
+ let mut environ_buf_offset = 0;
+ let mut environ = vec![];
+
+ for pair in ctx.env.iter() {
+ let env_bytes = pair.as_bytes_with_nul();
+ let env_ptr = environ_buf + environ_buf_offset;
+
+ if let Err(e) = enc_slice_of(vmctx, env_bytes, env_ptr) {
+ return enc_errno(e);
+ }
+
+ environ.push(env_ptr);
+
+ environ_buf_offset = if let Some(new_offset) = environ_buf_offset.checked_add(
+ wasm32::uintptr_t::cast(env_bytes.len())
+ .expect("cast overflow would have been caught by `enc_slice_of` above"),
+ ) {
+ new_offset
+ } else {
+ return wasm32::__WASI_EOVERFLOW;
+ }
+ }
+ enc_slice_of(vmctx, environ.as_slice(), environ_ptr)
+ .map(|_| wasm32::__WASI_ESUCCESS)
+ .unwrap_or_else(|e| e)
+}
+
+pub fn wasi_environ_sizes_get(
+ vmctx: &mut Vmctx,
+ environ_count_ptr: wasm32::uintptr_t,
+ environ_size_ptr: wasm32::uintptr_t,
+) -> wasm32::__wasi_errno_t {
+ let ctx = vmctx.get_embed_ctx::<WasiCtx>();
+
+ let environ_count = ctx.env.len();
+ if let Some(environ_size) = ctx.env.iter().try_fold(0, |acc: u32, pair| {
+ acc.checked_add(pair.as_bytes_with_nul().len() as u32)
+ }) {
+ if let Err(e) = enc_usize_byref(vmctx, environ_count_ptr, environ_count) {
+ return enc_errno(e);
+ }
+ if let Err(e) = enc_usize_byref(vmctx, environ_size_ptr, environ_size as usize) {
+ return enc_errno(e);
+ }
+ wasm32::__WASI_ESUCCESS
+ } else {
+ wasm32::__WASI_EOVERFLOW
+ }
+}
+
+pub fn wasi_random_get(
+ vmctx: &mut Vmctx,
+ buf_ptr: wasm32::uintptr_t,
+ buf_len: wasm32::size_t,
+) -> wasm32::__wasi_errno_t {
+ use rand::{thread_rng, RngCore};
+
+ let buf = match dec_slice_of_mut::<u8>(vmctx, buf_ptr, buf_len) {
+ Ok(buf) => buf,
+ Err(e) => return enc_errno(e),
+ };
+ thread_rng().fill_bytes(buf);
+
+ return wasm32::__WASI_ESUCCESS;
+}
+
+fn _wasi_poll_oneoff_handle_timeout_event(
+ output_slice: &mut [wasm32::__wasi_event_t],
+ timeout: Option<ClockEventData>,
+) -> wasm32::size_t {
+ if let Some(ClockEventData { userdata, .. }) = timeout {
+ let output_event = host::__wasi_event_t {
+ userdata,
+ type_: wasm32::__WASI_EVENTTYPE_CLOCK,
+ error: wasm32::__WASI_ESUCCESS,
+ u: host::__wasi_event_t___wasi_event_u {
+ fd_readwrite: host::__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t {
+ nbytes: 0,
+ flags: 0,
+ },
+ },
+ };
+ output_slice[0] = enc_event(output_event);
+ 1
+ } else {
+ // shouldn't happen
+ 0
+ }
+}
+
+fn _wasi_poll_oneoff_handle_fd_event<'t>(
+ output_slice: &mut [wasm32::__wasi_event_t],
+ events: impl Iterator<Item = (&'t FdEventData, &'t nix::poll::PollFd)>,
+) -> wasm32::size_t {
+ let mut output_slice_cur = output_slice.iter_mut();
+ let mut revents_count = 0;
+ for (fd_event, poll_fd) in events {
+ let revents = match poll_fd.revents() {
+ Some(revents) => revents,
+ None => continue,
+ };
+ let mut nbytes = 0;
+ if fd_event.type_ == wasm32::__WASI_EVENTTYPE_FD_READ {
+ let _ = unsafe { fionread(fd_event.fd, &mut nbytes) };
+ }
+ let output_event = if revents.contains(nix::poll::EventFlags::POLLNVAL) {
+ host::__wasi_event_t {
+ userdata: fd_event.userdata,
+ type_: fd_event.type_,
+ error: wasm32::__WASI_EBADF,
+ u: host::__wasi_event_t___wasi_event_u {
+ fd_readwrite:
+ host::__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t {
+ nbytes: 0,
+ flags: wasm32::__WASI_EVENT_FD_READWRITE_HANGUP,
+ },
+ },
+ }
+ } else if revents.contains(nix::poll::EventFlags::POLLERR) {
+ host::__wasi_event_t {
+ userdata: fd_event.userdata,
+ type_: fd_event.type_,
+ error: wasm32::__WASI_EIO,
+ u: host::__wasi_event_t___wasi_event_u {
+ fd_readwrite:
+ host::__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t {
+ nbytes: 0,
+ flags: wasm32::__WASI_EVENT_FD_READWRITE_HANGUP,
+ },
+ },
+ }
+ } else if revents.contains(nix::poll::EventFlags::POLLHUP) {
+ host::__wasi_event_t {
+ userdata: fd_event.userdata,
+ type_: fd_event.type_,
+ error: wasm32::__WASI_ESUCCESS,
+ u: host::__wasi_event_t___wasi_event_u {
+ fd_readwrite:
+ host::__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t {
+ nbytes: 0,
+ flags: wasm32::__WASI_EVENT_FD_READWRITE_HANGUP,
+ },
+ },
+ }
+ } else if revents.contains(nix::poll::EventFlags::POLLIN)
+ | revents.contains(nix::poll::EventFlags::POLLOUT)
+ {
+ host::__wasi_event_t {
+ userdata: fd_event.userdata,
+ type_: fd_event.type_,
+ error: wasm32::__WASI_ESUCCESS,
+ u: host::__wasi_event_t___wasi_event_u {
+ fd_readwrite:
+ host::__wasi_event_t___wasi_event_u___wasi_event_u_fd_readwrite_t {
+ nbytes: nbytes as host::__wasi_filesize_t,
+ flags: 0,
+ },
+ },
+ }
+ } else {
+ continue;
+ };
+ *output_slice_cur.next().unwrap() = enc_event(output_event);
+ revents_count += 1;
+ }
+ revents_count
+}
+
+pub fn wasi_poll_oneoff(
+ vmctx: &mut Vmctx,
+ input: wasm32::uintptr_t,
+ output: wasm32::uintptr_t,
+ nsubscriptions: wasm32::size_t,
+ nevents: wasm32::uintptr_t,
+) -> wasm32::__wasi_errno_t {
+ if nsubscriptions as u64 > wasm32::__wasi_filesize_t::max_value() {
+ return wasm32::__WASI_EINVAL;
+ }
+ enc_pointee(vmctx, nevents, 0).unwrap();
+
+ let input_slice =
+ dec_slice_of::<wasm32::__wasi_subscription_t>(vmctx, input, nsubscriptions).unwrap();
+
+ let output_slice =
+ dec_slice_of_mut::<wasm32::__wasi_event_t>(vmctx, output, nsubscriptions).unwrap();
+
+ let input: Vec<_> = input_slice.iter().map(|x| dec_subscription(x)).collect();
+
+ let timeout = input
+ .iter()
+ .filter_map(|event| match event {
+ Ok(event) if event.type_ == wasm32::__WASI_EVENTTYPE_CLOCK => Some(ClockEventData {
+ delay: wasi_clock_to_relative_ns_delay(unsafe { event.u.clock }) / 1_000_000,
+ userdata: event.userdata,
+ }),
+ _ => None,
+ })
+ .min_by_key(|event| event.delay);
+ let fd_events: Vec<_> = input
+ .iter()
+ .filter_map(|event| match event {
+ Ok(event)
+ if event.type_ == wasm32::__WASI_EVENTTYPE_FD_READ
+ || event.type_ == wasm32::__WASI_EVENTTYPE_FD_WRITE =>
+ {
+ Some(FdEventData {
+ fd: unsafe { event.u.fd_readwrite.fd } as c_int,
+ type_: event.type_,
+ userdata: event.userdata,
+ })
+ }
+ _ => None,
+ })
+ .collect();
+ if fd_events.is_empty() && timeout.is_none() {
+ return wasm32::__WASI_ESUCCESS;
+ }
+ let mut poll_fds: Vec<_> = fd_events
+ .iter()
+ .map(|event| {
+ let mut flags = nix::poll::EventFlags::empty();
+ match event.type_ {
+ wasm32::__WASI_EVENTTYPE_FD_READ => flags.insert(nix::poll::EventFlags::POLLIN),
+ wasm32::__WASI_EVENTTYPE_FD_WRITE => flags.insert(nix::poll::EventFlags::POLLOUT),
+ // An event on a file descriptor can currently only be of type FD_READ or FD_WRITE
+ // Nothing else has been defined in the specification, and these are also the only two
+ // events we filtered before. If we get something else here, the code has a serious bug.
+ _ => unreachable!(),
+ };
+ nix::poll::PollFd::new(event.fd, flags)
+ })
+ .collect();
+ let timeout = timeout.map(|ClockEventData { delay, userdata }| ClockEventData {
+ delay: cmp::min(delay, c_int::max_value() as u128),
+ userdata,
+ });
+ let poll_timeout = timeout.map(|timeout| timeout.delay as c_int).unwrap_or(-1);
+ let ready = loop {
+ match nix::poll::poll(&mut poll_fds, poll_timeout) {
+ Err(_) => {
+ if nix::errno::Errno::last() == nix::errno::Errno::EINTR {
+ continue;
+ }
+ return wasm32::errno_from_nix(nix::errno::Errno::last());
+ }
+ Ok(ready) => break ready as usize,
+ }
+ };
+ let events_count = if ready == 0 {
+ _wasi_poll_oneoff_handle_timeout_event(output_slice, timeout)
+ } else {
+ let events = fd_events.iter().zip(poll_fds.iter()).take(ready);
+ _wasi_poll_oneoff_handle_fd_event(output_slice, events)
+ };
+ if let Err(e) = enc_pointee(vmctx, nevents, events_count) {
+ return enc_errno(e);
+ }
+ wasm32::__WASI_ESUCCESS
+}
diff --git a/third_party/rust/lucet-wasi-wasmsbx/src/hostcalls/mod.rs b/third_party/rust/lucet-wasi-wasmsbx/src/hostcalls/mod.rs
new file mode 100644
index 0000000000..ed9b5e038d
--- /dev/null
+++ b/third_party/rust/lucet-wasi-wasmsbx/src/hostcalls/mod.rs
@@ -0,0 +1,448 @@
+//! Hostcalls that implement
+//! [WASI](https://github.com/CraneStation/wasmtime-wasi/blob/wasi/docs/WASI-overview.md).
+//!
+//! This code borrows heavily from [wasmtime-wasi](https://github.com/CraneStation/wasmtime-wasi),
+//! which in turn borrows from cloudabi-utils. See `LICENSE.wasmtime-wasi` for license information.
+
+#![allow(non_camel_case_types)]
+#![allow(unused_unsafe)]
+
+mod fs;
+mod fs_helpers;
+mod misc;
+mod timers;
+
+use crate::wasm32;
+
+use fs::*;
+use lucet_runtime::lucet_hostcalls;
+use misc::*;
+
+lucet_hostcalls! {
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_proc_exit(&mut vmctx, rval: wasm32::__wasi_exitcode_t,) -> ! {
+ wasi_proc_exit(vmctx, rval)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_args_get(
+ &mut vmctx,
+ argv_ptr: wasm32::uintptr_t,
+ argv_buf: wasm32::uintptr_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_args_get(vmctx, argv_ptr, argv_buf)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_args_sizes_get(&mut vmctx,
+ argc_ptr: wasm32::uintptr_t,
+ argv_buf_size_ptr: wasm32::uintptr_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_args_sizes_get(vmctx, argc_ptr, argv_buf_size_ptr)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_sched_yield(&mut vmctx,) -> wasm32::__wasi_errno_t {
+ wasi_sched_yield(vmctx)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_clock_res_get(
+ &mut vmctx,
+ clock_id: wasm32::__wasi_clockid_t,
+ resolution_ptr: wasm32::uintptr_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_clock_res_get(vmctx, clock_id, resolution_ptr)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_clock_time_get(
+ &mut vmctx,
+ clock_id: wasm32::__wasi_clockid_t,
+ precision: wasm32::__wasi_timestamp_t,
+ time_ptr: wasm32::uintptr_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_clock_time_get(vmctx, clock_id, precision, time_ptr)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_environ_get(
+ &mut vmctx,
+ environ_ptr: wasm32::uintptr_t,
+ environ_buf: wasm32::uintptr_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_environ_get(vmctx, environ_ptr, environ_buf)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_environ_sizes_get(
+ &mut vmctx,
+ environ_count_ptr: wasm32::uintptr_t,
+ environ_size_ptr: wasm32::uintptr_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_environ_sizes_get(vmctx, environ_count_ptr, environ_size_ptr)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_fd_close(
+ &mut vmctx,
+ fd: wasm32::__wasi_fd_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_fd_close(vmctx, fd)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_fd_fdstat_get(
+ &mut vmctx,
+ fd: wasm32::__wasi_fd_t,
+ fdstat_ptr: wasm32::uintptr_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_fd_fdstat_get(vmctx, fd, fdstat_ptr)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_fd_fdstat_set_flags(
+ &mut vmctx,
+ fd: wasm32::__wasi_fd_t,
+ fdflags: wasm32::__wasi_fdflags_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_fd_fdstat_set_flags(vmctx, fd, fdflags)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_fd_tell(&mut vmctx,
+ fd: wasm32::__wasi_fd_t,
+ offset: wasm32::uintptr_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_fd_tell(vmctx, fd, offset)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_fd_seek(&mut vmctx,
+ fd: wasm32::__wasi_fd_t,
+ offset: wasm32::__wasi_filedelta_t,
+ whence: wasm32::__wasi_whence_t,
+ newoffset: wasm32::uintptr_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_fd_seek(vmctx, fd, offset, whence, newoffset)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_fd_prestat_get(
+ &mut vmctx,
+ fd: wasm32::__wasi_fd_t,
+ prestat_ptr: wasm32::uintptr_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_fd_prestat_get(vmctx, fd, prestat_ptr)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_fd_prestat_dir_name(
+ &mut vmctx,
+ fd: wasm32::__wasi_fd_t,
+ path_ptr: wasm32::uintptr_t,
+ path_len: wasm32::size_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_fd_prestat_dir_name(vmctx, fd, path_ptr, path_len)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_fd_read(
+ &mut vmctx,
+ fd: wasm32::__wasi_fd_t,
+ iovs_ptr: wasm32::uintptr_t,
+ iovs_len: wasm32::size_t,
+ nread: wasm32::uintptr_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_fd_read(vmctx, fd, iovs_ptr, iovs_len, nread)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_fd_write(
+ &mut vmctx,
+ fd: wasm32::__wasi_fd_t,
+ iovs_ptr: wasm32::uintptr_t,
+ iovs_len: wasm32::size_t,
+ nwritten: wasm32::uintptr_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_fd_write(vmctx, fd, iovs_ptr, iovs_len, nwritten)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_path_open(
+ &mut vmctx,
+ dirfd: wasm32::__wasi_fd_t,
+ dirflags: wasm32::__wasi_lookupflags_t,
+ path_ptr: wasm32::uintptr_t,
+ path_len: wasm32::size_t,
+ oflags: wasm32::__wasi_oflags_t,
+ fs_rights_base: wasm32::__wasi_rights_t,
+ fs_rights_inheriting: wasm32::__wasi_rights_t,
+ fs_flags: wasm32::__wasi_fdflags_t,
+ fd_out_ptr: wasm32::uintptr_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_path_open(vmctx, dirfd, dirflags, path_ptr, path_len,
+ oflags, fs_rights_base, fs_rights_inheriting, fs_flags,
+ fd_out_ptr)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_random_get(
+ &mut vmctx,
+ buf_ptr: wasm32::uintptr_t,
+ buf_len: wasm32::size_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_random_get(vmctx, buf_ptr, buf_len)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_poll_oneoff(
+ &mut vmctx,
+ input: wasm32::uintptr_t,
+ output: wasm32::uintptr_t,
+ nsubscriptions: wasm32::size_t,
+ nevents: wasm32::uintptr_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_poll_oneoff(vmctx, input, output, nsubscriptions, nevents)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_fd_filestat_get(
+ &mut vmctx,
+ fd: wasm32::__wasi_fd_t,
+ filestat_ptr: wasm32::uintptr_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_fd_filestat_get(vmctx, fd, filestat_ptr)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_path_filestat_get(
+ &mut vmctx,
+ dirfd: wasm32::__wasi_fd_t,
+ dirflags: wasm32::__wasi_lookupflags_t,
+ path_ptr: wasm32::uintptr_t,
+ path_len: wasm32::size_t,
+ filestat_ptr: wasm32::uintptr_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_path_filestat_get(vmctx, dirfd, dirflags, path_ptr,
+ path_len, filestat_ptr)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_path_create_directory(
+ &mut vmctx,
+ dirfd: wasm32::__wasi_fd_t,
+ path_ptr: wasm32::uintptr_t,
+ path_len: wasm32::size_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_path_create_directory(vmctx, dirfd, path_ptr, path_len)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_path_unlink_file(
+ &mut vmctx,
+ dirfd: wasm32::__wasi_fd_t,
+ path_ptr: wasm32::uintptr_t,
+ path_len: wasm32::size_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_path_unlink_file(vmctx, dirfd, path_ptr, path_len)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_fd_allocate(
+ &mut vmctx,
+ fd: wasm32::__wasi_fd_t,
+ offset: wasm32::__wasi_filesize_t,
+ len: wasm32::__wasi_filesize_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_fd_allocate(vmctx, fd, offset, len)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_fd_advise(
+ &mut vmctx,
+ fd: wasm32::__wasi_fd_t,
+ offset: wasm32::__wasi_filesize_t,
+ len: wasm32::__wasi_filesize_t,
+ advice: wasm32::__wasi_advice_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_fd_advise(vmctx, fd, offset, len, advice)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_fd_datasync(
+ &mut vmctx,
+ fd: wasm32::__wasi_fd_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_fd_datasync(vmctx, fd)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_fd_sync(
+ &mut vmctx,
+ fd: wasm32::__wasi_fd_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_fd_sync(vmctx, fd)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_fd_fdstat_set_rights(
+ &mut vmctx,
+ fd: wasm32::__wasi_fd_t,
+ fs_rights_base: wasm32::__wasi_rights_t,
+ fs_rights_inheriting: wasm32::__wasi_rights_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_fd_fdstat_set_rights(vmctx, fd, fs_rights_base, fs_rights_inheriting)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_fd_filestat_set_size(
+ &mut vmctx,
+ fd: wasm32::__wasi_fd_t,
+ st_size: wasm32::__wasi_filesize_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_fd_filestat_set_size(vmctx, fd, st_size)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_fd_filestat_set_times(
+ &mut vmctx,
+ fd: wasm32::__wasi_fd_t,
+ st_atim: wasm32::__wasi_timestamp_t,
+ st_mtim: wasm32::__wasi_timestamp_t,
+ fst_flags: wasm32::__wasi_fstflags_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_fd_filestat_set_times(vmctx, fd, st_atim, st_mtim, fst_flags)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_fd_pread(
+ &mut vmctx,
+ fd: wasm32::__wasi_fd_t,
+ iovs_ptr: wasm32::uintptr_t,
+ iovs_len: wasm32::size_t,
+ offset: wasm32::__wasi_filesize_t,
+ nread: wasm32::uintptr_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_fd_pread(vmctx, fd, iovs_ptr, iovs_len, offset, nread)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_fd_pwrite(
+ &mut vmctx,
+ fd: wasm32::__wasi_fd_t,
+ iovs_ptr: wasm32::uintptr_t,
+ iovs_len: wasm32::size_t,
+ offset: wasm32::__wasi_filesize_t,
+ nwritten: wasm32::uintptr_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_fd_pwrite(vmctx, fd, iovs_ptr, iovs_len, offset, nwritten)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_fd_readdir(
+ &mut vmctx,
+ fd: wasm32::__wasi_fd_t,
+ buf: wasm32::uintptr_t,
+ buf_len: wasm32::size_t,
+ cookie: wasm32::__wasi_dircookie_t,
+ bufused: wasm32::uintptr_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_fd_readdir(vmctx, fd, buf, buf_len, cookie, bufused)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_fd_renumber(
+ &mut vmctx,
+ from: wasm32::__wasi_fd_t,
+ to: wasm32::__wasi_fd_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_fd_renumber(vmctx, from, to)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_path_filestat_set_times(
+ &mut vmctx,
+ dirfd: wasm32::__wasi_fd_t,
+ dirflags: wasm32::__wasi_lookupflags_t,
+ path_ptr: wasm32::uintptr_t,
+ path_len: wasm32::size_t,
+ st_atim: wasm32::__wasi_timestamp_t,
+ st_mtim: wasm32::__wasi_timestamp_t,
+ fst_flags: wasm32::__wasi_fstflags_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_path_filestat_set_times(vmctx, dirfd, dirflags, path_ptr, path_len, st_atim, st_mtim, fst_flags)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_path_link(
+ &mut vmctx,
+ old_fd: wasm32::__wasi_fd_t,
+ old_flags: wasm32::__wasi_lookupflags_t,
+ old_path_ptr: wasm32::uintptr_t,
+ old_path_len: wasm32::size_t,
+ new_fd: wasm32::__wasi_fd_t,
+ new_path_ptr: wasm32::uintptr_t,
+ new_path_len: wasm32::size_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_path_link(vmctx, old_fd, old_flags, old_path_ptr, old_path_len,
+ new_fd, new_path_ptr, new_path_len)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_path_readlink(
+ &mut vmctx,
+ dirfd: wasm32::__wasi_fd_t,
+ path_ptr: wasm32::uintptr_t,
+ path_len: wasm32::size_t,
+ buf_ptr: wasm32::uintptr_t,
+ buf_len: wasm32::size_t,
+ bufused: wasm32::uintptr_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_path_readlink(vmctx, dirfd, path_ptr, path_len, buf_ptr, buf_len, bufused)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_path_remove_directory(
+ &mut vmctx,
+ dirfd: wasm32::__wasi_fd_t,
+ path_ptr: wasm32::uintptr_t,
+ path_len: wasm32::size_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_path_remove_directory(vmctx, dirfd, path_ptr, path_len)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_path_rename(
+ &mut vmctx,
+ old_dirfd: wasm32::__wasi_fd_t,
+ old_path_ptr: wasm32::uintptr_t,
+ old_path_len: wasm32::size_t,
+ new_dirfd: wasm32::__wasi_fd_t,
+ new_path_ptr: wasm32::uintptr_t,
+ new_path_len: wasm32::size_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_path_rename(vmctx, old_dirfd, old_path_ptr, old_path_len,
+ new_dirfd, new_path_ptr, new_path_len)
+ }
+
+ #[no_mangle] pub unsafe extern "C"
+ fn __wasi_path_symlink(
+ &mut vmctx,
+ old_path_ptr: wasm32::uintptr_t,
+ old_path_len: wasm32::size_t,
+ dir_fd: wasm32::__wasi_fd_t,
+ new_path_ptr: wasm32::uintptr_t,
+ new_path_len: wasm32::size_t,
+ ) -> wasm32::__wasi_errno_t {
+ wasi_path_symlink(vmctx, old_path_ptr, old_path_len,
+ dir_fd, new_path_ptr, new_path_len)
+ }
+}
+
+#[doc(hidden)]
+pub fn ensure_linked() {
+ unsafe {
+ std::ptr::read_volatile(__wasi_proc_exit as *const extern "C" fn());
+ }
+}
diff --git a/third_party/rust/lucet-wasi-wasmsbx/src/hostcalls/timers.rs b/third_party/rust/lucet-wasi-wasmsbx/src/hostcalls/timers.rs
new file mode 100644
index 0000000000..926a998445
--- /dev/null
+++ b/third_party/rust/lucet-wasi-wasmsbx/src/hostcalls/timers.rs
@@ -0,0 +1,113 @@
+use nix::libc::{self, c_int, c_char};
+
+#[cfg(not(target_os = "macos"))]
+mod notmac
+{
+ use super::*;
+
+ pub unsafe fn clock_gettime_helper(clock_id: libc::clockid_t, tp: *mut libc::timespec) -> c_int {
+ libc::clock_gettime(clock_id, tp)
+ }
+
+ pub unsafe fn clock_getres_helper(clock_id: libc::clockid_t, res: *mut libc::timespec) -> c_int {
+ libc::clock_getres(clock_id, res)
+ }
+
+ pub unsafe fn futimens_helper(fd: c_int, times: *const libc::timespec) -> c_int {
+ libc::futimens(fd, times)
+ }
+
+ pub unsafe fn utimensat_helper(dirfd: c_int, path: *const c_char, times: *const libc::timespec, flag: c_int) -> c_int {
+ libc::utimensat(dirfd, path, times, flag)
+ }
+}
+
+#[cfg(target_os = "macos")]
+mod mac
+{
+ use super::*;
+ use std::mem::MaybeUninit;
+ use std::sync::Once;
+
+ use mach::mach_time::*;
+
+ // Referring these 3 sources
+ //https://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x
+ //https://stackoverflow.com/questions/11680461/monotonic-clock-on-osx
+ //https://gist.github.com/lifthrasiir/393ffb3e9900709fa2e3ae2a540b635f
+
+ static mut CONVERSION_FACTOR : f64 = 0.0;
+ static INIT : Once = Once::new();
+
+ unsafe fn get_cached_conversion_factor() -> f64 {
+ unsafe {
+ INIT.call_once(|| {
+ let mut timebase = MaybeUninit::<mach_timebase_info_data_t>::uninit();
+ mach_timebase_info(timebase.as_mut_ptr());
+ let timebase = unsafe { timebase.assume_init() };
+
+ let numer_d : f64 = timebase.numer as f64;
+ let denom_d : f64 = timebase.denom as f64;
+
+ CONVERSION_FACTOR = numer_d / denom_d;
+ });
+ }
+ CONVERSION_FACTOR
+ }
+
+ pub unsafe fn clock_gettime_helper(clock_id: libc::clockid_t, tp: *mut libc::timespec) -> c_int {
+ if !(clock_id == libc::CLOCK_REALTIME || clock_id == libc::CLOCK_MONOTONIC) {
+ (*libc::__error()) = libc::EINVAL;
+ return -1;
+ }
+
+ if clock_id == libc::CLOCK_REALTIME {
+ let mut micro = MaybeUninit::<libc::timeval>::uninit();
+ libc::gettimeofday(micro.as_mut_ptr(), core::ptr::null_mut());
+ let micro = unsafe { micro.assume_init() };
+
+ (*tp).tv_sec = micro.tv_sec;
+ (*tp).tv_nsec = i64::from(micro.tv_usec) * 1000;
+ return 0;
+ } else {
+ let time : u64 = mach_absolute_time();
+ let time_d : f64 = time as f64;
+ let conv : f64 = get_cached_conversion_factor();
+ let nseconds : f64 = time_d * conv;
+ let seconds : f64 = nseconds / 1e9;
+ (*tp).tv_sec = seconds as i64;
+ (*tp).tv_nsec = nseconds as i64;
+ return 0;
+ }
+ }
+
+ pub unsafe fn clock_getres_helper(clock_id: libc::clockid_t, res: *mut libc::timespec) -> c_int {
+ if !(clock_id == libc::CLOCK_REALTIME || clock_id == libc::CLOCK_MONOTONIC) {
+ (*libc::__error()) = libc::EINVAL;
+ return -1;
+ }
+
+ (*res).tv_sec = 0 as i64;
+ (*res).tv_nsec =
+ if clock_id == libc::CLOCK_REALTIME {
+ 1000 as i64
+ } else {
+ 1 as i64
+ };
+ return 0;
+ }
+
+ pub unsafe fn futimens_helper(_fd: c_int, _times: *const libc::timespec) -> c_int {
+ panic!("futimens not implemented");
+ }
+
+ pub unsafe fn utimensat_helper(_dirfd: c_int, _path: *const c_char, _times: *const libc::timespec, _flag: c_int) -> c_int {
+ panic!("utimensat not implemented");
+ }
+}
+
+#[cfg(not(target_os = "macos"))]
+pub use notmac::*;
+
+#[cfg(target_os = "macos")]
+pub use mac::*; \ No newline at end of file