|
|
@@ -0,0 +1,1014 @@
|
|
|
+/*
|
|
|
+ * Copyright (C) 2023 Intel Corporation. All rights reserved.
|
|
|
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
+ */
|
|
|
+
|
|
|
+#include "platform_api_extension.h"
|
|
|
+#include "libc_errno.h"
|
|
|
+#include <unistd.h>
|
|
|
+
|
|
|
+#if !defined(__APPLE__) && !defined(ESP_PLATFORM)
|
|
|
+#define CONFIG_HAS_PWRITEV 1
|
|
|
+#define CONFIG_HAS_PREADV 1
|
|
|
+#else
|
|
|
+#define CONFIG_HAS_PWRITEV 0
|
|
|
+#define CONFIG_HAS_PREADV 0
|
|
|
+#endif
|
|
|
+
|
|
|
+#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(ESP_PLATFORM)
|
|
|
+#define CONFIG_HAS_FDATASYNC 1
|
|
|
+#else
|
|
|
+#define CONFIG_HAS_FDATASYNC 0
|
|
|
+#endif
|
|
|
+
|
|
|
+/*
|
|
|
+ * For NuttX, CONFIG_HAS_ISATTY is provided by its platform header.
|
|
|
+ * (platform_internal.h)
|
|
|
+ */
|
|
|
+#if !defined(CONFIG_HAS_D_INO)
|
|
|
+#if !defined(__NuttX__)
|
|
|
+#define CONFIG_HAS_D_INO 1
|
|
|
+#define CONFIG_HAS_ISATTY 1
|
|
|
+#else
|
|
|
+#define CONFIG_HAS_D_INO 0
|
|
|
+#endif
|
|
|
+#endif
|
|
|
+
|
|
|
+#if !defined(__APPLE__) && !defined(ESP_PLATFORM) && !defined(__COSMOPOLITAN__)
|
|
|
+#define CONFIG_HAS_POSIX_FALLOCATE 1
|
|
|
+#else
|
|
|
+#define CONFIG_HAS_POSIX_FALLOCATE 0
|
|
|
+#endif
|
|
|
+
|
|
|
+#if defined(O_DSYNC)
|
|
|
+#define CONFIG_HAS_O_DSYNC
|
|
|
+#endif
|
|
|
+
|
|
|
+// POSIX requires O_RSYNC to be defined, but Linux explicitly doesn't support
|
|
|
+// it.
|
|
|
+#if defined(O_RSYNC) && !defined(__linux__)
|
|
|
+#define CONFIG_HAS_O_RSYNC
|
|
|
+#endif
|
|
|
+
|
|
|
+#if defined(O_SYNC)
|
|
|
+#define CONFIG_HAS_O_SYNC
|
|
|
+#endif
|
|
|
+
|
|
|
+// Converts a POSIX timespec to a WASI timestamp.
|
|
|
+static __wasi_timestamp_t
|
|
|
+convert_timespec(const struct timespec *ts)
|
|
|
+{
|
|
|
+ if (ts->tv_sec < 0)
|
|
|
+ return 0;
|
|
|
+ if ((__wasi_timestamp_t)ts->tv_sec >= UINT64_MAX / 1000000000)
|
|
|
+ return UINT64_MAX;
|
|
|
+ return (__wasi_timestamp_t)ts->tv_sec * 1000000000
|
|
|
+ + (__wasi_timestamp_t)ts->tv_nsec;
|
|
|
+}
|
|
|
+
|
|
|
+// Converts a POSIX stat structure to a WASI filestat structure
|
|
|
+static void
|
|
|
+convert_stat(os_file_handle handle, const struct stat *in,
|
|
|
+ __wasi_filestat_t *out)
|
|
|
+{
|
|
|
+ out->st_dev = in->st_dev;
|
|
|
+ out->st_ino = in->st_ino;
|
|
|
+ out->st_nlink = (__wasi_linkcount_t)in->st_nlink;
|
|
|
+ out->st_size = (__wasi_filesize_t)in->st_size;
|
|
|
+#ifdef __APPLE__
|
|
|
+ out->st_atim = convert_timespec(&in->st_atimespec);
|
|
|
+ out->st_mtim = convert_timespec(&in->st_mtimespec);
|
|
|
+ out->st_ctim = convert_timespec(&in->st_ctimespec);
|
|
|
+#else
|
|
|
+ out->st_atim = convert_timespec(&in->st_atim);
|
|
|
+ out->st_mtim = convert_timespec(&in->st_mtim);
|
|
|
+ out->st_ctim = convert_timespec(&in->st_ctim);
|
|
|
+#endif
|
|
|
+
|
|
|
+ // Convert the file type. In the case of sockets there is no way we
|
|
|
+ // can easily determine the exact socket type.
|
|
|
+ if (S_ISBLK(in->st_mode)) {
|
|
|
+ out->st_filetype = __WASI_FILETYPE_BLOCK_DEVICE;
|
|
|
+ }
|
|
|
+ else if (S_ISCHR(in->st_mode)) {
|
|
|
+ out->st_filetype = __WASI_FILETYPE_CHARACTER_DEVICE;
|
|
|
+ }
|
|
|
+ else if (S_ISDIR(in->st_mode)) {
|
|
|
+ out->st_filetype = __WASI_FILETYPE_DIRECTORY;
|
|
|
+ }
|
|
|
+ else if (S_ISFIFO(in->st_mode)) {
|
|
|
+ out->st_filetype = __WASI_FILETYPE_SOCKET_STREAM;
|
|
|
+ }
|
|
|
+ else if (S_ISLNK(in->st_mode)) {
|
|
|
+ out->st_filetype = __WASI_FILETYPE_SYMBOLIC_LINK;
|
|
|
+ }
|
|
|
+ else if (S_ISREG(in->st_mode)) {
|
|
|
+ out->st_filetype = __WASI_FILETYPE_REGULAR_FILE;
|
|
|
+ }
|
|
|
+ else if (S_ISSOCK(in->st_mode)) {
|
|
|
+ int socktype;
|
|
|
+ socklen_t socktypelen = sizeof(socktype);
|
|
|
+
|
|
|
+ if (getsockopt(handle, SOL_SOCKET, SO_TYPE, &socktype, &socktypelen)
|
|
|
+ < 0) {
|
|
|
+ out->st_filetype = __WASI_FILETYPE_UNKNOWN;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (socktype) {
|
|
|
+ case SOCK_DGRAM:
|
|
|
+ out->st_filetype = __WASI_FILETYPE_SOCKET_DGRAM;
|
|
|
+ break;
|
|
|
+ case SOCK_STREAM:
|
|
|
+ out->st_filetype = __WASI_FILETYPE_SOCKET_STREAM;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ out->st_filetype = __WASI_FILETYPE_UNKNOWN;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ out->st_filetype = __WASI_FILETYPE_UNKNOWN;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+convert_timestamp(__wasi_timestamp_t in, struct timespec *out)
|
|
|
+{
|
|
|
+ // Store sub-second remainder.
|
|
|
+#if defined(__SYSCALL_SLONG_TYPE)
|
|
|
+ out->tv_nsec = (__SYSCALL_SLONG_TYPE)(in % 1000000000);
|
|
|
+#else
|
|
|
+ out->tv_nsec = (long)(in % 1000000000);
|
|
|
+#endif
|
|
|
+ in /= 1000000000;
|
|
|
+
|
|
|
+ // Clamp to the maximum in case it would overflow our system's time_t.
|
|
|
+ out->tv_sec = (time_t)in < BH_TIME_T_MAX ? (time_t)in : BH_TIME_T_MAX;
|
|
|
+}
|
|
|
+
|
|
|
+// Converts the provided timestamps and flags to a set of arguments for
|
|
|
+// futimens() and utimensat().
|
|
|
+static void
|
|
|
+convert_utimens_arguments(__wasi_timestamp_t st_atim,
|
|
|
+ __wasi_timestamp_t st_mtim,
|
|
|
+ __wasi_fstflags_t fstflags, struct timespec *ts)
|
|
|
+{
|
|
|
+ if ((fstflags & __WASI_FILESTAT_SET_ATIM_NOW) != 0) {
|
|
|
+ ts[0].tv_nsec = UTIME_NOW;
|
|
|
+ }
|
|
|
+ else if ((fstflags & __WASI_FILESTAT_SET_ATIM) != 0) {
|
|
|
+ convert_timestamp(st_atim, &ts[0]);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ ts[0].tv_nsec = UTIME_OMIT;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((fstflags & __WASI_FILESTAT_SET_MTIM_NOW) != 0) {
|
|
|
+ ts[1].tv_nsec = UTIME_NOW;
|
|
|
+ }
|
|
|
+ else if ((fstflags & __WASI_FILESTAT_SET_MTIM) != 0) {
|
|
|
+ convert_timestamp(st_mtim, &ts[1]);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ ts[1].tv_nsec = UTIME_OMIT;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_fstat(os_file_handle handle, struct __wasi_filestat_t *buf)
|
|
|
+{
|
|
|
+ struct stat stat_buf;
|
|
|
+ int ret = fstat(handle, &stat_buf);
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ convert_stat(handle, &stat_buf, buf);
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_fstatat(os_file_handle handle, const char *path,
|
|
|
+ struct __wasi_filestat_t *buf, __wasi_lookupflags_t lookup_flags)
|
|
|
+{
|
|
|
+ struct stat stat_buf;
|
|
|
+ int ret = fstatat(handle, path, &stat_buf,
|
|
|
+ (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW)
|
|
|
+ ? AT_SYMLINK_FOLLOW
|
|
|
+ : AT_SYMLINK_NOFOLLOW);
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ convert_stat(handle, &stat_buf, buf);
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_file_get_fdflags(os_file_handle handle, __wasi_fdflags_t *flags)
|
|
|
+{
|
|
|
+ int ret = fcntl(handle, F_GETFL);
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ *flags = 0;
|
|
|
+
|
|
|
+ if ((ret & O_APPEND) != 0)
|
|
|
+ *flags |= __WASI_FDFLAG_APPEND;
|
|
|
+#ifdef CONFIG_HAS_O_DSYNC
|
|
|
+ if ((ret & O_DSYNC) != 0)
|
|
|
+ *flags |= __WASI_FDFLAG_DSYNC;
|
|
|
+#endif
|
|
|
+ if ((ret & O_NONBLOCK) != 0)
|
|
|
+ *flags |= __WASI_FDFLAG_NONBLOCK;
|
|
|
+#ifdef CONFIG_HAS_O_RSYNC
|
|
|
+ if ((ret & O_RSYNC) != 0)
|
|
|
+ *flags |= __WASI_FDFLAG_RSYNC;
|
|
|
+#endif
|
|
|
+#ifdef CONFIG_HAS_O_SYNC
|
|
|
+ if ((ret & O_SYNC) != 0)
|
|
|
+ *flags |= __WASI_FDFLAG_SYNC;
|
|
|
+#endif
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_file_set_fdflags(os_file_handle handle, __wasi_fdflags_t flags)
|
|
|
+{
|
|
|
+ int fcntl_flags = 0;
|
|
|
+
|
|
|
+ if ((flags & __WASI_FDFLAG_APPEND) != 0)
|
|
|
+ fcntl_flags |= O_APPEND;
|
|
|
+ if ((flags & __WASI_FDFLAG_DSYNC) != 0)
|
|
|
+#ifdef CONFIG_HAS_O_DSYNC
|
|
|
+ fcntl_flags |= O_DSYNC;
|
|
|
+#else
|
|
|
+ return __WASI_ENOTSUP;
|
|
|
+#endif
|
|
|
+ if ((flags & __WASI_FDFLAG_NONBLOCK) != 0)
|
|
|
+ fcntl_flags |= O_NONBLOCK;
|
|
|
+ if ((flags & __WASI_FDFLAG_RSYNC) != 0)
|
|
|
+#ifdef CONFIG_HAS_O_RSYNC
|
|
|
+ fcntl_flags |= O_RSYNC;
|
|
|
+#else
|
|
|
+ return __WASI_ENOTSUP;
|
|
|
+#endif
|
|
|
+ if ((flags & __WASI_FDFLAG_SYNC) != 0)
|
|
|
+#ifdef CONFIG_HAS_O_SYNC
|
|
|
+ fcntl_flags |= O_SYNC;
|
|
|
+#else
|
|
|
+ return __WASI_ENOTSUP;
|
|
|
+#endif
|
|
|
+
|
|
|
+ int ret = fcntl(handle, F_SETFL, fcntl_flags);
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_fdatasync(os_file_handle handle)
|
|
|
+{
|
|
|
+#if CONFIG_HAS_FDATASYNC
|
|
|
+ int ret = fdatasync(handle);
|
|
|
+#else
|
|
|
+ int ret = fsync(handle);
|
|
|
+#endif
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_fsync(os_file_handle handle)
|
|
|
+{
|
|
|
+ int ret = fsync(handle);
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_open_preopendir(const char *path, os_file_handle *out)
|
|
|
+{
|
|
|
+
|
|
|
+ int fd = open(path, O_RDONLY | O_DIRECTORY, 0);
|
|
|
+
|
|
|
+ if (fd < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ *out = fd;
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_openat(os_file_handle handle, const char *path, __wasi_oflags_t oflags,
|
|
|
+ __wasi_fdflags_t fs_flags, __wasi_lookupflags_t lookup_flags,
|
|
|
+ wasi_libc_file_access_mode read_write_mode, os_file_handle *out)
|
|
|
+{
|
|
|
+ int open_flags = 0;
|
|
|
+
|
|
|
+ // Convert open flags.
|
|
|
+ if ((oflags & __WASI_O_CREAT) != 0) {
|
|
|
+ open_flags |= O_CREAT;
|
|
|
+ }
|
|
|
+ if ((oflags & __WASI_O_DIRECTORY) != 0)
|
|
|
+ open_flags |= O_DIRECTORY;
|
|
|
+ if ((oflags & __WASI_O_EXCL) != 0)
|
|
|
+ open_flags |= O_EXCL;
|
|
|
+ if ((oflags & __WASI_O_TRUNC) != 0) {
|
|
|
+ open_flags |= O_TRUNC;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Convert file descriptor flags.
|
|
|
+ if ((fs_flags & __WASI_FDFLAG_APPEND) != 0)
|
|
|
+ open_flags |= O_APPEND;
|
|
|
+ if ((fs_flags & __WASI_FDFLAG_DSYNC) != 0) {
|
|
|
+#ifdef CONFIG_HAS_O_DSYNC
|
|
|
+ open_flags |= O_DSYNC;
|
|
|
+#else
|
|
|
+ return __WASI_ENOTSUP;
|
|
|
+#endif
|
|
|
+ }
|
|
|
+ if ((fs_flags & __WASI_FDFLAG_NONBLOCK) != 0)
|
|
|
+ open_flags |= O_NONBLOCK;
|
|
|
+ if ((fs_flags & __WASI_FDFLAG_RSYNC) != 0) {
|
|
|
+#ifdef CONFIG_HAS_O_RSYNC
|
|
|
+ open_flags |= O_RSYNC;
|
|
|
+#else
|
|
|
+ return __WASI_ENOTSUP;
|
|
|
+#endif
|
|
|
+ }
|
|
|
+ if ((fs_flags & __WASI_FDFLAG_SYNC) != 0) {
|
|
|
+#ifdef CONFIG_HAS_O_SYNC
|
|
|
+ open_flags |= O_SYNC;
|
|
|
+#else
|
|
|
+ return __WASI_ENOTSUP;
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) == 0) {
|
|
|
+ open_flags |= O_NOFOLLOW;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (read_write_mode) {
|
|
|
+ case WASI_LIBC_ACCESS_MODE_READ_WRITE:
|
|
|
+ open_flags |= O_RDWR;
|
|
|
+ break;
|
|
|
+ case WASI_LIBC_ACCESS_MODE_READ_ONLY:
|
|
|
+ open_flags |= O_RDONLY;
|
|
|
+ break;
|
|
|
+ case WASI_LIBC_ACCESS_MODE_WRITE_ONLY:
|
|
|
+ open_flags |= O_WRONLY;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return __WASI_EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ int fd = openat(handle, path, open_flags, 0666);
|
|
|
+
|
|
|
+ if (fd < 0) {
|
|
|
+ int openat_errno = errno;
|
|
|
+ // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket.
|
|
|
+ if (openat_errno == ENXIO) {
|
|
|
+ struct stat sb;
|
|
|
+ int ret = fstatat(handle, path, &sb,
|
|
|
+ (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW)
|
|
|
+ ? 0
|
|
|
+ : AT_SYMLINK_NOFOLLOW);
|
|
|
+ return ret == 0 && S_ISSOCK(sb.st_mode) ? __WASI_ENOTSUP
|
|
|
+ : __WASI_ENXIO;
|
|
|
+ }
|
|
|
+ // Linux returns ENOTDIR instead of ELOOP when using
|
|
|
+ // O_NOFOLLOW|O_DIRECTORY on a symlink.
|
|
|
+ if (openat_errno == ENOTDIR
|
|
|
+ && (open_flags & (O_NOFOLLOW | O_DIRECTORY)) != 0) {
|
|
|
+ struct stat sb;
|
|
|
+ int ret = fstatat(handle, path, &sb, AT_SYMLINK_NOFOLLOW);
|
|
|
+ if (S_ISLNK(sb.st_mode)) {
|
|
|
+ return __WASI_ELOOP;
|
|
|
+ }
|
|
|
+ (void)ret;
|
|
|
+ }
|
|
|
+ // FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on
|
|
|
+ // a symlink.
|
|
|
+ if ((lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) == 0
|
|
|
+ && openat_errno == EMLINK)
|
|
|
+ return __WASI_ELOOP;
|
|
|
+
|
|
|
+ return convert_errno(openat_errno);
|
|
|
+ }
|
|
|
+
|
|
|
+ *out = fd;
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_file_get_access_mode(os_file_handle handle,
|
|
|
+ wasi_libc_file_access_mode *access_mode)
|
|
|
+{
|
|
|
+ int ret = fcntl(handle, F_GETFL, 0);
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ switch (ret & O_ACCMODE) {
|
|
|
+ case O_RDONLY:
|
|
|
+ *access_mode = WASI_LIBC_ACCESS_MODE_READ_ONLY;
|
|
|
+ break;
|
|
|
+ case O_WRONLY:
|
|
|
+ *access_mode = WASI_LIBC_ACCESS_MODE_WRITE_ONLY;
|
|
|
+ break;
|
|
|
+ case O_RDWR:
|
|
|
+ *access_mode = WASI_LIBC_ACCESS_MODE_READ_WRITE;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return __WASI_EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_close(os_file_handle handle, bool is_stdio)
|
|
|
+{
|
|
|
+ if (is_stdio)
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+
|
|
|
+ int ret = close(handle);
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_preadv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt,
|
|
|
+ __wasi_filesize_t offset, size_t *nread)
|
|
|
+{
|
|
|
+#if CONFIG_HAS_PREADV
|
|
|
+ ssize_t len =
|
|
|
+ preadv(handle, (const struct iovec *)iov, (int)iovcnt, (off_t)offset);
|
|
|
+ if (len < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ *nread = (size_t)len;
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+#else
|
|
|
+ if (iovcnt == 1) {
|
|
|
+ ssize_t len = pread(handle, iov->buf, iov->buf_len, offset);
|
|
|
+
|
|
|
+ if (len < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ *nread = len;
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Allocate a single buffer to fit all data.
|
|
|
+ size_t totalsize = 0;
|
|
|
+ for (int i = 0; i < iovcnt; ++i)
|
|
|
+ totalsize += iov[i].buf_len;
|
|
|
+
|
|
|
+ char *buf = BH_MALLOC(totalsize);
|
|
|
+
|
|
|
+ if (buf == NULL) {
|
|
|
+ return __WASI_ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Perform a single read operation.
|
|
|
+ ssize_t len = pread(handle, buf, totalsize, offset);
|
|
|
+
|
|
|
+ if (len < 0) {
|
|
|
+ BH_FREE(buf);
|
|
|
+ return convert_errno(errno);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Copy data back to vectors.
|
|
|
+ size_t bufoff = 0;
|
|
|
+ for (int i = 0; i < iovcnt; ++i) {
|
|
|
+ if (bufoff + iov[i].buf_len < (size_t)len) {
|
|
|
+ memcpy(iov[i].buf, buf + bufoff, iov[i].buf_len);
|
|
|
+ bufoff += iov[i].buf_len;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ memcpy(iov[i].buf, buf + bufoff, len - bufoff);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ BH_FREE(buf);
|
|
|
+ *nread = len;
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_pwritev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt,
|
|
|
+ __wasi_filesize_t offset, size_t *nwritten)
|
|
|
+{
|
|
|
+ if (iovcnt == 0)
|
|
|
+ return __WASI_EINVAL;
|
|
|
+
|
|
|
+ ssize_t len = 0;
|
|
|
+#if CONFIG_HAS_PWRITEV
|
|
|
+ len =
|
|
|
+ pwritev(handle, (const struct iovec *)iov, (int)iovcnt, (off_t)offset);
|
|
|
+#else
|
|
|
+ if (iovcnt == 1) {
|
|
|
+ len = pwrite(handle, iov->buf, iov->buf_len, offset);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // Allocate a single buffer to fit all data.
|
|
|
+ size_t totalsize = 0;
|
|
|
+ for (int i = 0; i < iovcnt; ++i)
|
|
|
+ totalsize += iov[i].buf_len;
|
|
|
+ char *buf = BH_MALLOC(totalsize);
|
|
|
+ if (buf == NULL) {
|
|
|
+ return __WASI_ENOMEM;
|
|
|
+ }
|
|
|
+ size_t bufoff = 0;
|
|
|
+ for (int i = 0; i < iovcnt; ++i) {
|
|
|
+ memcpy(buf + bufoff, iov[i].buf, iov[i].buf_len);
|
|
|
+ bufoff += iov[i].buf_len;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Perform a single write operation.
|
|
|
+ len = pwrite(handle, buf, totalsize, offset);
|
|
|
+ BH_FREE(buf);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ if (len < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ *nwritten = (size_t)len;
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_readv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt,
|
|
|
+ size_t *nread)
|
|
|
+{
|
|
|
+ ssize_t len = readv(handle, (const struct iovec *)iov, (int)iovcnt);
|
|
|
+
|
|
|
+ if (len < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ *nread = (size_t)len;
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_writev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt,
|
|
|
+ size_t *nwritten)
|
|
|
+{
|
|
|
+ ssize_t len = writev(handle, (const struct iovec *)iov, (int)iovcnt);
|
|
|
+
|
|
|
+ if (len < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ *nwritten = (size_t)len;
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_fallocate(os_file_handle handle, __wasi_filesize_t offset,
|
|
|
+ __wasi_filesize_t length)
|
|
|
+{
|
|
|
+#if CONFIG_HAS_POSIX_FALLOCATE
|
|
|
+ int ret = posix_fallocate(handle, (off_t)offset, (off_t)length);
|
|
|
+#else
|
|
|
+ // At least ensure that the file is grown to the right size.
|
|
|
+ // TODO(ed): See if this can somehow be implemented without any race
|
|
|
+ // conditions. We may end up shrinking the file right now.
|
|
|
+ struct stat sb;
|
|
|
+ int ret = fstat(handle, &sb);
|
|
|
+ off_t newsize = (off_t)(offset + length);
|
|
|
+
|
|
|
+ if (ret == 0 && sb.st_size < newsize)
|
|
|
+ ret = ftruncate(handle, newsize);
|
|
|
+#endif
|
|
|
+
|
|
|
+ if (ret != 0)
|
|
|
+ return convert_errno(ret);
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_ftruncate(os_file_handle handle, __wasi_filesize_t size)
|
|
|
+{
|
|
|
+ int ret = ftruncate(handle, (off_t)size);
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_futimens(os_file_handle handle, __wasi_timestamp_t access_time,
|
|
|
+ __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags)
|
|
|
+{
|
|
|
+ struct timespec ts[2];
|
|
|
+ convert_utimens_arguments(access_time, modification_time, fstflags, ts);
|
|
|
+
|
|
|
+ int ret = futimens(handle, ts);
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_utimensat(os_file_handle handle, const char *path,
|
|
|
+ __wasi_timestamp_t access_time,
|
|
|
+ __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags,
|
|
|
+ __wasi_lookupflags_t lookup_flags)
|
|
|
+{
|
|
|
+ struct timespec ts[2];
|
|
|
+ convert_utimens_arguments(access_time, modification_time, fstflags, ts);
|
|
|
+
|
|
|
+ int ret = utimensat(handle, path, ts,
|
|
|
+ (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW)
|
|
|
+ ? 0
|
|
|
+ : AT_SYMLINK_NOFOLLOW);
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_readlinkat(os_file_handle handle, const char *path, char *buf,
|
|
|
+ size_t bufsize, size_t *nread)
|
|
|
+{
|
|
|
+ // Linux requires that the buffer size is positive. whereas POSIX does
|
|
|
+ // not. Use a fake buffer to store the results if the size is zero.
|
|
|
+ char fakebuf[1];
|
|
|
+ ssize_t len = readlinkat(handle, path, bufsize == 0 ? fakebuf : buf,
|
|
|
+ bufsize == 0 ? sizeof(fakebuf) : bufsize);
|
|
|
+
|
|
|
+ if (len < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ *nread = (size_t)len < bufsize ? (size_t)len : bufsize;
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_linkat(os_file_handle from_handle, const char *from_path,
|
|
|
+ os_file_handle to_handle, const char *to_path,
|
|
|
+ __wasi_lookupflags_t lookup_flags)
|
|
|
+{
|
|
|
+ int ret = linkat(
|
|
|
+ from_handle, from_path, to_handle, to_path,
|
|
|
+ (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) ? AT_SYMLINK_FOLLOW : 0);
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_symlinkat(const char *old_path, os_file_handle handle, const char *new_path)
|
|
|
+{
|
|
|
+ int ret = symlinkat(old_path, handle, new_path);
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_mkdirat(os_file_handle handle, const char *path)
|
|
|
+{
|
|
|
+ int ret = mkdirat(handle, path, 0777);
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_renameat(os_file_handle old_handle, const char *old_path,
|
|
|
+ os_file_handle new_handle, const char *new_path)
|
|
|
+{
|
|
|
+
|
|
|
+ int ret = renameat(old_handle, old_path, new_handle, new_path);
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_unlinkat(os_file_handle handle, const char *path, bool is_dir)
|
|
|
+{
|
|
|
+ int ret = unlinkat(handle, path, is_dir ? AT_REMOVEDIR : 0);
|
|
|
+
|
|
|
+#ifndef __linux__
|
|
|
+ if (ret < 0) {
|
|
|
+ // Non-Linux implementations may return EPERM when attempting to remove
|
|
|
+ // a directory without REMOVEDIR. While that's what POSIX specifies,
|
|
|
+ // it's less useful. Adjust this to EISDIR. It doesn't matter that this
|
|
|
+ // is not atomic with the unlinkat, because if the file is removed and a
|
|
|
+ // directory is created before fstatat sees it, we're racing with that
|
|
|
+ // change anyway and unlinkat could have legitimately seen the directory
|
|
|
+ // if the race had turned out differently.
|
|
|
+ if (errno == EPERM) {
|
|
|
+ struct stat statbuf;
|
|
|
+ if (fstatat(handle, path, &statbuf, AT_SYMLINK_NOFOLLOW) == 0
|
|
|
+ && S_ISDIR(statbuf.st_mode)) {
|
|
|
+ errno = EISDIR;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // POSIX permits either EEXIST or ENOTEMPTY when the directory is not
|
|
|
+ // empty. Map it to ENOTEMPTY.
|
|
|
+ else if (errno == EEXIST) {
|
|
|
+ errno = ENOTEMPTY;
|
|
|
+ }
|
|
|
+
|
|
|
+ return convert_errno(errno);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_lseek(os_file_handle handle, __wasi_filedelta_t offset,
|
|
|
+ __wasi_whence_t whence, __wasi_filesize_t *new_offset)
|
|
|
+{
|
|
|
+ int nwhence;
|
|
|
+
|
|
|
+ switch (whence) {
|
|
|
+ case __WASI_WHENCE_CUR:
|
|
|
+ nwhence = SEEK_CUR;
|
|
|
+ break;
|
|
|
+ case __WASI_WHENCE_END:
|
|
|
+ nwhence = SEEK_END;
|
|
|
+ break;
|
|
|
+ case __WASI_WHENCE_SET:
|
|
|
+ nwhence = SEEK_SET;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return __WASI_EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ off_t ret = lseek(handle, offset, nwhence);
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ *new_offset = (__wasi_filesize_t)ret;
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_fadvise(os_file_handle handle, __wasi_filesize_t offset,
|
|
|
+ __wasi_filesize_t length, __wasi_advice_t advice)
|
|
|
+{
|
|
|
+#ifdef POSIX_FADV_NORMAL
|
|
|
+ int nadvice;
|
|
|
+ switch (advice) {
|
|
|
+ case __WASI_ADVICE_DONTNEED:
|
|
|
+ nadvice = POSIX_FADV_DONTNEED;
|
|
|
+ break;
|
|
|
+ case __WASI_ADVICE_NOREUSE:
|
|
|
+ nadvice = POSIX_FADV_NOREUSE;
|
|
|
+ break;
|
|
|
+ case __WASI_ADVICE_NORMAL:
|
|
|
+ nadvice = POSIX_FADV_NORMAL;
|
|
|
+ break;
|
|
|
+ case __WASI_ADVICE_RANDOM:
|
|
|
+ nadvice = POSIX_FADV_RANDOM;
|
|
|
+ break;
|
|
|
+ case __WASI_ADVICE_SEQUENTIAL:
|
|
|
+ nadvice = POSIX_FADV_SEQUENTIAL;
|
|
|
+ break;
|
|
|
+ case __WASI_ADVICE_WILLNEED:
|
|
|
+ nadvice = POSIX_FADV_WILLNEED;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return __WASI_EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ int ret = posix_fadvise(handle, (off_t)offset, (off_t)length, nadvice);
|
|
|
+
|
|
|
+ if (ret != 0)
|
|
|
+ return convert_errno(ret);
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+#else
|
|
|
+ // Advisory information can be safely ignored if not supported
|
|
|
+ switch (advice) {
|
|
|
+ case __WASI_ADVICE_DONTNEED:
|
|
|
+ case __WASI_ADVICE_NOREUSE:
|
|
|
+ case __WASI_ADVICE_NORMAL:
|
|
|
+ case __WASI_ADVICE_RANDOM:
|
|
|
+ case __WASI_ADVICE_SEQUENTIAL:
|
|
|
+ case __WASI_ADVICE_WILLNEED:
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+ default:
|
|
|
+ return __WASI_EINVAL;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_isatty(os_file_handle handle)
|
|
|
+{
|
|
|
+#if CONFIG_HAS_ISATTY
|
|
|
+ int ret = isatty(handle);
|
|
|
+
|
|
|
+ if (ret == 1)
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+
|
|
|
+ return __WASI_ENOTTY;
|
|
|
+#else
|
|
|
+ return __WASI_ENOTSUP;
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+os_file_handle
|
|
|
+os_convert_stdin_handle(os_raw_file_handle raw_stdin)
|
|
|
+{
|
|
|
+#ifndef STDIN_FILENO
|
|
|
+#define STDIN_FILENO 0
|
|
|
+#endif
|
|
|
+ return raw_stdin >= 0 ? raw_stdin : STDIN_FILENO;
|
|
|
+}
|
|
|
+
|
|
|
+os_file_handle
|
|
|
+os_convert_stdout_handle(os_raw_file_handle raw_stdout)
|
|
|
+{
|
|
|
+#ifndef STDOUT_FILENO
|
|
|
+#define STDOUT_FILENO 1
|
|
|
+#endif
|
|
|
+ return raw_stdout >= 0 ? raw_stdout : STDOUT_FILENO;
|
|
|
+}
|
|
|
+
|
|
|
+os_file_handle
|
|
|
+os_convert_stderr_handle(os_raw_file_handle raw_stderr)
|
|
|
+{
|
|
|
+#ifndef STDERR_FILENO
|
|
|
+#define STDERR_FILENO 2
|
|
|
+#endif
|
|
|
+ return raw_stderr >= 0 ? raw_stderr : STDERR_FILENO;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_fdopendir(os_file_handle handle, os_dir_stream *dir_stream)
|
|
|
+{
|
|
|
+ *dir_stream = fdopendir(handle);
|
|
|
+
|
|
|
+ if (*dir_stream == NULL)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_rewinddir(os_dir_stream dir_stream)
|
|
|
+{
|
|
|
+ rewinddir(dir_stream);
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_seekdir(os_dir_stream dir_stream, __wasi_dircookie_t position)
|
|
|
+{
|
|
|
+ seekdir(dir_stream, (long)position);
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_readdir(os_dir_stream dir_stream, __wasi_dirent_t *entry,
|
|
|
+ const char **d_name)
|
|
|
+{
|
|
|
+ errno = 0;
|
|
|
+
|
|
|
+ struct dirent *dent = readdir(dir_stream);
|
|
|
+
|
|
|
+ if (dent == NULL) {
|
|
|
+ *d_name = NULL;
|
|
|
+ if (errno != 0) {
|
|
|
+ return convert_errno(errno);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ long offset = (__wasi_dircookie_t)telldir(dir_stream);
|
|
|
+
|
|
|
+ size_t namlen = strlen(dent->d_name);
|
|
|
+
|
|
|
+ *d_name = dent->d_name;
|
|
|
+ entry->d_next = offset;
|
|
|
+ entry->d_namlen = (__wasi_dirnamlen_t)namlen;
|
|
|
+#if CONFIG_HAS_D_INO
|
|
|
+ entry->d_ino = dent->d_ino;
|
|
|
+#else
|
|
|
+ entry->d_ino = 0;
|
|
|
+#endif
|
|
|
+
|
|
|
+ switch (dent->d_type) {
|
|
|
+ case DT_BLK:
|
|
|
+ entry->d_type = __WASI_FILETYPE_BLOCK_DEVICE;
|
|
|
+ break;
|
|
|
+ case DT_CHR:
|
|
|
+ entry->d_type = __WASI_FILETYPE_CHARACTER_DEVICE;
|
|
|
+ break;
|
|
|
+ case DT_DIR:
|
|
|
+ entry->d_type = __WASI_FILETYPE_DIRECTORY;
|
|
|
+ break;
|
|
|
+ case DT_FIFO:
|
|
|
+ entry->d_type = __WASI_FILETYPE_SOCKET_STREAM;
|
|
|
+ break;
|
|
|
+ case DT_LNK:
|
|
|
+ entry->d_type = __WASI_FILETYPE_SYMBOLIC_LINK;
|
|
|
+ break;
|
|
|
+ case DT_REG:
|
|
|
+ entry->d_type = __WASI_FILETYPE_REGULAR_FILE;
|
|
|
+ break;
|
|
|
+#ifdef DT_SOCK
|
|
|
+ case DT_SOCK:
|
|
|
+ // Technically not correct, but good enough.
|
|
|
+ entry->d_type = __WASI_FILETYPE_SOCKET_STREAM;
|
|
|
+ break;
|
|
|
+#endif
|
|
|
+ default:
|
|
|
+ entry->d_type = __WASI_FILETYPE_UNKNOWN;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+__wasi_errno_t
|
|
|
+os_closedir(os_dir_stream dir_stream)
|
|
|
+{
|
|
|
+ int ret = closedir(dir_stream);
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ return convert_errno(errno);
|
|
|
+
|
|
|
+ return __WASI_ESUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+os_dir_stream
|
|
|
+os_get_invalid_dir_stream()
|
|
|
+{
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+bool
|
|
|
+os_is_dir_stream_valid(os_dir_stream *dir_stream)
|
|
|
+{
|
|
|
+ assert(dir_stream != NULL);
|
|
|
+
|
|
|
+ return *dir_stream != NULL;
|
|
|
+}
|
|
|
+
|
|
|
+bool
|
|
|
+os_is_handle_valid(os_file_handle *handle)
|
|
|
+{
|
|
|
+ assert(handle != NULL);
|
|
|
+
|
|
|
+ return *handle > -1;
|
|
|
+}
|
|
|
+
|
|
|
+char *
|
|
|
+os_realpath(const char *path, char *resolved_path)
|
|
|
+{
|
|
|
+ return realpath(path, resolved_path);
|
|
|
+}
|