summaryrefslogtreecommitdiffstats
path: root/libc-bottom-half/cloudlibc/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 13:54:38 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 13:54:38 +0000
commit8c1ab65c0f548d20b7f177bdb736daaf603340e1 (patch)
treedf55b7e75bf43f2bf500845b105afe3ac3a5157e /libc-bottom-half/cloudlibc/src
parentInitial commit. (diff)
downloadwasi-libc-8c1ab65c0f548d20b7f177bdb736daaf603340e1.tar.xz
wasi-libc-8c1ab65c0f548d20b7f177bdb736daaf603340e1.zip
Adding upstream version 0.0~git20221206.8b7148f.upstream/0.0_git20221206.8b7148f
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libc-bottom-half/cloudlibc/src')
-rw-r--r--libc-bottom-half/cloudlibc/src/common/clock.h19
-rw-r--r--libc-bottom-half/cloudlibc/src/common/limits.h35
-rw-r--r--libc-bottom-half/cloudlibc/src/common/time.h63
-rw-r--r--libc-bottom-half/cloudlibc/src/include/_/cdefs.h36
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/closedir.c10
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/dirent_impl.h31
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/dirfd.c11
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/fdclosedir.c16
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c44
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/opendirat.c23
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c132
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/rewinddir.c15
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/scandirat.c168
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/seekdir.c15
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/telldir.c11
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/errno/errno.c87
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/fcntl/fcntl.c62
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/fcntl/openat.c80
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fadvise.c24
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fallocate.c13
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/poll/poll.c129
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sched/sched_yield.c16
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/stdio/renameat.c17
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/stdlib/_Exit.c14
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/ioctl/ioctl.c88
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/select/pselect.c125
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/select/select.c25
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/socket/getsockopt.c48
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/socket/recv.c40
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/socket/send.c32
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/socket/shutdown.c27
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/socket/socket_impl.h57
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/stat/fstat.c21
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c31
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/stat/futimens.c29
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/stat/mkdirat.c18
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/stat/stat_impl.h116
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/stat/utimensat.c38
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/time/gettimeofday.c18
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/uio/preadv.c24
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/uio/pwritev.c24
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/uio/readv.c40
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/uio/writev.c40
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/time/CLOCK_MONOTONIC.c12
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/time/CLOCK_REALTIME.c12
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/time/clock_getres.c21
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/time/clock_gettime.c22
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/time/clock_nanosleep.c37
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/time/nanosleep.c15
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/time/time.c16
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/close.c16
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/faccessat.c53
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/fdatasync.c16
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/fsync.c16
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/ftruncate.c22
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/linkat.c24
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/lseek.c25
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/pread.c31
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/pwrite.c31
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/read.c18
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/readlinkat.c21
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/sleep.c13
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/symlinkat.c17
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/unlinkat.c17
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/usleep.c18
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/write.c19
66 files changed, 2404 insertions, 0 deletions
diff --git a/libc-bottom-half/cloudlibc/src/common/clock.h b/libc-bottom-half/cloudlibc/src/common/clock.h
new file mode 100644
index 0000000..58d40a1
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/common/clock.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#ifndef COMMON_CLOCK_H
+#define COMMON_CLOCK_H
+
+#include <wasi/api.h>
+
+// In this implementation we define clockid_t as a pointer type, so that
+// we can implement them as full objects. Right now we only use those
+// objects to store the raw ABI-level clock identifier, but in the
+// future we can use this to provide support for pthread_getcpuclockid()
+// and clock file descriptors.
+struct __clockid {
+ __wasi_clockid_t id;
+};
+
+#endif
diff --git a/libc-bottom-half/cloudlibc/src/common/limits.h b/libc-bottom-half/cloudlibc/src/common/limits.h
new file mode 100644
index 0000000..67e2f07
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/common/limits.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2015 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#ifndef COMMON_LIMITS_H
+#define COMMON_LIMITS_H
+
+#include <limits.h>
+
+#define NUMERIC_MIN(t) \
+ _Generic((t)0, char \
+ : CHAR_MIN, signed char \
+ : SCHAR_MIN, unsigned char : 0, short \
+ : SHRT_MIN, unsigned short : 0, int \
+ : INT_MIN, unsigned int : 0, long \
+ : LONG_MIN, unsigned long : 0, long long \
+ : LLONG_MIN, unsigned long long : 0, default \
+ : (void)0)
+
+#define NUMERIC_MAX(t) \
+ _Generic((t)0, char \
+ : CHAR_MAX, signed char \
+ : SCHAR_MAX, unsigned char \
+ : UCHAR_MAX, short \
+ : SHRT_MAX, unsigned short \
+ : USHRT_MAX, int \
+ : INT_MAX, unsigned int \
+ : UINT_MAX, long \
+ : LONG_MAX, unsigned long \
+ : ULONG_MAX, long long \
+ : LLONG_MAX, unsigned long long \
+ : ULLONG_MAX, default \
+ : (void)0)
+
+#endif
diff --git a/libc-bottom-half/cloudlibc/src/common/time.h b/libc-bottom-half/cloudlibc/src/common/time.h
new file mode 100644
index 0000000..08e2852
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/common/time.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#ifndef COMMON_TIME_H
+#define COMMON_TIME_H
+
+#include <common/limits.h>
+
+#include <sys/time.h>
+
+#include <wasi/api.h>
+#include <stdbool.h>
+#include <time.h>
+
+#define NSEC_PER_SEC 1000000000
+
+static inline bool timespec_to_timestamp_exact(
+ const struct timespec *timespec, __wasi_timestamp_t *timestamp) {
+ // Invalid nanoseconds field.
+ if (timespec->tv_nsec < 0 || timespec->tv_nsec >= NSEC_PER_SEC)
+ return false;
+
+ // Timestamps before the Epoch are not supported.
+ if (timespec->tv_sec < 0)
+ return false;
+
+ // Make sure our timestamp does not overflow.
+ return !__builtin_mul_overflow(timespec->tv_sec, NSEC_PER_SEC, timestamp) &&
+ !__builtin_add_overflow(*timestamp, timespec->tv_nsec, timestamp);
+}
+
+static inline bool timespec_to_timestamp_clamp(
+ const struct timespec *timespec, __wasi_timestamp_t *timestamp) {
+ // Invalid nanoseconds field.
+ if (timespec->tv_nsec < 0 || timespec->tv_nsec >= NSEC_PER_SEC)
+ return false;
+
+ if (timespec->tv_sec < 0) {
+ // Timestamps before the Epoch are not supported.
+ *timestamp = 0;
+ } else if (__builtin_mul_overflow(timespec->tv_sec, NSEC_PER_SEC, timestamp) ||
+ __builtin_add_overflow(*timestamp, timespec->tv_nsec, timestamp)) {
+ // Make sure our timestamp does not overflow.
+ *timestamp = NUMERIC_MAX(__wasi_timestamp_t);
+ }
+ return true;
+}
+
+static inline struct timespec timestamp_to_timespec(
+ __wasi_timestamp_t timestamp) {
+ // Decompose timestamp into seconds and nanoseconds.
+ return (struct timespec){.tv_sec = timestamp / NSEC_PER_SEC,
+ .tv_nsec = timestamp % NSEC_PER_SEC};
+}
+
+static inline struct timeval timestamp_to_timeval(
+ __wasi_timestamp_t timestamp) {
+ struct timespec ts = timestamp_to_timespec(timestamp);
+ return (struct timeval){.tv_sec = ts.tv_sec, ts.tv_nsec / 1000};
+}
+
+#endif
diff --git a/libc-bottom-half/cloudlibc/src/include/_/cdefs.h b/libc-bottom-half/cloudlibc/src/include/_/cdefs.h
new file mode 100644
index 0000000..d9a6f54
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/include/_/cdefs.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2015-2017 Nuxi, https://nuxi.nl/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+#ifndef ___CDEFS_H_
+#define ___CDEFS_H_
+
+// Compiler-independent annotations.
+
+#define __strong_reference(oldsym, newsym) \
+ extern __typeof__(oldsym) newsym __attribute__((__alias__(#oldsym)))
+
+// Convenience macros.
+
+#define __arraycount(x) (sizeof(x) / sizeof((x)[0]))
+
+#endif
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/closedir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/closedir.c
new file mode 100644
index 0000000..3585737
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/closedir.c
@@ -0,0 +1,10 @@
+// Copyright (c) 2015 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <dirent.h>
+#include <unistd.h>
+
+int closedir(DIR *dirp) {
+ return close(fdclosedir(dirp));
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/dirent_impl.h b/libc-bottom-half/cloudlibc/src/libc/dirent/dirent_impl.h
new file mode 100644
index 0000000..642400a
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/dirent_impl.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#ifndef DIRENT_DIRENT_IMPL_H
+#define DIRENT_DIRENT_IMPL_H
+
+#include <wasi/api.h>
+#include <stddef.h>
+
+struct dirent;
+
+#define DIRENT_DEFAULT_BUFFER_SIZE 4096
+
+struct _DIR {
+ // Directory file descriptor and cookie.
+ int fd;
+ __wasi_dircookie_t cookie;
+
+ // Read buffer.
+ char *buffer;
+ size_t buffer_processed;
+ size_t buffer_size;
+ size_t buffer_used;
+
+ // Object returned by readdir().
+ struct dirent *dirent;
+ size_t dirent_size;
+};
+
+#endif
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/dirfd.c b/libc-bottom-half/cloudlibc/src/libc/dirent/dirfd.c
new file mode 100644
index 0000000..033157a
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/dirfd.c
@@ -0,0 +1,11 @@
+// Copyright (c) 2015 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <dirent.h>
+
+#include "dirent_impl.h"
+
+int dirfd(DIR *dirp) {
+ return dirp->fd;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/fdclosedir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/fdclosedir.c
new file mode 100644
index 0000000..cabe702
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/fdclosedir.c
@@ -0,0 +1,16 @@
+// Copyright (c) 2015 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <dirent.h>
+#include <stdlib.h>
+
+#include "dirent_impl.h"
+
+int fdclosedir(DIR *dirp) {
+ int fd = dirp->fd;
+ free(dirp->buffer);
+ free(dirp->dirent);
+ free(dirp);
+ return fd;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c
new file mode 100644
index 0000000..4a2136a
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c
@@ -0,0 +1,44 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "dirent_impl.h"
+
+DIR *fdopendir(int fd) {
+ // Allocate new directory object and read buffer.
+ DIR *dirp = malloc(sizeof(*dirp));
+ if (dirp == NULL)
+ return NULL;
+ dirp->buffer = malloc(DIRENT_DEFAULT_BUFFER_SIZE);
+ if (dirp->buffer == NULL) {
+ free(dirp);
+ return NULL;
+ }
+
+ // Ensure that this is really a directory by already loading the first
+ // chunk of data.
+ __wasi_errno_t error =
+ // TODO: Remove the cast on `dirp->buffer` once the witx is updated with char8 support.
+ __wasi_fd_readdir(fd, (uint8_t *)dirp->buffer, DIRENT_DEFAULT_BUFFER_SIZE,
+ __WASI_DIRCOOKIE_START, &dirp->buffer_used);
+ if (error != 0) {
+ free(dirp->buffer);
+ free(dirp);
+ errno = error;
+ return NULL;
+ }
+
+ // Initialize other members.
+ dirp->fd = fd;
+ dirp->cookie = __WASI_DIRCOOKIE_START;
+ dirp->buffer_processed = 0;
+ dirp->buffer_size = DIRENT_DEFAULT_BUFFER_SIZE;
+ dirp->dirent = NULL;
+ dirp->dirent_size = 1;
+ return dirp;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/opendirat.c b/libc-bottom-half/cloudlibc/src/libc/dirent/opendirat.c
new file mode 100644
index 0000000..e6d1c2a
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/opendirat.c
@@ -0,0 +1,23 @@
+// Copyright (c) 2015 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/libc.h>
+#include <wasi/libc-nocwd.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <unistd.h>
+
+DIR *__wasilibc_nocwd_opendirat(int dir, const char *dirname) {
+ // Open directory.
+ int fd = __wasilibc_nocwd_openat_nomode(dir, dirname, O_RDONLY | O_NONBLOCK | O_DIRECTORY);
+ if (fd == -1)
+ return NULL;
+
+ // Create directory handle.
+ DIR *result = fdopendir(fd);
+ if (result == NULL)
+ close(fd);
+ return result;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c
new file mode 100644
index 0000000..bfd1921
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c
@@ -0,0 +1,132 @@
+// Copyright (c) 2015-2017 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dirent_impl.h"
+
+static_assert(DT_BLK == __WASI_FILETYPE_BLOCK_DEVICE, "Value mismatch");
+static_assert(DT_CHR == __WASI_FILETYPE_CHARACTER_DEVICE, "Value mismatch");
+static_assert(DT_DIR == __WASI_FILETYPE_DIRECTORY, "Value mismatch");
+static_assert(DT_FIFO == __WASI_FILETYPE_SOCKET_STREAM, "Value mismatch");
+static_assert(DT_LNK == __WASI_FILETYPE_SYMBOLIC_LINK, "Value mismatch");
+static_assert(DT_REG == __WASI_FILETYPE_REGULAR_FILE, "Value mismatch");
+static_assert(DT_UNKNOWN == __WASI_FILETYPE_UNKNOWN, "Value mismatch");
+
+// Grows a buffer to be large enough to hold a certain amount of data.
+#define GROW(buffer, buffer_size, target_size) \
+ do { \
+ if ((buffer_size) < (target_size)) { \
+ size_t new_size = (buffer_size); \
+ while (new_size < (target_size)) \
+ new_size *= 2; \
+ void *new_buffer = realloc(buffer, new_size); \
+ if (new_buffer == NULL) \
+ return NULL; \
+ (buffer) = new_buffer; \
+ (buffer_size) = new_size; \
+ } \
+ } while (0)
+
+struct dirent *readdir(DIR *dirp) {
+ for (;;) {
+ // Extract the next dirent header.
+ size_t buffer_left = dirp->buffer_used - dirp->buffer_processed;
+ if (buffer_left < sizeof(__wasi_dirent_t)) {
+ // End-of-file.
+ if (dirp->buffer_used < dirp->buffer_size)
+ return NULL;
+ goto read_entries;
+ }
+ __wasi_dirent_t entry;
+ memcpy(&entry, dirp->buffer + dirp->buffer_processed, sizeof(entry));
+
+ size_t entry_size = sizeof(__wasi_dirent_t) + entry.d_namlen;
+ if (entry.d_namlen == 0) {
+ // Invalid pathname length. Skip the entry.
+ dirp->buffer_processed += entry_size;
+ continue;
+ }
+
+ // The entire entry must be present in buffer space. If not, read
+ // the entry another time. Ensure that the read buffer is large
+ // enough to fit at least this single entry.
+ if (buffer_left < entry_size) {
+ GROW(dirp->buffer, dirp->buffer_size, entry_size);
+ goto read_entries;
+ }
+
+ // Skip entries having null bytes in the filename.
+ const char *name = dirp->buffer + dirp->buffer_processed + sizeof(entry);
+ if (memchr(name, '\0', entry.d_namlen) != NULL) {
+ dirp->buffer_processed += entry_size;
+ continue;
+ }
+
+ // Return the next directory entry. Ensure that the dirent is large
+ // enough to fit the filename.
+ GROW(dirp->dirent, dirp->dirent_size,
+ offsetof(struct dirent, d_name) + entry.d_namlen + 1);
+ struct dirent *dirent = dirp->dirent;
+ dirent->d_type = entry.d_type;
+ memcpy(dirent->d_name, name, entry.d_namlen);
+ dirent->d_name[entry.d_namlen] = '\0';
+
+ // `fd_readdir` implementations may set the inode field to zero if the
+ // the inode number is unknown. In that case, do an `fstatat` to get the
+ // inode number.
+ off_t d_ino = entry.d_ino;
+ unsigned char d_type = entry.d_type;
+ if (d_ino == 0 && strcmp(dirent->d_name, "..") != 0) {
+ struct stat statbuf;
+ if (fstatat(dirp->fd, dirent->d_name, &statbuf, AT_SYMLINK_NOFOLLOW) != 0) {
+ if (errno == ENOENT) {
+ // The file disappeared before we could read it, so skip it.
+ dirp->buffer_processed += entry_size;
+ continue;
+ }
+ return NULL;
+ }
+
+ // Fill in the inode.
+ d_ino = statbuf.st_ino;
+
+ // In case someone raced with us and replaced the object with this name
+ // with another of a different type, update the type too.
+ d_type = __wasilibc_iftodt(statbuf.st_mode & S_IFMT);
+ }
+ dirent->d_ino = d_ino;
+ dirent->d_type = d_type;
+
+ dirp->cookie = entry.d_next;
+ dirp->buffer_processed += entry_size;
+ return dirent;
+
+ read_entries:
+ // Discard data currently stored in the input buffer.
+ dirp->buffer_used = dirp->buffer_processed = dirp->buffer_size;
+
+ // Load more directory entries and continue.
+ __wasi_errno_t error =
+ // TODO: Remove the cast on `dirp->buffer` once the witx is updated with char8 support.
+ __wasi_fd_readdir(dirp->fd, (uint8_t *)dirp->buffer, dirp->buffer_size,
+ dirp->cookie, &dirp->buffer_used);
+ if (error != 0) {
+ errno = error;
+ return NULL;
+ }
+ dirp->buffer_processed = 0;
+ }
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/rewinddir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/rewinddir.c
new file mode 100644
index 0000000..8dd5882
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/rewinddir.c
@@ -0,0 +1,15 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <dirent.h>
+
+#include "dirent_impl.h"
+
+void rewinddir(DIR *dirp) {
+ // Update cookie.
+ dirp->cookie = __WASI_DIRCOOKIE_START;
+ // Mark entire buffer as processed to force a read of new data.
+ dirp->buffer_used = dirp->buffer_processed = dirp->buffer_size;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/scandirat.c b/libc-bottom-half/cloudlibc/src/libc/dirent/scandirat.c
new file mode 100644
index 0000000..079b3b3
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/scandirat.c
@@ -0,0 +1,168 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <wasi/libc.h>
+#include <wasi/libc-nocwd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "dirent_impl.h"
+
+static int sel_true(const struct dirent *de) {
+ return 1;
+}
+
+int __wasilibc_nocwd_scandirat(int dirfd, const char *dir, struct dirent ***namelist,
+ int (*sel)(const struct dirent *),
+ int (*compar)(const struct dirent **, const struct dirent **)) {
+ struct stat statbuf;
+
+ // Match all files if no select function is provided.
+ if (sel == NULL)
+ sel = sel_true;
+
+ // Open the directory.
+ int fd = __wasilibc_nocwd_openat_nomode(dirfd, dir, O_RDONLY | O_NONBLOCK | O_DIRECTORY);
+ if (fd == -1)
+ return -1;
+
+ // Allocate a read buffer for the directory entries.
+ size_t buffer_size = DIRENT_DEFAULT_BUFFER_SIZE;
+ char *buffer = malloc(buffer_size);
+ if (buffer == NULL) {
+ close(fd);
+ return -1;
+ }
+ size_t buffer_processed = buffer_size;
+ size_t buffer_used = buffer_size;
+
+ // Space for the array to return to the caller.
+ struct dirent **dirents = NULL;
+ size_t dirents_size = 0;
+ size_t dirents_used = 0;
+
+ __wasi_dircookie_t cookie = __WASI_DIRCOOKIE_START;
+ for (;;) {
+ // Extract the next dirent header.
+ size_t buffer_left = buffer_used - buffer_processed;
+ if (buffer_left < sizeof(__wasi_dirent_t)) {
+ // End-of-file.
+ if (buffer_used < buffer_size)
+ break;
+ goto read_entries;
+ }
+ __wasi_dirent_t entry;
+ memcpy(&entry, buffer + buffer_processed, sizeof(entry));
+
+ size_t entry_size = sizeof(__wasi_dirent_t) + entry.d_namlen;
+ if (entry.d_namlen == 0) {
+ // Invalid pathname length. Skip the entry.
+ buffer_processed += entry_size;
+ continue;
+ }
+
+ // The entire entry must be present in buffer space. If not, read
+ // the entry another time. Ensure that the read buffer is large
+ // enough to fit at least this single entry.
+ if (buffer_left < entry_size) {
+ while (buffer_size < entry_size)
+ buffer_size *= 2;
+ char *new_buffer = realloc(buffer, buffer_size);
+ if (new_buffer == NULL)
+ goto bad;
+ buffer = new_buffer;
+ goto read_entries;
+ }
+
+ // Skip entries having null bytes in the filename.
+ const char *name = buffer + buffer_processed + sizeof(entry);
+ buffer_processed += entry_size;
+ if (memchr(name, '\0', entry.d_namlen) != NULL)
+ continue;
+
+ // Create the new directory entry.
+ struct dirent *dirent =
+ malloc(offsetof(struct dirent, d_name) + entry.d_namlen + 1);
+ if (dirent == NULL)
+ goto bad;
+ dirent->d_type = entry.d_type;
+ memcpy(dirent->d_name, name, entry.d_namlen);
+ dirent->d_name[entry.d_namlen] = '\0';
+
+ // `fd_readdir` implementations may set the inode field to zero if the
+ // the inode number is unknown. In that case, do an `fstatat` to get the
+ // inode number.
+ off_t d_ino = entry.d_ino;
+ unsigned char d_type = entry.d_type;
+ if (d_ino == 0) {
+ if (fstatat(fd, dirent->d_name, &statbuf, AT_SYMLINK_NOFOLLOW) != 0) {
+ return -1;
+ }
+
+ // Fill in the inode.
+ d_ino = statbuf.st_ino;
+
+ // In case someone raced with us and replaced the object with this name
+ // with another of a different type, update the type too.
+ d_type = __wasilibc_iftodt(statbuf.st_mode & S_IFMT);
+ }
+ dirent->d_ino = d_ino;
+ dirent->d_type = d_type;
+
+ cookie = entry.d_next;
+
+ if (sel(dirent)) {
+ // Add the entry to the results.
+ if (dirents_used == dirents_size) {
+ dirents_size = dirents_size < 8 ? 8 : dirents_size * 2;
+ struct dirent **new_dirents =
+ realloc(dirents, dirents_size * sizeof(*dirents));
+ if (new_dirents == NULL) {
+ free(dirent);
+ goto bad;
+ }
+ dirents = new_dirents;
+ }
+ dirents[dirents_used++] = dirent;
+ } else {
+ // Discard the entry.
+ free(dirent);
+ }
+ continue;
+
+ read_entries:;
+ // Load more directory entries and continue.
+ // TODO: Remove the cast on `buffer` once the witx is updated with char8 support.
+ __wasi_errno_t error = __wasi_fd_readdir(fd, (uint8_t *)buffer, buffer_size,
+ cookie, &buffer_used);
+ if (error != 0) {
+ errno = error;
+ goto bad;
+ }
+ buffer_processed = 0;
+ }
+
+ // Sort results and return them.
+ free(buffer);
+ close(fd);
+ (qsort)(dirents, dirents_used, sizeof(*dirents),
+ (int (*)(const void *, const void *))compar);
+ *namelist = dirents;
+ return dirents_used;
+
+bad:
+ // Deallocate partially created results.
+ for (size_t i = 0; i < dirents_used; ++i)
+ free(dirents[i]);
+ free(dirents);
+ free(buffer);
+ close(fd);
+ return -1;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/seekdir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/seekdir.c
new file mode 100644
index 0000000..a865d92
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/seekdir.c
@@ -0,0 +1,15 @@
+// Copyright (c) 2015 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <dirent.h>
+
+#include "dirent_impl.h"
+
+void seekdir(DIR *dirp, long loc) {
+ // Update cookie.
+ dirp->cookie = (unsigned long)loc;
+ // Mark entire buffer as processed to force a read of new data.
+ // TODO(ed): We could prevent a read if the offset is in the buffer.
+ dirp->buffer_used = dirp->buffer_processed = dirp->buffer_size;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/telldir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/telldir.c
new file mode 100644
index 0000000..05687ee
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/telldir.c
@@ -0,0 +1,11 @@
+// Copyright (c) 2015 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <dirent.h>
+
+#include "dirent_impl.h"
+
+long telldir(DIR *dirp) {
+ return dirp->cookie;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/errno/errno.c b/libc-bottom-half/cloudlibc/src/libc/errno/errno.c
new file mode 100644
index 0000000..d7a27f3
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/errno/errno.c
@@ -0,0 +1,87 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <errno.h>
+#include <threads.h>
+
+static_assert(E2BIG == __WASI_ERRNO_2BIG, "Value mismatch");
+static_assert(EACCES == __WASI_ERRNO_ACCES, "Value mismatch");
+static_assert(EADDRINUSE == __WASI_ERRNO_ADDRINUSE, "Value mismatch");
+static_assert(EADDRNOTAVAIL == __WASI_ERRNO_ADDRNOTAVAIL, "Value mismatch");
+static_assert(EAFNOSUPPORT == __WASI_ERRNO_AFNOSUPPORT, "Value mismatch");
+static_assert(EAGAIN == __WASI_ERRNO_AGAIN, "Value mismatch");
+static_assert(EALREADY == __WASI_ERRNO_ALREADY, "Value mismatch");
+static_assert(EBADF == __WASI_ERRNO_BADF, "Value mismatch");
+static_assert(EBADMSG == __WASI_ERRNO_BADMSG, "Value mismatch");
+static_assert(EBUSY == __WASI_ERRNO_BUSY, "Value mismatch");
+static_assert(ECANCELED == __WASI_ERRNO_CANCELED, "Value mismatch");
+static_assert(ECHILD == __WASI_ERRNO_CHILD, "Value mismatch");
+static_assert(ECONNABORTED == __WASI_ERRNO_CONNABORTED, "Value mismatch");
+static_assert(ECONNREFUSED == __WASI_ERRNO_CONNREFUSED, "Value mismatch");
+static_assert(ECONNRESET == __WASI_ERRNO_CONNRESET, "Value mismatch");
+static_assert(EDEADLK == __WASI_ERRNO_DEADLK, "Value mismatch");
+static_assert(EDESTADDRREQ == __WASI_ERRNO_DESTADDRREQ, "Value mismatch");
+static_assert(EDOM == __WASI_ERRNO_DOM, "Value mismatch");
+static_assert(EDQUOT == __WASI_ERRNO_DQUOT, "Value mismatch");
+static_assert(EEXIST == __WASI_ERRNO_EXIST, "Value mismatch");
+static_assert(EFAULT == __WASI_ERRNO_FAULT, "Value mismatch");
+static_assert(EFBIG == __WASI_ERRNO_FBIG, "Value mismatch");
+static_assert(EHOSTUNREACH == __WASI_ERRNO_HOSTUNREACH, "Value mismatch");
+static_assert(EIDRM == __WASI_ERRNO_IDRM, "Value mismatch");
+static_assert(EILSEQ == __WASI_ERRNO_ILSEQ, "Value mismatch");
+static_assert(EINPROGRESS == __WASI_ERRNO_INPROGRESS, "Value mismatch");
+static_assert(EINTR == __WASI_ERRNO_INTR, "Value mismatch");
+static_assert(EINVAL == __WASI_ERRNO_INVAL, "Value mismatch");
+static_assert(EIO == __WASI_ERRNO_IO, "Value mismatch");
+static_assert(EISCONN == __WASI_ERRNO_ISCONN, "Value mismatch");
+static_assert(EISDIR == __WASI_ERRNO_ISDIR, "Value mismatch");
+static_assert(ELOOP == __WASI_ERRNO_LOOP, "Value mismatch");
+static_assert(EMFILE == __WASI_ERRNO_MFILE, "Value mismatch");
+static_assert(EMLINK == __WASI_ERRNO_MLINK, "Value mismatch");
+static_assert(EMSGSIZE == __WASI_ERRNO_MSGSIZE, "Value mismatch");
+static_assert(EMULTIHOP == __WASI_ERRNO_MULTIHOP, "Value mismatch");
+static_assert(ENAMETOOLONG == __WASI_ERRNO_NAMETOOLONG, "Value mismatch");
+static_assert(ENETDOWN == __WASI_ERRNO_NETDOWN, "Value mismatch");
+static_assert(ENETRESET == __WASI_ERRNO_NETRESET, "Value mismatch");
+static_assert(ENETUNREACH == __WASI_ERRNO_NETUNREACH, "Value mismatch");
+static_assert(ENFILE == __WASI_ERRNO_NFILE, "Value mismatch");
+static_assert(ENOBUFS == __WASI_ERRNO_NOBUFS, "Value mismatch");
+static_assert(ENODEV == __WASI_ERRNO_NODEV, "Value mismatch");
+static_assert(ENOENT == __WASI_ERRNO_NOENT, "Value mismatch");
+static_assert(ENOEXEC == __WASI_ERRNO_NOEXEC, "Value mismatch");
+static_assert(ENOLCK == __WASI_ERRNO_NOLCK, "Value mismatch");
+static_assert(ENOLINK == __WASI_ERRNO_NOLINK, "Value mismatch");
+static_assert(ENOMEM == __WASI_ERRNO_NOMEM, "Value mismatch");
+static_assert(ENOMSG == __WASI_ERRNO_NOMSG, "Value mismatch");
+static_assert(ENOPROTOOPT == __WASI_ERRNO_NOPROTOOPT, "Value mismatch");
+static_assert(ENOSPC == __WASI_ERRNO_NOSPC, "Value mismatch");
+static_assert(ENOSYS == __WASI_ERRNO_NOSYS, "Value mismatch");
+static_assert(ENOTCAPABLE == __WASI_ERRNO_NOTCAPABLE, "Value mismatch");
+static_assert(ENOTCONN == __WASI_ERRNO_NOTCONN, "Value mismatch");
+static_assert(ENOTDIR == __WASI_ERRNO_NOTDIR, "Value mismatch");
+static_assert(ENOTEMPTY == __WASI_ERRNO_NOTEMPTY, "Value mismatch");
+static_assert(ENOTRECOVERABLE == __WASI_ERRNO_NOTRECOVERABLE, "Value mismatch");
+static_assert(ENOTSOCK == __WASI_ERRNO_NOTSOCK, "Value mismatch");
+static_assert(ENOTSUP == __WASI_ERRNO_NOTSUP, "Value mismatch");
+static_assert(ENOTTY == __WASI_ERRNO_NOTTY, "Value mismatch");
+static_assert(ENXIO == __WASI_ERRNO_NXIO, "Value mismatch");
+static_assert(EOVERFLOW == __WASI_ERRNO_OVERFLOW, "Value mismatch");
+static_assert(EOWNERDEAD == __WASI_ERRNO_OWNERDEAD, "Value mismatch");
+static_assert(EPERM == __WASI_ERRNO_PERM, "Value mismatch");
+static_assert(EPIPE == __WASI_ERRNO_PIPE, "Value mismatch");
+static_assert(EPROTO == __WASI_ERRNO_PROTO, "Value mismatch");
+static_assert(EPROTONOSUPPORT == __WASI_ERRNO_PROTONOSUPPORT, "Value mismatch");
+static_assert(EPROTOTYPE == __WASI_ERRNO_PROTOTYPE, "Value mismatch");
+static_assert(ERANGE == __WASI_ERRNO_RANGE, "Value mismatch");
+static_assert(EROFS == __WASI_ERRNO_ROFS, "Value mismatch");
+static_assert(ESPIPE == __WASI_ERRNO_SPIPE, "Value mismatch");
+static_assert(ESRCH == __WASI_ERRNO_SRCH, "Value mismatch");
+static_assert(ESTALE == __WASI_ERRNO_STALE, "Value mismatch");
+static_assert(ETIMEDOUT == __WASI_ERRNO_TIMEDOUT, "Value mismatch");
+static_assert(ETXTBSY == __WASI_ERRNO_TXTBSY, "Value mismatch");
+static_assert(EXDEV == __WASI_ERRNO_XDEV, "Value mismatch");
+
+thread_local int errno = 0;
diff --git a/libc-bottom-half/cloudlibc/src/libc/fcntl/fcntl.c b/libc-bottom-half/cloudlibc/src/libc/fcntl/fcntl.c
new file mode 100644
index 0000000..5d4055d
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/fcntl/fcntl.c
@@ -0,0 +1,62 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+
+int fcntl(int fildes, int cmd, ...) {
+ switch (cmd) {
+ case F_GETFD:
+ // Act as if the close-on-exec flag is always set.
+ return FD_CLOEXEC;
+ case F_SETFD:
+ // The close-on-exec flag is ignored.
+ return 0;
+ case F_GETFL: {
+ // Obtain the flags and the rights of the descriptor.
+ __wasi_fdstat_t fds;
+ __wasi_errno_t error = __wasi_fd_fdstat_get(fildes, &fds);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+
+ // Roughly approximate the access mode by converting the rights.
+ int oflags = fds.fs_flags;
+ if ((fds.fs_rights_base &
+ (__WASI_RIGHTS_FD_READ | __WASI_RIGHTS_FD_READDIR)) != 0) {
+ if ((fds.fs_rights_base & __WASI_RIGHTS_FD_WRITE) != 0)
+ oflags |= O_RDWR;
+ else
+ oflags |= O_RDONLY;
+ } else if ((fds.fs_rights_base & __WASI_RIGHTS_FD_WRITE) != 0) {
+ oflags |= O_WRONLY;
+ } else {
+ oflags |= O_SEARCH;
+ }
+ return oflags;
+ }
+ case F_SETFL: {
+ // Set new file descriptor flags.
+ va_list ap;
+ va_start(ap, cmd);
+ int flags = va_arg(ap, int);
+ va_end(ap);
+
+ __wasi_fdflags_t fs_flags = flags & 0xfff;
+ __wasi_errno_t error =
+ __wasi_fd_fdstat_set_flags(fildes, fs_flags);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+ }
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/fcntl/openat.c b/libc-bottom-half/cloudlibc/src/libc/fcntl/openat.c
new file mode 100644
index 0000000..09cbbf8
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/fcntl/openat.c
@@ -0,0 +1,80 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <wasi/libc.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+static_assert(O_APPEND == __WASI_FDFLAGS_APPEND, "Value mismatch");
+static_assert(O_DSYNC == __WASI_FDFLAGS_DSYNC, "Value mismatch");
+static_assert(O_NONBLOCK == __WASI_FDFLAGS_NONBLOCK, "Value mismatch");
+static_assert(O_RSYNC == __WASI_FDFLAGS_RSYNC, "Value mismatch");
+static_assert(O_SYNC == __WASI_FDFLAGS_SYNC, "Value mismatch");
+
+static_assert(O_CREAT >> 12 == __WASI_OFLAGS_CREAT, "Value mismatch");
+static_assert(O_DIRECTORY >> 12 == __WASI_OFLAGS_DIRECTORY, "Value mismatch");
+static_assert(O_EXCL >> 12 == __WASI_OFLAGS_EXCL, "Value mismatch");
+static_assert(O_TRUNC >> 12 == __WASI_OFLAGS_TRUNC, "Value mismatch");
+
+int __wasilibc_nocwd_openat_nomode(int fd, const char *path, int oflag) {
+ // Compute rights corresponding with the access modes provided.
+ // Attempt to obtain all rights, except the ones that contradict the
+ // access mode provided to openat().
+ __wasi_rights_t max =
+ ~(__WASI_RIGHTS_FD_DATASYNC | __WASI_RIGHTS_FD_READ |
+ __WASI_RIGHTS_FD_WRITE | __WASI_RIGHTS_FD_ALLOCATE |
+ __WASI_RIGHTS_FD_READDIR | __WASI_RIGHTS_FD_FILESTAT_SET_SIZE);
+ switch (oflag & O_ACCMODE) {
+ case O_RDONLY:
+ case O_RDWR:
+ case O_WRONLY:
+ if ((oflag & O_RDONLY) != 0) {
+ max |= __WASI_RIGHTS_FD_READ | __WASI_RIGHTS_FD_READDIR;
+ }
+ if ((oflag & O_WRONLY) != 0) {
+ max |= __WASI_RIGHTS_FD_DATASYNC | __WASI_RIGHTS_FD_WRITE |
+ __WASI_RIGHTS_FD_ALLOCATE |
+ __WASI_RIGHTS_FD_FILESTAT_SET_SIZE;
+ }
+ break;
+ case O_EXEC:
+ break;
+ case O_SEARCH:
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ // Ensure that we can actually obtain the minimal rights needed.
+ __wasi_fdstat_t fsb_cur;
+ __wasi_errno_t error = __wasi_fd_fdstat_get(fd, &fsb_cur);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+
+ // Path lookup properties.
+ __wasi_lookupflags_t lookup_flags = 0;
+ if ((oflag & O_NOFOLLOW) == 0)
+ lookup_flags |= __WASI_LOOKUPFLAGS_SYMLINK_FOLLOW;
+
+ // Open file with appropriate rights.
+ __wasi_fdflags_t fs_flags = oflag & 0xfff;
+ __wasi_rights_t fs_rights_base = max & fsb_cur.fs_rights_inheriting;
+ __wasi_rights_t fs_rights_inheriting = fsb_cur.fs_rights_inheriting;
+ __wasi_fd_t newfd;
+ error = __wasi_path_open(fd, lookup_flags, path,
+ (oflag >> 12) & 0xfff,
+ fs_rights_base, fs_rights_inheriting, fs_flags,
+ &newfd);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return newfd;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fadvise.c b/libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fadvise.c
new file mode 100644
index 0000000..d683d39
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fadvise.c
@@ -0,0 +1,24 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <errno.h>
+#include <fcntl.h>
+
+static_assert(POSIX_FADV_DONTNEED == __WASI_ADVICE_DONTNEED,
+ "Value mismatch");
+static_assert(POSIX_FADV_NOREUSE == __WASI_ADVICE_NOREUSE, "Value mismatch");
+static_assert(POSIX_FADV_NORMAL == __WASI_ADVICE_NORMAL, "Value mismatch");
+static_assert(POSIX_FADV_RANDOM == __WASI_ADVICE_RANDOM, "Value mismatch");
+static_assert(POSIX_FADV_SEQUENTIAL == __WASI_ADVICE_SEQUENTIAL,
+ "Value mismatch");
+static_assert(POSIX_FADV_WILLNEED == __WASI_ADVICE_WILLNEED,
+ "Value mismatch");
+
+int posix_fadvise(int fd, off_t offset, off_t len, int advice) {
+ if (offset < 0 || len < 0)
+ return EINVAL;
+ return __wasi_fd_advise(fd, offset, len, advice);
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fallocate.c b/libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fallocate.c
new file mode 100644
index 0000000..4b41c4b
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fallocate.c
@@ -0,0 +1,13 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <fcntl.h>
+
+int posix_fallocate(int fd, off_t offset, off_t len) {
+ if (offset < 0 || len < 0)
+ return EINVAL;
+ return __wasi_fd_allocate(fd, offset, len);
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/poll/poll.c b/libc-bottom-half/cloudlibc/src/libc/poll/poll.c
new file mode 100644
index 0000000..cde4e81
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/poll/poll.c
@@ -0,0 +1,129 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <poll.h>
+#include <stdbool.h>
+
+int poll(struct pollfd *fds, size_t nfds, int timeout) {
+ // Construct events for poll().
+ size_t maxevents = 2 * nfds + 1;
+ __wasi_subscription_t subscriptions[maxevents];
+ size_t nsubscriptions = 0;
+ for (size_t i = 0; i < nfds; ++i) {
+ struct pollfd *pollfd = &fds[i];
+ if (pollfd->fd < 0)
+ continue;
+ bool created_events = false;
+ if ((pollfd->events & POLLRDNORM) != 0) {
+ __wasi_subscription_t *subscription = &subscriptions[nsubscriptions++];
+ *subscription = (__wasi_subscription_t){
+ .userdata = (uintptr_t)pollfd,
+ .u.tag = __WASI_EVENTTYPE_FD_READ,
+ .u.u.fd_read.file_descriptor = pollfd->fd,
+ };
+ created_events = true;
+ }
+ if ((pollfd->events & POLLWRNORM) != 0) {
+ __wasi_subscription_t *subscription = &subscriptions[nsubscriptions++];
+ *subscription = (__wasi_subscription_t){
+ .userdata = (uintptr_t)pollfd,
+ .u.tag = __WASI_EVENTTYPE_FD_WRITE,
+ .u.u.fd_write.file_descriptor = pollfd->fd,
+ };
+ created_events = true;
+ }
+
+ // As entries are decomposed into separate read/write subscriptions,
+ // we cannot detect POLLERR, POLLHUP and POLLNVAL if POLLRDNORM and
+ // POLLWRNORM are not specified. Disallow this for now.
+ if (!created_events) {
+ errno = ENOSYS;
+ return -1;
+ }
+ }
+
+ // Create extra event for the timeout.
+ if (timeout >= 0) {
+ __wasi_subscription_t *subscription = &subscriptions[nsubscriptions++];
+ *subscription = (__wasi_subscription_t){
+ .u.tag = __WASI_EVENTTYPE_CLOCK,
+ .u.u.clock.id = __WASI_CLOCKID_REALTIME,
+ .u.u.clock.timeout = (__wasi_timestamp_t)timeout * 1000000,
+ };
+ }
+
+ // Execute poll().
+ size_t nevents;
+ __wasi_event_t events[nsubscriptions];
+ __wasi_errno_t error =
+ __wasi_poll_oneoff(subscriptions, events, nsubscriptions, &nevents);
+ if (error != 0) {
+ // WASI's poll requires at least one subscription, or else it returns
+ // `EINVAL`. Since a `poll` with nothing to wait for is valid in POSIX,
+ // return `ENOTSUP` to indicate that we don't support that case.
+ //
+ // Wasm has no signal handling, so if none of the user-provided `pollfd`
+ // elements, nor the timeout, led us to producing even one subscription
+ // to wait for, there would be no way for the poll to wake up. WASI
+ // returns `EINVAL` in this case, but for users of `poll`, `ENOTSUP` is
+ // more likely to be understood.
+ if (nsubscriptions == 0)
+ errno = ENOTSUP;
+ else
+ errno = error;
+ return -1;
+ }
+
+ // Clear revents fields.
+ for (size_t i = 0; i < nfds; ++i) {
+ struct pollfd *pollfd = &fds[i];
+ pollfd->revents = 0;
+ }
+
+ // Set revents fields.
+ for (size_t i = 0; i < nevents; ++i) {
+ const __wasi_event_t *event = &events[i];
+ if (event->type == __WASI_EVENTTYPE_FD_READ ||
+ event->type == __WASI_EVENTTYPE_FD_WRITE) {
+ struct pollfd *pollfd = (struct pollfd *)(uintptr_t)event->userdata;
+ if (event->error == __WASI_ERRNO_BADF) {
+ // Invalid file descriptor.
+ pollfd->revents |= POLLNVAL;
+ } else if (event->error == __WASI_ERRNO_PIPE) {
+ // Hangup on write side of pipe.
+ pollfd->revents |= POLLHUP;
+ } else if (event->error != 0) {
+ // Another error occurred.
+ pollfd->revents |= POLLERR;
+ } else {
+ // Data can be read or written.
+ if (event->type == __WASI_EVENTTYPE_FD_READ) {
+ pollfd->revents |= POLLRDNORM;
+ if (event->fd_readwrite.flags & __WASI_EVENTRWFLAGS_FD_READWRITE_HANGUP) {
+ pollfd->revents |= POLLHUP;
+ }
+ } else if (event->type == __WASI_EVENTTYPE_FD_WRITE) {
+ pollfd->revents |= POLLWRNORM;
+ if (event->fd_readwrite.flags & __WASI_EVENTRWFLAGS_FD_READWRITE_HANGUP) {
+ pollfd->revents |= POLLHUP;
+ }
+ }
+ }
+ }
+ }
+
+ // Return the number of events with a non-zero revents value.
+ int retval = 0;
+ for (size_t i = 0; i < nfds; ++i) {
+ struct pollfd *pollfd = &fds[i];
+ // POLLHUP contradicts with POLLWRNORM.
+ if ((pollfd->revents & POLLHUP) != 0)
+ pollfd->revents &= ~POLLWRNORM;
+ if (pollfd->revents != 0)
+ ++retval;
+ }
+ return retval;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sched/sched_yield.c b/libc-bottom-half/cloudlibc/src/libc/sched/sched_yield.c
new file mode 100644
index 0000000..6100ea5
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sched/sched_yield.c
@@ -0,0 +1,16 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <sched.h>
+
+int sched_yield(void) {
+ __wasi_errno_t error = __wasi_sched_yield();
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/stdio/renameat.c b/libc-bottom-half/cloudlibc/src/libc/stdio/renameat.c
new file mode 100644
index 0000000..c1706db
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/stdio/renameat.c
@@ -0,0 +1,17 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+int __wasilibc_nocwd_renameat(int oldfd, const char *old, int newfd, const char *new) {
+ __wasi_errno_t error = __wasi_path_rename(oldfd, old, newfd, new);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/stdlib/_Exit.c b/libc-bottom-half/cloudlibc/src/libc/stdlib/_Exit.c
new file mode 100644
index 0000000..5e266f0
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/stdlib/_Exit.c
@@ -0,0 +1,14 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <_/cdefs.h>
+#include <stdnoreturn.h>
+#include <unistd.h>
+
+noreturn void _Exit(int status) {
+ __wasi_proc_exit(status);
+}
+
+__strong_reference(_Exit, _exit);
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/ioctl/ioctl.c b/libc-bottom-half/cloudlibc/src/libc/sys/ioctl/ioctl.c
new file mode 100644
index 0000000..7d03cc6
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/ioctl/ioctl.c
@@ -0,0 +1,88 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/ioctl.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <stdarg.h>
+
+int ioctl(int fildes, int request, ...) {
+ switch (request) {
+ case FIONREAD: {
+ // Poll the file descriptor to determine how many bytes can be read.
+ __wasi_subscription_t subscriptions[2] = {
+ {
+ .u.tag = __WASI_EVENTTYPE_FD_READ,
+ .u.u.fd_read.file_descriptor = fildes,
+ },
+ {
+ .u.tag = __WASI_EVENTTYPE_CLOCK,
+ .u.u.clock.id = __WASI_CLOCKID_MONOTONIC,
+ },
+ };
+ __wasi_event_t events[__arraycount(subscriptions)];
+ size_t nevents;
+ __wasi_errno_t error = __wasi_poll_oneoff(
+ subscriptions, events, __arraycount(subscriptions), &nevents);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+
+ // Location where result should be written.
+ va_list ap;
+ va_start(ap, request);
+ int *result = va_arg(ap, int *);
+ va_end(ap);
+
+ // Extract number of bytes for reading from poll results.
+ for (size_t i = 0; i < nevents; ++i) {
+ __wasi_event_t *event = &events[i];
+ if (event->error != 0) {
+ errno = event->error;
+ return -1;
+ }
+ if (event->type == __WASI_EVENTTYPE_FD_READ) {
+ *result = event->fd_readwrite.nbytes;
+ return 0;
+ }
+ }
+
+ // No data available for reading.
+ *result = 0;
+ return 0;
+ }
+ case FIONBIO: {
+ // Obtain the current file descriptor flags.
+ __wasi_fdstat_t fds;
+ __wasi_errno_t error = __wasi_fd_fdstat_get(fildes, &fds);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+
+ // Toggle the non-blocking flag based on the argument.
+ va_list ap;
+ va_start(ap, request);
+ if (*va_arg(ap, const int *) != 0)
+ fds.fs_flags |= __WASI_FDFLAGS_NONBLOCK;
+ else
+ fds.fs_flags &= ~__WASI_FDFLAGS_NONBLOCK;
+ va_end(ap);
+
+ // Update the file descriptor flags.
+ error = __wasi_fd_fdstat_set_flags(fildes, fds.fs_flags);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+ }
+ default:
+ // Invalid request.
+ errno = EINVAL;
+ return -1;
+ }
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/select/pselect.c b/libc-bottom-half/cloudlibc/src/libc/sys/select/pselect.c
new file mode 100644
index 0000000..fdc470e
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/select/pselect.c
@@ -0,0 +1,125 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <common/time.h>
+
+#include <sys/select.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+
+int pselect(int nfds, fd_set *restrict readfds, fd_set *restrict writefds,
+ fd_set *restrict errorfds, const struct timespec *restrict timeout,
+ const sigset_t *sigmask) {
+ // Negative file descriptor upperbound.
+ if (nfds < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ // This implementation does not support polling for exceptional
+ // conditions, such as out-of-band data on TCP sockets.
+ if (errorfds != NULL && errorfds->__nfds > 0) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ // Replace NULL pointers by the empty set.
+ fd_set empty;
+ FD_ZERO(&empty);
+ if (readfds == NULL)
+ readfds = &empty;
+ if (writefds == NULL)
+ writefds = &empty;
+
+ // Determine the maximum number of events.
+ size_t maxevents = readfds->__nfds + writefds->__nfds + 1;
+ __wasi_subscription_t subscriptions[maxevents];
+ size_t nsubscriptions = 0;
+
+ // Convert the readfds set.
+ for (size_t i = 0; i < readfds->__nfds; ++i) {
+ int fd = readfds->__fds[i];
+ if (fd < nfds) {
+ __wasi_subscription_t *subscription = &subscriptions[nsubscriptions++];
+ *subscription = (__wasi_subscription_t){
+ .userdata = fd,
+ .u.tag = __WASI_EVENTTYPE_FD_READ,
+ .u.u.fd_read.file_descriptor = fd,
+ };
+ }
+ }
+
+ // Convert the writefds set.
+ for (size_t i = 0; i < writefds->__nfds; ++i) {
+ int fd = writefds->__fds[i];
+ if (fd < nfds) {
+ __wasi_subscription_t *subscription = &subscriptions[nsubscriptions++];
+ *subscription = (__wasi_subscription_t){
+ .userdata = fd,
+ .u.tag = __WASI_EVENTTYPE_FD_WRITE,
+ .u.u.fd_write.file_descriptor = fd,
+ };
+ }
+ }
+
+ // Create extra event for the timeout.
+ if (timeout != NULL) {
+ __wasi_subscription_t *subscription = &subscriptions[nsubscriptions++];
+ *subscription = (__wasi_subscription_t){
+ .u.tag = __WASI_EVENTTYPE_CLOCK,
+ .u.u.clock.id = __WASI_CLOCKID_REALTIME,
+ };
+ if (!timespec_to_timestamp_clamp(timeout, &subscription->u.u.clock.timeout)) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ // Execute poll().
+ size_t nevents;
+ __wasi_event_t events[nsubscriptions];
+ __wasi_errno_t error =
+ __wasi_poll_oneoff(subscriptions, events, nsubscriptions, &nevents);
+ if (error != 0) {
+ // WASI's poll requires at least one subscription, or else it returns
+ // `EINVAL`. Since a `pselect` with nothing to wait for is valid in POSIX,
+ // return `ENOTSUP` to indicate that we don't support that case.
+ //
+ // Wasm has no signal handling, so if none of the user-provided `pollfd`
+ // elements, nor the timeout, led us to producing even one subscription
+ // to wait for, there would be no way for the poll to wake up. WASI
+ // returns `EINVAL` in this case, but for users of `poll`, `ENOTSUP` is
+ // more likely to be understood.
+ if (nsubscriptions == 0)
+ errno = ENOTSUP;
+ else
+ errno = error;
+ return -1;
+ }
+
+ // Test for EBADF.
+ for (size_t i = 0; i < nevents; ++i) {
+ const __wasi_event_t *event = &events[i];
+ if ((event->type == __WASI_EVENTTYPE_FD_READ ||
+ event->type == __WASI_EVENTTYPE_FD_WRITE) &&
+ event->error == __WASI_ERRNO_BADF) {
+ errno = EBADF;
+ return -1;
+ }
+ }
+
+ // Clear and set entries in the result sets.
+ FD_ZERO(readfds);
+ FD_ZERO(writefds);
+ for (size_t i = 0; i < nevents; ++i) {
+ const __wasi_event_t *event = &events[i];
+ if (event->type == __WASI_EVENTTYPE_FD_READ) {
+ readfds->__fds[readfds->__nfds++] = event->userdata;
+ } else if (event->type == __WASI_EVENTTYPE_FD_WRITE) {
+ writefds->__fds[writefds->__nfds++] = event->userdata;
+ }
+ }
+ return readfds->__nfds + writefds->__nfds;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/select/select.c b/libc-bottom-half/cloudlibc/src/libc/sys/select/select.c
new file mode 100644
index 0000000..ebe5e8c
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/select/select.c
@@ -0,0 +1,25 @@
+// Copyright (c) 2015 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/select.h>
+
+#include <errno.h>
+#include <stddef.h>
+
+int select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds,
+ fd_set *restrict errorfds, struct timeval *restrict timeout) {
+ if (timeout != NULL) {
+ // Timeout specified.
+ if (timeout->tv_usec < 0 || timeout->tv_usec >= 1000000) {
+ errno = EINVAL;
+ return -1;
+ }
+ struct timespec ts = {.tv_sec = timeout->tv_sec,
+ .tv_nsec = (long)timeout->tv_usec * 1000};
+ return pselect(nfds, readfds, writefds, errorfds, &ts, NULL);
+ } else {
+ // No timeout specified.
+ return pselect(nfds, readfds, writefds, errorfds, NULL, NULL);
+ }
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/socket/getsockopt.c b/libc-bottom-half/cloudlibc/src/libc/sys/socket/getsockopt.c
new file mode 100644
index 0000000..1fe41c4
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/socket/getsockopt.c
@@ -0,0 +1,48 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/socket.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <string.h>
+
+int getsockopt(int socket, int level, int option_name,
+ void *restrict option_value, socklen_t *restrict option_len) {
+ // Only support SOL_SOCKET options for now.
+ if (level != SOL_SOCKET) {
+ errno = ENOPROTOOPT;
+ return -1;
+ }
+
+ int value;
+ switch (option_name) {
+ case SO_TYPE: {
+ // Return the type of the socket. This information can simply be
+ // obtained by looking at the file descriptor type.
+ __wasi_fdstat_t fsb;
+ if (__wasi_fd_fdstat_get(socket, &fsb) != 0) {
+ errno = EBADF;
+ return -1;
+ }
+ if (fsb.fs_filetype != __WASI_FILETYPE_SOCKET_DGRAM &&
+ fsb.fs_filetype != __WASI_FILETYPE_SOCKET_STREAM) {
+ errno = ENOTSOCK;
+ return -1;
+ }
+ value = fsb.fs_filetype;
+ break;
+ }
+ default: {
+ errno = ENOPROTOOPT;
+ return -1;
+ }
+ }
+
+ // Copy out integer value.
+ memcpy(option_value, &value,
+ *option_len < sizeof(int) ? *option_len : sizeof(int));
+ *option_len = sizeof(int);
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/socket/recv.c b/libc-bottom-half/cloudlibc/src/libc/sys/socket/recv.c
new file mode 100644
index 0000000..d35f889
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/socket/recv.c
@@ -0,0 +1,40 @@
+// Copyright (c) 2015-2017 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/socket.h>
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <errno.h>
+#include <stdint.h>
+
+static_assert(MSG_PEEK == __WASI_RIFLAGS_RECV_PEEK, "Value mismatch");
+static_assert(MSG_WAITALL == __WASI_RIFLAGS_RECV_WAITALL, "Value mismatch");
+
+ssize_t recv(int socket, void *restrict buffer, size_t length, int flags) {
+ // Validate flags.
+ if ((flags & ~(MSG_PEEK | MSG_WAITALL)) != 0) {
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+
+ // Prepare input parameters.
+ __wasi_iovec_t iov = {.buf = buffer, .buf_len = length};
+ __wasi_iovec_t *ri_data = &iov;
+ size_t ri_data_len = 1;
+ __wasi_riflags_t ri_flags = flags;
+
+ // Perform system call.
+ size_t ro_datalen;
+ __wasi_roflags_t ro_flags;
+ __wasi_errno_t error = __wasi_sock_recv(socket,
+ ri_data, ri_data_len, ri_flags,
+ &ro_datalen,
+ &ro_flags);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return ro_datalen;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/socket/send.c b/libc-bottom-half/cloudlibc/src/libc/sys/socket/send.c
new file mode 100644
index 0000000..85a298a
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/socket/send.c
@@ -0,0 +1,32 @@
+// Copyright (c) 2015-2017 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/socket.h>
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <errno.h>
+
+ssize_t send(int socket, const void *buffer, size_t length, int flags) {
+ // This implementation does not support any flags.
+ if (flags != 0) {
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+
+ // Prepare input parameters.
+ __wasi_ciovec_t iov = {.buf = buffer, .buf_len = length};
+ __wasi_ciovec_t *si_data = &iov;
+ size_t si_data_len = 1;
+ __wasi_siflags_t si_flags = 0;
+
+ // Perform system call.
+ size_t so_datalen;
+ __wasi_errno_t error = __wasi_sock_send(socket, si_data, si_data_len, si_flags, &so_datalen);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return so_datalen;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/socket/shutdown.c b/libc-bottom-half/cloudlibc/src/libc/sys/socket/shutdown.c
new file mode 100644
index 0000000..261fcb8
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/socket/shutdown.c
@@ -0,0 +1,27 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/socket.h>
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <errno.h>
+
+static_assert(SHUT_RD == __WASI_SDFLAGS_RD, "Value mismatch");
+static_assert(SHUT_WR == __WASI_SDFLAGS_WR, "Value mismatch");
+
+int shutdown(int socket, int how) {
+ // Validate shutdown flags.
+ if (how != SHUT_RD && how != SHUT_WR && how != SHUT_RDWR) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ __wasi_errno_t error = __wasi_sock_shutdown(socket, how);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return error;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/socket/socket_impl.h b/libc-bottom-half/cloudlibc/src/libc/sys/socket/socket_impl.h
new file mode 100644
index 0000000..7b1a366
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/socket/socket_impl.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2015-2017 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#ifndef SYS_SOCKET_SOCKET_IMPL_H
+#define SYS_SOCKET_SOCKET_IMPL_H
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+
+#include <assert.h>
+#include <stdalign.h>
+#include <stddef.h>
+#include <stdint.h>
+
+static_assert(sizeof(struct sockaddr_storage) >= sizeof(struct sockaddr),
+ "struct sockaddr_storage too small");
+static_assert(alignof(struct sockaddr_storage) == alignof(struct sockaddr),
+ "struct sockaddr alignment incorrect");
+static_assert(sizeof(struct sockaddr_storage) >= sizeof(struct sockaddr_in),
+ "struct sockaddr_storage too small");
+static_assert(alignof(struct sockaddr_storage) == alignof(struct sockaddr_in),
+ "struct sockaddr_in alignment incorrect");
+static_assert(sizeof(struct sockaddr_storage) >= sizeof(struct sockaddr_in6),
+ "struct sockaddr_storage too small");
+static_assert(alignof(struct sockaddr_storage) == alignof(struct sockaddr_in6),
+ "struct sockaddr_in6 alignment incorrect");
+static_assert(sizeof(struct sockaddr_storage) >= sizeof(struct sockaddr_un),
+ "struct sockaddr_storage too small");
+static_assert(alignof(struct sockaddr_storage) == alignof(struct sockaddr_un),
+ "struct sockaddr_un alignment incorrect");
+
+// Returns the control message header stored at a provided memory
+// address, ensuring that it is stored within the ancillary data buffer
+// of a message header.
+static inline struct cmsghdr *CMSG_GET(const struct msghdr *mhdr, void *cmsg) {
+ // Safety belt: require that the returned object is properly aligned.
+ assert((uintptr_t)cmsg % alignof(struct cmsghdr) == 0 &&
+ "Attempted to access unaligned control message header");
+
+ // Safety belt: the computed starting address of the control message
+ // header may only lie inside the ancillary data buffer, or right
+ // after it in case we've reached the end of the buffer.
+ const unsigned char *begin = mhdr->msg_control;
+ const unsigned char *end = begin + mhdr->msg_controllen;
+ assert((unsigned char *)cmsg >= begin &&
+ (unsigned char *)cmsg < end + alignof(struct cmsghdr) &&
+ "Computed object outside of buffer boundaries");
+
+ // Only return the control message header in case all of its fields
+ // lie within the ancillary data buffer.
+ return CMSG_DATA((struct cmsghdr *)cmsg) <= end ? cmsg : NULL;
+}
+
+#endif
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstat.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstat.c
new file mode 100644
index 0000000..b8ffdb5
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstat.c
@@ -0,0 +1,21 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/stat.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+
+#include "stat_impl.h"
+
+int fstat(int fildes, struct stat *buf) {
+ __wasi_filestat_t internal_stat;
+ __wasi_errno_t error = __wasi_fd_filestat_get(fildes, &internal_stat);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ to_public_stat(&internal_stat, buf);
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c
new file mode 100644
index 0000000..25b29ac
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c
@@ -0,0 +1,31 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/stat.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "stat_impl.h"
+
+int __wasilibc_nocwd_fstatat(int fd, const char *restrict path, struct stat *restrict buf,
+ int flag) {
+ // Create lookup properties.
+ __wasi_lookupflags_t lookup_flags = 0;
+ if ((flag & AT_SYMLINK_NOFOLLOW) == 0)
+ lookup_flags |= __WASI_LOOKUPFLAGS_SYMLINK_FOLLOW;
+
+ // Perform system call.
+ __wasi_filestat_t internal_stat;
+ __wasi_errno_t error =
+ __wasi_path_filestat_get(fd, lookup_flags, path, &internal_stat);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ to_public_stat(&internal_stat, buf);
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/futimens.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/futimens.c
new file mode 100644
index 0000000..13357fc
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/futimens.c
@@ -0,0 +1,29 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/stat.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+
+#include "stat_impl.h"
+
+int futimens(int fd, const struct timespec *times) {
+ // Convert timestamps and extract NOW/OMIT flags.
+ __wasi_timestamp_t st_atim;
+ __wasi_timestamp_t st_mtim;
+ __wasi_fstflags_t flags;
+ if (!utimens_get_timestamps(times, &st_atim, &st_mtim, &flags)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ // Perform system call.
+ __wasi_errno_t error = __wasi_fd_filestat_set_times(fd, st_atim, st_mtim, flags);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/mkdirat.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/mkdirat.c
new file mode 100644
index 0000000..fd27d5e
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/mkdirat.c
@@ -0,0 +1,18 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/stat.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <string.h>
+
+int __wasilibc_nocwd_mkdirat_nomode(int fd, const char *path) {
+ __wasi_errno_t error = __wasi_path_create_directory(fd, path);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/stat_impl.h b/libc-bottom-half/cloudlibc/src/libc/sys/stat/stat_impl.h
new file mode 100644
index 0000000..9726b51
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/stat_impl.h
@@ -0,0 +1,116 @@
+// Copyright (c) 2015-2017 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#ifndef SYS_STAT_STAT_IMPL_H
+#define SYS_STAT_STAT_IMPL_H
+
+#include <common/time.h>
+
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <stdbool.h>
+
+static_assert(S_ISBLK(S_IFBLK), "Value mismatch");
+static_assert(S_ISCHR(S_IFCHR), "Value mismatch");
+static_assert(S_ISDIR(S_IFDIR), "Value mismatch");
+static_assert(S_ISFIFO(S_IFIFO), "Value mismatch");
+static_assert(S_ISLNK(S_IFLNK), "Value mismatch");
+static_assert(S_ISREG(S_IFREG), "Value mismatch");
+static_assert(S_ISSOCK(S_IFSOCK), "Value mismatch");
+
+static inline void to_public_stat(const __wasi_filestat_t *in,
+ struct stat *out) {
+ // Ensure that we don't truncate any values.
+ static_assert(sizeof(in->dev) == sizeof(out->st_dev), "Size mismatch");
+ static_assert(sizeof(in->ino) == sizeof(out->st_ino), "Size mismatch");
+ /*
+ * The non-standard __st_filetype field appears to only be used for shared
+ * memory, which we don't currently support.
+ */
+ /* nlink_t is 64-bit on wasm32, following the x32 ABI. */
+ static_assert(sizeof(in->nlink) <= sizeof(out->st_nlink), "Size shortfall");
+ static_assert(sizeof(in->size) == sizeof(out->st_size), "Size mismatch");
+
+ *out = (struct stat){
+ .st_dev = in->dev,
+ .st_ino = in->ino,
+ .st_nlink = in->nlink,
+ .st_size = in->size,
+ .st_atim = timestamp_to_timespec(in->atim),
+ .st_mtim = timestamp_to_timespec(in->mtim),
+ .st_ctim = timestamp_to_timespec(in->ctim),
+ };
+
+ // Convert file type to legacy types encoded in st_mode.
+ switch (in->filetype) {
+ case __WASI_FILETYPE_BLOCK_DEVICE:
+ out->st_mode |= S_IFBLK;
+ break;
+ case __WASI_FILETYPE_CHARACTER_DEVICE:
+ out->st_mode |= S_IFCHR;
+ break;
+ case __WASI_FILETYPE_DIRECTORY:
+ out->st_mode |= S_IFDIR;
+ break;
+ case __WASI_FILETYPE_REGULAR_FILE:
+ out->st_mode |= S_IFREG;
+ break;
+ case __WASI_FILETYPE_SOCKET_DGRAM:
+ case __WASI_FILETYPE_SOCKET_STREAM:
+ out->st_mode |= S_IFSOCK;
+ break;
+ case __WASI_FILETYPE_SYMBOLIC_LINK:
+ out->st_mode |= S_IFLNK;
+ break;
+ }
+}
+
+static inline bool utimens_get_timestamps(const struct timespec *times,
+ __wasi_timestamp_t *st_atim,
+ __wasi_timestamp_t *st_mtim,
+ __wasi_fstflags_t *flags) {
+ if (times == NULL) {
+ // Update both timestamps.
+ *flags = __WASI_FSTFLAGS_ATIM_NOW | __WASI_FSTFLAGS_MTIM_NOW;
+ *st_atim = (__wasi_timestamp_t) { 0 };
+ *st_mtim = (__wasi_timestamp_t) { 0 };
+ } else {
+ // Set individual timestamps.
+ *flags = 0;
+ switch (times[0].tv_nsec) {
+ case UTIME_NOW:
+ *flags |= __WASI_FSTFLAGS_ATIM_NOW;
+ *st_atim = (__wasi_timestamp_t) { 0 };
+ break;
+ case UTIME_OMIT:
+ *st_atim = (__wasi_timestamp_t) { 0 };
+ break;
+ default:
+ *flags |= __WASI_FSTFLAGS_ATIM;
+ if (!timespec_to_timestamp_exact(&times[0], st_atim))
+ return false;
+ break;
+ }
+
+ switch (times[1].tv_nsec) {
+ case UTIME_NOW:
+ *flags |= __WASI_FSTFLAGS_MTIM_NOW;
+ *st_mtim = (__wasi_timestamp_t) { 0 };
+ break;
+ case UTIME_OMIT:
+ *st_mtim = (__wasi_timestamp_t) { 0 };
+ break;
+ default:
+ *flags |= __WASI_FSTFLAGS_MTIM;
+ if (!timespec_to_timestamp_exact(&times[1], st_mtim))
+ return false;
+ break;
+ }
+ }
+ return true;
+}
+
+#endif
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/utimensat.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/utimensat.c
new file mode 100644
index 0000000..19508a1
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/utimensat.c
@@ -0,0 +1,38 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/stat.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "stat_impl.h"
+
+int __wasilibc_nocwd_utimensat(int fd, const char *path, const struct timespec times[2],
+ int flag) {
+ // Convert timestamps and extract NOW/OMIT flags.
+ __wasi_timestamp_t st_atim;
+ __wasi_timestamp_t st_mtim;
+ __wasi_fstflags_t flags;
+ if (!utimens_get_timestamps(times, &st_atim, &st_mtim, &flags)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ // Create lookup properties.
+ __wasi_lookupflags_t lookup_flags = 0;
+ if ((flag & AT_SYMLINK_NOFOLLOW) == 0)
+ lookup_flags |= __WASI_LOOKUPFLAGS_SYMLINK_FOLLOW;
+
+ // Perform system call.
+ __wasi_errno_t error =
+ __wasi_path_filestat_set_times(fd, lookup_flags, path, st_atim, st_mtim, flags);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/time/gettimeofday.c b/libc-bottom-half/cloudlibc/src/libc/sys/time/gettimeofday.c
new file mode 100644
index 0000000..596bdff
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/time/gettimeofday.c
@@ -0,0 +1,18 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <common/time.h>
+
+#include <sys/time.h>
+
+#include <wasi/api.h>
+
+int gettimeofday(struct timeval *restrict tp, void *tz) {
+ if (tp != NULL) {
+ __wasi_timestamp_t ts = 0;
+ (void)__wasi_clock_time_get(__WASI_CLOCKID_REALTIME, 1000, &ts);
+ *tp = timestamp_to_timeval(ts);
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/uio/preadv.c b/libc-bottom-half/cloudlibc/src/libc/sys/uio/preadv.c
new file mode 100644
index 0000000..36f882d
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/uio/preadv.c
@@ -0,0 +1,24 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+
+ssize_t preadv(int fildes, const struct iovec *iov, int iovcnt, off_t offset) {
+ if (iovcnt < 0 || offset < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ size_t bytes_read;
+ __wasi_errno_t error = __wasi_fd_pread(
+ fildes, (const __wasi_iovec_t *)iov, iovcnt, offset, &bytes_read);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return bytes_read;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/uio/pwritev.c b/libc-bottom-half/cloudlibc/src/libc/sys/uio/pwritev.c
new file mode 100644
index 0000000..d6f8851
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/uio/pwritev.c
@@ -0,0 +1,24 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+
+ssize_t pwritev(int fildes, const struct iovec *iov, int iovcnt, off_t offset) {
+ if (iovcnt < 0 || offset < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ size_t bytes_written;
+ __wasi_errno_t error = __wasi_fd_pwrite(
+ fildes, (const __wasi_ciovec_t *)iov, iovcnt, offset, &bytes_written);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return bytes_written;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/uio/readv.c b/libc-bottom-half/cloudlibc/src/libc/sys/uio/readv.c
new file mode 100644
index 0000000..e3eaebe
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/uio/readv.c
@@ -0,0 +1,40 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/uio.h>
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <errno.h>
+#include <stddef.h>
+
+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");
+
+ssize_t readv(int fildes, const struct iovec *iov, int iovcnt) {
+ if (iovcnt < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ size_t bytes_read;
+ __wasi_errno_t error = __wasi_fd_read(
+ fildes, (const __wasi_iovec_t *)iov, iovcnt, &bytes_read);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return bytes_read;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/uio/writev.c b/libc-bottom-half/cloudlibc/src/libc/sys/uio/writev.c
new file mode 100644
index 0000000..459b152
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/uio/writev.c
@@ -0,0 +1,40 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/uio.h>
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <errno.h>
+#include <stddef.h>
+
+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");
+
+ssize_t writev(int fildes, const struct iovec *iov, int iovcnt) {
+ if (iovcnt < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ size_t bytes_written;
+ __wasi_errno_t error = __wasi_fd_write(
+ fildes, (const __wasi_ciovec_t *)iov, iovcnt, &bytes_written);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return bytes_written;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/time/CLOCK_MONOTONIC.c b/libc-bottom-half/cloudlibc/src/libc/time/CLOCK_MONOTONIC.c
new file mode 100644
index 0000000..a4c4a62
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/time/CLOCK_MONOTONIC.c
@@ -0,0 +1,12 @@
+// Copyright (c) 2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <common/clock.h>
+
+#include <wasi/api.h>
+#include <time.h>
+
+const struct __clockid _CLOCK_MONOTONIC = {
+ .id = __WASI_CLOCKID_MONOTONIC,
+};
diff --git a/libc-bottom-half/cloudlibc/src/libc/time/CLOCK_REALTIME.c b/libc-bottom-half/cloudlibc/src/libc/time/CLOCK_REALTIME.c
new file mode 100644
index 0000000..9523754
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/time/CLOCK_REALTIME.c
@@ -0,0 +1,12 @@
+// Copyright (c) 2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <common/clock.h>
+
+#include <wasi/api.h>
+#include <time.h>
+
+const struct __clockid _CLOCK_REALTIME = {
+ .id = __WASI_CLOCKID_REALTIME,
+};
diff --git a/libc-bottom-half/cloudlibc/src/libc/time/clock_getres.c b/libc-bottom-half/cloudlibc/src/libc/time/clock_getres.c
new file mode 100644
index 0000000..8030d4b
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/time/clock_getres.c
@@ -0,0 +1,21 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <common/clock.h>
+#include <common/time.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <time.h>
+
+int clock_getres(clockid_t clock_id, struct timespec *res) {
+ __wasi_timestamp_t ts;
+ __wasi_errno_t error = __wasi_clock_res_get(clock_id->id, &ts);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ *res = timestamp_to_timespec(ts);
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/time/clock_gettime.c b/libc-bottom-half/cloudlibc/src/libc/time/clock_gettime.c
new file mode 100644
index 0000000..c7e1a60
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/time/clock_gettime.c
@@ -0,0 +1,22 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <common/clock.h>
+#include <common/time.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <time.h>
+
+int __clock_gettime(clockid_t clock_id, struct timespec *tp) {
+ __wasi_timestamp_t ts;
+ __wasi_errno_t error = __wasi_clock_time_get(clock_id->id, 1, &ts);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ *tp = timestamp_to_timespec(ts);
+ return 0;
+}
+weak_alias(__clock_gettime, clock_gettime);
diff --git a/libc-bottom-half/cloudlibc/src/libc/time/clock_nanosleep.c b/libc-bottom-half/cloudlibc/src/libc/time/clock_nanosleep.c
new file mode 100644
index 0000000..d375056
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/time/clock_nanosleep.c
@@ -0,0 +1,37 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <common/clock.h>
+#include <common/time.h>
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <errno.h>
+#include <time.h>
+
+static_assert(TIMER_ABSTIME == __WASI_SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME,
+ "Value mismatch");
+
+int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *rqtp,
+ struct timespec *rmtp) {
+ if ((flags & ~TIMER_ABSTIME) != 0)
+ return EINVAL;
+
+ // Prepare polling subscription.
+ __wasi_subscription_t sub = {
+ .u.tag = __WASI_EVENTTYPE_CLOCK,
+ .u.u.clock.id = clock_id->id,
+ .u.u.clock.flags = flags,
+ };
+ if (!timespec_to_timestamp_clamp(rqtp, &sub.u.u.clock.timeout))
+ return EINVAL;
+
+ // Block until polling event is triggered.
+ size_t nevents;
+ __wasi_event_t ev;
+ __wasi_errno_t error = __wasi_poll_oneoff(&sub, &ev, 1, &nevents);
+ return error == 0 && ev.error == 0 ? 0 : ENOTSUP;
+}
+
+weak_alias(clock_nanosleep, __clock_nanosleep);
diff --git a/libc-bottom-half/cloudlibc/src/libc/time/nanosleep.c b/libc-bottom-half/cloudlibc/src/libc/time/nanosleep.c
new file mode 100644
index 0000000..5f26c5c
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/time/nanosleep.c
@@ -0,0 +1,15 @@
+// Copyright (c) 2015 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <errno.h>
+#include <time.h>
+
+int nanosleep(const struct timespec *rqtp, struct timespec *rem) {
+ int error = clock_nanosleep(CLOCK_REALTIME, 0, rqtp, rem);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/time/time.c b/libc-bottom-half/cloudlibc/src/libc/time/time.c
new file mode 100644
index 0000000..52bc0e4
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/time/time.c
@@ -0,0 +1,16 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <common/time.h>
+
+#include <wasi/api.h>
+#include <time.h>
+
+time_t time(time_t *tloc) {
+ __wasi_timestamp_t ts = 0;
+ (void)__wasi_clock_time_get(__WASI_CLOCKID_REALTIME, NSEC_PER_SEC, &ts);
+ if (tloc != NULL)
+ *tloc = ts / NSEC_PER_SEC;
+ return ts / NSEC_PER_SEC;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/close.c b/libc-bottom-half/cloudlibc/src/libc/unistd/close.c
new file mode 100644
index 0000000..2f5814b
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/close.c
@@ -0,0 +1,16 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <unistd.h>
+
+int close(int fildes) {
+ __wasi_errno_t error = __wasi_fd_close(fildes);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/faccessat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/faccessat.c
new file mode 100644
index 0000000..ffaef6e
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/faccessat.c
@@ -0,0 +1,53 @@
+// Copyright (c) 2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+int __wasilibc_nocwd_faccessat(int fd, const char *path, int amode, int flag) {
+ // Validate function parameters.
+ if ((amode & ~(F_OK | R_OK | W_OK | X_OK)) != 0 ||
+ (flag & ~AT_EACCESS) != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ // Check for target file existence and obtain the file type.
+ __wasi_lookupflags_t lookup_flags = __WASI_LOOKUPFLAGS_SYMLINK_FOLLOW;
+ __wasi_filestat_t file;
+ __wasi_errno_t error =
+ __wasi_path_filestat_get(fd, lookup_flags, path, &file);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+
+ // Test whether the requested access rights are present on the
+ // directory file descriptor.
+ if (amode != 0) {
+ __wasi_fdstat_t directory;
+ error = __wasi_fd_fdstat_get(fd, &directory);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+
+ __wasi_rights_t min = 0;
+ if ((amode & R_OK) != 0)
+ min |= file.filetype == __WASI_FILETYPE_DIRECTORY
+ ? __WASI_RIGHTS_FD_READDIR
+ : __WASI_RIGHTS_FD_READ;
+ if ((amode & W_OK) != 0)
+ min |= __WASI_RIGHTS_FD_WRITE;
+
+ if ((min & directory.fs_rights_inheriting) != min) {
+ errno = EACCES;
+ return -1;
+ }
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/fdatasync.c b/libc-bottom-half/cloudlibc/src/libc/unistd/fdatasync.c
new file mode 100644
index 0000000..b821b06
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/fdatasync.c
@@ -0,0 +1,16 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <unistd.h>
+
+int fdatasync(int fildes) {
+ __wasi_errno_t error = __wasi_fd_datasync(fildes);
+ if (error != 0) {
+ errno = error == ENOTCAPABLE ? EBADF : error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/fsync.c b/libc-bottom-half/cloudlibc/src/libc/unistd/fsync.c
new file mode 100644
index 0000000..733e294
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/fsync.c
@@ -0,0 +1,16 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <unistd.h>
+
+int fsync(int fildes) {
+ __wasi_errno_t error = __wasi_fd_sync(fildes);
+ if (error != 0) {
+ errno = error == ENOTCAPABLE ? EINVAL : error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/ftruncate.c b/libc-bottom-half/cloudlibc/src/libc/unistd/ftruncate.c
new file mode 100644
index 0000000..7792597
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/ftruncate.c
@@ -0,0 +1,22 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <unistd.h>
+
+int ftruncate(int fildes, off_t length) {
+ if (length < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ __wasi_filesize_t st_size = length;
+ __wasi_errno_t error =
+ __wasi_fd_filestat_set_size(fildes, st_size);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/linkat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/linkat.c
new file mode 100644
index 0000000..d57f562
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/linkat.c
@@ -0,0 +1,24 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+int __wasilibc_nocwd_linkat(int fd1, const char *path1, int fd2, const char *path2, int flag) {
+ // Create lookup properties.
+ __wasi_lookupflags_t lookup1_flags = 0;
+ if ((flag & AT_SYMLINK_FOLLOW) != 0)
+ lookup1_flags |= __WASI_LOOKUPFLAGS_SYMLINK_FOLLOW;
+
+ // Perform system call.
+ __wasi_errno_t error = __wasi_path_link(fd1, lookup1_flags, path1, fd2, path2);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/lseek.c b/libc-bottom-half/cloudlibc/src/libc/unistd/lseek.c
new file mode 100644
index 0000000..3e0429f
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/lseek.c
@@ -0,0 +1,25 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <errno.h>
+#include <unistd.h>
+
+static_assert(SEEK_CUR == __WASI_WHENCE_CUR, "Value mismatch");
+static_assert(SEEK_END == __WASI_WHENCE_END, "Value mismatch");
+static_assert(SEEK_SET == __WASI_WHENCE_SET, "Value mismatch");
+
+off_t __lseek(int fildes, off_t offset, int whence) {
+ __wasi_filesize_t new_offset;
+ __wasi_errno_t error =
+ __wasi_fd_seek(fildes, offset, whence, &new_offset);
+ if (error != 0) {
+ errno = error == ENOTCAPABLE ? ESPIPE : error;
+ return -1;
+ }
+ return new_offset;
+}
+
+weak_alias(__lseek, lseek);
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/pread.c b/libc-bottom-half/cloudlibc/src/libc/unistd/pread.c
new file mode 100644
index 0000000..c9944bc
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/pread.c
@@ -0,0 +1,31 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <unistd.h>
+
+ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset) {
+ if (offset < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ __wasi_iovec_t iov = {.buf = buf, .buf_len = nbyte};
+ size_t bytes_read;
+ __wasi_errno_t error =
+ __wasi_fd_pread(fildes, &iov, 1, offset, &bytes_read);
+ if (error != 0) {
+ __wasi_fdstat_t fds;
+ if (error == ENOTCAPABLE && __wasi_fd_fdstat_get(fildes, &fds) == 0) {
+ // Determine why we got ENOTCAPABLE.
+ if ((fds.fs_rights_base & __WASI_RIGHTS_FD_READ) == 0)
+ error = EBADF;
+ else
+ error = ESPIPE;
+ }
+ errno = error;
+ return -1;
+ }
+ return bytes_read;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/pwrite.c b/libc-bottom-half/cloudlibc/src/libc/unistd/pwrite.c
new file mode 100644
index 0000000..f80bc40
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/pwrite.c
@@ -0,0 +1,31 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <unistd.h>
+
+ssize_t pwrite(int fildes, const void *buf, size_t nbyte, off_t offset) {
+ if (offset < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ __wasi_ciovec_t iov = {.buf = buf, .buf_len = nbyte};
+ size_t bytes_written;
+ __wasi_errno_t error =
+ __wasi_fd_pwrite(fildes, &iov, 1, offset, &bytes_written);
+ if (error != 0) {
+ __wasi_fdstat_t fds;
+ if (error == ENOTCAPABLE && __wasi_fd_fdstat_get(fildes, &fds) == 0) {
+ // Determine why we got ENOTCAPABLE.
+ if ((fds.fs_rights_base & __WASI_RIGHTS_FD_WRITE) == 0)
+ error = EBADF;
+ else
+ error = ESPIPE;
+ }
+ errno = error;
+ return -1;
+ }
+ return bytes_written;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/read.c b/libc-bottom-half/cloudlibc/src/libc/unistd/read.c
new file mode 100644
index 0000000..1582126
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/read.c
@@ -0,0 +1,18 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <unistd.h>
+
+ssize_t read(int fildes, void *buf, size_t nbyte) {
+ __wasi_iovec_t iov = {.buf = buf, .buf_len = nbyte};
+ size_t bytes_read;
+ __wasi_errno_t error = __wasi_fd_read(fildes, &iov, 1, &bytes_read);
+ if (error != 0) {
+ errno = error == ENOTCAPABLE ? EBADF : error;
+ return -1;
+ }
+ return bytes_read;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/readlinkat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/readlinkat.c
new file mode 100644
index 0000000..7a3bce2
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/readlinkat.c
@@ -0,0 +1,21 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+ssize_t __wasilibc_nocwd_readlinkat(int fd, const char *restrict path, char *restrict buf,
+ size_t bufsize) {
+ size_t bufused;
+ // TODO: Remove the cast on `buf` once the witx is updated with char8 support.
+ __wasi_errno_t error = __wasi_path_readlink(fd, path,
+ (uint8_t*)buf, bufsize, &bufused);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return bufused;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/sleep.c b/libc-bottom-half/cloudlibc/src/libc/unistd/sleep.c
new file mode 100644
index 0000000..970287b
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/sleep.c
@@ -0,0 +1,13 @@
+// Copyright (c) 2015 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <time.h>
+#include <unistd.h>
+
+unsigned int sleep(unsigned int seconds) {
+ struct timespec ts = {.tv_sec = seconds, .tv_nsec = 0};
+ if (clock_nanosleep(CLOCK_REALTIME, 0, &ts, NULL) != 0)
+ return seconds;
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/symlinkat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/symlinkat.c
new file mode 100644
index 0000000..0aa38be
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/symlinkat.c
@@ -0,0 +1,17 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+int __wasilibc_nocwd_symlinkat(const char *path1, int fd, const char *path2) {
+ __wasi_errno_t error = __wasi_path_symlink(path1, fd, path2);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/unlinkat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/unlinkat.c
new file mode 100644
index 0000000..351bf92
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/unlinkat.c
@@ -0,0 +1,17 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <wasi/libc.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+int unlinkat(int fd, const char *path, int flag) {
+ if ((flag & AT_REMOVEDIR) != 0) {
+ return __wasilibc_rmdirat(fd, path);
+ }
+ return __wasilibc_unlinkat(fd, path);
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/usleep.c b/libc-bottom-half/cloudlibc/src/libc/unistd/usleep.c
new file mode 100644
index 0000000..8005f17
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/usleep.c
@@ -0,0 +1,18 @@
+// Copyright (c) 2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <errno.h>
+#include <time.h>
+#include <unistd.h>
+
+int usleep(useconds_t useconds) {
+ struct timespec ts = {.tv_sec = useconds / 1000000,
+ .tv_nsec = useconds % 1000000 * 1000};
+ int error = clock_nanosleep(CLOCK_REALTIME, 0, &ts, NULL);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/write.c b/libc-bottom-half/cloudlibc/src/libc/unistd/write.c
new file mode 100644
index 0000000..a6567e8
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/write.c
@@ -0,0 +1,19 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <unistd.h>
+
+ssize_t write(int fildes, const void *buf, size_t nbyte) {
+ __wasi_ciovec_t iov = {.buf = buf, .buf_len = nbyte};
+ size_t bytes_written;
+ __wasi_errno_t error =
+ __wasi_fd_write(fildes, &iov, 1, &bytes_written);
+ if (error != 0) {
+ errno = error == ENOTCAPABLE ? EBADF : error;
+ return -1;
+ }
+ return bytes_written;
+}