|
@@ -0,0 +1,2888 @@
|
|
|
|
|
+// 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 <sys/types.h>
|
|
|
|
|
+
|
|
|
|
|
+#include <sys/ioctl.h>
|
|
|
|
|
+#include <sys/mman.h>
|
|
|
|
|
+#include <sys/resource.h>
|
|
|
|
|
+#include <sys/socket.h>
|
|
|
|
|
+#include <sys/stat.h>
|
|
|
|
|
+#include <sys/time.h>
|
|
|
|
|
+#include <sys/uio.h>
|
|
|
|
|
+
|
|
|
|
|
+#include <assert.h>
|
|
|
|
|
+#include <dirent.h>
|
|
|
|
|
+#include <errno.h>
|
|
|
|
|
+#include <fcntl.h>
|
|
|
|
|
+#include <poll.h>
|
|
|
|
|
+#include <sched.h>
|
|
|
|
|
+#include <signal.h>
|
|
|
|
|
+#include <stdbool.h>
|
|
|
|
|
+#include <stddef.h>
|
|
|
|
|
+#include <stdio.h>
|
|
|
|
|
+#include <stdlib.h>
|
|
|
|
|
+#include <string.h>
|
|
|
|
|
+#include <time.h>
|
|
|
|
|
+#include <unistd.h>
|
|
|
|
|
+
|
|
|
|
|
+#include <wasmtime_ssp.h>
|
|
|
|
|
+
|
|
|
|
|
+#include "locking.h"
|
|
|
|
|
+#include "numeric_limits.h"
|
|
|
|
|
+#include "posix.h"
|
|
|
|
|
+#include "random.h"
|
|
|
|
|
+#include "refcount.h"
|
|
|
|
|
+#include "rights.h"
|
|
|
|
|
+#include "str.h"
|
|
|
|
|
+
|
|
|
|
|
+#include "bh_common.h"
|
|
|
|
|
+#include "bh_memory.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 + (__wasi_timestamp_t)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 = bh_malloc((uint32)(sizeof(*prestats) * size));
|
|
|
|
|
+ if (prestats == NULL)
|
|
|
|
|
+ return false;
|
|
|
|
|
+
|
|
|
|
|
+ if (pt->prestats && pt->size > 0) {
|
|
|
|
|
+ bh_memcpy_s(prestats, (uint32)(sizeof(*prestats) * size),
|
|
|
|
|
+ pt->prestats, (uint32)(sizeof(*prestats) * pt->size));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (pt->prestats)
|
|
|
|
|
+ bh_free(pt->prestats);
|
|
|
|
|
+
|
|
|
|
|
+ // 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 = bh_strdup(dir);
|
|
|
|
|
+ rwlock_unlock(&pt->lock);
|
|
|
|
|
+
|
|
|
|
|
+ if (pt->prestats[fd].dir == NULL)
|
|
|
|
|
+ return false;
|
|
|
|
|
+
|
|
|
|
|
+ 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 = bh_malloc((uint32)(sizeof(*entries) * size));
|
|
|
|
|
+ if (entries == NULL)
|
|
|
|
|
+ return false;
|
|
|
|
|
+
|
|
|
|
|
+ if (ft->entries && ft->size > 0) {
|
|
|
|
|
+ bh_memcpy_s(entries, (uint32)(sizeof(*entries) * size),
|
|
|
|
|
+ ft->entries, (uint32)(sizeof(*entries) * ft->size));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (ft->entries)
|
|
|
|
|
+ bh_free(ft->entries);
|
|
|
|
|
+
|
|
|
|
|
+ // 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 = bh_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_rights_t)__WASI_RIGHT_FD_WRITE;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case O_WRONLY:
|
|
|
|
|
+ *rights_base &= ~(__wasi_rights_t)__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;
|
|
|
|
|
+ }
|
|
|
|
|
+ bh_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 = (__wasi_fd_t)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);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+__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;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ bh_memcpy_s(path, path_len, 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;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 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, (int)iovcnt, (off_t)offset);
|
|
|
|
|
+ fd_object_release(fo);
|
|
|
|
|
+ if (len < 0)
|
|
|
|
|
+ return convert_errno(errno);
|
|
|
|
|
+ *nread = (size_t)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 = bh_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) {
|
|
|
|
|
+ bh_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) {
|
|
|
|
|
+ bh_memcpy_s(iov[i].buf, iov[i].buf_len, buf + bufoff, iov[i].buf_len);
|
|
|
|
|
+ bufoff += iov[i].buf_len;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ bh_memcpy_s(iov[i].buf, iov[i].buf_len, buf + bufoff, len - bufoff);
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ bh_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, (int)iovcnt, (off_t)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 = bh_malloc(totalsize);
|
|
|
|
|
+ if (buf == NULL) {
|
|
|
|
|
+ fd_object_release(fo);
|
|
|
|
|
+ return __WASI_ENOMEM;
|
|
|
|
|
+ }
|
|
|
|
|
+ size_t bufoff = 0;
|
|
|
|
|
+ for (size_t i = 0; i < iovcnt; ++i) {
|
|
|
|
|
+ bh_memcpy_s(buf + bufoff, totalsize - 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);
|
|
|
|
|
+ bh_free(buf);
|
|
|
|
|
+ }
|
|
|
|
|
+#endif
|
|
|
|
|
+ fd_object_release(fo);
|
|
|
|
|
+ if (len < 0)
|
|
|
|
|
+ return convert_errno(errno);
|
|
|
|
|
+ *nwritten = (size_t)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, (int)iovcnt);
|
|
|
|
|
+ fd_object_release(fo);
|
|
|
|
|
+ if (len < 0)
|
|
|
|
|
+ return convert_errno(errno);
|
|
|
|
|
+ *nread = (size_t)len;
|
|
|
|
|
+ return 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+__wasi_errno_t wasmtime_ssp_fd_renumber(
|
|
|
|
|
+#if !defined(WASMTIME_SSP_STATIC_CURFDS)
|
|
|
|
|
+ struct fd_table *curfds,
|
|
|
|
|
+ struct fd_prestats *prestats,
|
|
|
|
|
+#endif
|
|
|
|
|
+ __wasi_fd_t from,
|
|
|
|
|
+ __wasi_fd_t to
|
|
|
|
|
+) {
|
|
|
|
|
+ // Don't allow renumbering over 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, to, &prestat);
|
|
|
|
|
+ if (error != 0) {
|
|
|
|
|
+ error = fd_prestats_get_entry(prestats, from, &prestat);
|
|
|
|
|
+ }
|
|
|
|
|
+ rwlock_unlock(&prestats->lock);
|
|
|
|
|
+ if (error == 0) {
|
|
|
|
|
+ return __WASI_ENOTSUP;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ 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);
|
|
|
|
|
+ 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 = (__wasi_filesize_t)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 = (__wasi_filesize_t)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, (int)iovcnt);
|
|
|
|
|
+ fd_object_release(fo);
|
|
|
|
|
+ if (len < 0)
|
|
|
|
|
+ return convert_errno(errno);
|
|
|
|
|
+ *nwritten = (size_t)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), (off_t)offset, (off_t)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), (off_t)offset, (off_t)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,
|
|
|
|
|
+ size_t *p_len
|
|
|
|
|
+) {
|
|
|
|
|
+ char *buf = NULL;
|
|
|
|
|
+ size_t len = 32;
|
|
|
|
|
+ size_t len_org = len;
|
|
|
|
|
+
|
|
|
|
|
+ for (;;) {
|
|
|
|
|
+ char *newbuf = bh_malloc((uint32)len);
|
|
|
|
|
+
|
|
|
|
|
+ if (newbuf == NULL) {
|
|
|
|
|
+ if (buf)
|
|
|
|
|
+ bh_free(buf);
|
|
|
|
|
+ return NULL;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (buf != NULL) {
|
|
|
|
|
+ bh_memcpy_s(newbuf, (uint32)len, buf, (uint32)len_org);
|
|
|
|
|
+ bh_free(buf);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ buf = newbuf;
|
|
|
|
|
+ ssize_t ret = readlinkat(fd, path, buf, len);
|
|
|
|
|
+ if (ret < 0) {
|
|
|
|
|
+ bh_free(buf);
|
|
|
|
|
+ return NULL;
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((size_t)ret + 1 < len) {
|
|
|
|
|
+ buf[ret] = '\0';
|
|
|
|
|
+ *p_len = len;
|
|
|
|
|
+ return buf;
|
|
|
|
|
+ }
|
|
|
|
|
+ len_org = len;
|
|
|
|
|
+ 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) {
|
|
|
|
|
+ bh_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;
|
|
|
|
|
+ size_t symlink_len;
|
|
|
|
|
+
|
|
|
|
|
+ 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, &symlink_len);
|
|
|
|
|
+ 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, &symlink_len);
|
|
|
|
|
+ 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.
|
|
|
|
|
+ bh_free(paths_start[0]);
|
|
|
|
|
+ pa->path = ".";
|
|
|
|
|
+ pa->path_start = NULL;
|
|
|
|
|
+ goto success;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Finished expanding symlink. Continue processing along the
|
|
|
|
|
+ // original path.
|
|
|
|
|
+ bh_free(paths_start[curpath--]);
|
|
|
|
|
+ }
|
|
|
|
|
+ continue;
|
|
|
|
|
+
|
|
|
|
|
+ push_symlink:
|
|
|
|
|
+ // Prevent infinite loops by placing an upper limit on the number of
|
|
|
|
|
+ // symlink expansions.
|
|
|
|
|
+ if (++expansions == 128) {
|
|
|
|
|
+ bh_free(symlink);
|
|
|
|
|
+ error = __WASI_ELOOP;
|
|
|
|
|
+ goto fail;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (*paths[curpath] == '\0') {
|
|
|
|
|
+ // The original path already finished processing. Replace it by
|
|
|
|
|
+ // this symlink entirely.
|
|
|
|
|
+ bh_free(paths_start[curpath]);
|
|
|
|
|
+ } else if (curpath + 1 == sizeof(paths) / sizeof(paths[0])) {
|
|
|
|
|
+ // Too many nested symlinks. Stop processing.
|
|
|
|
|
+ bh_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)
|
|
|
|
|
+ bh_strcat_s(symlink, (uint32)symlink_len, "/");
|
|
|
|
|
+ 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)
|
|
|
|
|
+ bh_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) {
|
|
|
|
|
+ bh_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.
|
|
|
|
|
+ size_t target_len;
|
|
|
|
|
+ char *target = readlinkat_dup(old_pa.fd, old_pa.path, &target_len);
|
|
|
|
|
+ if (target != NULL) {
|
|
|
|
|
+ ret = symlinkat(target, new_pa.fd, new_pa.path);
|
|
|
|
|
+ bh_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_FD_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_base |= __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) {
|
|
|
|
|
+ int openat_errno = errno;
|
|
|
|
|
+ // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket.
|
|
|
|
|
+ if (openat_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;
|
|
|
|
|
+ }
|
|
|
|
|
+ // Linux returns ENOTDIR instead of ELOOP when using O_NOFOLLOW|O_DIRECTORY
|
|
|
|
|
+ // on a symlink.
|
|
|
|
|
+ if (openat_errno == ENOTDIR && (noflags & (O_NOFOLLOW | O_DIRECTORY)) != 0) {
|
|
|
|
|
+ struct stat sb;
|
|
|
|
|
+ int ret = fstatat(pa.fd, pa.path, &sb, AT_SYMLINK_NOFOLLOW);
|
|
|
|
|
+ if (S_ISLNK(sb.st_mode)) {
|
|
|
|
|
+ path_put(&pa);
|
|
|
|
|
+ return __WASI_ELOOP;
|
|
|
|
|
+ }
|
|
|
|
|
+ (void)ret;
|
|
|
|
|
+ }
|
|
|
|
|
+ path_put(&pa);
|
|
|
|
|
+ // FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on
|
|
|
|
|
+ // a symlink.
|
|
|
|
|
+ if (!pa.follow && openat_errno == EMLINK)
|
|
|
|
|
+ return __WASI_ELOOP;
|
|
|
|
|
+ return convert_errno(openat_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;
|
|
|
|
|
+ bh_memcpy_s((char *)buf + *bufused, bufavail, 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, (long)cookie);
|
|
|
|
|
+ fo->directory.offset = cookie;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ *bufused = 0;
|
|
|
|
|
+ while (*bufused < nbyte) {
|
|
|
|
|
+ // 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 = (__wasi_dircookie_t)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 = (uint32)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 ? (size_t)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) {
|
|
|
|
|
+ return 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 = (__wasi_linkcount_t)in->st_nlink,
|
|
|
|
|
+ .st_size = (__wasi_filesize_t)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 = (__syscall_slong_t)(in % 1000000000);
|
|
|
|
|
+ in /= 1000000000;
|
|
|
|
|
+
|
|
|
|
|
+ // Clamp to the maximum in case it would overflow our system's time_t.
|
|
|
|
|
+ out->tv_sec = (time_t)in < NUMERIC_MAX(time_t) ? (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), (off_t)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) {
|
|
|
|
|
+ bh_free(target);
|
|
|
|
|
+ return error;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ int ret = symlinkat(target, pa.fd, pa.path);
|
|
|
|
|
+ path_put(&pa);
|
|
|
|
|
+ bh_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 = bh_malloc((uint32)(nsubscriptions * sizeof(*fos)));
|
|
|
|
|
+ if (fos == NULL)
|
|
|
|
|
+ return __WASI_ENOMEM;
|
|
|
|
|
+ struct pollfd *pfds = bh_malloc((uint32)(nsubscriptions * sizeof(*pfds)));
|
|
|
|
|
+ if (pfds == NULL) {
|
|
|
|
|
+ bh_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 : (int)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 = (__wasi_filesize_t)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]);
|
|
|
|
|
+ bh_free(fos);
|
|
|
|
|
+ bh_free(pfds);
|
|
|
|
|
+ return error;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void wasmtime_ssp_proc_exit(
|
|
|
|
|
+ __wasi_exitcode_t rval
|
|
|
|
|
+) {
|
|
|
|
|
+ _Exit((int32)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 = (size_t)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 = (size_t)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;
|
|
|
|
|
+ bh_memcpy_s(argv_buf, argv_environ->argv_buf_size,
|
|
|
|
|
+ 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;
|
|
|
|
|
+ bh_memcpy_s(environ_buf, argv_environ->environ_buf_size,
|
|
|
|
|
+ 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;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+bool 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)
|
|
|
|
|
+{
|
|
|
|
|
+ uint64 total_size;
|
|
|
|
|
+ size_t i;
|
|
|
|
|
+
|
|
|
|
|
+ memset(argv_environ, 0, sizeof(struct argv_environ_values));
|
|
|
|
|
+
|
|
|
|
|
+ argv_environ->argc = argv_offsets_len;
|
|
|
|
|
+ argv_environ->argv_buf_size = argv_buf_len;
|
|
|
|
|
+
|
|
|
|
|
+ total_size = sizeof(char *) * (uint64)argv_offsets_len;
|
|
|
|
|
+ if (total_size >= UINT32_MAX
|
|
|
|
|
+ || !(argv_environ->argv = bh_malloc((uint32)total_size)))
|
|
|
|
|
+ return false;
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ if (argv_buf_len >= UINT32_MAX
|
|
|
|
|
+ || !(argv_environ->argv_buf = bh_malloc((uint32)argv_buf_len)))
|
|
|
|
|
+ goto fail1;
|
|
|
|
|
+
|
|
|
|
|
+ for (i = 0; i < argv_offsets_len; ++i) {
|
|
|
|
|
+ argv_environ->argv[i] = argv_environ->argv_buf + argv_offsets[i];
|
|
|
|
|
+ }
|
|
|
|
|
+ bh_memcpy_s(argv_environ->argv_buf, (uint32)argv_buf_len,
|
|
|
|
|
+ argv_buf, (uint32)argv_buf_len);
|
|
|
|
|
+
|
|
|
|
|
+ argv_environ->environ_count = environ_offsets_len;
|
|
|
|
|
+ argv_environ->environ_buf_size = environ_buf_len;
|
|
|
|
|
+
|
|
|
|
|
+ total_size = sizeof(char *) * (uint64)environ_offsets_len;
|
|
|
|
|
+ if (total_size >= UINT32_MAX
|
|
|
|
|
+ || !(argv_environ->environ = bh_malloc((uint32)total_size)))
|
|
|
|
|
+ goto fail2;
|
|
|
|
|
+
|
|
|
|
|
+ if (environ_buf_len >= UINT32_MAX
|
|
|
|
|
+ || !(argv_environ->environ_buf = bh_malloc((uint32)environ_buf_len)))
|
|
|
|
|
+ goto fail3;
|
|
|
|
|
+
|
|
|
|
|
+ for (i = 0; i < environ_offsets_len; ++i) {
|
|
|
|
|
+ argv_environ->environ[i] = argv_environ->environ_buf + environ_offsets[i];
|
|
|
|
|
+ }
|
|
|
|
|
+ bh_memcpy_s(argv_environ->environ_buf, (uint32)environ_buf_len,
|
|
|
|
|
+ environ_buf, (uint32)environ_buf_len);
|
|
|
|
|
+
|
|
|
|
|
+ return true;
|
|
|
|
|
+
|
|
|
|
|
+fail3:
|
|
|
|
|
+ bh_free(argv_environ->environ);
|
|
|
|
|
+fail2:
|
|
|
|
|
+ bh_free(argv_environ->argv_buf);
|
|
|
|
|
+fail1:
|
|
|
|
|
+ bh_free(argv_environ->argv);
|
|
|
|
|
+
|
|
|
|
|
+ memset(argv_environ, 0, sizeof(struct argv_environ_values));
|
|
|
|
|
+ return false;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void argv_environ_destroy(struct argv_environ_values *argv_environ)
|
|
|
|
|
+{
|
|
|
|
|
+ if (argv_environ->argv_buf)
|
|
|
|
|
+ bh_free(argv_environ->argv_buf);
|
|
|
|
|
+ if (argv_environ->argv)
|
|
|
|
|
+ bh_free(argv_environ->argv);
|
|
|
|
|
+ if (argv_environ->environ_buf)
|
|
|
|
|
+ bh_free(argv_environ->environ_buf);
|
|
|
|
|
+ if (argv_environ->environ)
|
|
|
|
|
+ bh_free(argv_environ->environ);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void fd_table_destroy(struct fd_table *ft)
|
|
|
|
|
+{
|
|
|
|
|
+ if (ft->entries) {
|
|
|
|
|
+ for (uint32 i = 0; i < ft->size; i++) {
|
|
|
|
|
+ if (ft->entries[i].object != NULL) {
|
|
|
|
|
+ fd_object_release(ft->entries[i].object);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ bh_free(ft->entries);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void fd_prestats_destroy(struct fd_prestats *pt)
|
|
|
|
|
+{
|
|
|
|
|
+ if (pt->prestats) {
|
|
|
|
|
+ for (uint32 i = 0; i < pt->size; i++) {
|
|
|
|
|
+ if (pt->prestats[i].dir != NULL) {
|
|
|
|
|
+ bh_free((void*)pt->prestats[i].dir);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ bh_free(pt->prestats);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|