diff --git a/Cargo.toml b/Cargo.toml index e8fa5e9ac9..e2adda09ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ wasmtime-runtime = { path = "wasmtime-runtime" } wasmtime-jit = { path = "wasmtime-jit" } wasmtime-obj = { path = "wasmtime-obj" } wasmtime-wast = { path = "wasmtime-wast" } +wasmtime-wasi = { path = "wasmtime-wasi" } docopt = "1.0.1" serde = "1.0.75" serde_derive = "1.0.75" @@ -39,5 +40,7 @@ target-lexicon = { version = "0.3.0", default-features = false } pretty_env_logger = "0.3.0" file-per-thread-logger = "0.1.1" wabt = "0.7" +libc = "0.2.50" +errno = "0.2.4" [workspace] diff --git a/README.md b/README.md index d6e620e10d..7703cdf5d7 100644 --- a/README.md +++ b/README.md @@ -14,26 +14,27 @@ utility or as a library embedded in a larger application. [![Gitter chat](https://badges.gitter.im/CraneStation/CraneStation.svg)](https://gitter.im/CraneStation/Lobby) ![Minimum rustc 1.32](https://img.shields.io/badge/rustc-1.32+-green.svg) -*Wasmtime is complete enough to pass the WebAssembly spec testsuite.* Support for -system APIs is coming soon! +Wasmtime passes the WebAssembly spec testsuite, and supports a new system +API proposal called [WebAssembly System Interface], or WASI. -One goal for this project is to implement [CloudABI](https://cloudabi.org/) using -WebAssembly as the code format, provide [CloudABI system calls] as WebAssembly -host imports, and then port the [Rust CloudABI package] and [CloudABI libc] to it -to support Rust, C, C++, and other toolchains. +There are Rust C, and C++ toolchains that can compile programs with WASI. See +[here][WASI intro] for more information, and [here][WASI tutorial] for a +tutorial on compiling and running programs using WASI and wasmtime, as +well as an overview of the filesystem sandboxing system. -CloudABI is a natural complement for WebAssembly, since WebAssembly provides -sandboxing for code but doesn't have any builtin I/O, and CloudABI provides -sandboxed I/O. +Wasmtime does not yet implement Spectre mitiations, such as those being +pioneered [by](https://www.wasmjit.org/blog/spectre-mitigations-part-1.html) +[wasmjit](https://www.wasmjit.org/blog/spectre-mitigations-part-2.html), +however this is a subject of ongoing research. [CloudABI]: https://cloudabi.org/ -[CloudABI system calls]: https://github.com/NuxiNL/cloudabi#specification-of-the-abi -[Rust CloudABI package]: https://crates.io/crates/cloudabi -[CloudABI libc]: https://github.com/NuxiNL/cloudlibc +[WebAssembly System Interface]: docs/WASI-overview.md +[WASI intro]: docs/WASI-intro.md +[WASI tutorial]: docs/WASI-tutorial.md Additional goals for Wasmtime include: - - Support a variety of host APIs (not just CloudABI), with fast calling sequences, - and develop proposals for system calls in the WebAssembly + - Support a variety of host APIs (not just WASI Core), with fast calling sequences, + and develop proposals for additional API modules to be part of WASI. [Reference Sysroot](https://github.com/WebAssembly/reference-sysroot). - Implement the [proposed WebAssembly C API]. - Facilitate testing, experimentation, and development around the [Cranelift] and diff --git a/docs/WASI-api.md b/docs/WASI-api.md new file mode 100644 index 0000000000..f40899af15 --- /dev/null +++ b/docs/WASI-api.md @@ -0,0 +1,2305 @@ + + +# WASI Core API + +This is the API-level documentation for WASI Core. The function names +are prefixed with "\_\_wasi\_" to reflect how they are spelled in +flat-namespace contexts, however at the wasm module level, they are +unprefixed, because they're inside a module namespace (currently +"wasi\_unstable"). + +Functions that start with `__wasi_fd_` operate on file descriptors, +while functions that start with `__wasi_path_` operate on filesystem +paths, which are relative to directory file descriptors. + +Much inspiration and content here is derived from [CloudABI] and [POSIX], +though there are also several differences from CloudABI and POSIX. For +example, WASI Core has no concept of processes in the traditional Unix +sense. While wasm linear memories have some of the aspects of processes, +and it's possible to *emulate* the full semantics of processes on top of +them, this can sometimes be unnatural and inefficient. The goal for +WASI Core is to be a WebAssembly-native API that exposes APIs that fit +well into the underlying WebAssembly platform, rather than to directly +emulate other platforms. + +This is also a work in progress, and the API here is still evolving. + +[CloudABI]: https://github.com/NuxiNL/cloudabi +[POSIX]: http://pubs.opengroup.org/onlinepubs/9699919799/ + +## System calls + +- [`__wasi_args_get()`](#args_get) +- [`__wasi_args_sizes_get()`](#args_sizes_get) +- [`__wasi_clock_res_get()`](#clock_res_get) +- [`__wasi_clock_time_get()`](#clock_time_get) +- [`__wasi_environ_get()`](#environ_get) +- [`__wasi_environ_sizes_get()`](#environ_sizes_get) +- [`__wasi_fd_advise()`](#fd_advise) +- [`__wasi_fd_allocate()`](#fd_allocate) +- [`__wasi_fd_close()`](#fd_close) +- [`__wasi_fd_datasync()`](#fd_datasync) +- [`__wasi_fd_fdstat_get()`](#fd_fdstat_get) +- [`__wasi_fd_fdstat_set_flags()`](#fd_fdstat_set_flags) +- [`__wasi_fd_fdstat_set_rights()`](#fd_fdstat_set_rights) +- [`__wasi_fd_filestat_get()`](#fd_filestat_get) +- [`__wasi_fd_filestat_set_size()`](#fd_filestat_set_size) +- [`__wasi_fd_filestat_set_times()`](#fd_filestat_set_times) +- [`__wasi_fd_pread()`](#fd_pread) +- [`__wasi_fd_prestat_get()`](#fd_prestat_get) +- [`__wasi_fd_prestat_dir_name()`](#fd_prestat_dir_name) +- [`__wasi_fd_pwrite()`](#fd_pwrite) +- [`__wasi_fd_read()`](#fd_read) +- [`__wasi_fd_readdir()`](#fd_readdir) +- [`__wasi_fd_renumber()`](#fd_renumber) +- [`__wasi_fd_seek()`](#fd_seek) +- [`__wasi_fd_sync()`](#fd_sync) +- [`__wasi_fd_tell()`](#fd_tell) +- [`__wasi_fd_write()`](#fd_write) +- [`__wasi_path_create_directory()`](#path_create_directory) +- [`__wasi_path_filestat_get()`](#path_filestat_get) +- [`__wasi_path_filestat_set_times()`](#path_filestat_set_times) +- [`__wasi_path_link()`](#path_link) +- [`__wasi_path_open()`](#path_open) +- [`__wasi_path_readlink()`](#path_readlink) +- [`__wasi_path_remove_directory()`](#path_remove_directory) +- [`__wasi_path_rename()`](#path_rename) +- [`__wasi_path_symlink()`](#path_symlink) +- [`__wasi_path_unlink_file()`](#path_unlink_file) +- [`__wasi_poll_oneoff()`](#poll_oneoff) +- [`__wasi_proc_exit()`](#proc_exit) +- [`__wasi_proc_raise()`](#proc_raise) +- [`__wasi_random_get()`](#random_get) +- [`__wasi_sched_yield()`](#sched_yield) +- [`__wasi_sock_recv()`](#sock_recv) +- [`__wasi_sock_send()`](#sock_send) +- [`__wasi_sock_shutdown()`](#sock_shutdown) + +### `__wasi_args_get()` + +Read command-line argument data. + +The sizes of the buffers should match that returned by [`__wasi_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. + +### `__wasi_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. + +### `__wasi_clock_res_get()` + +Return the resolution of a clock. + +Implementations are required to provide a non-zero value for supported clocks. +For unsupported clocks, return [`__WASI_EINVAL`](#errno.inval). + +Note: This is similar to `clock_getres` in POSIX. + +Inputs: + +- [\_\_wasi\_clockid\_t](#clockid) clock\_id + + The clock for which to return the resolution. + +Outputs: + +- [\_\_wasi\_timestamp\_t](#timestamp) resolution + + The resolution of the clock. + +### `__wasi_clock_time_get()` + +Return the time value of a clock. + +Note: This is similar to `clock_gettime` in POSIX. + +Inputs: + +- [\_\_wasi\_clockid\_t](#clockid) clock\_id + + The clock for which to return the time. + +- [\_\_wasi\_timestamp\_t](#timestamp) precision + + The maximum lag (exclusive) that the returned + time value may have, compared to its actual + value. + +Outputs: + +- [\_\_wasi\_timestamp\_t](#timestamp) time + + The time value of the clock. + +### `__wasi_environ_get()` + +Read environment variable data. + +The sizes of the buffers should match that returned by [`__wasi_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. + +### `__wasi_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. + +### `__wasi_fd_advise()` + +Provide file advisory information on a file descriptor. + +Note: This is similar to `posix_fadvise` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor for the file for which to provide file advisory information. + +- [\_\_wasi\_filesize\_t](#filesize) offset + + The offset within the file to which the advisory applies. + +- [\_\_wasi\_filesize\_t](#filesize) len + + The length of the region to which the advisory applies. + +- [\_\_wasi\_advice\_t](#advice) advice + + The advice. + +### `__wasi_fd_allocate()` + +Force the allocation of space in a file. + +Note: This is similar to `posix_fallocate` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor for the file in which to allocate space. + +- [\_\_wasi\_filesize\_t](#filesize) offset + + The offset at which to start the allocation. + +- [\_\_wasi\_filesize\_t](#filesize) len + + The length of the area that is allocated. + +### `__wasi_fd_close()` + +Close a file descriptor. + +Note: This is similar to `close` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor to close. + +### `__wasi_fd_datasync()` + +Synchronize the data of a file to disk. + +Note: This is similar to `fdatasync` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor of the file to synchronize to disk. + +### `__wasi_fd_fdstat_get()` + +Get the attributes of a file descriptor. + +Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well +as additional fields. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor to inspect. + +- [\_\_wasi\_fdstat\_t](#fdstat) \*buf + + The buffer where the file descriptor's attributes are stored. + +### `__wasi_fd_fdstat_set_flags()` + +Adjust the flags associated with a file descriptor. + +Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor to operate on. + +- [\_\_wasi\_fdflags\_t](#fdflags) flags + + The desired values of the file descriptor + flags. + +### `__wasi_fd_fdstat_set_rights()` + +Adjust the rights associated with a file descriptor. + +This can only be used to remove rights, and returns +[`__WASI_ENOTCAPABLE`](#errno.notcapable) if called in a way that would attempt +to add rights. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor to operate on. + +- [\_\_wasi\_rights\_t](#rights) fs\_rights\_base and [\_\_wasi\_rights\_t](#rights) fs\_rights\_inheriting + + The desired rights of the file descriptor. + +### `__wasi_fd_filestat_get()` + +Return the attributes of an open file. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor to inspect. + +- [\_\_wasi\_filestat\_t](#filestat) \*buf + + The buffer where the file's attributes are + stored. + +### `__wasi_fd_filestat_set_size()` + +Adjust the size of an open file. If this increases the file's size, the extra +bytes are filled with zeros. + +Note: This is similar to `ftruncate` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + A file descriptor for the file to adjust. + +- [\_\_wasi\_filesize\_t](#filesize) st\_size + + The desired file size. + +### `__wasi_fd_filestat_set_times()` + +Adjust the timestamps of an open file or directory. + +Note: This is similar to `futimens` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor to operate on. + +- [\_\_wasi\_timestamp\_t](#timestamp) st\_atim + + The desired values of the data access timestamp. + +- [\_\_wasi\_timestamp\_t](#timestamp) st\_mtim + + The desired values of the data modification timestamp. + +- [\_\_wasi\_fstflags\_t](#fstflags) fst\_flags + + A bitmask indicating which timestamps to adjust. + +### `__wasi_fd_pread()` + +Read from a file descriptor, without using and updating the +file descriptor's offset. + +Note: This is similar to `preadv` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor from which to read data. + +- const [\_\_wasi\_iovec\_t](#iovec) \*iovs and size\_t iovs\_len + + List of scatter/gather vectors in which to store data. + +- [\_\_wasi\_filesize\_t](#filesize) offset + + The offset within the file at which to read. + +Outputs: + +- size\_t nread + + The number of bytes read. + +### `__wasi_fd_prestat_get()` + +Return a description of the given preopened file descriptor. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor about which to retrieve information. + +- [\_\_wasi\_prestat\_t](#prestat) \*buf + + The buffer where the description is stored. + +### `__wasi_fd_prestat_dir_name()` + +Return a description of the given preopened file descriptor. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor about which to retrieve information. + +- const char \*path and size\_t path\_len + + A buffer into which to write the preopened directory name. + +### `__wasi_fd_pwrite()` + +Write to a file descriptor, without using and updating the +file descriptor's offset. + +Note: This is similar to `pwritev` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor to which to write data. + +- const [\_\_wasi\_ciovec\_t](#ciovec) \*iovs and size\_t iovs\_len + + List of scatter/gather vectors from which to retrieve data. + +- [\_\_wasi\_filesize\_t](#filesize) offset + + The offset within the file at which to write. + +Outputs: + +- size\_t nwritten + + The number of bytes written. + +### `__wasi_fd_read()` + +Read from a file descriptor. + +Note: This is similar to `readv` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor from which to read data. + +- const [\_\_wasi\_iovec\_t](#iovec) \*iovs and size\_t iovs\_len + + List of scatter/gather vectors to which to store data. + +Outputs: + +- size\_t nread + + The number of bytes read. + +### `__wasi_fd_readdir()` + +Read directory entries from a directory. + +When successful, the contents of the output buffer consist of +a sequence of directory entries. Each directory entry consists +of a [`__wasi_dirent_t`](#dirent) object, followed by [`__wasi_dirent_t::d_namlen`](#dirent.d_namlen) bytes +holding the name of the directory entry. + +This function fills the output buffer as much as possible, +potentially truncating the last directory entry. This allows +the caller to grow its read buffer size in case it's too small +to fit a single large directory entry, or skip the oversized +directory entry. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The directory from which to read the directory + entries. + +- void \*buf and size\_t buf\_len + + The buffer where directory entries are stored. + +- [\_\_wasi\_dircookie\_t](#dircookie) cookie + + The location within the directory to start + reading. + +Outputs: + +- size\_t bufused + + The number of bytes stored in the read buffer. + If less than the size of the read buffer, the + end of the directory has been reached. + +### `__wasi_fd_renumber()` + +Atomically replace a file descriptor by renumbering another +file descriptor. + +Due to the strong focus on thread safety, this environment +does not provide a mechanism to duplicate or renumber a file +descriptor to an arbitrary number, like dup2(). This would be +prone to race conditions, as an actual file descriptor with the +same number could be allocated by a different thread at the same +time. + +This function provides a way to atomically renumber file +descriptors, which would disappear if dup2() were to be +removed entirely. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) from + + The file descriptor to renumber. + +- [\_\_wasi\_fd\_t](#fd) to + + The file descriptor to overwrite. + +### `__wasi_fd_seek()` + +Move the offset of a file descriptor. + +Note: This is similar to `lseek` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor to operate on. + +- [\_\_wasi\_filedelta\_t](#filedelta) offset + + The number of bytes to move. + +- [\_\_wasi\_whence\_t](#whence) whence + + The base from which the offset is relative. + +Outputs: + +- [\_\_wasi\_filesize\_t](#filesize) newoffset + + The new offset of the file descriptor, + relative to the start of the file. + +### `__wasi_fd_sync()` + +Synchronize the data and metadata of a file to disk. + +Note: This is similar to `fsync` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor of the file containing the data + and metadata to synchronize to disk. + +### `__wasi_fd_tell()` + +Return the current offset of a file descriptor. + +Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor to inspect. + +Outputs: + +- [\_\_wasi\_filesize\_t](#filesize) offset + + The current offset of the file descriptor, relative to the start of the file. + +### `__wasi_fd_write()` + +Write to a file descriptor. + +Note: This is similar to `writev` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor to which to write data. + +- const [\_\_wasi\_ciovec\_t](#ciovec) \*iovs and size\_t iovs\_len + + List of scatter/gather vectors from which to retrieve data. + +Outputs: + +- size\_t nwritten + + The number of bytes written. + +### `__wasi_path_create_directory()` + +Create a directory. + +Note: This is similar to `mkdirat` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The working directory at which the resolution of the path starts. + +- const char \*path and size\_t path\_len + + The path at which to create the directory. + +### `__wasi_path_filestat_get()` + +Return the attributes of a file or directory. + +Note: This is similar to `stat` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The working directory at which the resolution of the path starts. + +- [\_\_wasi\_lookupflags\_t](#lookupflags) flags + + Flags determining the method of how the path is resolved. + +- const char \*path and size\_t path\_len + + The path of the file or directory to inspect. + +- [\_\_wasi\_filestat\_t](#filestat) \*buf + + The buffer where the file's attributes are + stored. + +### `__wasi_path_filestat_set_times()` + +Adjust the timestamps of a file or directory. + +Note: This is similar to `utimensat` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The working directory at which the resolution of the path starts. + +- [\_\_wasi\_lookupflags\_t](#lookupflags) flags + + Flags determining the method of how the path is resolved. + +- const char \*path and size\_t path\_len + + The path of the file or directory to operate on. + +- [\_\_wasi\_timestamp\_t](#timestamp) st\_atim + + The desired values of the data access timestamp. + +- [\_\_wasi\_timestamp\_t](#timestamp) st\_mtim + + The desired values of the data modification timestamp. + +- [\_\_wasi\_fstflags\_t](#fstflags) fst\_flags + + A bitmask indicating which timestamps to adjust. + +### `__wasi_path_link()` + +Create a hard link. + +Note: This is similar to `linkat` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) old\_fd + + The working directory at which the resolution of the old path starts. + +- [\_\_wasi\_lookupflags\_t](#lookupflags) old\_flags + + Flags determining the method of how the path is resolved. + +- const char \*old\_path and size\_t old\_path\_len + + The source path from which to link. + +- [\_\_wasi\_fd\_t](#fd) new\_fd + + The working directory at which the resolution of the new path starts. + +- const char \*new\_path and size\_t new\_path\_len + + The destination path at which to create the hard link. + +### `__wasi_path_open()` + +Open a file or directory. + +Note: This is similar to `openat` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) dirfd + + The working directory at which the resolution of the path starts. + +- [\_\_wasi\_lookupflags\_t](#lookupflags) dirflags + + Flags determining the method of how the path is resolved. + +- const char \*path and size\_t path\_len + + The path of the file or directory to open. + +- [\_\_wasi\_oflags\_t](#oflags) o_flags + + The method by which to open the file. + +- [\_\_wasi\_rights\_t](#rights) fs\_rights\_base and [\_\_wasi\_rights\_t](#rights) fs\_rights\_inheriting + + The initial rights of the newly created file descriptor. The + implementation is allowed to return a file descriptor with fewer + rights than specified, if and only if those rights do not apply + to the type of file being opened. + + The *base* rights are rights that will apply to operations using + the file descriptor itself, while the *inheriting* rights are + rights that apply to file descriptors derived from it. + +- [\_\_wasi\_fdflags\_t](#fdflags) fs\_flags + + The initial flags of the file descriptor. + +Outputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor of the file that has been + opened. + +### `__wasi_path_readlink()` + +Read the contents of a symbolic link. + +Note: This is similar to `readlinkat` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The working directory at which the resolution of the path starts. + +- const char \*path and size\_t path\_len + + The path of the symbolic link from which to read. + +- char \*buf and size\_t buf\_len + + The buffer to which to write the contents of the symbolic link. + +Outputs: + +- size\_t bufused + + The number of bytes placed in the buffer. + +### `__wasi_path_remove_directory()` + +Remove a directory. + +Return [`__WASI_ENOTEMPTY`](#errno.notempty) if the directory is not empty. + +Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The working directory at which the resolution of the path starts. + +- const char \*path and size\_t path\_len + + The path to a directory to remove. + +### `__wasi_path_rename()` + +Rename a file or directory. + +Note: This is similar to `renameat` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) old\_fd + + The working directory at which the resolution of the old path starts. + +- const char \*old\_path and size\_t old\_path\_len + + The source path of the file or directory to rename. + +- [\_\_wasi\_fd\_t](#fd) new\_fd + + The working directory at which the resolution of the new path starts. + +- const char \*new\_path and size\_t new\_path\_len + + The destination path to which to rename the file or directory. + +### `__wasi_path_symlink()` + +Create a symbolic link. + +Note: This is similar to `symlinkat` in POSIX. + +Inputs: + +- const char \*old\_path and size\_t old_path\_len + + The contents of the symbolic link. + +- [\_\_wasi\_fd\_t](#fd) fd + + The working directory at which the resolution of the path starts. + +- const char \*new\_path and size\_t new\_path\_len + + The destination path at which to create the symbolic link. + +### `__wasi_path_unlink_file()` + +Unlink a file. + +Return [`__WASI_EISDIR`](#errno.isdir) if the path refers to a directory. + +Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The working directory at which the resolution of the path starts. + +- const char \*path and size\_t path\_len + + The path to a file to unlink. + +### `__wasi_poll_oneoff()` + +Concurrently poll for the occurrence of a set of events. + +Inputs: + +- const [\_\_wasi\_subscription\_t](#subscription) \*in + + The events to which to subscribe. + +- [\_\_wasi\_event\_t](#event) \*out + + The events that have occurred. + +- size\_t nsubscriptions + + Both the number of subscriptions and events. + +Outputs: + +- size\_t nevents + + The number of events stored. + +### `__wasi_proc_exit()` + +Terminate the process normally. An exit code of 0 indicates successful +termination of the program. The meanings of other values is dependent on +the environment. + +Note: This is similar to `_Exit` in POSIX. + +Inputs: + +- [\_\_wasi\_exitcode\_t](#exitcode) rval + + The exit code returned by the process. + +Does not return. + +### `__wasi_proc_raise()` + +Send a signal to the process of the calling thread. + +Note: This is similar to `raise` in POSIX. + +Inputs: + +- [\_\_wasi\_signal\_t](#signal) sig + + The signal condition to trigger. + +### `__wasi_random_get()` + +Write high-quality random data into a buffer. + +This function may execute slowly, so when large mounts of random +data are required, it's advisable to use this function to seed a +pseudo-random number generator, rather than to provide the +random data directly. + +Inputs: + +- void \*buf and size\_t buf\_len + + The buffer to fill with random data. + +### `__wasi_sched_yield()` + +Temporarily yield execution of the calling thread. + +Note: This is similar to `sched_yield` in POSIX. + +### `__wasi_sock_recv()` + +Receive a message from a socket. + +Note: This is similar to `recv` in POSIX, though it also supports reading +the data into multiple buffers in the manner of `readv`. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) sock + + The socket on which to receive data. + +- const [\_\_wasi\_iovec\_t](#iovec) \*ri\_data and size\_t ri\_data\_len + + List of scatter/gather vectors to which to store data. + +- [\_\_wasi\_riflags\_t](#riflags) ri\_flags + + Message flags. + +Outputs: + +- size\_t ro\_datalen + + Number of bytes stored in [`ri_data`](#sock_recv.ri_data). + +- [\_\_wasi\_roflags\_t](#roflags) ro\_flags + + Message flags. + +### `__wasi_sock_send()` + +Send a message on a socket. + +Note: This is similar to `send` in POSIX, though it also supports writing +the data from multiple buffers in the manner of `writev`. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) sock + + The socket on which to send data. + +- const [\_\_wasi\_ciovec\_t](#ciovec) \*si\_data and size\_t si\_data\_len + + List of scatter/gather vectors to which to retrieve data + +- [\_\_wasi\_siflags\_t](#siflags) si\_flags + + Message flags. + +Outputs: + +- size\_t so\_datalen + + Number of bytes transmitted. + +### `__wasi_sock_shutdown()` + +Shut down socket send and receive channels. + +Note: This is similar to `shutdown` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) sock + + The socket on which to shutdown channels. + +- [\_\_wasi\_sdflags\_t](#sdflags) how + + Which channels on the socket to shut down. + +## Types + +### `__wasi_advice_t` (`uint8_t`) + +File or memory access pattern advisory information. + +Used by [`__wasi_fd_advise()`](#fd_advise). + +Possible values: + +- **`__WASI_ADVICE_DONTNEED`** + + The application expects that it will not access the + specified data in the near future. + +- **`__WASI_ADVICE_NOREUSE`** + + The application expects to access the specified data + once and then not reuse it thereafter. + +- **`__WASI_ADVICE_NORMAL`** + + The application has no advice to give on its behavior + with respect to the specified data. + +- **`__WASI_ADVICE_RANDOM`** + + The application expects to access the specified data + in a random order. + +- **`__WASI_ADVICE_SEQUENTIAL`** + + The application expects to access the specified data + sequentially from lower offsets to higher offsets. + +- **`__WASI_ADVICE_WILLNEED`** + + The application expects to access the specified data + in the near future. + +### `__wasi_ciovec_t` (`struct`) + +A region of memory for scatter/gather writes. + +Used by [`__wasi_fd_pwrite()`](#fd_pwrite), [`__wasi_fd_write()`](#fd_write), and [`__wasi_sock_send()`](#sock_send). + +Members: + +- const void \*buf and size\_t buf\_len + + The address and length of the buffer to be written. + +### `__wasi_clockid_t` (`uint32_t`) + +Identifiers for clocks. + +Used by [`__wasi_subscription_t`](#subscription), [`__wasi_clock_res_get()`](#clock_res_get), and [`__wasi_clock_time_get()`](#clock_time_get). + +Possible values: + +- **`__WASI_CLOCK_MONOTONIC`** + + The store-wide monotonic clock, which is defined as a + clock measuring real time, whose value cannot be + adjusted and which cannot have negative clock jumps. + + The epoch of this clock is undefined. The absolute + time value of this clock therefore has no meaning. + +- **`__WASI_CLOCK_PROCESS_CPUTIME_ID`** + + The CPU-time clock associated with the current + process. + +- **`__WASI_CLOCK_REALTIME`** + + The clock measuring real time. Time value + zero corresponds with 1970-01-01T00:00:00Z. + +- **`__WASI_CLOCK_THREAD_CPUTIME_ID`** + + The CPU-time clock associated with the current thread. + +### `__wasi_device_t` (`uint64_t`) + +Identifier for a device containing a file system. Can be used +in combination with [`__wasi_inode_t`](#inode) to uniquely identify a file or +directory in the filesystem. + +Used by [`__wasi_filestat_t`](#filestat). + +### `__wasi_dircookie_t` (`uint64_t`) + +A reference to the offset of a directory entry. + +Used by [`__wasi_dirent_t`](#dirent) and [`__wasi_fd_readdir()`](#fd_readdir). + +Special values: + +- **`__WASI_DIRCOOKIE_START`** + + Permanent reference to the first directory entry + within a directory. + +### `__wasi_dirent_t` (`struct`) + +A directory entry. + +Members: + +- [\_\_wasi\_dircookie\_t](#dircookie) d\_next + + The offset of the next directory entry stored in this + directory. + +- [\_\_wasi\_inode\_t](#inode) d\_ino + + The serial number of the file referred to by this + directory entry. + +- uint32\_t d\_namlen + + The length of the name of the directory entry. + +- [\_\_wasi\_filetype\_t](#filetype) d\_type + + The type of the file referred to by this directory + entry. + +### `__wasi_errno_t` (`uint16_t`) + +Error codes returned by functions. + +Not all of these error codes are returned by the functions +provided by this API; some are used in higher-level library layers, +and others are provided merely for alignment with POSIX. + +Used by [`__wasi_event_t`](#event). + +Possible values: + +- **`__WASI_ESUCCESS`** + + No error occurred. System call completed successfully. + +- **`__WASI_E2BIG`** + + Argument list too long. + +- **`__WASI_EACCES`** + + Permission denied. + +- **`__WASI_EADDRINUSE`** + + Address in use. + +- **`__WASI_EADDRNOTAVAIL`** + + Address not available. + +- **`__WASI_EAFNOSUPPORT`** + + Address family not supported. + +- **`__WASI_EAGAIN`** + + Resource unavailable, or operation would block. + +- **`__WASI_EALREADY`** + + Connection already in progress. + +- **`__WASI_EBADF`** + + Bad file descriptor. + +- **`__WASI_EBADMSG`** + + Bad message. + +- **`__WASI_EBUSY`** + + Device or resource busy. + +- **`__WASI_ECANCELED`** + + Operation canceled. + +- **`__WASI_ECHILD`** + + No child processes. + +- **`__WASI_ECONNABORTED`** + + Connection aborted. + +- **`__WASI_ECONNREFUSED`** + + Connection refused. + +- **`__WASI_ECONNRESET`** + + Connection reset. + +- **`__WASI_EDEADLK`** + + Resource deadlock would occur. + +- **`__WASI_EDESTADDRREQ`** + + Destination address required. + +- **`__WASI_EDOM`** + + Mathematics argument out of domain of function. + +- **`__WASI_EDQUOT`** + + Reserved. + +- **`__WASI_EEXIST`** + + File exists. + +- **`__WASI_EFAULT`** + + Bad address. + +- **`__WASI_EFBIG`** + + File too large. + +- **`__WASI_EHOSTUNREACH`** + + Host is unreachable. + +- **`__WASI_EIDRM`** + + Identifier removed. + +- **`__WASI_EILSEQ`** + + Illegal byte sequence. + +- **`__WASI_EINPROGRESS`** + + Operation in progress. + +- **`__WASI_EINTR`** + + Interrupted function. + +- **`__WASI_EINVAL`** + + Invalid argument. + +- **`__WASI_EIO`** + + I/O error. + +- **`__WASI_EISCONN`** + + Socket is connected. + +- **`__WASI_EISDIR`** + + Is a directory. + +- **`__WASI_ELOOP`** + + Too many levels of symbolic links. + +- **`__WASI_EMFILE`** + + File descriptor value too large. + +- **`__WASI_EMLINK`** + + Too many links. + +- **`__WASI_EMSGSIZE`** + + Message too large. + +- **`__WASI_EMULTIHOP`** + + Reserved. + +- **`__WASI_ENAMETOOLONG`** + + Filename too long. + +- **`__WASI_ENETDOWN`** + + Network is down. + +- **`__WASI_ENETRESET`** + + Connection aborted by network. + +- **`__WASI_ENETUNREACH`** + + Network unreachable. + +- **`__WASI_ENFILE`** + + Too many files open in system. + +- **`__WASI_ENOBUFS`** + + No buffer space available. + +- **`__WASI_ENODEV`** + + No such device. + +- **`__WASI_ENOENT`** + + No such file or directory. + +- **`__WASI_ENOEXEC`** + + Executable file format error. + +- **`__WASI_ENOLCK`** + + No locks available. + +- **`__WASI_ENOLINK`** + + Reserved. + +- **`__WASI_ENOMEM`** + + Not enough space. + +- **`__WASI_ENOMSG`** + + No message of the desired type. + +- **`__WASI_ENOPROTOOPT`** + + Protocol not available. + +- **`__WASI_ENOSPC`** + + No space left on device. + +- **`__WASI_ENOSYS`** + + Function not supported. + +- **`__WASI_ENOTCONN`** + + The socket is not connected. + +- **`__WASI_ENOTDIR`** + + Not a directory or a symbolic link to a directory. + +- **`__WASI_ENOTEMPTY`** + + Directory not empty. + +- **`__WASI_ENOTRECOVERABLE`** + + State not recoverable. + +- **`__WASI_ENOTSOCK`** + + Not a socket. + +- **`__WASI_ENOTSUP`** + + Not supported, or operation not supported on socket. + +- **`__WASI_ENOTTY`** + + Inappropriate I/O control operation. + +- **`__WASI_ENXIO`** + + No such device or address. + +- **`__WASI_EOVERFLOW`** + + Value too large to be stored in data type. + +- **`__WASI_EOWNERDEAD`** + + Previous owner died. + +- **`__WASI_EPERM`** + + Operation not permitted. + +- **`__WASI_EPIPE`** + + Broken pipe. + +- **`__WASI_EPROTO`** + + Protocol error. + +- **`__WASI_EPROTONOSUPPORT`** + + Protocol not supported. + +- **`__WASI_EPROTOTYPE`** + + Protocol wrong type for socket. + +- **`__WASI_ERANGE`** + + Result too large. + +- **`__WASI_EROFS`** + + Read-only file system. + +- **`__WASI_ESPIPE`** + + Invalid seek. + +- **`__WASI_ESRCH`** + + No such process. + +- **`__WASI_ESTALE`** + + Reserved. + +- **`__WASI_ETIMEDOUT`** + + Connection timed out. + +- **`__WASI_ETXTBSY`** + + Text file busy. + +- **`__WASI_EXDEV`** + + Cross-device link. + +- **`__WASI_ENOTCAPABLE`** + + Extension: Capabilities insufficient. + +### `__wasi_event_t` (`struct`) + +An event that occurred. + +Used by [`__wasi_poll_oneoff()`](#poll_oneoff). + +Members: + +- [\_\_wasi\_userdata\_t](#userdata) userdata + + User-provided value that got attached to + [`__wasi_subscription_t::userdata`](#subscription.userdata). + +- [\_\_wasi\_errno\_t](#errno) error + + If non-zero, an error that occurred while processing + the subscription request. + +- [\_\_wasi\_eventtype\_t](#eventtype) type + + The type of the event that occurred. + +- When `type` is [`__WASI_EVENTTYPE_FD_READ`](#eventtype.fd_read) or [`__WASI_EVENTTYPE_FD_WRITE`](#eventtype.fd_write): + + - **`u.fd_readwrite`** + + - [\_\_wasi\_filesize\_t](#filesize) nbytes + + The number of bytes available for reading or writing. + + - [\_\_wasi\_eventrwflags\_t](#eventrwflags) flags + + The state of the file descriptor. + +### `__wasi_eventrwflags_t` (`uint16_t` bitfield) + +The state of the file descriptor subscribed to with +[`__WASI_EVENTTYPE_FD_READ`](#eventtype.fd_read) or [`__WASI_EVENTTYPE_FD_WRITE`](#eventtype.fd_write). + +Used by [`__wasi_event_t`](#event). + +Possible values: + +- **`__WASI_EVENT_FD_READWRITE_HANGUP`** + + The peer of this socket has closed or disconnected. + +### `__wasi_eventtype_t` (`uint8_t`) + +Type of a subscription to an event or its occurrence. + +Used by [`__wasi_event_t`](#event) and [`__wasi_subscription_t`](#subscription). + +Possible values: + +- **`__WASI_EVENTTYPE_CLOCK`** + + The time value of clock [`__wasi_subscription_t::u.clock.clock_id`](#subscription.u.clock.clock_id) + has reached timestamp [`__wasi_subscription_t::u.clock.timeout`](#subscription.u.clock.timeout). + +- **`__WASI_EVENTTYPE_FD_READ`** + + File descriptor [`__wasi_subscription_t::u.fd_readwrite.fd`](#subscription.u.fd_readwrite.fd) has + data available for reading. This event always triggers + for regular files. + +- **`__WASI_EVENTTYPE_FD_WRITE`** + + File descriptor [`__wasi_subscription_t::u.fd_readwrite.fd`](#subscription.u.fd_readwrite.fd) has + capacity available for writing. This event always + triggers for regular files. + +### `__wasi_exitcode_t` (`uint32_t`) + +Exit code generated by a process when exiting. + +Used by [`__wasi_proc_exit()`](#proc_exit). + +### `__wasi_fd_t` (`uint32_t`) + +A file descriptor number. + +Used by many functions in this API. + +As in POSIX, three file descriptor numbers are provided to instances +on startup -- 0, 1, and 2, (a.k.a. `STDIN_FILENO`, `STDOUT_FILENO`, +and `STDERR_FILENO`). + +Other than these, WASI implementations are not required to allocate +new file descriptors in ascending order. + +### `__wasi_fdflags_t` (`uint16_t` bitfield) + +File descriptor flags. + +Used by [`__wasi_fdstat_t`](#fdstat), [`__wasi_fd_fdstat_set_flags()`](#fd_fdstat_set_flags), and [`__wasi_path_open()`](#path_open). + +Possible values: + +- **`__WASI_FDFLAG_APPEND`** + + Append mode: Data written to the file is always + appended to the file's end. + +- **`__WASI_FDFLAG_DSYNC`** + + Write according to synchronized I/O data integrity + completion. Only the data stored in the file is + synchronized. + +- **`__WASI_FDFLAG_NONBLOCK`** + + Non-blocking mode. + +- **`__WASI_FDFLAG_RSYNC`** + + Synchronized read I/O operations. + +- **`__WASI_FDFLAG_SYNC`** + + Write according to synchronized I/O file integrity completion. + In addition to synchronizing the data stored in the file, the + implementation may also synchronously update the file's metadata. + +### `__wasi_fdstat_t` (`struct`) + +File descriptor attributes. + +Used by [`__wasi_fd_fdstat_get()`](#fd_fdstat_get). + +Members: + +- [\_\_wasi\_filetype\_t](#filetype) fs\_filetype + + File type. + +- [\_\_wasi\_fdflags\_t](#fdflags) fs\_flags + + File descriptor flags. + +- [\_\_wasi\_rights\_t](#rights) fs\_rights\_base + + Rights that apply to this file descriptor. + +- [\_\_wasi\_rights\_t](#rights) fs\_rights\_inheriting + + Maximum set of rights that may be installed on new + file descriptors that are created through this file + descriptor, e.g., through [`__wasi_path_open()`](#path_open). + +### `__wasi_filedelta_t` (`int64_t`) + +Relative offset within a file. + +Used by [`__wasi_fd_seek()`](#fd_seek). + +### `__wasi_filesize_t` (`uint64_t`) + +Non-negative file size or length of a region within a file. + +Used by [`__wasi_event_t`](#event), [`__wasi_filestat_t`](#filestat), [`__wasi_fd_pread()`](#fd_pread), [`__wasi_fd_pwrite()`](#fd_pwrite), [`__wasi_fd_seek()`](#fd_seek), [`__wasi_path_tell()`](#path_tell), [`__wasi_fd_advise()`](#fd_advise), [`__wasi_fd_allocate()`](#fd_allocate), and [`__wasi_fd_filestat_set_size()`](#fd_filestat_set_size). + +### `__wasi_filestat_t` (`struct`) + +File attributes. + +Used by [`__wasi_fd_filestat_get()`](#fd_filestat_get) and [`__wasi_path_filestat_get()`](#path_filestat_get). + +Members: + +- [\_\_wasi\_device\_t](#device) st\_dev + + Device ID of device containing the file. + +- [\_\_wasi\_inode\_t](#inode) st\_ino + + File serial number. + +- [\_\_wasi\_filetype\_t](#filetype) st\_filetype + + File type. + +- [\_\_wasi\_linkcount\_t](#linkcount) st\_nlink + + Number of hard links to the file. + +- [\_\_wasi\_filesize\_t](#filesize) st\_size + + For regular files, the file size in bytes. For + symbolic links, the length in bytes of the pathname + contained in the symbolic link. + +- [\_\_wasi\_timestamp\_t](#timestamp) st\_atim + + Last data access timestamp. + +- [\_\_wasi\_timestamp\_t](#timestamp) st\_mtim + + Last data modification timestamp. + +- [\_\_wasi\_timestamp\_t](#timestamp) st\_ctim + + Last file status change timestamp. + +### `__wasi_filetype_t` (`uint8_t`) + +The type of a file descriptor or file. + +Used by [`__wasi_dirent_t`](#dirent), [`__wasi_fdstat_t`](#fdstat), and [`__wasi_filestat_t`](#filestat). + +Possible values: + +- **`__WASI_FILETYPE_UNKNOWN`** + + The type of the file descriptor or file is unknown or + is different from any of the other types specified. + +- **`__WASI_FILETYPE_BLOCK_DEVICE`** + + The file descriptor or file refers to a block device + inode. + +- **`__WASI_FILETYPE_CHARACTER_DEVICE`** + + The file descriptor or file refers to a character + device inode. + +- **`__WASI_FILETYPE_DIRECTORY`** + + The file descriptor or file refers to a directory + inode. + +- **`__WASI_FILETYPE_REGULAR_FILE`** + + The file descriptor or file refers to a regular file + inode. + +- **`__WASI_FILETYPE_SOCKET_DGRAM`** + + The file descriptor or file refers to a datagram + socket. + +- **`__WASI_FILETYPE_SOCKET_STREAM`** + + The file descriptor or file refers to a byte-stream + socket. + +- **`__WASI_FILETYPE_SYMBOLIC_LINK`** + + The file refers to a symbolic link inode. + +### `__wasi_fstflags_t` (`uint16_t` bitfield) + +Which file time attributes to adjust. + +Used by [`__wasi_fd_filestat_set_times()`](#fd_filestat_set_times) and [`__wasi_path_filestat_set_times()`](#path_filestat_set_times). + +Possible values: + +- **`__WASI_FILESTAT_SET_ATIM`** + + Adjust the last data access timestamp to the value + stored in [`__wasi_filestat_t::st_atim`](#filestat.st_atim). + +- **`__WASI_FILESTAT_SET_ATIM_NOW`** + + Adjust the last data access timestamp to the time + of clock [`__WASI_CLOCK_REALTIME`](#clockid.realtime). + +- **`__WASI_FILESTAT_SET_MTIM`** + + Adjust the last data modification timestamp to the + value stored in [`__wasi_filestat_t::st_mtim`](#filestat.st_mtim). + +- **`__WASI_FILESTAT_SET_MTIM_NOW`** + + Adjust the last data modification timestamp to the + time of clock [`__WASI_CLOCK_REALTIME`](#clockid.realtime). + +### `__wasi_inode_t` (`uint64_t`) + +File serial number that is unique within its file system. + +Used by [`__wasi_dirent_t`](#dirent) and [`__wasi_filestat_t`](#filestat). + +### `__wasi_iovec_t` (`struct`) + +A region of memory for scatter/gather reads. + +Used by [`__wasi_fd_pread()`](#fd_pread), [`__wasi_fd_read()`](#fd_read), and [`__wasi_sock_recv()`](#sock_recv). + +Members: + +- void \*buf and size\_t buf\_len + + The address and length of the buffer to be filled. + +### `__wasi_linkcount_t` (`uint32_t`) + +Number of hard links to an inode. + +Used by [`__wasi_filestat_t`](#filestat). + +### `__wasi_lookupflags_t` (`uint32_t` bitfield) + +Flags determining the method of how paths are resolved. + +Used by [`__wasi_path_filestat_get()`](#path_filestat_get), [`__wasi_path_filestat_set_times()`](#path_filestat_set_times), [`__wasi_path_link()`](#path_link), and [`__wasi_path_open()`](#path_open). + +Possible values: + +- **`__WASI_LOOKUP_SYMLINK_FOLLOW`** + + As long as the resolved path corresponds to a symbolic + link, it is expanded. + +### `__wasi_oflags_t` (`uint16_t` bitfield) + +Open flags used by [`__wasi_path_open()`](#path_open). + +Used by [`__wasi_path_open()`](#path_open). + +Possible values: + +- **`__WASI_O_CREAT`** + + Create file if it does not exist. + +- **`__WASI_O_DIRECTORY`** + + Fail if not a directory. + +- **`__WASI_O_EXCL`** + + Fail if file already exists. + +- **`__WASI_O_TRUNC`** + + Truncate file to size 0. + +### `__wasi_riflags_t` (`uint16_t` bitfield) + +Flags provided to [`__wasi_sock_recv()`](#sock_recv). + +Used by [`__wasi_sock_recv()`](#sock_recv). + +Possible values: + +- **`__WASI_SOCK_RECV_PEEK`** + + Returns the message without removing it from the + socket's receive queue. + +- **`__WASI_SOCK_RECV_WAITALL`** + + On byte-stream sockets, block until the full amount + of data can be returned. + +### `__wasi_rights_t` (`uint64_t` bitfield) + +File descriptor rights, determining which actions may be +performed. + +Used by [`__wasi_fdstat_t`](#fdstat), [`__wasi_fd_fdstat_set_rights()`](#fd_fdstat_set_rights), and [`__wasi_path_open()`](#path_open). + +Possible values: + +- **`__WASI_RIGHT_FD_DATASYNC`** + + The right to invoke [`__wasi_fd_datasync()`](#fd_datasync). + + If [`__WASI_RIGHT_PATH_OPEN`](#rights.path_open) is set, includes the right to + invoke [`__wasi_path_open()`](#path_open) with [`__WASI_FDFLAG_DSYNC`](#fdflags.dsync). + +- **`__WASI_RIGHT_FD_READ`** + + The right to invoke [`__wasi_fd_read()`](#fd_read) and [`__wasi_sock_recv()`](#sock_recv). + + If [`__WASI_RIGHT_FD_SEEK`](#rights.fd_seek) is set, includes the right to invoke + [`__wasi_fd_pread()`](#fd_pread). + +- **`__WASI_RIGHT_FD_SEEK`** + + The right to invoke [`__wasi_fd_seek()`](#fd_seek). This flag implies + [`__WASI_RIGHT_FD_TELL`](#rights.fd_tell). + +- **`__WASI_RIGHT_FD_FDSTAT_SET_FLAGS`** + + The right to invoke [`__wasi_fd_fdstat_set_flags()`](#fd_fdstat_set_flags). + +- **`__WASI_RIGHT_FD_SYNC`** + + The right to invoke [`__wasi_fd_sync()`](#fd_sync). + + If [`__WASI_RIGHT_PATH_OPEN`](#rights.path_open) is set, includes the right to + invoke [`__wasi_path_open()`](#path_open) with [`__WASI_FDFLAG_RSYNC`](#fdflags.rsync) and + [`__WASI_FDFLAG_DSYNC`](#fdflags.dsync). + +- **`__WASI_RIGHT_FD_TELL`** + + The right to invoke [`__wasi_fd_seek()`](#fd_seek) in such a way that the + file offset remains unaltered (i.e., [`__WASI_WHENCE_CUR`](#whence.cur) with + offset zero), or to invoke [`__wasi_fd_tell()`](#fd_tell). + +- **`__WASI_RIGHT_FD_WRITE`** + + The right to invoke [`__wasi_fd_write()`](#fd_write) and [`__wasi_sock_send()`](#sock_send). + + If [`__WASI_RIGHT_FD_SEEK`](#rights.fd_seek) is set, includes the right to + invoke [`__wasi_fd_pwrite()`](#fd_pwrite). + +- **`__WASI_RIGHT_FD_ADVISE`** + + The right to invoke [`__wasi_fd_advise()`](#fd_advise). + +- **`__WASI_RIGHT_FD_ALLOCATE`** + + The right to invoke [`__wasi_fd_allocate()`](#fd_allocate). + +- **`__WASI_RIGHT_PATH_CREATE_DIRECTORY`** + + The right to invoke [`__wasi_path_create_directory()`](#path_create_directory). + +- **`__WASI_RIGHT_PATH_CREATE_FILE`** + + If [`__WASI_RIGHT_PATH_OPEN`](#rights.path_open) is set, the right to invoke + [`__wasi_path_open()`](#path_open) with [`__WASI_O_CREAT`](#oflags.creat). + +- **`__WASI_RIGHT_PATH_LINK_SOURCE`** + + The right to invoke [`__wasi_path_link()`](#path_link) with the file + descriptor as the source directory. + +- **`__WASI_RIGHT_PATH_LINK_TARGET`** + + The right to invoke [`__wasi_path_link()`](#path_link) with the file + descriptor as the target directory. + +- **`__WASI_RIGHT_PATH_OPEN`** + + The right to invoke [`__wasi_path_open()`](#path_open). + +- **`__WASI_RIGHT_FD_READDIR`** + + The right to invoke [`__wasi_fd_readdir()`](#fd_readdir). + +- **`__WASI_RIGHT_PATH_READLINK`** + + The right to invoke [`__wasi_path_readlink()`](#path_readlink). + +- **`__WASI_RIGHT_PATH_RENAME_SOURCE`** + + The right to invoke [`__wasi_path_rename()`](#path_rename) with the file + descriptor as the source directory. + +- **`__WASI_RIGHT_PATH_RENAME_TARGET`** + + The right to invoke [`__wasi_path_rename()`](#path_rename) with the file + descriptor as the target directory. + +- **`__WASI_RIGHT_PATH_FILESTAT_GET`** + + The right to invoke [`__wasi_path_filestat_get()`](#path_filestat_get). + +- **`__WASI_RIGHT_PATH_FILESTAT_SET_SIZE`** + + The right to change a file's size (there is no `__wasi_path_filestat_set_size()`). + + If [`__WASI_RIGHT_PATH_OPEN`](#rights.path_open) is set, includes the right to + invoke [`__wasi_path_open()`](#path_open) with [`__WASI_O_TRUNC`](#oflags.trunc). + +- **`__WASI_RIGHT_PATH_FILESTAT_SET_TIMES`** + + The right to invoke [`__wasi_path_filestat_set_times()`](#path_filestat_set_times). + +- **`__WASI_RIGHT_FD_FILESTAT_GET`** + + The right to invoke [`__wasi_fd_filestat_get()`](#fd_filestat_get). + +- **`__WASI_RIGHT_FD_FILESTAT_SET_SIZE`** + + The right to invoke [`__wasi_fd_filestat_set_size()`](#fd_filestat_set_size). + +- **`__WASI_RIGHT_FD_FILESTAT_SET_TIMES`** + + The right to invoke [`__wasi_fd_filestat_set_times()`](#fd_filestat_set_times). + +- **`__WASI_RIGHT_PATH_SYMLINK`** + + The right to invoke [`__wasi_path_symlink()`](#path_symlink). + +- **`__WASI_RIGHT_PATH_UNLINK_FILE`** + + The right to invoke [`__wasi_path_unlink_file()`](#path_unlink_file). + +- **`__WASI_RIGHT_PATH_REMOVE_DIRECTORY`** + + The right to invoke [`__wasi_path_remove_directory()`](#path_remove_directory). + +- **`__WASI_RIGHT_POLL_FD_READWRITE`** + + If [`__WASI_RIGHT_FD_READ`](#rights.fd_read) is set, includes the right to + invoke [`__wasi_poll_oneoff()`](#poll_oneoff) to subscribe to [`__WASI_EVENTTYPE_FD_READ`](#eventtype.fd_read). + + If [`__WASI_RIGHT_FD_WRITE`](#rights.fd_write) is set, includes the right to + invoke [`__wasi_poll_oneoff()`](#poll_oneoff) to subscribe to [`__WASI_EVENTTYPE_FD_WRITE`](#eventtype.fd_write). + +- **`__WASI_RIGHT_SOCK_SHUTDOWN`** + + The right to invoke [`__wasi_sock_shutdown()`](#sock_shutdown). + +### `__wasi_roflags_t` (`uint16_t` bitfield) + +Flags returned by [`__wasi_sock_recv()`](#sock_recv). + +Used by [`__wasi_sock_recv()`](#sock_recv). + +Possible values: + +- **`__WASI_SOCK_RECV_DATA_TRUNCATED`** + + Returned by [`__wasi_sock_recv()`](#sock_recv): Message data has been + truncated. + +### `__wasi_sdflags_t` (`uint8_t` bitfield) + +Which channels on a socket to shut down. + +Used by [`__wasi_sock_shutdown()`](#sock_shutdown). + +Possible values: + +- **`__WASI_SHUT_RD`** + + Disables further receive operations. + +- **`__WASI_SHUT_WR`** + + Disables further send operations. + +### `__wasi_siflags_t` (`uint16_t` bitfield) + +Flags provided to [`__wasi_sock_send()`](#sock_send). As there are currently no flags +defined, it must be set to zero. + +Used by [`__wasi_sock_send()`](#sock_send). + +### `__wasi_signal_t` (`uint8_t`) + +Signal condition. + +Used by [`__wasi_proc_raise()`](#proc_raise). + +Possible values: + +- **`__WASI_SIGABRT`** + + Process abort signal. + + Action: Terminates the process. + +- **`__WASI_SIGALRM`** + + Alarm clock. + + Action: Terminates the process. + +- **`__WASI_SIGBUS`** + + Access to an undefined portion of a memory object. + + Action: Terminates the process. + +- **`__WASI_SIGCHLD`** + + Child process terminated, stopped, or continued. + + Action: Ignored. + +- **`__WASI_SIGCONT`** + + Continue executing, if stopped. + + Action: Continues executing, if stopped. + +- **`__WASI_SIGFPE`** + + Erroneous arithmetic operation. + + Action: Terminates the process. + +- **`__WASI_SIGHUP`** + + Hangup. + + Action: Terminates the process. + +- **`__WASI_SIGILL`** + + Illegal instruction. + + Action: Terminates the process. + +- **`__WASI_SIGINT`** + + Terminate interrupt signal. + + Action: Terminates the process. + +- **`__WASI_SIGKILL`** + + Kill. + + Action: Terminates the process. + +- **`__WASI_SIGPIPE`** + + Write on a pipe with no one to read it. + + Action: Ignored. + +- **`__WASI_SIGQUIT`** + + Terminal quit signal. + + Action: Terminates the process. + +- **`__WASI_SIGSEGV`** + + Invalid memory reference. + + Action: Terminates the process. + +- **`__WASI_SIGSTOP`** + + Stop executing. + + Action: Stops executing. + +- **`__WASI_SIGSYS`** + + Bad system call. + + Action: Terminates the process. + +- **`__WASI_SIGTERM`** + + Termination signal. + + Action: Terminates the process. + +- **`__WASI_SIGTRAP`** + + Trace/breakpoint trap. + + Action: Terminates the process. + +- **`__WASI_SIGTSTP`** + + Terminal stop signal. + + Action: Stops executing. + +- **`__WASI_SIGTTIN`** + + Background process attempting read. + + Action: Stops executing. + +- **`__WASI_SIGTTOU`** + + Background process attempting write. + + Action: Stops executing. + +- **`__WASI_SIGURG`** + + High bandwidth data is available at a socket. + + Action: Ignored. + +- **`__WASI_SIGUSR1`** + + User-defined signal 1. + + Action: Terminates the process. + +- **`__WASI_SIGUSR2`** + + User-defined signal 2. + + Action: Terminates the process. + +- **`__WASI_SIGVTALRM`** + + Virtual timer expired. + + Action: Terminates the process. + +- **`__WASI_SIGXCPU`** + + CPU time limit exceeded. + + Action: Terminates the process. + +- **`__WASI_SIGXFSZ`** + + File size limit exceeded. + + Action: Terminates the process. + +### `__wasi_subclockflags_t` (`uint16_t` bitfield) + +Flags determining how to interpret the timestamp provided in +[`__wasi_subscription_t::u.clock.timeout`](#subscription.u.clock.timeout). + +Used by [`__wasi_subscription_t`](#subscription). + +Possible values: + +- **`__WASI_SUBSCRIPTION_CLOCK_ABSTIME`** + + If set, treat the timestamp provided in + [`__wasi_subscription_t::u.clock.timeout`](#subscription.u.clock.timeout) as an absolute timestamp + of clock [`__wasi_subscription_t::u.clock.clock_id`](#subscription.u.clock.clock_id). + + If clear, treat the timestamp provided in + [`__wasi_subscription_t::u.clock.timeout`](#subscription.u.clock.timeout) relative to the current + time value of clock [`__wasi_subscription_t::u.clock.clock_id`](#subscription.u.clock.clock_id). + +### `__wasi_subscription_t` (`struct`) + +Subscription to an event. + +Used by [`__wasi_poll_oneoff()`](#poll_oneoff). + +Members: + +- [\_\_wasi\_userdata\_t](#userdata) userdata + + User-provided value that is attached to the subscription in the + implementation and returned through + [`__wasi_event_t::userdata`](#event.userdata). + +- [\_\_wasi\_eventtype\_t](#eventtype) type + + The type of the event to which to subscribe. + +- When `type` is [`__WASI_EVENTTYPE_CLOCK`](#eventtype.u.clock): + + - **`u.clock`** + + - [\_\_wasi\_userdata\_t](#userdata) identifier + + The user-defined unique identifier of the clock. + + - [\_\_wasi\_clockid\_t](#clockid) clock\_id + + The clock against which to compare the timestamp. + + - [\_\_wasi\_timestamp\_t](#timestamp) timeout + + The absolute or relative timestamp. + + - [\_\_wasi\_timestamp\_t](#timestamp) precision + + The amount of time that the implementation may wait additionally + to coalesce with other events. + + - [\_\_wasi\_subclockflags\_t](#subclockflags) flags + + Flags specifying whether the timeout is absolute or relative. + +- When `type` is [`__WASI_EVENTTYPE_FD_READ`](#eventtype.fd_read) or [`__WASI_EVENTTYPE_FD_WRITE`](#eventtype.fd_write): + + - **`u.fd_readwrite`** + + - [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor on which to wait for it to become ready + for reading or writing. + +### `__wasi_timestamp_t` (`uint64_t`) + +Timestamp in nanoseconds. + +Used by [`__wasi_filestat_t`](#filestat), [`__wasi_subscription_t`](#subscription), [`__wasi_clock_res_get()`](#clock_res_get), [`__wasi_clock_time_get()`](#clock_time_get), [`__wasi_fd_filestat_set_times()`](#fd_filestat_set_times), and [`__wasi_path_filestat_set_times()`](#path_filestat_set_times). + +### `__wasi_userdata_t` (`uint64_t`) + +User-provided value that may be attached to objects that is +retained when extracted from the implementation. + +Used by [`__wasi_event_t`](#event) and [`__wasi_subscription_t`](#subscription). + +### `__wasi_whence_t` (`uint8_t`) + +The position relative to which to set the offset of the file descriptor. + +Used by [`__wasi_fd_seek()`](#fd_seek). + +Possible values: + +- **`__WASI_WHENCE_CUR`** + + Seek relative to current position. + +- **`__WASI_WHENCE_END`** + + Seek relative to end-of-file. + +- **`__WASI_WHENCE_SET`** + + Seek relative to start-of-file. + diff --git a/docs/WASI-background.md b/docs/WASI-background.md new file mode 100644 index 0000000000..5c54200a01 --- /dev/null +++ b/docs/WASI-background.md @@ -0,0 +1,179 @@ +One of the biggest challenges in WebAssembly is figuring out what it's +supposed to be. + +## A brief tangent on some related history + +The LLVM WebAssembly backend has gone down countless paths that it has +ended up abandoning. One of the early questions was whether we should use +an existing object file format, such as ELF, or design a new format. + +Using an existing format is very appealing. We'd be able to use existing +tools, and be familiar to developers. It would even make porting some +kinds of applications easier. And existing formats carry with them +decades of "lessons learned" from many people in many settings, building, +running, and porting real-world applications. + +The actual WebAssembly format that gets handed to platforms to run is +its own format, but there'd be ways to make things work. To reuse existing +linkers, we could have a post-processing tool which translates from the +linker's existing output format into a runnable WebAssembly module. We +actually made a fair amount of progress toward building this. + +But then, using ELF for example, we'd need to create a custom segment +type (in the `PT_LOPROC`-`PT_HIPROC` range) instead of the standard +`PT_LOAD` for loading code, because WebAssembly functions aren't actually +loaded into the program address space. And same for the `PT_LOAD` for the +data too, because especially once WebAssembly supports threads, memory +initialization will need to +[work differently](https://github.com/WebAssembly/bulk-memory-operations/blob/master/proposals/bulk-memory-operations/Overview.md#design). +And we could omit the `PT_GNU_STACK`, because WebAssembly's stack can't +be executable. And maybe we could omit `PT_PHDR` because unless +we replicate the segment headers in data, they won't actually be +accessible in memory. And so on. + +And while in theory everything can be done within the nominal ELF +standard, in practice we'd have to make major changes to existing ELF +tools to support this way of using ELF, which would defeat many of the +advantages we were hoping to get. And we'd still be stuck with a custom +post-processing step. And it'd be harder to optimize the system to +take advantage of the unique features of WebAssembly, because everything +would have to work within this external set of constraints. + +So while the LLVM WebAssembly backend started out trying to use ELF, we +eventually decided to back out of that and design a +[new format](https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md). + +## Now let's talk APIs + +It's apparent to anyone who's looked under the covers at Emscripten's interface +between WebAssembly and the outside world that the current system is particular +to the way Emscripten currently works, and not well suited for broader adoption. +This is especially true as interest grows in running WebAssembly outside +of browsers and outside of JS VMs. + +It's been obvious since WebAssembly was just getting started that it'd eventually +want some kind of "system call"-like API, which could be standardized, and +implemented in any general-purpose WebAssembly VM. + +And while there are many existing systems we could model this after, [POSIX] +stands out, as being a vendor-neutral standard with considerable momentum. Many +people, including us, have been assuming that WebAssembly would eventually +have some kind of POSIX API. Some people have even started experimenting with +what +[this](https://github.com/WAVM/Wavix/) +[might](https://github.com/jfbastien/musl) +[look](https://github.com/golang/go/blob/e5489cfc12a99f25331831055a79750bfa227943/misc/wasm/wasm_exec.js) +[like](https://github.com/emscripten-core/emscripten/blob/incoming/src/library_syscall.js). + +But while a lot of things map fairly well, some things are less clear. One of +the big questions is how to deal with the concept of a "process". POSIX's IPC +mechanisms are designed around process, and in fact, the term "IPC" itself +has "process" baked into it. The way we even think about what "IPC" means +bakes in in understandings about what processes are and what communication +between them looks like. + +Pipes, Unix-domain sockets, POSIX shared memory, signals, files with `fcntl` +`F_SETLK`/`F_GETLK`-style locking (which is process-associated), are are tied +to processes. But what *is* a process, when we're talking about WebAssembly? + +## Stick a fork in it + +Suppose we say that a WebAssembly instance is a "process", for the purposes +of the POSIX API. This initially seems to work out well, but it leaves us +with several holes to fill. Foremost is `fork`. `fork` is one of the pillars +of Unix, but it's difficult to implement outside of a full Unix-style OS. We +probably *can* make it work in all the places we want to run WebAssembly, but +do we want to? It'd add a bunch of complexity, inefficiency, subtle behavioral +differences, or realistically, a combination of all three. + +Ok, so maybe we can encourage applications to use `posix_spawn` instead. And +some already do, but in doing so we do loose some of the value of POSIX's +momentum. And even with `posix_spawn`, many applications will explicitly do +things like `waidpid` on the resulting PID. We can make this work too, but +we should also take a moment and step back to think about IPC in general. + +In WebAssembly, instances can synchronously call each other, and it can be +very efficient. This is not something that typical processes can do. Arguably, +a lot of what we now think of as "IPC" is just working around the inability +of processes to have calls between each other. And, WebAssembly instances will +be able to import each others' memories and tables, and eventually even pass +around slices to their memories. In WebAssembly circles we don't even tend to +think of these as IPC mechanisms, because the process metaphor just doesn't +fit very well here. We're going to want applications to use these mechanisms, +because they're efficient and take advantage of the platform, rather than +using traditional Unix-style IPC which will often entail emulation and +inefficiencies. + +Of course, there will always be a role for aiding porting of existing +applications. Libraries that emulate various details of Unix semantics are +valuable. But we can consider them tools for solving certain practical +problems, rather than the primary interfaces of the system, because they +miss out on some of the platform's fundamental features. + +## Mm-Mm Mmap + +Some of the fundamental assumptions of `mmap` are that there exists a +relatively large virtual address space, and that unmapped pages don't +occupy actual memory. The former doesn't tend to hold in WebAssembly, +where linear address spaces tend to be only as big as necessary. + +For the latter, would it be possible to make a WebAssembly engine capable +of unmapping pages in the middle of a linear memory region, and releasing +the resources? Sure. Is this a programming technique we want WebAssembly +programs doing in general, requiring all VMs to implement this? +Probably not. + +What's emerging is a sense that what we want is a core set of +APIs that can be implemented very broadly, and then optional API +modules that VMs can opt into supporting if it makes sense for them. +And with this mindset, `mmap` feels like it belongs in one of these +optional sets, rather than in the core. + +(although note that even for the use case of reading files quickly, +`mmap` +[isn't always better than just reading into a buffer](https://blog.burntsushi.net/ripgrep/). + +## A WebAssembly port of Debian? + +This is a thought-experiment. Debian is ported to numerous hardware +architectures. WebAssembly in some settings is presented as a hardware +architecture. Would it make sense to port the Debian userspace to +WebAssembly? What would this look like? What would it be useful for? + +It would be kind of cool to have a WebAssembly-powered Unix shell +environment or even a graphical desktop environment running inside a +browser. But would it be *really* cool? Significantly more cool than, +say, an SSH or VNC session to an instance in the cloud? Because to do +much with it, you'll want a filesystem, a network stack, and so on, +and there's only so much that browsers will let you do. + +To be sure, it certainly would be cool. But there's a tendency in +some circles to think of something like Debian as the natural end goal +in a system API and toolchain for WebAssembly. We feel this tendency +too ourselves. But it's never really been clear how it's supposed to +work. + +The insight here is that we can split the design space, rather than +trying to solve everything at once. We can have a core set of APIs +that will be enough for most applications, but that doesn't try to +support all of Debian userland. This will make implementations more +portable, flexible, testable, and robust than if we tried to make +every implementation support everything, or come up with custom +subsets. + +As mentioned above, there is room for additional optional APIs to be +added beyond the core WASI set. And there's absolutely a place for +tools and libraries that features that aren't in the standard +platform. So people interested in working on a Debian port can still +have a path forward, but we don't need to let this become a focus for +the core WASI design. + +## A picture emerges + +While much of what's written here seems relatively obvious in +retrospect, this clarity is relatively new. We're now seeing many of the +ideas which have been swirling around, some as old as WebAssembly +itself, come together into a cohesive overall plan, which makes this +an exciting time. + +[POSIX]: http://pubs.opengroup.org/onlinepubs/9699919799/ diff --git a/docs/WASI-capabilities.md b/docs/WASI-capabilities.md new file mode 100644 index 0000000000..88cff8f418 --- /dev/null +++ b/docs/WASI-capabilities.md @@ -0,0 +1,78 @@ +# Additional background on Capabilities + +## Unforgeable references + +One of the key words that describes capabilities is *unforgeable*. + +A pointer in C is forgeable, because untrusted code could cast an integer +to a pointer, thus *forging* access to whatever that pointer value points +to. + +MVP WebAssembly doesn't have unforgeable references, but what we can do instead +is just use integer values which are indices into a table that's held outside +the reach of untrusted code. The indices themselves are forgeable, but +ultimately the table is the thing which holds the actual capabilities, and +its elements are unforgeable. There's no way to gain access to a new resource +by making up a new index. + +When the reference-types proposal lands, references will be unforgeable, and +will likely subsume the current integer-based APIs, at the WASI API layer. + +## Static vs dynamic capabilities + +There are two levels of capabilities that we can describe: static and dynamic. + +The static capabilities of a wasm module are its imports. These essentially +declare the set of "rights" the module itself will be able to request. +An important caveat though is that this doesn't consider capabilities which +may be passed into an instance at runtime. + +The dynamic capabilities of a wasm module are a set of boolean values +associated with a file descriptor, indicating individual "rights". This +includes things like the right to read, or to write, using a given file +descriptor. + +## Filesystem rules + +It happens that integer indices representing capabilities is same thing that +POSIX does, except that POSIX calls these indices *file descriptors*. + +One difference though is that POSIX normally allows processes to request +a file descriptor for any file in the entire filesystem hierarchy, which is +granted based on whatever security policies are in place. This doesn't +violate the capability model, but it doesn't take full advantage of it. + +CloudABI, Fuchsia, and other capability-oriented systems prefer to take +advantage of the hierarchical nature of the filesystem and require untrusted +code to have a capability for a directory in order to access things inside +that directory. + +So you can launch untrusted code, and at runtime give it access to specific +directories, without having to set permissions in the filesystem or in +per-application or per-user configuration settings. + +## Berkeley socket rules + +Sockets aren't naturally hierarchical though, so we'll need to decide what +capabilities look like. This is an area that isn't yet implemented. + +In CloudABI, users launch programs with the sockets they need already +created. That's a potentially startup point, which might be enough for +simple cases. + +We also anticipate an eventual extension to that, where we create a capability +that represents a set of possible sockets that can be created. A set +might be described by ranges of permitted ports, ranges of permitted +addresses, or sets of permitted protocols. In this case the actual socket +wouldn't be created until the application actually requests it. + +## Other info + +CloudABI's intro to capability-based OS security provides additional background info: + +https://github.com/NuxiNL/cloudabi#capability-based-security + + +The Fuchsia project has a blog post on the topic of capability-based OS security: + +https://fuchsia.googlesource.com/docs/+/HEAD/the-book/dotdot.md diff --git a/docs/WASI-documents.md b/docs/WASI-documents.md new file mode 100644 index 0000000000..83ffd4acd2 --- /dev/null +++ b/docs/WASI-documents.md @@ -0,0 +1,22 @@ +# WASI Document Guide + +To get started using WASI, see [the intro document](WASI-intro.md) and +[the tutorial](WASI-tutorial.md). + +For more detail on what WASI is, see [the overview](WASI-overview.md). + +For specifics on the API, see the [API documentation](https://github.com/CraneStation/wasmtime-wasi/blob/wasi/docs/WASI-api.md). +Additionally, a C header file describing the WASI API is +[here](https://github.com/CraneStation/reference-sysroot-wasi/blob/misc/libc-bottom-half/headers/public/wasi/core.h). + +For some discussion of capability-based design, see the [Capabilities document](WASI-capabilities.md). + +For some discussion of WASI's design inspiration, see the [Background document](WASI-background.md). + +For background on some of the design decisions in WASI, see [the rationale](WASI-rationale.md). + +For some ideas of things that we may want to change about WASI in the +short term, see the [possible changes](WASI-some-possible-changes.md) document. +For longer-term ideas, see the [possible future features](WASI-possible-future-features.md) +document. + diff --git a/docs/WASI-intro.md b/docs/WASI-intro.md new file mode 100644 index 0000000000..bdf6d632a5 --- /dev/null +++ b/docs/WASI-intro.md @@ -0,0 +1,83 @@ +# Welcome to WASI! + +WASI stands for WebAssembly System Interface. It's an API designed by +the [Wasmtime] project that provides access to several operating-system-like +features, including files and filesystems, Berkeley sockets, clocks, and +random numbers, that we'll be proposing for standardization. + +It's designed to be independent of browsers, so it doesn't depend on +Web APIs or JS, and isn't limited by the need to be compatible with JS. +And it has integrated capability-based security, so it extends +WebAssembly's characteristic sandboxing to include I/O. + +See the [WASI Overview](WASI-overview.md) for more detailed background +information, and the [WASI Tutorial](WASI-tutorial.md) for a walkthrough +showing how various pieces fit together. + +Note that everything here is a prototype, and while a lot of stuff works, +there are numerous missing features and some rough edges. One big thing +that's not done yet is the actual mechanism to provide a directory as a +pre-opened capability, to allow files to be opened. Some of the pieces +are there (`__wasilibc_register_preopened_fd`) but they're not used yet. +Networking support is also incomplete. + +## How can I write programs that use WASI? + +The two toolchains that currently work well are the Rust toolchain and +a specially packaged C and C++ toolchain. Of course, we hope other +toolchains will be able to implement WASI as well! + +### Rust + +To install a WASI-enabled Rust toolchain, follow the instructions here: + +https://github.com/alexcrichton/rust/releases/tag/wasi3 + +Until now, Rust's WebAssembly support has had two main options, the +Emscripten-based option, and the wasm32-unknown-unknown option. The latter +option is lighter-weight, but only supports `no_std`. WASI enables a new +wasm32-unknown-wasi target, which is similar to wasm32-unknown-unknown in +that it doesn't depend on Emscripten, but it can use WASI to provide a +decent subset of libstd. + +### C/C++ + +All the parts needed to support wasm are included in upstream clang, lld, and +compiler-rt, as of the LLVM 8.0 release. However, to use it, you'll need +to build WebAssembly-targeted versions of the library parts, and it can +be tricky to get all the CMake invocations lined up properly. + +To make things easier, we provide +[prebuilt packages](https://github.com/CraneStation/wasi-sdk/releases) +that provide builds of Clang and sysroot libraries. + +Note that C++ support has a notable +[bug](https://bugs.llvm.org/show_bug.cgi?id=40412) in clang which affects + in libcxx. This will be fixed in future versions. + +## How can I run programs that use WASI? + +Currently the options are [Wasmtime] and the [browser polyfill], though we +intend WASI to be implementable in many wasm VMs. + +[Wasmtime]: https://github.com/CraneStation/wasmtime +[browser polyfill]: https://wasi.dev/polyfill/ + +### Wasmtime + +[Wasmtime] is a non-Web WebAssembly engine which is part of the +[CraneStation project](https://github.com/CraneStation/). To build +it, download the code and build with `cargo build --release`. It can +run WASI-using wasm programs by simply running `wasmtime foo.wasm`, +or `cargo run --bin wasmtime foo.wasm`. + +### The browser polyfill + +The polyfill is online [here](https://wasi.dev/polyfill/). + +The source is [here](https://github.com/CraneStation/wasmtime-wasi/tree/wasi/lib/wasi/sandboxed-system-primitives/polyfill). + +## Where can I learn more? + +Beyond the [WASI Overview](WASI-overview.md), take a look at the +various [WASI documents](WASI-documents.md). diff --git a/docs/WASI-overview.md b/docs/WASI-overview.md new file mode 100644 index 0000000000..975a30d492 --- /dev/null +++ b/docs/WASI-overview.md @@ -0,0 +1,163 @@ +# WASI: WebAssembly System Interface + +WebAssembly System Interface, or WASI, is a new family of API's being +designed by the [Wasmtime] project to propose as a standard engine-independent +non-Web system-oriented API for WebAssembly. Initially, the focus is on +WASI Core, an API module that covers files, networking, and a few other +things. Additional modules are expected to be added in the future. + +WebAssembly is designed to run well on the Web, however it's +[not limited to the Web](https://github.com/WebAssembly/design/blob/master/NonWeb.md). +The core WebAssembly language is independent of its surrounding +environment, and WebAssembly interacts with the outside world +exclusively through APIs. On the Web, it naturally uses the +existing Web APIs provided by browsers. However outside of +browsers, there's currently no standard set of APIs that +WebAssembly programs can be written to. This makes it difficult to +create truly portable non-Web WebAssembly programs. + +WASI is an initiative to fill this gap, with a clean set of APIs +which can be implemented on multiple platforms by multiple engines, +and which don't depend on browser functionality (although they +still can run in browsers; see below). + +## Capability-Oriented + +The design follows +[CloudABI](https://cloudabi.org/)'s +(and in turn +[Capsicum](https://www.cl.cam.ac.uk/research/security/capsicum/))'s concept of +[capability-based security](https://en.wikipedia.org/wiki/Capability-based_security), +which fits well into WebAssembly's sandbox model. Files, +directories, network sockets, and other resources are identified +by UNIX-like file descriptors, which are indices into external +tables whose elements represent capabilities. Similar to how core +WebAssembly provides no ability to access the outside world without +calling imported functions, WASI APIs provide no ability to access +the outside world without an associated capability. + +For example, instead of a typical +[open](http://pubs.opengroup.org/onlinepubs/009695399/functions/open.html) +system call, WASI provides an +[openat](https://linux.die.net/man/2/openat)-like +system call, requiring the calling process to have a file +descriptor for a directory that contains the file, representing the +capability to open files within that directory. (These ideas are +common in capability-based systems.) + +However, the WASI libc implementation still does provide an +implementation of open, by taking the approach of +[libpreopen](https://github.com/musec/libpreopen). +Programs may be granted capabilities for directories on launch, and +the library maintains a mapping from their filesystem path to the +file descriptor indices representing the associated capabilities. +When a program calls open, they look up the file name in the map, +and automatically supply the appropriate directory capability. It +also means WASI doesn't require the use of CloudABI's `program_main` +construct. This eases porting of existing applications without +compromising the underlying capability model. See the diagram below +for how libpreopen fits into the overall software architecture. + +WASI also automatically provides file descriptors for standard +input and output, and WASI libc provides a normal `printf`. In +general, WASI is aiming to support a fairly full-featured libc +implementation, with the current implementation work being based on +[musl](http://www.musl-libc.org/). + +## Portable System Interface for WebAssembly + +WASI is being designed from the ground up for WebAssembly, with +sandboxing, portability, and API tidiness in mind, making natural +use of WebAssembly features such as i64, import functions with +descriptive names and typed arguments, and aiming to avoid being +tied to a particular implementation. + +We'll often call functions in these APIs "syscalls", because they +serve an analogous purpose to system calls in native executables. +However, they're just functions that are provided by the +surrounding environment that can do I/O on behalf of the program. + +WASI is starting with a basic POSIX-like set of syscall functions, +though adapted to suit the needs of WebAssembly, such as in +excluding functions such as fork and exec which aren't easily +implementable in some of the places people want to run WebAssembly, +and such as in adopting a capabilities-oriented design. + +And, as WebAssembly grows support for +[host bindings](https://github.com/webassembly/host-bindings) +and related features, capabilities can evolve to being represented +as opaque, unforgeable +[reference typed values](https://github.com/WebAssembly/reference-types), +which can allow for finer-grained control over capabilities, and +make the API more accessible beyond the C-like languages that +POSIX-style APIs are typically aimed at. + +## WASI Software Architecture + +To facilitate use of the WASI API, a libc +implementation called WASI libc is being developed, which presents +a relatively normal musl-based libc interface, implemented on top +of a libpreopen-like layer and a system call wrapper layer (derived +from the "bottom half" of +[cloudlibc](https://github.com/NuxiNL/cloudlibc)). +The system call wrapper layer makes calls to the actual WASI +implementation, which may map these calls to whatever the +surrounding environment provides, whether it's native OS resources, +JS runtime resources, or something else entirely. + +[This libc is part of a "sysroot"](https://github.com/WebAssembly/reference-sysroot), +which is a directory containing compiled libraries and C/C++ header +files providing standard library and related facilities laid out in +a standard way to allow compilers to use it directly. + +With the [LLVM 8.0](http://llvm.org/) +release, the WebAssembly backend is now officially stable, but LLVM +itself doesn't provide a libc - a standard C library, which you +need to build anything with clang. This is what the WASI-enabled +sysroot provides, so the combination of clang in LLVM 8.0 and the +new WASI-enabled sysroot provides usable Rust and C compilation +environments that can produce wasm modules that can be run in +[Wasmtime] with WASI support, in browsers with the WASI polyfill, +and in the future other engines as well. + +![WASI software architecture diagram](wasi-software-architecture.png "WASI software architecture diagram") + +## Future Evolution + +The first version of WASI is relatively simple, small, and +POSIX-like in order to make it easy for implementers to prototype +it and port existing code to it, making it a good way to start +building momentum and allow us to start getting feedback based on +experience. + +Future versions will change based on experience +and feedback with the first version, and add features to address +new use cases. They may also see significant architectural +changes. One possibility is that this API could +evolve into something like +[Fuchsia](https://en.wikipedia.org/wiki/Google_Fuchsia)'s +low-level APIs, which are more complex and abstract, though also +more capable. + +We also expect that whatever WASI evolves into in the future, it +should be possible to implement this initial API as a library +on top. + +## Can WASI apps run on the Web? + +Yes! We have a polyfill which implements WASI and runs in browsers. +At the WebAssembly level, WASI is just a set of callable functions that +can be imported by a .wasm module, and these imports can be implemented +in a variety of ways, including by a JavaScript polyfill library running +within browsers. + +And in the future, it's possible that +[builtin modules](https://github.com/tc39/ecma262/issues/395) +could take these ideas even further allowing easier and tighter +integration between .wasm modules importing WASI and the Web. + +## Work in Progress + +WASI is currently experimental. Feedback is welcome! + +[Wasmtime]: https://github.com/CraneStation/wasmtime diff --git a/docs/WASI-possible-future-features.md b/docs/WASI-possible-future-features.md new file mode 100644 index 0000000000..e71fad31be --- /dev/null +++ b/docs/WASI-possible-future-features.md @@ -0,0 +1,49 @@ +# Possible Future Features + +This are features we're interested in, but don't have yet, and which will require +some amount of design work. + +## File Locking + +POSIX's answer is `fcntl` with `F_SETLK`/`F_GETLK`/etc., which provide advisory +record locking. Unfortunately, these locks are associated with processes, which +means that if two parts of a program independently open a file and try to lock +it, if they're in the same process, they automatically share the lock. + +Other locking APIs exist on various platforms, but none is widely standardized. + +POSIX `F_SETLK`-style locking is used by SQLite. + +## File change monitoring + +POSIX has no performant way to monitor many files or directories for changes. + +Many popular operating systems have system-specific APIs to do this though, so +it'd be desirable to come up with a portable API to provide access to this +functionality. + +## Scalable event-based I/O + +POSIX's `select` and `poll` have the property that each time they're called, +the implementation has to scan through all the file descriptors to report if any +of them has I/O ready, which is inefficient when there are large numbers of +open files or sockets. + +Many popular operating systems have system-specific APIs that provide +alternative ways to monitor large numbers of I/O streams though, so it'd be +desirable to come up with a portable API to provide access to this +functionality. + +## Crash recovery + +POSIX doesn't have clear guidance on what applications can expect their +data will look like if the system crashes or the storage device is otherwise +taken offline abruptly. + +We have `fsync` and `fdatasync`, but even these have been a topic of +[much discussion]. + +[much discussion]: https://wiki.postgresql.org/wiki/Fsync_Errors + +Also, currently WASI's docs don't make any guarantees about things like +`path_rename` being atomic. diff --git a/docs/WASI-rationale.md b/docs/WASI-rationale.md new file mode 100644 index 0000000000..d1c8e09385 --- /dev/null +++ b/docs/WASI-rationale.md @@ -0,0 +1,160 @@ +## Why not a more traditional set of POSIX-like syscalls? + +In related work, the LLVM wasm backend started out trying to use ELF object +files for wasm, to be as conventional as possible. But wasm doesn't fit into +ELF in some very fundamental ways. Code isn't in the address space, callers +have to know their callee's exact signatures, imports and exports don't have +ELF semantics, function pointers require tables to be populated, index 0 is +valid in some contexts where it isn't in ELF, and so on. It ultimately got +to the point where the work we were considering doing to *emulate* ELF +interfaces to make existing tools happy looked like more than the work that +would be required to just build new tools. + +The analogy isn't perfect, but there are some parallels to what we're now +figuring out about system calls. Many people, including us, had initially +assumed that at least some parts of the wasm ecosystem would eventually +standardize on a basic map of POSIX-like or Linux-like system calls into wasm +imports. However, this turns out to be more complex than it initially seems. + +One of WebAssembly's unique attributes is the ability to run sandboxed +without relying on OS process boundaries. Requiring a 1-to-1 correspondence +between wasm instances and heavyweight OS processes would take away this key +advantage for many use cases. Fork/exec are the obvious example of an API +that's difficult to implement well if you don't have POSIX-style processes, +but a lot of other things in POSIX are tied to processes too. So it isn't +a simple matter to take POSIX, or even a simple subset of it, to WebAssembly. + +We should note that Spectre concerns are relevant here, though for now we'll +just observe that actual security depends on the details of implementations +and use cases, and it's not necessarily a show-stopper. + +Another area where WebAssembly differs from traditional POSIX-like platforms +is in its Capability-oriented approach to security. WebAssembly core has no +ability to address the outside world, except through interacting with +imports/exports. And when reference types are added, they'll be able to +represent very fine-grained and dynamic capabilities. + +A capability-oriented system interface fits naturally into WebAssembly's +existing sandbox model, by extending the simple story that a wasm module +can't do anything until given capabilities. There are ways to sandbox +traditional OS filesystem APIs too, but in a multiple-implementation +ecosystem where the methods for setting up path filtering will likely +differ between implementations, designing the platform around capabilities +will make it easier for people to consistently configure the capabilities +available to wasm modules. + +This is where we see WASI heading. + +## Why not non-blocking? + +This is an open question. We're using blocking APIs for now because that's +*by far* the simpler way to get the overall system to a usable state, on +both the wasm runtime side and the toolchain side. But one can make an +argument that non-blocking APIs would have various advantages, so we +look forward to discussing this topic with the WebAssembly CG subgroup +once it's set up. + +## Why not async? + +We have some ideas about how the current API could be extended to be async. +In particular, we can imagine making a distinction between WebAssembly +programs which are *Commands* and those which we'll call *Reactors*. +Commands have a `main` function which is called once, and when `main` +exits, the program is complete. Reactors have a setup function, but +once that completes, the instance remains live and is called from callbacks. +In a Reactor, there's an event loop which lives outside of the nominal +program. + +With this distinction, we may be able to say things like: + - In a Reactor, WASI APIs are available, but all functions have an + additional argument, which specifies a function to call as a continuation + once the I/O completes. This way, we can use the same conceptual APIs, + but adapt them to run in an callback-based async environment. + - In a Command, WASI APIs don't have callback parameters. Whether or not + they're non-blocking is an open question (see the previous question). + +Reactors might then be able to run in browsers on the main thread, +while Commands in browsers might be limited to running in Workers. + +## Why no mmap and friends? + +True mmap support is something that could be added in the future, +though it is expected to require integration with the core language. +See "Finer-grained control over memory" in WebAssembly's +[Future Features] document for an overview. + +Ignoring the many non-standard mmap extensions out there, +the core mmap behavior is not portable in several respects, even +across POSIX-style systems. See +[LevelDB's decision to stop using mmap], for one example in +practice, and search for the word "unspecified" in the +[POSIX mmap spec] for some others. + +And, some features of mmap can lead to userspace triggering +signals. Accessing memory beyond the end of the file, including in +the case where someone else changes the size of the file, leads to a +`SIGBUS` on POSIX-style systems. Protection modes other than +`PROT_READ|PROT_WRITE` can produce `SIGSEGV`. While some VMs are +prepared to catch such signals transparently, this is a burdensome +requirement for others. + +Another issue is that while WASI is a synchronous I/O API today, +this design may change in the future. `mmap` can create situations +where doing a load can entail blocking I/O, which can make it +harder to characterize all the places where blocking I/O may occur. + +And lastly, WebAssembly linear memory doesn't support the semantics +of mapping and unmapping pages. Most WebAssembly VMs would not +easily be able to support freeing the memory of a page in the middle +of a linear memory region, for example. + +To make things easier for people porting programs that just use +mmap to read and write files in a simple way, WASI libc includes a +minimal userspace emulation of `mmap` and `munmap`. + +[POSIX mmap spec]: http://pubs.opengroup.org/onlinepubs/7908799/xsh/mmap.html +[LevelDB's decision to stop using mmap]: https://groups.google.com/forum/#!topic/leveldb/C5Hh__JfdrQ +[Future Features]: https://webassembly.org/docs/future-features/. + +## Why no UNIX-domain sockets? + +UNIX-domain sockets can communicate three things: + - bytes + - file descriptors + - user credentials + +The concept of "users" doesn't fit within WASI, because many implementations +won't be multi-user in that way. + +It can be useful to pass file descriptor between wasm instances, however in +wasm this can be done by passing them as arguments in plain function calls, +which is much simpler and quicker. And, in WASI implementations where file +descriptors don't correspond to an underlying Unix file descriptor concept, +it's not feasible to do this if the other side of the socket isn't a +cooperating WebAssembly engine. + +We may eventually want to introduce a concept of a WASI-domain socket, for +bidirectional byte-oriented local communication. + +## Why no dup? + +The main use cases for `dup` are setting up the classic Unix dance of setting +up file descriptors in advance of performing a `fork`. Since WASI has no `fork`, +these don't apply. + +And avoiding `dup` for now avoids committing to the POSIX concepts of +descriptors being distinct from file descriptions in subtle ways. + +## Why are `path_remove_directory` and `path_unlink_file` separate syscalls? + +In POSIX, there's a single `unlinkat` function, which has a flag word, +and with the `AT_REMOVEDIR` flag one can specify whether one wishes to +remove a file or a directory. However, there really are two distinct +functions being performed here, and having one system call that can +select between two different behaviors doesn't simplify the actual API +compared to just having two system calls. + +More importantly, in WASI, system call imports represent a static list +of the capabilities requested by a wasm module. Therefore, WASI prefers +each system call to do just one thing, so that it's clear what a wasm +module that imports it might be able to do with it. diff --git a/docs/WASI-some-possible-changes.md b/docs/WASI-some-possible-changes.md new file mode 100644 index 0000000000..aedec3a674 --- /dev/null +++ b/docs/WASI-some-possible-changes.md @@ -0,0 +1,114 @@ +# Possible changes + +The following are a list of relatively straightforward changes +to WASI core that should be considered. + +## Split file/networking/random/clock from args/environ/exit. + +Currently everything is mixed together in one big "core" module. But we can +split them out to allow minimal configurations that don't support this style +of files and networking. + +## Move higher-level and unused errno codes out of the core API. + +The core API currently defines errno codes such as `EDOM` which are +not used for anything. POSIX requires them to be defined, however +that can be done in the higher-level libraries, rather than in the +WASI core API itself. + +## Detecting EOF from read/recv explicitly. + +POSIX's `read` returns 0 if and only if it reaches the end of a file or stream. + +Say you have a read buffer of 1024 bytes, and are reading a file that happens +to be 7 bytes long. The first `read` call will return 7, but unless you happen +to know how big the file is supposed to be, you can't distinguish between +that being all there is, and `read` getting interrupted and returning less +data than you requested. + +Many applications today do an extra `read` when they encounter the end of a +file, to ensure that they get a `read` that returns 0 bytes read, to confirm +that they've reached the end of the file. If `read` instead had a way to +indicate that it had reached the end, this extra call wouldn't be necessary. + +And, `read` on a socket is almost equivalent to `recv` with no flags -- except for +one surprising special case: on a datagram socket, if there's a zero-length +datagram, `read` can't consume it, while `recv` can. This is because `read` can't +indicate that it successfully read 0 bytes, because it has overloaded the meaning +of 0 to indicate eof-of-file. + +So, it would be tidier from multiple perspectives if `read` could indicate +that it had reached the end of a file or stream, independently of how many +bytes it has read. + +## Merging read and recv + +These are very similar, and differ only in subtle ways. It'd make the API +easier to understand if they were unified. + +## Trap instead of returning EFAULT + +POSIX system calls return EFAULT when given invalid pointers, however from an +application perspective, it'd be more natural for them to just segfault. + +## More detailed capability error reporting + +Replace `__WASI_ENOTCAPABLE` with error codes that indicate *which* capabilities +were required but not present. + +## Split `__wasi_path_open` into `__wasi_path_open_file` and `__wasi_path_open_directory`? + +We could also split `__WASI_RIGHT_PATH_OPEN` into file vs directory, +(obviating `__WASI_O_DIRECTORY`). + +## Fix the y2556 bug + +In some places, timestamps are measured in nanoseconds since the UNIX epoch, +so our calculations indicate a 64-bit counter will overflow on +Sunday, July 21, 2554, at 11:34:33 pm UTC. + +These timestamps aren't used in that many places, so it wouldn't cost that +much to widen these timestamps. We can either just extend the current type to +128 bits (two i64's in wasm) or move to a `timespec`-like `tv_sec`/`tv_nsec` +pair. + +## Remove `fd_allocate`? + +Darwin doesn't implement `fd_allocate`, despite it being a in POSIX +since 2001. So we don't currently know any way to implement `fd_allocate` +on Darwin that's safe from race conditions. Should we remove it from the API? + +## Redesign `fstflags_t` + +The relationship between `*_SET_*TIM` and `*_SET_*TIM_NOW` is non-obvious. +We should look at this again. + +## readdir + +Truncating entries that don't fit into a buffer may be error-prone. Should +we redesign how directory reading works? + +## symlinks + +Symlinks are fairly UNIX-specific. Should we remove `__wasi_path_symlink` +and `__wasi_path_readlink`? + +Also, symlink resolution doesn't benefit from libpreopen-style path +translation. Should we move symlink resolution into the libpreopen layer +and do it entirely in "userspace"? + +## Remove the `path_len` argument from `__wasi_fd_prestat_dir_name` + +The buffer should be sized to the length returned from `__wasi_fd_prestat_get`, +so it's not necessary to pass the length back into the runtime. + +## Add a `__wasi_path_filestat_set_size` function? + +Along with libc/libpreopen support, this would enable implementing the +POSIX `truncate` function. + +## errno values returned by `path_open` + +We should specify the errno value returned when `path_open` is told +to open a directory and `__WASI_LOOKUP_SYMLINK_FOLLOW` isn't set, and +the path refers to a symbolic link. diff --git a/docs/WASI-tutorial.md b/docs/WASI-tutorial.md new file mode 100644 index 0000000000..684202ac43 --- /dev/null +++ b/docs/WASI-tutorial.md @@ -0,0 +1,159 @@ +# WASI tutorial + +Let's start with a simple C program which performs a file copy, which will +show to compile and run programs, as well as perform simple sandbox +configuration. The C code here uses standard POSIX APIs, and doesn't have +any knowledge of WASI, WebAssembly, or sandboxing. + +```c +#include +#include +#include +#include +#include +#include + +int +main(int argc, char **argv) { + int n, m; + char buf[BUFSIZ]; + + if (argc != 3) { + fprintf(stderr, "usage: %s \n", argv[0]); + exit(1); + } + + int in = open(argv[1], O_RDONLY); + if (in < 0) { + fprintf(stderr, "error opening input %s: %s\n", argv[1], strerror(errno)); + exit(1); + } + + int out = open(argv[2], O_WRONLY | O_CREAT, 0660); + if (out < 0) { + fprintf(stderr, "error opening output %s: %s\n", argv[2], strerror(errno)); + exit(1); + } + + while ((n = read(in, buf, BUFSIZ)) > 0) { + while (n > 0) { + m = write(out, buf, n); + if (m < 0) { + fprintf(stderr, "write error: %s\n", strerror(errno)); + exit(1); + } + n -= m; + } + } + + if (n < 0) { + fprintf(stderr, "read error: %s\n", strerror(errno)); + exit(1); + } + + return EXIT_SUCCESS; +} +``` + +We'll put this source in a file called `demo.c`. + +The [wasi-sdk](https://github.com/CraneStation/wasi-sdk/releases) provides a clang +which is configured to target WASI and use the WASI sysroot by default, so we can +compile our program like so: + +``` +$ clang demo.c +``` + +A few things to note here. First, this is just regular clang, configured to use +a WebAssembly target and sysroot. The name `a.out` is the traditional default +output name that C compilers use, and can be overridden with the "-o" flag in the +usual way. And, the output of clang here is a standard WebAssembly module: + +``` +$ file a.out +a.out: WebAssembly (wasm) binary module version 0x1 (MVP) +``` + +It's a single file containing a self-contained wasm module, that doesn't require +any supporting JS code. + +We can execute it with wasmtime directly, like so: + +``` +$ wasmtime a.out +usage: a.out +``` + +Ok, this program needs some command-line arguments. So let's give it some: + +``` +$ echo hello world > test.txt +$ wasmtime a.out test.txt /tmp/somewhere.txt +error opening input test.txt: Capabilities insufficient +``` + +Aha, now we're seeing the sandboxing in action. This program is attempting to +access a file by the name of `test.txt`, however it hasn't been given the +capability to do so. + +So let's give it capabilities to access files in the requisite directories: + +``` +$ wasmtime --dir=. --dir=/tmp a.out test.txt /tmp/somewhere.txt +$ cat /tmp/somewhere.txt +hello world +``` + +Now our program runs as expected! + +As a brief aside, note that we used the path `.` above to grant the program +access to the current directory. This is needed because the mapping from +paths to associated capabilities is performed by libc, so it's part of the +WebAssembly program, and we don't expose the actual current working +directory to the WebAssembly program. So providing a full path doesn't work: + +``` +$ wasmtime --dir=$PWD --dir=/tmp a.out test.txt /tmp/somewhere.txt +$ cat /tmp/somewhere.txt +error opening input test.txt: Capabilities insufficient +``` + +So, we always have to use `.` to refer to the current directory. + +Speaking of `.`, what about `..`? Does that give programs a way to break +out of the sandbox? Let's see: + +``` +$ wasmtime --dir=. --dir=/tmp a.out test.txt /tmp/../etc/passwd +$ cat /tmp/somewhere.txt +error opening output /tmp/../etc/passwd: Capabilities insufficient +``` + +The sandbox says no. And note that this is the capabilities system saying no +here ("Capabilities insufficient"), rather than Unix access controls +("Permission denied"). Even if the user running wasmtime had write access to +`/etc/passwd`, WASI programs don't have the capability to access files outside +of the directories they've been granted. This is true when resolving symbolic +links as well. + +Wasmtime also has the ability to remap directories, with the `--mapdir` +command-line option: + +``` +$ wasmtime --dir=. --mapdir=/tmp:/var/tmp a.out test.txt /tmp/somewhere.txt +$ cat /var/tmp/somewhere.txt +hello world +``` + +This maps the name `/tmp` within the WebAssembly program to `/var/tmp` in the +host filesystem. So the WebAssembly program itself never sees the `/var/tmp` path, +but that's where the output file goes. + +See [here](WASI-capabilities.md) for more information on the capability-based +security model. + +The capability model is very powerful, and what's shown here is just the beginning. +In the future, we'll be exposing much more functionality, including finer-grained +capabilities, capabilities for network ports, and the ability for applications to +explicitly request capabilities. diff --git a/src/wasmtime.rs b/src/wasmtime.rs index def4557ffa..09a22bd650 100644 --- a/src/wasmtime.rs +++ b/src/wasmtime.rs @@ -37,16 +37,20 @@ use cranelift_codegen::settings; use cranelift_codegen::settings::Configurable; use cranelift_native; use docopt::Docopt; +use errno::errno; use file_per_thread_logger; use pretty_env_logger; use std::error::Error; +use std::ffi::{CString, OsStr}; use std::fs::File; use std::io; use std::io::prelude::*; +use std::path::Component; use std::path::{Path, PathBuf}; use std::process::exit; use wabt; use wasmtime_jit::{ActionOutcome, Context}; +use wasmtime_wasi::instantiate_wasi; use wasmtime_wast::instantiate_spectest; static LOG_FILENAME_PREFIX: &str = "wasmtime.dbg."; @@ -59,8 +63,8 @@ including calling the start function if one is present. Additional functions given with --invoke are then called. Usage: - wasmtime [-odg] ... - wasmtime [-odg] ... --invoke= + wasmtime [-odg] [--preload=...] [--env=...] [--dir=...] [--mapdir=...] [...] + wasmtime [-odg] [--preload=...] [--env=...] [--dir=...] [--mapdir=...] --invoke= [...] wasmtime --help | --version Options: @@ -68,17 +72,27 @@ Options: -o, --optimize runs optimization passes on the translated functions -g generate debug information -d, --debug enable debug output on stderr/stdout + --preload= load an additional wasm module before loading the main module + --env= pass an environment variable (\"key=value\") to the program + --dir= grant access to the given host directory + --mapdir= where has the form :, grant access to + the given host directory with the given wasm directory name -h, --help print this help message --version print the Cranelift version "; #[derive(Deserialize, Debug, Clone)] struct Args { - arg_file: Vec, + arg_file: String, + arg_arg: Vec, flag_optimize: bool, flag_debug: bool, flag_g: bool, flag_invoke: Option, + flag_preload: Vec, + flag_env: Vec, + flag_dir: Vec, + flag_mapdir: Vec, } fn read_to_end(path: PathBuf) -> Result, io::Error> { @@ -100,6 +114,91 @@ fn read_wasm(path: PathBuf) -> Result, String> { }) } +fn compute_preopen_dirs(flag_dir: &[String], flag_mapdir: &[String]) -> Vec<(String, libc::c_int)> { + let mut preopen_dirs = Vec::new(); + + for dir in flag_dir { + let fd = unsafe { + libc::open( + CString::new(dir.as_bytes()).unwrap().as_ptr(), + libc::O_RDONLY | libc::O_DIRECTORY, + ) + }; + if fd < 0 { + println!("error while pre-opening directory {}: {}", dir, errno()); + exit(1); + } + + preopen_dirs.push((dir.clone(), fd)); + } + + for mapdir in flag_mapdir { + let parts: Vec<&str> = mapdir.split(':').collect(); + if parts.len() != 2 { + println!("--mapdir argument must contain exactly one colon, separating a guest directory name and a host directory name"); + exit(1); + } + let (key, value) = (parts[0], parts[1]); + let fd = unsafe { + libc::open( + CString::new(value.as_bytes()).unwrap().as_ptr(), + libc::O_RDONLY | libc::O_DIRECTORY, + ) + }; + if fd < 0 { + println!("error while pre-opening directory {}: {}", value, errno()); + exit(1); + } + + preopen_dirs.push((key.to_string(), fd)); + } + + preopen_dirs +} + +/// Compute the argv array values. +fn compute_argv(argv0: &str, arg_arg: &[String]) -> Vec { + let mut result = Vec::new(); + + // Add argv[0], which is the program name. Only include the base name of the + // main wasm module, to avoid leaking path information. + result.push( + Path::new(argv0) + .components() + .next_back() + .map(Component::as_os_str) + .and_then(OsStr::to_str) + .unwrap_or("") + .to_owned(), + ); + + // Add the remaining arguments. + for arg in arg_arg { + result.push(arg.to_owned()); + } + + result +} + +/// Compute the environ array values. +fn compute_environ(flag_env: &[String]) -> Vec<(String, String)> { + let mut result = Vec::new(); + + // Add the environment variables, which must be of the form "key=value". + for env in flag_env { + let split = env.splitn(2, '=').collect::>(); + if split.len() != 2 { + println!( + "environment variables must be of the form \"key=value\"; got \"{}\"", + env + ); + } + result.push((split[0].to_owned(), split[1].to_owned())); + } + + result +} + fn main() { let args: Args = Docopt::new(USAGE) .and_then(|d| { @@ -139,20 +238,52 @@ fn main() { instantiate_spectest().expect("instantiating spectest"), ); + // Make wasi available by default. + let global_exports = context.get_global_exports(); + let preopen_dirs = compute_preopen_dirs(&args.flag_dir, &args.flag_mapdir); + let argv = compute_argv(&args.arg_file, &args.arg_arg); + let environ = compute_environ(&args.flag_env); + context.name_instance( + "wasi_unstable".to_owned(), + instantiate_wasi("", global_exports, &preopen_dirs, &argv, &environ) + .expect("instantiating wasi"), + ); + + // FIXME: Also recognize "env", for compatibility with clang/llvm 8.0. And use + // "__wasi_" prefixes for compaitility with prototype reference-sysroot. + let global_exports = context.get_global_exports(); + context.name_instance( + "env".to_owned(), + instantiate_wasi("__wasi_", global_exports, &preopen_dirs, &argv, &environ) + .expect("instantiating wasi"), + ); + // Enable/disable producing of debug info. context.set_debug_info(args.flag_g); - for filename in &args.arg_file { + // Load the preload wasm modules. + for filename in &args.flag_preload { let path = Path::new(&filename); match handle_module(&mut context, &args, path) { Ok(()) => {} Err(message) => { let name = path.as_os_str().to_string_lossy(); - println!("error while processing {}: {}", name, message); + println!("error while processing preload {}: {}", name, message); exit(1); } } } + + // Load the main wasm module. + let path = Path::new(&args.arg_file); + match handle_module(&mut context, &args, path) { + Ok(()) => {} + Err(message) => { + let name = path.as_os_str().to_string_lossy(); + println!("error while processing main module {}: {}", name, message); + exit(1); + } + } } fn handle_module(context: &mut Context, args: &Args, path: &Path) -> Result<(), String> { diff --git a/wasi-software-architecture.png b/wasi-software-architecture.png new file mode 100644 index 0000000000..6cb8345cc4 Binary files /dev/null and b/wasi-software-architecture.png differ diff --git a/wasmtime-debug/Cargo.toml b/wasmtime-debug/Cargo.toml index 2782f2000a..dc9734fc88 100644 --- a/wasmtime-debug/Cargo.toml +++ b/wasmtime-debug/Cargo.toml @@ -13,7 +13,7 @@ edition = "2018" [dependencies] gimli = "0.17.0" -wasmparser = { version = "0.28.0" } +wasmparser = { version = "0.29.0" } cranelift-codegen = "0.30.0" cranelift-entity = "0.30.0" cranelift-wasm = "0.30.0" diff --git a/wasmtime-jit/src/context.rs b/wasmtime-jit/src/context.rs index abb5f4c287..b017d52a1b 100644 --- a/wasmtime-jit/src/context.rs +++ b/wasmtime-jit/src/context.rs @@ -212,4 +212,12 @@ impl Context { ) -> Result<&'instance [u8], ActionError> { inspect_memory(instance, field_name, start, len) } + + /// Return a handle to the global_exports mapping, needed by some modules + /// for instantiation. + pub fn get_global_exports( + &mut self, + ) -> Rc>>> { + Rc::clone(&mut self.global_exports) + } } diff --git a/wasmtime-runtime/Cargo.toml b/wasmtime-runtime/Cargo.toml index 315184159f..3715e0c016 100644 --- a/wasmtime-runtime/Cargo.toml +++ b/wasmtime-runtime/Cargo.toml @@ -18,9 +18,9 @@ cranelift-wasm = "0.30.0" wasmtime-environ = { path = "../wasmtime-environ", default-features = false } region = "2.0.0" lazy_static = "1.2.0" -libc = { version = "0.2.44", default-features = false } +libc = { version = "0.2.48", default-features = false } errno = "0.2.4" -memoffset = "0.2.1" +memoffset = "0.3.0" cast = { version = "0.2.2", default-features = false } failure = { version = "0.1.3", default-features = false } failure_derive = { version = "0.1.3", default-features = false } @@ -31,7 +31,7 @@ winapi = { version = "0.3.6", features = ["winbase", "memoryapi"] } [build-dependencies] cmake = "0.1.35" -bindgen = "0.47.1" +bindgen = "0.49.0" regex = "1.0.6" [features] diff --git a/wasmtime-wasi/Cargo.toml b/wasmtime-wasi/Cargo.toml new file mode 100644 index 0000000000..f08ac14f74 --- /dev/null +++ b/wasmtime-wasi/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "wasmtime-wasi" +version = "0.0.0" +authors = ["The Cranelift Project Developers"] +publish = false +description = "WASI API support for Wasmtime" +categories = ["wasm"] +repository = "https://github.com/CraneStation/wasmtime" +license = "Apache-2.0 WITH LLVM-exception" +readme = "README.md" + +[dependencies] +wasmtime-runtime = { path = "../wasmtime-runtime" } +wasmtime-environ = { path = "../wasmtime-environ" } +wasmtime-jit = { path = "../wasmtime-jit" } +cranelift-codegen = "0.30.0" +cranelift-entity = "0.30.0" +cranelift-wasm = "0.30.0" +target-lexicon = "0.3.0" +cast = { version = "0.2.2", default-features = false } +log = { version = "0.4.6", default-features = false } +libc = "0.2.50" + +[build-dependencies] +cmake = "0.1.35" +bindgen = "0.49.0" + +[badges] +maintenance = { status = "experimental" } +travis-ci = { repository = "CraneStation/wasmtime" } diff --git a/wasmtime-wasi/LICENSE b/wasmtime-wasi/LICENSE new file mode 100644 index 0000000000..f9d81955f4 --- /dev/null +++ b/wasmtime-wasi/LICENSE @@ -0,0 +1,220 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + diff --git a/wasmtime-wasi/README.md b/wasmtime-wasi/README.md new file mode 100644 index 0000000000..4ee92fc9a8 --- /dev/null +++ b/wasmtime-wasi/README.md @@ -0,0 +1,8 @@ +This is the `wasmtime-wasi` crate, which implements the +WebAssembly System Interface (WASI) API. + +WASI is greatly inspired by and directly derived from [CloudABI]. +It differs in that it has aspirations to expand to a greater +scope, and to better support the needs of WebAssembly engines. + +[CloudABI]: https://cloudabi.org/ diff --git a/wasmtime-wasi/build.rs b/wasmtime-wasi/build.rs new file mode 100644 index 0000000000..ed070ea88b --- /dev/null +++ b/wasmtime-wasi/build.rs @@ -0,0 +1,36 @@ +extern crate bindgen; +extern crate cmake; + +use cmake::Config; +use std::env; +use std::path::PathBuf; + +fn main() { + let dst = Config::new("sandboxed-system-primitives").build(); + + println!("cargo:rustc-link-search=native={}", dst.display()); + println!("cargo:rustc-link-lib=static=SandboxedSystemPrimitives"); + + let bindings_builder = bindgen::Builder::default() + .header("sandboxed-system-primitives/include/wasmtime_ssp.h") + .header("sandboxed-system-primitives/src/posix.h") + .whitelist_function("wasmtime_ssp_.*") + .whitelist_function("fd_table_init") + .whitelist_function("fd_table_insert_existing") + .whitelist_function("fd_prestats_init") + .whitelist_function("fd_prestats_insert") + .whitelist_function("argv_environ_init") + .whitelist_type("__wasi_.*") + .whitelist_type("fd_table") + .whitelist_type("fd_prestats") + .whitelist_type("argv_environ_values") + .whitelist_var("__WASI_.*"); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + + bindings_builder + .generate() + .expect("Unable to generate bindings") + .write_to_file(out_path.join("wasmtime_ssp.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/wasmtime-wasi/js-polyfill/WASI-small.png b/wasmtime-wasi/js-polyfill/WASI-small.png new file mode 100644 index 0000000000..ef55a0bf6d Binary files /dev/null and b/wasmtime-wasi/js-polyfill/WASI-small.png differ diff --git a/wasmtime-wasi/js-polyfill/build.sh b/wasmtime-wasi/js-polyfill/build.sh new file mode 100755 index 0000000000..f87e973776 --- /dev/null +++ b/wasmtime-wasi/js-polyfill/build.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -euo pipefail + +EMCC=emcc + +# TODO: Remove the clang include once Emscripten supports + +"$EMCC" ../sandboxed-system-primitives/src/*.c \ + -DWASMTIME_SSP_WASI_API \ + -DWASMTIME_SSP_STATIC_CURFDS \ + -I../sandboxed-system-primitives/include \ + -Iclang \ + --shell-file shell.html \ + polyfill.c \ + -s WARN_ON_UNDEFINED_SYMBOLS=0 \ + -s EXPORTED_FUNCTIONS="['_main', '_handleFiles', '___wasi_args_get', '___wasi_args_sizes_get', '___wasi_clock_res_get', '___wasi_clock_time_get', '___wasi_environ_get', '___wasi_environ_sizes_get', '___wasi_fd_prestat_get', '___wasi_fd_prestat_dir_name', '___wasi_fd_close', '___wasi_fd_datasync', '___wasi_fd_pread', '___wasi_fd_pwrite', '___wasi_fd_read', '___wasi_fd_renumber', '___wasi_fd_seek', '___wasi_fd_tell', '___wasi_fd_fdstat_get', '___wasi_fd_fdstat_set_flags', '___wasi_fd_fdstat_set_rights', '___wasi_fd_sync', '___wasi_fd_write', '___wasi_fd_advise', '___wasi_fd_allocate', '___wasi_path_create_directory', '___wasi_path_link', '___wasi_path_open', '___wasi_fd_readdir', '___wasi_path_readlink', '___wasi_path_rename', '___wasi_fd_filestat_get', '___wasi_fd_filestat_set_times', '___wasi_fd_filestat_set_size', '___wasi_path_filestat_get', '___wasi_path_filestat_set_times', '___wasi_path_symlink', '___wasi_path_unlink_file', '___wasi_path_remove_directory', '___wasi_poll_oneoff', '___wasi_proc_exit', '___wasi_proc_raise', '___wasi_random_get', '___wasi_sched_yield', '___wasi_sock_recv', '___wasi_sock_send', '___wasi_sock_shutdown']" \ + --pre-js wasi.js \ + -o polyfill.html diff --git a/wasmtime-wasi/js-polyfill/clang/stdatomic.h b/wasmtime-wasi/js-polyfill/clang/stdatomic.h new file mode 100644 index 0000000000..b4845a74e4 --- /dev/null +++ b/wasmtime-wasi/js-polyfill/clang/stdatomic.h @@ -0,0 +1,190 @@ +/*===---- stdatomic.h - Standard header for atomic types and operations -----=== + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + *===-----------------------------------------------------------------------=== + */ + +#ifndef __CLANG_STDATOMIC_H +#define __CLANG_STDATOMIC_H + +/* If we're hosted, fall back to the system's stdatomic.h. FreeBSD, for + * example, already has a Clang-compatible stdatomic.h header. + */ +#if __STDC_HOSTED__ && __has_include_next() +# include_next +#else + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* 7.17.1 Introduction */ + +#define ATOMIC_BOOL_LOCK_FREE __CLANG_ATOMIC_BOOL_LOCK_FREE +#define ATOMIC_CHAR_LOCK_FREE __CLANG_ATOMIC_CHAR_LOCK_FREE +#define ATOMIC_CHAR16_T_LOCK_FREE __CLANG_ATOMIC_CHAR16_T_LOCK_FREE +#define ATOMIC_CHAR32_T_LOCK_FREE __CLANG_ATOMIC_CHAR32_T_LOCK_FREE +#define ATOMIC_WCHAR_T_LOCK_FREE __CLANG_ATOMIC_WCHAR_T_LOCK_FREE +#define ATOMIC_SHORT_LOCK_FREE __CLANG_ATOMIC_SHORT_LOCK_FREE +#define ATOMIC_INT_LOCK_FREE __CLANG_ATOMIC_INT_LOCK_FREE +#define ATOMIC_LONG_LOCK_FREE __CLANG_ATOMIC_LONG_LOCK_FREE +#define ATOMIC_LLONG_LOCK_FREE __CLANG_ATOMIC_LLONG_LOCK_FREE +#define ATOMIC_POINTER_LOCK_FREE __CLANG_ATOMIC_POINTER_LOCK_FREE + +/* 7.17.2 Initialization */ + +#define ATOMIC_VAR_INIT(value) (value) +#define atomic_init __c11_atomic_init + +/* 7.17.3 Order and consistency */ + +typedef enum memory_order { + memory_order_relaxed = __ATOMIC_RELAXED, + memory_order_consume = __ATOMIC_CONSUME, + memory_order_acquire = __ATOMIC_ACQUIRE, + memory_order_release = __ATOMIC_RELEASE, + memory_order_acq_rel = __ATOMIC_ACQ_REL, + memory_order_seq_cst = __ATOMIC_SEQ_CST +} memory_order; + +#define kill_dependency(y) (y) + +/* 7.17.4 Fences */ + +/* These should be provided by the libc implementation. */ +void atomic_thread_fence(memory_order); +void atomic_signal_fence(memory_order); + +#define atomic_thread_fence(order) __c11_atomic_thread_fence(order) +#define atomic_signal_fence(order) __c11_atomic_signal_fence(order) + +/* 7.17.5 Lock-free property */ + +#define atomic_is_lock_free(obj) __c11_atomic_is_lock_free(sizeof(*(obj))) + +/* 7.17.6 Atomic integer types */ + +#ifdef __cplusplus +typedef _Atomic(bool) atomic_bool; +#else +typedef _Atomic(_Bool) atomic_bool; +#endif +typedef _Atomic(char) atomic_char; +typedef _Atomic(signed char) atomic_schar; +typedef _Atomic(unsigned char) atomic_uchar; +typedef _Atomic(short) atomic_short; +typedef _Atomic(unsigned short) atomic_ushort; +typedef _Atomic(int) atomic_int; +typedef _Atomic(unsigned int) atomic_uint; +typedef _Atomic(long) atomic_long; +typedef _Atomic(unsigned long) atomic_ulong; +typedef _Atomic(long long) atomic_llong; +typedef _Atomic(unsigned long long) atomic_ullong; +typedef _Atomic(uint_least16_t) atomic_char16_t; +typedef _Atomic(uint_least32_t) atomic_char32_t; +typedef _Atomic(wchar_t) atomic_wchar_t; +typedef _Atomic(int_least8_t) atomic_int_least8_t; +typedef _Atomic(uint_least8_t) atomic_uint_least8_t; +typedef _Atomic(int_least16_t) atomic_int_least16_t; +typedef _Atomic(uint_least16_t) atomic_uint_least16_t; +typedef _Atomic(int_least32_t) atomic_int_least32_t; +typedef _Atomic(uint_least32_t) atomic_uint_least32_t; +typedef _Atomic(int_least64_t) atomic_int_least64_t; +typedef _Atomic(uint_least64_t) atomic_uint_least64_t; +typedef _Atomic(int_fast8_t) atomic_int_fast8_t; +typedef _Atomic(uint_fast8_t) atomic_uint_fast8_t; +typedef _Atomic(int_fast16_t) atomic_int_fast16_t; +typedef _Atomic(uint_fast16_t) atomic_uint_fast16_t; +typedef _Atomic(int_fast32_t) atomic_int_fast32_t; +typedef _Atomic(uint_fast32_t) atomic_uint_fast32_t; +typedef _Atomic(int_fast64_t) atomic_int_fast64_t; +typedef _Atomic(uint_fast64_t) atomic_uint_fast64_t; +typedef _Atomic(intptr_t) atomic_intptr_t; +typedef _Atomic(uintptr_t) atomic_uintptr_t; +typedef _Atomic(size_t) atomic_size_t; +typedef _Atomic(ptrdiff_t) atomic_ptrdiff_t; +typedef _Atomic(intmax_t) atomic_intmax_t; +typedef _Atomic(uintmax_t) atomic_uintmax_t; + +/* 7.17.7 Operations on atomic types */ + +#define atomic_store(object, desired) __c11_atomic_store(object, desired, __ATOMIC_SEQ_CST) +#define atomic_store_explicit __c11_atomic_store + +#define atomic_load(object) __c11_atomic_load(object, __ATOMIC_SEQ_CST) +#define atomic_load_explicit __c11_atomic_load + +#define atomic_exchange(object, desired) __c11_atomic_exchange(object, desired, __ATOMIC_SEQ_CST) +#define atomic_exchange_explicit __c11_atomic_exchange + +#define atomic_compare_exchange_strong(object, expected, desired) __c11_atomic_compare_exchange_strong(object, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) +#define atomic_compare_exchange_strong_explicit __c11_atomic_compare_exchange_strong + +#define atomic_compare_exchange_weak(object, expected, desired) __c11_atomic_compare_exchange_weak(object, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) +#define atomic_compare_exchange_weak_explicit __c11_atomic_compare_exchange_weak + +#define atomic_fetch_add(object, operand) __c11_atomic_fetch_add(object, operand, __ATOMIC_SEQ_CST) +#define atomic_fetch_add_explicit __c11_atomic_fetch_add + +#define atomic_fetch_sub(object, operand) __c11_atomic_fetch_sub(object, operand, __ATOMIC_SEQ_CST) +#define atomic_fetch_sub_explicit __c11_atomic_fetch_sub + +#define atomic_fetch_or(object, operand) __c11_atomic_fetch_or(object, operand, __ATOMIC_SEQ_CST) +#define atomic_fetch_or_explicit __c11_atomic_fetch_or + +#define atomic_fetch_xor(object, operand) __c11_atomic_fetch_xor(object, operand, __ATOMIC_SEQ_CST) +#define atomic_fetch_xor_explicit __c11_atomic_fetch_xor + +#define atomic_fetch_and(object, operand) __c11_atomic_fetch_and(object, operand, __ATOMIC_SEQ_CST) +#define atomic_fetch_and_explicit __c11_atomic_fetch_and + +/* 7.17.8 Atomic flag type and operations */ + +typedef struct atomic_flag { atomic_bool _Value; } atomic_flag; + +#define ATOMIC_FLAG_INIT { 0 } + +/* These should be provided by the libc implementation. */ +#ifdef __cplusplus +bool atomic_flag_test_and_set(volatile atomic_flag *); +bool atomic_flag_test_and_set_explicit(volatile atomic_flag *, memory_order); +#else +_Bool atomic_flag_test_and_set(volatile atomic_flag *); +_Bool atomic_flag_test_and_set_explicit(volatile atomic_flag *, memory_order); +#endif +void atomic_flag_clear(volatile atomic_flag *); +void atomic_flag_clear_explicit(volatile atomic_flag *, memory_order); + +#define atomic_flag_test_and_set(object) __c11_atomic_exchange(&(object)->_Value, 1, __ATOMIC_SEQ_CST) +#define atomic_flag_test_and_set_explicit(object, order) __c11_atomic_exchange(&(object)->_Value, 1, order) + +#define atomic_flag_clear(object) __c11_atomic_store(&(object)->_Value, 0, __ATOMIC_SEQ_CST) +#define atomic_flag_clear_explicit(object, order) __c11_atomic_store(&(object)->_Value, 0, order) + +#ifdef __cplusplus +} +#endif + +#endif /* __STDC_HOSTED__ */ +#endif /* __CLANG_STDATOMIC_H */ + diff --git a/wasmtime-wasi/js-polyfill/polyfill.c b/wasmtime-wasi/js-polyfill/polyfill.c new file mode 100644 index 0000000000..a615225ead --- /dev/null +++ b/wasmtime-wasi/js-polyfill/polyfill.c @@ -0,0 +1,45 @@ +#include +#include "wasmtime_ssp.h" +#include "../src/posix.h" + +static __thread struct fd_table curfds_pointee; + +int main(int argc, char *argv[]) { + return 0; +} + +void handleFiles(void) { + struct fd_table *curfds = &curfds_pointee; + + fd_table_init(curfds); + + // Prepopulate curfds with stdin, stdout, and stderr file descriptors. + if (!fd_table_insert_existing(curfds, 0, 0)) + __builtin_trap(); + if (!fd_table_insert_existing(curfds, 1, 1)) + __builtin_trap(); + if (!fd_table_insert_existing(curfds, 2, 2)) + __builtin_trap(); + + EM_ASM(" \ + const imports = { wasi_unstable: WASIPolyfill }; \ + let file = document.getElementById('input').files[0]; \ + let file_with_mime_type = file.slice(0, file.size, 'application/wasm'); \ + let response = new Response(file_with_mime_type); \ + WebAssembly.instantiateStreaming(response, imports) \ + .then(obj => { \ + setInstance(obj.instance); \ + try { \ + obj.instance.exports._start(); \ + } catch (e) { \ + if (e instanceof WASIExit) { \ + handleWASIExit(e); \ + } else { \ + } \ + } \ + }) \ + .catch(error => { \ + console.log('error! ' + error); \ + }); \ + "); +} diff --git a/wasmtime-wasi/js-polyfill/shell.html b/wasmtime-wasi/js-polyfill/shell.html new file mode 100644 index 0000000000..f44eda52ca --- /dev/null +++ b/wasmtime-wasi/js-polyfill/shell.html @@ -0,0 +1,88 @@ + + + + + + + WASI Web Polyfill + + + +
WASI
+
Downloading...
+
+ +
+ WASI logo + +
+ + + {{{ SCRIPT }}} + + diff --git a/wasmtime-wasi/js-polyfill/wasi.js b/wasmtime-wasi/js-polyfill/wasi.js new file mode 100644 index 0000000000..11dfc82b59 --- /dev/null +++ b/wasmtime-wasi/js-polyfill/wasi.js @@ -0,0 +1,486 @@ +// To implement `proc_exit`, we define a custom exception object +// that we can throw to unwind the stack and carry the exit value. +function WASIExit(return_value, message, fileName, lineNumber) { + let instance = new Error(message, fileName, lineNumber); + instance.return_value = return_value; + Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); + if (Error.captureStackTrace) { + Error.captureStackTrace(instance, WASIExit); + } + return instance; +} + +WASIExit.prototype = Object.create(Error.prototype, { + constructor: { + value: Error, + enumerable: false, + writable: true, + configurable: true + } +}); + +if (Object.setPrototypeOf) { + Object.setPrototypeOf(WASIExit, Error); +} else { + WASIExit.__proto__ = Error; +} + +function handleWASIExit(e) { + if (e.return_value != 0) { + console.log('program exited with non-zero exit status ' + e.return_value); + } +} + +// The current guest wasm instance. +var currentInstance; + +// There are two heaps in play, the guest heap, which belongs to the WASI-using +// program, and the host heap, which belongs to the Emscripten-compiled polyfill +// library. The following declare support for the guest heap in a similar manner +// to Emscripten's heap. + +var GUEST_HEAP, +/** @type {ArrayBuffer} */ + GUEST_buffer, +/** @type {Int8Array} */ + GUEST_HEAP8, +/** @type {Uint8Array} */ + GUEST_HEAPU8, +/** @type {Int16Array} */ + GUEST_HEAP16, +/** @type {Uint16Array} */ + GUEST_HEAPU16, +/** @type {Int32Array} */ + GUEST_HEAP32, +/** @type {Uint32Array} */ + GUEST_HEAPU32, +/** @type {Float32Array} */ + GUEST_HEAPF32, +/** @type {Float64Array} */ + GUEST_HEAPF64; + +function setInstance(instance) { + currentInstance = instance; + updateGuestBuffer(); +} + +/// We call updateGuestBuffer any time the guest's memory may have changed, +/// such as when creating a new instance, or after calling _malloc. +function updateGuestBuffer() { + var buf = currentInstance.exports.memory.buffer; + Module['GUEST_buffer'] = GUEST_buffer = buf; + Module['GUEST_HEAP8'] = GUEST_HEAP8 = new Int8Array(GUEST_buffer); + Module['GUEST_HEAP16'] = GUEST_HEAP16 = new Int16Array(GUEST_buffer); + Module['GUEST_HEAP32'] = GUEST_HEAP32 = new Int32Array(GUEST_buffer); + Module['GUEST_HEAPU8'] = GUEST_HEAPU8 = new Uint8Array(GUEST_buffer); + Module['GUEST_HEAPU16'] = GUEST_HEAPU16 = new Uint16Array(GUEST_buffer); + Module['GUEST_HEAPU32'] = GUEST_HEAPU32 = new Uint32Array(GUEST_buffer); + Module['GUEST_HEAPF32'] = GUEST_HEAPF32 = new Float32Array(GUEST_buffer); + Module['GUEST_HEAPF64'] = GUEST_HEAPF64 = new Float64Array(GUEST_buffer); +} + +function copyin_bytes(src, len) { + let dst = _malloc(len); + updateGuestBuffer(); + + for (let i = 0; i < len; ++i) { + HEAP8[dst + i] = GUEST_HEAP8[src + i]; + } + return dst; +} + +function copyout_bytes(dst, src, len) { + updateGuestBuffer(); + + for (let i = 0; i < len; ++i) { + GUEST_HEAP8[dst + i] = HEAP8[src + i]; + } + _free(src); +} + +function copyout_i32(dst, src) { + updateGuestBuffer(); + + GUEST_HEAP32[dst>>2] = HEAP32[src>>2]; + _free(src); +} + +function copyout_i64(dst, src) { + updateGuestBuffer(); + + GUEST_HEAP32[dst>>2] = HEAP32[src>>2]; + GUEST_HEAP32[(dst + 4)>>2] = HEAP32[(src + 4)>>2]; + _free(src); +} + +function translate_ciovs(iovs, iovs_len) { + host_iovs = _malloc(8 * iovs_len); + updateGuestBuffer(); + + for (let i = 0; i < iovs_len; ++i) { + let ptr = GUEST_HEAP32[(iovs + i * 8 + 0) >> 2]; + let len = GUEST_HEAP32[(iovs + i * 8 + 4) >> 2]; + let buf = copyin_bytes(ptr, len); + HEAP32[(host_iovs + i * 8 + 0)>>2] = buf; + HEAP32[(host_iovs + i * 8 + 4)>>2] = len; + } + return host_iovs; +} + +function free_ciovs(host_iovs, iovs_len) { + for (let i = 0; i < iovs_len; ++i) { + let buf = HEAP32[(host_iovs + i * 8 + 0) >> 2]; + _free(buf); + } + _free(host_iovs); +} + +function translate_iovs(iovs, iovs_len) { + host_iovs = _malloc(8 * iovs_len); + updateGuestBuffer(); + + for (let i = 0; i < iovs_len; ++i) { + let len = GUEST_HEAP32[(iovs + i * 8 + 4) >> 2]; + let buf = _malloc(len); + updateGuestBuffer(); + HEAP32[(host_iovs + i * 8 + 0)>>2] = buf; + HEAP32[(host_iovs + i * 8 + 4)>>2] = len; + } + return host_iovs; +} + +function free_iovs(host_iovs, iovs_len, iovs) { + updateGuestBuffer(); + for (let i = 0; i < iovs_len; ++i) { + let buf = HEAP32[(host_iovs + i * 8 + 0) >> 2]; + let len = HEAP32[(host_iovs + i * 8 + 4) >> 2]; + let ptr = GUEST_HEAP32[(host_iovs + i * 8 + 0) >> 2]; + copyout_bytes(ptr, buf, len); + } + _free(host_iovs); +} + +var WASIPolyfill = { + +args_get: function(argv, argv_buf) { + return 0; +}, + +args_sizes_get: function(argc, argv_buf_size) { + updateGuestBuffer(); + + // TODO: Implement command-line arguments. + GUEST_HEAP32[(argc) >> 2] = 0; + GUEST_HEAP32[(argv_buf_size) >> 2] = 0; + return 0; +}, + +clock_res_get: function(clock_id, resolution) { + let host_resolution = _malloc(8); + let ret = ___wasi_clock_res_get(clock_id, host_resolution); + copyout_i64(resolution, host_resolution); + return ret; +}, + +clock_time_get: function(clock_id, precision, time) { + let host_time = _malloc(8); + let ret = ___wasi_clock_time_get(clock_id, precision, host_time); + copyout_i64(time, host_time); + return ret; +}, + +environ_get: function(environ, environ_buf) { + return 0; +}, + +environ_sizes_get: function(environ_size, environ_buf_size) { + updateGuestBuffer(); + + // TODO: Implement environment variables. + GUEST_HEAP32[(environ_size) >> 2] = 0; + GUEST_HEAP32[(environ_buf_size) >> 2] = 0; + return 0; +}, + +fd_prestat_get: function(fd, buf) { + let host_buf = _malloc(8); // sizeof __wasi_prestat_t + let ret = ___wasi_fd_prestat_get(fd, host_buf); + copyout_bytes(buf, host_buf, 8); + return ret; +}, + +fd_prestat_dir_name: function(fd, path, path_len) { + let host_buf = _malloc(path_len); + let ret = ___wasi_fd_prestat_get(fd, host_buf, path_len); + copyout_bytes(buf, host_buf, path_len); + return ret; +}, + +fd_close: function(fd) { + return ___wasi_fd_close(fd); +}, + +fd_datasync: function(fd) { + return ___wasi_fd_datasync(fd); +}, + +fd_pread: function(fd, iovs, iovs_len, offset, nread) { + let host_iovs = translate_iovs(iovs, iovs_len); + let host_nread = _malloc(4); + let ret = ___wasi_fd_pread(fd, host_iovs, iovs_len, offset, host_nread); + copyout_i32(nread, host_nread); + free_iovs(host_iovs, iovs_len); + return ret; +}, + +fd_pwrite: function(fd, iovs, iovs_len, offset, nwritten) { + let host_iovs = translate_ciovs(iovs, iovs_len); + let host_nwritten = _malloc(4); + let ret = ___wasi_fd_pwrite(fd, host_iovs, iovs_len, offset, host_nwritten); + copyout_i32(nwritten, host_nwritten); + free_ciovs(host_iovs, iovs_len); + return ret; +}, + +fd_read: function(fd, iovs, iovs_len, nread) { + let host_iovs = translate_iovs(iovs, iovs_len); + let host_nread = _malloc(4); + let ret = ___wasi_fd_read(fd, host_iovs, iovs_len, host_nread); + copyout_i32(nread, host_nread); + free_iovs(host_iovs, iovs_len); + return ret; +}, + +fd_renumber: function(from, to) { + return ___wasi_fd_renumber(from, to); +}, + +fd_seek: function(fd, offset, whence, newoffset) { + let host_newoffset = _malloc(8); + let ret = ___wasi_fd_seek(fd, offset, whence, host_newoffset); + copyout_i64(newoffset, host_newoffset); + return ret; +}, + +fd_tell: function(fd, newoffset) { + let host_newoffset = _malloc(8); + let ret = ___wasi_fd_seek(fd, host_newoffset); + copyout_i64(newoffset, host_newoffset); + return ret; +}, + +fd_fdstat_get: function(fd, buf) { + let host_buf = _malloc(24); // sizeof __wasi_fdstat_t + let ret = ___wasi_fd_fdstat_get(fd, host_buf); + copyout_bytes(buf, host_buf, 24); + return ret; +}, + +fd_fdstat_set_flags: function(fd, flags) { + return ___wasi_fd_fdstat_set_flags(fd, flags); +}, + +fd_fdstat_set_rights: function(fd, fs_rights_base, fs_rights_inheriting) { + return ___wasi_fd_fdstat_set_rights(fd, fs_rights_base, fs_rights_inheriting); +}, + +fd_sync: function(fd) { + return ___wasi_fd_sync(fd); +}, + +fd_write: function(fd, iovs, iovs_len, nwritten) { + let host_iovs = translate_ciovs(iovs, iovs_len); + let host_nwritten = _malloc(4); + let ret = ___wasi_fd_write(fd, host_iovs, iovs_len, host_nwritten); + copyout_i32(nwritten, host_nwritten); + free_ciovs(host_iovs, iovs_len); + return ret; +}, + +fd_advise: function(fd, offset, len, advice) { + return ___wasi_fd_advise(fd, offset, len, advice); +}, + +fd_allocate: function(fd, offset, len) { + return ___wasi_fd_allocate(fd, offset, len); +}, + +path_create_directory: function(fd, path, path_len) { + let host_path = copyin_bytes(path, path_len); + let ret = ___wasi_path_create_directory(fd, host_path, path_len); + _free(host_path); + return ret; +}, + +path_link: function(fd0, path0, path_len0, fd1, path1, path_len1) { + let host_path0 = copyin_bytes(path0, path_len0); + let host_path1 = copyin_bytes(path1, path_len1); + let ret = ___wasi_path_link(fd, host_path0, path_len0, fd1, host_path1, path1_len); + _free(host_path1); + _free(host_path0); + return ret; +}, + +path_open: function(dirfd, dirflags, path, path_len, oflags, fs_rights_base, fs_rights_inheriting, fs_flags, fd) { + let host_path = copyin_bytes(path, path_len); + let host_fd = _malloc(4); + let ret = ___wasi_path_open(dirfd, dirflags, host_path, path_len, oflags, fs_rights_base, fs_rights_inheriting, fs_flags, host_fd); + copyout_i32(fd, host_fd); + _free(host_path); + return ret; +}, + +fd_readdir: function(fd, buf, buf_len, cookie, buf_used) { + let host_buf = _malloc(buf_len); + let host_buf_used = _malloc(4); + let ret = ___wasi_fd_readdir(fd, buf, buf_len, cookie, host_buf_used); + copyout_i32(buf_used, host_buf_used); + copyout_bytes(buf, host_buf, buf_len); + return ret; +}, + +path_readlink: function(fd, path, path_len, buf, buf_len, buf_used) { + let host_path = copyin_bytes(path, path_len); + let host_buf = _malloc(buf_len); + let host_buf_used = _malloc(4); + let ret = ___wasi_path_readlink(fd, path, path_len, buf, buf_len, host_buf_used); + copyout_i32(buf_used, host_buf_used); + copyout_bytes(buf, host_buf, buf_len); + _free(host_path); + return ret; +}, + +path_rename: function(fd0, path0, path_len0, fd1, path1, path_len1) { + let host_path0 = copyin_bytes(path0, path_len0); + let host_path1 = copyin_bytes(path1, path_len1); + let ret = ___wasi_path_rename(fd, host_path0, path_len0, fd1, host_path1, path1_len); + _free(host_path1); + _free(host_path0); + return ret; +}, + +fd_filestat_get: function(fd, buf) { + let host_buf = _malloc(56); // sizeof __wasi_filestat_t + let ret = ___wasi_fd_filestat_get(host_buf); + copyout_bytes(buf, host_buf, 56); + return ret; +}, + +fd_filestat_set_size: function(fd, size) { + return ___wasi_fd_filestat_set_size(fd, size); +}, + +fd_filestat_set_times: function(fd, st_atim, st_mtim, fstflags) { + return ___wasi_fd_filestat_set_times(fd, st_atim, st_mtim, fstflags); +}, + +path_filestat_get: function(fd, path, path_len, buf) { + let host_path = copyin_bytes(path, path_len); + let host_buf = _malloc(56); // sizeof __wasi_filestat_t + let ret = ___wasi_path_filestat_get(fd, host_path, path_len, host_buf); + copyout_bytes(buf, host_buf, 56); + _free(host_path); + return ret; +}, + +path_filestat_set_times: function(fd, path, path_len, st_atim, st_mtim, flags) { + let host_path = copyin_bytes(path, path_len); + let ret = ___wasi_path_filestat_set_times(fd, host_path, st_atim, st_mtim, fstflags); + _free(host_path); + return ret; +}, + +path_symlink: function(path0, path_len0, fd, path1, path_len1) { + let host_path0 = copyin_bytes(path0, path0_len); + let host_path1 = copyin_bytes(path1, path1_len); + let ret = ___wasi_path_symlink(host_path0, path_len0, fd, host_path1, path_len1); + _free(host_path1); + _free(host_path0); + return ret; +}, + +path_unlink_file: function(fd, path, path_len, flags) { + let host_path = copyin_bytes(path, path_len); + let ret = ___wasi_path_unlink_file(fd, host_path, path_len, flags); + _free(host_path); + return ret; +}, + +path_remove_directory: function(fd, path, path_len, flags) { + let host_path = copyin_bytes(path, path_len); + let ret = ___wasi_path_remove_directory(fd, host_path, path_len, flags); + _free(host_path); + return ret; +}, + +poll_oneoff: function(in_, out, nsubscriptions, nevents) { + let host_in = copyin_bytes(in_, nsubscriptions * 56); // sizeof __wasi_subscription_t + let host_out = _malloc(nsubscriptions * 32); // sizeof __wasi_event_t + let host_nevents = _malloc(4); + let ret = ___wasi_poll_oneoff(host_in, host_out, host_nevents); + copyout_bytes(out, host_out, nsubscriptions * 32); + copyout_i32(nevents, host_nevents); + _free(host_in); + return ret; +}, + +proc_exit: function(rval) { + let message; + if (rval == 0) { + message = "success"; + } else { + message = "error code " + rval; + } + throw new WASIExit(rval, message); +}, + +proc_raise: function(sig) { + if (sig == 18 || // SIGSTOP + sig == 19 || // SIGTSTP + sig == 20 || // SIGTTIN + sig == 21 || // SIGTTOU + sig == 22 || // SIGURG + sig == 16 || // SIGCHLD + sig == 13) // SIGPIPE + { + return 0; + } + + let message = "raised signal " + sig; + throw new WASIExit(128 + sig, message); +}, + +random_get: function(buf, buf_len) { + let host_buf = _malloc(buf_len); + let ret = __wasi_random_get(host_buf, buf_len); + copyout_bytes(buf, host_buf, buf_len); + return ret; +}, + +sched_yield: function() { + return __wasi_sched_yield(); +}, + +sock_recv: function(sock, ri_data, ri_data_len, ri_flags, ro_datalen, ro_flags) { + let host_ri_data = translate_iovs(ri_data, ri_data_len); + let host_ro_datalen = _malloc(4); + let ret = ___wasi_sock_recv(sock, host_ri_data, ri_data_len, ri_flags, host_ro_data, ro_flags); + copyout_i32(ro_datalen, host_ro_datalen); + free_iovs(host_ri_data, ri_data_len); + return ret; +}, + +sock_send: function(sock, si_data, si_data_len, si_flags, so_datalen) { + let host_si_data = translate_ciovs(si_data, si_data_len); + let host_so_datalen = _malloc(4); + let ret = ___wasi_sock_send(sock, host_si_data, si_data_len, si_flags, host_so_datalen); + copyout_i32(so_datalen, host_so_datalen); + free_ciovs(host_si_data, si_data_len); + return ret; +}, + +sock_shutdown: function(sock, how) { + return __wasi_sock_shutdown(sock, how); +} + +}; diff --git a/wasmtime-wasi/sandboxed-system-primitives/CMakeLists.txt b/wasmtime-wasi/sandboxed-system-primitives/CMakeLists.txt new file mode 100644 index 0000000000..3fb11b3b77 --- /dev/null +++ b/wasmtime-wasi/sandboxed-system-primitives/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.0) +project(SandboxedSystemPrimitives C) + +include_directories(include) + +add_library(SandboxedSystemPrimitives STATIC src/posix.c src/random.c src/str.c) + +install(TARGETS SandboxedSystemPrimitives DESTINATION .) diff --git a/wasmtime-wasi/sandboxed-system-primitives/LICENSE b/wasmtime-wasi/sandboxed-system-primitives/LICENSE new file mode 100644 index 0000000000..83386f80ae --- /dev/null +++ b/wasmtime-wasi/sandboxed-system-primitives/LICENSE @@ -0,0 +1,7 @@ +Please see the LICENSE file in each top-level directory for the terms applicable to that directory and its relative sub-directories. + +The relevant directories and licenses are: + +src/ - BSD-2-Clause; see src/LICENSE for details +include/ - CC0 1.0 Universal (CC0 1.0) Public Domain Dedication +polyfill/clang/ - MIT; see the header of polyfill/clang/stdatomic.h for details diff --git a/wasmtime-wasi/sandboxed-system-primitives/README.md b/wasmtime-wasi/sandboxed-system-primitives/README.md new file mode 100644 index 0000000000..feba35e9c3 --- /dev/null +++ b/wasmtime-wasi/sandboxed-system-primitives/README.md @@ -0,0 +1,9 @@ +This repository contains adapted forms of the CloudABI project's "libemulator" +library, which includes implementations the CloudABI system calls using +standard native platform support. See the README.md and LICENSE files in +the individual subdirectories for details. + +src/ - cloudabi-utils' libemulator; see src/README.md for details +include/ - wasi headers + +This is currently an experimental prototype. diff --git a/wasmtime-wasi/sandboxed-system-primitives/include/LICENSE b/wasmtime-wasi/sandboxed-system-primitives/include/LICENSE new file mode 100644 index 0000000000..0e259d42c9 --- /dev/null +++ b/wasmtime-wasi/sandboxed-system-primitives/include/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/wasmtime-wasi/sandboxed-system-primitives/include/wasmtime_ssp.h b/wasmtime-wasi/sandboxed-system-primitives/include/wasmtime_ssp.h new file mode 100644 index 0000000000..992cb24872 --- /dev/null +++ b/wasmtime-wasi/sandboxed-system-primitives/include/wasmtime_ssp.h @@ -0,0 +1,865 @@ +/* + * Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. + * See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. + * + * This file declares an interface similar to WASI, but augmented to expose + * some implementation details such as the curfds arguments that we pass + * around to avoid storing them in TLS. + */ + +#ifndef WASMTIME_SSP_H +#define WASMTIME_SSP_H + +#include +#include + +_Static_assert(_Alignof(int8_t) == 1, "non-wasi data layout"); +_Static_assert(_Alignof(uint8_t) == 1, "non-wasi data layout"); +_Static_assert(_Alignof(int16_t) == 2, "non-wasi data layout"); +_Static_assert(_Alignof(uint16_t) == 2, "non-wasi data layout"); +_Static_assert(_Alignof(int32_t) == 4, "non-wasi data layout"); +_Static_assert(_Alignof(uint32_t) == 4, "non-wasi data layout"); +_Static_assert(_Alignof(int64_t) == 8, "non-wasi data layout"); +_Static_assert(_Alignof(uint64_t) == 8, "non-wasi data layout"); + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint8_t __wasi_advice_t; +#define __WASI_ADVICE_NORMAL (0) +#define __WASI_ADVICE_SEQUENTIAL (1) +#define __WASI_ADVICE_RANDOM (2) +#define __WASI_ADVICE_WILLNEED (3) +#define __WASI_ADVICE_DONTNEED (4) +#define __WASI_ADVICE_NOREUSE (5) + +typedef uint32_t __wasi_clockid_t; +#define __WASI_CLOCK_REALTIME (0) +#define __WASI_CLOCK_MONOTONIC (1) +#define __WASI_CLOCK_PROCESS_CPUTIME_ID (2) +#define __WASI_CLOCK_THREAD_CPUTIME_ID (3) + +typedef uint64_t __wasi_device_t; + +typedef uint64_t __wasi_dircookie_t; +#define __WASI_DIRCOOKIE_START (0) + +typedef uint16_t __wasi_errno_t; +#define __WASI_ESUCCESS (0) +#define __WASI_E2BIG (1) +#define __WASI_EACCES (2) +#define __WASI_EADDRINUSE (3) +#define __WASI_EADDRNOTAVAIL (4) +#define __WASI_EAFNOSUPPORT (5) +#define __WASI_EAGAIN (6) +#define __WASI_EALREADY (7) +#define __WASI_EBADF (8) +#define __WASI_EBADMSG (9) +#define __WASI_EBUSY (10) +#define __WASI_ECANCELED (11) +#define __WASI_ECHILD (12) +#define __WASI_ECONNABORTED (13) +#define __WASI_ECONNREFUSED (14) +#define __WASI_ECONNRESET (15) +#define __WASI_EDEADLK (16) +#define __WASI_EDESTADDRREQ (17) +#define __WASI_EDOM (18) +#define __WASI_EDQUOT (19) +#define __WASI_EEXIST (20) +#define __WASI_EFAULT (21) +#define __WASI_EFBIG (22) +#define __WASI_EHOSTUNREACH (23) +#define __WASI_EIDRM (24) +#define __WASI_EILSEQ (25) +#define __WASI_EINPROGRESS (26) +#define __WASI_EINTR (27) +#define __WASI_EINVAL (28) +#define __WASI_EIO (29) +#define __WASI_EISCONN (30) +#define __WASI_EISDIR (31) +#define __WASI_ELOOP (32) +#define __WASI_EMFILE (33) +#define __WASI_EMLINK (34) +#define __WASI_EMSGSIZE (35) +#define __WASI_EMULTIHOP (36) +#define __WASI_ENAMETOOLONG (37) +#define __WASI_ENETDOWN (38) +#define __WASI_ENETRESET (39) +#define __WASI_ENETUNREACH (40) +#define __WASI_ENFILE (41) +#define __WASI_ENOBUFS (42) +#define __WASI_ENODEV (43) +#define __WASI_ENOENT (44) +#define __WASI_ENOEXEC (45) +#define __WASI_ENOLCK (46) +#define __WASI_ENOLINK (47) +#define __WASI_ENOMEM (48) +#define __WASI_ENOMSG (49) +#define __WASI_ENOPROTOOPT (50) +#define __WASI_ENOSPC (51) +#define __WASI_ENOSYS (52) +#define __WASI_ENOTCONN (53) +#define __WASI_ENOTDIR (54) +#define __WASI_ENOTEMPTY (55) +#define __WASI_ENOTRECOVERABLE (56) +#define __WASI_ENOTSOCK (57) +#define __WASI_ENOTSUP (58) +#define __WASI_ENOTTY (59) +#define __WASI_ENXIO (60) +#define __WASI_EOVERFLOW (61) +#define __WASI_EOWNERDEAD (62) +#define __WASI_EPERM (63) +#define __WASI_EPIPE (64) +#define __WASI_EPROTO (65) +#define __WASI_EPROTONOSUPPORT (66) +#define __WASI_EPROTOTYPE (67) +#define __WASI_ERANGE (68) +#define __WASI_EROFS (69) +#define __WASI_ESPIPE (70) +#define __WASI_ESRCH (71) +#define __WASI_ESTALE (72) +#define __WASI_ETIMEDOUT (73) +#define __WASI_ETXTBSY (74) +#define __WASI_EXDEV (75) +#define __WASI_ENOTCAPABLE (76) + +typedef uint16_t __wasi_eventrwflags_t; +#define __WASI_EVENT_FD_READWRITE_HANGUP (0x0001) + +typedef uint8_t __wasi_eventtype_t; +#define __WASI_EVENTTYPE_CLOCK (0) +#define __WASI_EVENTTYPE_FD_READ (1) +#define __WASI_EVENTTYPE_FD_WRITE (2) + +typedef uint32_t __wasi_exitcode_t; + +typedef uint32_t __wasi_fd_t; + +typedef uint16_t __wasi_fdflags_t; +#define __WASI_FDFLAG_APPEND (0x0001) +#define __WASI_FDFLAG_DSYNC (0x0002) +#define __WASI_FDFLAG_NONBLOCK (0x0004) +#define __WASI_FDFLAG_RSYNC (0x0008) +#define __WASI_FDFLAG_SYNC (0x0010) + +typedef int64_t __wasi_filedelta_t; + +typedef uint64_t __wasi_filesize_t; + +typedef uint8_t __wasi_filetype_t; +#define __WASI_FILETYPE_UNKNOWN (0) +#define __WASI_FILETYPE_BLOCK_DEVICE (1) +#define __WASI_FILETYPE_CHARACTER_DEVICE (2) +#define __WASI_FILETYPE_DIRECTORY (3) +#define __WASI_FILETYPE_REGULAR_FILE (4) +#define __WASI_FILETYPE_SOCKET_DGRAM (5) +#define __WASI_FILETYPE_SOCKET_STREAM (6) +#define __WASI_FILETYPE_SYMBOLIC_LINK (7) + +typedef uint16_t __wasi_fstflags_t; +#define __WASI_FILESTAT_SET_ATIM (0x0001) +#define __WASI_FILESTAT_SET_ATIM_NOW (0x0002) +#define __WASI_FILESTAT_SET_MTIM (0x0004) +#define __WASI_FILESTAT_SET_MTIM_NOW (0x0008) + +typedef uint64_t __wasi_inode_t; + +typedef uint32_t __wasi_linkcount_t; + +typedef uint32_t __wasi_lookupflags_t; +#define __WASI_LOOKUP_SYMLINK_FOLLOW (0x00000001) + +typedef uint16_t __wasi_oflags_t; +#define __WASI_O_CREAT (0x0001) +#define __WASI_O_DIRECTORY (0x0002) +#define __WASI_O_EXCL (0x0004) +#define __WASI_O_TRUNC (0x0008) + +typedef uint16_t __wasi_riflags_t; +#define __WASI_SOCK_RECV_PEEK (0x0001) +#define __WASI_SOCK_RECV_WAITALL (0x0002) + +typedef uint64_t __wasi_rights_t; +#define __WASI_RIGHT_FD_DATASYNC (0x0000000000000001) +#define __WASI_RIGHT_FD_READ (0x0000000000000002) +#define __WASI_RIGHT_FD_SEEK (0x0000000000000004) +#define __WASI_RIGHT_FD_FDSTAT_SET_FLAGS (0x0000000000000008) +#define __WASI_RIGHT_FD_SYNC (0x0000000000000010) +#define __WASI_RIGHT_FD_TELL (0x0000000000000020) +#define __WASI_RIGHT_FD_WRITE (0x0000000000000040) +#define __WASI_RIGHT_FD_ADVISE (0x0000000000000080) +#define __WASI_RIGHT_FD_ALLOCATE (0x0000000000000100) +#define __WASI_RIGHT_PATH_CREATE_DIRECTORY (0x0000000000000200) +#define __WASI_RIGHT_PATH_CREATE_FILE (0x0000000000000400) +#define __WASI_RIGHT_PATH_LINK_SOURCE (0x0000000000000800) +#define __WASI_RIGHT_PATH_LINK_TARGET (0x0000000000001000) +#define __WASI_RIGHT_PATH_OPEN (0x0000000000002000) +#define __WASI_RIGHT_FD_READDIR (0x0000000000004000) +#define __WASI_RIGHT_PATH_READLINK (0x0000000000008000) +#define __WASI_RIGHT_PATH_RENAME_SOURCE (0x0000000000010000) +#define __WASI_RIGHT_PATH_RENAME_TARGET (0x0000000000020000) +#define __WASI_RIGHT_PATH_FILESTAT_GET (0x0000000000040000) +#define __WASI_RIGHT_PATH_FILESTAT_SET_SIZE (0x0000000000080000) +#define __WASI_RIGHT_PATH_FILESTAT_SET_TIMES (0x0000000000100000) +#define __WASI_RIGHT_FD_FILESTAT_GET (0x0000000000200000) +#define __WASI_RIGHT_FD_FILESTAT_SET_SIZE (0x0000000000400000) +#define __WASI_RIGHT_FD_FILESTAT_SET_TIMES (0x0000000000800000) +#define __WASI_RIGHT_PATH_SYMLINK (0x0000000001000000) +#define __WASI_RIGHT_PATH_REMOVE_DIRECTORY (0x0000000002000000) +#define __WASI_RIGHT_PATH_UNLINK_FILE (0x0000000004000000) +#define __WASI_RIGHT_POLL_FD_READWRITE (0x0000000008000000) +#define __WASI_RIGHT_SOCK_SHUTDOWN (0x0000000010000000) + +typedef uint16_t __wasi_roflags_t; +#define __WASI_SOCK_RECV_DATA_TRUNCATED (0x0001) + +typedef uint8_t __wasi_sdflags_t; +#define __WASI_SHUT_RD (0x01) +#define __WASI_SHUT_WR (0x02) + +typedef uint16_t __wasi_siflags_t; + +typedef uint8_t __wasi_signal_t; +// 0 is reserved; POSIX has special semantics for kill(pid, 0). +#define __WASI_SIGHUP (1) +#define __WASI_SIGINT (2) +#define __WASI_SIGQUIT (3) +#define __WASI_SIGILL (4) +#define __WASI_SIGTRAP (5) +#define __WASI_SIGABRT (6) +#define __WASI_SIGBUS (7) +#define __WASI_SIGFPE (8) +#define __WASI_SIGKILL (9) +#define __WASI_SIGUSR1 (10) +#define __WASI_SIGSEGV (11) +#define __WASI_SIGUSR2 (12) +#define __WASI_SIGPIPE (13) +#define __WASI_SIGALRM (14) +#define __WASI_SIGTERM (15) +#define __WASI_SIGCHLD (16) +#define __WASI_SIGCONT (17) +#define __WASI_SIGSTOP (18) +#define __WASI_SIGTSTP (19) +#define __WASI_SIGTTIN (20) +#define __WASI_SIGTTOU (21) +#define __WASI_SIGURG (22) +#define __WASI_SIGXCPU (23) +#define __WASI_SIGXFSZ (24) +#define __WASI_SIGVTALRM (25) +#define __WASI_SIGPROF (26) +#define __WASI_SIGWINCH (27) +#define __WASI_SIGPOLL (28) +#define __WASI_SIGPWR (29) +#define __WASI_SIGSYS (30) + +typedef uint16_t __wasi_subclockflags_t; +#define __WASI_SUBSCRIPTION_CLOCK_ABSTIME (0x0001) + +typedef uint64_t __wasi_timestamp_t; + +typedef uint64_t __wasi_userdata_t; + +typedef uint8_t __wasi_whence_t; +#define __WASI_WHENCE_CUR (0) +#define __WASI_WHENCE_END (1) +#define __WASI_WHENCE_SET (2) + +typedef uint8_t __wasi_preopentype_t; +#define __WASI_PREOPENTYPE_DIR (0) + +struct fd_table; +struct fd_prestats; +struct argv_environ_values; + +typedef struct __wasi_dirent_t { + __wasi_dircookie_t d_next; + __wasi_inode_t d_ino; + uint32_t d_namlen; + __wasi_filetype_t d_type; +} __wasi_dirent_t; +_Static_assert(offsetof(__wasi_dirent_t, d_next) == 0, "non-wasi data layout"); +_Static_assert(offsetof(__wasi_dirent_t, d_ino) == 8, "non-wasi data layout"); +_Static_assert(offsetof(__wasi_dirent_t, d_namlen) == 16, "non-wasi data layout"); +_Static_assert(offsetof(__wasi_dirent_t, d_type) == 20, "non-wasi data layout"); +_Static_assert(sizeof(__wasi_dirent_t) == 24, "non-wasi data layout"); +_Static_assert(_Alignof(__wasi_dirent_t) == 8, "non-wasi data layout"); + +typedef struct __wasi_event_t { + __wasi_userdata_t userdata; + __wasi_errno_t error; + __wasi_eventtype_t type; + union __wasi_event_u { + struct __wasi_event_u_fd_readwrite_t { + __wasi_filesize_t nbytes; + __wasi_eventrwflags_t flags; + } fd_readwrite; + } u; +} __wasi_event_t; +_Static_assert(offsetof(__wasi_event_t, userdata) == 0, "non-wasi data layout"); +_Static_assert(offsetof(__wasi_event_t, error) == 8, "non-wasi data layout"); +_Static_assert(offsetof(__wasi_event_t, type) == 10, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_event_t, u.fd_readwrite.nbytes) == 16, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_event_t, u.fd_readwrite.flags) == 24, "non-wasi data layout"); +_Static_assert(sizeof(__wasi_event_t) == 32, "non-wasi data layout"); +_Static_assert(_Alignof(__wasi_event_t) == 8, "non-wasi data layout"); + +typedef struct __wasi_prestat_t { + __wasi_preopentype_t pr_type; + union __wasi_prestat_u { + struct __wasi_prestat_u_dir_t { + size_t pr_name_len; + } dir; + } u; +} __wasi_prestat_t; +_Static_assert(offsetof(__wasi_prestat_t, pr_type) == 0, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 4 || + offsetof(__wasi_prestat_t, u.dir.pr_name_len) == 4, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 8 || + offsetof(__wasi_prestat_t, u.dir.pr_name_len) == 8, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 4 || + sizeof(__wasi_prestat_t) == 8, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 8 || + sizeof(__wasi_prestat_t) == 16, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 4 || + _Alignof(__wasi_prestat_t) == 4, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 8 || + _Alignof(__wasi_prestat_t) == 8, "non-wasi data layout"); + +typedef struct __wasi_fdstat_t { + __wasi_filetype_t fs_filetype; + __wasi_fdflags_t fs_flags; + __wasi_rights_t fs_rights_base; + __wasi_rights_t fs_rights_inheriting; +} __wasi_fdstat_t; +_Static_assert( + offsetof(__wasi_fdstat_t, fs_filetype) == 0, "non-wasi data layout"); +_Static_assert(offsetof(__wasi_fdstat_t, fs_flags) == 2, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_fdstat_t, fs_rights_base) == 8, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_fdstat_t, fs_rights_inheriting) == 16, + "non-wasi data layout"); +_Static_assert(sizeof(__wasi_fdstat_t) == 24, "non-wasi data layout"); +_Static_assert(_Alignof(__wasi_fdstat_t) == 8, "non-wasi data layout"); + +typedef struct __wasi_filestat_t { + __wasi_device_t st_dev; + __wasi_inode_t st_ino; + __wasi_filetype_t st_filetype; + __wasi_linkcount_t st_nlink; + __wasi_filesize_t st_size; + __wasi_timestamp_t st_atim; + __wasi_timestamp_t st_mtim; + __wasi_timestamp_t st_ctim; +} __wasi_filestat_t; +_Static_assert(offsetof(__wasi_filestat_t, st_dev) == 0, "non-wasi data layout"); +_Static_assert(offsetof(__wasi_filestat_t, st_ino) == 8, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_filestat_t, st_filetype) == 16, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_filestat_t, st_nlink) == 20, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_filestat_t, st_size) == 24, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_filestat_t, st_atim) == 32, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_filestat_t, st_mtim) == 40, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_filestat_t, st_ctim) == 48, "non-wasi data layout"); +_Static_assert(sizeof(__wasi_filestat_t) == 56, "non-wasi data layout"); +_Static_assert(_Alignof(__wasi_filestat_t) == 8, "non-wasi data layout"); + +typedef struct __wasi_ciovec_t { + const void *buf; + size_t buf_len; +} __wasi_ciovec_t; +_Static_assert(offsetof(__wasi_ciovec_t, buf) == 0, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 4 || + offsetof(__wasi_ciovec_t, buf_len) == 4, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 8 || + offsetof(__wasi_ciovec_t, buf_len) == 8, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 4 || + sizeof(__wasi_ciovec_t) == 8, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 8 || + sizeof(__wasi_ciovec_t) == 16, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 4 || + _Alignof(__wasi_ciovec_t) == 4, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 8 || + _Alignof(__wasi_ciovec_t) == 8, "non-wasi data layout"); + +typedef struct __wasi_iovec_t { + void *buf; + size_t buf_len; +} __wasi_iovec_t; +_Static_assert(offsetof(__wasi_iovec_t, buf) == 0, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 4 || + offsetof(__wasi_iovec_t, buf_len) == 4, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 8 || + offsetof(__wasi_iovec_t, buf_len) == 8, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 4 || + sizeof(__wasi_iovec_t) == 8, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 8 || + sizeof(__wasi_iovec_t) == 16, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 4 || + _Alignof(__wasi_iovec_t) == 4, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 8 || + _Alignof(__wasi_iovec_t) == 8, "non-wasi data layout"); + +typedef struct __wasi_subscription_t { + __wasi_userdata_t userdata; + __wasi_eventtype_t type; + union __wasi_subscription_u { + struct __wasi_subscription_u_clock_t { + __wasi_userdata_t identifier; + __wasi_clockid_t clock_id; + __wasi_timestamp_t timeout; + __wasi_timestamp_t precision; + __wasi_subclockflags_t flags; + } clock; + struct __wasi_subscription_u_fd_readwrite_t { + __wasi_fd_t fd; + } fd_readwrite; + } u; +} __wasi_subscription_t; +_Static_assert( + offsetof(__wasi_subscription_t, userdata) == 0, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_subscription_t, type) == 8, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_subscription_t, u.clock.identifier) == 16, + "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_subscription_t, u.clock.clock_id) == 24, + "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_subscription_t, u.clock.timeout) == 32, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_subscription_t, u.clock.precision) == 40, + "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_subscription_t, u.clock.flags) == 48, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_subscription_t, u.fd_readwrite.fd) == 16, + "non-wasi data layout"); +_Static_assert(sizeof(__wasi_subscription_t) == 56, "non-wasi data layout"); +_Static_assert(_Alignof(__wasi_subscription_t) == 8, "non-wasi data layout"); + +#if defined(WASMTIME_SSP_WASI_API) +#define WASMTIME_SSP_SYSCALL_NAME(name) \ + asm("__wasi_" #name) +#else +#define WASMTIME_SSP_SYSCALL_NAME(name) +#endif + +__wasi_errno_t wasmtime_ssp_args_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct argv_environ_values *arg_environ, +#endif + char **argv, + char *argv_buf +) WASMTIME_SSP_SYSCALL_NAME(args_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_args_sizes_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct argv_environ_values *arg_environ, +#endif + size_t *argc, + size_t *argv_buf_size +) WASMTIME_SSP_SYSCALL_NAME(args_sizes_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_clock_res_get( + __wasi_clockid_t clock_id, + __wasi_timestamp_t *resolution +) WASMTIME_SSP_SYSCALL_NAME(clock_res_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_clock_time_get( + __wasi_clockid_t clock_id, + __wasi_timestamp_t precision, + __wasi_timestamp_t *time +) WASMTIME_SSP_SYSCALL_NAME(clock_time_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_environ_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct argv_environ_values *arg_environ, +#endif + char **environ, + char *environ_buf +) WASMTIME_SSP_SYSCALL_NAME(environ_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_environ_sizes_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct argv_environ_values *arg_environ, +#endif + size_t *environ_count, + size_t *environ_buf_size +) WASMTIME_SSP_SYSCALL_NAME(environ_sizes_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_prestat_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_prestats *prestats, +#endif + __wasi_fd_t fd, + __wasi_prestat_t *buf +) WASMTIME_SSP_SYSCALL_NAME(fd_prestat_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_prestat_dir_name( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_prestats *prestats, +#endif + __wasi_fd_t fd, + char *path, + size_t path_len +) WASMTIME_SSP_SYSCALL_NAME(fd_prestat_dir_name) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_close( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, + struct fd_prestats *prestats, +#endif + __wasi_fd_t fd +) WASMTIME_SSP_SYSCALL_NAME(fd_close) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_datasync( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd +) WASMTIME_SSP_SYSCALL_NAME(fd_datasync) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_pread( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const __wasi_iovec_t *iovs, + size_t iovs_len, + __wasi_filesize_t offset, + size_t *nread +) WASMTIME_SSP_SYSCALL_NAME(fd_pread) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_pwrite( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const __wasi_ciovec_t *iovs, + size_t iovs_len, + __wasi_filesize_t offset, + size_t *nwritten +) WASMTIME_SSP_SYSCALL_NAME(fd_pwrite) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_read( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const __wasi_iovec_t *iovs, + size_t iovs_len, + size_t *nread +) WASMTIME_SSP_SYSCALL_NAME(fd_read) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_renumber( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t from, + __wasi_fd_t to +) WASMTIME_SSP_SYSCALL_NAME(fd_renumber) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_seek( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filedelta_t offset, + __wasi_whence_t whence, + __wasi_filesize_t *newoffset +) WASMTIME_SSP_SYSCALL_NAME(fd_seek) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_tell( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filesize_t *newoffset +) WASMTIME_SSP_SYSCALL_NAME(fd_tell) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_fdstat_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_fdstat_t *buf +) WASMTIME_SSP_SYSCALL_NAME(fd_fdstat_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_fdstat_set_flags( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_fdflags_t flags +) WASMTIME_SSP_SYSCALL_NAME(fd_fdstat_set_flags) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_fdstat_set_rights( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_rights_t fs_rights_base, + __wasi_rights_t fs_rights_inheriting +) WASMTIME_SSP_SYSCALL_NAME(fd_fdstat_set_rights) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_sync( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd +) WASMTIME_SSP_SYSCALL_NAME(fd_sync) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_write( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const __wasi_ciovec_t *iovs, + size_t iovs_len, + size_t *nwritten +) WASMTIME_SSP_SYSCALL_NAME(fd_write) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_advise( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filesize_t offset, + __wasi_filesize_t len, + __wasi_advice_t advice +) WASMTIME_SSP_SYSCALL_NAME(fd_advise) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_allocate( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filesize_t offset, + __wasi_filesize_t len +) WASMTIME_SSP_SYSCALL_NAME(fd_allocate) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_path_create_directory( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const char *path, + size_t path_len +) WASMTIME_SSP_SYSCALL_NAME(path_create_directory) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_path_link( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t old_fd, + __wasi_lookupflags_t old_flags, + const char *old_path, + size_t old_path_len, + __wasi_fd_t new_fd, + const char *new_path, + size_t new_path_len +) WASMTIME_SSP_SYSCALL_NAME(path_link) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_path_open( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t dirfd, + __wasi_lookupflags_t dirflags, + const char *path, + size_t path_len, + __wasi_oflags_t oflags, + __wasi_rights_t fs_rights_base, + __wasi_rights_t fs_rights_inheriting, + __wasi_fdflags_t fs_flags, + __wasi_fd_t *fd +) WASMTIME_SSP_SYSCALL_NAME(path_open) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_readdir( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + void *buf, + size_t buf_len, + __wasi_dircookie_t cookie, + size_t *bufused +) WASMTIME_SSP_SYSCALL_NAME(fd_readdir) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_path_readlink( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const char *path, + size_t path_len, + char *buf, + size_t buf_len, + size_t *bufused +) WASMTIME_SSP_SYSCALL_NAME(path_readlink) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_path_rename( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t old_fd, + const char *old_path, + size_t old_path_len, + __wasi_fd_t new_fd, + const char *new_path, + size_t new_path_len +) WASMTIME_SSP_SYSCALL_NAME(path_rename) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_filestat_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filestat_t *buf +) WASMTIME_SSP_SYSCALL_NAME(fd_filestat_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_filestat_set_times( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_timestamp_t st_atim, + __wasi_timestamp_t st_mtim, + __wasi_fstflags_t fstflags +) WASMTIME_SSP_SYSCALL_NAME(fd_filestat_set_times) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_filestat_set_size( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filesize_t st_size +) WASMTIME_SSP_SYSCALL_NAME(fd_filestat_set_size) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_path_filestat_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_lookupflags_t flags, + const char *path, + size_t path_len, + __wasi_filestat_t *buf +) WASMTIME_SSP_SYSCALL_NAME(path_filestat_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_path_filestat_set_times( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_lookupflags_t flags, + const char *path, + size_t path_len, + __wasi_timestamp_t st_atim, + __wasi_timestamp_t st_mtim, + __wasi_fstflags_t fstflags +) WASMTIME_SSP_SYSCALL_NAME(path_filestat_set_times) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_path_symlink( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + const char *old_path, + size_t old_path_len, + __wasi_fd_t fd, + const char *new_path, + size_t new_path_len +) WASMTIME_SSP_SYSCALL_NAME(path_symlink) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_path_unlink_file( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const char *path, + size_t path_len +) WASMTIME_SSP_SYSCALL_NAME(path_unlink_file) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_path_remove_directory( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const char *path, + size_t path_len +) WASMTIME_SSP_SYSCALL_NAME(path_remove_directory) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_poll_oneoff( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + const __wasi_subscription_t *in, + __wasi_event_t *out, + size_t nsubscriptions, + size_t *nevents +) WASMTIME_SSP_SYSCALL_NAME(poll_oneoff) __attribute__((__warn_unused_result__)); + +_Noreturn void wasmtime_ssp_proc_exit( + __wasi_exitcode_t rval +) WASMTIME_SSP_SYSCALL_NAME(proc_exit); + +__wasi_errno_t wasmtime_ssp_proc_raise( + __wasi_signal_t sig +) WASMTIME_SSP_SYSCALL_NAME(proc_raise) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_random_get( + void *buf, + size_t buf_len +) WASMTIME_SSP_SYSCALL_NAME(random_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_sock_recv( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t sock, + const __wasi_iovec_t *ri_data, + size_t ri_data_len, + __wasi_riflags_t ri_flags, + size_t *ro_datalen, + __wasi_roflags_t *ro_flags +) WASMTIME_SSP_SYSCALL_NAME(sock_recv) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_sock_send( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t sock, + const __wasi_ciovec_t *si_data, + size_t si_data_len, + __wasi_siflags_t si_flags, + size_t *so_datalen +) WASMTIME_SSP_SYSCALL_NAME(sock_send) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_sock_shutdown( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t sock, + __wasi_sdflags_t how +) WASMTIME_SSP_SYSCALL_NAME(sock_shutdown) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_sched_yield(void) + WASMTIME_SSP_SYSCALL_NAME(sched_yield) __attribute__((__warn_unused_result__)); + +#ifdef __cplusplus +} +#endif + +#undef WASMTIME_SSP_SYSCALL_NAME + +#endif diff --git a/wasmtime-wasi/sandboxed-system-primitives/src/LICENSE b/wasmtime-wasi/sandboxed-system-primitives/src/LICENSE new file mode 100644 index 0000000000..04c6f48a27 --- /dev/null +++ b/wasmtime-wasi/sandboxed-system-primitives/src/LICENSE @@ -0,0 +1,24 @@ +All code is distributed under the following license: + + Copyright (c) 2015 Nuxi, https://nuxi.nl/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. diff --git a/wasmtime-wasi/sandboxed-system-primitives/src/README.md b/wasmtime-wasi/sandboxed-system-primitives/src/README.md new file mode 100644 index 0000000000..77a34bb5c3 --- /dev/null +++ b/wasmtime-wasi/sandboxed-system-primitives/src/README.md @@ -0,0 +1,14 @@ +This directory consists of selected files copied from the [libemulator] +directory in the [cloudabi-utils] repository, with minor modifications, +along with the accompanying LICENSE file from that repository. + +The modifications are marked with `WASMTIME_*` preprocessor macros. + +The files were copied at git revision +be1ce21e1dded9c0c0a6ebe144cbea01cf44a874 +which is dated +Sun Jan 13 23:26:03 2019 +0100 +. + +[libemulator]: https://github.com/NuxiNL/cloudabi-utils/tree/master/src/libemulator +[cloudabi-utils]: https://github.com/NuxiNL/cloudabi-utils diff --git a/wasmtime-wasi/sandboxed-system-primitives/src/config.h b/wasmtime-wasi/sandboxed-system-primitives/src/config.h new file mode 100644 index 0000000000..5fa4ce1dda --- /dev/null +++ b/wasmtime-wasi/sandboxed-system-primitives/src/config.h @@ -0,0 +1,93 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016 Nuxi, https://nuxi.nl/ + +#ifndef CONFIG_H +#define CONFIG_H + +#if defined(__FreeBSD__) || defined(__APPLE__) +#define CONFIG_HAS_ARC4RANDOM_BUF 1 +#else +#define CONFIG_HAS_ARC4RANDOM_BUF 0 +#endif + +#ifdef __linux__ +#define CONFIG_HAS_GETENTROPY 1 +#else +#define CONFIG_HAS_GETENTROPY 0 +#endif + +#if defined(__CloudABI__) +#define CONFIG_HAS_CAP_ENTER 1 +#else +#define CONFIG_HAS_CAP_ENTER 0 +#endif + +#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__EMSCRIPTEN__) +#define CONFIG_HAS_CLOCK_NANOSLEEP 1 +#else +#define CONFIG_HAS_CLOCK_NANOSLEEP 0 +#endif + +#if !defined(__APPLE__) && !defined(__FreeBSD__) +#define CONFIG_HAS_FDATASYNC 1 +#else +#define CONFIG_HAS_FDATASYNC 0 +#endif + +#ifndef __CloudABI__ +#define CONFIG_HAS_ISATTY 1 +#else +#define CONFIG_HAS_ISATTY 0 +#endif + +#ifndef __APPLE__ +#define CONFIG_HAS_POSIX_FALLOCATE 1 +#else +#define CONFIG_HAS_POSIX_FALLOCATE 0 +#endif + +#ifndef __APPLE__ +#define CONFIG_HAS_PREADV 1 +#else +#define CONFIG_HAS_PREADV 0 +#endif + +#if defined(__APPLE__) || defined(__CloudABI__) +#define CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP 1 +#else +#define CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP 0 +#endif + +#ifndef __APPLE__ +#define CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK 1 +#else +#define CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK 0 +#endif + +#ifndef __APPLE__ +#define CONFIG_HAS_PWRITEV 1 +#else +#define CONFIG_HAS_PWRITEV 0 +#endif + +#ifdef __APPLE__ +#define st_atimespec st_atim +#define st_mtimespec st_mtim +#define st_ctimespec st_ctim +#endif + +#ifdef __APPLE__ +#define CONFIG_TLS_USE_GSBASE 1 +#else +#define CONFIG_TLS_USE_GSBASE 0 +#endif + +#endif diff --git a/wasmtime-wasi/sandboxed-system-primitives/src/locking.h b/wasmtime-wasi/sandboxed-system-primitives/src/locking.h new file mode 100644 index 0000000000..0efb788ed5 --- /dev/null +++ b/wasmtime-wasi/sandboxed-system-primitives/src/locking.h @@ -0,0 +1,215 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016 Nuxi, https://nuxi.nl/ + +#ifndef LOCKING_H +#define LOCKING_H + +#include "config.h" + +#include +#include +#include +#include +#include + +#ifndef __has_extension +#define __has_extension(x) 0 +#endif + +#if __has_extension(c_thread_safety_attributes) +#define LOCK_ANNOTATE(x) __attribute__((x)) +#else +#define LOCK_ANNOTATE(x) +#endif + +// Lock annotation macros. + +#define LOCKABLE LOCK_ANNOTATE(lockable) + +#define LOCKS_EXCLUSIVE(...) LOCK_ANNOTATE(exclusive_lock_function(__VA_ARGS__)) +#define LOCKS_SHARED(...) LOCK_ANNOTATE(shared_lock_function(__VA_ARGS__)) + +#define TRYLOCKS_EXCLUSIVE(...) \ + LOCK_ANNOTATE(exclusive_trylock_function(__VA_ARGS__)) +#define TRYLOCKS_SHARED(...) LOCK_ANNOTATE(shared_trylock_function(__VA_ARGS__)) + +#define UNLOCKS(...) LOCK_ANNOTATE(unlock_function(__VA_ARGS__)) + +#define REQUIRES_EXCLUSIVE(...) \ + LOCK_ANNOTATE(exclusive_locks_required(__VA_ARGS__)) +#define REQUIRES_SHARED(...) LOCK_ANNOTATE(shared_locks_required(__VA_ARGS__)) +#define REQUIRES_UNLOCKED(...) LOCK_ANNOTATE(locks_excluded(__VA_ARGS__)) + +#define NO_LOCK_ANALYSIS LOCK_ANNOTATE(no_thread_safety_analysis) + +// Mutex that uses the lock annotations. + +struct LOCKABLE mutex { + pthread_mutex_t object; +}; + +#define MUTEX_INITIALIZER \ + { PTHREAD_MUTEX_INITIALIZER } + +static inline void mutex_init(struct mutex *lock) REQUIRES_UNLOCKED(*lock) { + pthread_mutex_init(&lock->object, NULL); +} + +static inline void mutex_destroy(struct mutex *lock) REQUIRES_UNLOCKED(*lock) { + pthread_mutex_destroy(&lock->object); +} + +static inline void mutex_lock(struct mutex *lock) + LOCKS_EXCLUSIVE(*lock) NO_LOCK_ANALYSIS { + pthread_mutex_lock(&lock->object); +} + +static inline void mutex_unlock(struct mutex *lock) + UNLOCKS(*lock) NO_LOCK_ANALYSIS { + pthread_mutex_unlock(&lock->object); +} + +// Read-write lock that uses the lock annotations. + +struct LOCKABLE rwlock { + pthread_rwlock_t object; +}; + +static inline void rwlock_init(struct rwlock *lock) REQUIRES_UNLOCKED(*lock) { + pthread_rwlock_init(&lock->object, NULL); +} + +static inline void rwlock_rdlock(struct rwlock *lock) + LOCKS_SHARED(*lock) NO_LOCK_ANALYSIS { + pthread_rwlock_rdlock(&lock->object); +} + +static inline void rwlock_wrlock(struct rwlock *lock) + LOCKS_EXCLUSIVE(*lock) NO_LOCK_ANALYSIS { + pthread_rwlock_wrlock(&lock->object); +} + +static inline void rwlock_unlock(struct rwlock *lock) + UNLOCKS(*lock) NO_LOCK_ANALYSIS { + pthread_rwlock_unlock(&lock->object); +} + +// Condition variable that uses the lock annotations. + +struct LOCKABLE cond { + pthread_cond_t object; +#if !CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK || \ + !CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP + clockid_t clock; +#endif +}; + +static inline void cond_init_monotonic(struct cond *cond) { +#if CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK + pthread_condattr_t attr; + pthread_condattr_init(&attr); + pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); + pthread_cond_init(&cond->object, &attr); + pthread_condattr_destroy(&attr); +#else + pthread_cond_init(&cond->object, NULL); +#endif +#if !CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK || \ + !CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP + cond->clock = CLOCK_MONOTONIC; +#endif +} + +static inline void cond_init_realtime(struct cond *cond) { + pthread_cond_init(&cond->object, NULL); +#if !CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK || \ + !CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP + cond->clock = CLOCK_REALTIME; +#endif +} + +static inline void cond_destroy(struct cond *cond) { + pthread_cond_destroy(&cond->object); +} + +static inline void cond_signal(struct cond *cond) { + pthread_cond_signal(&cond->object); +} + +static inline bool cond_timedwait(struct cond *cond, struct mutex *lock, + uint64_t timeout, bool abstime) + REQUIRES_EXCLUSIVE(*lock) NO_LOCK_ANALYSIS { + struct timespec ts = { + .tv_sec = (time_t)(timeout / 1000000000), + .tv_nsec = (long)(timeout % 1000000000), + }; + + if (abstime) { +#if !CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK + // No native support for sleeping on monotonic clocks. Convert the + // timeout to a relative value and then to an absolute value for the + // realtime clock. + if (cond->clock != CLOCK_REALTIME) { + struct timespec ts_monotonic; + clock_gettime(cond->clock, &ts_monotonic); + ts.tv_sec -= ts_monotonic.tv_sec; + ts.tv_nsec -= ts_monotonic.tv_nsec; + if (ts.tv_nsec < 0) { + ts.tv_nsec += 1000000000; + --ts.tv_sec; + } + + struct timespec ts_realtime; + clock_gettime(CLOCK_REALTIME, &ts_realtime); + ts.tv_sec += ts_realtime.tv_sec; + ts.tv_nsec += ts_realtime.tv_nsec; + if (ts.tv_nsec >= 1000000000) { + ts.tv_nsec -= 1000000000; + ++ts.tv_sec; + } + } +#endif + } else { +#if CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP + // Implementation supports relative timeouts. + int ret = + pthread_cond_timedwait_relative_np(&cond->object, &lock->object, &ts); + assert((ret == 0 || ret == ETIMEDOUT) && + "pthread_cond_timedwait_relative_np() failed"); + return ret == ETIMEDOUT; +#else + // Convert to absolute timeout. + struct timespec ts_now; +#if CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK + clock_gettime(cond->clock, &ts_now); +#else + clock_gettime(CLOCK_REALTIME, &ts_now); +#endif + ts.tv_sec += ts_now.tv_sec; + ts.tv_nsec += ts_now.tv_nsec; + if (ts.tv_nsec >= 1000000000) { + ts.tv_nsec -= 1000000000; + ++ts.tv_sec; + } +#endif + } + + int ret = pthread_cond_timedwait(&cond->object, &lock->object, &ts); + assert((ret == 0 || ret == ETIMEDOUT) && "pthread_cond_timedwait() failed"); + return ret == ETIMEDOUT; +} + +static inline void cond_wait(struct cond *cond, struct mutex *lock) + REQUIRES_EXCLUSIVE(*lock) NO_LOCK_ANALYSIS { + pthread_cond_wait(&cond->object, &lock->object); +} + +#endif diff --git a/wasmtime-wasi/sandboxed-system-primitives/src/numeric_limits.h b/wasmtime-wasi/sandboxed-system-primitives/src/numeric_limits.h new file mode 100644 index 0000000000..30dddd19aa --- /dev/null +++ b/wasmtime-wasi/sandboxed-system-primitives/src/numeric_limits.h @@ -0,0 +1,42 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2015 Nuxi, https://nuxi.nl/ + +#ifndef COMMON_LIMITS_H +#define COMMON_LIMITS_H + +#include + +#define NUMERIC_MIN(t) \ + _Generic((t)0, char \ + : CHAR_MIN, signed char \ + : SCHAR_MIN, unsigned char : 0, short \ + : SHRT_MIN, unsigned short : 0, int \ + : INT_MIN, unsigned int : 0, long \ + : LONG_MIN, unsigned long : 0, long long \ + : LLONG_MIN, unsigned long long : 0, default \ + : (void)0) + +#define NUMERIC_MAX(t) \ + _Generic((t)0, char \ + : CHAR_MAX, signed char \ + : SCHAR_MAX, unsigned char \ + : UCHAR_MAX, short \ + : SHRT_MAX, unsigned short \ + : USHRT_MAX, int \ + : INT_MAX, unsigned int \ + : UINT_MAX, long \ + : LONG_MAX, unsigned long \ + : ULONG_MAX, long long \ + : LLONG_MAX, unsigned long long \ + : ULLONG_MAX, default \ + : (void)0) + +#endif diff --git a/wasmtime-wasi/sandboxed-system-primitives/src/posix.c b/wasmtime-wasi/sandboxed-system-primitives/src/posix.c new file mode 100644 index 0000000000..2c211542fd --- /dev/null +++ b/wasmtime-wasi/sandboxed-system-primitives/src/posix.c @@ -0,0 +1,2817 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016-2018 Nuxi, https://nuxi.nl/ + +#include "config.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "locking.h" +#include "numeric_limits.h" +#include "posix.h" +#include "random.h" +#include "refcount.h" +#include "rights.h" +#include "str.h" + +// struct iovec must have the same layout as __wasi_iovec_t. +static_assert(offsetof(struct iovec, iov_base) == + offsetof(__wasi_iovec_t, buf), + "Offset mismatch"); +static_assert(sizeof(((struct iovec *)0)->iov_base) == + sizeof(((__wasi_iovec_t *)0)->buf), + "Size mismatch"); +static_assert(offsetof(struct iovec, iov_len) == + offsetof(__wasi_iovec_t, buf_len), + "Offset mismatch"); +static_assert(sizeof(((struct iovec *)0)->iov_len) == + sizeof(((__wasi_iovec_t *)0)->buf_len), + "Size mismatch"); +static_assert(sizeof(struct iovec) == sizeof(__wasi_iovec_t), + "Size mismatch"); + +// struct iovec must have the same layout as __wasi_ciovec_t. +static_assert(offsetof(struct iovec, iov_base) == + offsetof(__wasi_ciovec_t, buf), + "Offset mismatch"); +static_assert(sizeof(((struct iovec *)0)->iov_base) == + sizeof(((__wasi_ciovec_t *)0)->buf), + "Size mismatch"); +static_assert(offsetof(struct iovec, iov_len) == + offsetof(__wasi_ciovec_t, buf_len), + "Offset mismatch"); +static_assert(sizeof(((struct iovec *)0)->iov_len) == + sizeof(((__wasi_ciovec_t *)0)->buf_len), + "Size mismatch"); +static_assert(sizeof(struct iovec) == sizeof(__wasi_ciovec_t), + "Size mismatch"); + +#if defined(WASMTIME_SSP_STATIC_CURFDS) +static __thread struct fd_table *curfds; +static __thread struct fd_prestats *prestats; +static __thread struct argv_environ_values *argv_environ; +#endif + +// Converts a POSIX error code to a CloudABI error code. +static __wasi_errno_t convert_errno(int error) { + static const __wasi_errno_t errors[] = { +#define X(v) [v] = __WASI_##v + X(E2BIG), + X(EACCES), + X(EADDRINUSE), + X(EADDRNOTAVAIL), + X(EAFNOSUPPORT), + X(EAGAIN), + X(EALREADY), + X(EBADF), + X(EBADMSG), + X(EBUSY), + X(ECANCELED), + X(ECHILD), + X(ECONNABORTED), + X(ECONNREFUSED), + X(ECONNRESET), + X(EDEADLK), + X(EDESTADDRREQ), + X(EDOM), + X(EDQUOT), + X(EEXIST), + X(EFAULT), + X(EFBIG), + X(EHOSTUNREACH), + X(EIDRM), + X(EILSEQ), + X(EINPROGRESS), + X(EINTR), + X(EINVAL), + X(EIO), + X(EISCONN), + X(EISDIR), + X(ELOOP), + X(EMFILE), + X(EMLINK), + X(EMSGSIZE), + X(EMULTIHOP), + X(ENAMETOOLONG), + X(ENETDOWN), + X(ENETRESET), + X(ENETUNREACH), + X(ENFILE), + X(ENOBUFS), + X(ENODEV), + X(ENOENT), + X(ENOEXEC), + X(ENOLCK), + X(ENOLINK), + X(ENOMEM), + X(ENOMSG), + X(ENOPROTOOPT), + X(ENOSPC), + X(ENOSYS), +#ifdef ENOTCAPABLE + X(ENOTCAPABLE), +#endif + X(ENOTCONN), + X(ENOTDIR), + X(ENOTEMPTY), + X(ENOTRECOVERABLE), + X(ENOTSOCK), + X(ENOTSUP), + X(ENOTTY), + X(ENXIO), + X(EOVERFLOW), + X(EOWNERDEAD), + X(EPERM), + X(EPIPE), + X(EPROTO), + X(EPROTONOSUPPORT), + X(EPROTOTYPE), + X(ERANGE), + X(EROFS), + X(ESPIPE), + X(ESRCH), + X(ESTALE), + X(ETIMEDOUT), + X(ETXTBSY), + X(EXDEV), +#undef X +#if EOPNOTSUPP != ENOTSUP + [EOPNOTSUPP] = __WASI_ENOTSUP, +#endif +#if EWOULDBLOCK != EAGAIN + [EWOULDBLOCK] = __WASI_EAGAIN, +#endif + }; + if (error < 0 || (size_t)error >= sizeof(errors) / sizeof(errors[0]) || + errors[error] == 0) + return __WASI_ENOSYS; + return errors[error]; +} + +// Converts a POSIX timespec to a CloudABI timestamp. +static __wasi_timestamp_t convert_timespec( + const struct timespec *ts +) { + if (ts->tv_sec < 0) + return 0; + if ((__wasi_timestamp_t)ts->tv_sec >= UINT64_MAX / 1000000000) + return UINT64_MAX; + return (__wasi_timestamp_t)ts->tv_sec * 1000000000 + ts->tv_nsec; +} + +// Converts a CloudABI clock identifier to a POSIX clock identifier. +static bool convert_clockid( + __wasi_clockid_t in, + clockid_t *out +) { + switch (in) { + case __WASI_CLOCK_MONOTONIC: + *out = CLOCK_MONOTONIC; + return true; + case __WASI_CLOCK_PROCESS_CPUTIME_ID: + *out = CLOCK_PROCESS_CPUTIME_ID; + return true; + case __WASI_CLOCK_REALTIME: + *out = CLOCK_REALTIME; + return true; + case __WASI_CLOCK_THREAD_CPUTIME_ID: + *out = CLOCK_THREAD_CPUTIME_ID; + return true; + default: + return false; + } +} + +__wasi_errno_t wasmtime_ssp_clock_res_get( + __wasi_clockid_t clock_id, + __wasi_timestamp_t *resolution +) { + clockid_t nclock_id; + if (!convert_clockid(clock_id, &nclock_id)) + return __WASI_EINVAL; + struct timespec ts; + if (clock_getres(nclock_id, &ts) < 0) + return convert_errno(errno); + *resolution = convert_timespec(&ts); + return 0; +} + +__wasi_errno_t wasmtime_ssp_clock_time_get( + __wasi_clockid_t clock_id, + __wasi_timestamp_t precision, + __wasi_timestamp_t *time +) { + clockid_t nclock_id; + if (!convert_clockid(clock_id, &nclock_id)) + return __WASI_EINVAL; + struct timespec ts; + if (clock_gettime(nclock_id, &ts) < 0) + return convert_errno(errno); + *time = convert_timespec(&ts); + return 0; +} + +struct fd_prestat { + const char *dir; +}; + +void fd_prestats_init( + struct fd_prestats *pt +) { + rwlock_init(&pt->lock); + pt->prestats = NULL; + pt->size = 0; + pt->used = 0; +#if defined(WASMTIME_SSP_STATIC_CURFDS) + prestats = pt; +#endif +} + +// Grows the preopened resource table to a required lower bound and a +// minimum number of free preopened resource table entries. +static bool fd_prestats_grow( + struct fd_prestats *pt, + size_t min, + size_t incr +) REQUIRES_EXCLUSIVE(pt->lock) { + if (pt->size <= min || pt->size < (pt->used + incr) * 2) { + // Keep on doubling the table size until we've met our constraints. + size_t size = pt->size == 0 ? 1 : pt->size; + while (size <= min || size < (pt->used + incr) * 2) + size *= 2; + + // Grow the file descriptor table's allocation. + struct fd_prestat *prestats = realloc(pt->prestats, sizeof(*prestats) * size); + if (prestats == NULL) + return false; + + // Mark all new file descriptors as unused. + for (size_t i = pt->size; i < size; ++i) + prestats[i].dir = NULL; + pt->prestats = prestats; + pt->size = size; + } + return true; +} + +// Inserts a preopened resource record into the preopened resource table. +bool fd_prestats_insert( + struct fd_prestats *pt, + const char *dir, + __wasi_fd_t fd +) { + // Grow the preopened resource table if needed. + rwlock_wrlock(&pt->lock); + if (!fd_prestats_grow(pt, fd, 1)) { + rwlock_unlock(&pt->lock); + return false; + } + + pt->prestats[fd].dir = strdup(dir); + rwlock_unlock(&pt->lock); + return true; +} + +// Looks up a preopened resource table entry by number. +static __wasi_errno_t fd_prestats_get_entry( + struct fd_prestats *pt, + __wasi_fd_t fd, + struct fd_prestat **ret +) REQUIRES_SHARED(pt->lock) { + // Test for file descriptor existence. + if (fd >= pt->size) + return __WASI_EBADF; + struct fd_prestat *prestat = &pt->prestats[fd]; + if (prestat->dir == NULL) + return __WASI_EBADF; + + *ret = prestat; + return 0; +} + +struct fd_object { + struct refcount refcount; + __wasi_filetype_t type; + int number; + + union { + // Data associated with directory file descriptors. + struct { + struct mutex lock; // Lock to protect members below. + DIR *handle; // Directory handle. + __wasi_dircookie_t offset; // Offset of the directory. + } directory; + }; +}; + +struct fd_entry { + struct fd_object *object; + __wasi_rights_t rights_base; + __wasi_rights_t rights_inheriting; +}; + +void fd_table_init( + struct fd_table *ft +) { + rwlock_init(&ft->lock); + ft->entries = NULL; + ft->size = 0; + ft->used = 0; +#if defined(WASMTIME_SSP_STATIC_CURFDS) + curfds = ft; +#endif +} + +// Looks up a file descriptor table entry by number and required rights. +static __wasi_errno_t fd_table_get_entry( + struct fd_table *ft, + __wasi_fd_t fd, + __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting, + struct fd_entry **ret +) REQUIRES_SHARED(ft->lock) { + // Test for file descriptor existence. + if (fd >= ft->size) + return __WASI_EBADF; + struct fd_entry *fe = &ft->entries[fd]; + if (fe->object == NULL) + return __WASI_EBADF; + + // Validate rights. + if ((~fe->rights_base & rights_base) != 0 || + (~fe->rights_inheriting & rights_inheriting) != 0) + return __WASI_ENOTCAPABLE; + *ret = fe; + return 0; +} + +// Grows the file descriptor table to a required lower bound and a +// minimum number of free file descriptor table entries. +static bool fd_table_grow( + struct fd_table *ft, + size_t min, + size_t incr +) REQUIRES_EXCLUSIVE(ft->lock) { + if (ft->size <= min || ft->size < (ft->used + incr) * 2) { + // Keep on doubling the table size until we've met our constraints. + size_t size = ft->size == 0 ? 1 : ft->size; + while (size <= min || size < (ft->used + incr) * 2) + size *= 2; + + // Grow the file descriptor table's allocation. + struct fd_entry *entries = realloc(ft->entries, sizeof(*entries) * size); + if (entries == NULL) + return false; + + // Mark all new file descriptors as unused. + for (size_t i = ft->size; i < size; ++i) + entries[i].object = NULL; + ft->entries = entries; + ft->size = size; + } + return true; +} + +// Allocates a new file descriptor object. +static __wasi_errno_t fd_object_new( + __wasi_filetype_t type, + struct fd_object **fo +) TRYLOCKS_SHARED(0, (*fo)->refcount) { + *fo = malloc(sizeof(**fo)); + if (*fo == NULL) + return __WASI_ENOMEM; + refcount_init(&(*fo)->refcount, 1); + (*fo)->type = type; + (*fo)->number = -1; + return 0; +} + +// Attaches a file descriptor to the file descriptor table. +static void fd_table_attach( + struct fd_table *ft, + __wasi_fd_t fd, + struct fd_object *fo, + __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting +) REQUIRES_EXCLUSIVE(ft->lock) CONSUMES(fo->refcount) { + assert(ft->size > fd && "File descriptor table too small"); + struct fd_entry *fe = &ft->entries[fd]; + assert(fe->object == NULL && "Attempted to overwrite an existing descriptor"); + fe->object = fo; + fe->rights_base = rights_base; + fe->rights_inheriting = rights_inheriting; + ++ft->used; + assert(ft->size >= ft->used * 2 && "File descriptor too full"); +} + +// Detaches a file descriptor from the file descriptor table. +static void fd_table_detach( + struct fd_table *ft, + __wasi_fd_t fd, + struct fd_object **fo +) REQUIRES_EXCLUSIVE(ft->lock) PRODUCES((*fo)->refcount) { + assert(ft->size > fd && "File descriptor table too small"); + struct fd_entry *fe = &ft->entries[fd]; + *fo = fe->object; + assert(*fo != NULL && "Attempted to detach nonexistent descriptor"); + fe->object = NULL; + assert(ft->used > 0 && "Reference count mismatch"); + --ft->used; +} + +// Determines the type of a file descriptor and its maximum set of +// rights that should be attached to it. +static __wasi_errno_t fd_determine_type_rights( + int fd, + __wasi_filetype_t *type, + __wasi_rights_t *rights_base, + __wasi_rights_t *rights_inheriting +) { + struct stat sb; + if (fstat(fd, &sb) < 0) + return convert_errno(errno); + if (S_ISBLK(sb.st_mode)) { + *type = __WASI_FILETYPE_BLOCK_DEVICE; + *rights_base = RIGHTS_BLOCK_DEVICE_BASE; + *rights_inheriting = RIGHTS_BLOCK_DEVICE_INHERITING; + } else if (S_ISCHR(sb.st_mode)) { + *type = __WASI_FILETYPE_CHARACTER_DEVICE; +#if CONFIG_HAS_ISATTY + if (isatty(fd)) { + *rights_base = RIGHTS_TTY_BASE; + *rights_inheriting = RIGHTS_TTY_INHERITING; + } else +#endif + { + *rights_base = RIGHTS_CHARACTER_DEVICE_BASE; + *rights_inheriting = RIGHTS_CHARACTER_DEVICE_INHERITING; + } + } else if (S_ISDIR(sb.st_mode)) { + *type = __WASI_FILETYPE_DIRECTORY; + *rights_base = RIGHTS_DIRECTORY_BASE; + *rights_inheriting = RIGHTS_DIRECTORY_INHERITING; + } else if (S_ISREG(sb.st_mode)) { + *type = __WASI_FILETYPE_REGULAR_FILE; + *rights_base = RIGHTS_REGULAR_FILE_BASE; + *rights_inheriting = RIGHTS_REGULAR_FILE_INHERITING; + } else if (S_ISSOCK(sb.st_mode)) { + int socktype; + socklen_t socktypelen = sizeof(socktype); + if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &socktype, &socktypelen) < 0) + return convert_errno(errno); + switch (socktype) { + case SOCK_DGRAM: + *type = __WASI_FILETYPE_SOCKET_DGRAM; + break; + case SOCK_STREAM: + *type = __WASI_FILETYPE_SOCKET_STREAM; + break; + default: + return __WASI_EINVAL; + } + *rights_base = RIGHTS_SOCKET_BASE; + *rights_inheriting = RIGHTS_SOCKET_INHERITING; + } else if (S_ISFIFO(sb.st_mode)) { + *type = __WASI_FILETYPE_SOCKET_STREAM; + *rights_base = RIGHTS_SOCKET_BASE; + *rights_inheriting = RIGHTS_SOCKET_INHERITING; + } else { + return __WASI_EINVAL; + } + + // Strip off read/write bits based on the access mode. + switch (fcntl(fd, F_GETFL) & O_ACCMODE) { + case O_RDONLY: + *rights_base &= ~__WASI_RIGHT_FD_WRITE; + break; + case O_WRONLY: + *rights_base &= ~__WASI_RIGHT_FD_READ; + break; + } + return 0; +} + +// Returns the underlying file descriptor number of a file descriptor +// object. This function can only be applied to objects that have an +// underlying file descriptor number. +static int fd_number( + const struct fd_object *fo +) { + int number = fo->number; + assert(number >= 0 && "fd_number() called on virtual file descriptor"); + return number; +} + +// Lowers the reference count on a file descriptor object. When the +// reference count reaches zero, its resources are cleaned up. +static void fd_object_release( + struct fd_object *fo +) UNLOCKS(fo->refcount) { + if (refcount_release(&fo->refcount)) { + switch (fo->type) { + case __WASI_FILETYPE_DIRECTORY: + // For directories we may keep track of a DIR object. Calling + // closedir() on it also closes the underlying file descriptor. + mutex_destroy(&fo->directory.lock); + if (fo->directory.handle == NULL) { + close(fd_number(fo)); + } else { + closedir(fo->directory.handle); + } + break; + default: + close(fd_number(fo)); + break; + } + free(fo); + } +} + +// Inserts an already existing file descriptor into the file descriptor +// table. +bool fd_table_insert_existing( + struct fd_table *ft, + __wasi_fd_t in, + int out +) { + __wasi_filetype_t type; + __wasi_rights_t rights_base, rights_inheriting; + if (fd_determine_type_rights(out, &type, &rights_base, &rights_inheriting) != + 0) + return false; + + struct fd_object *fo; + __wasi_errno_t error = fd_object_new(type, &fo); + if (error != 0) + return false; + fo->number = out; + if (type == __WASI_FILETYPE_DIRECTORY) { + mutex_init(&fo->directory.lock); + fo->directory.handle = NULL; + } + + // Grow the file descriptor table if needed. + rwlock_wrlock(&ft->lock); + if (!fd_table_grow(ft, in, 1)) { + rwlock_unlock(&ft->lock); + fd_object_release(fo); + return false; + } + + fd_table_attach(ft, in, fo, rights_base, rights_inheriting); + rwlock_unlock(&ft->lock); + return true; +} + +// Picks an unused slot from the file descriptor table. +static __wasi_fd_t fd_table_unused( + struct fd_table *ft +) REQUIRES_SHARED(ft->lock) { + assert(ft->size > ft->used && "File descriptor table has no free slots"); + for (;;) { + __wasi_fd_t fd = random_uniform(ft->size); + if (ft->entries[fd].object == NULL) + return fd; + } +} + +// Inserts a file descriptor object into an unused slot of the file +// descriptor table. +static __wasi_errno_t fd_table_insert( + struct fd_table *ft, + struct fd_object *fo, + __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting, + __wasi_fd_t *out +) REQUIRES_UNLOCKED(ft->lock) UNLOCKS(fo->refcount) { + // Grow the file descriptor table if needed. + rwlock_wrlock(&ft->lock); + if (!fd_table_grow(ft, 0, 1)) { + rwlock_unlock(&ft->lock); + fd_object_release(fo); + return convert_errno(errno); + } + + *out = fd_table_unused(ft); + fd_table_attach(ft, *out, fo, rights_base, rights_inheriting); + rwlock_unlock(&ft->lock); + return 0; +} + +// Inserts a numerical file descriptor into the file descriptor table. +static __wasi_errno_t fd_table_insert_fd( + struct fd_table *ft, + int in, + __wasi_filetype_t type, + __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting, + __wasi_fd_t *out +) REQUIRES_UNLOCKED(ft->lock) { + struct fd_object *fo; + __wasi_errno_t error = fd_object_new(type, &fo); + if (error != 0) { + close(in); + return error; + } + fo->number = in; + if (type == __WASI_FILETYPE_DIRECTORY) { + mutex_init(&fo->directory.lock); + fo->directory.handle = NULL; + } + return fd_table_insert(ft, fo, rights_base, rights_inheriting, out); +} + +// Inserts a pair of numerical file descriptors into the file descriptor +// table. +static __wasi_errno_t fd_table_insert_fdpair( + struct fd_table *ft, + const int *in, + __wasi_filetype_t type, + __wasi_rights_t rights_base1, + __wasi_rights_t rights_base2, + __wasi_rights_t rights_inheriting, + __wasi_fd_t *out1, + __wasi_fd_t *out2 +) REQUIRES_UNLOCKED(ft->lock) { + struct fd_object *fo1; + __wasi_errno_t error = fd_object_new(type, &fo1); + if (error != 0) { + close(in[0]); + close(in[1]); + return error; + } + fo1->number = in[0]; + struct fd_object *fo2; + error = fd_object_new(type, &fo2); + if (error != 0) { + fd_object_release(fo1); + close(in[1]); + return error; + } + fo2->number = in[1]; + + // Grow the file descriptor table if needed. + rwlock_wrlock(&ft->lock); + if (!fd_table_grow(ft, 0, 2)) { + rwlock_unlock(&ft->lock); + fd_object_release(fo1); + fd_object_release(fo2); + return convert_errno(errno); + } + + *out1 = fd_table_unused(ft); + fd_table_attach(ft, *out1, fo1, rights_base1, rights_inheriting); + *out2 = fd_table_unused(ft); + fd_table_attach(ft, *out2, fo2, rights_base2, rights_inheriting); + rwlock_unlock(&ft->lock); + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_prestat_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_prestats *prestats, +#endif + __wasi_fd_t fd, + __wasi_prestat_t *buf +) { + rwlock_rdlock(&prestats->lock); + struct fd_prestat *prestat; + __wasi_errno_t error = fd_prestats_get_entry(prestats, fd, &prestat); + if (error != 0) { + rwlock_unlock(&prestats->lock); + return error; + } + + *buf = (__wasi_prestat_t) { + .pr_type = __WASI_PREOPENTYPE_DIR, + }; + + buf->u.dir.pr_name_len = strlen(prestat->dir); + + rwlock_unlock(&prestats->lock); + + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_prestat_dir_name( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_prestats *prestats, +#endif + __wasi_fd_t fd, + char *path, + size_t path_len +) { + rwlock_rdlock(&prestats->lock); + struct fd_prestat *prestat; + __wasi_errno_t error = fd_prestats_get_entry(prestats, fd, &prestat); + if (error != 0) { + rwlock_unlock(&prestats->lock); + return error; + } + if (path_len != strlen(prestat->dir)) { + rwlock_unlock(&prestats->lock); + return EINVAL; + } + + memcpy(path, prestat->dir, path_len); + + rwlock_unlock(&prestats->lock); + + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_close( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, + struct fd_prestats *prestats, +#endif + __wasi_fd_t fd +) { + // Don't allow closing a pre-opened resource. + // TODO: Eventually, we do want to permit this, once libpreopen in + // userspace is capable of removing entries from its tables as well. + { + rwlock_rdlock(&prestats->lock); + struct fd_prestat *prestat; + __wasi_errno_t error = fd_prestats_get_entry(prestats, fd, &prestat); + rwlock_unlock(&prestats->lock); + if (error == 0) { + return __WASI_ENOTSUP; + } + } + + // Validate the file descriptor. + struct fd_table *ft = curfds; + rwlock_wrlock(&ft->lock); + struct fd_entry *fe; + __wasi_errno_t error = fd_table_get_entry(ft, fd, 0, 0, &fe); + if (error != 0) { + rwlock_unlock(&ft->lock); + return error; + } + + // Remove it from the file descriptor table. + struct fd_object *fo; + fd_table_detach(ft, fd, &fo); + rwlock_unlock(&ft->lock); + fd_object_release(fo); + return 0; +} + +static __wasi_errno_t fd_create_socketpair( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_filetype_t type, + int socktype, + __wasi_fd_t *fd1, + __wasi_fd_t *fd2 +) { + int fds[2]; + if (socketpair(AF_UNIX, socktype, 0, fds) < 0) + return convert_errno(errno); + return fd_table_insert_fdpair(curfds, fds, type, RIGHTS_SOCKET_BASE, + RIGHTS_SOCKET_BASE, RIGHTS_SOCKET_INHERITING, + fd1, fd2); +} + +// Look up a file descriptor object in a locked file descriptor table +// and increases its reference count. +static __wasi_errno_t fd_object_get_locked( + struct fd_object **fo, + struct fd_table *ft, + __wasi_fd_t fd, + __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting +) TRYLOCKS_EXCLUSIVE(0, (*fo)->refcount) REQUIRES_EXCLUSIVE(ft->lock) { + // Test whether the file descriptor number is valid. + struct fd_entry *fe; + __wasi_errno_t error = + fd_table_get_entry(ft, fd, rights_base, rights_inheriting, &fe); + if (error != 0) + return error; + + // Increase the reference count on the file descriptor object. A copy + // of the rights are also stored, so callers can still access those if + // needed. + *fo = fe->object; + refcount_acquire(&(*fo)->refcount); + return 0; +} + +// Temporarily locks the file descriptor table to look up a file +// descriptor object, increases its reference count and drops the lock. +static __wasi_errno_t fd_object_get( + struct fd_table *curfds, + struct fd_object **fo, + __wasi_fd_t fd, + __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting +) TRYLOCKS_EXCLUSIVE(0, (*fo)->refcount) { + struct fd_table *ft = curfds; + rwlock_rdlock(&ft->lock); + __wasi_errno_t error = + fd_object_get_locked(fo, ft, fd, rights_base, rights_inheriting); + rwlock_unlock(&ft->lock); + return error; +} + +__wasi_errno_t wasmtime_ssp_fd_datasync( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd +) { + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_DATASYNC, 0); + if (error != 0) + return error; + +#if CONFIG_HAS_FDATASYNC + int ret = fdatasync(fd_number(fo)); +#else + int ret = fsync(fd_number(fo)); +#endif + fd_object_release(fo); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_pread( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const __wasi_iovec_t *iov, + size_t iovcnt, + __wasi_filesize_t offset, + size_t *nread +) { + if (iovcnt == 0) + return __WASI_EINVAL; + + struct fd_object *fo; + __wasi_errno_t error = fd_object_get(curfds, + &fo, fd, __WASI_RIGHT_FD_READ | __WASI_RIGHT_FD_SEEK, 0); + if (error != 0) + return error; + +#if CONFIG_HAS_PREADV + ssize_t len = + preadv(fd_number(fo), (const struct iovec *)iov, iovcnt, offset); + fd_object_release(fo); + if (len < 0) + return convert_errno(errno); + *nread = len; + return 0; +#else + if (iovcnt == 1) { + ssize_t len = pread(fd_number(fo), iov->buf, iov->buf_len, offset); + fd_object_release(fo); + if (len < 0) + return convert_errno(errno); + *nread = len; + return 0; + } else { + // Allocate a single buffer to fit all data. + size_t totalsize = 0; + for (size_t i = 0; i < iovcnt; ++i) + totalsize += iov[i].buf_len; + char *buf = malloc(totalsize); + if (buf == NULL) { + fd_object_release(fo); + return __WASI_ENOMEM; + } + + // Perform a single read operation. + ssize_t len = pread(fd_number(fo), buf, totalsize, offset); + fd_object_release(fo); + if (len < 0) { + free(buf); + return convert_errno(errno); + } + + // Copy data back to vectors. + size_t bufoff = 0; + for (size_t i = 0; i < iovcnt; ++i) { + if (bufoff + iov[i].buf_len < len) { + memcpy(iov[i].buf, buf + bufoff, iov[i].buf_len); + bufoff += iov[i].buf_len; + } else { + memcpy(iov[i].buf, buf + bufoff, len - bufoff); + break; + } + } + free(buf); + *nread = len; + return 0; + } +#endif +} + +__wasi_errno_t wasmtime_ssp_fd_pwrite( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const __wasi_ciovec_t *iov, + size_t iovcnt, + __wasi_filesize_t offset, + size_t *nwritten +) { + if (iovcnt == 0) + return __WASI_EINVAL; + + struct fd_object *fo; + __wasi_errno_t error = fd_object_get(curfds, + &fo, fd, __WASI_RIGHT_FD_WRITE | __WASI_RIGHT_FD_SEEK, 0); + if (error != 0) + return error; + + ssize_t len; +#if CONFIG_HAS_PWRITEV + len = pwritev(fd_number(fo), (const struct iovec *)iov, iovcnt, offset); +#else + if (iovcnt == 1) { + len = pwrite(fd_number(fo), iov->buf, iov->buf_len, offset); + } else { + // Allocate a single buffer to fit all data. + size_t totalsize = 0; + for (size_t i = 0; i < iovcnt; ++i) + totalsize += iov[i].buf_len; + char *buf = malloc(totalsize); + if (buf == NULL) { + fd_object_release(fo); + return __WASI_ENOMEM; + } + size_t bufoff = 0; + for (size_t i = 0; i < iovcnt; ++i) { + memcpy(buf + bufoff, iov[i].buf, iov[i].buf_len); + bufoff += iov[i].buf_len; + } + + // Perform a single write operation. + len = pwrite(fd_number(fo), buf, totalsize, offset); + free(buf); + } +#endif + fd_object_release(fo); + if (len < 0) + return convert_errno(errno); + *nwritten = len; + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_read( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const __wasi_iovec_t *iov, + size_t iovcnt, + size_t *nread +) { + struct fd_object *fo; + __wasi_errno_t error = fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_READ, 0); + if (error != 0) + return error; + + ssize_t len = readv(fd_number(fo), (const struct iovec *)iov, iovcnt); + fd_object_release(fo); + if (len < 0) + return convert_errno(errno); + *nread = len; + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_renumber( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t from, + __wasi_fd_t to +) { + struct fd_table *ft = curfds; + rwlock_wrlock(&ft->lock); + struct fd_entry *fe_from; + __wasi_errno_t error = fd_table_get_entry(ft, from, 0, 0, &fe_from); + if (error != 0) { + rwlock_unlock(&ft->lock); + return error; + } + struct fd_entry *fe_to; + error = fd_table_get_entry(ft, to, 0, 0, &fe_to); + if (error != 0) { + rwlock_unlock(&ft->lock); + return error; + } + + struct fd_object *fo; + fd_table_detach(ft, to, &fo); + refcount_acquire(&fe_from->object->refcount); + fd_table_attach(ft, to, fe_from->object, fe_from->rights_base, + fe_from->rights_inheriting); + rwlock_unlock(&ft->lock); + fd_object_release(fo); + + // Remove the old fd from the file descriptor table. + fd_table_detach(ft, from, &fo); + fd_object_release(fo); + --ft->used; + + rwlock_unlock(&ft->lock); + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_seek( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filedelta_t offset, + __wasi_whence_t whence, + __wasi_filesize_t *newoffset +) { + int nwhence; + switch (whence) { + case __WASI_WHENCE_CUR: + nwhence = SEEK_CUR; + break; + case __WASI_WHENCE_END: + nwhence = SEEK_END; + break; + case __WASI_WHENCE_SET: + nwhence = SEEK_SET; + break; + default: + return __WASI_EINVAL; + } + + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, + offset == 0 && whence == __WASI_WHENCE_CUR + ? __WASI_RIGHT_FD_TELL + : __WASI_RIGHT_FD_SEEK | __WASI_RIGHT_FD_TELL, + 0); + if (error != 0) + return error; + + off_t ret = lseek(fd_number(fo), offset, nwhence); + fd_object_release(fo); + if (ret < 0) + return convert_errno(errno); + *newoffset = ret; + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_tell( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filesize_t *newoffset +) { + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_TELL, 0); + if (error != 0) + return error; + + off_t ret = lseek(fd_number(fo), 0, SEEK_CUR); + fd_object_release(fo); + if (ret < 0) + return convert_errno(errno); + *newoffset = ret; + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_fdstat_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_fdstat_t *buf +) { + struct fd_table *ft = curfds; + rwlock_rdlock(&ft->lock); + struct fd_entry *fe; + __wasi_errno_t error = fd_table_get_entry(ft, fd, 0, 0, &fe); + if (error != 0) { + rwlock_unlock(&ft->lock); + return error; + } + + // Extract file descriptor type and rights. + struct fd_object *fo = fe->object; + *buf = (__wasi_fdstat_t){ + .fs_filetype = fo->type, + .fs_rights_base = fe->rights_base, + .fs_rights_inheriting = fe->rights_inheriting, + }; + + // Fetch file descriptor flags. + int ret; + switch (fo->type) { + default: + ret = fcntl(fd_number(fo), F_GETFL); + break; + } + rwlock_unlock(&ft->lock); + if (ret < 0) + return convert_errno(errno); + + if ((ret & O_APPEND) != 0) + buf->fs_flags |= __WASI_FDFLAG_APPEND; +#ifdef O_DSYNC + if ((ret & O_DSYNC) != 0) + buf->fs_flags |= __WASI_FDFLAG_DSYNC; +#endif + if ((ret & O_NONBLOCK) != 0) + buf->fs_flags |= __WASI_FDFLAG_NONBLOCK; +#ifdef O_RSYNC + if ((ret & O_RSYNC) != 0) + buf->fs_flags |= __WASI_FDFLAG_RSYNC; +#endif + if ((ret & O_SYNC) != 0) + buf->fs_flags |= __WASI_FDFLAG_SYNC; + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_fdstat_set_flags( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_fdflags_t fs_flags +) { + int noflags = 0; + if ((fs_flags & __WASI_FDFLAG_APPEND) != 0) + noflags |= O_APPEND; + if ((fs_flags & __WASI_FDFLAG_DSYNC) != 0) +#ifdef O_DSYNC + noflags |= O_DSYNC; +#else + noflags |= O_SYNC; +#endif + if ((fs_flags & __WASI_FDFLAG_NONBLOCK) != 0) + noflags |= O_NONBLOCK; + if ((fs_flags & __WASI_FDFLAG_RSYNC) != 0) +#ifdef O_RSYNC + noflags |= O_RSYNC; +#else + noflags |= O_SYNC; +#endif + if ((fs_flags & __WASI_FDFLAG_SYNC) != 0) + noflags |= O_SYNC; + + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_FDSTAT_SET_FLAGS, 0); + if (error != 0) + return error; + + int ret = fcntl(fd_number(fo), F_SETFL, noflags); + fd_object_release(fo); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_fdstat_set_rights( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_rights_t fs_rights_base, + __wasi_rights_t fs_rights_inheriting +) { + struct fd_table *ft = curfds; + rwlock_wrlock(&ft->lock); + struct fd_entry *fe; + __wasi_errno_t error = + fd_table_get_entry(ft, fd, fs_rights_base, fs_rights_inheriting, &fe); + if (error != 0) { + rwlock_unlock(&ft->lock); + return error; + } + + // Restrict the rights on the file descriptor. + fe->rights_base = fs_rights_base; + fe->rights_inheriting = fs_rights_inheriting; + rwlock_unlock(&ft->lock); + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_sync( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd +) { + struct fd_object *fo; + __wasi_errno_t error = fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_SYNC, 0); + if (error != 0) + return error; + + int ret = fsync(fd_number(fo)); + fd_object_release(fo); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_write( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const __wasi_ciovec_t *iov, + size_t iovcnt, + size_t *nwritten +) { + struct fd_object *fo; + __wasi_errno_t error = fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_WRITE, 0); + if (error != 0) + return error; + + ssize_t len = writev(fd_number(fo), (const struct iovec *)iov, iovcnt); + fd_object_release(fo); + if (len < 0) + return convert_errno(errno); + *nwritten = len; + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_advise( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filesize_t offset, + __wasi_filesize_t len, + __wasi_advice_t advice +) { +#ifdef POSIX_FADV_NORMAL + int nadvice; + switch (advice) { + case __WASI_ADVICE_DONTNEED: + nadvice = POSIX_FADV_DONTNEED; + break; + case __WASI_ADVICE_NOREUSE: + nadvice = POSIX_FADV_NOREUSE; + break; + case __WASI_ADVICE_NORMAL: + nadvice = POSIX_FADV_NORMAL; + break; + case __WASI_ADVICE_RANDOM: + nadvice = POSIX_FADV_RANDOM; + break; + case __WASI_ADVICE_SEQUENTIAL: + nadvice = POSIX_FADV_SEQUENTIAL; + break; + case __WASI_ADVICE_WILLNEED: + nadvice = POSIX_FADV_WILLNEED; + break; + default: + return __WASI_EINVAL; + } + + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_ADVISE, 0); + if (error != 0) + return error; + + int ret = posix_fadvise(fd_number(fo), offset, len, nadvice); + fd_object_release(fo); + if (ret != 0) + return convert_errno(ret); + return 0; +#else + // Advisory information can safely be ignored if unsupported. + switch (advice) { + case __WASI_ADVICE_DONTNEED: + case __WASI_ADVICE_NOREUSE: + case __WASI_ADVICE_NORMAL: + case __WASI_ADVICE_RANDOM: + case __WASI_ADVICE_SEQUENTIAL: + case __WASI_ADVICE_WILLNEED: + break; + default: + return __WASI_EINVAL; + } + + // At least check for file descriptor existence. + struct fd_table *ft = curfds; + rwlock_rdlock(&ft->lock); + struct fd_entry *fe; + __wasi_errno_t error = + fd_table_get_entry(ft, fd, __WASI_RIGHT_FD_ADVISE, 0, &fe); + rwlock_unlock(&ft->lock); + return error; +#endif +} + +__wasi_errno_t wasmtime_ssp_fd_allocate( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filesize_t offset, + __wasi_filesize_t len +) { + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_ALLOCATE, 0); + if (error != 0) + return error; + +#if CONFIG_HAS_POSIX_FALLOCATE + int ret = posix_fallocate(fd_number(fo), offset, len); +#else + // At least ensure that the file is grown to the right size. + // TODO(ed): See if this can somehow be implemented without any race + // conditions. We may end up shrinking the file right now. + struct stat sb; + int ret = fstat(fd_number(fo), &sb); + if (ret == 0 && sb.st_size < offset + len) + ret = ftruncate(fd_number(fo), offset + len); +#endif + + fd_object_release(fo); + if (ret != 0) + return convert_errno(ret); + return 0; +} + +// Reads the entire contents of a symbolic link, returning the contents +// in an allocated buffer. The allocated buffer is large enough to fit +// at least one extra byte, so the caller may append a trailing slash to +// it. This is needed by path_get(). +static char *readlinkat_dup( + int fd, + const char *path +) { + char *buf = NULL; + size_t len = 32; + for (;;) { + char *newbuf = realloc(buf, len); + if (newbuf == NULL) { + free(buf); + return NULL; + } + buf = newbuf; + ssize_t ret = readlinkat(fd, path, buf, len); + if (ret < 0) { + free(buf); + return NULL; + } + if ((size_t)ret + 1 < len) { + buf[ret] = '\0'; + return buf; + } + len *= 2; + } +} + +// Lease to a directory, so a path underneath it can be accessed. +// +// This structure is used by system calls that operate on pathnames. In +// this environment, pathnames always consist of a pair of a file +// descriptor representing the directory where the lookup needs to start +// and the actual pathname string. +struct path_access { + int fd; // Directory file descriptor. + const char *path; // Pathname. + bool follow; // Whether symbolic links should be followed. + char *path_start; // Internal: pathname to free. + struct fd_object *fd_object; // Internal: directory file descriptor object. +}; + +// Creates a lease to a file descriptor and pathname pair. If the +// operating system does not implement Capsicum, it also normalizes the +// pathname to ensure the target path is placed underneath the +// directory. +static __wasi_errno_t path_get( + struct fd_table *curfds, + struct path_access *pa, + __wasi_fd_t fd, + __wasi_lookupflags_t flags, + const char *upath, + size_t upathlen, + __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting, + bool needs_final_component +) TRYLOCKS_EXCLUSIVE(0, pa->fd_object->refcount) { + char *path = str_nullterminate(upath, upathlen); + if (path == NULL) + return convert_errno(errno); + + // Fetch the directory file descriptor. + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, rights_base, rights_inheriting); + if (error != 0) { + free(path); + return error; + } + +#if CONFIG_HAS_CAP_ENTER + // Rely on the kernel to constrain access to automatically constrain + // access to files stored underneath this directory. + pa->fd = fd_number(fo); + pa->path = pa->path_start = path; + pa->follow = (flags & __WASI_LOOKUP_SYMLINK_FOLLOW) != 0; + pa->fd_object = fo; + return 0; +#else + // The implementation provides no mechanism to constrain lookups to a + // directory automatically. Emulate this logic by resolving the + // pathname manually. + + // 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. + int fds[128]; + fds[0] = fd_number(fo); + size_t curfd = 0; + + // Stack of pathname strings used for symlink expansion. By using a + // stack, there is no need to concatenate any pathname strings while + // expanding symlinks. + char *paths[32]; + char *paths_start[32]; + paths[0] = paths_start[0] = path; + size_t curpath = 0; + size_t expansions = 0; + + char *symlink; + for (;;) { + // Extract the next pathname component from 'paths[curpath]', null + // terminate it and store it in 'file'. 'ends_with_slashes' stores + // whether the pathname component is followed by one or more + // trailing slashes, as this requires it to be a directory. + char *file = paths[curpath]; + char *file_end = file + strcspn(file, "/"); + paths[curpath] = file_end + strspn(file_end, "/"); + bool ends_with_slashes = *file_end == '/'; + *file_end = '\0'; + + // Test for empty pathname strings and absolute paths. + if (file == file_end) { + error = ends_with_slashes ? __WASI_ENOTCAPABLE : __WASI_ENOENT; + goto fail; + } + + if (strcmp(file, ".") == 0) { + // Skip component. + } else if (strcmp(file, "..") == 0) { + // Pop a directory off the stack. + if (curfd == 0) { + // Attempted to go to parent directory of the directory file + // descriptor. + error = __WASI_ENOTCAPABLE; + goto fail; + } + close(fds[curfd--]); + } else if (curpath > 0 || *paths[curpath] != '\0' || + (ends_with_slashes && !needs_final_component)) { + // A pathname component whose name we're not interested in that is + // followed by a slash or is followed by other pathname + // components. In other words, a pathname component that must be a + // directory. First attempt to obtain a directory file descriptor + // for it. + int newdir = +#ifdef O_SEARCH + openat(fds[curfd], file, O_SEARCH | O_DIRECTORY | O_NOFOLLOW); +#else + openat(fds[curfd], file, O_RDONLY | O_DIRECTORY | O_NOFOLLOW); +#endif + if (newdir != -1) { + // Success. Push it onto the directory stack. + if (curfd + 1 == sizeof(fds) / sizeof(fds[0])) { + close(newdir); + error = __WASI_ENAMETOOLONG; + goto fail; + } + fds[++curfd] = newdir; + } else { + // Failed to open it. Attempt symlink expansion. + if (errno != ELOOP && errno != EMLINK && errno != ENOTDIR) { + error = convert_errno(errno); + goto fail; + } + symlink = readlinkat_dup(fds[curfd], file); + if (symlink != NULL) + goto push_symlink; + + // readlink returns EINVAL if the path isn't a symlink. In that case, + // it's more informative to return ENOTDIR. + if (errno == EINVAL) + errno = ENOTDIR; + + error = convert_errno(errno); + goto fail; + } + } else { + // The final pathname component. Depending on whether it ends with + // a slash or the symlink-follow flag is set, perform symlink + // expansion. + if (ends_with_slashes || + (flags & __WASI_LOOKUP_SYMLINK_FOLLOW) != 0) { + symlink = readlinkat_dup(fds[curfd], file); + if (symlink != NULL) + goto push_symlink; + if (errno != EINVAL && errno != ENOENT) { + error = convert_errno(errno); + goto fail; + } + } + + // Not a symlink, meaning we're done. Return the filename, + // together with the directory containing this file. + // + // If the file was followed by a trailing slash, we must retain + // it, to ensure system calls properly return ENOTDIR. + // Unfortunately, this opens up a race condition, because this + // means that users of path_get() will perform symlink expansion a + // second time. There is nothing we can do to mitigate this, as + // far as I know. + if (ends_with_slashes) + *file_end = '/'; + pa->path = file; + pa->path_start = paths_start[0]; + goto success; + } + + if (*paths[curpath] == '\0') { + if (curpath == 0) { + // No further pathname components to process. We may end up here + // when called on paths like ".", "a/..", but also if the path + // had trailing slashes and the caller is not interested in the + // name of the pathname component. + free(paths_start[0]); + pa->path = "."; + pa->path_start = NULL; + goto success; + } + + // Finished expanding symlink. Continue processing along the + // original path. + free(paths_start[curpath--]); + } + continue; + + push_symlink: + // Prevent infinite loops by placing an upper limit on the number of + // symlink expansions. + if (++expansions == 128) { + free(symlink); + error = __WASI_ELOOP; + goto fail; + } + + if (*paths[curpath] == '\0') { + // The original path already finished processing. Replace it by + // this symlink entirely. + free(paths_start[curpath]); + } else if (curpath + 1 == sizeof(paths) / sizeof(paths[0])) { + // Too many nested symlinks. Stop processing. + free(symlink); + error = __WASI_ELOOP; + goto fail; + } else { + // The original path still has components left. Retain the + // components that remain, so we can process them afterwards. + ++curpath; + } + + // Append a trailing slash to the symlink if the path leading up to + // it also contained one. Otherwise we would not throw ENOTDIR if + // the target is not a directory. + if (ends_with_slashes) + strcat(symlink, "/"); + paths[curpath] = paths_start[curpath] = symlink; + } + +success: + // Return the lease. Close all directories, except the one the caller + // needs to use. + for (size_t i = 1; i < curfd; ++i) + close(fds[i]); + pa->fd = fds[curfd]; + pa->follow = false; + pa->fd_object = fo; + return 0; + +fail: + // Failure. Free all resources. + for (size_t i = 1; i <= curfd; ++i) + close(fds[i]); + for (size_t i = 0; i <= curpath; ++i) + free(paths_start[i]); + fd_object_release(fo); + return error; +#endif +} + +static __wasi_errno_t path_get_nofollow( + struct fd_table *curfds, + struct path_access *pa, + __wasi_fd_t fd, + const char *path, + size_t pathlen, + __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting, + bool needs_final_component +) TRYLOCKS_EXCLUSIVE(0, pa->fd_object->refcount) { + __wasi_lookupflags_t flags = 0; + return path_get(curfds, pa, fd, flags, path, pathlen, rights_base, rights_inheriting, + needs_final_component); +} + +static void path_put( + struct path_access *pa +) UNLOCKS(pa->fd_object->refcount) { + free(pa->path_start); + if (fd_number(pa->fd_object) != pa->fd) + close(pa->fd); + fd_object_release(pa->fd_object); +} + +__wasi_errno_t wasmtime_ssp_path_create_directory( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const char *path, + size_t pathlen +) { + struct path_access pa; + __wasi_errno_t error = + path_get_nofollow(curfds, &pa, fd, path, pathlen, + __WASI_RIGHT_PATH_CREATE_DIRECTORY, 0, true); + if (error != 0) + return error; + + int ret = mkdirat(pa.fd, pa.path, 0777); + path_put(&pa); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_path_link( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t old_fd, + __wasi_lookupflags_t old_flags, + const char *old_path, + size_t old_path_len, + __wasi_fd_t new_fd, + const char *new_path, + size_t new_path_len +) { + struct path_access old_pa; + __wasi_errno_t error = path_get(curfds, &old_pa, old_fd, old_flags, old_path, old_path_len, + __WASI_RIGHT_PATH_LINK_SOURCE, 0, false); + if (error != 0) + return error; + + struct path_access new_pa; + error = path_get_nofollow(curfds, &new_pa, new_fd, new_path, new_path_len, + __WASI_RIGHT_PATH_LINK_TARGET, 0, true); + if (error != 0) { + path_put(&old_pa); + return error; + } + + int ret = linkat(old_pa.fd, old_pa.path, new_pa.fd, new_pa.path, + old_pa.follow ? AT_SYMLINK_FOLLOW : 0); + if (ret < 0 && errno == ENOTSUP && !old_pa.follow) { + // OS X doesn't allow creating hardlinks to symbolic links. + // Duplicate the symbolic link instead. + char *target = readlinkat_dup(old_pa.fd, old_pa.path); + if (target != NULL) { + ret = symlinkat(target, new_pa.fd, new_pa.path); + free(target); + } + } + path_put(&old_pa); + path_put(&new_pa); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_path_open( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t dirfd, + __wasi_lookupflags_t dirflags, + const char *path, + size_t pathlen, + __wasi_oflags_t oflags, + __wasi_rights_t fs_rights_base, + __wasi_rights_t fs_rights_inheriting, + __wasi_fdflags_t fs_flags, + __wasi_fd_t *fd +) { + // Rights that should be installed on the new file descriptor. + __wasi_rights_t rights_base = fs_rights_base; + __wasi_rights_t rights_inheriting = fs_rights_inheriting; + + // Which open() mode should be used to satisfy the needed rights. + bool read = + (rights_base & (__WASI_RIGHT_FD_READ | __WASI_RIGHT_FD_READDIR)) != 0; + bool write = + (rights_base & (__WASI_RIGHT_FD_DATASYNC | __WASI_RIGHT_FD_WRITE | + __WASI_RIGHT_FD_ALLOCATE | + __WASI_RIGHT_PATH_FILESTAT_SET_SIZE)) != 0; + int noflags = write ? read ? O_RDWR : O_WRONLY : O_RDONLY; + + // Which rights are needed on the directory file descriptor. + __wasi_rights_t needed_base = __WASI_RIGHT_PATH_OPEN; + __wasi_rights_t needed_inheriting = rights_base | rights_inheriting; + + // Convert open flags. + if ((oflags & __WASI_O_CREAT) != 0) { + noflags |= O_CREAT; + needed_base |= __WASI_RIGHT_PATH_CREATE_FILE; + } + if ((oflags & __WASI_O_DIRECTORY) != 0) + noflags |= O_DIRECTORY; + if ((oflags & __WASI_O_EXCL) != 0) + noflags |= O_EXCL; + if ((oflags & __WASI_O_TRUNC) != 0) { + noflags |= O_TRUNC; + needed_inheriting |= __WASI_RIGHT_PATH_FILESTAT_SET_SIZE; + } + + // Convert file descriptor flags. + if ((fs_flags & __WASI_FDFLAG_APPEND) != 0) + noflags |= O_APPEND; + if ((fs_flags & __WASI_FDFLAG_DSYNC) != 0) { +#ifdef O_DSYNC + noflags |= O_DSYNC; +#else + noflags |= O_SYNC; +#endif + needed_inheriting |= __WASI_RIGHT_FD_DATASYNC; + } + if ((fs_flags & __WASI_FDFLAG_NONBLOCK) != 0) + noflags |= O_NONBLOCK; + if ((fs_flags & __WASI_FDFLAG_RSYNC) != 0) { +#ifdef O_RSYNC + noflags |= O_RSYNC; +#else + noflags |= O_SYNC; +#endif + needed_inheriting |= __WASI_RIGHT_FD_SYNC; + } + if ((fs_flags & __WASI_FDFLAG_SYNC) != 0) { + noflags |= O_SYNC; + needed_inheriting |= __WASI_RIGHT_FD_SYNC; + } + if (write && (noflags & (O_APPEND | O_TRUNC)) == 0) + needed_inheriting |= __WASI_RIGHT_FD_SEEK; + + struct path_access pa; + __wasi_errno_t error = + path_get(curfds, &pa, dirfd, dirflags, path, pathlen, needed_base, needed_inheriting, + (oflags & __WASI_O_CREAT) != 0); + if (error != 0) + return error; + if (!pa.follow) + noflags |= O_NOFOLLOW; + + int nfd = openat(pa.fd, pa.path, noflags, 0666); + if (nfd < 0) { + // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket. + if (errno == ENXIO) { + struct stat sb; + int ret = + fstatat(pa.fd, pa.path, &sb, pa.follow ? 0 : AT_SYMLINK_NOFOLLOW); + path_put(&pa); + return ret == 0 && S_ISSOCK(sb.st_mode) ? __WASI_ENOTSUP + : __WASI_ENXIO; + } + path_put(&pa); + // FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on + // a symlink. + if (!pa.follow && errno == EMLINK) + return __WASI_ELOOP; + return convert_errno(errno); + } + path_put(&pa); + + // Determine the type of the new file descriptor and which rights + // contradict with this type. + __wasi_filetype_t type; + __wasi_rights_t max_base, max_inheriting; + error = fd_determine_type_rights(nfd, &type, &max_base, &max_inheriting); + if (error != 0) { + close(nfd); + return error; + } + return fd_table_insert_fd(curfds, nfd, type, rights_base & max_base, + rights_inheriting & max_inheriting, fd); +} + +// Copies out directory entry metadata or filename, potentially +// truncating it in the process. +static void fd_readdir_put( + void *buf, + size_t bufsize, + size_t *bufused, + const void *elem, + size_t elemsize +) { + size_t bufavail = bufsize - *bufused; + if (elemsize > bufavail) + elemsize = bufavail; + memcpy((char *)buf + *bufused, elem, elemsize); + *bufused += elemsize; +} + +__wasi_errno_t wasmtime_ssp_fd_readdir( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + void *buf, + size_t nbyte, + __wasi_dircookie_t cookie, + size_t *bufused +) { + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_READDIR, 0); + if (error != 0) { + return error; + } + + // Create a directory handle if none has been opened yet. + mutex_lock(&fo->directory.lock); + DIR *dp = fo->directory.handle; + if (dp == NULL) { + dp = fdopendir(fd_number(fo)); + if (dp == NULL) { + mutex_unlock(&fo->directory.lock); + fd_object_release(fo); + return convert_errno(errno); + } + fo->directory.handle = dp; + fo->directory.offset = __WASI_DIRCOOKIE_START; + } + + // Seek to the right position if the requested offset does not match + // the current offset. + if (fo->directory.offset != cookie) { + if (cookie == __WASI_DIRCOOKIE_START) + rewinddir(dp); + else + seekdir(dp, cookie); + fo->directory.offset = cookie; + } + + *bufused = 0; + while (nbyte > 0) { + // Read the next directory entry. + errno = 0; + struct dirent *de = readdir(dp); + if (de == NULL) { + mutex_unlock(&fo->directory.lock); + fd_object_release(fo); + return errno == 0 || *bufused > 0 ? 0 : convert_errno(errno); + } + fo->directory.offset = telldir(dp); + + // Craft a directory entry and copy that back. + size_t namlen = strlen(de->d_name); + __wasi_dirent_t cde = { + .d_next = fo->directory.offset, + .d_ino = de->d_ino, + .d_namlen = namlen, + }; + switch (de->d_type) { + case DT_BLK: + cde.d_type = __WASI_FILETYPE_BLOCK_DEVICE; + break; + case DT_CHR: + cde.d_type = __WASI_FILETYPE_CHARACTER_DEVICE; + break; + case DT_DIR: + cde.d_type = __WASI_FILETYPE_DIRECTORY; + break; + case DT_FIFO: + cde.d_type = __WASI_FILETYPE_SOCKET_STREAM; + break; + case DT_LNK: + cde.d_type = __WASI_FILETYPE_SYMBOLIC_LINK; + break; + case DT_REG: + cde.d_type = __WASI_FILETYPE_REGULAR_FILE; + break; +#ifdef DT_SOCK + case DT_SOCK: + // Technically not correct, but good enough. + cde.d_type = __WASI_FILETYPE_SOCKET_STREAM; + break; +#endif + default: + cde.d_type = __WASI_FILETYPE_UNKNOWN; + break; + } + fd_readdir_put(buf, nbyte, bufused, &cde, sizeof(cde)); + fd_readdir_put(buf, nbyte, bufused, de->d_name, namlen); + } + mutex_unlock(&fo->directory.lock); + fd_object_release(fo); + return 0; +} + +__wasi_errno_t wasmtime_ssp_path_readlink( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const char *path, + size_t pathlen, + char *buf, + size_t bufsize, + size_t *bufused +) { + struct path_access pa; + __wasi_errno_t error = path_get_nofollow(curfds, + &pa, fd, path, pathlen, __WASI_RIGHT_PATH_READLINK, 0, false); + if (error != 0) + return error; + + // Linux requires that the buffer size is positive. whereas POSIX does + // not. Use a fake buffer to store the results if the size is zero. + char fakebuf[1]; + ssize_t len = readlinkat(pa.fd, pa.path, bufsize == 0 ? fakebuf : buf, + bufsize == 0 ? sizeof(fakebuf) : bufsize); + path_put(&pa); + if (len < 0) + return convert_errno(errno); + *bufused = (size_t)len < bufsize ? len : bufsize; + return 0; +} + +__wasi_errno_t wasmtime_ssp_path_rename( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t old_fd, + const char *old_path, + size_t old_path_len, + __wasi_fd_t new_fd, + const char *new_path, + size_t new_path_len +) { + struct path_access old_pa; + __wasi_errno_t error = path_get_nofollow(curfds, &old_pa, old_fd, old_path, old_path_len, + __WASI_RIGHT_PATH_RENAME_SOURCE, 0, true); + if (error != 0) + return error; + + struct path_access new_pa; + error = path_get_nofollow(curfds, &new_pa, new_fd, new_path, new_path_len, + __WASI_RIGHT_PATH_RENAME_TARGET, 0, true); + if (error != 0) { + path_put(&old_pa); + return error; + } + + int ret = renameat(old_pa.fd, old_pa.path, new_pa.fd, new_pa.path); + path_put(&old_pa); + path_put(&new_pa); + if (ret < 0) { + // Linux returns EBUSY in cases where EINVAL would be more suited. + return errno == EBUSY ? __WASI_EINVAL : convert_errno(errno); + } + return 0; +} + +// Converts a POSIX stat structure to a CloudABI filestat structure. +static void convert_stat( + const struct stat *in, + __wasi_filestat_t *out +) { + *out = (__wasi_filestat_t){ + .st_dev = in->st_dev, + .st_ino = in->st_ino, + .st_nlink = in->st_nlink, + .st_size = in->st_size, + .st_atim = convert_timespec(&in->st_atim), + .st_mtim = convert_timespec(&in->st_mtim), + .st_ctim = convert_timespec(&in->st_ctim), + }; +} + +__wasi_errno_t wasmtime_ssp_fd_filestat_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filestat_t *buf +) { + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_FILESTAT_GET, 0); + if (error != 0) + return error; + + int ret; + switch (fo->type) { + default: { + struct stat sb; + ret = fstat(fd_number(fo), &sb); + convert_stat(&sb, buf); + break; + } + } + buf->st_filetype = fo->type; + fd_object_release(fo); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +static void convert_timestamp( + __wasi_timestamp_t in, + struct timespec *out +) { + // Store sub-second remainder. + out->tv_nsec = in % 1000000000; + in /= 1000000000; + + // Clamp to the maximum in case it would overflow our system's time_t. + out->tv_sec = in < NUMERIC_MAX(time_t) ? in : NUMERIC_MAX(time_t); +} + +// Converts the provided timestamps and flags to a set of arguments for +// futimens() and utimensat(). +static void convert_utimens_arguments( + __wasi_timestamp_t st_atim, + __wasi_timestamp_t st_mtim, + __wasi_fstflags_t fstflags, + struct timespec *ts +) { + if ((fstflags & __WASI_FILESTAT_SET_ATIM_NOW) != 0) { + ts[0].tv_nsec = UTIME_NOW; + } else if ((fstflags & __WASI_FILESTAT_SET_ATIM) != 0) { + convert_timestamp(st_atim, &ts[0]); + } else { + ts[0].tv_nsec = UTIME_OMIT; + } + + if ((fstflags & __WASI_FILESTAT_SET_MTIM_NOW) != 0) { + ts[1].tv_nsec = UTIME_NOW; + } else if ((fstflags & __WASI_FILESTAT_SET_MTIM) != 0) { + convert_timestamp(st_mtim, &ts[1]); + } else { + ts[1].tv_nsec = UTIME_OMIT; + } +} + +__wasi_errno_t wasmtime_ssp_fd_filestat_set_size( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filesize_t st_size +) { + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_FILESTAT_SET_SIZE, 0); + if (error != 0) + return error; + + int ret = ftruncate(fd_number(fo), st_size); + fd_object_release(fo); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_filestat_set_times( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_timestamp_t st_atim, + __wasi_timestamp_t st_mtim, + __wasi_fstflags_t fstflags +) { + if ((fstflags & ~(__WASI_FILESTAT_SET_ATIM | __WASI_FILESTAT_SET_ATIM_NOW | + __WASI_FILESTAT_SET_MTIM | __WASI_FILESTAT_SET_MTIM_NOW)) != 0) + return __WASI_EINVAL; + + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_FILESTAT_SET_TIMES, 0); + if (error != 0) + return error; + + struct timespec ts[2]; + convert_utimens_arguments(st_atim, st_mtim, fstflags, ts); + int ret = futimens(fd_number(fo), ts); + + fd_object_release(fo); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_path_filestat_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_lookupflags_t flags, + const char *path, + size_t pathlen, + __wasi_filestat_t *buf +) { + struct path_access pa; + __wasi_errno_t error = + path_get(curfds, &pa, fd, flags, path, pathlen, __WASI_RIGHT_PATH_FILESTAT_GET, 0, false); + if (error != 0) + return error; + + struct stat sb; + int ret = fstatat(pa.fd, pa.path, &sb, pa.follow ? 0 : AT_SYMLINK_NOFOLLOW); + path_put(&pa); + if (ret < 0) + return convert_errno(errno); + convert_stat(&sb, buf); + + // Convert the file type. In the case of sockets there is no way we + // can easily determine the exact socket type. + if (S_ISBLK(sb.st_mode)) + buf->st_filetype = __WASI_FILETYPE_BLOCK_DEVICE; + else if (S_ISCHR(sb.st_mode)) + buf->st_filetype = __WASI_FILETYPE_CHARACTER_DEVICE; + else if (S_ISDIR(sb.st_mode)) + buf->st_filetype = __WASI_FILETYPE_DIRECTORY; + else if (S_ISFIFO(sb.st_mode)) + buf->st_filetype = __WASI_FILETYPE_SOCKET_STREAM; + else if (S_ISLNK(sb.st_mode)) + buf->st_filetype = __WASI_FILETYPE_SYMBOLIC_LINK; + else if (S_ISREG(sb.st_mode)) + buf->st_filetype = __WASI_FILETYPE_REGULAR_FILE; + else if (S_ISSOCK(sb.st_mode)) + buf->st_filetype = __WASI_FILETYPE_SOCKET_STREAM; + return 0; +} + +__wasi_errno_t wasmtime_ssp_path_filestat_set_times( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_lookupflags_t flags, + const char *path, + size_t pathlen, + __wasi_timestamp_t st_atim, + __wasi_timestamp_t st_mtim, + __wasi_fstflags_t fstflags +) { + if ((fstflags & ~(__WASI_FILESTAT_SET_ATIM | __WASI_FILESTAT_SET_ATIM_NOW | + __WASI_FILESTAT_SET_MTIM | __WASI_FILESTAT_SET_MTIM_NOW)) != 0) + return __WASI_EINVAL; + + struct path_access pa; + __wasi_errno_t error = path_get(curfds, + &pa, fd, flags, path, pathlen, __WASI_RIGHT_PATH_FILESTAT_SET_TIMES, 0, false); + if (error != 0) + return error; + + struct timespec ts[2]; + convert_utimens_arguments(st_atim, st_mtim, fstflags, ts); + int ret = utimensat(pa.fd, pa.path, ts, pa.follow ? 0 : AT_SYMLINK_NOFOLLOW); + + path_put(&pa); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_path_symlink( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + const char *old_path, + size_t old_path_len, + __wasi_fd_t fd, + const char *new_path, + size_t new_path_len +) { + char *target = str_nullterminate(old_path, old_path_len); + if (target == NULL) + return convert_errno(errno); + + struct path_access pa; + __wasi_errno_t error = path_get_nofollow(curfds, + &pa, fd, new_path, new_path_len, __WASI_RIGHT_PATH_SYMLINK, 0, true); + if (error != 0) { + free(target); + return error; + } + + int ret = symlinkat(target, pa.fd, pa.path); + path_put(&pa); + free(target); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_path_unlink_file( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const char *path, + size_t pathlen +) { + struct path_access pa; + __wasi_errno_t error = path_get_nofollow(curfds, + &pa, fd, path, pathlen, __WASI_RIGHT_PATH_UNLINK_FILE, 0, true); + if (error != 0) + return error; + + int ret = unlinkat(pa.fd, pa.path, 0); +#ifndef __linux__ + // Non-Linux implementations may return EPERM when attempting to remove a + // directory without REMOVEDIR. While that's what POSIX specifies, it's + // less useful. Adjust this to EISDIR. It doesn't matter that this is not + // atomic with the unlinkat, because if the file is removed and a directory + // is created before fstatat sees it, we're racing with that change anyway + // and unlinkat could have legitimately seen the directory if the race had + // turned out differently. + if (ret < 0 && errno == EPERM) { + struct stat statbuf; + if (fstatat(pa.fd, pa.path, &statbuf, AT_SYMLINK_NOFOLLOW) == 0 && + S_ISDIR(statbuf.st_mode)) { + errno = EISDIR; + } + } +#endif + path_put(&pa); + if (ret < 0) { + return convert_errno(errno); + } + return 0; +} + +__wasi_errno_t wasmtime_ssp_path_remove_directory( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const char *path, + size_t pathlen +) { + struct path_access pa; + __wasi_errno_t error = path_get_nofollow(curfds, + &pa, fd, path, pathlen, __WASI_RIGHT_PATH_REMOVE_DIRECTORY, 0, true); + if (error != 0) + return error; + + int ret = unlinkat(pa.fd, pa.path, AT_REMOVEDIR); +#ifndef __linux__ + // POSIX permits either EEXIST or ENOTEMPTY when the directory is not empty. + // Map it to ENOTEMPTY. + if (ret < 0 && errno == EEXIST) { + errno = ENOTEMPTY; + } +#endif + path_put(&pa); + if (ret < 0) { + return convert_errno(errno); + } + return 0; +} + +__wasi_errno_t wasmtime_ssp_poll_oneoff( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + const __wasi_subscription_t *in, + __wasi_event_t *out, + size_t nsubscriptions, + size_t *nevents +) NO_LOCK_ANALYSIS { + // Sleeping. + if (nsubscriptions == 1 && in[0].type == __WASI_EVENTTYPE_CLOCK) { + out[0] = (__wasi_event_t){ + .userdata = in[0].userdata, + .type = in[0].type, + }; +#if CONFIG_HAS_CLOCK_NANOSLEEP + clockid_t clock_id; + if (convert_clockid(in[0].u.clock.clock_id, &clock_id)) { + struct timespec ts; + convert_timestamp(in[0].u.clock.timeout, &ts); + int ret = clock_nanosleep( + clock_id, + (in[0].u.clock.flags & __WASI_SUBSCRIPTION_CLOCK_ABSTIME) != 0 + ? TIMER_ABSTIME + : 0, + &ts, NULL); + if (ret != 0) + out[0].error = convert_errno(ret); + } else { + out[0].error = __WASI_ENOTSUP; + } +#else + switch (in[0].u.clock.clock_id) { + case __WASI_CLOCK_MONOTONIC: + if ((in[0].u.clock.flags & __WASI_SUBSCRIPTION_CLOCK_ABSTIME) != 0) { + // TODO(ed): Implement. + fputs("Unimplemented absolute sleep on monotonic clock\n", stderr); + out[0].error = __WASI_ENOSYS; + } else { + // Perform relative sleeps on the monotonic clock also using + // nanosleep(). This is incorrect, but good enough for now. + struct timespec ts; + convert_timestamp(in[0].u.clock.timeout, &ts); + nanosleep(&ts, NULL); + } + break; + case __WASI_CLOCK_REALTIME: + if ((in[0].u.clock.flags & __WASI_SUBSCRIPTION_CLOCK_ABSTIME) != 0) { + // Sleeping to an absolute point in time can only be done + // by waiting on a condition variable. + struct mutex mutex; + mutex_init(&mutex); + struct cond cond; + cond_init_realtime(&cond); + mutex_lock(&mutex); + cond_timedwait(&cond, &mutex, in[0].u.clock.timeout, true); + mutex_unlock(&mutex); + mutex_destroy(&mutex); + cond_destroy(&cond); + } else { + // Relative sleeps can be done using nanosleep(). + struct timespec ts; + convert_timestamp(in[0].u.clock.timeout, &ts); + nanosleep(&ts, NULL); + } + break; + default: + out[0].error = __WASI_ENOTSUP; + break; + } +#endif + *nevents = 1; + return 0; + } + + // Last option: call into poll(). This can only be done in case all + // subscriptions consist of __WASI_EVENTTYPE_FD_READ and + // __WASI_EVENTTYPE_FD_WRITE entries. There may be up to one + // __WASI_EVENTTYPE_CLOCK entry to act as a timeout. These are also + // the subscriptions generate by cloudlibc's poll() and select(). + struct fd_object **fos = malloc(nsubscriptions * sizeof(*fos)); + if (fos == NULL) + return __WASI_ENOMEM; + struct pollfd *pfds = malloc(nsubscriptions * sizeof(*pfds)); + if (pfds == NULL) { + free(fos); + return __WASI_ENOMEM; + } + + // Convert subscriptions to pollfd entries. Increase the reference + // count on the file descriptors to ensure they remain valid across + // the call to poll(). + struct fd_table *ft = curfds; + rwlock_rdlock(&ft->lock); + *nevents = 0; + const __wasi_subscription_t *clock_subscription = NULL; + for (size_t i = 0; i < nsubscriptions; ++i) { + const __wasi_subscription_t *s = &in[i]; + switch (s->type) { + case __WASI_EVENTTYPE_FD_READ: + case __WASI_EVENTTYPE_FD_WRITE: { + __wasi_errno_t error = + fd_object_get_locked(&fos[i], ft, s->u.fd_readwrite.fd, + __WASI_RIGHT_POLL_FD_READWRITE, 0); + if (error == 0) { + // Proper file descriptor on which we can poll(). + pfds[i] = (struct pollfd){ + .fd = fd_number(fos[i]), + .events = s->type == __WASI_EVENTTYPE_FD_READ ? POLLRDNORM + : POLLWRNORM, + }; + } else { + // Invalid file descriptor or rights missing. + fos[i] = NULL; + pfds[i] = (struct pollfd){.fd = -1}; + out[(*nevents)++] = (__wasi_event_t){ + .userdata = s->userdata, + .error = error, + .type = s->type, + }; + } + break; + } + case __WASI_EVENTTYPE_CLOCK: + if (clock_subscription == NULL && + (s->u.clock.flags & __WASI_SUBSCRIPTION_CLOCK_ABSTIME) == 0) { + // Relative timeout. + fos[i] = NULL; + pfds[i] = (struct pollfd){.fd = -1}; + clock_subscription = s; + break; + } + // Fallthrough. + default: + // Unsupported event. + fos[i] = NULL; + pfds[i] = (struct pollfd){.fd = -1}; + out[(*nevents)++] = (__wasi_event_t){ + .userdata = s->userdata, + .error = __WASI_ENOSYS, + .type = s->type, + }; + break; + } + } + rwlock_unlock(&ft->lock); + + // Use a zero-second timeout in case we've already generated events in + // the loop above. + int timeout; + if (*nevents != 0) { + timeout = 0; + } else if (clock_subscription != NULL) { + __wasi_timestamp_t ts = clock_subscription->u.clock.timeout / 1000000; + timeout = ts > INT_MAX ? -1 : ts; + } else { + timeout = -1; + } + int ret = poll(pfds, nsubscriptions, timeout); + + __wasi_errno_t error = 0; + if (ret == -1) { + error = convert_errno(errno); + } else if (ret == 0 && *nevents == 0 && clock_subscription != NULL) { + // No events triggered. Trigger the clock event. + out[(*nevents)++] = (__wasi_event_t){ + .userdata = clock_subscription->userdata, + .type = __WASI_EVENTTYPE_CLOCK, + }; + } else { + // Events got triggered. Don't trigger the clock event. + for (size_t i = 0; i < nsubscriptions; ++i) { + if (pfds[i].fd >= 0) { + __wasi_filesize_t nbytes = 0; + if (in[i].type == __WASI_EVENTTYPE_FD_READ) { + int l; + if (ioctl(fd_number(fos[i]), FIONREAD, &l) == 0) + nbytes = l; + } + if ((pfds[i].revents & POLLNVAL) != 0) { + // Bad file descriptor. This normally cannot occur, as + // referencing the file descriptor object will always ensure + // the descriptor is valid. Still, macOS may sometimes return + // this on FIFOs when reaching end-of-file. + out[(*nevents)++] = (__wasi_event_t){ + .userdata = in[i].userdata, +#ifdef __APPLE__ + .u.fd_readwrite.nbytes = nbytes, + .u.fd_readwrite.flags = __WASI_EVENT_FD_READWRITE_HANGUP, +#else + .error = __WASI_EBADF, +#endif + .type = in[i].type, + }; + } else if ((pfds[i].revents & POLLERR) != 0) { + // File descriptor is in an error state. + out[(*nevents)++] = (__wasi_event_t){ + .userdata = in[i].userdata, + .error = __WASI_EIO, + .type = in[i].type, + }; + } else if ((pfds[i].revents & POLLHUP) != 0) { + // End-of-file. + out[(*nevents)++] = (__wasi_event_t){ + .userdata = in[i].userdata, + .type = in[i].type, + .u.fd_readwrite.nbytes = nbytes, + .u.fd_readwrite.flags = __WASI_EVENT_FD_READWRITE_HANGUP, + }; + } else if ((pfds[i].revents & (POLLRDNORM | POLLWRNORM)) != 0) { + // Read or write possible. + out[(*nevents)++] = (__wasi_event_t){ + .userdata = in[i].userdata, + .type = in[i].type, + .u.fd_readwrite.nbytes = nbytes, + }; + } + } + } + } + + for (size_t i = 0; i < nsubscriptions; ++i) + if (fos[i] != NULL) + fd_object_release(fos[i]); + free(fos); + free(pfds); + return error; +} + +void wasmtime_ssp_proc_exit( + __wasi_exitcode_t rval +) { + _Exit(rval); +} + +__wasi_errno_t wasmtime_ssp_proc_raise( + __wasi_signal_t sig +) { + static const int signals[] = { +#define X(v) [__WASI_##v] = v + X(SIGABRT), X(SIGALRM), X(SIGBUS), X(SIGCHLD), X(SIGCONT), X(SIGFPE), + X(SIGHUP), X(SIGILL), X(SIGINT), X(SIGKILL), X(SIGPIPE), X(SIGQUIT), + X(SIGSEGV), X(SIGSTOP), X(SIGSYS), X(SIGTERM), X(SIGTRAP), X(SIGTSTP), + X(SIGTTIN), X(SIGTTOU), X(SIGURG), X(SIGUSR1), X(SIGUSR2), X(SIGVTALRM), + X(SIGXCPU), X(SIGXFSZ), +#undef X + }; + if (sig >= sizeof(signals) / sizeof(signals[0]) || signals[sig] == 0) + return __WASI_EINVAL; + +#if CONFIG_TLS_USE_GSBASE + // TLS on OS X depends on installing a SIGSEGV handler. Reset SIGSEGV + // to the default action before raising. + if (sig == __WASI_SIGSEGV) { + struct sigaction sa = { + .sa_handler = SIG_DFL, + }; + sigemptyset(&sa.sa_mask); + sigaction(SIGSEGV, &sa, NULL); + } +#endif + + if (raise(signals[sig]) < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_random_get( + void *buf, + size_t nbyte +) { + random_buf(buf, nbyte); + return 0; +} + +__wasi_errno_t wasmtime_ssp_sock_recv( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t sock, + const __wasi_iovec_t *ri_data, + size_t ri_data_len, + __wasi_riflags_t ri_flags, + size_t *ro_datalen, + __wasi_roflags_t *ro_flags +) { + // Convert input to msghdr. + struct msghdr hdr = { + .msg_iov = (struct iovec *)ri_data, + .msg_iovlen = ri_data_len, + }; + int nflags = 0; + if ((ri_flags & __WASI_SOCK_RECV_PEEK) != 0) + nflags |= MSG_PEEK; + if ((ri_flags & __WASI_SOCK_RECV_WAITALL) != 0) + nflags |= MSG_WAITALL; + + struct fd_object *fo; + __wasi_errno_t error = fd_object_get(curfds, &fo, sock, __WASI_RIGHT_FD_READ, 0); + if (error != 0) { + return error; + } + + ssize_t datalen = recvmsg(fd_number(fo), &hdr, nflags); + fd_object_release(fo); + if (datalen < 0) { + return convert_errno(errno); + } + + + // Convert msghdr to output. + *ro_datalen = datalen; + *ro_flags = 0; + if ((hdr.msg_flags & MSG_TRUNC) != 0) + *ro_flags |= __WASI_SOCK_RECV_DATA_TRUNCATED; + return 0; +} + +__wasi_errno_t wasmtime_ssp_sock_send( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t sock, + const __wasi_ciovec_t *si_data, + size_t si_data_len, + __wasi_siflags_t si_flags, + size_t *so_datalen +) NO_LOCK_ANALYSIS { + // Convert input to msghdr. + struct msghdr hdr = { + .msg_iov = (struct iovec *)si_data, + .msg_iovlen = si_data_len, + }; + + // Attach file descriptors if present. + __wasi_errno_t error; + + // Send message. + struct fd_object *fo; + error = fd_object_get(curfds, &fo, sock, __WASI_RIGHT_FD_WRITE, 0); + if (error != 0) + goto out; + ssize_t len = sendmsg(fd_number(fo), &hdr, 0); + fd_object_release(fo); + if (len < 0) { + error = convert_errno(errno); + } else { + *so_datalen = len; + } + +out: + return error; +} + +__wasi_errno_t wasmtime_ssp_sock_shutdown( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t sock, + __wasi_sdflags_t how +) { + int nhow; + switch (how) { + case __WASI_SHUT_RD: + nhow = SHUT_RD; + break; + case __WASI_SHUT_WR: + nhow = SHUT_WR; + break; + case __WASI_SHUT_RD | __WASI_SHUT_WR: + nhow = SHUT_RDWR; + break; + default: + return __WASI_EINVAL; + } + + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, sock, __WASI_RIGHT_SOCK_SHUTDOWN, 0); + if (error != 0) + return error; + + int ret = shutdown(fd_number(fo), nhow); + fd_object_release(fo); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_sched_yield(void) { + if (sched_yield() < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_args_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct argv_environ_values *argv_environ, +#endif + char **argv, + char *argv_buf +) { + for (size_t i = 0; i < argv_environ->argc; ++i) { + argv[i] = argv_buf + (argv_environ->argv[i] - argv_environ->argv_buf); + } + argv[argv_environ->argc] = NULL; + memcpy(argv_buf, argv_environ->argv_buf, argv_environ->argv_buf_size); + return __WASI_ESUCCESS; +} + +__wasi_errno_t wasmtime_ssp_args_sizes_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct argv_environ_values *argv_environ, +#endif + size_t *argc, + size_t *argv_buf_size +) { + *argc = argv_environ->argc; + *argv_buf_size = argv_environ->argv_buf_size; + return __WASI_ESUCCESS; +} + +__wasi_errno_t wasmtime_ssp_environ_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct argv_environ_values *argv_environ, +#endif + char **environ, + char *environ_buf +) { + for (size_t i = 0; i < argv_environ->environ_count; ++i) { + environ[i] = environ_buf + (argv_environ->environ[i] - argv_environ->environ_buf); + } + environ[argv_environ->environ_count] = NULL; + memcpy(environ_buf, argv_environ->environ_buf, argv_environ->environ_buf_size); + return __WASI_ESUCCESS; +} + +__wasi_errno_t wasmtime_ssp_environ_sizes_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct argv_environ_values *argv_environ, +#endif + size_t *environ_count, + size_t *environ_buf_size +) { + *environ_count = argv_environ->environ_count; + *environ_buf_size = argv_environ->environ_buf_size; + return __WASI_ESUCCESS; +} + +void argv_environ_init(struct argv_environ_values *argv_environ, + const size_t *argv_offsets, size_t argv_offsets_len, + const char *argv_buf, size_t argv_buf_len, + const size_t *environ_offsets, size_t environ_offsets_len, + const char *environ_buf, size_t environ_buf_len) +{ + argv_environ->argc = argv_offsets_len; + argv_environ->argv_buf_size = argv_buf_len; + argv_environ->argv = malloc(argv_offsets_len * sizeof(char *)); + argv_environ->argv_buf = malloc(argv_buf_len); + if (argv_environ->argv == NULL || argv_environ->argv_buf == NULL) { + abort(); + } + for (size_t i = 0; i < argv_offsets_len; ++i) { + argv_environ->argv[i] = argv_environ->argv_buf + argv_offsets[i]; + } + memcpy(argv_environ->argv_buf, argv_buf, argv_buf_len); + + argv_environ->environ_count = environ_offsets_len; + argv_environ->environ_buf_size = environ_buf_len; + argv_environ->environ = malloc(environ_offsets_len * sizeof(char *)); + argv_environ->environ_buf = malloc(environ_buf_len); + if (argv_environ->environ == NULL || argv_environ->environ_buf == NULL) { + abort(); + } + for (size_t i = 0; i < environ_offsets_len; ++i) { + argv_environ->environ[i] = argv_environ->environ_buf + environ_offsets[i]; + } + memcpy(argv_environ->environ_buf, environ_buf, environ_buf_len); +} diff --git a/wasmtime-wasi/sandboxed-system-primitives/src/posix.h b/wasmtime-wasi/sandboxed-system-primitives/src/posix.h new file mode 100644 index 0000000000..6711c9b2e3 --- /dev/null +++ b/wasmtime-wasi/sandboxed-system-primitives/src/posix.h @@ -0,0 +1,59 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016-2018 Nuxi, https://nuxi.nl/ + +#ifndef POSIX_H +#define POSIX_H + +#include +#include + +#include "locking.h" + +struct fd_entry; +struct fd_prestat; +struct syscalls; + +struct fd_table { + struct rwlock lock; + struct fd_entry *entries; + size_t size; + size_t used; +}; + +struct fd_prestats { + struct rwlock lock; + struct fd_prestat *prestats; + size_t size; + size_t used; +}; + +struct argv_environ_values { + size_t argc; + size_t argv_buf_size; + char **argv; + char *argv_buf; + size_t environ_count; + size_t environ_buf_size; + char **environ; + char *environ_buf; +}; + +void fd_table_init(struct fd_table *); +bool fd_table_insert_existing(struct fd_table *, __wasi_fd_t, int); +void fd_prestats_init(struct fd_prestats *); +bool fd_prestats_insert(struct fd_prestats *, const char *, __wasi_fd_t); +void argv_environ_init(struct argv_environ_values *, + const size_t *argv_offsets, size_t argv_offsets_len, + const char *argv_buf, size_t argv_buf_len, + const size_t *environ_offsets, size_t environ_offsets_len, + const char *environ_buf, size_t environ_buf_len); + +#endif diff --git a/wasmtime-wasi/sandboxed-system-primitives/src/queue.h b/wasmtime-wasi/sandboxed-system-primitives/src/queue.h new file mode 100644 index 0000000000..c46190a853 --- /dev/null +++ b/wasmtime-wasi/sandboxed-system-primitives/src/queue.h @@ -0,0 +1,92 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016 Nuxi, https://nuxi.nl/ + +#ifndef QUEUE_H +#define QUEUE_H + +#include + +// LIST: Double-linked list. + +#define LIST_HEAD(name, type) \ + struct name { \ + struct type *l_first; \ + } +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ + struct { \ + struct type *l_next; \ + struct type **l_prev; \ + } + +#define LIST_FOREACH(var, head, field) \ + for ((var) = (head)->l_first; (var) != NULL; (var) = (var)->field.l_next) +#define LIST_INIT(head) \ + do { \ + (head)->l_first = NULL; \ + } while (0) +#define LIST_INSERT_HEAD(head, element, field) \ + do { \ + (element)->field.l_next = (head)->l_first; \ + if ((head)->l_first != NULL) \ + (head)->l_first->field.l_prev = &(element)->field.l_next; \ + (head)->l_first = (element); \ + (element)->field.l_prev = &(head)->l_first; \ + } while (0) +#define LIST_REMOVE(element, field) \ + do { \ + if ((element)->field.l_next != NULL) \ + (element)->field.l_next->field.l_prev = (element)->field.l_prev; \ + *(element)->field.l_prev = (element)->field.l_next; \ + } while (0) + +// TAILQ: Double-linked list with tail pointer. + +#define TAILQ_HEAD(name, type) \ + struct name { \ + struct type *t_first; \ + struct type **t_last; \ + } + +#define TAILQ_ENTRY(type) \ + struct { \ + struct type *t_next; \ + struct type **t_prev; \ + } + +#define TAILQ_EMPTY(head) ((head)->t_first == NULL) +#define TAILQ_FIRST(head) ((head)->t_first) +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = (head)->t_first; (var) != NULL; (var) = (var)->field.t_next) +#define TAILQ_INIT(head) \ + do { \ + (head)->t_first = NULL; \ + (head)->t_last = &(head)->t_first; \ + } while (0) +#define TAILQ_INSERT_TAIL(head, elm, field) \ + do { \ + (elm)->field.t_next = NULL; \ + (elm)->field.t_prev = (head)->t_last; \ + *(head)->t_last = (elm); \ + (head)->t_last = &(elm)->field.t_next; \ + } while (0) +#define TAILQ_REMOVE(head, element, field) \ + do { \ + if ((element)->field.t_next != NULL) \ + (element)->field.t_next->field.t_prev = (element)->field.t_prev; \ + else \ + (head)->t_last = (element)->field.t_prev; \ + *(element)->field.t_prev = (element)->field.t_next; \ + } while (0) + +#endif diff --git a/wasmtime-wasi/sandboxed-system-primitives/src/random.c b/wasmtime-wasi/sandboxed-system-primitives/src/random.c new file mode 100644 index 0000000000..85c3acf700 --- /dev/null +++ b/wasmtime-wasi/sandboxed-system-primitives/src/random.c @@ -0,0 +1,76 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016 Nuxi, https://nuxi.nl/ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "random.h" + +#if CONFIG_HAS_ARC4RANDOM_BUF + +void random_buf(void *buf, size_t len) { + arc4random_buf(buf, len); +} + +#elif CONFIG_HAS_GETENTROPY + +void random_buf(void *buf, size_t len) { + getentropy(buf, len); +} + +#else + +static int urandom; + +static void open_urandom(void) { + urandom = open("/dev/urandom", O_RDONLY); + if (urandom < 0) { + fputs("Failed to open /dev/urandom\n", stderr); + abort(); + } +} + +void random_buf(void *buf, size_t len) { + static pthread_once_t open_once = PTHREAD_ONCE_INIT; + pthread_once(&open_once, open_urandom); + + if (read(urandom, buf, len) != len) { + fputs("Short read on /dev/urandom\n", stderr); + abort(); + } +} + +#endif + +// Calculates a random number within the range [0, upper - 1] without +// any modulo bias. +// +// The function below repeatedly obtains a random number from +// arc4random() until it lies within the range [2^k % upper, 2^k). As +// this range has length k * upper, we can safely obtain a number +// without any modulo bias. +uintmax_t random_uniform(uintmax_t upper) { + // Compute 2^k % upper + // == (2^k - upper) % upper + // == -upper % upper. + uintmax_t lower = -upper % upper; + for (;;) { + uintmax_t value; + random_buf(&value, sizeof(value)); + if (value >= lower) + return value % upper; + } +} diff --git a/wasmtime-wasi/sandboxed-system-primitives/src/random.h b/wasmtime-wasi/sandboxed-system-primitives/src/random.h new file mode 100644 index 0000000000..d87a292cf0 --- /dev/null +++ b/wasmtime-wasi/sandboxed-system-primitives/src/random.h @@ -0,0 +1,20 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016 Nuxi, https://nuxi.nl/ + +#ifndef RANDOM_H +#define RANDOM_H + +#include + +void random_buf(void *, size_t); +uintmax_t random_uniform(uintmax_t); + +#endif diff --git a/wasmtime-wasi/sandboxed-system-primitives/src/refcount.h b/wasmtime-wasi/sandboxed-system-primitives/src/refcount.h new file mode 100644 index 0000000000..51752bc9b1 --- /dev/null +++ b/wasmtime-wasi/sandboxed-system-primitives/src/refcount.h @@ -0,0 +1,47 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016 Nuxi, https://nuxi.nl/ + +#ifndef REFCOUNT_H +#define REFCOUNT_H + +#include +#include +#include + +#include "locking.h" + +// Simple reference counter. +struct LOCKABLE refcount { + atomic_uint count; +}; + +#define PRODUCES(...) LOCKS_SHARED(__VA_ARGS__) NO_LOCK_ANALYSIS +#define CONSUMES(...) UNLOCKS(__VA_ARGS__) NO_LOCK_ANALYSIS + +// Initialize the reference counter. +static void refcount_init(struct refcount *r, unsigned int count) PRODUCES(*r) { + atomic_init(&r->count, count); +} + +// Increment the reference counter. +static inline void refcount_acquire(struct refcount *r) PRODUCES(*r) { + atomic_fetch_add_explicit(&r->count, 1, memory_order_acquire); +} + +// Decrement the reference counter, returning whether the reference +// dropped to zero. +static inline bool refcount_release(struct refcount *r) CONSUMES(*r) { + int old = atomic_fetch_sub_explicit(&r->count, 1, memory_order_release); + assert(old != 0 && "Reference count becoming negative"); + return old == 1; +} + +#endif diff --git a/wasmtime-wasi/sandboxed-system-primitives/src/rights.h b/wasmtime-wasi/sandboxed-system-primitives/src/rights.h new file mode 100644 index 0000000000..04dbc80c17 --- /dev/null +++ b/wasmtime-wasi/sandboxed-system-primitives/src/rights.h @@ -0,0 +1,82 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016 Nuxi, https://nuxi.nl/ + +#ifndef RIGHTS_H +#define RIGHTS_H + +#define RIGHTS_ALL \ + (__WASI_RIGHT_FD_DATASYNC | __WASI_RIGHT_FD_READ | \ + __WASI_RIGHT_FD_SEEK | __WASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + __WASI_RIGHT_FD_SYNC | __WASI_RIGHT_FD_TELL | __WASI_RIGHT_FD_WRITE | \ + __WASI_RIGHT_FD_ADVISE | __WASI_RIGHT_FD_ALLOCATE | \ + __WASI_RIGHT_PATH_CREATE_DIRECTORY | __WASI_RIGHT_PATH_CREATE_FILE | \ + __WASI_RIGHT_PATH_LINK_SOURCE | __WASI_RIGHT_PATH_LINK_TARGET | \ + __WASI_RIGHT_PATH_OPEN | __WASI_RIGHT_FD_READDIR | \ + __WASI_RIGHT_PATH_READLINK | __WASI_RIGHT_PATH_RENAME_SOURCE | \ + __WASI_RIGHT_PATH_RENAME_TARGET | __WASI_RIGHT_PATH_FILESTAT_GET | \ + __WASI_RIGHT_PATH_FILESTAT_SET_SIZE | \ + __WASI_RIGHT_PATH_FILESTAT_SET_TIMES | \ + __WASI_RIGHT_FD_FILESTAT_GET | __WASI_RIGHT_FD_FILESTAT_SET_TIMES | \ + __WASI_RIGHT_FD_FILESTAT_SET_SIZE | \ + __WASI_RIGHT_PATH_SYMLINK | __WASI_RIGHT_PATH_UNLINK_FILE | \ + __WASI_RIGHT_PATH_REMOVE_DIRECTORY | \ + __WASI_RIGHT_POLL_FD_READWRITE | __WASI_RIGHT_SOCK_SHUTDOWN) + +// Block and character device interaction is outside the scope of +// CloudABI. Simply allow everything. +#define RIGHTS_BLOCK_DEVICE_BASE RIGHTS_ALL +#define RIGHTS_BLOCK_DEVICE_INHERITING RIGHTS_ALL +#define RIGHTS_CHARACTER_DEVICE_BASE RIGHTS_ALL +#define RIGHTS_CHARACTER_DEVICE_INHERITING RIGHTS_ALL + +// Only allow directory operations on directories. Directories can only +// yield file descriptors to other directories and files. +#define RIGHTS_DIRECTORY_BASE \ + (__WASI_RIGHT_FD_FDSTAT_SET_FLAGS | __WASI_RIGHT_FD_SYNC | \ + __WASI_RIGHT_FD_ADVISE | __WASI_RIGHT_PATH_CREATE_DIRECTORY | \ + __WASI_RIGHT_PATH_CREATE_FILE | __WASI_RIGHT_PATH_LINK_SOURCE | \ + __WASI_RIGHT_PATH_LINK_TARGET | __WASI_RIGHT_PATH_OPEN | \ + __WASI_RIGHT_FD_READDIR | __WASI_RIGHT_PATH_READLINK | \ + __WASI_RIGHT_PATH_RENAME_SOURCE | __WASI_RIGHT_PATH_RENAME_TARGET | \ + __WASI_RIGHT_PATH_FILESTAT_GET | \ + __WASI_RIGHT_PATH_FILESTAT_SET_TIMES | \ + __WASI_RIGHT_FD_FILESTAT_GET | __WASI_RIGHT_FD_FILESTAT_SET_TIMES | \ + __WASI_RIGHT_PATH_SYMLINK | __WASI_RIGHT_PATH_UNLINK_FILE | \ + __WASI_RIGHT_PATH_REMOVE_DIRECTORY | \ + __WASI_RIGHT_POLL_FD_READWRITE) +#define RIGHTS_DIRECTORY_INHERITING \ + (RIGHTS_DIRECTORY_BASE | RIGHTS_REGULAR_FILE_BASE) + +// Operations that apply to regular files. +#define RIGHTS_REGULAR_FILE_BASE \ + (__WASI_RIGHT_FD_DATASYNC | __WASI_RIGHT_FD_READ | \ + __WASI_RIGHT_FD_SEEK | __WASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + __WASI_RIGHT_FD_SYNC | __WASI_RIGHT_FD_TELL | __WASI_RIGHT_FD_WRITE | \ + __WASI_RIGHT_FD_ADVISE | __WASI_RIGHT_FD_ALLOCATE | \ + __WASI_RIGHT_FD_FILESTAT_GET | __WASI_RIGHT_FD_FILESTAT_SET_SIZE | \ + __WASI_RIGHT_FD_FILESTAT_SET_TIMES | __WASI_RIGHT_POLL_FD_READWRITE) +#define RIGHTS_REGULAR_FILE_INHERITING 0 + +// Operations that apply to sockets and socket pairs. +#define RIGHTS_SOCKET_BASE \ + (__WASI_RIGHT_FD_READ | __WASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + __WASI_RIGHT_FD_WRITE | __WASI_RIGHT_FD_FILESTAT_GET | \ + __WASI_RIGHT_POLL_FD_READWRITE | __WASI_RIGHT_SOCK_SHUTDOWN) +#define RIGHTS_SOCKET_INHERITING RIGHTS_ALL + +// Operations that apply to TTYs. +#define RIGHTS_TTY_BASE \ + (__WASI_RIGHT_FD_READ | __WASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + __WASI_RIGHT_FD_WRITE | __WASI_RIGHT_FD_FILESTAT_GET | \ + __WASI_RIGHT_POLL_FD_READWRITE) +#define RIGHTS_TTY_INHERITING 0 + +#endif diff --git a/wasmtime-wasi/sandboxed-system-primitives/src/signals.h b/wasmtime-wasi/sandboxed-system-primitives/src/signals.h new file mode 100644 index 0000000000..16f5d575ca --- /dev/null +++ b/wasmtime-wasi/sandboxed-system-primitives/src/signals.h @@ -0,0 +1,17 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016 Nuxi, https://nuxi.nl/ + +#ifndef SIGNALS_H +#define SIGNALS_H + +void signals_init(void); + +#endif diff --git a/wasmtime-wasi/sandboxed-system-primitives/src/str.c b/wasmtime-wasi/sandboxed-system-primitives/src/str.c new file mode 100644 index 0000000000..02a9518f47 --- /dev/null +++ b/wasmtime-wasi/sandboxed-system-primitives/src/str.c @@ -0,0 +1,33 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016 Nuxi, https://nuxi.nl/ + +#include "config.h" + +#include +#include +#include + +#include "str.h" + +char *str_nullterminate(const char *s, size_t len) { + // Copy string. + char *ret = strndup(s, len); + if (ret == NULL) + return NULL; + + // Ensure that it contains no null bytes within. + if (strlen(ret) != len) { + free(ret); + errno = EILSEQ; + return NULL; + } + return ret; +} diff --git a/wasmtime-wasi/sandboxed-system-primitives/src/str.h b/wasmtime-wasi/sandboxed-system-primitives/src/str.h new file mode 100644 index 0000000000..a9c5f226fa --- /dev/null +++ b/wasmtime-wasi/sandboxed-system-primitives/src/str.h @@ -0,0 +1,19 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016 Nuxi, https://nuxi.nl/ + +#ifndef STR_H +#define STR_H + +#include "config.h" + +char *str_nullterminate(const char *, size_t); + +#endif diff --git a/wasmtime-wasi/src/host.rs b/wasmtime-wasi/src/host.rs new file mode 100644 index 0000000000..918ea92f5e --- /dev/null +++ b/wasmtime-wasi/src/host.rs @@ -0,0 +1,6 @@ +#![allow(non_camel_case_types, dead_code)] + +include!(concat!(env!("OUT_DIR"), "/wasmtime_ssp.rs")); + +pub type char = ::std::os::raw::c_char; +pub type void = ::std::os::raw::c_void; diff --git a/wasmtime-wasi/src/instantiate.rs b/wasmtime-wasi/src/instantiate.rs new file mode 100644 index 0000000000..9a55078476 --- /dev/null +++ b/wasmtime-wasi/src/instantiate.rs @@ -0,0 +1,203 @@ +use crate::host::{ + argv_environ_init, argv_environ_values, fd_prestats, fd_prestats_init, fd_prestats_insert, + fd_table, fd_table_init, fd_table_insert_existing, +}; +use cranelift_codegen::ir::types; +use cranelift_codegen::{ir, isa}; +use cranelift_entity::PrimaryMap; +use cranelift_wasm::DefinedFuncIndex; +use std::cell::RefCell; +use std::collections::HashMap; +use std::ffi::CString; +use std::mem; +use std::rc::Rc; +use syscalls; +use target_lexicon::HOST; +use wasmtime_environ::{translate_signature, Export, Module}; +use wasmtime_runtime::{Imports, InstanceHandle, InstantiationError, VMFunctionBody}; + +pub(crate) struct WASIState { + pub curfds: Box, + pub prestats: Box, + pub argv_environ: Box, +} + +/// Return an instance implementing the "wasi" interface. +pub fn instantiate_wasi( + prefix: &str, + global_exports: Rc>>>, + preopened_dirs: &[(String, libc::c_int)], + argv: &[String], + environ: &[(String, String)], +) -> Result { + let pointer_type = types::Type::triple_pointer_type(&HOST); + let mut module = Module::new(); + let mut finished_functions: PrimaryMap = + PrimaryMap::new(); + let call_conv = isa::CallConv::triple_default(&HOST); + + macro_rules! signature { + ($name:ident) => {{ + let sig = module.signatures.push(translate_signature( + ir::Signature { + params: syscalls::$name::params() + .into_iter() + .map(ir::AbiParam::new) + .collect(), + returns: syscalls::$name::results() + .into_iter() + .map(ir::AbiParam::new) + .collect(), + call_conv, + }, + pointer_type, + )); + let func = module.functions.push(sig); + module.exports.insert( + prefix.to_owned() + stringify!($name), + Export::Function(func), + ); + finished_functions.push(syscalls::$name::SHIM as *const VMFunctionBody); + }}; + } + + signature!(args_get); + signature!(args_sizes_get); + signature!(clock_res_get); + signature!(clock_time_get); + signature!(environ_get); + signature!(environ_sizes_get); + signature!(fd_prestat_get); + signature!(fd_prestat_dir_name); + signature!(fd_close); + signature!(fd_datasync); + signature!(fd_pread); + signature!(fd_pwrite); + signature!(fd_read); + signature!(fd_renumber); + signature!(fd_seek); + signature!(fd_tell); + signature!(fd_fdstat_get); + signature!(fd_fdstat_set_flags); + signature!(fd_fdstat_set_rights); + signature!(fd_sync); + signature!(fd_write); + signature!(fd_advise); + signature!(fd_allocate); + signature!(path_create_directory); + signature!(path_link); + signature!(path_open); + signature!(fd_readdir); + signature!(path_readlink); + signature!(path_rename); + signature!(fd_filestat_get); + signature!(fd_filestat_set_times); + signature!(fd_filestat_set_size); + signature!(path_filestat_get); + signature!(path_filestat_set_times); + signature!(path_symlink); + signature!(path_unlink_file); + signature!(path_remove_directory); + signature!(poll_oneoff); + signature!(proc_exit); + signature!(proc_raise); + signature!(random_get); + signature!(sched_yield); + signature!(sock_recv); + signature!(sock_send); + signature!(sock_shutdown); + + let imports = Imports::none(); + let data_initializers = Vec::new(); + let signatures = PrimaryMap::new(); + let mut curfds = Box::new(unsafe { mem::zeroed::() }); + let mut prestats = Box::new(unsafe { mem::zeroed::() }); + let mut argv_environ = Box::new(unsafe { mem::zeroed::() }); + + unsafe { + let argv_environ: &mut argv_environ_values = &mut *argv_environ; + let (argv_offsets, argv_buf, environ_offsets, environ_buf) = + allocate_argv_environ(argv, environ); + argv_environ_init( + argv_environ, + argv_offsets.as_ptr(), + argv_offsets.len(), + argv_buf.as_ptr(), + argv_buf.len(), + environ_offsets.as_ptr(), + environ_offsets.len(), + environ_buf.as_ptr(), + environ_buf.len(), + ); + + let curfds: *mut fd_table = &mut *curfds; + fd_table_init(curfds); + + let prestats: *mut fd_prestats = &mut *prestats; + fd_prestats_init(prestats); + + // Prepopulate curfds with stdin, stdout, and stderr file descriptors. + assert!(fd_table_insert_existing(curfds, 0, 0)); + assert!(fd_table_insert_existing(curfds, 1, 1)); + assert!(fd_table_insert_existing(curfds, 2, 2)); + + let mut wasm_fd = 3; + for (dir, fd) in preopened_dirs { + assert!(fd_table_insert_existing(curfds, wasm_fd, *fd)); + assert!(fd_prestats_insert( + prestats, + CString::new(dir.as_str()).unwrap().as_ptr(), + wasm_fd, + )); + wasm_fd += 1; + } + } + + let host_state = WASIState { + curfds, + prestats, + argv_environ, + }; + + InstanceHandle::new( + Rc::new(module), + global_exports, + finished_functions.into_boxed_slice(), + imports, + &data_initializers, + signatures.into_boxed_slice(), + None, + Box::new(host_state), + ) +} + +fn allocate_argv_environ( + argv: &[String], + environ: &[(String, String)], +) -> (Vec, Vec, Vec, Vec) { + let mut argv_offsets = Vec::new(); + let mut argv_buf = Vec::new(); + let mut environ_offsets = Vec::new(); + let mut environ_buf = Vec::new(); + + for arg in argv { + argv_offsets.push(argv_buf.len()); + for c in arg.bytes() { + argv_buf.push(c as libc::c_char); + } + argv_buf.push('\0' as libc::c_char); + } + for (key, value) in environ { + environ_offsets.push(environ_buf.len()); + for c in key.bytes() { + environ_buf.push(c as libc::c_char); + } + environ_buf.push('=' as libc::c_char); + for c in value.bytes() { + environ_buf.push(c as libc::c_char); + } + environ_buf.push('\0' as libc::c_char); + } + + (argv_offsets, argv_buf, environ_offsets, environ_buf) +} diff --git a/wasmtime-wasi/src/lib.rs b/wasmtime-wasi/src/lib.rs new file mode 100644 index 0000000000..0fd0ae5101 --- /dev/null +++ b/wasmtime-wasi/src/lib.rs @@ -0,0 +1,18 @@ +extern crate cast; +extern crate cranelift_codegen; +extern crate cranelift_entity; +extern crate cranelift_wasm; +extern crate target_lexicon; +extern crate wasmtime_environ; +extern crate wasmtime_jit; +extern crate wasmtime_runtime; +#[macro_use] +extern crate log; + +mod host; +mod instantiate; +mod syscalls; +mod translate; +mod wasm32; + +pub use instantiate::instantiate_wasi; diff --git a/wasmtime-wasi/src/syscalls.rs b/wasmtime-wasi/src/syscalls.rs new file mode 100644 index 0000000000..9b3ae4d84e --- /dev/null +++ b/wasmtime-wasi/src/syscalls.rs @@ -0,0 +1,1521 @@ +use crate::host::{argv_environ_values, fd_prestats, fd_table}; +use crate::instantiate::WASIState; +use cranelift_codegen::ir::types::{Type, I32, I64}; +use host; +use std::{ptr, slice, str}; +use translate::*; +use wasm32; +use wasmtime_runtime::VMContext; + +fn str_for_trace<'str>(ptr: *const i8, len: usize) -> Result<&'str str, str::Utf8Error> { + str::from_utf8(unsafe { slice::from_raw_parts(ptr as *const u8, len) }) +} + +fn return_encoded_errno(e: host::__wasi_errno_t) -> wasm32::__wasi_errno_t { + let errno = encode_errno(e); + trace!(" -> errno={}", wasm32::strerror(errno)); + errno +} + +unsafe fn get_curfds(vmctx: *mut VMContext) -> *mut fd_table { + (&mut *(&mut *vmctx) + .host_state() + .downcast_mut::() + .unwrap() + .curfds) as *mut fd_table +} + +unsafe fn get_prestats(vmctx: *mut VMContext) -> *mut fd_prestats { + (&mut *(&mut *vmctx) + .host_state() + .downcast_mut::() + .unwrap() + .prestats) as *mut fd_prestats +} + +unsafe fn get_argv_environ(vmctx: *mut VMContext) -> *mut argv_environ_values { + (&mut *(&mut *vmctx) + .host_state() + .downcast_mut::() + .unwrap() + .argv_environ) as *mut argv_environ_values +} + +pub trait AbiRet { + type Abi; + fn convert(self) -> Self::Abi; + fn codegen_tys() -> Vec; +} + +pub trait AbiParam { + type Abi; + fn convert(arg: Self::Abi) -> Self; + fn codegen_ty() -> Type; +} + +macro_rules! cast32 { + ($($i:ident)*) => ($( + impl AbiRet for $i { + type Abi = i32; + + fn convert(self) -> Self::Abi { + self as i32 + } + + fn codegen_tys() -> Vec { vec![I32] } + } + + impl AbiParam for $i { + type Abi = i32; + + fn convert(param: i32) -> Self { + param as $i + } + + fn codegen_ty() -> Type { I32 } + } + )*) +} + +macro_rules! cast64 { + ($($i:ident)*) => ($( + impl AbiRet for $i { + type Abi = i64; + + fn convert(self) -> Self::Abi { + self as i64 + } + + fn codegen_tys() -> Vec { vec![I64] } + } + + impl AbiParam for $i { + type Abi = i64; + + fn convert(param: i64) -> Self { + param as $i + } + + fn codegen_ty() -> Type { I64 } + } + )*) +} + +cast32!(i8 i16 i32 u8 u16 u32); +cast64!(i64 u64); + +impl AbiRet for () { + type Abi = (); + fn convert(self) {} + fn codegen_tys() -> Vec { + Vec::new() + } +} + +macro_rules! syscalls { + ($(pub unsafe extern "C" fn $name:ident($ctx:ident: *mut VMContext $(, $arg:ident: $ty:ty)*,) -> $ret:ty { + $($body:tt)* + })*) => ($( + pub mod $name { + use super::*; + + /// Returns the codegen types of all the parameters to the shim + /// generated + pub fn params() -> Vec { + vec![$(<$ty as AbiParam>::codegen_ty()),*] + } + + /// Returns the codegen types of all the results of the shim + /// generated + pub fn results() -> Vec { + <$ret as AbiRet>::codegen_tys() + } + + /// The actual function pointer to the shim for a syscall. + /// + /// NB: ideally we'd expose `shim` below, but it seems like there's + /// a compiler bug which prvents that from being cast to a `usize`. + pub static SHIM: unsafe extern "C" fn( + *mut VMContext, + $(<$ty as AbiParam>::Abi),* + ) -> <$ret as AbiRet>::Abi = shim; + + unsafe extern "C" fn shim( + $ctx: *mut VMContext, + $($arg: <$ty as AbiParam>::Abi,)* + ) -> <$ret as AbiRet>::Abi { + let r = super::$name($ctx, $(<$ty as AbiParam>::convert($arg),)*); + <$ret as AbiRet>::convert(r) + } + } + + pub unsafe extern "C" fn $name($ctx: *mut VMContext, $($arg: $ty,)*) -> $ret { + $($body)* + } + )*) +} + +syscalls! { + + pub unsafe extern "C" fn args_get( + vmctx: *mut VMContext, + argv: wasm32::uintptr_t, + argv_buf: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "args_get(argv={:#x?}, argv_buf={:#x?})", + argv, + argv_buf, + ); + + let vmctx = &mut *vmctx; + let argv_environ = get_argv_environ(vmctx); + let argc = match cast::u32((*argv_environ).argc) { + Ok(argc) => argc, + Err(_) => return wasm32::__WASI_ENOMEM, + }; + let argv_buf_size = match cast::u32((*argv_environ).argv_buf_size) { + Ok(argc) => argc, + Err(_) => return wasm32::__WASI_ENOMEM, + }; + + let (host_argv_buf, _argv_buf_size) = match decode_char_slice(vmctx, argv_buf, argv_buf_size) { + Ok((argv_buf, argv_buf_size)) => (argv_buf, argv_buf_size), + Err(e) => return return_encoded_errno(e), + }; + // Add 1 so that we can add an extra NULL pointer at the end. + let (argv, _argc) = match decode_charstar_slice(vmctx, argv, argc + 1) { + Ok((argv, argc)) => (argv, argc), + Err(e) => return return_encoded_errno(e), + }; + let mut host_argv = Vec::new(); + host_argv.resize((*argv_environ).argc + 1, ptr::null_mut()); + + let e = host::wasmtime_ssp_args_get(argv_environ, host_argv.as_mut_ptr(), host_argv_buf); + + encode_charstar_slice(argv, host_argv.as_mut_ptr(), (*argv_environ).argc + 1, + argv_buf, host_argv_buf); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn args_sizes_get( + vmctx: *mut VMContext, + argc: wasm32::uintptr_t, + argv_buf_size: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "args_sizes_get(argc={:#x?}, argv_buf_size={:#x?})", + argc, + argv_buf_size, + ); + + let vmctx = &mut *vmctx; + #[allow(unused_assignments)] + let mut host_argc = match decode_usize_byref(vmctx, argc) { + Ok(host_argc) => host_argc, + Err(e) => return return_encoded_errno(e), + }; + #[allow(unused_assignments)] + let mut host_argv_buf_size = match decode_usize_byref(vmctx, argv_buf_size) { + Ok(host_argv_buf_size) => host_argv_buf_size, + Err(e) => return return_encoded_errno(e), + }; + + let vmctx = &mut *vmctx; + let argv_environ = get_argv_environ(vmctx); + + let e = host::wasmtime_ssp_args_sizes_get(argv_environ, &mut host_argc, &mut host_argv_buf_size); + + trace!(" | *argc={:?}", host_argc); + encode_usize_byref(vmctx, argc, host_argc).unwrap(); + + trace!(" | *argv_buf_size={:?}", host_argv_buf_size); + encode_usize_byref(vmctx, argv_buf_size, host_argv_buf_size).unwrap(); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn clock_res_get( + vmctx: *mut VMContext, + clock_id: wasm32::__wasi_clockid_t, + resolution: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "clock_res_get(clock_id={:?}, resolution={:#x?})", + clock_id, + resolution, + ); + + let vmctx = &mut *vmctx; + let clock_id = decode_clockid(clock_id); + let mut host_resolution = match decode_timestamp_byref(vmctx, resolution) { + Ok(host_resolution) => host_resolution, + Err(e) => return return_encoded_errno(e), + }; + + let e = host::wasmtime_ssp_clock_res_get(clock_id, &mut host_resolution); + + trace!(" | *resolution={:?}", host_resolution); + encode_timestamp_byref(vmctx, resolution, host_resolution).unwrap(); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn clock_time_get( + vmctx: *mut VMContext, + clock_id: wasm32::__wasi_clockid_t, + precision: wasm32::__wasi_timestamp_t, + time: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "clock_time_get(clock_id={:?}, precision={:?}, time={:#x?})", + clock_id, + precision, + time, + ); + + let vmctx = &mut *vmctx; + let clock_id = decode_clockid(clock_id); + let precision = decode_timestamp(precision); + let mut host_time = match decode_timestamp_byref(vmctx, time) { + Ok(host_time) => host_time, + Err(e) => return return_encoded_errno(e), + }; + + let e = host::wasmtime_ssp_clock_time_get(clock_id, precision, &mut host_time); + + trace!(" | *time={:?}", host_time); + encode_timestamp_byref(vmctx, time, host_time).unwrap(); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn environ_get( + vmctx: *mut VMContext, + environ: wasm32::uintptr_t, + environ_buf: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "environ_get(environ={:#x?}, environ_buf={:#x?})", + environ, + environ_buf, + ); + + let vmctx = &mut *vmctx; + let argv_environ = get_argv_environ(vmctx); + let environ_count = match cast::u32((*argv_environ).environ_count) { + Ok(host_environ_count) => host_environ_count, + Err(_) => return wasm32::__WASI_ENOMEM, + }; + let environ_buf_size = match cast::u32((*argv_environ).environ_buf_size) { + Ok(host_environ_buf_size) => host_environ_buf_size, + Err(_) => return wasm32::__WASI_ENOMEM, + }; + + let (host_environ_buf, _environ_buf_len) = match decode_char_slice(vmctx, environ_buf, environ_buf_size) { + Ok((environ_buf, environ_buf_len)) => (environ_buf, environ_buf_len), + Err(e) => return return_encoded_errno(e), + }; + // Add 1 so that we can add an extra NULL pointer at the end. + let (environ, _environ_count) = match decode_charstar_slice(vmctx, environ, environ_count + 1) { + Ok((environ, environ_count)) => (environ, environ_count), + Err(e) => return return_encoded_errno(e), + }; + let mut host_environ = Vec::new(); + host_environ.resize((*argv_environ).environ_count + 1, ptr::null_mut()); + + let e = host::wasmtime_ssp_environ_get(argv_environ, host_environ.as_mut_ptr(), host_environ_buf); + + encode_charstar_slice(environ, host_environ.as_mut_ptr(), (*argv_environ).environ_count + 1, + environ_buf, host_environ_buf); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn environ_sizes_get( + vmctx: *mut VMContext, + environ_count: wasm32::uintptr_t, + environ_buf_size: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "environ_sizes_get(environ_count={:#x?}, environ_buf_size={:#x?})", + environ_count, + environ_buf_size, + ); + + let vmctx = &mut *vmctx; + #[allow(unused_assignments)] + let mut host_environ_count = match decode_usize_byref(vmctx, environ_count) { + Ok(host_environ_count) => host_environ_count, + Err(e) => return return_encoded_errno(e), + }; + #[allow(unused_assignments)] + let mut host_environ_buf_size = match decode_usize_byref(vmctx, environ_buf_size) { + Ok(host_environ_buf_size) => host_environ_buf_size, + Err(e) => return return_encoded_errno(e), + }; + + let vmctx = &mut *vmctx; + let argv_environ = get_argv_environ(vmctx); + + let e = host::wasmtime_ssp_environ_sizes_get(argv_environ, &mut host_environ_count, &mut host_environ_buf_size); + + trace!(" | *environ_count={:?}", host_environ_count); + encode_usize_byref(vmctx, environ_count, host_environ_count).unwrap(); + + trace!(" | *environ_buf_size={:?}", host_environ_buf_size); + encode_usize_byref(vmctx, environ_buf_size, host_environ_buf_size).unwrap(); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn fd_prestat_get( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + buf: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!("fd_prestat_get(fd={:?}, buf={:#x?})", fd, buf); + + let vmctx = &mut *vmctx; + let prestats = get_prestats(vmctx); + let fd = decode_fd(fd); + let mut host_buf = match decode_prestat_byref(vmctx, buf) { + Ok(host_buf) => host_buf, + Err(e) => return return_encoded_errno(e), + }; + + let e = host::wasmtime_ssp_fd_prestat_get(prestats, fd, &mut host_buf); + + encode_prestat_byref(vmctx, buf, host_buf).unwrap(); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn fd_prestat_dir_name( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + path: wasm32::uintptr_t, + path_len: wasm32::size_t, + ) -> wasm32::__wasi_errno_t { + trace!("fd_prestat_dir_name(fd={:?}, path={:#x?}, path_len={})", fd, path, path_len); + + let vmctx = &mut *vmctx; + let prestats = get_prestats(vmctx); + let (path, path_len) = match decode_char_slice(vmctx, path, path_len) { + Ok((path, path_len)) => (path, path_len), + Err(e) => return return_encoded_errno(e), + }; + + trace!(" | (path,path_len)={:?}", str_for_trace(path, path_len)); + + let e = host::wasmtime_ssp_fd_prestat_dir_name(prestats, fd, path, path_len); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn fd_close( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + ) -> wasm32::__wasi_errno_t { + trace!("fd_close(fd={:?})", fd); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let prestats = get_prestats(vmctx); + let fd = decode_fd(fd); + + let e = host::wasmtime_ssp_fd_close(curfds, prestats, fd); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn fd_datasync( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + ) -> wasm32::__wasi_errno_t { + trace!("fd_datasync(fd={:?})", fd); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + + let e = host::wasmtime_ssp_fd_datasync(curfds, fd); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn fd_pread( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + iovs: wasm32::uintptr_t, + iovs_len: wasm32::size_t, + offset: wasm32::__wasi_filesize_t, + nread: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "fd_pread(fd={:?}, iovs={:#x?}, iovs_len={:?}, offset={}, nread={:#x?})", + fd, + iovs, + iovs_len, + offset, + nread + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + let iovs = match decode_iovec_slice(vmctx, iovs, iovs_len) { + Ok(iovs) => iovs, + Err(e) => return return_encoded_errno(e), + }; + let offset = decode_filesize(offset); + let mut host_nread = match decode_usize_byref(vmctx, nread) { + Ok(host_nread) => host_nread, + Err(e) => return return_encoded_errno(e), + }; + + let e = host::wasmtime_ssp_fd_pread( + curfds, + fd, + iovs.as_ptr(), + iovs.len(), + offset, + &mut host_nread, + ); + + trace!(" | *nread={:?}", host_nread); + encode_usize_byref(vmctx, nread, host_nread).unwrap(); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn fd_pwrite( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + iovs: wasm32::uintptr_t, + iovs_len: wasm32::size_t, + offset: wasm32::__wasi_filesize_t, + nwritten: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "fd_pwrite(fd={:?}, iovs={:#x?}, iovs_len={:?}, offset={}, nwritten={:#x?})", + fd, + iovs, + iovs_len, + offset, + nwritten + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + let iovs = match decode_ciovec_slice(vmctx, iovs, iovs_len) { + Ok(iovs) => iovs, + Err(e) => return return_encoded_errno(e), + }; + let offset = decode_filesize(offset); + let mut host_nwritten = match decode_usize_byref(vmctx, nwritten) { + Ok(host_nwritten) => host_nwritten, + Err(e) => return return_encoded_errno(e), + }; + + let e = host::wasmtime_ssp_fd_pwrite( + curfds, + fd, + iovs.as_ptr(), + iovs.len(), + offset, + &mut host_nwritten, + ); + + trace!(" | *nwritten={:?}", host_nwritten); + encode_usize_byref(vmctx, nwritten, host_nwritten).unwrap(); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn fd_read( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + iovs: wasm32::uintptr_t, + iovs_len: wasm32::size_t, + nread: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "fd_read(fd={:?}, iovs={:#x?}, iovs_len={:?}, nread={:#x?})", + fd, + iovs, + iovs_len, + nread + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + let iovs = match decode_iovec_slice(vmctx, iovs, iovs_len) { + Ok(iovs) => iovs, + Err(e) => return return_encoded_errno(e), + }; + let mut host_nread = match decode_usize_byref(vmctx, nread) { + Ok(host_nread) => host_nread, + Err(e) => return return_encoded_errno(e), + }; + + let e = host::wasmtime_ssp_fd_read(curfds, fd, iovs.as_ptr(), iovs.len(), &mut host_nread); + + trace!(" | *nread={:?}", host_nread); + encode_usize_byref(vmctx, nread, host_nread).unwrap(); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn fd_renumber( + vmctx: *mut VMContext, + from: wasm32::__wasi_fd_t, + to: wasm32::__wasi_fd_t, + ) -> wasm32::__wasi_errno_t { + trace!("fd_renumber(from={:?}, to={:?})", from, to); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let from = decode_fd(from); + let to = decode_fd(to); + + let e = host::wasmtime_ssp_fd_renumber(curfds, from, to); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn fd_seek( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + offset: wasm32::__wasi_filedelta_t, + whence: wasm32::__wasi_whence_t, + newoffset: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "fd_seek(fd={:?}, offset={:?}, whence={}, newoffset={:#x?})", + fd, + offset, + wasm32::whence_to_str(whence), + newoffset + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + let offset = decode_filedelta(offset); + let whence = decode_whence(whence); + let mut host_newoffset = match decode_filesize_byref(vmctx, newoffset) { + Ok(host_newoffset) => host_newoffset, + Err(e) => return return_encoded_errno(e), + }; + + let e = host::wasmtime_ssp_fd_seek(curfds, fd, offset, whence, &mut host_newoffset); + + trace!(" | *newoffset={:?}", host_newoffset); + encode_filesize_byref(vmctx, newoffset, host_newoffset).unwrap(); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn fd_tell( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + newoffset: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!("fd_tell(fd={:?}, newoffset={:#x?})", fd, newoffset); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + let mut host_newoffset = match decode_filesize_byref(vmctx, newoffset) { + Ok(host_newoffset) => host_newoffset, + Err(e) => return return_encoded_errno(e), + }; + + let e = host::wasmtime_ssp_fd_tell(curfds, fd, &mut host_newoffset); + + trace!(" | *newoffset={:?}", host_newoffset); + encode_filesize_byref(vmctx, newoffset, host_newoffset).unwrap(); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn fd_fdstat_get( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + buf: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!("fd_fdstat_get(fd={:?}, buf={:#x?})", fd, buf); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + let mut host_buf = match decode_fdstat_byref(vmctx, buf) { + Ok(host_buf) => host_buf, + Err(e) => return return_encoded_errno(e), + }; + + let e = host::wasmtime_ssp_fd_fdstat_get(curfds, fd, &mut host_buf); + + trace!(" | *buf={:?}", host_buf); + encode_fdstat_byref(vmctx, buf, host_buf).unwrap(); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn fd_fdstat_set_flags( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + flags: wasm32::__wasi_fdflags_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "fd_fdstat_set_flags(fd={:?}, flags={:#x?})", + fd, + flags + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + let flags = decode_fdflags(flags); + + let e = host::wasmtime_ssp_fd_fdstat_set_flags(curfds, fd, flags); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn fd_fdstat_set_rights( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + fs_rights_base: wasm32::__wasi_rights_t, + fs_rights_inheriting: wasm32::__wasi_rights_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "fd_fdstat_set_rights(fd={:?}, fs_rights_base={:#x?}, fs_rights_inheriting={:#x?})", + fd, + fs_rights_base, + fs_rights_inheriting + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + let fs_rights_base = decode_rights(fs_rights_base); + let fs_rights_inheriting = decode_rights(fs_rights_inheriting); + + let e = host::wasmtime_ssp_fd_fdstat_set_rights(curfds, fd, fs_rights_base, fs_rights_inheriting); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn fd_sync( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + ) -> wasm32::__wasi_errno_t { + trace!("fd_sync(fd={:?})", fd); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + + let e = host::wasmtime_ssp_fd_sync(curfds, fd); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn fd_write( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + iovs: wasm32::uintptr_t, + iovs_len: wasm32::size_t, + nwritten: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "fd_write(fd={:?}, iovs={:#x?}, iovs_len={:?}, nwritten={:#x?})", + fd, + iovs, + iovs_len, + nwritten + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + let iovs = match decode_ciovec_slice(vmctx, iovs, iovs_len) { + Ok(iovs) => iovs, + Err(e) => return return_encoded_errno(e), + }; + let mut host_nwritten = match decode_usize_byref(vmctx, nwritten) { + Ok(host_nwritten) => host_nwritten, + Err(e) => return return_encoded_errno(e), + }; + + let e = host::wasmtime_ssp_fd_write(curfds, fd, iovs.as_ptr(), iovs.len(), &mut host_nwritten); + + trace!(" | *nwritten={:?}", host_nwritten); + encode_usize_byref(vmctx, nwritten, host_nwritten).unwrap(); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn fd_advise( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + offset: wasm32::__wasi_filesize_t, + len: wasm32::__wasi_filesize_t, + advice: wasm32::__wasi_advice_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "fd_advise(fd={:?}, offset={}, len={}, advice={:?})", + fd, + offset, + len, + advice + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + let offset = decode_filesize(offset); + let len = decode_filesize(len); + let advice = decode_advice(advice); + + let e = host::wasmtime_ssp_fd_advise(curfds, fd, offset, len, advice); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn fd_allocate( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + offset: wasm32::__wasi_filesize_t, + len: wasm32::__wasi_filesize_t, + ) -> wasm32::__wasi_errno_t { + trace!("fd_allocate(fd={:?}, offset={}, len={})", fd, offset, len); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + let offset = decode_filesize(offset); + let len = decode_filesize(len); + + let e = host::wasmtime_ssp_fd_allocate(curfds, fd, offset, len); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn path_create_directory( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + path: wasm32::uintptr_t, + path_len: wasm32::size_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "path_create_directory(fd={:?}, path={:#x?}, path_len={})", + fd, + path, + path_len, + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + let (path, path_len) = match decode_char_slice(vmctx, path, path_len) { + Ok((path, path_len)) => (path, path_len), + Err(e) => return return_encoded_errno(e), + }; + + trace!(" | (path,path_len)={:?}", str_for_trace(path, path_len)); + + let e = host::wasmtime_ssp_path_create_directory(curfds, fd, path, path_len); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn path_link( + vmctx: *mut VMContext, + fd0: wasm32::__wasi_fd_t, + flags0: wasm32::__wasi_lookupflags_t, + path0: wasm32::uintptr_t, + path_len0: wasm32::size_t, + fd1: wasm32::__wasi_fd_t, + path1: wasm32::uintptr_t, + path_len1: wasm32::size_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "path_link(fd0={:?}, flags0={:?}, path0={:#x?}, path_len0={}, fd1={:?}, path1={:#x?}, path_len1={})", + fd0, + flags0, + path0, + path_len0, + fd1, + path1, + path_len1 + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd0 = decode_fd(fd0); + let flags0 = decode_lookupflags(flags0); + let (path0, path_len0) = match decode_char_slice(vmctx, path0, path_len0) { + Ok((path0, path_len0)) => (path0, path_len0), + Err(e) => return return_encoded_errno(e), + }; + let fd1 = decode_fd(fd1); + let (path1, path_len1) = match decode_char_slice(vmctx, path1, path_len1) { + Ok((path1, path_len1)) => (path1, path_len1), + Err(e) => return return_encoded_errno(e), + }; + + trace!(" | (path0,path_len0)={:?}", str_for_trace(path0, path_len0)); + trace!(" | (path1,path_len1)={:?}", str_for_trace(path1, path_len1)); + + let e = + host::wasmtime_ssp_path_link(curfds, fd0, flags0, path0, path_len0, fd1, path1, path_len1); + + return_encoded_errno(e) + } + + // TODO: When multi-value happens, switch to that instead of passing + // the `fd` by reference? + pub unsafe extern "C" fn path_open( + vmctx: *mut VMContext, + dirfd: wasm32::__wasi_fd_t, + dirflags: wasm32::__wasi_lookupflags_t, + path: 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: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "path_open(dirfd={:?}, dirflags={:?}, path={:#x?}, path_len={:?}, oflags={:#x?}, fs_rights_base={:#x?}, fs_rights_inheriting={:#x?}, fs_flags={:#x?}, fd={:#x?})", + dirfd, + dirflags, + path, + path_len, + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + fd + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let dirfd = decode_fd(dirfd); + let dirflags = decode_lookupflags(dirflags); + let (path, path_len) = match decode_char_slice(vmctx, path, path_len) { + Ok((path, path_len)) => (path, path_len), + Err(e) => return return_encoded_errno(e), + }; + let oflags = decode_oflags(oflags); + let fs_rights_base = decode_rights(fs_rights_base); + let fs_rights_inheriting = decode_rights(fs_rights_inheriting); + let fs_flags = decode_fdflags(fs_flags); + let mut host_fd = match decode_fd_byref(vmctx, fd) { + Ok(host_fd) => host_fd, + Err(e) => return return_encoded_errno(e), + }; + + trace!(" | (path,path_len)={:?}", str_for_trace(path, path_len)); + + let e = host::wasmtime_ssp_path_open( + curfds, + dirfd, + dirflags, + path, + path_len, + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + &mut host_fd, + ); + + trace!(" | *fd={:?}", host_fd); + encode_fd_byref(vmctx, fd, host_fd).unwrap(); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn fd_readdir( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + buf: wasm32::uintptr_t, + buf_len: wasm32::size_t, + cookie: wasm32::__wasi_dircookie_t, + buf_used: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "fd_readdir(fd={:?}, buf={:#x?}, buf_len={}, cookie={:#x?}, buf_used={:#x?})", + fd, + buf, + buf_len, + cookie, + buf_used, + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + let (buf, buf_len) = match decode_char_slice(vmctx, buf, buf_len) { + Ok((buf, buf_len)) => (buf, buf_len), + Err(e) => return return_encoded_errno(e), + }; + let cookie = decode_dircookie(cookie); + let mut host_buf_used = match decode_usize_byref(vmctx, buf_used) { + Ok(host_buf_used) => host_buf_used, + Err(e) => return return_encoded_errno(e), + }; + + trace!(" | (buf,buf_len)={:?}", str_for_trace(buf, buf_len)); + + let e = host::wasmtime_ssp_fd_readdir( + curfds, + fd, + buf as *mut host::void, + buf_len, + cookie, + &mut host_buf_used, + ); + + trace!(" | *buf_used={:?}", host_buf_used); + encode_usize_byref(vmctx, buf_used, host_buf_used).unwrap(); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn path_readlink( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + path: wasm32::uintptr_t, + path_len: wasm32::size_t, + buf: wasm32::uintptr_t, + buf_len: wasm32::size_t, + buf_used: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "path_readlink(fd={:?}, path={:#x?}, path_len={:?}, buf={:#x?}, buf_len={}, buf_used={:#x?})", + fd, + path, + path_len, + buf, + buf_len, + buf_used, + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + let (path, path_len) = match decode_char_slice(vmctx, path, path_len) { + Ok((path, path_len)) => (path, path_len), + Err(e) => return return_encoded_errno(e), + }; + let (buf, buf_len) = match decode_char_slice(vmctx, buf, buf_len) { + Ok((buf, buf_len)) => (buf, buf_len), + Err(e) => return return_encoded_errno(e), + }; + let mut host_buf_used = match decode_usize_byref(vmctx, buf_used) { + Ok(host_buf_used) => host_buf_used, + Err(e) => return return_encoded_errno(e), + }; + + trace!(" | (path,path_len)={:?}", str_for_trace(path, path_len)); + + let e = host::wasmtime_ssp_path_readlink( + curfds, + fd, + path, + path_len, + buf, + buf_len, + &mut host_buf_used, + ); + + trace!(" | *buf_used={:?}", host_buf_used); + trace!(" | (buf,*buf_used)={:?}", str_for_trace(buf, host_buf_used)); + encode_usize_byref(vmctx, buf_used, host_buf_used).unwrap(); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn path_rename( + vmctx: *mut VMContext, + fd0: wasm32::__wasi_fd_t, + path0: wasm32::uintptr_t, + path_len0: wasm32::size_t, + fd1: wasm32::__wasi_fd_t, + path1: wasm32::uintptr_t, + path_len1: wasm32::size_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "path_rename(fd0={:?}, path0={:#x?}, path_len0={:?}, fd1={:?}, path1={:#x?}, path_len1={:?})", + fd0, + path0, + path_len0, + fd1, + path1, + path_len1, + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd0 = decode_fd(fd0); + let (path0, path_len0) = match decode_char_slice(vmctx, path0, path_len0) { + Ok((path0, path_len0)) => (path0, path_len0), + Err(e) => return return_encoded_errno(e), + }; + let fd1 = decode_fd(fd1); + let (path1, path_len1) = match decode_char_slice(vmctx, path1, path_len1) { + Ok((path1, path_len1)) => (path1, path_len1), + Err(e) => return return_encoded_errno(e), + }; + + trace!(" | (path0,path_len0)={:?}", str_for_trace(path0, path_len0)); + trace!(" | (path1,path_len1)={:?}", str_for_trace(path1, path_len1)); + + let e = host::wasmtime_ssp_path_rename(curfds, fd0, path0, path_len0, fd1, path1, path_len1); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn fd_filestat_get( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + buf: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!("fd_filestat_get(fd={:?}, buf={:#x?})", fd, buf); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + let mut host_buf = match decode_filestat_byref(vmctx, buf) { + Ok(host_buf) => host_buf, + Err(e) => return return_encoded_errno(e), + }; + + let e = host::wasmtime_ssp_fd_filestat_get(curfds, fd, &mut host_buf); + + trace!(" | *buf={:?}", host_buf); + encode_filestat_byref(vmctx, buf, host_buf).unwrap(); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn fd_filestat_set_times( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + st_atim: wasm32::__wasi_timestamp_t, + st_mtim: wasm32::__wasi_timestamp_t, + fstflags: wasm32::__wasi_fstflags_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "fd_filestat_set_times(fd={:?}, st_atim={}, st_mtim={}, fstflags={:#x?})", + fd, + st_atim, st_mtim, + fstflags + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + let st_atim = decode_timestamp(st_atim); + let st_mtim = decode_timestamp(st_mtim); + let fstflags = decode_fstflags(fstflags); + + let e = host::wasmtime_ssp_fd_filestat_set_times(curfds, fd, st_atim, st_mtim, fstflags); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn fd_filestat_set_size( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + size: wasm32::__wasi_filesize_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "fd_filestat_set_size(fd={:?}, size={})", + fd, + size + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + let size = decode_filesize(size); + + let e = host::wasmtime_ssp_fd_filestat_set_size(curfds, fd, size); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn path_filestat_get( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + flags: wasm32::__wasi_lookupflags_t, + path: wasm32::uintptr_t, + path_len: wasm32::size_t, + buf: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "path_filestat_get(fd={:?}, flags={:?}, path={:#x?}, path_len={}, buf={:#x?})", + fd, + flags, + path, + path_len, + buf + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + let flags = decode_lookupflags(flags); + let (path, path_len) = match decode_char_slice(vmctx, path, path_len) { + Ok((path, path_len)) => (path, path_len), + Err(e) => return return_encoded_errno(e), + }; + let mut host_buf = match decode_filestat_byref(vmctx, buf) { + Ok(host_buf) => host_buf, + Err(e) => return return_encoded_errno(e), + }; + + trace!(" | (path,path_len)={:?}", str_for_trace(path, path_len)); + + let e = host::wasmtime_ssp_path_filestat_get(curfds, fd, flags, path, path_len, &mut host_buf); + + trace!(" | *buf={:?}", host_buf); + encode_filestat_byref(vmctx, buf, host_buf).unwrap(); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn path_filestat_set_times( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + flags: wasm32::__wasi_lookupflags_t, + path: wasm32::uintptr_t, + path_len: wasm32::size_t, + st_atim: wasm32::__wasi_timestamp_t, + st_mtim: wasm32::__wasi_timestamp_t, + fstflags: wasm32::__wasi_fstflags_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "path_filestat_set_times(fd={:?}, flags={:?}, path={:#x?}, path_len={}, st_atim={}, st_mtim={}, fstflags={:#x?})", + fd, + flags, + path, + path_len, + st_atim, st_mtim, + fstflags + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + let flags = decode_lookupflags(flags); + let (path, path_len) = match decode_char_slice(vmctx, path, path_len) { + Ok((path, path_len)) => (path, path_len), + Err(e) => return return_encoded_errno(e), + }; + let st_atim = decode_timestamp(st_atim); + let st_mtim = decode_timestamp(st_mtim); + let fstflags = decode_fstflags(fstflags); + + trace!(" | (path,path_len)={:?}", str_for_trace(path, path_len)); + + let e = host::wasmtime_ssp_path_filestat_set_times(curfds, fd, flags, path, path_len, st_atim, st_mtim, fstflags); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn path_symlink( + vmctx: *mut VMContext, + path0: wasm32::uintptr_t, + path_len0: wasm32::size_t, + fd: wasm32::__wasi_fd_t, + path1: wasm32::uintptr_t, + path_len1: wasm32::size_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "path_symlink(path0={:#x?}, path_len0={}, fd={:?}, path1={:#x?}, path_len1={})", + path0, + path_len0, + fd, + path1, + path_len1 + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let (path0, path_len0) = match decode_char_slice(vmctx, path0, path_len0) { + Ok((path0, path_len0)) => (path0, path_len0), + Err(e) => return return_encoded_errno(e), + }; + let fd = decode_fd(fd); + let (path1, path_len1) = match decode_char_slice(vmctx, path1, path_len1) { + Ok((path1, path_len1)) => (path1, path_len1), + Err(e) => return return_encoded_errno(e), + }; + + trace!(" | (path0,path_len0)={:?}", str_for_trace(path0, path_len0)); + trace!(" | (path1,path_len1)={:?}", str_for_trace(path1, path_len1)); + + let e = host::wasmtime_ssp_path_symlink(curfds, path0, path_len0, fd, path1, path_len1); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn path_unlink_file( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + path: wasm32::uintptr_t, + path_len: wasm32::size_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "path_unlink_file(fd={:?}, path={:#x?}, path_len={})", + fd, + path, + path_len + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + let (path, path_len) = match decode_char_slice(vmctx, path, path_len) { + Ok((path, path_len)) => (path, path_len), + Err(e) => return return_encoded_errno(e), + }; + + trace!(" | (path,path_len)={:?}", str_for_trace(path, path_len)); + + let e = host::wasmtime_ssp_path_unlink_file(curfds, fd, path, path_len); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn path_remove_directory( + vmctx: *mut VMContext, + fd: wasm32::__wasi_fd_t, + path: wasm32::uintptr_t, + path_len: wasm32::size_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "path_remove_directory(fd={:?}, path={:#x?}, path_len={})", + fd, + path, + path_len + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let fd = decode_fd(fd); + let (path, path_len) = match decode_char_slice(vmctx, path, path_len) { + Ok((path, path_len)) => (path, path_len), + Err(e) => return return_encoded_errno(e), + }; + + trace!(" | (path,path_len)={:?}", str_for_trace(path, path_len)); + + let e = host::wasmtime_ssp_path_remove_directory(curfds, fd, path, path_len); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn poll_oneoff( + vmctx: *mut VMContext, + in_: wasm32::uintptr_t, + out: wasm32::uintptr_t, + nsubscriptions: wasm32::size_t, + nevents: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "poll_oneoff(in={:#x?}, out={:#x?}, nsubscriptions={}, nevents={:#x?})", + in_, + out, + nsubscriptions, + nevents, + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let in_ = match decode_subscription_slice(vmctx, in_, nsubscriptions) { + Ok(in_) => in_, + Err(e) => return return_encoded_errno(e), + }; + let mut host_out = match decode_event_slice(vmctx, out, nsubscriptions) { + Ok(out) => out, + Err(e) => return return_encoded_errno(e), + }; + let mut host_nevents = match decode_usize_byref(vmctx, nevents) { + Ok(host_nevents) => host_nevents, + Err(e) => return return_encoded_errno(e), + }; + + assert!(in_.len() == host_out.len()); + + let e = host::wasmtime_ssp_poll_oneoff( + curfds, + in_.as_ptr(), + host_out.as_mut_ptr(), + in_.len(), + &mut host_nevents, + ); + + trace!(" | *nevents={:?}", host_nevents); + encode_usize_byref(vmctx, nevents, host_nevents).unwrap(); + + if log_enabled!(log::Level::Trace) { + for (index, _event) in host_out.iter().enumerate() { + // TODO: Format the output for tracing. + trace!(" | *out[{}]=...", index); + } + } + encode_event_slice(vmctx, out, host_out).unwrap(); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn proc_exit(_vmctx: *mut VMContext, rval: u32,) -> () { + trace!("proc_exec(rval={:?})", rval); + + let rval = decode_exitcode(rval); + + // TODO: Rather than call __wasi_proc_exit here, we should trigger a + // stack unwind similar to a trap. + host::wasmtime_ssp_proc_exit(rval); + } + + pub unsafe extern "C" fn proc_raise( + _vmctx: *mut VMContext, + _sig: wasm32::__wasi_signal_t, + ) -> wasm32::__wasi_errno_t { + unimplemented!("__wasi_proc_raise"); + } + + pub unsafe extern "C" fn random_get( + vmctx: *mut VMContext, + buf: wasm32::uintptr_t, + buf_len: wasm32::size_t, + ) -> wasm32::__wasi_errno_t { + trace!("random_get(buf={:#x?}, buf_len={:?})", buf, buf_len); + + let vmctx = &mut *vmctx; + let (buf, buf_len) = match decode_char_slice(vmctx, buf, buf_len) { + Ok((buf, buf_len)) => (buf, buf_len), + Err(e) => return return_encoded_errno(e), + }; + + let e = host::wasmtime_ssp_random_get(buf as *mut host::void, buf_len); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn sched_yield(_vmctx: *mut VMContext,) -> wasm32::__wasi_errno_t { + let e = host::wasmtime_ssp_sched_yield(); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn sock_recv( + vmctx: *mut VMContext, + sock: wasm32::__wasi_fd_t, + ri_data: wasm32::uintptr_t, + ri_data_len: wasm32::size_t, + ri_flags: wasm32::__wasi_riflags_t, + ro_datalen: wasm32::uintptr_t, + ro_flags: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "sock_recv(sock={:?}, ri_data={:#x?}, ri_data_len={}, ri_flags={:#x?}, ro_datalen={:#x?}, ro_flags={:#x?})", + sock, + ri_data, ri_data_len, ri_flags, + ro_datalen, ro_flags + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let sock = decode_fd(sock); + let ri_data = match decode_iovec_slice(vmctx, ri_data, ri_data_len) { + Ok(ri_data) => ri_data, + Err(e) => return return_encoded_errno(e), + }; + let ri_flags = decode_riflags(ri_flags); + let mut host_ro_datalen = match decode_usize_byref(vmctx, ro_datalen) { + Ok(host_ro_datalen) => host_ro_datalen, + Err(e) => return return_encoded_errno(e), + }; + let mut host_ro_flags = match decode_roflags_byref(vmctx, ro_flags) { + Ok(host_ro_flags) => host_ro_flags, + Err(e) => return return_encoded_errno(e), + }; + + let e = host::wasmtime_ssp_sock_recv(curfds, sock, ri_data.as_ptr(), ri_data.len(), ri_flags, + &mut host_ro_datalen, &mut host_ro_flags); + + // TODO: Format the output for tracing. + trace!(" | *ro_datalen={}", host_ro_datalen); + trace!(" | *ro_flags={}", host_ro_flags); + encode_usize_byref(vmctx, ro_datalen, host_ro_datalen).unwrap(); + encode_roflags_byref(vmctx, ro_flags, host_ro_flags).unwrap(); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn sock_send( + vmctx: *mut VMContext, + sock: wasm32::__wasi_fd_t, + si_data: wasm32::uintptr_t, + si_data_len: wasm32::size_t, + si_flags: wasm32::__wasi_siflags_t, + so_datalen: wasm32::uintptr_t, + ) -> wasm32::__wasi_errno_t { + trace!( + "sock_send(sock={:?}, si_data={:#x?}, si_data_len={}, si_flags={:#x?}, so_datalen={:#x?})", + sock, + si_data, si_data_len, si_flags, so_datalen, + ); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let sock = decode_fd(sock); + let si_data = match decode_ciovec_slice(vmctx, si_data, si_data_len) { + Ok(si_data) => si_data, + Err(e) => return return_encoded_errno(e), + }; + let si_flags = decode_siflags(si_flags); + let mut host_so_datalen = match decode_usize_byref(vmctx, so_datalen) { + Ok(so_datalen) => so_datalen, + Err(e) => return return_encoded_errno(e), + }; + + let e = host::wasmtime_ssp_sock_send(curfds, sock, si_data.as_ptr(), si_data.len(), si_flags, &mut host_so_datalen); + + trace!(" | *so_datalen={:?}", host_so_datalen); + encode_usize_byref(vmctx, so_datalen, host_so_datalen).unwrap(); + + return_encoded_errno(e) + } + + pub unsafe extern "C" fn sock_shutdown( + vmctx: *mut VMContext, + sock: wasm32::__wasi_fd_t, + how: wasm32::__wasi_sdflags_t, + ) -> wasm32::__wasi_errno_t { + trace!("sock_shutdown(sock={:?}, how={:?})", sock, how); + + let vmctx = &mut *vmctx; + let curfds = get_curfds(vmctx); + let sock = decode_fd(sock); + let how = decode_sdflags(how); + + let e = host::wasmtime_ssp_sock_shutdown(curfds, sock, how); + + return_encoded_errno(e) + } +} diff --git a/wasmtime-wasi/src/translate.rs b/wasmtime-wasi/src/translate.rs new file mode 100644 index 0000000000..197468d809 --- /dev/null +++ b/wasmtime-wasi/src/translate.rs @@ -0,0 +1,508 @@ +use cast; +use cast::From as _0; +use host; +use std::mem::{align_of, size_of}; +use std::slice; +use wasm32; +use wasmtime_runtime::{Export, VMContext}; + +/// Translate a wasm pointer into a native pointer. +/// +/// This is unsafe due to trusting the contents of vmctx. The pointer result +/// is bounds and alignment checked. +unsafe fn decode_ptr( + vmctx: &mut VMContext, + ptr: wasm32::uintptr_t, + len: usize, + align: usize, +) -> Result<*mut u8, host::__wasi_errno_t> { + match vmctx.lookup_global_export("memory") { + Some(Export::Memory { + definition, + vmctx: _, + memory: _, + }) => { + if len > 0 { + // Check for overflow within the access. + let last = match (ptr as usize).checked_add(len - 1) { + Some(sum) => sum, + None => { + println!("!!! overflow"); + return Err(host::__WASI_EFAULT as host::__wasi_errno_t); + } + }; + // Check for out of bounds. + if last >= (*definition).current_length { + println!("!!! out of bounds"); + return Err(host::__WASI_EFAULT as host::__wasi_errno_t); + } + } + // Check alignment. + if (ptr as usize) % align != 0 { + println!("!!! bad alignment: {} % {}", ptr, align); + return Err(host::__WASI_EINVAL as host::__wasi_errno_t); + } + // Ok, translate the address. + Ok((((*definition).base as usize) + (ptr as usize)) as *mut u8) + } + // No export named "__wasi_memory", or the export isn't a memory. + // FIXME: Is EINVAL the best code here? + x => { + println!( + "!!! no export named __wasi_memory, or the export isn't a mem: {:?}", + x + ); + Err(host::__WASI_EINVAL as host::__wasi_errno_t) + } + } +} + +unsafe fn decode_ptr_to( + vmctx: &mut VMContext, + ptr: wasm32::uintptr_t, +) -> Result<*mut T, host::__wasi_errno_t> { + decode_ptr(vmctx, ptr, size_of::(), align_of::()).map(|ptr| ptr as *mut T) +} + +unsafe fn decode_pointee( + vmctx: &mut VMContext, + ptr: wasm32::uintptr_t, +) -> Result { + let ptr = decode_ptr_to::(vmctx, ptr)?; + + // Size and alignment are checked by `decode_ptr_to`. + Ok(ptr.read()) +} + +pub unsafe fn encode_pointee( + vmctx: &mut VMContext, + ptr: wasm32::uintptr_t, + t: T, +) -> Result<(), host::__wasi_errno_t> { + let ptr = decode_ptr_to::(vmctx, ptr)?; + + // Size and alignment are checked by `decode_ptr_to`. + Ok(ptr.write(t)) +} + +pub unsafe fn decode_slice_of( + vmctx: &mut VMContext, + ptr: wasm32::uintptr_t, + len: wasm32::size_t, +) -> Result<(*mut T, usize), host::__wasi_errno_t> { + let len = cast::usize(len); + + let ptr = decode_ptr( + vmctx, + ptr, + size_of::().checked_mul(len).unwrap(), + align_of::(), + )? as *mut T; + + Ok((ptr, len)) +} + +pub fn decode_usize(len: wasm32::size_t) -> usize { + cast::usize(len) +} + +pub fn encode_usize(len: usize) -> wasm32::size_t { + cast::u32(len).unwrap() +} + +pub unsafe fn decode_filesize(filesize: wasm32::__wasi_filesize_t) -> host::__wasi_filesize_t { + filesize +} + +pub fn decode_fd(fd: wasm32::__wasi_fd_t) -> host::__wasi_fd_t { + fd +} + +pub fn decode_filedelta(filedelta: wasm32::__wasi_filedelta_t) -> host::__wasi_filedelta_t { + filedelta +} + +pub fn decode_whence(whence: wasm32::__wasi_whence_t) -> host::__wasi_whence_t { + whence +} + +pub fn decode_clockid(clockid: wasm32::__wasi_clockid_t) -> host::__wasi_clockid_t { + clockid +} + +pub fn decode_timestamp(timestamp: wasm32::__wasi_timestamp_t) -> host::__wasi_timestamp_t { + timestamp +} + +pub fn decode_exitcode(exitcode: wasm32::__wasi_exitcode_t) -> host::__wasi_exitcode_t { + exitcode +} + +pub fn decode_lookupflags(lookupflags: wasm32::__wasi_lookupflags_t) -> host::__wasi_lookupflags_t { + lookupflags +} + +pub fn decode_roflags(roflags: wasm32::__wasi_roflags_t) -> host::__wasi_roflags_t { + roflags +} + +pub fn decode_oflags(oflags: wasm32::__wasi_oflags_t) -> host::__wasi_oflags_t { + oflags +} + +pub fn decode_advice(advice: wasm32::__wasi_advice_t) -> host::__wasi_advice_t { + advice +} + +pub fn decode_dircookie(dircookie: wasm32::__wasi_dircookie_t) -> host::__wasi_dircookie_t { + dircookie +} + +pub fn decode_preopentype(preopentype: wasm32::__wasi_preopentype_t) -> host::__wasi_preopentype_t { + preopentype +} + +pub fn encode_preopentype(preopentype: host::__wasi_preopentype_t) -> wasm32::__wasi_preopentype_t { + preopentype +} + +pub fn decode_filetype(filetype: wasm32::__wasi_filetype_t) -> host::__wasi_filetype_t { + filetype +} + +pub fn encode_filetype(filetype: host::__wasi_filetype_t) -> wasm32::__wasi_filetype_t { + filetype +} + +pub fn decode_fstflags(fstflags: wasm32::__wasi_fstflags_t) -> host::__wasi_fstflags_t { + fstflags +} + +#[allow(dead_code)] +pub fn encode_fstflags(fstflags: host::__wasi_fstflags_t) -> wasm32::__wasi_fstflags_t { + fstflags +} + +pub fn decode_fdflags(fdflags: wasm32::__wasi_fdflags_t) -> host::__wasi_fdflags_t { + fdflags +} + +pub fn encode_fdflags(fdflags: host::__wasi_fdflags_t) -> wasm32::__wasi_fdflags_t { + fdflags +} + +pub fn decode_sdflags(sdflags: wasm32::__wasi_sdflags_t) -> host::__wasi_sdflags_t { + sdflags +} + +pub fn decode_rights(rights: wasm32::__wasi_rights_t) -> host::__wasi_rights_t { + rights +} + +pub fn encode_rights(rights: host::__wasi_rights_t) -> wasm32::__wasi_rights_t { + rights +} + +pub fn decode_riflags(riflags: wasm32::__wasi_riflags_t) -> host::__wasi_riflags_t { + riflags +} + +pub fn decode_siflags(siflags: wasm32::__wasi_siflags_t) -> host::__wasi_siflags_t { + siflags +} + +pub unsafe fn decode_char_slice( + vmctx: &mut VMContext, + ptr: wasm32::uintptr_t, + len: wasm32::size_t, +) -> Result<(*mut host::char, usize), host::__wasi_errno_t> { + decode_slice_of::(vmctx, ptr, len) +} + +pub unsafe fn decode_charstar_slice( + vmctx: &mut VMContext, + ptr: wasm32::uintptr_t, + count: wasm32::size_t, +) -> Result<(*mut wasm32::uintptr_t, usize), host::__wasi_errno_t> { + decode_slice_of::(vmctx, ptr, count) +} + +pub unsafe fn encode_charstar_slice( + ptr: *mut wasm32::uintptr_t, + host_ptr: *mut *mut libc::c_char, + count: usize, + guest_base: wasm32::uintptr_t, + host_base: *mut libc::c_char, +) { + for i in 0..count { + let host = host_ptr.add(i).read(); + let guest = if host.is_null() { + 0 + } else { + guest_base + (host as usize - host_base as usize) as wasm32::uintptr_t + }; + ptr.add(i).write(guest); + } +} + +pub unsafe fn decode_ciovec( + vmctx: &mut VMContext, + ciovec: &wasm32::__wasi_ciovec_t, +) -> Result { + let len = cast::usize(ciovec.buf_len); + Ok(host::__wasi_ciovec_t { + buf: decode_ptr(vmctx, ciovec.buf, len, 1)? as *const host::void, + buf_len: len, + }) +} + +pub unsafe fn decode_iovec( + vmctx: &mut VMContext, + iovec: &wasm32::__wasi_iovec_t, +) -> Result { + let len = cast::usize(iovec.buf_len); + Ok(host::__wasi_iovec_t { + buf: decode_ptr(vmctx, iovec.buf, len, 1)? as *mut host::void, + buf_len: len, + }) +} + +pub unsafe fn decode_ciovec_slice( + vmctx: &mut VMContext, + ptr: wasm32::uintptr_t, + len: wasm32::size_t, +) -> Result, host::__wasi_errno_t> { + let slice = decode_slice_of::(vmctx, ptr, len)?; + let slice = slice::from_raw_parts(slice.0, slice.1); + slice.iter().map(|iov| decode_ciovec(vmctx, iov)).collect() +} + +pub unsafe fn decode_iovec_slice( + vmctx: &mut VMContext, + ptr: wasm32::uintptr_t, + len: wasm32::size_t, +) -> Result, host::__wasi_errno_t> { + let slice = decode_slice_of::(vmctx, ptr, len)?; + let slice = slice::from_raw_parts(slice.0, slice.1); + slice.iter().map(|iov| decode_iovec(vmctx, iov)).collect() +} + +pub unsafe fn decode_subscription( + _vmctx: &mut VMContext, + _subscription: wasm32::__wasi_subscription_t, +) -> host::__wasi_subscription_t { + unimplemented!("decode_subscription"); +} + +pub unsafe fn decode_subscription_slice( + vmctx: &mut VMContext, + ptr: wasm32::uintptr_t, + len: wasm32::size_t, +) -> Result, host::__wasi_errno_t> { + let slice = decode_slice_of::(vmctx, ptr, len)?; + let slice = slice::from_raw_parts(slice.0, slice.1); + Ok(slice + .iter() + .map(|subscription| decode_subscription(vmctx, *subscription)) + .collect()) +} + +pub unsafe fn decode_event( + _vmctx: &mut VMContext, + _event: wasm32::__wasi_event_t, +) -> host::__wasi_event_t { + unimplemented!("decode_event"); +} + +pub unsafe fn decode_event_slice( + vmctx: &mut VMContext, + ptr: wasm32::uintptr_t, + len: wasm32::size_t, +) -> Result, host::__wasi_errno_t> { + let slice = decode_slice_of::(vmctx, ptr, len)?; + let slice = slice::from_raw_parts(slice.0, slice.1); + Ok(slice + .iter() + .map(|event| decode_event(vmctx, *event)) + .collect()) +} + +pub unsafe fn encode_event_slice( + _vmctx: &mut VMContext, + _ptr: wasm32::uintptr_t, + _host: Vec, +) -> Result<(), host::__wasi_errno_t> { + unimplemented!("encode_event_slice"); +} + +pub unsafe fn decode_fd_byref( + vmctx: &mut VMContext, + fd_ptr: wasm32::uintptr_t, +) -> Result { + decode_pointee::(vmctx, fd_ptr).map(decode_fd) +} + +pub unsafe fn encode_fd_byref( + vmctx: &mut VMContext, + fd_ptr: wasm32::uintptr_t, + fd: host::__wasi_fd_t, +) -> Result<(), host::__wasi_errno_t> { + encode_pointee::(vmctx, fd_ptr, wasm32::size_t::cast(fd)) +} + +pub unsafe fn decode_timestamp_byref( + vmctx: &mut VMContext, + timestamp_ptr: wasm32::uintptr_t, +) -> Result { + decode_pointee::(vmctx, timestamp_ptr) + .map(host::__wasi_timestamp_t::cast) +} + +pub unsafe fn encode_timestamp_byref( + vmctx: &mut VMContext, + timestamp_ptr: wasm32::uintptr_t, + host_timestamp: host::__wasi_timestamp_t, +) -> Result<(), host::__wasi_errno_t> { + encode_pointee::( + vmctx, + timestamp_ptr, + wasm32::__wasi_timestamp_t::cast(host_timestamp), + ) +} + +pub unsafe fn decode_filesize_byref( + vmctx: &mut VMContext, + filesize_ptr: wasm32::uintptr_t, +) -> Result { + decode_pointee::(vmctx, filesize_ptr) + .map(host::__wasi_filesize_t::cast) +} + +pub unsafe fn encode_filesize_byref( + vmctx: &mut VMContext, + filesize_ptr: wasm32::uintptr_t, + host_filesize: host::__wasi_filesize_t, +) -> Result<(), host::__wasi_errno_t> { + encode_pointee::( + vmctx, + filesize_ptr, + wasm32::__wasi_filesize_t::cast(host_filesize), + ) +} + +pub unsafe fn decode_roflags_byref( + vmctx: &mut VMContext, + roflags_ptr: wasm32::uintptr_t, +) -> Result { + decode_pointee::(vmctx, roflags_ptr).map(decode_roflags) +} + +pub unsafe fn encode_roflags_byref( + vmctx: &mut VMContext, + roflags_ptr: wasm32::uintptr_t, + host_roflags: host::__wasi_roflags_t, +) -> Result<(), host::__wasi_errno_t> { + encode_pointee::( + vmctx, + roflags_ptr, + wasm32::__wasi_roflags_t::cast(host_roflags), + ) +} + +pub unsafe fn decode_usize_byref( + vmctx: &mut VMContext, + usize_ptr: wasm32::uintptr_t, +) -> Result { + decode_pointee::(vmctx, usize_ptr).map(decode_usize) +} + +pub unsafe fn encode_usize_byref( + vmctx: &mut VMContext, + usize_ptr: wasm32::uintptr_t, + host_usize: usize, +) -> Result<(), host::__wasi_errno_t> { + encode_pointee::(vmctx, usize_ptr, wasm32::size_t::cast(host_usize).unwrap()) +} + +pub unsafe fn decode_prestat_byref( + vmctx: &mut VMContext, + prestat_ptr: wasm32::uintptr_t, +) -> Result { + let wasm32_prestat = decode_pointee::(vmctx, prestat_ptr)?; + + Ok(host::__wasi_prestat_t { + pr_type: decode_preopentype(wasm32_prestat.pr_type), + u: host::__wasi_prestat_t___wasi_prestat_u { + dir: host::__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t { + pr_name_len: decode_usize(wasm32_prestat.u.dir.pr_name_len), + }, + }, + }) +} + +pub unsafe fn encode_prestat_byref( + vmctx: &mut VMContext, + prestat_ptr: wasm32::uintptr_t, + host_prestat: host::__wasi_prestat_t, +) -> Result<(), host::__wasi_errno_t> { + let wasm32_prestat = wasm32::__wasi_prestat_t { + pr_type: encode_preopentype(host_prestat.pr_type), + u: wasm32::__wasi_prestat_t___wasi_prestat_u { + dir: wasm32::__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t { + pr_name_len: encode_usize(host_prestat.u.dir.pr_name_len), + }, + }, + }; + + encode_pointee::(vmctx, prestat_ptr, wasm32_prestat) +} + +pub unsafe fn decode_fdstat_byref( + vmctx: &mut VMContext, + fdstat_ptr: wasm32::uintptr_t, +) -> Result { + let wasm32_fdstat = decode_pointee::(vmctx, fdstat_ptr)?; + + Ok(host::__wasi_fdstat_t { + fs_filetype: decode_filetype(wasm32_fdstat.fs_filetype), + fs_flags: decode_fdflags(wasm32_fdstat.fs_flags), + fs_rights_base: decode_rights(wasm32_fdstat.fs_rights_base), + fs_rights_inheriting: decode_rights(wasm32_fdstat.fs_rights_inheriting), + }) +} + +pub unsafe fn encode_fdstat_byref( + vmctx: &mut VMContext, + fdstat_ptr: wasm32::uintptr_t, + host_fdstat: host::__wasi_fdstat_t, +) -> Result<(), host::__wasi_errno_t> { + let wasm32_fdstat = wasm32::__wasi_fdstat_t { + fs_filetype: encode_filetype(host_fdstat.fs_filetype), + fs_flags: encode_fdflags(host_fdstat.fs_flags), + __bindgen_padding_0: 0, + fs_rights_base: encode_rights(host_fdstat.fs_rights_base), + fs_rights_inheriting: encode_rights(host_fdstat.fs_rights_inheriting), + }; + + encode_pointee::(vmctx, fdstat_ptr, wasm32_fdstat) +} + +pub unsafe fn decode_filestat_byref( + _vmctx: &mut VMContext, + _filestat_ptr: wasm32::uintptr_t, +) -> Result { + unimplemented!("decode_filestat_byref"); +} + +pub unsafe fn encode_filestat_byref( + _vmctx: &mut VMContext, + _filestat_ptr: wasm32::uintptr_t, + _host_filestat: host::__wasi_filestat_t, +) -> Result<(), host::__wasi_errno_t> { + unimplemented!("encode_filestat_byref"); +} + +pub fn encode_errno(e: host::__wasi_errno_t) -> wasm32::__wasi_errno_t { + assert!(e <= wasm32::__WASI_ENOTCAPABLE); + e +} diff --git a/wasmtime-wasi/src/wasm32.rs b/wasmtime-wasi/src/wasm32.rs new file mode 100644 index 0000000000..ff0bb803a1 --- /dev/null +++ b/wasmtime-wasi/src/wasm32.rs @@ -0,0 +1,1286 @@ +//! WASI types as defined in wasm32. This file was originally generated +//! by running bindgen over wasi/core.h with a wasm32 target, and the content +//! still largely reflects that, however it's been heavily modified, to +//! be host-independent, to avoid exposing libc implementation details, +//! to clean up cases where the headers use complex preprocessor macros, +//! and to + +#![allow(non_camel_case_types)] +#![allow(dead_code)] + +// C types +pub type char = i8; +pub type schar = i8; +pub type uchar = u8; +pub type short = i16; +pub type ushort = u16; +pub type int = i32; +pub type uint = u32; +pub type long = i32; +pub type ulong = u32; +pub type longlong = i64; +pub type ulonglong = u64; + +// libc stdint types +pub type int8_t = i8; +pub type uint8_t = u8; +pub type int16_t = i16; +pub type uint16_t = u16; +pub type int32_t = i32; +pub type uint32_t = u32; +pub type int64_t = i64; +pub type uint64_t = u64; +pub type intmax_t = i64; +pub type uintmax_t = u64; +pub type int_least8_t = i8; +pub type int_least16_t = i16; +pub type int_least32_t = i32; +pub type int_least64_t = i64; +pub type uint_least8_t = u8; +pub type uint_least16_t = u16; +pub type uint_least32_t = u32; +pub type uint_least64_t = u64; +pub type int_fast8_t = i8; +pub type int_fast16_t = i32; +pub type int_fast32_t = i32; +pub type int_fast64_t = i64; +pub type uint_fast8_t = u8; +pub type uint_fast16_t = u32; +pub type uint_fast32_t = u32; +pub type uint_fast64_t = u64; +pub type size_t = ulong; +pub type intptr_t = long; +pub type uintptr_t = ulong; +pub type wchar_t = i32; + +// libc types +pub type dev_t = u64; +pub type uid_t = u32; +pub type gid_t = u32; +pub type ino_t = u64; +pub type ino64_t = u64; +pub type mode_t = u32; +pub type nlink_t = u64; +pub type off_t = i64; +pub type off64_t = i64; +pub type pid_t = i32; +pub type clock_t = i64; +pub type rlim_t = u64; +pub type rlim64_t = u64; +pub type id_t = u32; +pub type time_t = i64; +pub type useconds_t = u32; +pub type suseconds_t = i64; +pub type daddr_t = i32; +pub type key_t = i32; +pub type clockid_t = i32; +pub type timer_t = uintptr_t; // *mut ::std::os::raw::c_void +pub type blksize_t = i64; +pub type blkcnt_t = i64; +pub type blkcnt64_t = i64; +pub type fsblkcnt_t = u64; +pub type fsblkcnt64_t = u64; +pub type fsfilcnt_t = u64; +pub type fsfilcnt64_t = u64; +pub type fsword_t = i64; +pub type ssize_t = i32; +pub type loff_t = off64_t; +pub type caddr_t = uintptr_t; // *mut i8 +pub type socklen_t = u32; +pub type sig_atomic_t = i32; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct fsid_t { + pub __val: [i32; 2usize], +} +#[allow(non_snake_case)] +#[test] +fn bindgen_test_layout_fsid_t() { + assert_eq!( + ::std::mem::size_of::(), + 8usize, + concat!("Size of: ", stringify!(fsid_t)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(fsid_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).__val as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(fsid_t), + "::", + stringify!(__val) + ) + ); +} + +// WASI types +pub type __wasi_advice_t = u8; +pub type __wasi_clockid_t = u32; +pub type __wasi_device_t = u64; +pub type __wasi_dircookie_t = u64; +pub type __wasi_errno_t = u16; +pub type __wasi_eventrwflags_t = u16; +pub type __wasi_eventtype_t = u8; +pub type __wasi_exitcode_t = u32; +pub type __wasi_fd_t = u32; +pub type __wasi_fdflags_t = u16; +pub type __wasi_fdsflags_t = u16; +pub type __wasi_filedelta_t = i64; +pub type __wasi_filesize_t = u64; +pub type __wasi_filetype_t = u8; +pub type __wasi_preopentype_t = u8; +pub type __wasi_fstflags_t = u16; +pub type __wasi_inode_t = u64; +pub type __wasi_linkcount_t = u32; +pub type __wasi_lookupflags_t = u32; +pub type __wasi_oflags_t = u16; +pub type __wasi_riflags_t = u16; +pub type __wasi_rights_t = u64; +pub type __wasi_roflags_t = u16; +pub type __wasi_sdflags_t = u8; +pub type __wasi_siflags_t = u16; +pub type __wasi_signal_t = u8; +pub type __wasi_subclockflags_t = u16; +pub type __wasi_timestamp_t = u64; +pub type __wasi_userdata_t = u64; +pub type __wasi_whence_t = u8; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_dirent_t { + pub d_next: __wasi_dircookie_t, + pub d_ino: __wasi_inode_t, + pub d_namlen: u32, + pub d_type: __wasi_filetype_t, + pub __bindgen_padding_0: [u8; 3usize], +} +#[test] +fn bindgen_test_layout_wasi_dirent_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_dirent_t>(), + 24usize, + concat!("Size of: ", stringify!(__wasi_dirent_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_dirent_t>())).d_next as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_dirent_t), + "::", + stringify!(d_next) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_dirent_t>())).d_ino as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_dirent_t), + "::", + stringify!(d_ino) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_dirent_t>())).d_namlen as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(__wasi_dirent_t), + "::", + stringify!(d_namlen) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_dirent_t>())).d_type as *const _ as usize }, + 20usize, + concat!( + "Offset of field: ", + stringify!(__wasi_dirent_t), + "::", + stringify!(d_type) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct __wasi_event_t { + pub userdata: __wasi_userdata_t, + pub error: __wasi_errno_t, + pub type_: __wasi_eventtype_t, + pub __bindgen_padding_0: u32, + pub __bindgen_anon_1: __wasi_event_t__bindgen_ty_1, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct __wasi_prestat_t { + pub pr_type: __wasi_preopentype_t, + pub u: __wasi_prestat_t___wasi_prestat_u, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union __wasi_prestat_t___wasi_prestat_u { + pub dir: __wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t { + pub pr_name_len: size_t, +} +#[test] +fn bindgen_test_layout___wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t>(), + 4usize, + concat!( + "Size of: ", + stringify!(__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t) + ) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t>(), + 4usize, + concat!( + "Alignment of ", + stringify!(__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t>())) + .pr_name_len as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_prestat_t___wasi_prestat_u___wasi_prestat_u_dir_t), + "::", + stringify!(pr_name_len) + ) + ); +} +#[test] +fn bindgen_test_layout___wasi_prestat_t___wasi_prestat_u() { + assert_eq!( + ::std::mem::size_of::<__wasi_prestat_t___wasi_prestat_u>(), + 4usize, + concat!("Size of: ", stringify!(__wasi_prestat_t___wasi_prestat_u)) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_prestat_t___wasi_prestat_u>(), + 4usize, + concat!( + "Alignment of ", + stringify!(__wasi_prestat_t___wasi_prestat_u) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_prestat_t___wasi_prestat_u>())).dir as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_prestat_t___wasi_prestat_u), + "::", + stringify!(dir) + ) + ); +} +#[test] +fn bindgen_test_layout___wasi_prestat_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_prestat_t>(), + 8usize, + concat!("Size of: ", stringify!(__wasi_prestat_t)) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_prestat_t>(), + 4usize, + concat!("Alignment of ", stringify!(__wasi_prestat_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_prestat_t>())).pr_type as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_prestat_t), + "::", + stringify!(pr_type) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_prestat_t>())).u as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(__wasi_prestat_t), + "::", + stringify!(u) + ) + ); +} +#[allow(non_snake_case)] +#[repr(C)] +#[derive(Copy, Clone)] +pub union __wasi_event_t__bindgen_ty_1 { + pub fd_readwrite: __wasi_event_t__bindgen_ty_1__bindgen_ty_1, + _bindgen_union_align: [u64; 2usize], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_event_t__bindgen_ty_1__bindgen_ty_1 { + pub nbytes: __wasi_filesize_t, + pub flags: __wasi_eventrwflags_t, + pub __bindgen_padding_0: [u16; 3usize], +} +#[allow(non_snake_case)] +#[test] +fn bindgen_test_layout_wasi_event_t__bindgen_ty_1__bindgen_ty_1() { + assert_eq!( + ::std::mem::size_of::<__wasi_event_t__bindgen_ty_1__bindgen_ty_1>(), + 16usize, + concat!( + "Size of: ", + stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_1) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_event_t__bindgen_ty_1__bindgen_ty_1>())).nbytes + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(nbytes) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_event_t__bindgen_ty_1__bindgen_ty_1>())).flags as *const _ + as usize + }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(flags) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_event_t__bindgen_ty_1__bindgen_ty_2 { + pub signal: __wasi_signal_t, + pub exitcode: __wasi_exitcode_t, +} +#[allow(non_snake_case)] +#[test] +fn bindgen_test_layout_wasi_event_t__bindgen_ty_1__bindgen_ty_2() { + assert_eq!( + ::std::mem::size_of::<__wasi_event_t__bindgen_ty_1__bindgen_ty_2>(), + 8usize, + concat!( + "Size of: ", + stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_2) + ) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_event_t__bindgen_ty_1__bindgen_ty_2>(), + 4usize, + concat!( + "Alignment of ", + stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_2) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_event_t__bindgen_ty_1__bindgen_ty_2>())).signal + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_2), + "::", + stringify!(signal) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_event_t__bindgen_ty_1__bindgen_ty_2>())).exitcode + as *const _ as usize + }, + 4usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t__bindgen_ty_1__bindgen_ty_2), + "::", + stringify!(exitcode) + ) + ); +} +#[allow(non_snake_case)] +#[test] +fn bindgen_test_layout_wasi_event_t__bindgen_ty_1() { + assert_eq!( + ::std::mem::size_of::<__wasi_event_t__bindgen_ty_1>(), + 16usize, + concat!("Size of: ", stringify!(__wasi_event_t__bindgen_ty_1)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_event_t__bindgen_ty_1>())).fd_readwrite as *const _ + as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t__bindgen_ty_1), + "::", + stringify!(fd_readwrite) + ) + ); +} +#[test] +fn bindgen_test_layout_wasi_event_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_event_t>(), + 32usize, + concat!("Size of: ", stringify!(__wasi_event_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_event_t>())).userdata as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t), + "::", + stringify!(userdata) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_event_t>())).error as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t), + "::", + stringify!(error) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_event_t>())).type_ as *const _ as usize }, + 10usize, + concat!( + "Offset of field: ", + stringify!(__wasi_event_t), + "::", + stringify!(type_) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_fdstat_t { + pub fs_filetype: __wasi_filetype_t, + pub fs_flags: __wasi_fdflags_t, + pub __bindgen_padding_0: u32, + pub fs_rights_base: __wasi_rights_t, + pub fs_rights_inheriting: __wasi_rights_t, +} +#[test] +fn bindgen_test_layout_wasi_fdstat_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_fdstat_t>(), + 24usize, + concat!("Size of: ", stringify!(__wasi_fdstat_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_fdstat_t>())).fs_filetype as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_fdstat_t), + "::", + stringify!(fs_filetype) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_fdstat_t>())).fs_flags as *const _ as usize }, + 2usize, + concat!( + "Offset of field: ", + stringify!(__wasi_fdstat_t), + "::", + stringify!(fs_flags) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_fdstat_t>())).fs_rights_base as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_fdstat_t), + "::", + stringify!(fs_rights_base) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_fdstat_t>())).fs_rights_inheriting as *const _ as usize + }, + 16usize, + concat!( + "Offset of field: ", + stringify!(__wasi_fdstat_t), + "::", + stringify!(fs_rights_inheriting) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_filestat_t { + pub st_dev: __wasi_device_t, + pub st_ino: __wasi_inode_t, + pub st_filetype: __wasi_filetype_t, + pub st_nlink: __wasi_linkcount_t, + pub st_size: __wasi_filesize_t, + pub st_atim: __wasi_timestamp_t, + pub st_mtim: __wasi_timestamp_t, + pub st_ctim: __wasi_timestamp_t, +} +#[test] +fn bindgen_test_layout_wasi_filestat_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_filestat_t>(), + 56usize, + concat!("Size of: ", stringify!(__wasi_filestat_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_dev as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_dev) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_ino as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_ino) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_filetype as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_filetype) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_nlink as *const _ as usize }, + 20usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_nlink) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_size as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_size) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_atim as *const _ as usize }, + 32usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_atim) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_mtim as *const _ as usize }, + 40usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_mtim) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_filestat_t>())).st_ctim as *const _ as usize }, + 48usize, + concat!( + "Offset of field: ", + stringify!(__wasi_filestat_t), + "::", + stringify!(st_ctim) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_ciovec_t { + pub buf: uintptr_t, // *const ::std::os::raw::c_void + pub buf_len: size_t, +} +#[test] +fn bindgen_test_layout_wasi_ciovec_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_ciovec_t>(), + 8usize, + concat!("Size of: ", stringify!(__wasi_ciovec_t)) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_ciovec_t>(), + 4usize, + concat!("Alignment of ", stringify!(__wasi_ciovec_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_ciovec_t>())).buf as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_ciovec_t), + "::", + stringify!(buf) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_ciovec_t>())).buf_len as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(__wasi_ciovec_t), + "::", + stringify!(buf_len) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_iovec_t { + pub buf: uintptr_t, // *mut ::std::os::raw::c_void + pub buf_len: size_t, +} +#[test] +fn bindgen_test_layout_wasi_iovec_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_iovec_t>(), + 8usize, + concat!("Size of: ", stringify!(__wasi_iovec_t)) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_iovec_t>(), + 4usize, + concat!("Alignment of ", stringify!(__wasi_iovec_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_iovec_t>())).buf as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_iovec_t), + "::", + stringify!(buf) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_iovec_t>())).buf_len as *const _ as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(__wasi_iovec_t), + "::", + stringify!(buf_len) + ) + ); +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct __wasi_subscription_t { + pub userdata: __wasi_userdata_t, + pub type_: __wasi_eventtype_t, + pub __bindgen_padding_0: u32, + pub __bindgen_anon_1: __wasi_subscription_t__bindgen_ty_1, +} +#[repr(C)] +#[derive(Copy, Clone)] +pub union __wasi_subscription_t__bindgen_ty_1 { + pub clock: __wasi_subscription_t__bindgen_ty_1__bindgen_ty_1, + pub fd_readwrite: __wasi_subscription_t__bindgen_ty_1__bindgen_ty_3, + _bindgen_union_align: [u64; 5usize], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_subscription_t__bindgen_ty_1__bindgen_ty_1 { + pub identifier: __wasi_userdata_t, + pub clock_id: __wasi_clockid_t, + pub __bindgen_padding_0: u32, + pub timeout: __wasi_timestamp_t, + pub precision: __wasi_timestamp_t, + pub flags: __wasi_subclockflags_t, + pub __bindgen_padding_1: [u16; 3usize], +} +#[allow(non_snake_case)] +#[test] +fn bindgen_test_layout_wasi_subscription_t__bindgen_ty_1__bindgen_ty_1() { + assert_eq!( + ::std::mem::size_of::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1>(), + 40usize, + concat!( + "Size of: ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1>())).identifier + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(identifier) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1>())).clock_id + as *const _ as usize + }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(clock_id) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1>())).timeout + as *const _ as usize + }, + 16usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(timeout) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1>())).precision + as *const _ as usize + }, + 24usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(precision) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1>())).flags + as *const _ as usize + }, + 32usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(flags) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_subscription_t__bindgen_ty_1__bindgen_ty_3 { + pub fd: __wasi_fd_t, +} +#[allow(non_snake_case)] +#[test] +fn bindgen_test_layout_wasi_subscription_t__bindgen_ty_1__bindgen_ty_3() { + assert_eq!( + ::std::mem::size_of::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_3>(), + 4usize, + concat!( + "Size of: ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_3) + ) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_3>(), + 4usize, + concat!( + "Alignment of ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_3) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_3>())).fd + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_3), + "::", + stringify!(fd) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __wasi_subscription_t__bindgen_ty_1__bindgen_ty_5 { + pub fd: __wasi_fd_t, +} +#[allow(non_snake_case)] +#[test] +fn bindgen_test_layout_wasi_subscription_t__bindgen_ty_1__bindgen_ty_5() { + assert_eq!( + ::std::mem::size_of::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_5>(), + 4usize, + concat!( + "Size of: ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_5) + ) + ); + assert_eq!( + ::std::mem::align_of::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_5>(), + 4usize, + concat!( + "Alignment of ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_5) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_subscription_t__bindgen_ty_1__bindgen_ty_5>())).fd + as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t__bindgen_ty_1__bindgen_ty_5), + "::", + stringify!(fd) + ) + ); +} +#[allow(non_snake_case)] +#[test] +fn bindgen_test_layout_wasi_subscription_t__bindgen_ty_1() { + assert_eq!( + ::std::mem::size_of::<__wasi_subscription_t__bindgen_ty_1>(), + 40usize, + concat!("Size of: ", stringify!(__wasi_subscription_t__bindgen_ty_1)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_subscription_t__bindgen_ty_1>())).clock as *const _ + as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t__bindgen_ty_1), + "::", + stringify!(clock) + ) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::<__wasi_subscription_t__bindgen_ty_1>())).fd_readwrite as *const _ + as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t__bindgen_ty_1), + "::", + stringify!(fd_readwrite) + ) + ); +} +#[allow(non_snake_case)] +#[test] +fn bindgen_test_layout_wasi_subscription_t() { + assert_eq!( + ::std::mem::size_of::<__wasi_subscription_t>(), + 56usize, + concat!("Size of: ", stringify!(__wasi_subscription_t)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_subscription_t>())).userdata as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t), + "::", + stringify!(userdata) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<__wasi_subscription_t>())).type_ as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(__wasi_subscription_t), + "::", + stringify!(type_) + ) + ); +} + +pub fn strerror(errno: __wasi_errno_t) -> &'static str { + match errno { + __WASI_ESUCCESS => "__WASI_ESUCCESS", + __WASI_E2BIG => "__WASI_E2BIG", + __WASI_EACCES => "__WASI_EACCES", + __WASI_EADDRINUSE => "__WASI_EADDRINUSE", + __WASI_EADDRNOTAVAIL => "__WASI_EADDRNOTAVAIL", + __WASI_EAFNOSUPPORT => "__WASI_EAFNOSUPPORT", + __WASI_EAGAIN => "__WASI_EAGAIN", + __WASI_EALREADY => "__WASI_EALREADY", + __WASI_EBADF => "__WASI_EBADF", + __WASI_EBADMSG => "__WASI_EBADMSG", + __WASI_EBUSY => "__WASI_EBUSY", + __WASI_ECANCELED => "__WASI_ECANCELED", + __WASI_ECHILD => "__WASI_ECHILD", + __WASI_ECONNABORTED => "__WASI_ECONNABORTED", + __WASI_ECONNREFUSED => "__WASI_ECONNREFUSED", + __WASI_ECONNRESET => "__WASI_ECONNRESET", + __WASI_EDEADLK => "__WASI_EDEADLK", + __WASI_EDESTADDRREQ => "__WASI_EDESTADDRREQ", + __WASI_EDOM => "__WASI_EDOM", + __WASI_EDQUOT => "__WASI_EDQUOT", + __WASI_EEXIST => "__WASI_EEXIST", + __WASI_EFAULT => "__WASI_EFAULT", + __WASI_EFBIG => "__WASI_EFBIG", + __WASI_EHOSTUNREACH => "__WASI_EHOSTUNREACH", + __WASI_EIDRM => "__WASI_EIDRM", + __WASI_EILSEQ => "__WASI_EILSEQ", + __WASI_EINPROGRESS => "__WASI_EINPROGRESS", + __WASI_EINTR => "__WASI_EINTR", + __WASI_EINVAL => "__WASI_EINVAL", + __WASI_EIO => "__WASI_EIO", + __WASI_EISCONN => "__WASI_EISCONN", + __WASI_EISDIR => "__WASI_EISDIR", + __WASI_ELOOP => "__WASI_ELOOP", + __WASI_EMFILE => "__WASI_EMFILE", + __WASI_EMLINK => "__WASI_EMLINK", + __WASI_EMSGSIZE => "__WASI_EMSGSIZE", + __WASI_EMULTIHOP => "__WASI_EMULTIHOP", + __WASI_ENAMETOOLONG => "__WASI_ENAMETOOLONG", + __WASI_ENETDOWN => "__WASI_ENETDOWN", + __WASI_ENETRESET => "__WASI_ENETRESET", + __WASI_ENETUNREACH => "__WASI_ENETUNREACH", + __WASI_ENFILE => "__WASI_ENFILE", + __WASI_ENOBUFS => "__WASI_ENOBUFS", + __WASI_ENODEV => "__WASI_ENODEV", + __WASI_ENOENT => "__WASI_ENOENT", + __WASI_ENOEXEC => "__WASI_ENOEXEC", + __WASI_ENOLCK => "__WASI_ENOLCK", + __WASI_ENOLINK => "__WASI_ENOLINK", + __WASI_ENOMEM => "__WASI_ENOMEM", + __WASI_ENOMSG => "__WASI_ENOMSG", + __WASI_ENOPROTOOPT => "__WASI_ENOPROTOOPT", + __WASI_ENOSPC => "__WASI_ENOSPC", + __WASI_ENOSYS => "__WASI_ENOSYS", + __WASI_ENOTCONN => "__WASI_ENOTCONN", + __WASI_ENOTDIR => "__WASI_ENOTDIR", + __WASI_ENOTEMPTY => "__WASI_ENOTEMPTY", + __WASI_ENOTRECOVERABLE => "__WASI_ENOTRECOVERABLE", + __WASI_ENOTSOCK => "__WASI_ENOTSOCK", + __WASI_ENOTSUP => "__WASI_ENOTSUP", + __WASI_ENOTTY => "__WASI_ENOTTY", + __WASI_ENXIO => "__WASI_ENXIO", + __WASI_EOVERFLOW => "__WASI_EOVERFLOW", + __WASI_EOWNERDEAD => "__WASI_EOWNERDEAD", + __WASI_EPERM => "__WASI_EPERM", + __WASI_EPIPE => "__WASI_EPIPE", + __WASI_EPROTO => "__WASI_EPROTO", + __WASI_EPROTONOSUPPORT => "__WASI_EPROTONOSUPPORT", + __WASI_EPROTOTYPE => "__WASI_EPROTOTYPE", + __WASI_ERANGE => "__WASI_ERANGE", + __WASI_EROFS => "__WASI_EROFS", + __WASI_ESPIPE => "__WASI_ESPIPE", + __WASI_ESRCH => "__WASI_ESRCH", + __WASI_ESTALE => "__WASI_ESTALE", + __WASI_ETIMEDOUT => "__WASI_ETIMEDOUT", + __WASI_ETXTBSY => "__WASI_ETXTBSY", + __WASI_EXDEV => "__WASI_EXDEV", + __WASI_ENOTCAPABLE => "__WASI_ENOTCAPABLE", + other => panic!("Undefined errno value {:?}", other), + } +} + +pub fn whence_to_str(whence: __wasi_whence_t) -> &'static str { + match whence { + __WASI_WHENCE_CUR => "__WASI_WHENCE_CUR", + __WASI_WHENCE_END => "__WASI_WHENCE_END", + __WASI_WHENCE_SET => "__WASI_WHENCE_SET", + other => panic!("Undefined whence value {:?}", other), + } +} + +// libc constants +pub const INT8_MIN: i32 = -128; +pub const INT16_MIN: i32 = -32768; +pub const INT32_MIN: i32 = -2147483648; +pub const INT8_MAX: u32 = 127; +pub const INT16_MAX: u32 = 32767; +pub const INT32_MAX: u32 = 2147483647; +pub const UINT8_MAX: u32 = 255; +pub const UINT16_MAX: u32 = 65535; +pub const UINT32_MAX: u32 = 4294967295; +pub const INT_LEAST8_MIN: i32 = -128; +pub const INT_LEAST16_MIN: i32 = -32768; +pub const INT_LEAST32_MIN: i32 = -2147483648; +pub const INT_LEAST8_MAX: u32 = 127; +pub const INT_LEAST16_MAX: u32 = 32767; +pub const INT_LEAST32_MAX: u32 = 2147483647; +pub const UINT_LEAST8_MAX: u32 = 255; +pub const UINT_LEAST16_MAX: u32 = 65535; +pub const UINT_LEAST32_MAX: u32 = 4294967295; +pub const INT_FAST8_MIN: i32 = -128; +pub const INT_FAST16_MIN: i32 = -2147483648; +pub const INT_FAST32_MIN: i32 = -2147483648; +pub const INT_FAST8_MAX: u32 = 127; +pub const INT_FAST16_MAX: u32 = 2147483647; +pub const INT_FAST32_MAX: u32 = 2147483647; +pub const UINT_FAST8_MAX: u32 = 255; +pub const UINT_FAST16_MAX: u32 = 4294967295; +pub const UINT_FAST32_MAX: u32 = 4294967295; +pub const INTPTR_MIN: i32 = -2147483648; +pub const INTPTR_MAX: u32 = 2147483647; +pub const UINTPTR_MAX: u32 = 4294967295; +pub const PTRDIFF_MIN: i32 = -2147483648; +pub const PTRDIFF_MAX: u32 = 2147483647; +pub const SIG_ATOMIC_MIN: i32 = -2147483648; +pub const SIG_ATOMIC_MAX: u32 = 2147483647; +pub const SIZE_MAX: u32 = 4294967295; +pub const WINT_MIN: i32 = -2147483648; +pub const WINT_MAX: i32 = 2147483647; + +// WASI constants +pub const __WASI_ADVICE_NORMAL: __wasi_advice_t = 0; +pub const __WASI_ADVICE_SEQUENTIAL: __wasi_advice_t = 1; +pub const __WASI_ADVICE_RANDOM: __wasi_advice_t = 2; +pub const __WASI_ADVICE_WILLNEED: __wasi_advice_t = 3; +pub const __WASI_ADVICE_DONTNEED: __wasi_advice_t = 4; +pub const __WASI_ADVICE_NOREUSE: __wasi_advice_t = 5; +pub const __WASI_CLOCK_REALTIME: __wasi_clockid_t = 0; +pub const __WASI_CLOCK_MONOTONIC: __wasi_clockid_t = 1; +pub const __WASI_CLOCK_PROCESS_CPUTIME_ID: __wasi_clockid_t = 2; +pub const __WASI_CLOCK_THREAD_CPUTIME_ID: __wasi_clockid_t = 3; +pub const __WASI_DIRCOOKIE_START: __wasi_dircookie_t = 0; +pub const __WASI_ESUCCESS: __wasi_errno_t = 0; +pub const __WASI_E2BIG: __wasi_errno_t = 1; +pub const __WASI_EACCES: __wasi_errno_t = 2; +pub const __WASI_EADDRINUSE: __wasi_errno_t = 3; +pub const __WASI_EADDRNOTAVAIL: __wasi_errno_t = 4; +pub const __WASI_EAFNOSUPPORT: __wasi_errno_t = 5; +pub const __WASI_EAGAIN: __wasi_errno_t = 6; +pub const __WASI_EALREADY: __wasi_errno_t = 7; +pub const __WASI_EBADF: __wasi_errno_t = 8; +pub const __WASI_EBADMSG: __wasi_errno_t = 9; +pub const __WASI_EBUSY: __wasi_errno_t = 10; +pub const __WASI_ECANCELED: __wasi_errno_t = 11; +pub const __WASI_ECHILD: __wasi_errno_t = 12; +pub const __WASI_ECONNABORTED: __wasi_errno_t = 13; +pub const __WASI_ECONNREFUSED: __wasi_errno_t = 14; +pub const __WASI_ECONNRESET: __wasi_errno_t = 15; +pub const __WASI_EDEADLK: __wasi_errno_t = 16; +pub const __WASI_EDESTADDRREQ: __wasi_errno_t = 17; +pub const __WASI_EDOM: __wasi_errno_t = 18; +pub const __WASI_EDQUOT: __wasi_errno_t = 19; +pub const __WASI_EEXIST: __wasi_errno_t = 20; +pub const __WASI_EFAULT: __wasi_errno_t = 21; +pub const __WASI_EFBIG: __wasi_errno_t = 22; +pub const __WASI_EHOSTUNREACH: __wasi_errno_t = 23; +pub const __WASI_EIDRM: __wasi_errno_t = 24; +pub const __WASI_EILSEQ: __wasi_errno_t = 25; +pub const __WASI_EINPROGRESS: __wasi_errno_t = 26; +pub const __WASI_EINTR: __wasi_errno_t = 27; +pub const __WASI_EINVAL: __wasi_errno_t = 28; +pub const __WASI_EIO: __wasi_errno_t = 29; +pub const __WASI_EISCONN: __wasi_errno_t = 30; +pub const __WASI_EISDIR: __wasi_errno_t = 31; +pub const __WASI_ELOOP: __wasi_errno_t = 32; +pub const __WASI_EMFILE: __wasi_errno_t = 33; +pub const __WASI_EMLINK: __wasi_errno_t = 34; +pub const __WASI_EMSGSIZE: __wasi_errno_t = 35; +pub const __WASI_EMULTIHOP: __wasi_errno_t = 36; +pub const __WASI_ENAMETOOLONG: __wasi_errno_t = 37; +pub const __WASI_ENETDOWN: __wasi_errno_t = 38; +pub const __WASI_ENETRESET: __wasi_errno_t = 39; +pub const __WASI_ENETUNREACH: __wasi_errno_t = 40; +pub const __WASI_ENFILE: __wasi_errno_t = 41; +pub const __WASI_ENOBUFS: __wasi_errno_t = 42; +pub const __WASI_ENODEV: __wasi_errno_t = 43; +pub const __WASI_ENOENT: __wasi_errno_t = 44; +pub const __WASI_ENOEXEC: __wasi_errno_t = 45; +pub const __WASI_ENOLCK: __wasi_errno_t = 46; +pub const __WASI_ENOLINK: __wasi_errno_t = 47; +pub const __WASI_ENOMEM: __wasi_errno_t = 48; +pub const __WASI_ENOMSG: __wasi_errno_t = 49; +pub const __WASI_ENOPROTOOPT: __wasi_errno_t = 50; +pub const __WASI_ENOSPC: __wasi_errno_t = 51; +pub const __WASI_ENOSYS: __wasi_errno_t = 52; +pub const __WASI_ENOTCONN: __wasi_errno_t = 53; +pub const __WASI_ENOTDIR: __wasi_errno_t = 54; +pub const __WASI_ENOTEMPTY: __wasi_errno_t = 55; +pub const __WASI_ENOTRECOVERABLE: __wasi_errno_t = 56; +pub const __WASI_ENOTSOCK: __wasi_errno_t = 57; +pub const __WASI_ENOTSUP: __wasi_errno_t = 58; +pub const __WASI_ENOTTY: __wasi_errno_t = 59; +pub const __WASI_ENXIO: __wasi_errno_t = 60; +pub const __WASI_EOVERFLOW: __wasi_errno_t = 61; +pub const __WASI_EOWNERDEAD: __wasi_errno_t = 62; +pub const __WASI_EPERM: __wasi_errno_t = 63; +pub const __WASI_EPIPE: __wasi_errno_t = 64; +pub const __WASI_EPROTO: __wasi_errno_t = 65; +pub const __WASI_EPROTONOSUPPORT: __wasi_errno_t = 66; +pub const __WASI_EPROTOTYPE: __wasi_errno_t = 67; +pub const __WASI_ERANGE: __wasi_errno_t = 68; +pub const __WASI_EROFS: __wasi_errno_t = 69; +pub const __WASI_ESPIPE: __wasi_errno_t = 70; +pub const __WASI_ESRCH: __wasi_errno_t = 71; +pub const __WASI_ESTALE: __wasi_errno_t = 72; +pub const __WASI_ETIMEDOUT: __wasi_errno_t = 73; +pub const __WASI_ETXTBSY: __wasi_errno_t = 74; +pub const __WASI_EXDEV: __wasi_errno_t = 75; +pub const __WASI_ENOTCAPABLE: __wasi_errno_t = 76; +pub const __WASI_EVENT_FD_READWRITE_HANGUP: __wasi_eventrwflags_t = 1; +pub const __WASI_EVENTTYPE_CLOCK: __wasi_eventtype_t = 0; +pub const __WASI_EVENTTYPE_FD_READ: __wasi_eventtype_t = 1; +pub const __WASI_EVENTTYPE_FD_WRITE: __wasi_eventtype_t = 2; +pub const __WASI_FDFLAG_APPEND: __wasi_fdflags_t = 1; +pub const __WASI_FDFLAG_DSYNC: __wasi_fdflags_t = 2; +pub const __WASI_FDFLAG_NONBLOCK: __wasi_fdflags_t = 4; +pub const __WASI_FDFLAG_RSYNC: __wasi_fdflags_t = 8; +pub const __WASI_FDFLAG_SYNC: __wasi_fdflags_t = 16; +pub const __WASI_PREOPENTYPE_DIR: __wasi_preopentype_t = 0; +pub const __WASI_FILETYPE_UNKNOWN: __wasi_filetype_t = 0; +pub const __WASI_FILETYPE_BLOCK_DEVICE: __wasi_filetype_t = 1; +pub const __WASI_FILETYPE_CHARACTER_DEVICE: __wasi_filetype_t = 2; +pub const __WASI_FILETYPE_DIRECTORY: __wasi_filetype_t = 3; +pub const __WASI_FILETYPE_REGULAR_FILE: __wasi_filetype_t = 4; +pub const __WASI_FILETYPE_SOCKET_DGRAM: __wasi_filetype_t = 5; +pub const __WASI_FILETYPE_SOCKET_STREAM: __wasi_filetype_t = 6; +pub const __WASI_FILETYPE_SYMBOLIC_LINK: __wasi_filetype_t = 7; +pub const __WASI_FILESTAT_SET_ATIM: __wasi_fstflags_t = 1; +pub const __WASI_FILESTAT_SET_ATIM_NOW: __wasi_fstflags_t = 2; +pub const __WASI_FILESTAT_SET_MTIM: __wasi_fstflags_t = 4; +pub const __WASI_FILESTAT_SET_MTIM_NOW: __wasi_fstflags_t = 8; +pub const __WASI_LOOKUP_SYMLINK_FOLLOW: __wasi_lookupflags_t = 1; +pub const __WASI_O_CREAT: __wasi_oflags_t = 1; +pub const __WASI_O_DIRECTORY: __wasi_oflags_t = 2; +pub const __WASI_O_EXCL: __wasi_oflags_t = 4; +pub const __WASI_O_TRUNC: __wasi_oflags_t = 8; +pub const __WASI_SOCK_RECV_PEEK: __wasi_riflags_t = 1; +pub const __WASI_SOCK_RECV_WAITALL: __wasi_riflags_t = 2; +pub const __WASI_RIGHT_FD_DATASYNC: __wasi_rights_t = 1; +pub const __WASI_RIGHT_FD_READ: __wasi_rights_t = 2; +pub const __WASI_RIGHT_FD_SEEK: __wasi_rights_t = 4; +pub const __WASI_RIGHT_FD_FDSTAT_SET_FLAGS: __wasi_rights_t = 8; +pub const __WASI_RIGHT_FD_SYNC: __wasi_rights_t = 16; +pub const __WASI_RIGHT_FD_TELL: __wasi_rights_t = 32; +pub const __WASI_RIGHT_FD_WRITE: __wasi_rights_t = 64; +pub const __WASI_RIGHT_FD_ADVISE: __wasi_rights_t = 128; +pub const __WASI_RIGHT_FD_ALLOCATE: __wasi_rights_t = 256; +pub const __WASI_RIGHT_PATH_CREATE_DIRECTORY: __wasi_rights_t = 512; +pub const __WASI_RIGHT_PATH_CREATE_FILE: __wasi_rights_t = 1024; +pub const __WASI_RIGHT_PATH_LINK_SOURCE: __wasi_rights_t = 2048; +pub const __WASI_RIGHT_PATH_LINK_TARGET: __wasi_rights_t = 4096; +pub const __WASI_RIGHT_PATH_OPEN: __wasi_rights_t = 8192; +pub const __WASI_RIGHT_FD_READDIR: __wasi_rights_t = 16384; +pub const __WASI_RIGHT_PATH_READLINK: __wasi_rights_t = 32768; +pub const __WASI_RIGHT_PATH_RENAME_SOURCE: __wasi_rights_t = 65536; +pub const __WASI_RIGHT_PATH_RENAME_TARGET: __wasi_rights_t = 131072; +pub const __WASI_RIGHT_PATH_FILESTAT_GET: __wasi_rights_t = 262144; +pub const __WASI_RIGHT_PATH_FILESTAT_SET_SIZE: __wasi_rights_t = 524288; +pub const __WASI_RIGHT_PATH_FILESTAT_SET_TIMES: __wasi_rights_t = 1048576; +pub const __WASI_RIGHT_FD_FILESTAT_GET: __wasi_rights_t = 2097152; +pub const __WASI_RIGHT_FD_FILESTAT_SET_SIZE: __wasi_rights_t = 4194304; +pub const __WASI_RIGHT_FD_FILESTAT_SET_TIMES: __wasi_rights_t = 8388608; +pub const __WASI_RIGHT_PATH_SYMLINK: __wasi_rights_t = 16777216; +pub const __WASI_RIGHT_PATH_REMOVE_DIRECTORY: __wasi_rights_t = 33554432; +pub const __WASI_RIGHT_PATH_UNLINK_FILE: __wasi_rights_t = 67108864; +pub const __WASI_RIGHT_POLL_FD_READWRITE: __wasi_rights_t = 134217728; +pub const __WASI_RIGHT_SOCK_SHUTDOWN: __wasi_rights_t = 268435456; +pub const __WASI_SOCK_RECV_DATA_TRUNCATED: __wasi_roflags_t = 1; +pub const __WASI_SHUT_RD: __wasi_sdflags_t = 1; +pub const __WASI_SHUT_WR: __wasi_sdflags_t = 2; +pub const __WASI_SIGHUP: __wasi_signal_t = 1; +pub const __WASI_SIGINT: __wasi_signal_t = 2; +pub const __WASI_SIGQUIT: __wasi_signal_t = 3; +pub const __WASI_SIGILL: __wasi_signal_t = 4; +pub const __WASI_SIGTRAP: __wasi_signal_t = 5; +pub const __WASI_SIGABRT: __wasi_signal_t = 6; +pub const __WASI_SIGBUS: __wasi_signal_t = 7; +pub const __WASI_SIGFPE: __wasi_signal_t = 8; +pub const __WASI_SIGKILL: __wasi_signal_t = 9; +pub const __WASI_SIGUSR1: __wasi_signal_t = 10; +pub const __WASI_SIGSEGV: __wasi_signal_t = 11; +pub const __WASI_SIGUSR2: __wasi_signal_t = 12; +pub const __WASI_SIGPIPE: __wasi_signal_t = 13; +pub const __WASI_SIGALRM: __wasi_signal_t = 14; +pub const __WASI_SIGTERM: __wasi_signal_t = 15; +pub const __WASI_SIGCHLD: __wasi_signal_t = 16; +pub const __WASI_SIGCONT: __wasi_signal_t = 17; +pub const __WASI_SIGSTOP: __wasi_signal_t = 18; +pub const __WASI_SIGTSTP: __wasi_signal_t = 19; +pub const __WASI_SIGTTIN: __wasi_signal_t = 20; +pub const __WASI_SIGTTOU: __wasi_signal_t = 21; +pub const __WASI_SIGURG: __wasi_signal_t = 22; +pub const __WASI_SIGXCPU: __wasi_signal_t = 23; +pub const __WASI_SIGXFSZ: __wasi_signal_t = 24; +pub const __WASI_SIGVTALRM: __wasi_signal_t = 25; +pub const __WASI_SIGPROF: __wasi_signal_t = 26; +pub const __WASI_SIGWINCH: __wasi_signal_t = 27; +pub const __WASI_SIGPOLL: __wasi_signal_t = 28; +pub const __WASI_SIGPWR: __wasi_signal_t = 29; +pub const __WASI_SIGSYS: __wasi_signal_t = 30; +pub const __WASI_SUBSCRIPTION_CLOCK_ABSTIME: __wasi_subclockflags_t = 1; +pub const __WASI_WHENCE_CUR: __wasi_whence_t = 0; +pub const __WASI_WHENCE_END: __wasi_whence_t = 1; +pub const __WASI_WHENCE_SET: __wasi_whence_t = 2;