diff options
Diffstat (limited to 'libc-bottom-half/cloudlibc')
67 files changed, 2426 insertions, 0 deletions
diff --git a/libc-bottom-half/cloudlibc/LICENSE b/libc-bottom-half/cloudlibc/LICENSE new file mode 100644 index 0000000..dd33b05 --- /dev/null +++ b/libc-bottom-half/cloudlibc/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2015-2017 Nuxi (https://nuxi.nl/) and contributors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/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 = ∅ + if (writefds == NULL) + writefds = ∅ + + // 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(×[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(×[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; +} |