#![allow(unused)] pub mod types; #[cfg(any(target_os = "linux", target_os = "macos"))] pub mod unix; #[cfg(any(target_os = "windows"))] pub mod windows; use self::types::*; use crate::{ ptr::{Array, WasmPtr}, state::WasiState, }; use rand::{thread_rng, Rng}; use wasmer_runtime_core::{memory::Memory, vm::Ctx}; #[cfg(any(target_os = "linux", target_os = "macos"))] pub use unix::*; #[cfg(any(target_os = "windows"))] pub use windows::*; #[allow(clippy::mut_from_ref)] fn get_wasi_state(ctx: &Ctx) -> &mut WasiState { unsafe { &mut *(ctx.data as *mut WasiState) } } #[must_use] fn write_buffer_array( memory: &Memory, from: &[Vec], ptr_buffer: WasmPtr, Array>, buffer: WasmPtr, ) -> __wasi_errno_t { let ptrs = if let Some(cells) = ptr_buffer.deref(memory, 0, from.len() as u32) { cells } else { return __WASI_EOVERFLOW; }; let mut current_buffer_offset = 0; for ((i, sub_buffer), ptr) in from.iter().enumerate().zip(ptrs.iter()) { ptr.set(WasmPtr::new(buffer.offset() + current_buffer_offset)); let cells = if let Some(cells) = buffer.deref(memory, current_buffer_offset, sub_buffer.len() as u32) { cells } else { return __WASI_EOVERFLOW; }; for (cell, &byte) in cells.iter().zip(sub_buffer.iter()) { cell.set(byte); } current_buffer_offset += sub_buffer.len() as u32; } __WASI_ESUCCESS } /// ### `args_get()` /// Read command-line argument data. /// The sizes of the buffers should match that returned by [`args_sizes_get()`](#args_sizes_get). /// Inputs: /// - `char **argv` /// A pointer to a buffer to write the argument pointers. /// - `char *argv_buf` /// A pointer to a buffer to write the argument string data. /// pub fn args_get( ctx: &mut Ctx, argv: WasmPtr, Array>, argv_buf: WasmPtr, ) -> __wasi_errno_t { let state = get_wasi_state(ctx); let memory = ctx.memory(0); write_buffer_array(memory, &*state.args, argv, argv_buf) } /// ### `args_sizes_get()` /// Return command-line argument data sizes. /// Outputs: /// - `size_t *argc` /// The number of arguments. /// - `size_t *argv_buf_size` /// The size of the argument string data. pub fn args_sizes_get( ctx: &mut Ctx, argc: WasmPtr, argv_buf_size: WasmPtr, ) -> __wasi_errno_t { let memory = ctx.memory(0); if let (Some(argc), Some(argv_buf_size)) = (argc.deref(memory), argv_buf_size.deref(memory)) { let state = get_wasi_state(ctx); argc.set(state.args.len() as u32); argv_buf_size.set(state.args.iter().map(|v| v.len() as u32).sum()); __WASI_ESUCCESS } else { __WASI_EOVERFLOW } } pub fn clock_res_get( ctx: &mut Ctx, clock_id: __wasi_clockid_t, resolution: WasmPtr<__wasi_timestamp_t>, ) -> __wasi_errno_t { let memory = ctx.memory(0); if let Some(out_addr) = resolution.deref(memory) { platform_clock_res_get(clock_id, out_addr) } else { __WASI_EFAULT } } pub fn clock_time_get( ctx: &mut Ctx, clock_id: __wasi_clockid_t, precision: __wasi_timestamp_t, time: WasmPtr<__wasi_timestamp_t>, ) -> __wasi_errno_t { let memory = ctx.memory(0); if let Some(out_addr) = time.deref(memory) { platform_clock_time_get(clock_id, precision, out_addr) } else { __WASI_EFAULT } } /// ### `environ_get()` /// Read environment variable data. /// The sizes of the buffers should match that returned by [`environ_sizes_get()`](#environ_sizes_get). /// Inputs: /// - `char **environ` /// A pointer to a buffer to write the environment variable pointers. /// - `char *environ_buf` /// A pointer to a buffer to write the environment variable string data. pub fn environ_get( ctx: &mut Ctx, environ: WasmPtr, Array>, environ_buf: WasmPtr, ) -> __wasi_errno_t { let state = get_wasi_state(ctx); let memory = ctx.memory(0); write_buffer_array(memory, &*state.args, environ, environ_buf) } /// ### `environ_sizes_get()` /// Return command-line argument data sizes. /// Outputs: /// - `size_t *environ_count` /// The number of environment variables. /// - `size_t *environ_buf_size` /// The size of the environment variable string data. pub fn environ_sizes_get( ctx: &mut Ctx, environ_count: WasmPtr, environ_buf_size: WasmPtr, ) -> __wasi_errno_t { let memory = ctx.memory(0); if let (Some(environ_count), Some(environ_buf_size)) = (environ_count.deref(memory), environ_buf_size.deref(memory)) { let state = get_wasi_state(ctx); environ_count.set(state.envs.len() as u32); environ_buf_size.set(state.envs.iter().map(|v| v.len() as u32).sum()); __WASI_ESUCCESS } else { __WASI_EOVERFLOW } } pub fn fd_advise( ctx: &mut Ctx, fd: __wasi_fd_t, offset: __wasi_filesize_t, len: __wasi_filesize_t, advice: __wasi_advice_t, ) -> __wasi_errno_t { unimplemented!() } pub fn fd_allocate( ctx: &mut Ctx, fd: __wasi_fd_t, offset: __wasi_filesize_t, len: __wasi_filesize_t, ) -> __wasi_errno_t { unimplemented!() } /// ### `fd_close()` /// Close an open file descriptor /// Inputs: /// - `__wasi_fd_t fd` /// A file descriptor mapping to an open file to close /// Errors: /// - `__WASI_EISDIR` /// If `fd` is a directory /// - `__WASI_EBADF` /// If `fd` is invalid or not open (TODO: consider __WASI_EINVAL) pub fn fd_close(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t { // FD is too large return __WASI_EMFILE; // FD is a directory (due to user input) return __WASI_EISDIR; // FD is invalid return __WASI_EBADF; __WASI_ESUCCESS } pub fn fd_datasync(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t { unimplemented!() } pub fn fd_fdstat_get( ctx: &mut Ctx, fd: __wasi_fd_t, buf: WasmPtr<__wasi_fdstat_t>, ) -> __wasi_errno_t { let mut state = get_wasi_state(ctx); let memory = ctx.memory(0); let stat = match state.fs.filestat_fd(fd) { Ok(stat) => stat, Err(errno) => return errno, }; if let Some(buf) = buf.deref(memory) { buf.set(stat); __WASI_ESUCCESS } else { __WASI_EFAULT } } pub fn fd_fdstat_set_flags( ctx: &mut Ctx, fd: __wasi_fd_t, flags: __wasi_fdflags_t, ) -> __wasi_errno_t { unimplemented!() } pub fn fd_fdstat_set_rights( ctx: &mut Ctx, fd: __wasi_fd_t, fs_rights_base: __wasi_rights_t, fs_rights_inheriting: __wasi_rights_t, ) -> __wasi_errno_t { unimplemented!() } pub fn fd_filestat_get( ctx: &mut Ctx, fd: __wasi_fd_t, buf: WasmPtr<__wasi_filestat_t>, ) -> __wasi_errno_t { unimplemented!() } pub fn fd_filestat_set_size( ctx: &mut Ctx, fd: __wasi_fd_t, st_size: __wasi_filesize_t, ) -> __wasi_errno_t { unimplemented!() } pub fn fd_filestat_set_times( ctx: &mut Ctx, fd: __wasi_fd_t, st_atim: __wasi_timestamp_t, st_mtim: __wasi_timestamp_t, fst_flags: __wasi_fstflags_t, ) -> __wasi_errno_t { unimplemented!() } pub fn fd_pread( ctx: &mut Ctx, fd: __wasi_fd_t, iovs: WasmPtr<__wasi_iovec_t, Array>, iovs_len: u32, offset: __wasi_filesize_t, nread: WasmPtr, ) -> __wasi_errno_t { let memory = ctx.memory(0); if let ((Some(iov_cells), Some(nread_cell))) = (iovs.deref(memory, 0, iovs_len), nread.deref(memory)) { platform_fd_pread(fd, iov_cells, iovs_len, offset, nread_cell) } else { __WASI_EFAULT } } pub fn fd_prestat_get( ctx: &mut Ctx, fd: __wasi_fd_t, buf: WasmPtr<__wasi_prestat_t>, ) -> __wasi_errno_t { let memory = ctx.memory(0); if let Some(prestat_ptr) = buf.deref(memory) { // open fd // write info to prestat_ptr __WASI_ESUCCESS } else { __WASI_EFAULT } } pub fn fd_prestat_dir_name( ctx: &mut Ctx, fd: __wasi_fd_t, path: WasmPtr, path_len: u32, ) -> __wasi_errno_t { let memory = ctx.memory(0); if let Some(path_chars) = path.deref(memory, 0, path_len) { if true /* check if dir */ { // get name // write name // if overflow __WASI_EOVERFLOW __WASI_ESUCCESS } else { __WASI_ENOTDIR } } else { __WASI_EFAULT } } pub fn fd_pwrite( ctx: &mut Ctx, fd: __wasi_fd_t, iovs: WasmPtr<__wasi_ciovec_t, Array>, iovs_len: u32, offset: __wasi_filesize_t, nwritten: WasmPtr, ) -> __wasi_errno_t { unimplemented!() } /// ### `fd_read()` /// Read data from file descriptor /// Inputs: /// - `__wasi_fd_t fd` /// File descriptor from which data will be read /// - `const __wasi_iovec_t *iovs` /// Vectors where data will be stored /// - `u32 iovs_len` /// Length of data in `iovs` /// Output: /// - `u32 *nread` /// Number of bytes read pub fn fd_read( ctx: &mut Ctx, fd: __wasi_fd_t, iovs: WasmPtr<__wasi_iovec_t, Array>, iovs_len: u32, nread: WasmPtr, ) -> __wasi_errno_t { let memory = ctx.memory(0); // check __WASI_RIGHT_FD_READ if let (Some(iovs_arr_cell), Some(nwritten_cell)) = (iovs.deref(memory, 0, iovs_len), nread.deref(memory)) { unimplemented!() } else { __WASI_EFAULT } } /// ### `fd_readdir()` /// Read data from directory specified by file descriptor /// Inputs: /// - `__wasi_fd_t fd` /// File descriptor from which directory data will be read /// - `void *buf` /// Buffer where directory entries are stored /// - `u32 buf_len` /// Length of data in `buf` /// - `__wasi_dircookie_t cookie` /// Where the directory reading should start from /// Output: /// - `u32 *bufused` /// The Number of bytes stored in `buf`; if less than `buf_len` then entire /// directory has been read pub fn fd_readdir( ctx: &mut Ctx, fd: __wasi_fd_t, buf: WasmPtr, buf_len: u32, cookie: __wasi_dircookie_t, bufused: WasmPtr, ) -> __wasi_errno_t { let memory = ctx.memory(0); if let (Some(buf_arr_cell), Some(bufused_cell)) = (buf.deref(memory, 0, buf_len), bufused.deref(memory)) { unimplemented!() } else { __WASI_EFAULT } } /// ### `fd_renumber()` /// Atomically copy file descriptor /// Inputs: /// - `__wasi_fd_t from` /// File descriptor to copy /// - `__wasi_fd_t to` /// Location to copy file descriptor to pub fn fd_renumber(ctx: &mut Ctx, from: __wasi_fd_t, to: __wasi_fd_t) -> __wasi_errno_t { unimplemented!() } /// ### `fd_seek()` /// Update file descriptor offset /// Inputs: /// - `__wasi_fd_t fd` /// File descriptor to mutate /// - `__wasi_filedelta_t offset` /// Number of bytes to adjust offset by /// - `__wasi_whence_t whence` /// What the offset is relative to /// Output: /// - `__wasi_filesize_t *fd` /// The new offset relative to the start of the file pub fn fd_seek( ctx: &mut Ctx, fd: __wasi_fd_t, offset: __wasi_filedelta_t, whence: __wasi_whence_t, newoffset: WasmPtr<__wasi_filesize_t>, ) -> __wasi_errno_t { let memory = ctx.memory(0); // TODO: check __WASI_RIGHT_FD_SEEK // TODO: handle directory input if let Some(new_offset_cell) = newoffset.deref(memory) { unimplemented!() } else { __WASI_EFAULT } } /// ### `fd_sync()` /// Synchronize file and metadata to disk (TODO: expand upon what this means in our system) /// Inputs: /// - `__wasi_fd_t fd` /// The file descriptor to sync /// Errors: /// TODO: figure out which errors this should return /// - `__WASI_EPERM` /// - `__WAIS_ENOTCAPABLE` pub fn fd_sync(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t { // TODO: check __WASI_RIGHT_FD_SYNC unimplemented!() } /// ### `fd_tell()` /// Get the offset of the file descriptor /// Inputs: /// - `__wasi_fd_t fd` /// The file descriptor to access /// Output: /// - `__wasi_filesize_t *offset` /// The offset of `fd` relative to the start of the file pub fn fd_tell( ctx: &mut Ctx, fd: __wasi_fd_t, offset: WasmPtr<__wasi_filesize_t>, ) -> __wasi_errno_t { // TODO: check __WASI_RIGHT_FD_TELL unimplemented!() } /// ### `fd_write()` /// Write data to the file descriptor /// Inputs: /// - `__wasi_fd_t` /// File descriptor (opened with writing) to write to /// - `const __wasi_ciovec_t *iovs` /// List of vectors to read data from /// - `u32 iovs_len` /// Length of data in `iovs` /// Output: /// - `u32 *nwritten` /// Number of bytes written /// Errors: /// pub fn fd_write( ctx: &mut Ctx, fd: __wasi_fd_t, iovs: WasmPtr<__wasi_ciovec_t, Array>, iovs_len: u32, nwritten: WasmPtr, ) -> __wasi_errno_t { let memory = ctx.memory(0); // TODO: check __WASI_RIGHT_FD_WRITE // return __WASI_EISDIR if dir (probably) if let (Some(iovs_arr_cell), Some(nwritten_cell)) = (iovs.deref(memory, 0, iovs_len), nwritten.deref(memory)) { unimplemented!() } else { __WASI_EFAULT } } /// ### `path_create_directory()` /// Create directory at a path /// Inputs: /// - `__wasi_fd_t fd` /// The directory that the path is relative to /// - `const char *path` /// String containing path data /// - `u32 path_len` /// The length of `path` /// Errors: /// Required Rights: /// - __WASI_RIGHT_PATH_CREATE_DIRECTORY /// This right must be set on the directory that the file is created in (TODO: verify that this is true) pub fn path_create_directory( ctx: &mut Ctx, fd: __wasi_fd_t, path: WasmPtr, path_len: u32, ) -> __wasi_errno_t { // check __WASI_RIGHT_PATH_CREATE_DIRECTORY unimplemented!() } /// ### `path_filestat_get()` /// Access metadata about a file or directory /// Inputs: /// - `__wasi_fd_t fd` /// The file to acces /// - `__wasi_lookupflags_t flags` /// Flags to control how the path is understood /// - `const char *path` /// String containing the file path /// - `u32 path_len` /// The length of the `path` string /// Output: /// - `__wasi_file_stat_t *buf` /// The location where the metadata will be stored pub fn path_filestat_get( ctx: &mut Ctx, fd: __wasi_fd_t, flags: __wasi_lookupflags_t, path: WasmPtr, path_len: u32, buf: WasmPtr<__wasi_filestat_t>, ) -> __wasi_errno_t { // check __WASI_RIGHT_PATH_FILESTAT_GET unimplemented!() } /// ### `path_filestat_set_times()` /// Update time metadata on a file or directory /// Inputs: /// - `__wasi_fd_t fd` /// The directory relative to which the path is resolved /// - `__wasi_lookupflags_t flags` /// Flags to control how the path is understood /// - `const char *path` /// String containing the file path /// - `u32 path_len` /// The length of the `path` string /// - `__wasi_timestamp_t st_atim` /// The timestamp that the last accessed time attribute is set to /// - `__wasi_timestamp_t st_mtim` /// The timestamp that the last modified time attribute is set to /// - `__wasi_fstflags_t fst_flags` /// A bitmask controlling which attributes are set pub fn path_filestat_set_times( ctx: &mut Ctx, fd: __wasi_fd_t, flags: __wasi_lookupflags_t, path: WasmPtr, path_len: u32, st_atim: __wasi_timestamp_t, st_mtim: __wasi_timestamp_t, fst_flags: __wasi_fstflags_t, ) -> __wasi_errno_t { unimplemented!() } /// ### `path_link()` /// Create a hard link /// Inputs: /// - `__wasi_fd_t old_fd` /// The directory relative to which the `old_path` is /// - `__wasi_lookupflags_t old_flags` /// Flags to control how `old_path` is understood /// - `const char *old_path` /// String containing the old file path /// - `u32 old_path_len` /// Length of the `old_path` string /// - `__wasi_fd_t new_fd` /// The directory relative to which the `new_path` is /// - `const char *new_path` /// String containing the new file path /// - `u32 old_path_len` /// Length of the `new_path` string pub fn path_link( ctx: &mut Ctx, old_fd: __wasi_fd_t, old_flags: __wasi_lookupflags_t, old_path: WasmPtr, old_path_len: u32, new_fd: __wasi_fd_t, new_path: WasmPtr, new_path_len: u32, ) -> __wasi_errno_t { unimplemented!() } /// ### `path_open()` /// Open file located at the given path /// Inputs: /// - `__wasi_fd_t dirfd` /// The fd corresponding to the directory that the file is in /// - `__wasi_lookupflags_t dirflags` /// Flags specifying how the path will be resolved /// - `char *path` /// The path of the file or directory to open /// - `u32 path_len` /// The length of the `path` string /// - `__wasi_oflags_t o_flags` /// How the file will be opened /// - `__wasi_rights_t fs_rights_base` /// The rights of the created file descriptor /// - `__wasi_rights_t fs_rightsinheriting` /// The rights of file descriptors derived from the created file descriptor /// - `__wasi_fdflags_t fs_flags` /// The flags of the file descriptor /// Output: /// - `__wasi_fd_t* fd` /// The new file descriptor /// Possible Errors: /// - `__WASI_EACCES`, `__WASI_EBADF`, `__WASI_EFAULT`, `__WASI_EFBIG?`, `__WASI_EINVAL`, `__WASI_EIO`, `__WASI_ELOOP`, `__WASI_EMFILE`, `__WASI_ENAMETOOLONG?`, `__WASI_ENFILE`, `__WASI_ENOENT`, `__WASI_ENOTDIR`, `__WASI_EROFS`, and `__WASI_ENOTCAPABLE` pub fn path_open( ctx: &mut Ctx, dirfd: __wasi_fd_t, dirflags: __wasi_lookupflags_t, path: WasmPtr, path_len: u32, o_flags: __wasi_oflags_t, fs_rights_base: __wasi_rights_t, fs_rights_inheriting: __wasi_rights_t, fs_flags: __wasi_fdflags_t, fd: WasmPtr<__wasi_fd_t>, ) -> __wasi_errno_t { let memory = ctx.memory(0); if path_len > 1024 /* TODO: find actual upper bound on name size (also this is a path, not a name :think-fish:) */ { return __WASI_ENAMETOOLONG; } // check for __WASI_RIGHT_PATH_OPEN somewhere, probably via dirfd if let (Some(fd_cell), Some(path_cell)) = (fd.deref(memory), path.deref(memory, 0, path_len)) { // o_flags: // - __WASI_O_FLAG_CREAT (create if it does not exist) // - __WASI_O_DIRECTORY (fail if not dir) // - __WASI_O_EXCL (fail if file exists) // - __WASI_O_TRUNC (truncate size to 0) if (o_flags & __WASI_O_DIRECTORY) != 0 { // fail if fd is not a dir // need to check and be able to clean up } unimplemented!(); } else { __WASI_EFAULT } } pub fn path_readlink( ctx: &mut Ctx, fd: __wasi_fd_t, path: WasmPtr, path_len: u32, buf: WasmPtr, buf_len: u32, bufused: WasmPtr, ) -> __wasi_errno_t { unimplemented!() } pub fn path_remove_directory( ctx: &mut Ctx, fd: __wasi_fd_t, path: WasmPtr, path_len: u32, ) -> __wasi_errno_t { unimplemented!() } pub fn path_rename( ctx: &mut Ctx, old_fd: __wasi_fd_t, old_path: WasmPtr, old_path_len: u32, new_fd: __wasi_fd_t, new_path: WasmPtr, new_path_len: u32, ) -> __wasi_errno_t { unimplemented!() } pub fn path_symlink( ctx: &mut Ctx, old_path: WasmPtr, old_path_len: u32, fd: __wasi_fd_t, new_path: WasmPtr, new_path_len: u32, ) -> __wasi_errno_t { unimplemented!() } pub fn path_unlink_file( ctx: &mut Ctx, fd: __wasi_fd_t, path: WasmPtr, path_len: u32, ) -> __wasi_errno_t { unimplemented!() } pub fn poll_oneoff( ctx: &mut Ctx, in_: WasmPtr<__wasi_subscription_t, Array>, out_: WasmPtr<__wasi_event_t, Array>, nsubscriptions: u32, nevents: WasmPtr, ) -> __wasi_errno_t { unimplemented!() } pub fn proc_exit(ctx: &mut Ctx, rval: __wasi_exitcode_t) { unimplemented!() } pub fn proc_raise(ctx: &mut Ctx, sig: __wasi_signal_t) -> __wasi_errno_t { unimplemented!() } /// ### `random_get()` /// Fill buffer with high-quality random data. This function may be slow and block /// Inputs: /// - `void *buf` /// A pointer to a buffer where the random bytes will be written /// - `size_t buf_len` /// The number of bytes that will be written pub fn random_get(ctx: &mut Ctx, buf: WasmPtr, buf_len: u32) -> __wasi_errno_t { let mut rng = thread_rng(); let memory = ctx.memory(0); if let Some(buf) = buf.deref(memory, 0, buf_len) { for i in 0..(buf_len as usize) { let random_byte = rng.gen::(); buf[i].set(random_byte); } } else { return __WASI_EFAULT; } __WASI_ESUCCESS } /// ### `sched_yield()` /// Yields execution of the thread pub fn sched_yield(ctx: &mut Ctx) -> __wasi_errno_t { __WASI_ESUCCESS } pub fn sock_recv( ctx: &mut Ctx, sock: __wasi_fd_t, ri_data: WasmPtr<__wasi_iovec_t, Array>, ri_data_len: u32, ri_flags: __wasi_riflags_t, ro_datalen: WasmPtr, ro_flags: WasmPtr<__wasi_roflags_t>, ) -> __wasi_errno_t { unimplemented!() } pub fn sock_send( ctx: &mut Ctx, sock: __wasi_fd_t, si_data: WasmPtr<__wasi_ciovec_t, Array>, si_data_len: u32, si_flags: __wasi_siflags_t, so_datalen: WasmPtr, ) -> __wasi_errno_t { unimplemented!() } pub fn sock_shutdown(ctx: &mut Ctx, sock: __wasi_fd_t, how: __wasi_sdflags_t) -> __wasi_errno_t { unimplemented!() }