You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1199 lines
39 KiB

/* TODO: Validate header usage */
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <math.h>
#include <printf.h>
#include <setjmp.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdnoreturn.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <sysexits.h>
#include "current_sandbox.h"
#include "sandbox_types.h"
#include "wasi.h"
/* Return abstract handle */
void *
wasi_context_init(wasi_options_t *options)
{
/* TODO: Add default types */
assert(options != NULL);
wasi_context_t *wasi_context = (wasi_context_t *)calloc(1, sizeof(wasi_context_t));
if (options->argc > 0) {
assert(options->argv != NULL);
/* Calculate argument buffer size */
__wasi_size_t argv_buf_size = 0;
__wasi_size_t argv_buffer_offsets[options->argc + 1];
for (int i = 0; i < options->argc; i++) {
argv_buffer_offsets[i] = argv_buf_size;
argv_buf_size += strlen(options->argv[i]) + 1;
}
argv_buffer_offsets[options->argc] = argv_buf_size;
/* Allocate and copy argument sizes and offsets*/
wasi_context->argc = options->argc;
wasi_context->argv = calloc(options->argc + 1, sizeof(char *));
if (wasi_context->argv == NULL) {
fprintf(stderr, "Error allocating argv: %s", strerror(errno));
exit(EXIT_FAILURE);
}
wasi_context->argv_buf_size = argv_buf_size;
wasi_context->argv_buf = calloc(argv_buf_size, sizeof(char));
if (wasi_context->argv_buf == NULL) {
fprintf(stderr, "Error allocating argv_buf: %s", strerror(errno));
exit(EXIT_FAILURE);
}
/* Copy the binary name minux the path as the first arg */
for (int i = 0; i < options->argc; i++) {
strncpy(&wasi_context->argv_buf[argv_buffer_offsets[i]], options->argv[i],
argv_buffer_offsets[i + 1] - argv_buffer_offsets[i]);
}
/* Write argv pointers from argv_buffer_offsets */
for (int i = 0; i < options->argc; i++) {
wasi_context->argv[i] = &(wasi_context->argv_buf[argv_buffer_offsets[i]]);
}
} else {
wasi_context->argc = 0;
wasi_context->argv = NULL;
wasi_context->argv_buf_size = 0;
wasi_context->argv_buf = NULL;
}
/* Calculate env sizes */
__wasi_size_t envc = 0;
__wasi_size_t env_buf_size = 0;
if (options->envp != NULL) {
for (char **environ_cursor = (char **)options->envp; *environ_cursor != NULL; environ_cursor++) {
envc++;
env_buf_size += strlen(*environ_cursor) + 1;
}
}
wasi_context->envc = envc;
if (envc > 0) {
/* Allocate env and env_buf */
wasi_context->env = (char **)calloc(envc, sizeof(char **));
if (wasi_context->env == NULL) {
fprintf(stderr, "Error allocating env: %s", strerror(errno));
exit(EXIT_FAILURE);
}
wasi_context->env_buf_size = env_buf_size;
wasi_context->env_buf = (char *)calloc(env_buf_size, sizeof(char));
if (wasi_context->env_buf == NULL) {
fprintf(stderr, "Error allocating env_buf: %s", strerror(errno));
exit(EXIT_FAILURE);
}
/* Write env and env_buf */
__wasi_size_t env_buf_written = 0;
for (int i = 0; i < envc; i++) {
wasi_context->env[i] = &(wasi_context->env_buf[env_buf_written]);
strcpy(wasi_context->env[i], options->envp[i]);
env_buf_written += (strlen(options->envp[i]) + 1);
}
}
/* Seed Random */
srandom(time(NULL));
/* TODO: Preopens */
return wasi_context;
}
void
wasi_context_destroy(wasi_context_t *context)
{
free(context->argv);
free(context->argv_buf);
free(context->env);
free(context->env_buf);
free(context);
}
/* WASI API implementations */
/**
* @brief Converts POSIX status codes to WASI
*
* @param errno_
* @return wasi_errno_t
*/
static __wasi_errno_t
wasi_fromerrno(int errno_)
{
switch (errno_) {
case 0:
return __WASI_ERRNO_SUCCESS;
case E2BIG:
return __WASI_ERRNO_2BIG;
case EACCES:
return __WASI_ERRNO_ACCES;
case EADDRINUSE:
return __WASI_ERRNO_ADDRINUSE;
case EADDRNOTAVAIL:
return __WASI_ERRNO_ADDRNOTAVAIL;
case EAFNOSUPPORT:
return __WASI_ERRNO_AFNOSUPPORT;
case EAGAIN:
return __WASI_ERRNO_AGAIN;
case EALREADY:
return __WASI_ERRNO_ALREADY;
case EBADF:
return __WASI_ERRNO_BADF;
case EBADMSG:
return __WASI_ERRNO_BADMSG;
case EBUSY:
return __WASI_ERRNO_BUSY;
case ECANCELED:
return __WASI_ERRNO_CANCELED;
case ECHILD:
return __WASI_ERRNO_CHILD;
case ECONNABORTED:
return __WASI_ERRNO_CONNABORTED;
case ECONNREFUSED:
return __WASI_ERRNO_CONNREFUSED;
case ECONNRESET:
return __WASI_ERRNO_CONNRESET;
case EDEADLK:
return __WASI_ERRNO_DEADLK;
case EDESTADDRREQ:
return __WASI_ERRNO_DESTADDRREQ;
case EDOM:
return __WASI_ERRNO_DOM;
case EDQUOT:
return __WASI_ERRNO_DQUOT;
case EEXIST:
return __WASI_ERRNO_EXIST;
case EFAULT:
return __WASI_ERRNO_FAULT;
case EFBIG:
return __WASI_ERRNO_FBIG;
case EHOSTUNREACH:
return __WASI_ERRNO_HOSTUNREACH;
case EIDRM:
return __WASI_ERRNO_IDRM;
case EILSEQ:
return __WASI_ERRNO_ILSEQ;
case EINPROGRESS:
return __WASI_ERRNO_INPROGRESS;
case EINTR:
return __WASI_ERRNO_INTR;
case EINVAL:
return __WASI_ERRNO_INVAL;
case EIO:
return __WASI_ERRNO_IO;
case EISCONN:
return __WASI_ERRNO_ISCONN;
case EISDIR:
return __WASI_ERRNO_ISDIR;
case ELOOP:
return __WASI_ERRNO_LOOP;
case EMFILE:
return __WASI_ERRNO_MFILE;
case EMLINK:
return __WASI_ERRNO_MLINK;
case EMSGSIZE:
return __WASI_ERRNO_MSGSIZE;
case EMULTIHOP:
return __WASI_ERRNO_MULTIHOP;
case ENAMETOOLONG:
return __WASI_ERRNO_NAMETOOLONG;
case ENETDOWN:
return __WASI_ERRNO_NETDOWN;
case ENETRESET:
return __WASI_ERRNO_NETRESET;
case ENETUNREACH:
return __WASI_ERRNO_NETUNREACH;
case ENFILE:
return __WASI_ERRNO_NFILE;
case ENOBUFS:
return __WASI_ERRNO_NOBUFS;
case ENODEV:
return __WASI_ERRNO_NODEV;
case ENOENT:
return __WASI_ERRNO_NOENT;
case ENOEXEC:
return __WASI_ERRNO_NOEXEC;
case ENOLCK:
return __WASI_ERRNO_NOLCK;
case ENOLINK:
return __WASI_ERRNO_NOLINK;
case ENOMEM:
return __WASI_ERRNO_NOMEM;
case ENOMSG:
return __WASI_ERRNO_NOMSG;
case ENOPROTOOPT:
return __WASI_ERRNO_NOPROTOOPT;
case ENOSPC:
return __WASI_ERRNO_NOSPC;
case ENOSYS:
return __WASI_ERRNO_NOSYS;
case ENOTCONN:
return __WASI_ERRNO_NOTCONN;
case ENOTDIR:
return __WASI_ERRNO_NOTDIR;
case ENOTEMPTY:
return __WASI_ERRNO_NOTEMPTY;
case ENOTRECOVERABLE:
return __WASI_ERRNO_NOTRECOVERABLE;
case ENOTSOCK:
return __WASI_ERRNO_NOTSOCK;
case ENOTSUP:
return __WASI_ERRNO_NOTSUP;
case ENOTTY:
return __WASI_ERRNO_NOTTY;
case ENXIO:
return __WASI_ERRNO_NXIO;
case EOVERFLOW:
return __WASI_ERRNO_OVERFLOW;
case EOWNERDEAD:
return __WASI_ERRNO_OWNERDEAD;
case EPERM:
return __WASI_ERRNO_PERM;
case EPIPE:
return __WASI_ERRNO_PIPE;
case EPROTO:
return __WASI_ERRNO_PROTO;
case EPROTONOSUPPORT:
return __WASI_ERRNO_PROTONOSUPPORT;
case EPROTOTYPE:
return __WASI_ERRNO_PROTOTYPE;
case ERANGE:
return __WASI_ERRNO_RANGE;
case EROFS:
return __WASI_ERRNO_ROFS;
case ESPIPE:
return __WASI_ERRNO_SPIPE;
case ESRCH:
return __WASI_ERRNO_SRCH;
case ESTALE:
return __WASI_ERRNO_STALE;
case ETIMEDOUT:
return __WASI_ERRNO_TIMEDOUT;
case ETXTBSY:
return __WASI_ERRNO_TXTBSY;
case EXDEV:
return __WASI_ERRNO_XDEV;
default:
fprintf(stderr, "wasi_fromerrno unexpectedly received: %s\n", strerror(errno_));
fflush(stderr);
}
assert(0);
return 0;
}
/**
* @brief Writes argument offsets and buffer into linear memory
* Callers of this syscall only provide the base address of the two buffers because the WASI specification
* assumes that the caller first called args_sizes_get and sized the buffers appropriately.
*
* @param argv - temp argv to store host pointers into sandbox linear memory
* @param argv_buf_retptr - host pointer to the start of the argv buffer in linear memory
* @return __WASI_ERRNO_SUCCESS or WASI_EINVAL
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_args_get(wasi_context_t *context, char **argv, char *argv_buf)
{
if (context == NULL || argv == NULL || argv_buf == NULL) return __WASI_ERRNO_INVAL;
if (context->argc > 0) memcpy(argv_buf, context->argv_buf, context->argv_buf_size);
for (__wasi_size_t i = 0; i < context->argc; i++) {
size_t offset = context->argv[i] - context->argv_buf;
argv[i] = &argv_buf[offset];
}
return __WASI_ERRNO_SUCCESS;
}
/**
* @brief Writes the argument count and size of the requried argument buffer
* This is called in order to size buffers that are subsequently passed to the WASI args_get syscall
*
* @param argc_retptr linear memory offset where we should write argc
* @param argv_buf_len_retptr linear memory offset where we should write the length of the args buffer
* @return __WASI_ERRNO_SUCCESS
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_args_sizes_get(wasi_context_t *context, __wasi_size_t *argc_retptr,
__wasi_size_t *argv_buf_len_retptr)
{
if (context == NULL || argc_retptr == NULL || argv_buf_len_retptr == NULL) return __WASI_ERRNO_INVAL;
*argc_retptr = context->argc;
*argv_buf_len_retptr = context->argv_buf_size;
return __WASI_ERRNO_SUCCESS;
}
/**
* @brief Return the resolution of a clock
* Implementations are required to provide a non-zero value for supported clocks. For unsupported clocks,
* return `errno::inval`.
*
* @param id The clock for which to return the resolution.
* @param res_retptr - The resolution of the clock
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_clock_res_get(wasi_context_t *context, __wasi_clockid_t id,
__wasi_timestamp_t *res_retptr)
{
/* similar to `clock_getres` in POSIX. */
return wasi_unsupported_syscall(__func__);
}
/**
* Return the time value of a clock
*
* @param clock_id The clock for which to return the time.
* @param precision The maximum lag (exclusive) that the returned time value may have, compared to its actual value.
* @param time_retptr The time value of the clock.
* @return __WASI_ERRNO_SUCCESS code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_clock_time_get(wasi_context_t *context, __wasi_clockid_t clock_id,
__wasi_timestamp_t precision, __wasi_timestamp_t *time_retptr)
{
struct timespec tp;
int rc = clock_gettime(clock_id, &tp);
if (rc == -1) { return wasi_fromerrno(errno); }
*time_retptr = (uint64_t)tp.tv_sec * 1000000000ULL + (uint64_t)tp.tv_nsec;
return __WASI_ERRNO_SUCCESS;
}
/**
* Read environment variable data.
* Callers of this syscall only provide the base address of the two buffers because the WASI specification
* assumes that the caller first called environ_sizes_get and sized the buffers appropriately.
*
* @param environ_baseretptr
* @param environ_buf_baseretptr
* @return __WASI_ERRNO_SUCCESS or WASI_EINVAL
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_environ_get(wasi_context_t *context, char **environ, char *environ_buf)
{
if (context == NULL || environ == NULL || environ_buf == NULL) return __WASI_ERRNO_INVAL;
for (__wasi_size_t i = 0; i < context->envc; i++) {
environ[i] = context->env_buf + (context->env[i] - context->env_buf);
}
memcpy(environ_buf, context->env_buf, context->env_buf_size);
return __WASI_ERRNO_SUCCESS;
}
/**
* Returns the environment variable count and the buffer size needed to store the environment strings in linear memory.
* This is called in order to size buffers that are subsequently passed to the WASI environ_get syscall
*
* @param environ_len - the pointer where the resulting number of environment variable arguments should be written
* @param environ_buf_len - the pointer where the resulting size of the environment variable data should be
* written
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_environ_sizes_get(wasi_context_t *context, __wasi_size_t *environ_len,
__wasi_size_t *environ_buf_len)
{
if (context == NULL || environ_len == NULL || environ_buf_len == NULL) return __WASI_ERRNO_INVAL;
*environ_len = context->envc;
*environ_buf_len = context->env_buf_size;
return __WASI_ERRNO_SUCCESS;
}
/**
* Provide file advisory information on a file descriptor.
*
* @param fd
* @param offset The offset within the file to which the advisory applies.
* @param len The length of the region to which the advisory applies.
* @param advice
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_fd_advise(wasi_context_t *context, __wasi_fd_t fd, __wasi_filesize_t offset,
__wasi_filesize_t len, __wasi_advice_t advice)
{
/* similar to `posix_fadvise` in POSIX. */
return wasi_unsupported_syscall(__func__);
}
/**
* Force the allocation of space in a file.
*
* @param fd
* @param offset The offset at which to start the allocation.
* @param len The length of the area that is allocated.
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_fd_allocate(wasi_context_t *context, __wasi_fd_t fd, __wasi_filesize_t offset,
__wasi_filesize_t len)
{
/* similar to `posix_fallocate` in POSIX. */
return wasi_unsupported_syscall(__func__);
};
/**
* Close a file descriptor.
*
* @param fd
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_fd_close(wasi_context_t *context, __wasi_fd_t fd)
{
return wasi_unsupported_syscall(__func__);
}
/**
* Synchronize the data of a file to disk.
*
* @param fd
* @return __WASI_ERRNO_SUCCESS, WASI_EBADF, WASI_EIO, WASI_ENOSPC, WASI_EROFS, WASI_EINVAL, WASI_ENOSPC, WASI_EDQUOT
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_fd_datasync(wasi_context_t *context, __wasi_fd_t fd)
{
return wasi_unsupported_syscall(__func__);
}
/**
* Get the attributes of a file descriptor.
*
* @param fd
* @param fdstat_retptr the offset where the resulting wasi_fdstat structure should be written
* @return __WASI_ERRNO_SUCCESS, WASI_EACCES, WASI_EAGAIN, WASI_EBADF, WASI_EFAULT, WASI_EINVAL, WASI_ELOOP,
* WASI_ENAMETOOLONG, WASI_ENOTDIR, WASI_ENOENT, WASI_ENOMEM, or WASI_EOVERFLOW
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_fd_fdstat_get(wasi_context_t *context, __wasi_fd_t fd, __wasi_fdstat_t *fdstat)
{
return wasi_unsupported_syscall(__func__);
}
/**
* Adjust the flags associated with a file descriptor
*
* @param fd
* @param fdflags The desired values of the file descriptor flags.
* @return __WASI_ERRNO_SUCCESS, WASI_EACCES, WASI_EAGAIN, WASI_EBADF, WASI_EFAULT, WASI_EINVAL, WASI_ENOENT, or
* WASI_EPERM
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_fd_fdstat_set_flags(wasi_context_t *context, __wasi_fd_t fd, __wasi_fdflags_t fdflags)
{
return wasi_unsupported_syscall(__func__);
}
/**
* Adjust the rights associated with a file descriptor.
* This can only be used to remove rights, and returns `errno::notcapable` if called in a way that would attempt to add
* rights
*
* @param fd
* @param fs_rights_base The desired rights of the file descriptor.
* @param fs_rights_inheriting
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_fd_fdstat_set_rights(wasi_context_t *context, __wasi_fd_t fd,
__wasi_rights_t fs_rights_base,
__wasi_rights_t fs_rights_inheriting)
{
return wasi_unsupported_syscall(__func__);
}
/**
* Return the attributes of an open file.
*
* @param fd
* @param filestat_retptr The buffer where we should store the file's attributes
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_fd_filestat_get(wasi_context_t *context, __wasi_fd_t fd, __wasi_filestat_t *filestat)
{
return wasi_unsupported_syscall(__func__);
}
/**
* Adjust the size of an open file, zeroing extra bytes on increase
*
* @param fd
* @param size The desired file size.
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_fd_filestat_set_size(wasi_context_t *context, __wasi_fd_t fd, __wasi_filesize_t size)
{
/* similar to `ftruncate` in POSIX. */
return wasi_unsupported_syscall(__func__);
}
/**
* Adjust the timestamps of an open file or directory
*
* @param fd
* @param atim The desired values of the data access timestamp.
* @param mtim The desired values of the data modification timestamp.
* @param fst_flags A bitmask indicating which timestamps to adjust.
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_fd_filestat_set_times(wasi_context_t *context, __wasi_fd_t fd, __wasi_timestamp_t atim,
__wasi_timestamp_t mtim, __wasi_fstflags_t fst_flags)
{
/* similar to `futimens` in POSIX. */
return wasi_unsupported_syscall(__func__);
}
/**
* Read from a file descriptor without updating the descriptor's offset
*
* @param fd
* @param iovs_baseptr List of scatter/gather vectors in which to store data.
* @param iovs_len The length of the array pointed to by `iovs`.
* @param offset The offset within the file at which to read.
* @param nread_retptr The number of bytes read.
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_fd_pread(wasi_context_t *context, __wasi_fd_t fd, const __wasi_iovec_t *iovs,
size_t iovs_len, __wasi_filesize_t offset, __wasi_size_t *nread_retptr)
{
/* similar to `preadv` in POSIX. */
return wasi_unsupported_syscall(__func__);
}
/**
* Return a description of the given preopened file descriptor.
*
* @param fd
* @param prestat_retptr The buffer where the description is stored.
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_fd_prestat_get(wasi_context_t *context, __wasi_fd_t fd, __wasi_prestat_t *prestat_retptr)
{
/* This signals that there are no file descriptors */
return __WASI_ERRNO_BADF;
}
/**
* Return a description of the given preopened file descriptor.
*
* @param fd
* @param path_retptr A buffer into which to write the preopened directory name.
* @param path_len The length of the buffer at path_retptr
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_fd_prestat_dir_name(wasi_context_t *context, __wasi_fd_t fd, char *path,
__wasi_size_t path_len)
{
return wasi_unsupported_syscall(__func__);
}
/**
* Write to a file descriptor without updating the descriptor's offset
*
* @param fd
* @param iovs_baseptr List of scatter/gather vectors from which to retrieve data.
* @param iovs_len The length of the array pointed to by `iovs`.
* @param offset The offset within the file at which to write.
* @param nwritten_retptr The number of bytes written.
* @return status code
*
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_fd_pwrite(wasi_context_t *context, __wasi_fd_t fd, const __wasi_ciovec_t *iovs,
size_t iovs_len, __wasi_filesize_t offset, __wasi_size_t *nwritten_retptr)
{
/* similar to `pwritev` in POSIX. */
return wasi_unsupported_syscall(__func__);
}
/**
* Read from a file descriptor
*
* @param fd
* @param iovs_baseptr
* @param iovs_len
* @param nwritten_retptr The number of bytes read.
* @return __WASI_ERRNO_SUCCESS, WASI_EAGAIN, WASI_EWOULDBLOCK, WASI_EBADF, WASI_EFAULT, WASI_EINTR, WASI_EIO,
* WASI_EISDIR, or others
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_fd_read(wasi_context_t *context, __wasi_fd_t fd, const __wasi_iovec_t *iovs,
size_t iovs_len, __wasi_size_t *nwritten_retptr)
{
/* Non-blocking copy on stdin */
if (fd == STDIN_FILENO) {
struct sandbox *current_sandbox = current_sandbox_get();
struct http_request *current_request = &current_sandbox->http_request;
int old_read = current_request->body_read_length;
int bytes_to_read = current_request->body_length - old_read;
for (int i = 0; i < iovs_len; i++) {
if (bytes_to_read == 0) goto done;
int amount_to_copy = iovs[i].buf_len > bytes_to_read ? bytes_to_read : iovs[i].buf_len;
memcpy(iovs[i].buf, current_request->body + current_request->body_read_length, amount_to_copy);
current_request->body_read_length += amount_to_copy;
bytes_to_read = current_request->body_length - current_request->body_read_length;
}
done:
*nwritten_retptr = current_request->body_read_length - old_read;
return __WASI_ERRNO_SUCCESS;
}
fprintf(stderr, "Attempted to read from fd %d, but we only support STDIN\n", fd);
return wasi_unsupported_syscall(__func__);
}
/**
* 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 `dirent` object,
* followed by `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.
*
* @param fd
* @param buf_baseptr The buffer where directory entries are stored
* @param buf_len
* @param cookie The location within the directory to start reading
* @param nwritten_retptr 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.
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_fd_readdir(wasi_context_t *context, __wasi_fd_t fd, uint8_t *buf, __wasi_size_t buf_len,
__wasi_dircookie_t cookie, __wasi_size_t *nwritten_retptr)
{
return wasi_unsupported_syscall(__func__);
}
/**
* 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.
*
* @param fd
* @param to the file descriptor to overwrite
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_fd_renumber(wasi_context_t *context, __wasi_fd_t fd, __wasi_fd_t to)
{
return wasi_unsupported_syscall(__func__);
}
/**
* Move the offset of a file descriptor
*
* @param fds
* @param file_offset The number of bytes to move.
* @param whence The base from which the offset is relative.
* @param newoffset_retptr The new offset of the file descriptor, relative to the start of the file.
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_fd_seek(wasi_context_t *context, __wasi_fd_t fd, __wasi_filedelta_t file_offset,
__wasi_whence_t whence, __wasi_filesize_t *newoffset_retptr)
{
return wasi_unsupported_syscall(__func__);
}
/**
* Synchronize the data and metadata of a file to disk
*
* @param fd
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_fd_sync(wasi_context_t *context, __wasi_fd_t fd)
{
/* similar to `fsync` in POSIX. */
return wasi_unsupported_syscall(__func__);
}
/**
* Return the current offset of a file descriptor
*
* @param fd
* @param fileoffset_retptr The current offset of the file descriptor, relative to the start of the file.
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_fd_tell(wasi_context_t *context, __wasi_fd_t fd, __wasi_filesize_t *fileoffset_retptr)
{
/* similar to `lseek(fd, 0, SEEK_CUR)` in POSIX. */
return wasi_unsupported_syscall(__func__);
}
/**
* Write to a file descriptor
*
* @param fd
* @param iovs_baseptr List of scatter/gather vectors from which to retrieve data.
* @param iovs_len The length of the array pointed to by `iovs`.
* @param nwritten_retptr
* @return __WASI_ERRNO_SUCCESS, WASI_EAGAIN, WASI_EWOULDBLOCK, WASI_EBADF, WASI_EFAULT,
* WASI_EFBIG, WASI_EINTR, WASI_EIO, WASI_ENOSPC, WASI_EPERM, WASI_EPIPE, or others
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_fd_write(wasi_context_t *context, __wasi_fd_t fd, const __wasi_ciovec_t *iovs,
size_t iovs_len, __wasi_size_t *nwritten_retptr)
{
if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {
struct sandbox *s = current_sandbox_get();
size_t buffer_remaining = 0;
size_t old_response_len = s->response.length;
__wasi_size_t sum = 0;
for (size_t i = 0; i < iovs_len; i++) {
buffer_remaining = s->module->max_response_size - s->response.length;
if (buffer_remaining == 0) {
*nwritten_retptr = s->response.length - old_response_len;
return __WASI_ERRNO_FBIG;
}
ssize_t to_write = buffer_remaining > iovs[i].buf_len ? iovs[i].buf_len : buffer_remaining;
memcpy(&s->response.buffer[s->response.length], iovs[i].buf, to_write);
#ifdef LOG_SANDBOX_STDERR
if (fd == STDERR_FILENO) {
debuglog("STDERR from Sandbox:");
write(2, iovs[i].buf, iovs[i].buf_len);
}
#endif
s->response.length += to_write;
}
*nwritten_retptr = s->response.length - old_response_len;
return __WASI_ERRNO_SUCCESS;
}
fprintf(stderr, "Attempted to write to fd %d, but we only support STDOUT or STDERR\n", fd);
return wasi_unsupported_syscall(__func__);
}
/**
* Create a directory
*
* @param fd
* @param path_baseptr
* @param path_len
* @return __WASI_ERRNO_SUCCESS, WASI_EACCES, WASI_EBADF, WASI_EDQUOT, WASI_EEXIST,
* WASI_EFAULT, WASI_EINVAL, WASI_ELOOP, WASI_EMLINK, WASI_ENAMETOOLONG,
* WASI_ENOENT, WASI_ENOMEM, WASI_ENOSPC, WASI_ENOTDIR, WASI_EPERM, or WASI_EROFS
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_path_create_directory(wasi_context_t *context, __wasi_fd_t fd, const char *path,
__wasi_size_t path_len)
{
return wasi_unsupported_syscall(__func__);
}
/**
* Return the attributes of a file or directory
*
* @param fd
* @param flags Flags determining the method of how the path is resolved.
* @param path_baseptr The path of the file or directory to inspect.
* @param filestat_retptr The buffer where the file's attributes are stored.
* @return __WASI_ERRNO_SUCCESS, WASI_EACCES, WASI_EBAD, WASI_EFAUL, WASI_EINVAL, WASI_ELOOP,
* WASI_ENAMETOOLON, WASI_ENOENT, WASI_ENOENT, WASI_ENOMEM, WASI_ENOTDI, or WASI_EOVERFLOW
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_path_filestat_get(wasi_context_t *context, __wasi_fd_t fd, __wasi_lookupflags_t flags,
const char *path, __wasi_size_t path_len,
__wasi_filestat_t *const filestat)
{
return wasi_unsupported_syscall(__func__);
}
/**
* Adjust the timestamps of a file or directory
*
* @param fd
* @param flags Flags determining the method of how the path is resolved.
* @param path_baseptr The path of the file or directory to operate on.
* @param path_len
* @param atim The desired values of the data access timestamp.
* @param mtim The desired values of the data modification timestamp.
* @param fst_flags A bitmask indicating which timestamps to adjust.
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_path_filestat_set_times(wasi_context_t *context, __wasi_fd_t fd,
__wasi_lookupflags_t flags, const char *path,
__wasi_size_t path_len, __wasi_timestamp_t atim,
__wasi_timestamp_t mtim, __wasi_fstflags_t fst_flags)
{
/* similar to `utimensat` in POSIX. */
return wasi_unsupported_syscall(__func__);
}
/**
* Create a hard link
*
* @param old_fd
* @param old_flags Flags determining the method of how the path is resolved.
* @param old_path_baseptr The source path from which to link.
* @param old_path_len
* @param new_fd The working directory at which the resolution of the new path starts.
* @param new_path_baseptr The destination path at which to create the hard link.
* @param new_path_len
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_path_link(wasi_context_t *context, __wasi_fd_t old_fd, __wasi_lookupflags_t old_flags,
const char *old_path, __wasi_size_t old_path_len, __wasi_fd_t new_fd,
const char *new_path, __wasi_size_t new_path_len)
{
/* similar to `linkat` in POSIX. */
return wasi_unsupported_syscall(__func__);
}
/**
* Open a file or directory
* The returned file descriptor is not guaranteed to be the lowest-numbered
* file descriptor not currently open; it is randomized to prevent
* applications from depending on making assumptions about indexes, since this
* is error-prone in multi-threaded contexts. The returned file descriptor is
* guaranteed to be less than 2**31.
*
* 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.
*
* @param dirfd
* @param lookupflags Flags determining the method of how the path is resolved.
* @param path_baseptr path of the file or directory to open relative to the fd directory.
* @param path_len
* @param oflags The method by which to open the file.
* @param fs_rights_base
* @param fs_rights_inheriting
* @param fdflags
* @param fd_off The file descriptor of the file that has been opened.
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_path_open(wasi_context_t *context, __wasi_fd_t dirfd, __wasi_lookupflags_t dirflags,
const char *path, __wasi_size_t path_len, __wasi_oflags_t oflags,
__wasi_rights_t fs_rights_base, __wasi_rights_t fs_rights_inheriting,
__wasi_fdflags_t fdflags, __wasi_fd_t *fd)
{
return wasi_unsupported_syscall(__func__);
}
/**
* Read the contents of a symbolic link
*
* @param fd
* @param path_baseptr The path of the symbolic link from which to read.
* @param path_len
* @param buf_baseretptr The buffer to which to write the contents of the symbolic link.
* @param buf_len
* @param nread_retptr The number of bytes placed in the buffer.
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_path_readlink(wasi_context_t *context, __wasi_fd_t fd, const char *path,
__wasi_size_t path_len, uint8_t *buf, __wasi_size_t buf_len,
__wasi_size_t *nread_retptr)
{
/* similar to `readlinkat` in POSIX. */
return wasi_unsupported_syscall(__func__);
}
/**
* Remove a directory
* Return `errno::notempty` if the directory is not empty.
*
* @param fd
* @param path_baseptr The path to a directory to remove.
* @param path_len
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_path_remove_directory(wasi_context_t *context, __wasi_fd_t fd, const char *path,
__wasi_size_t path_len)
{
/* similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. */
return wasi_unsupported_syscall(__func__);
}
/**
* Rename a file or directory
*
* @param fd
* @param old_path The source path of the file or directory to rename.
* @param new_fd The working directory at which the resolution of the new path starts.
* @param new_path The destination path to which to rename the file or directory.
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_path_rename(wasi_context_t *context, __wasi_fd_t fd, const char *old_path,
__wasi_size_t old_path_len, __wasi_fd_t new_fd, const char *new_path,
__wasi_size_t new_path_len)
{
/* similar to `renameat` in POSIX. */
return wasi_unsupported_syscall(__func__);
}
/**
* Create a symbolic link
*
* @param old_path_baseptr The contents of the symbolic link.
* @param old_path_len
* @param fd
* @param new_path_baseptr The path where we want the symbolic link.
* @param new_path_len
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_path_symlink(wasi_context_t *context, const char *old_path, __wasi_size_t old_path_len,
__wasi_fd_t fd, const char *new_path, __wasi_size_t new_path_len)
{
/* similar to `symlinkat` in POSIX. */
return wasi_unsupported_syscall(__func__);
}
/**
* Unlink a file
* Return `errno::isdir` if the path refers to a directory.
*
* @param fd
* @param path_baseptr
* @param path_len
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_path_unlink_file(wasi_context_t *context, __wasi_fd_t fd, const char *path,
__wasi_size_t path_len)
{
return wasi_unsupported_syscall(__func__);
}
/**
* Concurrently poll for the occurrence of a set of events.
*
* @param in The events to which to subscribe.
* @param out The events that have occurred.
* @param nsubscriptions Both the number of subscriptions and events.
* @param retptr The number of events stored.
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_poll_oneoff(wasi_context_t *context, const __wasi_subscription_t *in,
__wasi_event_t *out, __wasi_size_t nsubscriptions, __wasi_size_t *retptr0)
{
return wasi_unsupported_syscall(__func__);
}
/**
* 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.
*
* @param exitcode
*/
noreturn void
wasi_snapshot_preview1_backing_proc_exit(wasi_context_t *context, __wasi_exitcode_t exitcode)
{
current_sandbox_fini();
assert(0);
}
/**
* Send a signal to the process of the calling thread.
*
* @param sig The signal condition to trigger.
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_proc_raise(wasi_context_t *context, __wasi_signal_t sig)
{
/* similar to `raise` in POSIX. */
return wasi_unsupported_syscall(__func__);
}
/**
* Write high-quality random data into a buffer.
* This function blocks when the implementation is unable to immediately
* provide sufficient high-quality random data.
* 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.
*
* @param buf_baseretptr The buffer to fill with random data.
* @param buf_len The length of the buffer
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_random_get(wasi_context_t *context, uint8_t *buf, __wasi_size_t buf_len)
{
static bool has_udev = true;
static bool did_seed = false;
int urandom_fd;
ssize_t nread = 0;
if (!has_udev) goto NO_UDEV;
urandom_fd = open("/dev/urandom", O_RDONLY);
if (urandom_fd < 0) {
has_udev = false;
goto NO_UDEV;
}
while (nread < buf_len) {
size_t rc = read(urandom_fd, buf, buf_len);
if (rc < 0) goto ERR_READ;
nread += rc;
}
close(urandom_fd);
return __WASI_ERRNO_SUCCESS;
ERR_READ:
close(urandom_fd);
return wasi_fromerrno(errno);
NO_UDEV:
do {
__wasi_size_t buf_cursor = 0;
for (; (buf_len - buf_cursor) >= 4; buf_cursor += 4) { *((int *)(buf + buf_cursor)) = random(); }
for (; buf_cursor < buf_len; buf_cursor += 1) { *(buf + buf_cursor) = random() % UINT8_MAX; }
} while (0);
return __WASI_ERRNO_SUCCESS;
}
/**
* Temporarily yield execution of the calling thread similar to `sched_yield` in POSIX.
* This implementation ignores client calls and silently returns RC 0
*
* @return __WASI_ERRNO_SUCCESS
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_sched_yield(wasi_context_t *context)
{
return wasi_unsupported_syscall(__func__);
}
/**
* 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`.
*
* Unimplemented because receiving sockets is unsuitable for short-lived serverless functions
*
* @param fd
* @param ri_data_baseretptr List of scatter/gather vectors to which to store data.
* @param ri_data_len The length of the array pointed to by `ri_data`.
* @param ri_flags Message flags.
* @param ri_data_nbytes_retptr Number of bytes stored in ri_data flags.
* @param message_nbytes_retptr Number of bytes stored in message flags.
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_sock_recv(wasi_context_t *context, __wasi_fd_t fd, const __wasi_iovec_t *ri_data,
size_t ri_data_len, __wasi_riflags_t ri_flags,
__wasi_size_t *ri_data_nbytes_retptr, __wasi_roflags_t *message_nbytes_retptr)
{
return wasi_unsupported_syscall(__func__);
}
/**
* 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`.
*
* Unimplemented because receiving sockets is unsuitable for short-lived serverless functions
*
* @param fd
* @param si_data_baseptr List of scatter/gather vectors to which to retrieve data
* @param si_data_len The length of the array pointed to by `si_data`.
* @param si_flags Message flags.
* @param nsent_retptr Number of bytes transmitted.
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_sock_send(wasi_context_t *context, __wasi_fd_t fd, const __wasi_ciovec_t *si_data,
size_t si_data_len, __wasi_siflags_t si_flags, __wasi_size_t *nsent_retptr)
{
return wasi_unsupported_syscall(__func__);
}
/**
* Accept a new incoming connection.
* Note: This is similar to `accept` in POSIX.
*
* Unimplemented because receiving sockets is unsuitable for short-lived serverless functions
*
* @param fd The listening socket.
* @param flags The desired values of the file descriptor flags.
* @return New socket connection
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_sock_accept(wasi_context_t *context, __wasi_fd_t fd, __wasi_fdflags_t how)
{
return wasi_unsupported_syscall(__func__);
}
/**
* Shut down socket send and receive channels.
* Note: This is similar to `shutdown` in POSIX.
*
* Unimplemented because receiving sockets is unsuitable for short-lived serverless functions
*
* @param fd
* @param how Which channels on the socket to shut down.
* @return status code
*/
__wasi_errno_t
wasi_snapshot_preview1_backing_sock_shutdown(wasi_context_t *context, __wasi_fd_t fd, __wasi_sdflags_t how)
{
return wasi_unsupported_syscall(__func__);
}