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.
[](https://gitter.im/CraneStation/Lobby)

-*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.
+
+
+
+## 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...
+
+
+
+
+
+
+
+
+ {{{ 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;