From 4f0cc5ada6641519d3e69c01d4e0b688faead8d7 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Feb 2026 14:39:42 -0800 Subject: [PATCH 01/17] Merge wasip2/wasip3 reading/writing * Generalize the `wasip2_{read,write}_t` types to `wasi_{read,write}_t` with conditional internals depending on the WASI version. * Fix error handling for stdin to use stdin error codes instead of filesystem error codes. * Call `subtask.drop` for writes to acknowledge the subtask being done. * When stdin is done, drop the future for the error after it's read to ensure the future isn't read twice. --- libc-bottom-half/CMakeLists.txt | 3 +- .../cloudlibc/src/libc/poll/poll.c | 4 +- .../cloudlibc/src/libc/unistd/read.c | 29 +------ .../cloudlibc/src/libc/unistd/write.c | 32 +------- .../headers/private/wasi/descriptor_table.h | 64 +++++++-------- .../headers/private/wasi/file_utils.h | 21 ++--- .../{wasip2_file_utils.c => file_utils.c} | 50 ++++++++++-- libc-bottom-half/sources/tcp.c | 8 +- libc-bottom-half/sources/wasip2_file.c | 4 +- libc-bottom-half/sources/wasip2_stdio.c | 4 +- libc-bottom-half/sources/wasip3_file_utils.c | 31 ------- libc-bottom-half/sources/wasip3_stdio.c | 80 ++++++++++++++----- 12 files changed, 155 insertions(+), 175 deletions(-) rename libc-bottom-half/sources/{wasip2_file_utils.c => file_utils.c} (83%) delete mode 100644 libc-bottom-half/sources/wasip3_file_utils.c diff --git a/libc-bottom-half/CMakeLists.txt b/libc-bottom-half/CMakeLists.txt index 65012cb03..4bdce3ff8 100644 --- a/libc-bottom-half/CMakeLists.txt +++ b/libc-bottom-half/CMakeLists.txt @@ -139,6 +139,7 @@ else() sources/descriptor_table.c sources/getsockpeername.c sources/listen.c + sources/file_utils.c sources/recv.c sources/send.c sources/shutdown.c @@ -155,7 +156,6 @@ if(WASI STREQUAL "p2") sources/netdb.c sources/wasip2.c sources/wasip2_file.c - sources/wasip2_file_utils.c sources/wasip2_stdio.c ) endif() @@ -165,7 +165,6 @@ if (WASI STREQUAL "p3") sources/wasip3.c sources/wasip3_block_on.c sources/wasip3_file.c - sources/wasip3_file_utils.c sources/wasip3_stdio.c ) endif() diff --git a/libc-bottom-half/cloudlibc/src/libc/poll/poll.c b/libc-bottom-half/cloudlibc/src/libc/poll/poll.c index 95ec03561..796f97c4b 100644 --- a/libc-bottom-half/cloudlibc/src/libc/poll/poll.c +++ b/libc-bottom-half/cloudlibc/src/libc/poll/poll.c @@ -216,7 +216,7 @@ static int poll_impl(struct pollfd *fds, size_t nfds, int timeout) { if (pollfd->events & POLLRDNORM) { if (entry->vtable->get_read_stream) { - wasip2_read_t read; + wasi_read_t read; if (entry->vtable->get_read_stream(entry->data, &read) < 0) return -1; if (__wasilibc_poll_add_input_stream(&state, read.input, read.pollable) < 0) @@ -229,7 +229,7 @@ static int poll_impl(struct pollfd *fds, size_t nfds, int timeout) { if (pollfd->events & POLLWRNORM) { if (entry->vtable->get_write_stream) { - wasip2_write_t write; + wasi_write_t write; if (entry->vtable->get_write_stream(entry->data, &write) < 0) return -1; if (__wasilibc_poll_add_output_stream(&state, write.output, write.pollable) < 0) diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/read.c b/libc-bottom-half/cloudlibc/src/libc/unistd/read.c index 12db46717..36f2241ce 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/read.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/read.c @@ -27,12 +27,12 @@ ssize_t read(int fildes, void *buf, size_t nbyte) { return -1; } return bytes_read; -#elif defined(__wasip2__) +#else descriptor_table_entry_t *entry = descriptor_table_get_ref(fildes); if (!entry) return -1; if (entry->vtable->get_read_stream) { - wasip2_read_t read; + wasi_read_t read; if (entry->vtable->get_read_stream(entry->data, &read) < 0) return -1; return __wasilibc_read(&read, buf, nbyte); @@ -41,30 +41,5 @@ ssize_t read(int fildes, void *buf, size_t nbyte) { return entry->vtable->recvfrom(entry->data, buf, nbyte, 0, NULL, NULL); errno = EOPNOTSUPP; return -1; -#elif defined(__wasip3__) - filesystem_tuple2_stream_u8_future_result_void_error_code_t *stream; - off_t *off; - if (__wasilibc_read_stream3(fildes, &stream, &off)<0) - return -1; - wasip3_waitable_status_t status = - filesystem_stream_u8_read(stream->f0, buf, nbyte); - size_t amount = wasip3_waitable_block_on(status, stream->f0); - if (amount > 0 || nbyte == 0) { - if (off) - *off += amount; - return amount; - } else { - filesystem_result_void_error_code_t error; - status = filesystem_future_result_void_error_code_read(stream->f1, &error); - amount = wasip3_waitable_block_on(status, stream->f1); - if (amount > 0 && error.is_err) { - translate_error(error.val.err); - return -1; - } - // EOF - return 0; - } -#else -# error "Unsupported WASI version" #endif } diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/write.c b/libc-bottom-half/cloudlibc/src/libc/unistd/write.c index 8c2dead6d..ca191910b 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/write.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/write.c @@ -28,12 +28,12 @@ ssize_t write(int fildes, const void *buf, size_t nbyte) { return -1; } return bytes_written; -#elif defined(__wasip2__) +#else descriptor_table_entry_t *entry = descriptor_table_get_ref(fildes); if (!entry) return -1; if (entry->vtable->get_write_stream) { - wasip2_write_t write; + wasi_write_t write; if (entry->vtable->get_write_stream(entry->data, &write) < 0) return -1; return __wasilibc_write(&write, buf, nbyte); @@ -42,33 +42,5 @@ ssize_t write(int fildes, const void *buf, size_t nbyte) { return entry->vtable->sendto(entry->data, buf, nbyte, 0, NULL, 0); errno = EOPNOTSUPP; return -1; -#elif defined(__wasip3__) - wasip3_write_t *write_end; - off_t *off; - if (__wasilibc_write_stream3(fildes, &write_end, &off) < 0) - return -1; - if (WASIP3_SUBTASK_STATE(write_end->subtask) == WASIP3_SUBTASK_STARTING || - WASIP3_SUBTASK_STATE(write_end->subtask) == WASIP3_SUBTASK_STARTED) { - // the stream is still active - wasip3_waitable_status_t status = - filesystem_stream_u8_write(write_end->output, buf, nbyte); - size_t amount = wasip3_waitable_block_on(status, write_end->output); - if (amount > 0 || nbyte == 0) { - if (off) - *off += amount; - return amount; - } - // error or eof - wasip3_subtask_block_on(write_end->subtask); - write_end->subtask = WASIP3_SUBTASK_RETURNED; - } - if (write_end->pending_result.is_err) { - translate_error(write_end->pending_result.val.err); - return -1; - } - // EOF - return 0; -#else -# error "Unknown WASI version" #endif } diff --git a/libc-bottom-half/headers/private/wasi/descriptor_table.h b/libc-bottom-half/headers/private/wasi/descriptor_table.h index 254372fcd..e7841aeb5 100644 --- a/libc-bottom-half/headers/private/wasi/descriptor_table.h +++ b/libc-bottom-half/headers/private/wasi/descriptor_table.h @@ -8,48 +8,54 @@ #include #include -#ifdef __wasip2__ // Metadata for WASI reads which is used to delegate to `__wasilibc_read(...)` // to perform the actual read of a stream. -typedef struct wasip2_read_t { - // The `wasi:io/streams.input-stream` that this is reading from. - streams_borrow_input_stream_t input; +typedef struct wasi_read_t { // An optional pointer to the internal offset of this stream, updated on // successful reads. off_t *offset; - // A required pointer to an owned pollable for `input`. This is lazily - // initialized as-necessary. - poll_own_pollable_t *pollable; // Whether or not this read will use blocking I/O. bool blocking; // The timeout, in nanoseconds, for this operation. monotonic_clock_duration_t timeout; -} wasip2_read_t; +#ifdef __wasip2__ + // The `wasi:io/streams.input-stream` that this is reading from. + streams_borrow_input_stream_t input; + // A required pointer to an owned pollable for `input`. This is lazily + // initialized as-necessary. + poll_own_pollable_t *pollable; +#else + // The `stream` that's being read. + filesystem_stream_u8_t stream; + + // A callback/ptr pair to invoke when EOF is reached to set errno and return + // an error code. + int (*eof)(void*); + void *eof_data; +#endif +} wasi_read_t; -// Same as `wasip2_read_t`, but for writes. -typedef struct wasip2_write_t { - streams_borrow_output_stream_t output; +// Same as `wasip_read_t`, but for writes. +typedef struct wasi_write_t { off_t *offset; - poll_own_pollable_t *pollable; bool blocking; monotonic_clock_duration_t timeout; -} wasip2_write_t; -#endif - -#ifdef __wasip3__ -// create an alias to distinguish the handle type in the API -typedef uint32_t waitable_t; -/** - * This data structure represents the write end of a file - */ -typedef struct wasip3_write_t { +#ifdef __wasip2__ + streams_borrow_output_stream_t output; + poll_own_pollable_t *pollable; +#else filesystem_stream_u8_writer_t output; // contents will be filled by host (once write has an error) filesystem_result_void_error_code_t pending_result; // this task gets ready on error or eof - wasip3_subtask_t subtask; -} wasip3_write_t; + wasip3_subtask_t *subtask; +#endif +} wasi_write_t; + +#ifdef __wasip3__ +// create an alias to distinguish the handle type in the API +typedef uint32_t waitable_t; #endif /** @@ -71,18 +77,12 @@ typedef struct descriptor_vtable_t { // ===================================================================== // Generic I/O -#ifdef __wasip2__ /// Looks up metadata to perform a read operation for this stream. This is used /// to implement the `read` syscall, for example, and is also used with `poll` /// when waiting for readability. - int (*get_read_stream)(void*, wasip2_read_t*); + int (*get_read_stream)(void*, wasi_read_t*); /// Same as `get_read_stream`, but for output streams. - int (*get_write_stream)(void*, wasip2_write_t*); -#endif -#ifdef __wasip3__ - int (*get_read_stream3)(void*, filesystem_tuple2_stream_u8_future_result_void_error_code_t **out, off_t** off); - int (*get_write_stream3)(void*, wasip3_write_t **write_end, off_t**); -#endif + int (*get_write_stream)(void*, wasi_write_t*); /// Sets the nonblocking flag for this object to the specified value. int (*set_blocking)(void*, bool); diff --git a/libc-bottom-half/headers/private/wasi/file_utils.h b/libc-bottom-half/headers/private/wasi/file_utils.h index d929b83f2..11dc1e9eb 100644 --- a/libc-bottom-half/headers/private/wasi/file_utils.h +++ b/libc-bottom-half/headers/private/wasi/file_utils.h @@ -2,15 +2,14 @@ #define __wasi_file_utils_h #include - #if defined(__wasip2__) || defined(__wasip3__) + #include #include #include #include #include #include -#endif #ifdef __wasip2__ /// Handles a `wasi:io/streams.stream-error` for a `read`-style operation. @@ -50,7 +49,6 @@ static inline int wasip2_handle_write_error(streams_stream_error_t error) { int wasip2_string_from_c(const char *s, wasip2_string_t* out); #endif -#if defined(__wasip2__) || defined(__wasip3__) // Succeed only if fd is bound to a file handle in the descriptor table static inline int fd_to_file_handle(int fd, filesystem_borrow_descriptor_t* result) { descriptor_table_entry_t* entry = descriptor_table_get_ref(fd); @@ -62,25 +60,16 @@ static inline int fd_to_file_handle(int fd, filesystem_borrow_descriptor_t* resu } return entry->vtable->get_file(entry->data, result); } -#endif -#ifdef __wasip2__ // Reads from `read` into `buf`/`len` // // This perform the read configured by `read`, e.g. whether it's blocking or // not, and places the result in the specified buffer. Used to implement // `read` and `recvfrom`, for example. -ssize_t __wasilibc_read(wasip2_read_t *read, void *buf, size_t len); +ssize_t __wasilibc_read(wasi_read_t *read, void *buf, size_t len); // Same as `__wasilibc_read`, but for writes. -ssize_t __wasilibc_write(wasip2_write_t *write, const void *buf, size_t len); -#endif - -#ifdef __wasip3__ -int __wasilibc_write_stream3(int fildes, wasip3_write_t **write_end, off_t **off); -int __wasilibc_read_stream3(int fildes, filesystem_tuple2_stream_u8_future_result_void_error_code_t **stream, off_t **off); -#endif +ssize_t __wasilibc_write(wasi_write_t *write, const void *buf, size_t len); -#if defined(__wasip2__) || defined(__wasip3__) static inline unsigned dir_entry_type_to_d_type(filesystem_descriptor_type_t ty) { switch(ty) { case FILESYSTEM_DESCRIPTOR_TYPE_UNKNOWN: @@ -104,6 +93,6 @@ static inline unsigned dir_entry_type_to_d_type(filesystem_descriptor_type_t ty) } } -#endif +#endif // __wasip2__ || __wasip3__ -#endif +#endif // __wasi_file_utils_h diff --git a/libc-bottom-half/sources/wasip2_file_utils.c b/libc-bottom-half/sources/file_utils.c similarity index 83% rename from libc-bottom-half/sources/wasip2_file_utils.c rename to libc-bottom-half/sources/file_utils.c index abecd8263..9c2a50fbc 100644 --- a/libc-bottom-half/sources/wasip2_file_utils.c +++ b/libc-bottom-half/sources/file_utils.c @@ -4,9 +4,9 @@ #include #include #include +#include #ifdef __wasip2__ - /** * Validates that `ptr_signed` is a valid utf-8 string. * @@ -82,9 +82,11 @@ int wasip2_string_from_c(const char *s, wasip2_string_t *out) { out->len = len; return 0; } +#endif -ssize_t __wasilibc_write(wasip2_write_t *write, const void *buffer, +ssize_t __wasilibc_write(wasi_write_t *write, const void *buffer, size_t length) { +#if defined(__wasip2__) assert(write->output.__handle != 0); while (true) { streams_stream_error_t error; @@ -158,9 +160,36 @@ ssize_t __wasilibc_write(wasip2_write_t *write, const void *buffer, poll_method_pollable_block(pollable_borrow); } } +#elif defined(__wasip3__) + if (WASIP3_SUBTASK_STATE(*write->subtask) == WASIP3_SUBTASK_STARTING || + WASIP3_SUBTASK_STATE(*write->subtask) == WASIP3_SUBTASK_STARTED) { + // the stream is still active + wasip3_waitable_status_t status = + filesystem_stream_u8_write(write->output, buffer, length); + size_t amount = wasip3_waitable_block_on(status, write->output); + if (amount > 0 || length == 0) { + if (write->offset) + *write->offset += amount; + return amount; + } + // error or eof + wasip3_subtask_block_on(*write->subtask); + wasip3_subtask_drop(*write->subtask); + *write->subtask = WASIP3_SUBTASK_RETURNED; + } + if (write->pending_result.is_err) { + translate_error(write->pending_result.val.err); + return -1; + } + // EOF + return 0; +#else +#error "Unknown WASI version" +#endif } -ssize_t __wasilibc_read(wasip2_read_t *read, void *buffer, size_t length) { +ssize_t __wasilibc_read(wasi_read_t *read, void *buffer, size_t length) { +#if defined(__wasip2__) while (true) { wasip2_list_u8_t result; streams_stream_error_t error; @@ -223,6 +252,17 @@ ssize_t __wasilibc_read(wasip2_read_t *read, void *buffer, size_t length) { poll_method_pollable_block(pollable_borrow); } } +#elif defined(__wasip3__) + wasip3_waitable_status_t status = + filesystem_stream_u8_read(read->stream, buffer, length); + size_t amount = wasip3_waitable_block_on(status, read->stream); + if (amount > 0 || length == 0) { + if (read->offset) + *read->offset += amount; + return amount; + } + return read->eof(read->eof_data); +#else +#error "Unknown WASI version" +#endif } - -#endif // __wasip2__ diff --git a/libc-bottom-half/sources/tcp.c b/libc-bottom-half/sources/tcp.c index e32bf21e2..b9fe0be77 100644 --- a/libc-bottom-half/sources/tcp.c +++ b/libc-bottom-half/sources/tcp.c @@ -122,7 +122,7 @@ static void tcp_free(void *data) { } #ifdef __wasip2__ -static int tcp_get_read_stream(void *data, wasip2_read_t *read) { +static int tcp_get_read_stream(void *data, wasi_read_t *read) { tcp_socket_t *tcp = (tcp_socket_t *)data; if (tcp->state.tag != TCP_SOCKET_STATE_CONNECTED) { @@ -137,7 +137,7 @@ static int tcp_get_read_stream(void *data, wasip2_read_t *read) { return 0; } -static int tcp_get_write_stream(void *data, wasip2_write_t *write) { +static int tcp_get_write_stream(void *data, wasi_write_t *write) { tcp_socket_t *tcp = (tcp_socket_t *)data; if (tcp->state.tag != TCP_SOCKET_STATE_CONNECTED) { @@ -531,7 +531,7 @@ static ssize_t tcp_recvfrom(void *data, void *buffer, size_t length, int flags, return -1; } - wasip2_read_t read; + wasi_read_t read; if (tcp_get_read_stream(data, &read) < 0) return -1; @@ -555,7 +555,7 @@ static ssize_t tcp_sendto(void *data, const void *buffer, size_t length, return -1; } - wasip2_write_t write; + wasi_write_t write; if (tcp_get_write_stream(data, &write) < 0) return -1; diff --git a/libc-bottom-half/sources/wasip2_file.c b/libc-bottom-half/sources/wasip2_file.c index 116f6d4f3..7da5cb8a3 100644 --- a/libc-bottom-half/sources/wasip2_file.c +++ b/libc-bottom-half/sources/wasip2_file.c @@ -54,7 +54,7 @@ static void file_free(void *data) { free(file); } -static int file_get_read_stream(void *data, wasip2_read_t *read) { +static int file_get_read_stream(void *data, wasi_read_t *read) { file_t *file = (file_t *)data; if (file->read_stream.__handle == 0) { filesystem_error_code_t error_code; @@ -74,7 +74,7 @@ static int file_get_read_stream(void *data, wasip2_read_t *read) { return 0; } -static int file_get_write_stream(void *data, wasip2_write_t *write) { +static int file_get_write_stream(void *data, wasi_write_t *write) { file_t *file = (file_t *)data; if (file->write_stream.__handle == 0) { filesystem_error_code_t error_code; diff --git a/libc-bottom-half/sources/wasip2_stdio.c b/libc-bottom-half/sources/wasip2_stdio.c index c4681517d..47e9044c2 100644 --- a/libc-bottom-half/sources/wasip2_stdio.c +++ b/libc-bottom-half/sources/wasip2_stdio.c @@ -36,7 +36,7 @@ static void stdio_free(void *data) { free(stdio); } -static int stdio_get_read_stream(void *data, wasip2_read_t *read) { +static int stdio_get_read_stream(void *data, wasi_read_t *read) { stdio_t *stdio = (stdio_t *)data; if (stdio->fd != 0) { errno = EOPNOTSUPP; @@ -52,7 +52,7 @@ static int stdio_get_read_stream(void *data, wasip2_read_t *read) { return 0; } -static int stdio_get_write_stream(void *data, wasip2_write_t *write) { +static int stdio_get_write_stream(void *data, wasi_write_t *write) { stdio_t *stdio = (stdio_t *)data; if (stdio->output.__handle == 0) { if (stdio->fd == 1) { diff --git a/libc-bottom-half/sources/wasip3_file_utils.c b/libc-bottom-half/sources/wasip3_file_utils.c deleted file mode 100644 index e80b21ead..000000000 --- a/libc-bottom-half/sources/wasip3_file_utils.c +++ /dev/null @@ -1,31 +0,0 @@ -#include - -#ifdef __wasip3__ -#include - -int __wasilibc_write_stream3(int fildes, wasip3_write_t **write_end, - off_t **off) { - descriptor_table_entry_t *entry = descriptor_table_get_ref(fildes); - if (!entry) - return -1; - if (!entry->vtable->get_write_stream3) { - errno = EOPNOTSUPP; - return -1; - } - return (*entry->vtable->get_write_stream3)(entry->data, write_end, off); -} - -int __wasilibc_read_stream3( - int fildes, - filesystem_tuple2_stream_u8_future_result_void_error_code_t **stream, - off_t **off) { - descriptor_table_entry_t *entry = descriptor_table_get_ref(fildes); - if (!entry) - return -1; - if (!entry->vtable->get_read_stream3) { - errno = EOPNOTSUPP; - return -1; - } - return (*entry->vtable->get_read_stream3)(entry->data, stream, off); -} -#endif // __wasip3__ diff --git a/libc-bottom-half/sources/wasip3_stdio.c b/libc-bottom-half/sources/wasip3_stdio.c index 3f3091189..8522a1540 100644 --- a/libc-bottom-half/sources/wasip3_stdio.c +++ b/libc-bottom-half/sources/wasip3_stdio.c @@ -2,6 +2,7 @@ #include #ifdef __wasip3__ +#include #include #include #include @@ -20,7 +21,9 @@ typedef struct { typedef struct { // contains stream, result storage and result subtask - wasip3_write_t output; + wasip3_subtask_t subtask; + stdin_stream_u8_writer_t output; + stdin_result_void_error_code_t pending_result; // tristate: zero=unknown, valid handle=yes, -1=no terminal_output_own_terminal_output_t terminal_out; // stream creation function (delayed) @@ -45,38 +48,71 @@ static void stdout3_free(void *data) { stdout3_t *stdio = (stdout3_t *)data; if (stdio->terminal_out.__handle > 0) terminal_output_terminal_output_drop_own(stdio->terminal_out); - if (stdio->output.output) - stdin_stream_u8_drop_writable(stdio->output.output); - if (stdio->output.subtask) - wasip3_subtask_block_on(stdio->output.subtask); + if (stdio->output) + stdin_stream_u8_drop_writable(stdio->output); + if (stdio->subtask) + wasip3_subtask_block_on(stdio->subtask); free(stdio); } -static int stdout3_write(void *data, wasip3_write_t **out, off_t **offs) { +static int stdout3_write(void *data, wasi_write_t *out) { stdout3_t *stdio = (stdout3_t *)data; - if (!stdio->output.output) { - stdin_stream_u8_t read_side = stdin_stream_u8_new(&stdio->output.output); - stdio->output.subtask = (*stdio->stream_func)( - read_side, - (stdout_result_void_error_code_t *)&stdio->output.pending_result); + if (!stdio->output) { + assert(!stdio->subtask); + stdin_stream_u8_t read_side = stdin_stream_u8_new(&stdio->output); + stdio->subtask = (*stdio->stream_func)( + read_side, (stdout_result_void_error_code_t *)&stdio->pending_result); // subtask will be checked by write for error before writing } - *out = &stdio->output; - *offs = NULL; + out->offset = NULL; + out->blocking = true; + out->timeout = 0; + out->output = stdio->output; return 0; } -static int -stdin3_read(void *data, - filesystem_tuple2_stream_u8_future_result_void_error_code_t **out, - off_t **offs) { +static int stdin3_read_eof(void *data) { + stdin3_t *stdio = (stdin3_t *)data; + + stdin_result_void_error_code_t result; + wasip3_waitable_status_t status = + stdin_future_result_void_error_code_read(stdio->input.f1, &result); + size_t amt = wasip3_waitable_block_on(status, stdio->input.f1); + assert(amt == 1); + stdin_future_result_void_error_code_drop_readable(stdio->input.f1); + stdio->input.f1 = 0; + if (result.is_err) { + switch (result.val.err) { + case WASI_CLI_TYPES_ERROR_CODE_IO: + errno = EIO; + break; + case WASI_CLI_TYPES_ERROR_CODE_ILLEGAL_BYTE_SEQUENCE: + errno = EILSEQ; + break; + case WASI_CLI_TYPES_ERROR_CODE_PIPE: + errno = EPIPE; + break; + default: + assert(0); + break; + } + return -1; + } + return 0; +} + +static int stdin3_read(void *data, wasi_read_t *read) { stdin3_t *stdio = (stdin3_t *)data; if (!stdio->input.f0) { + assert(!stdio->input.f1); stdin_read_via_stream(&stdio->input); } - *out = (filesystem_tuple2_stream_u8_future_result_void_error_code_t *)&stdio - ->input; - *offs = NULL; + read->stream = stdio->input.f0; + read->offset = NULL; + read->blocking = true; + read->timeout = 0; + read->eof_data = data; + read->eof = stdin3_read_eof; return 0; } @@ -116,7 +152,7 @@ static int stdout3_isatty(void *data) { static descriptor_vtable_t stdin3_vtable = { .free = stdin3_free, - .get_read_stream3 = stdin3_read, + .get_read_stream = stdin3_read, .fstat = stdio3_fstat, .fcntl_getfl = stdin3_fcntl_getfl, .isatty = stdin3_isatty, @@ -124,7 +160,7 @@ static descriptor_vtable_t stdin3_vtable = { static descriptor_vtable_t stdout3_vtable = { .free = stdout3_free, - .get_write_stream3 = stdout3_write, + .get_write_stream = stdout3_write, .fstat = stdio3_fstat, .fcntl_getfl = stdout3_fcntl_getfl, .isatty = stdout3_isatty, From f967edb42abafb36a86740487b5b97e440bcbd21 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 3 Mar 2026 12:36:55 -0800 Subject: [PATCH 02/17] Update p3 symbols --- expected/wasm32-wasip3/defined-symbols.txt | 4 ++-- libc-bottom-half/sources/wasip3_stdio.c | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/expected/wasm32-wasip3/defined-symbols.txt b/expected/wasm32-wasip3/defined-symbols.txt index 02a9aafa9..850cfee4a 100644 --- a/expected/wasm32-wasip3/defined-symbols.txt +++ b/expected/wasm32-wasip3/defined-symbols.txt @@ -331,7 +331,7 @@ __wasilibc_parse_port __wasilibc_populate_preopens __wasilibc_pthread_self __wasilibc_random -__wasilibc_read_stream3 +__wasilibc_read __wasilibc_rename_newat __wasilibc_rename_oldat __wasilibc_reset_preopens @@ -345,7 +345,7 @@ __wasilibc_unspecified_addr __wasilibc_utimens __wasilibc_wasi_family_to_libc __wasilibc_wasi_to_sockaddr -__wasilibc_write_stream3 +__wasilibc_write __wasm_call_dtors __wcscoll_l __wcsftime_l diff --git a/libc-bottom-half/sources/wasip3_stdio.c b/libc-bottom-half/sources/wasip3_stdio.c index 8522a1540..3104f2def 100644 --- a/libc-bottom-half/sources/wasip3_stdio.c +++ b/libc-bottom-half/sources/wasip3_stdio.c @@ -79,6 +79,7 @@ static int stdin3_read_eof(void *data) { stdin_future_result_void_error_code_read(stdio->input.f1, &result); size_t amt = wasip3_waitable_block_on(status, stdio->input.f1); assert(amt == 1); + (void) amt; stdin_future_result_void_error_code_drop_readable(stdio->input.f1); stdio->input.f1 = 0; if (result.is_err) { From e4e0543e83112fa72f3be9ef01d14d21fd0fdec7 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 3 Mar 2026 13:10:31 -0800 Subject: [PATCH 03/17] Wasip3: File handing implementation This is an initial implementation of some basic file-handling functions such as reading, writing, opening directories, iterating them, etc. A number of previously failing tests are now passing and the `wasip2_file.c` implementation is now merged with WASIp3 with `file.c`. Bindings generation is also updated to flag some specific functions as being lowered synchronously as libc always blocks on the result. --- cmake/bindings.cmake | 11 + expected/wasm32-wasip2/defined-symbols.txt | 2 +- expected/wasm32-wasip3/defined-symbols.txt | 5 +- libc-bottom-half/CMakeLists.txt | 3 +- .../cloudlibc/src/libc/dirent/dirent_impl.h | 24 +- .../cloudlibc/src/libc/dirent/fdclosedir.c | 11 +- .../cloudlibc/src/libc/dirent/fdopendir.c | 14 +- .../cloudlibc/src/libc/dirent/readdir.c | 82 ++++-- .../cloudlibc/src/libc/dirent/rewinddir.c | 8 +- .../cloudlibc/src/libc/dirent/seekdir.c | 8 +- .../cloudlibc/src/libc/fcntl/openat.c | 15 +- .../cloudlibc/src/libc/stdio/renameat.c | 4 +- .../cloudlibc/src/libc/sys/stat/fstatat.c | 2 +- .../cloudlibc/src/libc/sys/stat/mkdirat.c | 2 +- .../cloudlibc/src/libc/sys/stat/utimensat.c | 2 +- .../cloudlibc/src/libc/time/clock_nanosleep.c | 9 +- .../cloudlibc/src/libc/unistd/faccessat.c | 2 +- .../cloudlibc/src/libc/unistd/linkat.c | 4 +- .../cloudlibc/src/libc/unistd/lseek.c | 9 +- .../cloudlibc/src/libc/unistd/read.c | 4 - .../cloudlibc/src/libc/unistd/readlinkat.c | 2 +- .../cloudlibc/src/libc/unistd/symlinkat.c | 4 +- .../cloudlibc/src/libc/unistd/write.c | 4 - .../headers/private/wasi/descriptor_table.h | 11 +- .../headers/private/wasi/file_utils.h | 12 +- .../headers/private/wasi/wasip3_block.h | 23 +- .../headers/public/wasi/__generated_wasip3.h | 23 +- libc-bottom-half/sources/__wasilibc_rmdirat.c | 2 +- libc-bottom-half/sources/__wasilibc_tell.c | 7 +- .../sources/__wasilibc_unlinkat.c | 2 +- .../sources/{wasip2_file.c => file.c} | 112 +++++++- libc-bottom-half/sources/file_utils.c | 52 ++-- libc-bottom-half/sources/wasip3.c | 245 +++++++++++++++--- libc-bottom-half/sources/wasip3_block_on.c | 73 +++--- libc-bottom-half/sources/wasip3_file.c | 14 - libc-bottom-half/sources/wasip3_stdio.c | 78 ++++-- test/CMakeLists.txt | 39 ++- test/src/close-stdio.c | 25 ++ test/src/stdio.c | 57 +++- test/stdio-test.cmake | 9 + 40 files changed, 731 insertions(+), 284 deletions(-) rename libc-bottom-half/sources/{wasip2_file.c => file.c} (72%) delete mode 100644 libc-bottom-half/sources/wasip3_file.c create mode 100644 test/src/close-stdio.c create mode 100644 test/stdio-test.cmake diff --git a/cmake/bindings.cmake b/cmake/bindings.cmake index ed1236ec0..529d41f28 100644 --- a/cmake/bindings.cmake +++ b/cmake/bindings.cmake @@ -107,6 +107,17 @@ add_custom_target( --rename wasi:cli/terminal-stdin@${wasip3-version}=terminal_stdin --rename wasi:cli/terminal-stdout@${wasip3-version}=terminal_stdout --rename wasi:cli/terminal-stderr@${wasip3-version}=terminal_stderr + + # Disable async bindings generation for some functions which are only + # ever called synchronously within libc. + "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.metadata-hash" + "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.metadata-hash-at" + "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.stat" + "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.get-flags" + "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.open-at" + "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.read-directory" + "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-until" + "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-for" ${CMAKE_SOURCE_DIR}/wasi/p3/wit COMMAND cmake -E copy wasip3.h ${bottom_half}/headers/public/wasi/__generated_wasip3.h COMMAND cmake -E copy wasip3_component_type.o ${bottom_half}/sources diff --git a/expected/wasm32-wasip2/defined-symbols.txt b/expected/wasm32-wasip2/defined-symbols.txt index d84358fe9..9996fa30a 100644 --- a/expected/wasm32-wasip2/defined-symbols.txt +++ b/expected/wasm32-wasip2/defined-symbols.txt @@ -1494,6 +1494,7 @@ vwprintf vwscanf wall_clock_now wall_clock_resolution +wasi_string_from_c wasip2_list_string_free wasip2_list_tuple2_string_string_free wasip2_list_u32_free @@ -1502,7 +1503,6 @@ wasip2_option_string_free wasip2_string_dup wasip2_string_dup_n wasip2_string_free -wasip2_string_from_c wasip2_string_set wasip2_tuple2_string_string_free wcpcpy diff --git a/expected/wasm32-wasip3/defined-symbols.txt b/expected/wasm32-wasip3/defined-symbols.txt index 850cfee4a..7596f452f 100644 --- a/expected/wasm32-wasip3/defined-symbols.txt +++ b/expected/wasm32-wasip3/defined-symbols.txt @@ -1463,21 +1463,25 @@ vswprintf vswscanf vwprintf vwscanf +wasi_string_from_c wasip3_backpressure_dec wasip3_backpressure_inc wasip3_context_get_0 wasip3_context_get_1 wasip3_context_set_0 wasip3_context_set_1 +wasip3_future_block_on wasip3_list_string_free wasip3_list_tuple2_string_string_free wasip3_list_u8_free wasip3_option_string_free +wasip3_stream_block_on wasip3_string_dup wasip3_string_dup_n wasip3_string_free wasip3_string_set wasip3_subtask_block_on +wasip3_subtask_block_on_and_drop wasip3_subtask_cancel wasip3_subtask_drop wasip3_task_cancel @@ -1495,7 +1499,6 @@ wasip3_thread_yield_cancellable wasip3_thread_yield_to_suspended wasip3_thread_yield_to_suspended_cancellable wasip3_tuple2_string_string_free -wasip3_waitable_block_on wasip3_waitable_join wasip3_waitable_set_drop wasip3_waitable_set_new diff --git a/libc-bottom-half/CMakeLists.txt b/libc-bottom-half/CMakeLists.txt index 4bdce3ff8..5cd333930 100644 --- a/libc-bottom-half/CMakeLists.txt +++ b/libc-bottom-half/CMakeLists.txt @@ -140,6 +140,7 @@ else() sources/getsockpeername.c sources/listen.c sources/file_utils.c + sources/file.c sources/recv.c sources/send.c sources/shutdown.c @@ -155,7 +156,6 @@ if(WASI STREQUAL "p2") list(APPEND bottom_half_sources sources/netdb.c sources/wasip2.c - sources/wasip2_file.c sources/wasip2_stdio.c ) endif() @@ -164,7 +164,6 @@ if (WASI STREQUAL "p3") list(APPEND bottom_half_sources sources/wasip3.c sources/wasip3_block_on.c - sources/wasip3_file.c sources/wasip3_stdio.c ) endif() diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/dirent_impl.h b/libc-bottom-half/cloudlibc/src/libc/dirent/dirent_impl.h index a76205e58..0d835c02d 100644 --- a/libc-bottom-half/cloudlibc/src/libc/dirent/dirent_impl.h +++ b/libc-bottom-half/cloudlibc/src/libc/dirent/dirent_impl.h @@ -28,7 +28,7 @@ struct _DIR { size_t skip; size_t offset; #elif defined(__wasip3__) - filesystem_stream_directory_entry_t stream; + filesystem_tuple2_stream_directory_entry_future_result_void_error_code_t stream; size_t skip; size_t offset; #else @@ -40,4 +40,26 @@ struct _DIR { size_t dirent_size; }; +static inline void dirent_close_streams(DIR *dirp) { +#if defined(__wasip1__) + // nothing to close ... +#elif defined(__wasip2__) + if (dirp->stream.__handle) { + filesystem_directory_entry_stream_drop_own(dirp->stream); + dirp->stream.__handle = 0; + } +#elif defined(__wasip3__) + if (dirp->stream.f0 != 0) { + filesystem_stream_directory_entry_drop_readable(dirp->stream.f0); + dirp->stream.f0 = 0; + } + if (dirp->stream.f1 != 0) { + filesystem_future_result_void_error_code_drop_readable(dirp->stream.f1); + dirp->stream.f1 = 0; + } +#else +# error "Unknown WASI version" +#endif +} + #endif diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/fdclosedir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/fdclosedir.c index c4fc4c434..03765388f 100644 --- a/libc-bottom-half/cloudlibc/src/libc/dirent/fdclosedir.c +++ b/libc-bottom-half/cloudlibc/src/libc/dirent/fdclosedir.c @@ -9,17 +9,10 @@ int fdclosedir(DIR *dirp) { int fd = dirp->fd; -#if defined(__wasip1__) +#ifdef __wasip1__ free(dirp->buffer); -#elif defined(__wasip2__) - if (dirp->stream.__handle != 0) - filesystem_directory_entry_stream_drop_own(dirp->stream); -#elif defined(__wasip3__) - if (dirp->stream != 0) - filesystem_stream_directory_entry_drop_readable(dirp->stream); -#else -# error "Unsupported WASI version" #endif + dirent_close_streams(dirp); 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 index 03dd1138d..edd0476fd 100644 --- a/libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c +++ b/libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c @@ -49,7 +49,7 @@ DIR *fdopendir(int fd) { dirp->dirent = NULL; dirp->dirent_size = 1; return dirp; -#elif defined(__wasip2__) +#elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; if (fd_to_file_handle(fd, &file_handle) < 0) { @@ -58,6 +58,7 @@ DIR *fdopendir(int fd) { } // Read the directory +#if defined(__wasip2__) filesystem_own_directory_entry_stream_t result; filesystem_error_code_t error_code; bool ok = filesystem_method_descriptor_read_directory(file_handle, @@ -69,19 +70,16 @@ DIR *fdopendir(int fd) { return NULL; } - dirp->fd = fd; dirp->stream = result; +#elif defined(__wasip3__) + filesystem_method_descriptor_read_directory(file_handle, &dirp->stream); +#endif + dirp->fd = fd; dirp->skip = 0; dirp->offset = 0; dirp->dirent = NULL; dirp->dirent_size = 1; return dirp; -#elif defined(__wasip3__) - (void) fd; - // TODO(wasip3) - errno = ENOTSUP; - free(dirp); - return NULL; #else # error "Unsupported WASI version" #endif diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c index 82b94613a..255c0ad99 100644 --- a/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c +++ b/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c @@ -31,8 +31,11 @@ static_assert(DT_REG == __WASI_FILETYPE_REGULAR_FILE, "Value mismatch"); static_assert(DT_UNKNOWN == __WASI_FILETYPE_UNKNOWN, "Value mismatch"); #endif +#ifdef __wasip3__ +#include +#endif + // Grows a buffer to be large enough to hold a certain amount of data. -#ifndef __wasip3__ static struct dirent* grow(struct dirent **buffer, size_t *buffer_size, size_t target_size) { if (*buffer_size < target_size) { size_t new_size = *buffer_size; @@ -48,7 +51,6 @@ static struct dirent* grow(struct dirent **buffer, size_t *buffer_size, size_t t } return *buffer; } -#endif #if defined(__wasip1__) struct dirent *readdir(DIR *dirp) { @@ -144,12 +146,13 @@ struct dirent *readdir(DIR *dirp) { } } -#elif defined(__wasip2__) +#elif defined(__wasip2__) || defined(__wasip3__) static int ensure_has_directory_stream(DIR *dirp, filesystem_borrow_descriptor_t *handle) { if (fd_to_file_handle(dirp->fd, handle) < 0) return -1; +#ifdef __wasip2__ if (dirp->stream.__handle != 0) return 0; @@ -161,10 +164,15 @@ static int ensure_has_directory_stream(DIR *dirp, filesystem_borrow_descriptor_t translate_error(error_code); return -1; } +#elif defined(__wasip3__) + if (dirp->stream.f0 == 0) + filesystem_method_descriptor_read_directory(*handle, &dirp->stream); +#endif return 0; } static struct dirent *readdir_next(DIR *dirp) { + bool ok; filesystem_metadata_hash_value_t metadata; filesystem_error_code_t error_code; filesystem_borrow_descriptor_t dir_handle; @@ -178,9 +186,9 @@ static struct dirent *readdir_next(DIR *dirp) { dirp->offset += 1; if (grow(&dirp->dirent, &dirp->dirent_size, offsetof(struct dirent, d_name) + 2) == NULL) return NULL; - bool ok = filesystem_method_descriptor_metadata_hash(dir_handle, - &metadata, - &error_code); + ok = filesystem_method_descriptor_metadata_hash(dir_handle, + &metadata, + &error_code); if (!ok) { translate_error(error_code); return NULL; @@ -206,11 +214,12 @@ static struct dirent *readdir_next(DIR *dirp) { return dirp->dirent; } +#if defined(__wasip2__) filesystem_borrow_directory_entry_stream_t stream = filesystem_borrow_directory_entry_stream(dirp->stream); filesystem_option_directory_entry_t dir_entry_optional; - bool ok = filesystem_method_directory_entry_stream_read_directory_entry(stream, - &dir_entry_optional, - &error_code); + ok = filesystem_method_directory_entry_stream_read_directory_entry(stream, + &dir_entry_optional, + &error_code); if (!ok) { translate_error(error_code); return NULL; @@ -222,10 +231,54 @@ static struct dirent *readdir_next(DIR *dirp) { filesystem_directory_entry_t dir_entry = dir_entry_optional.val; +#elif defined(__wasip3__) + filesystem_directory_entry_t dir_entry; + + // Loop until at least one stream entry is read, or until the stream is closed. + bool closed = false; + while (1) { + size_t amount = + wasip3_stream_block_on( + filesystem_stream_directory_entry_read(dirp->stream.f0, &dir_entry, 1), + dirp->stream.f0, + &closed); + + // If something was read, then break out and process that below. + if (amount > 0) + break; + + // If nothing was read and the stream isn't finished yet, try again. + if (!closed) + continue; + + // If the stream's result future hasn't been read yet, do so here. + if (dirp->stream.f1) { + filesystem_result_void_error_code_t result; + wasip3_future_block_on( + filesystem_future_result_void_error_code_read(dirp->stream.f1, &result), + dirp->stream.f1); + filesystem_future_result_void_error_code_drop_readable(dirp->stream.f1); + dirp->stream.f1 = 0; + if (result.is_err) + translate_error(result.val.err); + } + + // The stream is closed, so return NULL. This'll set `errno` based on the + // result of the future above. + return NULL; + } +#else +#error "Unknown WASI version" +#endif + // Ensure that the dirent is large enough to fit the filename size_t the_size = offsetof(struct dirent, d_name); if (grow(&dirp->dirent, &dirp->dirent_size, the_size + dir_entry.name.len + 1) == NULL) { +#ifdef __wasip2__ wasip2_string_free(&dir_entry.name); +#else + wasip3_string_free(&dir_entry.name); +#endif return NULL; } @@ -241,7 +294,11 @@ static struct dirent *readdir_next(DIR *dirp) { &dir_entry.name, &metadata, &error_code); +#ifdef __wasip2__ wasip2_string_free(&dir_entry.name); +#else + wasip3_string_free(&dir_entry.name); +#endif if (!ok) { translate_error(error_code); return NULL; @@ -260,13 +317,6 @@ struct dirent *readdir(DIR *dirp) { } return result; } -#elif defined(__wasip3__) -struct dirent *readdir(DIR *dirp) { - // TODO(wasip3) - errno = ENOTSUP; - free(dirp); - return NULL; -} #else # error "Unknown WASI version" #endif diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/rewinddir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/rewinddir.c index e92b143e5..97d6dc427 100644 --- a/libc-bottom-half/cloudlibc/src/libc/dirent/rewinddir.c +++ b/libc-bottom-half/cloudlibc/src/libc/dirent/rewinddir.c @@ -13,12 +13,8 @@ void rewinddir(DIR *dirp) { 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; -#elif defined(__wasip2__) - dirp->stream.__handle = 0; - dirp->skip = 0; - dirp->offset = 0; -#elif defined(__wasip3__) - dirp->stream = 0; +#elif defined(__wasip2__) || defined(__wasip3__) + dirent_close_streams(dirp); dirp->skip = 0; dirp->offset = 0; #else diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/seekdir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/seekdir.c index 80f76abdf..b87d079b0 100644 --- a/libc-bottom-half/cloudlibc/src/libc/dirent/seekdir.c +++ b/libc-bottom-half/cloudlibc/src/libc/dirent/seekdir.c @@ -13,12 +13,8 @@ void seekdir(DIR *dirp, 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; -#elif defined(__wasip2__) - dirp->stream.__handle = 0; - dirp->skip = loc; - dirp->offset = 0; -#elif defined(__wasip3__) - dirp->stream = 0; +#elif defined(__wasip2__) || defined(__wasip3__) + dirent_close_streams(dirp); dirp->skip = loc; dirp->offset = 0; #else diff --git a/libc-bottom-half/cloudlibc/src/libc/fcntl/openat.c b/libc-bottom-half/cloudlibc/src/libc/fcntl/openat.c index 418898542..9762263aa 100644 --- a/libc-bottom-half/cloudlibc/src/libc/fcntl/openat.c +++ b/libc-bottom-half/cloudlibc/src/libc/fcntl/openat.c @@ -87,7 +87,7 @@ int __wasilibc_nocwd_openat_nomode(int fd, const char *path, int oflag) { return -1; } return newfd; -#elif defined(__wasip2__) +#elif defined(__wasip2__) || defined(__wasip3__) // Set up path flags filesystem_path_flags_t lookup_flags = 0; if ((oflag & O_NOFOLLOW) == 0) @@ -140,8 +140,8 @@ int __wasilibc_nocwd_openat_nomode(int fd, const char *path, int oflag) { return -1; // Construct a WASI string for the path - wasip2_string_t path2; - if (wasip2_string_from_c(path, &path2) < 0) + wasi_string_t wasi_path; + if (wasi_string_from_c(path, &wasi_path) < 0) return -1; // Open the file, yielding a new handle @@ -149,7 +149,7 @@ int __wasilibc_nocwd_openat_nomode(int fd, const char *path, int oflag) { filesystem_error_code_t error_code; bool ok = filesystem_method_descriptor_open_at(file_handle, lookup_flags, - &path2, + &wasi_path, open_flags, fs_flags, &new_handle, @@ -161,13 +161,6 @@ int __wasilibc_nocwd_openat_nomode(int fd, const char *path, int oflag) { // Update the descriptor table with the new handle return __wasilibc_add_file(new_handle, oflag); -#elif defined(__wasip3__) - (void) fd; - (void) path; - (void) oflag; - // TODO(wasip3) - errno = ENOTSUP; - return -1; #else # error "Unsupported WASI version" #endif diff --git a/libc-bottom-half/cloudlibc/src/libc/stdio/renameat.c b/libc-bottom-half/cloudlibc/src/libc/stdio/renameat.c index c2b4801d6..39d2730ad 100644 --- a/libc-bottom-half/cloudlibc/src/libc/stdio/renameat.c +++ b/libc-bottom-half/cloudlibc/src/libc/stdio/renameat.c @@ -31,9 +31,9 @@ int __wasilibc_nocwd_renameat(int oldfd, const char *old, int newfd, const char // Convert the strings into WASI strings wasip2_string_t old_path, new_path; - if (wasip2_string_from_c(old, &old_path) < 0) + if (wasi_string_from_c(old, &old_path) < 0) return -1; - if (wasip2_string_from_c(new, &new_path) < 0) + if (wasi_string_from_c(new, &new_path) < 0) return -1; // Rename the file diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c index cdc97959b..7d8561edb 100644 --- a/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c +++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c @@ -43,7 +43,7 @@ int __wasilibc_nocwd_fstatat(int fd, const char *restrict path, struct stat *res // Convert the string into a Wasm string wasip2_string_t path_wasm_string; - if (wasip2_string_from_c(path, &path_wasm_string) < 0) + if (wasi_string_from_c(path, &path_wasm_string) < 0) return -1; // Get the metadata hash for this file diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/mkdirat.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/mkdirat.c index b1f177849..52f9fef26 100644 --- a/libc-bottom-half/cloudlibc/src/libc/sys/stat/mkdirat.c +++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/mkdirat.c @@ -30,7 +30,7 @@ int __wasilibc_nocwd_mkdirat_nomode(int fd, const char *path) { // Create the directory filesystem_error_code_t error; wasip2_string_t path2; - if (wasip2_string_from_c(path, &path2) < 0) + if (wasi_string_from_c(path, &path2) < 0) return -1; bool ok = filesystem_method_descriptor_create_directory_at(file_handle, &path2, diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/utimensat.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/utimensat.c index 53f5b06f5..4be2d0a1e 100644 --- a/libc-bottom-half/cloudlibc/src/libc/sys/stat/utimensat.c +++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/utimensat.c @@ -59,7 +59,7 @@ int __wasilibc_nocwd_utimensat(int fd, const char *path, const struct timespec t // Convert the string into a Wasm string wasip2_string_t path_wasm_string; - if (wasip2_string_from_c(path, &path_wasm_string) < 0) + if (wasi_string_from_c(path, &path_wasm_string) < 0) return -1; // Perform system call. diff --git a/libc-bottom-half/cloudlibc/src/libc/time/clock_nanosleep.c b/libc-bottom-half/cloudlibc/src/libc/time/clock_nanosleep.c index 9c72d59ee..d868b542d 100644 --- a/libc-bottom-half/cloudlibc/src/libc/time/clock_nanosleep.c +++ b/libc-bottom-half/cloudlibc/src/libc/time/clock_nanosleep.c @@ -8,9 +8,6 @@ #include #include #include -#ifdef __wasip3__ -#include -#endif #ifdef __wasip1__ static_assert(TIMER_ABSTIME == __WASI_SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME, @@ -61,13 +58,11 @@ int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *rqtp, poll_pollable_drop_own(pollable); #else // defined(__wasip3__) - wasip3_subtask_status_t status; if (flags & TIMER_ABSTIME) { - status = monotonic_clock_wait_until(duration); + monotonic_clock_wait_until(duration); } else { - status = monotonic_clock_wait_for(duration); + monotonic_clock_wait_for(duration); } - wasip3_subtask_block_on(status); #endif return 0; diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/faccessat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/faccessat.c index 5a6b93a4b..38a48945f 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/faccessat.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/faccessat.c @@ -64,7 +64,7 @@ int __wasilibc_nocwd_faccessat(int fd, const char *path, int amode, int flag) { // Convert the string into a WASI string wasip2_string_t wasi_path; - if (wasip2_string_from_c(path, &wasi_path) < 0) + if (wasi_string_from_c(path, &wasi_path) < 0) return -1; // Call stat() to check if the file exists diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/linkat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/linkat.c index 8d1184768..2cbd27dc1 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/linkat.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/linkat.c @@ -36,9 +36,9 @@ int __wasilibc_nocwd_linkat(int fd1, const char *path1, int fd2, const char *pat // Convert the strings into WASI strings wasip2_string_t path1_wasi, path2_wasi; - if (wasip2_string_from_c(path1, &path1_wasi) < 0) + if (wasi_string_from_c(path1, &path1_wasi) < 0) return -1; - if (wasip2_string_from_c(path2, &path2_wasi) < 0) + if (wasi_string_from_c(path2, &path2_wasi) < 0) return -1; // Create the link diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/lseek.c b/libc-bottom-half/cloudlibc/src/libc/unistd/lseek.c index c9a6e3d0f..1305686a5 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/lseek.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/lseek.c @@ -29,7 +29,7 @@ off_t __lseek(int fildes, off_t offset, int whence) { return -1; } return new_offset; -#elif defined(__wasip2__) +#elif defined(__wasip2__) || defined(__wasip3__) // Look up a stream for fildes descriptor_table_entry_t *entry = descriptor_table_get_ref(fildes); if (!entry) @@ -39,13 +39,6 @@ off_t __lseek(int fildes, off_t offset, int whence) { return -1; } return entry->vtable->seek(entry->data, offset, whence); -#elif defined(__wasip3__) - (void) fildes; - (void) offset; - (void) whence; - // TODO(wasip3) - errno = ENOTSUP; - return -1; #else # error "Unknown WASI version" #endif diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/read.c b/libc-bottom-half/cloudlibc/src/libc/unistd/read.c index 36f2241ce..d2f43e14f 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/read.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/read.c @@ -13,10 +13,6 @@ #include #endif -#ifdef __wasip3__ -#include -#endif - ssize_t read(int fildes, void *buf, size_t nbyte) { #if defined(__wasip1__) __wasi_iovec_t iov = {.buf = buf, .buf_len = nbyte}; diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/readlinkat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/readlinkat.c index a8339d7a6..656b9c501 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/readlinkat.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/readlinkat.c @@ -31,7 +31,7 @@ ssize_t __wasilibc_nocwd_readlinkat(int fd, const char *restrict path, char *res // Convert the path into a WASI path wasip2_string_t wasi_path, link_source; - if (wasip2_string_from_c(path, &wasi_path) < 0) + if (wasi_string_from_c(path, &wasi_path) < 0) return -1; // Read the link diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/symlinkat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/symlinkat.c index f727f8786..96aea65be 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/symlinkat.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/symlinkat.c @@ -27,9 +27,9 @@ int __wasilibc_nocwd_symlinkat(const char *path1, int fd, const char *path2) { // Convert the paths into WASI paths wasip2_string_t path1_wasi, path2_wasi; - if (wasip2_string_from_c(path1, &path1_wasi) < 0) + if (wasi_string_from_c(path1, &path1_wasi) < 0) return -1; - if (wasip2_string_from_c(path2, &path2_wasi) < 0) + if (wasi_string_from_c(path2, &path2_wasi) < 0) return -1; // Construct the link diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/write.c b/libc-bottom-half/cloudlibc/src/libc/unistd/write.c index ca191910b..c7e010e37 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/write.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/write.c @@ -13,10 +13,6 @@ #include #endif -#ifdef __wasip3__ -#include -#endif - ssize_t write(int fildes, const void *buf, size_t nbyte) { #if defined(__wasip1__) __wasi_ciovec_t iov = {.buf = buf, .buf_len = nbyte}; diff --git a/libc-bottom-half/headers/private/wasi/descriptor_table.h b/libc-bottom-half/headers/private/wasi/descriptor_table.h index e7841aeb5..4867a3e6f 100644 --- a/libc-bottom-half/headers/private/wasi/descriptor_table.h +++ b/libc-bottom-half/headers/private/wasi/descriptor_table.h @@ -45,11 +45,14 @@ typedef struct wasi_write_t { streams_borrow_output_stream_t output; poll_own_pollable_t *pollable; #else + // The actual stream that is being written to. filesystem_stream_u8_writer_t output; - // contents will be filled by host (once write has an error) - filesystem_result_void_error_code_t pending_result; - // this task gets ready on error or eof - wasip3_subtask_t *subtask; + // An indicator if `output` has been closed/dropped. + bool *done; + // A callback/ptr pair to invoke when EOF is reached to set errno and return + // an error code. + int (*eof)(void*); + void *eof_data; #endif } wasi_write_t; diff --git a/libc-bottom-half/headers/private/wasi/file_utils.h b/libc-bottom-half/headers/private/wasi/file_utils.h index 11dc1e9eb..cd6f7dded 100644 --- a/libc-bottom-half/headers/private/wasi/file_utils.h +++ b/libc-bottom-half/headers/private/wasi/file_utils.h @@ -38,16 +38,22 @@ static inline int wasip2_handle_write_error(streams_stream_error_t error) { errno = EIO; return -1; } +#endif + +#ifdef __wasip2__ +typedef wasip2_string_t wasi_string_t; +#else +typedef wasip3_string_t wasi_string_t; +#endif // Converts the C string `s` into a WASI string stored in `out`. // -// The returned `wasip2_string_t` should not be deallocated or free'd, and it +// The returned `wasip{2,3}_string_t` should not be deallocated or free'd, and it // can only be used while `s` is also valid. // // Returns 0 if `s` is valid utf-8. // Returns -1 and sets errno to `ENOENT` if `s` is not valid utf-8. -int wasip2_string_from_c(const char *s, wasip2_string_t* out); -#endif +int wasi_string_from_c(const char *s, wasi_string_t* out); // Succeed only if fd is bound to a file handle in the descriptor table static inline int fd_to_file_handle(int fd, filesystem_borrow_descriptor_t* result) { diff --git a/libc-bottom-half/headers/private/wasi/wasip3_block.h b/libc-bottom-half/headers/private/wasi/wasip3_block.h index a7dcf4a9a..d38d69e8e 100644 --- a/libc-bottom-half/headers/private/wasi/wasip3_block.h +++ b/libc-bottom-half/headers/private/wasi/wasip3_block.h @@ -8,9 +8,26 @@ #include // for waitable_t // Waits for a subtask to return -void wasip3_subtask_block_on(wasip3_subtask_status_t status); -// Waits for a (stream) transfer to complete -size_t wasip3_waitable_block_on(wasip3_waitable_status_t status, waitable_t stream); +void wasip3_subtask_block_on_and_drop(wasip3_subtask_t status); + +/// Waits for `future` to be resolved after a previous operation yielded +/// `status` as a result. +/// +/// This function will block if `status` indicates that the future is blocked, +/// and this function won't return until the future has become ready again. This is +/// suitable, for example, for waiting for future reads to complete. +void wasip3_future_block_on(wasip3_subtask_status_t status, uint32_t future); + +/// Waits for `stream` to be resolved after a previous operation yielded +/// `status` as a result. +/// +/// This function will block if `status` indicates that the stream is blocked, +/// and this function won't return until the stream has become ready again. This is +/// suitable, for example, for waiting for stream read or write to complete. +/// +/// The `closed` variable is set based on the result of the operation to +/// communicate what was received from the component model. +size_t wasip3_stream_block_on(wasip3_subtask_status_t status, uint32_t stream, bool *closed); #endif #endif // WASI_WASIP3_BLOCK_H diff --git a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h index 2a5d526ce..cb1aa21df 100644 --- a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h +++ b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h @@ -593,13 +593,6 @@ typedef struct filesystem_method_descriptor_link_at_args { filesystem_borrow_descriptor_t new_descriptor; wasip3_string_t new_path; } filesystem_method_descriptor_link_at_args_t; -typedef struct filesystem_method_descriptor_open_at_args { - filesystem_borrow_descriptor_t self; - filesystem_path_flags_t path_flags; - wasip3_string_t path; - filesystem_open_flags_t open_flags; - filesystem_descriptor_flags_t flags; -} filesystem_method_descriptor_open_at_args_t; typedef struct filesystem_method_descriptor_rename_at_args { filesystem_borrow_descriptor_t self; wasip3_string_t old_path; @@ -1014,9 +1007,9 @@ extern monotonic_clock_mark_t monotonic_clock_now(void); // corresponding to a clock tick. extern monotonic_clock_duration_t monotonic_clock_get_resolution(void); // Wait until the specified mark has occurred. -extern wasip3_subtask_status_t monotonic_clock_wait_until(monotonic_clock_mark_t when); +extern void monotonic_clock_wait_until(monotonic_clock_mark_t when); // Wait for the specified duration to elapse. -extern wasip3_subtask_status_t monotonic_clock_wait_for(monotonic_clock_duration_t how_long); +extern void monotonic_clock_wait_for(monotonic_clock_duration_t how_long); // Imported Functions from `wasi:clocks/system-clock@0.3.0-rc-2026-01-06` // Read the current value of the clock. @@ -1087,7 +1080,7 @@ extern wasip3_subtask_status_t filesystem_method_descriptor_sync_data(filesystem // // Note: This returns the value that was the `fs_flags` value returned // from `fdstat_get` in earlier versions of WASI. -extern wasip3_subtask_status_t filesystem_method_descriptor_get_flags(filesystem_borrow_descriptor_t self, filesystem_result_descriptor_flags_error_code_t *result); +extern bool filesystem_method_descriptor_get_flags(filesystem_borrow_descriptor_t self, filesystem_descriptor_flags_t *ret, filesystem_error_code_t *err); // Get the dynamic type of a descriptor. // // Note: This returns the same value as the `type` field of the `fd-stat` @@ -1122,7 +1115,7 @@ extern wasip3_subtask_status_t filesystem_method_descriptor_set_times(filesystem // // This function returns a future, which will resolve to an error code if // reading full contents of the directory fails. -extern wasip3_subtask_status_t filesystem_method_descriptor_read_directory(filesystem_borrow_descriptor_t self, filesystem_tuple2_stream_directory_entry_future_result_void_error_code_t *result); +extern void filesystem_method_descriptor_read_directory(filesystem_borrow_descriptor_t self, filesystem_tuple2_stream_directory_entry_future_result_void_error_code_t *ret); // Synchronize the data and metadata of a file to disk. // // This function succeeds with no effect if the file descriptor is not @@ -1143,7 +1136,7 @@ extern wasip3_subtask_status_t filesystem_method_descriptor_create_directory_at( // modified, use `metadata-hash`. // // Note: This was called `fd_filestat_get` in earlier versions of WASI. -extern wasip3_subtask_status_t filesystem_method_descriptor_stat(filesystem_borrow_descriptor_t self, filesystem_result_descriptor_stat_error_code_t *result); +extern bool filesystem_method_descriptor_stat(filesystem_borrow_descriptor_t self, filesystem_descriptor_stat_t *ret, filesystem_error_code_t *err); // Return the attributes of a file or directory. // // Note: This is similar to `fstatat` in POSIX, except that it does not @@ -1179,7 +1172,7 @@ extern wasip3_subtask_status_t filesystem_method_descriptor_link_at(filesystem_m // `error-code::read-only`. // // Note: This is similar to `openat` in POSIX. -extern wasip3_subtask_status_t filesystem_method_descriptor_open_at(filesystem_method_descriptor_open_at_args_t *args, filesystem_result_own_descriptor_error_code_t *result); +extern bool filesystem_method_descriptor_open_at(filesystem_borrow_descriptor_t self, filesystem_path_flags_t path_flags, wasip3_string_t *path, filesystem_open_flags_t open_flags, filesystem_descriptor_flags_t flags, filesystem_own_descriptor_t *ret, filesystem_error_code_t *err); // Read the contents of a symbolic link. // // If the contents contain an absolute or rooted path in the underlying @@ -1235,12 +1228,12 @@ extern wasip3_subtask_status_t filesystem_method_descriptor_is_same_object(files // computed hash. // // However, none of these is required. -extern wasip3_subtask_status_t filesystem_method_descriptor_metadata_hash(filesystem_borrow_descriptor_t self, filesystem_result_metadata_hash_value_error_code_t *result); +extern bool filesystem_method_descriptor_metadata_hash(filesystem_borrow_descriptor_t self, filesystem_metadata_hash_value_t *ret, filesystem_error_code_t *err); // Return a hash of the metadata associated with a filesystem object referred // to by a directory descriptor and a relative path. // // This performs the same hash computation as `metadata-hash`. -extern wasip3_subtask_status_t filesystem_method_descriptor_metadata_hash_at(filesystem_borrow_descriptor_t self, filesystem_path_flags_t path_flags, wasip3_string_t path, filesystem_result_metadata_hash_value_error_code_t *result); +extern bool filesystem_method_descriptor_metadata_hash_at(filesystem_borrow_descriptor_t self, filesystem_path_flags_t path_flags, wasip3_string_t *path, filesystem_metadata_hash_value_t *ret, filesystem_error_code_t *err); // Imported Functions from `wasi:filesystem/preopens@0.3.0-rc-2026-01-06` // Return the set of preopened directories, and their paths. diff --git a/libc-bottom-half/sources/__wasilibc_rmdirat.c b/libc-bottom-half/sources/__wasilibc_rmdirat.c index 6096a36c1..50bfef338 100644 --- a/libc-bottom-half/sources/__wasilibc_rmdirat.c +++ b/libc-bottom-half/sources/__wasilibc_rmdirat.c @@ -23,7 +23,7 @@ int __wasilibc_nocwd___wasilibc_rmdirat(int fd, const char *path) { // Create a WASI string for the path wasip2_string_t wasi_path; - if (wasip2_string_from_c(path, &wasi_path) < 0) + if (wasi_string_from_c(path, &wasi_path) < 0) return -1; filesystem_error_code_t error_code; diff --git a/libc-bottom-half/sources/__wasilibc_tell.c b/libc-bottom-half/sources/__wasilibc_tell.c index 8806596e2..658dfedb7 100644 --- a/libc-bottom-half/sources/__wasilibc_tell.c +++ b/libc-bottom-half/sources/__wasilibc_tell.c @@ -19,7 +19,7 @@ off_t __wasilibc_tell(int fildes) { return -1; } return offset; -#elif defined(__wasip2__) +#elif defined(__wasip2__) || defined(__wasip3__) // Look up a stream for fildes descriptor_table_entry_t *entry = descriptor_table_get_ref(fildes); if (!entry) @@ -29,11 +29,6 @@ off_t __wasilibc_tell(int fildes) { return -1; } return entry->vtable->seek(entry->data, 0, SEEK_CUR); -#elif defined(__wasip3__) - (void)fildes; - // TODO(wasip3) - errno = ENOTSUP; - return -1; #else #error "Unsupported WASI version" #endif diff --git a/libc-bottom-half/sources/__wasilibc_unlinkat.c b/libc-bottom-half/sources/__wasilibc_unlinkat.c index 4ac919000..7bd2cde8e 100644 --- a/libc-bottom-half/sources/__wasilibc_unlinkat.c +++ b/libc-bottom-half/sources/__wasilibc_unlinkat.c @@ -34,7 +34,7 @@ int __wasilibc_nocwd___wasilibc_unlinkat(int fd, const char *path) { // Create a Wasm string from the path wasip2_string_t wasi_path; - if (wasip2_string_from_c(path, &wasi_path) < 0) + if (wasi_string_from_c(path, &wasi_path) < 0) return -1; // Unlink the file diff --git a/libc-bottom-half/sources/wasip2_file.c b/libc-bottom-half/sources/file.c similarity index 72% rename from libc-bottom-half/sources/wasip2_file.c rename to libc-bottom-half/sources/file.c index 7da5cb8a3..78cdcef3a 100644 --- a/libc-bottom-half/sources/wasip2_file.c +++ b/libc-bottom-half/sources/file.c @@ -5,30 +5,38 @@ #include #include #include - -#ifdef __wasip2__ +#include #include "libc/sys/stat/stat_impl.h" typedef struct { filesystem_own_descriptor_t file_handle; + // Current position in stream, relative to the beginning of the + // *file_handle*, measured in bytes + off_t offset; + int oflag; +#ifdef __wasip2__ // Lazily initialized read/write streams which track the current position // in the file. Seeking will close these streams and cause them to be // reopened on the next operation, for example. streams_own_input_stream_t read_stream; streams_own_output_stream_t write_stream; - // Current position in stream, relative to the beginning of the - // *file_handle*, measured in bytes - off_t offset; // Used for checking readiness to read/write to stream. Lazily initialized // and initially set to 0. streams_own_pollable_t read_pollable; streams_own_pollable_t write_pollable; - int oflag; +#else + filesystem_tuple2_stream_u8_future_result_void_error_code_t read; + filesystem_stream_u8_writer_t write; + wasip3_subtask_t write_subtask; + filesystem_result_void_error_code_t write_pending_result; + bool write_done; +#endif } file_t; static void file_close_streams(void *data) { file_t *file = (file_t *)data; +#ifdef __wasip2__ if (file->read_pollable.__handle != 0) { poll_pollable_drop_own(file->read_pollable); file->read_pollable.__handle = 0; @@ -45,6 +53,28 @@ static void file_close_streams(void *data) { streams_output_stream_drop_own(file->write_stream); file->write_stream.__handle = 0; } +#else + if (file->read.f0 != 0) { + filesystem_stream_u8_drop_readable(file->read.f0); + file->read.f0 = 0; + } + if (file->read.f1 != 0) { + filesystem_future_result_void_error_code_drop_readable(file->read.f1); + file->read.f1 = 0; + } + if (file->write != 0) { + filesystem_stream_u8_drop_writable(file->write); + file->write = 0; + } + if (file->write_subtask != 0) { + // TODO: this should use `wasip3_subtask_cancel` but right now that's buggy + // in Wasmtime. For now assume closign the stream above is enough to have + // the subtask here finish promptly, so block on the result. + wasip3_subtask_block_on_and_drop(file->write_subtask); + file->write_subtask = 0; + } + file->write_done = false; +#endif } static void file_free(void *data) { @@ -54,8 +84,29 @@ static void file_free(void *data) { free(file); } +#ifndef __wasip2__ +static int file_read_eof(void *data) { + file_t *file = (file_t *)data; + + if (file->read.f1 != 0) { + filesystem_result_void_error_code_t result; + wasip3_future_block_on( + filesystem_future_result_void_error_code_read(file->read.f1, &result), + file->read.f1); + filesystem_future_result_void_error_code_drop_readable(file->read.f1); + file->read.f1 = 0; + if (result.is_err) { + translate_error(result.val.err); + return -1; + } + } + return 0; +} +#endif + static int file_get_read_stream(void *data, wasi_read_t *read) { file_t *file = (file_t *)data; +#ifdef __wasip2__ if (file->read_stream.__handle == 0) { filesystem_error_code_t error_code; bool ok = filesystem_method_descriptor_read_via_stream( @@ -67,15 +118,42 @@ static int file_get_read_stream(void *data, wasi_read_t *read) { } } read->input = streams_borrow_input_stream(file->read_stream); - read->offset = &file->offset; read->pollable = &file->read_pollable; +#else + if (file->read.f0 == 0) { + filesystem_method_descriptor_read_via_stream( + filesystem_borrow_descriptor(file->file_handle), file->offset, + &file->read); + } + read->stream = file->read.f0; + read->eof = file_read_eof; + read->eof_data = data; +#endif + read->offset = &file->offset; read->timeout = 0; read->blocking = true; return 0; } +#ifndef __wasip2__ +static int file_write_eof(void *data) { + file_t *file = (file_t *)data; + + if (file->write_subtask != 0) { + wasip3_subtask_block_on_and_drop(file->write_subtask); + file->write_subtask = 0; + } + if (file->write_pending_result.is_err) { + translate_error(file->write_pending_result.val.err); + return -1; + } + return 0; +} +#endif + static int file_get_write_stream(void *data, wasi_write_t *write) { file_t *file = (file_t *)data; +#ifdef __wasip2__ if (file->write_stream.__handle == 0) { filesystem_error_code_t error_code; bool ok; @@ -95,8 +173,24 @@ static int file_get_write_stream(void *data, wasi_write_t *write) { } } write->output = streams_borrow_output_stream(file->write_stream); - write->offset = &file->offset; write->pollable = &file->write_pollable; +#else + if (file->write == 0) { + assert(file->write_subtask == 0); + filesystem_stream_u8_t write_read = filesystem_stream_u8_new(&file->write); + wasip3_subtask_status_t status = + filesystem_method_descriptor_write_via_stream( + filesystem_borrow_descriptor(file->file_handle), write_read, + file->offset, &file->write_pending_result); + if (WASIP3_SUBTASK_STATE(status) != WASIP3_SUBTASK_RETURNED) + file->write_subtask = WASIP3_SUBTASK_HANDLE(status); + } + write->output = file->write; + write->eof = file_write_eof; + write->eof_data = data; + write->done = &file->write_done; +#endif + write->offset = &file->offset; write->timeout = 0; write->blocking = true; return 0; @@ -257,5 +351,3 @@ int __wasilibc_add_file(filesystem_own_descriptor_t file_handle, int oflag) { entry.data = file; return descriptor_table_insert(entry); } - -#endif // __wasip2__ diff --git a/libc-bottom-half/sources/file_utils.c b/libc-bottom-half/sources/file_utils.c index 9c2a50fbc..60175a446 100644 --- a/libc-bottom-half/sources/file_utils.c +++ b/libc-bottom-half/sources/file_utils.c @@ -6,7 +6,6 @@ #include #include -#ifdef __wasip2__ /** * Validates that `ptr_signed` is a valid utf-8 string. * @@ -72,7 +71,7 @@ static int validate_utf8(const char *ptr_signed) { return i; } -int wasip2_string_from_c(const char *s, wasip2_string_t *out) { +int wasi_string_from_c(const char *s, wasi_string_t *out) { int len = validate_utf8(s); if (len < 0) { errno = EILSEQ; @@ -82,7 +81,6 @@ int wasip2_string_from_c(const char *s, wasip2_string_t *out) { out->len = len; return 0; } -#endif ssize_t __wasilibc_write(wasi_write_t *write, const void *buffer, size_t length) { @@ -161,28 +159,22 @@ ssize_t __wasilibc_write(wasi_write_t *write, const void *buffer, } } #elif defined(__wasip3__) - if (WASIP3_SUBTASK_STATE(*write->subtask) == WASIP3_SUBTASK_STARTING || - WASIP3_SUBTASK_STATE(*write->subtask) == WASIP3_SUBTASK_STARTED) { - // the stream is still active - wasip3_waitable_status_t status = - filesystem_stream_u8_write(write->output, buffer, length); - size_t amount = wasip3_waitable_block_on(status, write->output); + assert(write->blocking); // TODO(wasip3) + assert(write->timeout == 0); // TODO(wasip3) + + // Perform writes until either a non-zero-length write completes or the stream + // is closed. + while (!*write->done) { + size_t amount = wasip3_stream_block_on( + filesystem_stream_u8_write(write->output, buffer, length), + write->output, write->done); if (amount > 0 || length == 0) { if (write->offset) *write->offset += amount; return amount; } - // error or eof - wasip3_subtask_block_on(*write->subtask); - wasip3_subtask_drop(*write->subtask); - *write->subtask = WASIP3_SUBTASK_RETURNED; } - if (write->pending_result.is_err) { - translate_error(write->pending_result.val.err); - return -1; - } - // EOF - return 0; + return write->eof(write->eof_data); #else #error "Unknown WASI version" #endif @@ -253,13 +245,21 @@ ssize_t __wasilibc_read(wasi_read_t *read, void *buffer, size_t length) { } } #elif defined(__wasip3__) - wasip3_waitable_status_t status = - filesystem_stream_u8_read(read->stream, buffer, length); - size_t amount = wasip3_waitable_block_on(status, read->stream); - if (amount > 0 || length == 0) { - if (read->offset) - *read->offset += amount; - return amount; + assert(read->blocking); // TODO(wasip3) + assert(read->timeout == 0); // TODO(wasip3) + + // Attempt reads until a nonzero-length read is encountered or the stream is + // closed. + bool closed = false; + while (!closed) { + size_t amount = wasip3_stream_block_on( + filesystem_stream_u8_read(read->stream, buffer, length), read->stream, + &closed); + if (amount > 0 || length == 0) { + if (read->offset) + *read->offset += amount; + return amount; + } } return read->eof(read->eof_data); #else diff --git a/libc-bottom-half/sources/wasip3.c b/libc-bottom-half/sources/wasip3.c index 8b0f3d960..8e25f23b5 100644 --- a/libc-bottom-half/sources/wasip3.c +++ b/libc-bottom-half/sources/wasip3.c @@ -57,11 +57,11 @@ extern int64_t __wasm_import_monotonic_clock_now(void); __attribute__((__import_module__("wasi:clocks/monotonic-clock@0.3.0-rc-2026-01-06"), __import_name__("get-resolution"))) extern int64_t __wasm_import_monotonic_clock_get_resolution(void); -__attribute__((__import_module__("wasi:clocks/monotonic-clock@0.3.0-rc-2026-01-06"), __import_name__("[async-lower]wait-until"))) -extern int32_t __wasm_import_monotonic_clock_wait_until(int64_t); +__attribute__((__import_module__("wasi:clocks/monotonic-clock@0.3.0-rc-2026-01-06"), __import_name__("wait-until"))) +extern void __wasm_import_monotonic_clock_wait_until(int64_t); -__attribute__((__import_module__("wasi:clocks/monotonic-clock@0.3.0-rc-2026-01-06"), __import_name__("[async-lower]wait-for"))) -extern int32_t __wasm_import_monotonic_clock_wait_for(int64_t); +__attribute__((__import_module__("wasi:clocks/monotonic-clock@0.3.0-rc-2026-01-06"), __import_name__("wait-for"))) +extern void __wasm_import_monotonic_clock_wait_for(int64_t); // Imported Functions from `wasi:clocks/system-clock@0.3.0-rc-2026-01-06` @@ -88,8 +88,8 @@ extern int32_t __wasm_import_filesystem_method_descriptor_advise(int32_t, int64_ __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.sync-data"))) extern int32_t __wasm_import_filesystem_method_descriptor_sync_data(int32_t, uint8_t *); -__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.get-flags"))) -extern int32_t __wasm_import_filesystem_method_descriptor_get_flags(int32_t, uint8_t *); +__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.get-flags"))) +extern void __wasm_import_filesystem_method_descriptor_get_flags(int32_t, uint8_t *); __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.get-type"))) extern int32_t __wasm_import_filesystem_method_descriptor_get_type(int32_t, uint8_t *); @@ -100,8 +100,8 @@ extern int32_t __wasm_import_filesystem_method_descriptor_set_size(int32_t, int6 __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.set-times"))) extern int32_t __wasm_import_filesystem_method_descriptor_set_times(uint8_t *, uint8_t *); -__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.read-directory"))) -extern int32_t __wasm_import_filesystem_method_descriptor_read_directory(int32_t, uint8_t *); +__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.read-directory"))) +extern void __wasm_import_filesystem_method_descriptor_read_directory(int32_t, uint8_t *); __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.sync"))) extern int32_t __wasm_import_filesystem_method_descriptor_sync(int32_t, uint8_t *); @@ -109,8 +109,8 @@ extern int32_t __wasm_import_filesystem_method_descriptor_sync(int32_t, uint8_t __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.create-directory-at"))) extern int32_t __wasm_import_filesystem_method_descriptor_create_directory_at(int32_t, uint8_t *, size_t, uint8_t *); -__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.stat"))) -extern int32_t __wasm_import_filesystem_method_descriptor_stat(int32_t, uint8_t *); +__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.stat"))) +extern void __wasm_import_filesystem_method_descriptor_stat(int32_t, uint8_t *); __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.stat-at"))) extern int32_t __wasm_import_filesystem_method_descriptor_stat_at(int32_t, int32_t, uint8_t *, size_t, uint8_t *); @@ -121,8 +121,8 @@ extern int32_t __wasm_import_filesystem_method_descriptor_set_times_at(uint8_t * __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.link-at"))) extern int32_t __wasm_import_filesystem_method_descriptor_link_at(uint8_t *, uint8_t *); -__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.open-at"))) -extern int32_t __wasm_import_filesystem_method_descriptor_open_at(uint8_t *, uint8_t *); +__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.open-at"))) +extern void __wasm_import_filesystem_method_descriptor_open_at(int32_t, int32_t, uint8_t *, size_t, int32_t, int32_t, uint8_t *); __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.readlink-at"))) extern int32_t __wasm_import_filesystem_method_descriptor_readlink_at(int32_t, uint8_t *, size_t, uint8_t *); @@ -142,11 +142,11 @@ extern int32_t __wasm_import_filesystem_method_descriptor_unlink_file_at(int32_t __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.is-same-object"))) extern int32_t __wasm_import_filesystem_method_descriptor_is_same_object(int32_t, int32_t, uint8_t *); -__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.metadata-hash"))) -extern int32_t __wasm_import_filesystem_method_descriptor_metadata_hash(int32_t, uint8_t *); +__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.metadata-hash"))) +extern void __wasm_import_filesystem_method_descriptor_metadata_hash(int32_t, uint8_t *); -__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.metadata-hash-at"))) -extern int32_t __wasm_import_filesystem_method_descriptor_metadata_hash_at(int32_t, int32_t, uint8_t *, size_t, uint8_t *); +__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.metadata-hash-at"))) +extern void __wasm_import_filesystem_method_descriptor_metadata_hash_at(int32_t, int32_t, uint8_t *, size_t, uint8_t *); // Imported Functions from `wasi:filesystem/preopens@0.3.0-rc-2026-01-06` @@ -1383,12 +1383,12 @@ monotonic_clock_duration_t monotonic_clock_get_resolution(void) { return (uint64_t) (ret); } -wasip3_subtask_status_t monotonic_clock_wait_until(monotonic_clock_mark_t when) { - return __wasm_import_monotonic_clock_wait_until((int64_t) (when)); +void monotonic_clock_wait_until(monotonic_clock_mark_t when) { + __wasm_import_monotonic_clock_wait_until((int64_t) (when)); } -wasip3_subtask_status_t monotonic_clock_wait_for(monotonic_clock_duration_t how_long) { - return __wasm_import_monotonic_clock_wait_for((int64_t) (how_long)); +void monotonic_clock_wait_for(monotonic_clock_duration_t how_long) { + __wasm_import_monotonic_clock_wait_for((int64_t) (how_long)); } void system_clock_now(system_clock_instant_t *ret) { @@ -1434,8 +1434,31 @@ wasip3_subtask_status_t filesystem_method_descriptor_sync_data(filesystem_borrow return __wasm_import_filesystem_method_descriptor_sync_data((self).__handle, (uint8_t*) result); } -wasip3_subtask_status_t filesystem_method_descriptor_get_flags(filesystem_borrow_descriptor_t self, filesystem_result_descriptor_flags_error_code_t *result) { - return __wasm_import_filesystem_method_descriptor_get_flags((self).__handle, (uint8_t*) result); +bool filesystem_method_descriptor_get_flags(filesystem_borrow_descriptor_t self, filesystem_descriptor_flags_t *ret, filesystem_error_code_t *err) { + __attribute__((__aligned__(1))) + uint8_t ret_area[2]; + uint8_t *ptr = (uint8_t *) &ret_area; + __wasm_import_filesystem_method_descriptor_get_flags((self).__handle, ptr); + filesystem_result_descriptor_flags_error_code_t result; + switch ((int32_t) *((uint8_t*) (ptr + 0))) { + case 0: { + result.is_err = false; + result.val.ok = (int32_t) *((uint8_t*) (ptr + 1)); + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) *((uint8_t*) (ptr + 1)); + break; + } + } + if (!result.is_err) { + *ret = result.val.ok; + return 1; + } else { + *err = result.val.err; + return 0; + } } wasip3_subtask_status_t filesystem_method_descriptor_get_type(filesystem_borrow_descriptor_t self, filesystem_result_descriptor_type_error_code_t *result) { @@ -1450,8 +1473,15 @@ wasip3_subtask_status_t filesystem_method_descriptor_set_times(filesystem_method return __wasm_import_filesystem_method_descriptor_set_times((uint8_t*) args, (uint8_t*) result); } -wasip3_subtask_status_t filesystem_method_descriptor_read_directory(filesystem_borrow_descriptor_t self, filesystem_tuple2_stream_directory_entry_future_result_void_error_code_t *result) { - return __wasm_import_filesystem_method_descriptor_read_directory((self).__handle, (uint8_t*) result); +void filesystem_method_descriptor_read_directory(filesystem_borrow_descriptor_t self, filesystem_tuple2_stream_directory_entry_future_result_void_error_code_t *ret) { + __attribute__((__aligned__(4))) + uint8_t ret_area[8]; + uint8_t *ptr = (uint8_t *) &ret_area; + __wasm_import_filesystem_method_descriptor_read_directory((self).__handle, ptr); + *ret = (filesystem_tuple2_stream_directory_entry_future_result_void_error_code_t) { + (filesystem_stream_directory_entry_t) ((uint32_t) *((int32_t*) (ptr + 0))), + (filesystem_future_result_void_error_code_t) ((uint32_t) *((int32_t*) (ptr + 4))), + }; } wasip3_subtask_status_t filesystem_method_descriptor_sync(filesystem_borrow_descriptor_t self, filesystem_result_void_error_code_t *result) { @@ -1462,8 +1492,84 @@ wasip3_subtask_status_t filesystem_method_descriptor_create_directory_at(filesys return __wasm_import_filesystem_method_descriptor_create_directory_at((self).__handle, (uint8_t *) (path).ptr, (path).len, (uint8_t*) result); } -wasip3_subtask_status_t filesystem_method_descriptor_stat(filesystem_borrow_descriptor_t self, filesystem_result_descriptor_stat_error_code_t *result) { - return __wasm_import_filesystem_method_descriptor_stat((self).__handle, (uint8_t*) result); +bool filesystem_method_descriptor_stat(filesystem_borrow_descriptor_t self, filesystem_descriptor_stat_t *ret, filesystem_error_code_t *err) { + __attribute__((__aligned__(8))) + uint8_t ret_area[104]; + uint8_t *ptr = (uint8_t *) &ret_area; + __wasm_import_filesystem_method_descriptor_stat((self).__handle, ptr); + filesystem_result_descriptor_stat_error_code_t result; + switch ((int32_t) *((uint8_t*) (ptr + 0))) { + case 0: { + result.is_err = false; + filesystem_option_instant_t option; + switch ((int32_t) *((uint8_t*) (ptr + 32))) { + case 0: { + option.is_some = false; + break; + } + case 1: { + option.is_some = true; + option.val = (system_clock_instant_t) { + (int64_t) *((int64_t*) (ptr + 40)), + (uint32_t) (uint32_t) (*((int32_t*) (ptr + 48))), + }; + break; + } + } + filesystem_option_instant_t option0; + switch ((int32_t) *((uint8_t*) (ptr + 56))) { + case 0: { + option0.is_some = false; + break; + } + case 1: { + option0.is_some = true; + option0.val = (system_clock_instant_t) { + (int64_t) *((int64_t*) (ptr + 64)), + (uint32_t) (uint32_t) (*((int32_t*) (ptr + 72))), + }; + break; + } + } + filesystem_option_instant_t option1; + switch ((int32_t) *((uint8_t*) (ptr + 80))) { + case 0: { + option1.is_some = false; + break; + } + case 1: { + option1.is_some = true; + option1.val = (system_clock_instant_t) { + (int64_t) *((int64_t*) (ptr + 88)), + (uint32_t) (uint32_t) (*((int32_t*) (ptr + 96))), + }; + break; + } + } + + result.val.ok = (filesystem_descriptor_stat_t) { + (filesystem_descriptor_type_t) (int32_t) *((uint8_t*) (ptr + 8)), + (filesystem_link_count_t) (uint64_t) (*((int64_t*) (ptr + 16))), + (filesystem_filesize_t) (uint64_t) (*((int64_t*) (ptr + 24))), + (filesystem_option_instant_t) option, + (filesystem_option_instant_t) option0, + (filesystem_option_instant_t) option1, + }; + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) *((uint8_t*) (ptr + 8)); + break; + } + } + if (!result.is_err) { + *ret = result.val.ok; + return 1; + } else { + *err = result.val.err; + return 0; + } } wasip3_subtask_status_t filesystem_method_descriptor_stat_at(filesystem_borrow_descriptor_t self, filesystem_path_flags_t path_flags, wasip3_string_t path, filesystem_result_descriptor_stat_error_code_t *result) { @@ -1478,8 +1584,31 @@ wasip3_subtask_status_t filesystem_method_descriptor_link_at(filesystem_method_d return __wasm_import_filesystem_method_descriptor_link_at((uint8_t*) args, (uint8_t*) result); } -wasip3_subtask_status_t filesystem_method_descriptor_open_at(filesystem_method_descriptor_open_at_args_t *args, filesystem_result_own_descriptor_error_code_t *result) { - return __wasm_import_filesystem_method_descriptor_open_at((uint8_t*) args, (uint8_t*) result); +bool filesystem_method_descriptor_open_at(filesystem_borrow_descriptor_t self, filesystem_path_flags_t path_flags, wasip3_string_t *path, filesystem_open_flags_t open_flags, filesystem_descriptor_flags_t flags, filesystem_own_descriptor_t *ret, filesystem_error_code_t *err) { + __attribute__((__aligned__(4))) + uint8_t ret_area[8]; + uint8_t *ptr = (uint8_t *) &ret_area; + __wasm_import_filesystem_method_descriptor_open_at((self).__handle, path_flags, (uint8_t *) (*path).ptr, (*path).len, open_flags, flags, ptr); + filesystem_result_own_descriptor_error_code_t result; + switch ((int32_t) *((uint8_t*) (ptr + 0))) { + case 0: { + result.is_err = false; + result.val.ok = (filesystem_own_descriptor_t) { *((int32_t*) (ptr + 4)) }; + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) *((uint8_t*) (ptr + 4)); + break; + } + } + if (!result.is_err) { + *ret = result.val.ok; + return 1; + } else { + *err = result.val.err; + return 0; + } } wasip3_subtask_status_t filesystem_method_descriptor_readlink_at(filesystem_borrow_descriptor_t self, wasip3_string_t path, filesystem_result_string_error_code_t *result) { @@ -1506,12 +1635,64 @@ wasip3_subtask_status_t filesystem_method_descriptor_is_same_object(filesystem_b return __wasm_import_filesystem_method_descriptor_is_same_object((self).__handle, (other).__handle, (uint8_t*) result); } -wasip3_subtask_status_t filesystem_method_descriptor_metadata_hash(filesystem_borrow_descriptor_t self, filesystem_result_metadata_hash_value_error_code_t *result) { - return __wasm_import_filesystem_method_descriptor_metadata_hash((self).__handle, (uint8_t*) result); +bool filesystem_method_descriptor_metadata_hash(filesystem_borrow_descriptor_t self, filesystem_metadata_hash_value_t *ret, filesystem_error_code_t *err) { + __attribute__((__aligned__(8))) + uint8_t ret_area[24]; + uint8_t *ptr = (uint8_t *) &ret_area; + __wasm_import_filesystem_method_descriptor_metadata_hash((self).__handle, ptr); + filesystem_result_metadata_hash_value_error_code_t result; + switch ((int32_t) *((uint8_t*) (ptr + 0))) { + case 0: { + result.is_err = false; + result.val.ok = (filesystem_metadata_hash_value_t) { + (uint64_t) (uint64_t) (*((int64_t*) (ptr + 8))), + (uint64_t) (uint64_t) (*((int64_t*) (ptr + 16))), + }; + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) *((uint8_t*) (ptr + 8)); + break; + } + } + if (!result.is_err) { + *ret = result.val.ok; + return 1; + } else { + *err = result.val.err; + return 0; + } } -wasip3_subtask_status_t filesystem_method_descriptor_metadata_hash_at(filesystem_borrow_descriptor_t self, filesystem_path_flags_t path_flags, wasip3_string_t path, filesystem_result_metadata_hash_value_error_code_t *result) { - return __wasm_import_filesystem_method_descriptor_metadata_hash_at((self).__handle, path_flags, (uint8_t *) (path).ptr, (path).len, (uint8_t*) result); +bool filesystem_method_descriptor_metadata_hash_at(filesystem_borrow_descriptor_t self, filesystem_path_flags_t path_flags, wasip3_string_t *path, filesystem_metadata_hash_value_t *ret, filesystem_error_code_t *err) { + __attribute__((__aligned__(8))) + uint8_t ret_area[24]; + uint8_t *ptr = (uint8_t *) &ret_area; + __wasm_import_filesystem_method_descriptor_metadata_hash_at((self).__handle, path_flags, (uint8_t *) (*path).ptr, (*path).len, ptr); + filesystem_result_metadata_hash_value_error_code_t result; + switch ((int32_t) *((uint8_t*) (ptr + 0))) { + case 0: { + result.is_err = false; + result.val.ok = (filesystem_metadata_hash_value_t) { + (uint64_t) (uint64_t) (*((int64_t*) (ptr + 8))), + (uint64_t) (uint64_t) (*((int64_t*) (ptr + 16))), + }; + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) *((uint8_t*) (ptr + 8)); + break; + } + } + if (!result.is_err) { + *ret = result.val.ok; + return 1; + } else { + *err = result.val.err; + return 0; + } } void filesystem_preopens_get_directories(filesystem_preopens_list_tuple2_own_descriptor_string_t *ret) { diff --git a/libc-bottom-half/sources/wasip3_block_on.c b/libc-bottom-half/sources/wasip3_block_on.c index a8f16420e..bcb025d9b 100644 --- a/libc-bottom-half/sources/wasip3_block_on.c +++ b/libc-bottom-half/sources/wasip3_block_on.c @@ -5,49 +5,52 @@ #include #include -void wasip3_subtask_block_on(wasip3_subtask_status_t status) { - // we don't encounter cancelled state because this function won't cancel - if (WASIP3_SUBTASK_STATE(status) == WASIP3_SUBTASK_STARTING || - WASIP3_SUBTASK_STATE(status) == WASIP3_SUBTASK_STARTED) { - wasip3_subtask_t handle = WASIP3_SUBTASK_HANDLE(status); - wasip3_waitable_set_t set = wasip3_waitable_set_new(); - wasip3_waitable_join(handle, set); +static void waitable_block_on(uint32_t waitable, wasip3_event_t *event) { + wasip3_waitable_set_t set = wasip3_waitable_set_new(); + wasip3_waitable_join(waitable, set); + wasip3_waitable_set_wait(set, event); + // remove from set + wasip3_waitable_join(waitable, 0); + wasip3_waitable_set_drop(set); +} + +void wasip3_subtask_block_on_and_drop(wasip3_subtask_t handle) { + wasip3_event_t event; + waitable_block_on(handle, &event); + assert(event.event == WASIP3_EVENT_SUBTASK); + assert(event.waitable == handle); + assert(event.code == WASIP3_SUBTASK_RETURNED); + wasip3_subtask_drop(handle); +} + +void wasip3_future_block_on(wasip3_waitable_status_t status, uint32_t future) { + if (status == WASIP3_WAITABLE_STATUS_BLOCKED) { wasip3_event_t event; - event.code = WASIP3_SUBTASK_STATE(status); - while (event.code != WASIP3_SUBTASK_RETURNED) { - wasip3_waitable_set_wait(set, &event); - assert(event.event == WASIP3_EVENT_SUBTASK); - assert(event.waitable == handle); - } - wasip3_subtask_drop(event.waitable); - wasip3_waitable_set_drop(set); + waitable_block_on(future, &event); + assert(event.event == WASIP3_EVENT_FUTURE_READ || + event.event == WASIP3_EVENT_FUTURE_WRITE); + assert(event.waitable == future); + status = event.code; } + assert(WASIP3_WAITABLE_STATE(status) == WASIP3_WAITABLE_COMPLETED || + WASIP3_WAITABLE_STATE(status) == WASIP3_WAITABLE_DROPPED); + assert(WASIP3_WAITABLE_COUNT(status) == 0); } -size_t wasip3_waitable_block_on(wasip3_waitable_status_t status, - waitable_t stream) { +size_t wasip3_stream_block_on(wasip3_waitable_status_t status, uint32_t stream, + bool *closed) { if (status == WASIP3_WAITABLE_STATUS_BLOCKED) { - wasip3_waitable_set_t set = wasip3_waitable_set_new(); - wasip3_waitable_join(stream, set); wasip3_event_t event; - wasip3_waitable_set_wait(set, &event); - assert(event.event == WASIP3_EVENT_STREAM_WRITE || - event.event == WASIP3_EVENT_STREAM_READ || - event.event == WASIP3_EVENT_FUTURE_READ); + waitable_block_on(stream, &event); + assert(event.event == WASIP3_EVENT_STREAM_READ || + event.event == WASIP3_EVENT_STREAM_WRITE); assert(event.waitable == stream); - // remove from set - wasip3_waitable_join(stream, 0); - wasip3_waitable_set_drop(set); - size_t amount = event.event == WASIP3_EVENT_FUTURE_READ ? 1 : event.code; - return amount; - } else if (WASIP3_WAITABLE_STATE(status) == WASIP3_WAITABLE_COMPLETED || - WASIP3_WAITABLE_STATE(status) == WASIP3_WAITABLE_DROPPED) { - size_t amount = WASIP3_WAITABLE_COUNT(status); - return amount; - } else { - // other status (e.g. cancelled) shouldn't occur - abort(); + status = event.code; } + assert(WASIP3_WAITABLE_STATE(status) == WASIP3_WAITABLE_COMPLETED || + WASIP3_WAITABLE_STATE(status) == WASIP3_WAITABLE_DROPPED); + *closed = WASIP3_WAITABLE_STATE(status) == WASIP3_WAITABLE_DROPPED; + return WASIP3_WAITABLE_COUNT(status); } #endif diff --git a/libc-bottom-half/sources/wasip3_file.c b/libc-bottom-half/sources/wasip3_file.c deleted file mode 100644 index 9992b0437..000000000 --- a/libc-bottom-half/sources/wasip3_file.c +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include - -#ifdef __wasip3__ - -int __wasilibc_add_file(filesystem_own_descriptor_t file_handle, int oflag) { - (void)file_handle; - (void)oflag; - // TODO(wasip3) - errno = EOPNOTSUPP; - return -1; -} - -#endif // __wasip3__ diff --git a/libc-bottom-half/sources/wasip3_stdio.c b/libc-bottom-half/sources/wasip3_stdio.c index 3104f2def..af345dc4b 100644 --- a/libc-bottom-half/sources/wasip3_stdio.c +++ b/libc-bottom-half/sources/wasip3_stdio.c @@ -23,7 +23,8 @@ typedef struct { // contains stream, result storage and result subtask wasip3_subtask_t subtask; stdin_stream_u8_writer_t output; - stdin_result_void_error_code_t pending_result; + bool output_done; + stdout_result_void_error_code_t pending_result; // tristate: zero=unknown, valid handle=yes, -1=no terminal_output_own_terminal_output_t terminal_out; // stream creation function (delayed) @@ -33,6 +34,23 @@ typedef struct { bool (*terminal_func)(terminal_stdout_own_terminal_output_t *ret); } stdout3_t; +static void translate_error(wasi_cli_types_error_code_t err) { + switch (err) { + case WASI_CLI_TYPES_ERROR_CODE_IO: + errno = EIO; + break; + case WASI_CLI_TYPES_ERROR_CODE_ILLEGAL_BYTE_SEQUENCE: + errno = EILSEQ; + break; + case WASI_CLI_TYPES_ERROR_CODE_PIPE: + errno = EPIPE; + break; + default: + assert(0); + break; + } +} + static void stdin3_free(void *data) { stdin3_t *stdio = (stdin3_t *)data; if (stdio->terminal_in.__handle > 0) @@ -51,53 +69,57 @@ static void stdout3_free(void *data) { if (stdio->output) stdin_stream_u8_drop_writable(stdio->output); if (stdio->subtask) - wasip3_subtask_block_on(stdio->subtask); + wasip3_subtask_block_on_and_drop(stdio->subtask); free(stdio); } +static int stdout3_write_eof(void *data) { + stdout3_t *stdio = (stdout3_t *)data; + if (stdio->subtask != 0) { + wasip3_subtask_block_on_and_drop(stdio->subtask); + stdio->subtask = 0; + } + if (stdio->pending_result.is_err) { + translate_error(stdio->pending_result.val.err); + return -1; + } + return 0; +} + static int stdout3_write(void *data, wasi_write_t *out) { stdout3_t *stdio = (stdout3_t *)data; if (!stdio->output) { assert(!stdio->subtask); stdin_stream_u8_t read_side = stdin_stream_u8_new(&stdio->output); - stdio->subtask = (*stdio->stream_func)( - read_side, (stdout_result_void_error_code_t *)&stdio->pending_result); - // subtask will be checked by write for error before writing + wasip3_subtask_status_t status = (*stdio->stream_func)( + read_side, &stdio->pending_result); + if (WASIP3_SUBTASK_STATE(status) != WASIP3_SUBTASK_RETURNED) + stdio->subtask = WASIP3_SUBTASK_HANDLE(status); } out->offset = NULL; out->blocking = true; out->timeout = 0; out->output = stdio->output; + out->done = &stdio->output_done; + out->eof = stdout3_write_eof; + out->eof_data = data; return 0; } static int stdin3_read_eof(void *data) { stdin3_t *stdio = (stdin3_t *)data; - stdin_result_void_error_code_t result; - wasip3_waitable_status_t status = - stdin_future_result_void_error_code_read(stdio->input.f1, &result); - size_t amt = wasip3_waitable_block_on(status, stdio->input.f1); - assert(amt == 1); - (void) amt; - stdin_future_result_void_error_code_drop_readable(stdio->input.f1); - stdio->input.f1 = 0; - if (result.is_err) { - switch (result.val.err) { - case WASI_CLI_TYPES_ERROR_CODE_IO: - errno = EIO; - break; - case WASI_CLI_TYPES_ERROR_CODE_ILLEGAL_BYTE_SEQUENCE: - errno = EILSEQ; - break; - case WASI_CLI_TYPES_ERROR_CODE_PIPE: - errno = EPIPE; - break; - default: - assert(0); - break; + if (stdio->input.f1 != 0) { + stdin_result_void_error_code_t result; + wasip3_future_block_on( + stdin_future_result_void_error_code_read(stdio->input.f1, &result), + stdio->input.f1); + stdin_future_result_void_error_code_drop_readable(stdio->input.f1); + stdio->input.f1 = 0; + if (result.is_err) { + translate_error(result.val.err); + return -1; } - return -1; } return 0; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f22db1c38..92e8ce7fa 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -263,42 +263,41 @@ add_wasilibc_test(append.c FS FAILP3) add_wasilibc_test(argv_two_args.c ARGV foo bar) add_wasilibc_test(clock_nanosleep.c) add_wasilibc_test(chdir.c FS FAILP3) -add_wasilibc_test(close.c FS FAILP3) +add_wasilibc_test(close.c FS) add_wasilibc_test(external_env.c ENV VAR1=foo VAR2=bar) add_wasilibc_test(fadvise.c FS FAILP3) add_wasilibc_test(fallocate.c FS FAILP3) add_wasilibc_test(fcntl.c FS FAILP3) add_wasilibc_test(fdatasync.c FS FAILP3) add_wasilibc_test(fdopen.c FS FAILP3) -add_wasilibc_test(feof.c FS FAILP3) +add_wasilibc_test(feof.c FS) add_wasilibc_test(file_permissions.c FS FAILP3) -add_wasilibc_test(file_nonblocking.c FS FAILP3) +add_wasilibc_test(file_nonblocking.c FS) add_wasilibc_test(fseek.c FS FAILP3) add_wasilibc_test(fstat.c FS FAILP3) add_wasilibc_test(fsync.c FS FAILP3) add_wasilibc_test(ftruncate.c FS FAILP3) add_wasilibc_test(fts.c FS FAILP3) -add_wasilibc_test(fwscanf.c FS FAILP3) +add_wasilibc_test(fwscanf.c FS) add_wasilibc_test(getentropy.c) add_wasilibc_test(hello.c PASS_REGULAR_EXPRESSION "Hello, World!") add_wasilibc_test(ioctl.c FS FAILP3) add_wasilibc_test(isatty.c FS FAILP3) add_wasilibc_test(link.c FS FAILP3) -add_wasilibc_test(lseek.c FS FAILP3) +add_wasilibc_test(lseek.c FS) add_wasilibc_test(memchr.c LDFLAGS -Wl,--stack-first -Wl,--initial-memory=327680) add_wasilibc_test(memcmp.c LDFLAGS -Wl,--stack-first -Wl,--initial-memory=327680) -add_wasilibc_test(opendir.c FS FAILP3 ARGV /) +add_wasilibc_test(opendir.c FS ARGV /) add_wasilibc_test(open_relative_path.c FS FAILP3 ARGV /) add_wasilibc_test(poll.c FS FAILP3) add_wasilibc_test(preadvwritev.c FS FAILP3) add_wasilibc_test(preadwrite.c FS FAILP3) add_wasilibc_test(readlink.c FS FAILP3) -add_wasilibc_test(readv.c FS FAILP3) +add_wasilibc_test(readv.c FS) add_wasilibc_test(rename.c FS FAILP3) add_wasilibc_test(rmdir.c FS FAILP3) add_wasilibc_test(scandir.c FS FAILP3) add_wasilibc_test(stat.c FS FAILP3) -add_wasilibc_test(stdio.c FS) add_wasilibc_test(strchrnul.c LDFLAGS -Wl,--stack-first -Wl,--initial-memory=327680) add_wasilibc_test(strlen.c LDFLAGS -Wl,--stack-first -Wl,--initial-memory=327680) add_wasilibc_test(strptime.c) @@ -315,6 +314,30 @@ add_wasilibc_test(nanosleep.c) add_wasilibc_test(sleep.c) add_wasilibc_test(write.c FS FAILP3) add_wasilibc_test(wasi-defines.c) +add_wasilibc_test(close-stdio.c) + +# Testing stdio is a bit special so handle it specially here. A single +# executable is built which is then piped into itself in a few situations to +# test stdio. +add_test_executable(stdio.wasm src/stdio.c) +add_test( + NAME stdio-close-output + COMMAND + ${CMAKE_COMMAND} + -DENGINE=${ENGINE} + -DWASM=$ + -DARG=close-output + -P ${CMAKE_CURRENT_SOURCE_DIR}/stdio-test.cmake +) +add_test( + NAME stdio-close-input + COMMAND + ${CMAKE_COMMAND} + -DENGINE=${ENGINE} + -DWASM=$ + -DARG=close-input + -P ${CMAKE_CURRENT_SOURCE_DIR}/stdio-test.cmake +) if (TARGET_TRIPLE MATCHES "-threads") add_wasilibc_test(busywait.c) diff --git a/test/src/close-stdio.c b/test/src/close-stdio.c new file mode 100644 index 000000000..6902e501c --- /dev/null +++ b/test/src/close-stdio.c @@ -0,0 +1,25 @@ +#include "test.h" +#include +#include +#include +#include +#include +#include +#include + +#define TEST(c) \ + do { \ + errno = 0; \ + if (!(c)) \ + t_error("%s failed (errno = %d)\n", #c, errno); \ + } while (0) + +int main() { + // Test that closing stdin/stdout/stderr is not an error + + TEST(close(0) == 0); + TEST(close(1) == 0); + TEST(close(2) == 0); + + return t_status; +} diff --git a/test/src/stdio.c b/test/src/stdio.c index f769f60e8..9320d8019 100644 --- a/test/src/stdio.c +++ b/test/src/stdio.c @@ -6,6 +6,7 @@ #include #include #include +#include #define TEST(c) \ do { \ @@ -14,12 +15,62 @@ t_error("%s failed (errno = %d)\n", #c, errno); \ } while (0) -int main(void) { - // Test that closing stdin/stdout/stderr is not an error +static int close_input_writer(void) { + TEST(write(1, "hello world\n", 12) == 12); + TEST(close(1) == 0); + return t_status; +} + +static int close_input_reader(void) { + char buf[13]; + ssize_t n = read(0, buf, 12); + TEST(n == 12); + buf[12] = '\0'; + TEST(strcmp(buf, "hello world\n") == 0); + // further reads should fail as there's nothing left + TEST(read(0, buf, 12) == 0); TEST(close(0) == 0); + return t_status; +} + +static int close_output_writer(void) { + while (write(1, "hello world\n", 12) == 12) { + // ... write again on the next iteration ... + } TEST(close(1) == 0); - TEST(close(2) == 0); + return t_status; +} + +static int close_output_reader(void) { + // Read one message successfully... + char buf[13]; + ssize_t n = read(0, buf, 12); + TEST(n == 12); + buf[12] = '\0'; + TEST(strcmp(buf, "hello world\n") == 0); + // ... but then close out stdin to let the other end know we're hanging up. + TEST(close(0) == 0); return t_status; } + +int main(int argc, char **argv) { + TEST(argc == 3); + bool writer = argv[2][0] == 'a'; + + if (strcmp(argv[1], "close-input") == 0) { + if (writer) + return close_input_writer(); + return close_input_reader(); + } + + if (strcmp(argv[1], "close-output") == 0) { + if (writer) + return close_output_writer(); + return close_output_reader(); + } + + printf("unknown test: %s\n", argv[1]); + return 1; +} diff --git a/test/stdio-test.cmake b/test/stdio-test.cmake new file mode 100644 index 000000000..48f86e282 --- /dev/null +++ b/test/stdio-test.cmake @@ -0,0 +1,9 @@ +# TODO + +execute_process( + COMMAND ${ENGINE} -Sp3 -Wcomponent-model-async ${WASM} ${ARG} a + COMMAND ${ENGINE} -Sp3 -Wcomponent-model-async ${WASM} ${ARG} b + TIMEOUT 5 + COMMAND_ERROR_IS_FATAL ANY + COMMAND_ECHO STDOUT +) From 2d60a7023ae56b4cbbf3415f1d28609f49914394 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Feb 2026 17:33:31 -0800 Subject: [PATCH 04/17] wasip3: Implement functions related to removing files/dirs Gets a few more tests passing --- cmake/bindings.cmake | 3 + .../cloudlibc/src/libc/unistd/faccessat.c | 10 +- .../headers/public/wasi/__generated_wasip3.h | 6 +- libc-bottom-half/sources/__wasilibc_rmdirat.c | 10 +- .../sources/__wasilibc_unlinkat.c | 10 +- libc-bottom-half/sources/wasip3.c | 142 ++++++++++++++++-- test/CMakeLists.txt | 16 +- 7 files changed, 150 insertions(+), 47 deletions(-) diff --git a/cmake/bindings.cmake b/cmake/bindings.cmake index 529d41f28..e7919a289 100644 --- a/cmake/bindings.cmake +++ b/cmake/bindings.cmake @@ -113,9 +113,12 @@ add_custom_target( "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.metadata-hash" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.metadata-hash-at" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.stat" + "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.stat-at" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.get-flags" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.open-at" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.read-directory" + "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.remove-directory-at" + "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.unlink-file-at" "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-until" "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-for" ${CMAKE_SOURCE_DIR}/wasi/p3/wit diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/faccessat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/faccessat.c index 38a48945f..052881173 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/faccessat.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/faccessat.c @@ -55,7 +55,7 @@ int __wasilibc_nocwd_faccessat(int fd, const char *path, int amode, int flag) { return -1; } } -#elif defined(__wasip2__) +#elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; @@ -63,7 +63,7 @@ int __wasilibc_nocwd_faccessat(int fd, const char *path, int amode, int flag) { return -1; // Convert the string into a WASI string - wasip2_string_t wasi_path; + wasi_string_t wasi_path; if (wasi_string_from_c(path, &wasi_path) < 0) return -1; @@ -109,12 +109,6 @@ int __wasilibc_nocwd_faccessat(int fd, const char *path, int amode, int flag) { return -1; } } -#elif defined(__wasip3__) - (void) fd; - (void) path; - // TODO(wasip3) - errno = ENOTSUP; - return -1; #else # error "Unsupported WASI version" #endif diff --git a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h index cb1aa21df..ac7fab491 100644 --- a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h +++ b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h @@ -1144,7 +1144,7 @@ extern bool filesystem_method_descriptor_stat(filesystem_borrow_descriptor_t sel // discussion of alternatives. // // Note: This was called `path_filestat_get` in earlier versions of WASI. -extern wasip3_subtask_status_t filesystem_method_descriptor_stat_at(filesystem_borrow_descriptor_t self, filesystem_path_flags_t path_flags, wasip3_string_t path, filesystem_result_descriptor_stat_error_code_t *result); +extern bool filesystem_method_descriptor_stat_at(filesystem_borrow_descriptor_t self, filesystem_path_flags_t path_flags, wasip3_string_t *path, filesystem_descriptor_stat_t *ret, filesystem_error_code_t *err); // Adjust the timestamps of a file or directory. // // Note: This is similar to `utimensat` in POSIX. @@ -1185,7 +1185,7 @@ extern wasip3_subtask_status_t filesystem_method_descriptor_readlink_at(filesyst // Return `error-code::not-empty` if the directory is not empty. // // Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. -extern wasip3_subtask_status_t filesystem_method_descriptor_remove_directory_at(filesystem_borrow_descriptor_t self, wasip3_string_t path, filesystem_result_void_error_code_t *result); +extern bool filesystem_method_descriptor_remove_directory_at(filesystem_borrow_descriptor_t self, wasip3_string_t *path, filesystem_error_code_t *err); // Rename a filesystem object. // // Note: This is similar to `renameat` in POSIX. @@ -1201,7 +1201,7 @@ extern wasip3_subtask_status_t filesystem_method_descriptor_symlink_at(filesyste // // Return `error-code::is-directory` if the path refers to a directory. // Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. -extern wasip3_subtask_status_t filesystem_method_descriptor_unlink_file_at(filesystem_borrow_descriptor_t self, wasip3_string_t path, filesystem_result_void_error_code_t *result); +extern bool filesystem_method_descriptor_unlink_file_at(filesystem_borrow_descriptor_t self, wasip3_string_t *path, filesystem_error_code_t *err); // Test whether two descriptors refer to the same filesystem object. // // In POSIX, this corresponds to testing whether the two descriptors have the diff --git a/libc-bottom-half/sources/__wasilibc_rmdirat.c b/libc-bottom-half/sources/__wasilibc_rmdirat.c index 50bfef338..bb0e965d7 100644 --- a/libc-bottom-half/sources/__wasilibc_rmdirat.c +++ b/libc-bottom-half/sources/__wasilibc_rmdirat.c @@ -15,14 +15,14 @@ int __wasilibc_nocwd___wasilibc_rmdirat(int fd, const char *path) { errno = error; return -1; } -#elif defined(__wasip2__) +#elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; if (fd_to_file_handle(fd, &file_handle) < 0) return -1; // Create a WASI string for the path - wasip2_string_t wasi_path; + wasi_string_t wasi_path; if (wasi_string_from_c(path, &wasi_path) < 0) return -1; filesystem_error_code_t error_code; @@ -34,12 +34,6 @@ int __wasilibc_nocwd___wasilibc_rmdirat(int fd, const char *path) { translate_error(error_code); return -1; } -#elif defined(__wasip3__) - (void)fd; - (void)path; - // TODO(wasip3) - errno = ENOTSUP; - return -1; #else #error "Unsupported WASI version" #endif diff --git a/libc-bottom-half/sources/__wasilibc_unlinkat.c b/libc-bottom-half/sources/__wasilibc_unlinkat.c index 7bd2cde8e..3b5488ddc 100644 --- a/libc-bottom-half/sources/__wasilibc_unlinkat.c +++ b/libc-bottom-half/sources/__wasilibc_unlinkat.c @@ -16,7 +16,7 @@ int __wasilibc_nocwd___wasilibc_unlinkat(int fd, const char *path) { return -1; } return 0; -#elif defined(__wasip2__) +#elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle descriptor_table_entry_t *entry = descriptor_table_get_ref(fd); if (!entry) @@ -33,7 +33,7 @@ int __wasilibc_nocwd___wasilibc_unlinkat(int fd, const char *path) { return -1; // Create a Wasm string from the path - wasip2_string_t wasi_path; + wasi_string_t wasi_path; if (wasi_string_from_c(path, &wasi_path) < 0) return -1; @@ -47,12 +47,6 @@ int __wasilibc_nocwd___wasilibc_unlinkat(int fd, const char *path) { } return 0; -#elif defined(__wasip3__) - (void)fd; - (void)path; - // TODO(wasip3) - errno = ENOTSUP; - return -1; #else #error "Unknown WASI version" #endif diff --git a/libc-bottom-half/sources/wasip3.c b/libc-bottom-half/sources/wasip3.c index 8e25f23b5..14e11467d 100644 --- a/libc-bottom-half/sources/wasip3.c +++ b/libc-bottom-half/sources/wasip3.c @@ -112,8 +112,8 @@ extern int32_t __wasm_import_filesystem_method_descriptor_create_directory_at(in __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.stat"))) extern void __wasm_import_filesystem_method_descriptor_stat(int32_t, uint8_t *); -__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.stat-at"))) -extern int32_t __wasm_import_filesystem_method_descriptor_stat_at(int32_t, int32_t, uint8_t *, size_t, uint8_t *); +__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.stat-at"))) +extern void __wasm_import_filesystem_method_descriptor_stat_at(int32_t, int32_t, uint8_t *, size_t, uint8_t *); __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.set-times-at"))) extern int32_t __wasm_import_filesystem_method_descriptor_set_times_at(uint8_t *, uint8_t *); @@ -127,8 +127,8 @@ extern void __wasm_import_filesystem_method_descriptor_open_at(int32_t, int32_t, __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.readlink-at"))) extern int32_t __wasm_import_filesystem_method_descriptor_readlink_at(int32_t, uint8_t *, size_t, uint8_t *); -__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.remove-directory-at"))) -extern int32_t __wasm_import_filesystem_method_descriptor_remove_directory_at(int32_t, uint8_t *, size_t, uint8_t *); +__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.remove-directory-at"))) +extern void __wasm_import_filesystem_method_descriptor_remove_directory_at(int32_t, uint8_t *, size_t, uint8_t *); __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.rename-at"))) extern int32_t __wasm_import_filesystem_method_descriptor_rename_at(uint8_t *, uint8_t *); @@ -136,8 +136,8 @@ extern int32_t __wasm_import_filesystem_method_descriptor_rename_at(uint8_t *, u __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.symlink-at"))) extern int32_t __wasm_import_filesystem_method_descriptor_symlink_at(uint8_t *, uint8_t *); -__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.unlink-file-at"))) -extern int32_t __wasm_import_filesystem_method_descriptor_unlink_file_at(int32_t, uint8_t *, size_t, uint8_t *); +__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.unlink-file-at"))) +extern void __wasm_import_filesystem_method_descriptor_unlink_file_at(int32_t, uint8_t *, size_t, uint8_t *); __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.is-same-object"))) extern int32_t __wasm_import_filesystem_method_descriptor_is_same_object(int32_t, int32_t, uint8_t *); @@ -1572,8 +1572,84 @@ bool filesystem_method_descriptor_stat(filesystem_borrow_descriptor_t self, file } } -wasip3_subtask_status_t filesystem_method_descriptor_stat_at(filesystem_borrow_descriptor_t self, filesystem_path_flags_t path_flags, wasip3_string_t path, filesystem_result_descriptor_stat_error_code_t *result) { - return __wasm_import_filesystem_method_descriptor_stat_at((self).__handle, path_flags, (uint8_t *) (path).ptr, (path).len, (uint8_t*) result); +bool filesystem_method_descriptor_stat_at(filesystem_borrow_descriptor_t self, filesystem_path_flags_t path_flags, wasip3_string_t *path, filesystem_descriptor_stat_t *ret, filesystem_error_code_t *err) { + __attribute__((__aligned__(8))) + uint8_t ret_area[104]; + uint8_t *ptr = (uint8_t *) &ret_area; + __wasm_import_filesystem_method_descriptor_stat_at((self).__handle, path_flags, (uint8_t *) (*path).ptr, (*path).len, ptr); + filesystem_result_descriptor_stat_error_code_t result; + switch ((int32_t) *((uint8_t*) (ptr + 0))) { + case 0: { + result.is_err = false; + filesystem_option_instant_t option; + switch ((int32_t) *((uint8_t*) (ptr + 32))) { + case 0: { + option.is_some = false; + break; + } + case 1: { + option.is_some = true; + option.val = (system_clock_instant_t) { + (int64_t) *((int64_t*) (ptr + 40)), + (uint32_t) (uint32_t) (*((int32_t*) (ptr + 48))), + }; + break; + } + } + filesystem_option_instant_t option0; + switch ((int32_t) *((uint8_t*) (ptr + 56))) { + case 0: { + option0.is_some = false; + break; + } + case 1: { + option0.is_some = true; + option0.val = (system_clock_instant_t) { + (int64_t) *((int64_t*) (ptr + 64)), + (uint32_t) (uint32_t) (*((int32_t*) (ptr + 72))), + }; + break; + } + } + filesystem_option_instant_t option1; + switch ((int32_t) *((uint8_t*) (ptr + 80))) { + case 0: { + option1.is_some = false; + break; + } + case 1: { + option1.is_some = true; + option1.val = (system_clock_instant_t) { + (int64_t) *((int64_t*) (ptr + 88)), + (uint32_t) (uint32_t) (*((int32_t*) (ptr + 96))), + }; + break; + } + } + + result.val.ok = (filesystem_descriptor_stat_t) { + (filesystem_descriptor_type_t) (int32_t) *((uint8_t*) (ptr + 8)), + (filesystem_link_count_t) (uint64_t) (*((int64_t*) (ptr + 16))), + (filesystem_filesize_t) (uint64_t) (*((int64_t*) (ptr + 24))), + (filesystem_option_instant_t) option, + (filesystem_option_instant_t) option0, + (filesystem_option_instant_t) option1, + }; + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) *((uint8_t*) (ptr + 8)); + break; + } + } + if (!result.is_err) { + *ret = result.val.ok; + return 1; + } else { + *err = result.val.err; + return 0; + } } wasip3_subtask_status_t filesystem_method_descriptor_set_times_at(filesystem_method_descriptor_set_times_at_args_t *args, filesystem_result_void_error_code_t *result) { @@ -1615,8 +1691,29 @@ wasip3_subtask_status_t filesystem_method_descriptor_readlink_at(filesystem_borr return __wasm_import_filesystem_method_descriptor_readlink_at((self).__handle, (uint8_t *) (path).ptr, (path).len, (uint8_t*) result); } -wasip3_subtask_status_t filesystem_method_descriptor_remove_directory_at(filesystem_borrow_descriptor_t self, wasip3_string_t path, filesystem_result_void_error_code_t *result) { - return __wasm_import_filesystem_method_descriptor_remove_directory_at((self).__handle, (uint8_t *) (path).ptr, (path).len, (uint8_t*) result); +bool filesystem_method_descriptor_remove_directory_at(filesystem_borrow_descriptor_t self, wasip3_string_t *path, filesystem_error_code_t *err) { + __attribute__((__aligned__(1))) + uint8_t ret_area[2]; + uint8_t *ptr = (uint8_t *) &ret_area; + __wasm_import_filesystem_method_descriptor_remove_directory_at((self).__handle, (uint8_t *) (*path).ptr, (*path).len, ptr); + filesystem_result_void_error_code_t result; + switch ((int32_t) *((uint8_t*) (ptr + 0))) { + case 0: { + result.is_err = false; + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) *((uint8_t*) (ptr + 1)); + break; + } + } + if (!result.is_err) { + return 1; + } else { + *err = result.val.err; + return 0; + } } wasip3_subtask_status_t filesystem_method_descriptor_rename_at(filesystem_method_descriptor_rename_at_args_t *args, filesystem_result_void_error_code_t *result) { @@ -1627,8 +1724,29 @@ wasip3_subtask_status_t filesystem_method_descriptor_symlink_at(filesystem_metho return __wasm_import_filesystem_method_descriptor_symlink_at((uint8_t*) args, (uint8_t*) result); } -wasip3_subtask_status_t filesystem_method_descriptor_unlink_file_at(filesystem_borrow_descriptor_t self, wasip3_string_t path, filesystem_result_void_error_code_t *result) { - return __wasm_import_filesystem_method_descriptor_unlink_file_at((self).__handle, (uint8_t *) (path).ptr, (path).len, (uint8_t*) result); +bool filesystem_method_descriptor_unlink_file_at(filesystem_borrow_descriptor_t self, wasip3_string_t *path, filesystem_error_code_t *err) { + __attribute__((__aligned__(1))) + uint8_t ret_area[2]; + uint8_t *ptr = (uint8_t *) &ret_area; + __wasm_import_filesystem_method_descriptor_unlink_file_at((self).__handle, (uint8_t *) (*path).ptr, (*path).len, ptr); + filesystem_result_void_error_code_t result; + switch ((int32_t) *((uint8_t*) (ptr + 0))) { + case 0: { + result.is_err = false; + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) *((uint8_t*) (ptr + 1)); + break; + } + } + if (!result.is_err) { + return 1; + } else { + *err = result.val.err; + return 0; + } } wasip3_subtask_status_t filesystem_method_descriptor_is_same_object(filesystem_borrow_descriptor_t self, filesystem_borrow_descriptor_t other, bool *result) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 92e8ce7fa..bf79303d9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -257,7 +257,7 @@ endfunction() # TODO: this test fails with `-Sthreads` in Wasmtime since that uses a different # implementation of WASI which causes this test to fail. if (NOT TARGET_TRIPLE MATCHES "-threads") - add_wasilibc_test(access.c FS FAILP3) + add_wasilibc_test(access.c FS) endif() add_wasilibc_test(append.c FS FAILP3) add_wasilibc_test(argv_two_args.c ARGV foo bar) @@ -266,14 +266,14 @@ add_wasilibc_test(chdir.c FS FAILP3) add_wasilibc_test(close.c FS) add_wasilibc_test(external_env.c ENV VAR1=foo VAR2=bar) add_wasilibc_test(fadvise.c FS FAILP3) -add_wasilibc_test(fallocate.c FS FAILP3) -add_wasilibc_test(fcntl.c FS FAILP3) +add_wasilibc_test(fallocate.c FS) +add_wasilibc_test(fcntl.c FS) add_wasilibc_test(fdatasync.c FS FAILP3) -add_wasilibc_test(fdopen.c FS FAILP3) +add_wasilibc_test(fdopen.c FS) add_wasilibc_test(feof.c FS) -add_wasilibc_test(file_permissions.c FS FAILP3) +add_wasilibc_test(file_permissions.c FS) add_wasilibc_test(file_nonblocking.c FS) -add_wasilibc_test(fseek.c FS FAILP3) +add_wasilibc_test(fseek.c FS) add_wasilibc_test(fstat.c FS FAILP3) add_wasilibc_test(fsync.c FS FAILP3) add_wasilibc_test(ftruncate.c FS FAILP3) @@ -281,8 +281,8 @@ add_wasilibc_test(fts.c FS FAILP3) add_wasilibc_test(fwscanf.c FS) add_wasilibc_test(getentropy.c) add_wasilibc_test(hello.c PASS_REGULAR_EXPRESSION "Hello, World!") -add_wasilibc_test(ioctl.c FS FAILP3) -add_wasilibc_test(isatty.c FS FAILP3) +add_wasilibc_test(ioctl.c FS) +add_wasilibc_test(isatty.c FS) add_wasilibc_test(link.c FS FAILP3) add_wasilibc_test(lseek.c FS) add_wasilibc_test(memchr.c LDFLAGS -Wl,--stack-first -Wl,--initial-memory=327680) From 710ee1db2e96d50f45cb20dd5d7870e7c8e0b9bb Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Feb 2026 17:35:22 -0800 Subject: [PATCH 05/17] wasip3: Fix `append.c` test Handle `O_APPEND` by calling `append-via-stream` --- libc-bottom-half/sources/file.c | 14 ++++++++++---- test/CMakeLists.txt | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/libc-bottom-half/sources/file.c b/libc-bottom-half/sources/file.c index 78cdcef3a..0b787dacd 100644 --- a/libc-bottom-half/sources/file.c +++ b/libc-bottom-half/sources/file.c @@ -178,10 +178,16 @@ static int file_get_write_stream(void *data, wasi_write_t *write) { if (file->write == 0) { assert(file->write_subtask == 0); filesystem_stream_u8_t write_read = filesystem_stream_u8_new(&file->write); - wasip3_subtask_status_t status = - filesystem_method_descriptor_write_via_stream( - filesystem_borrow_descriptor(file->file_handle), write_read, - file->offset, &file->write_pending_result); + wasip3_subtask_status_t status; + if (file->oflag & O_APPEND) { + status = filesystem_method_descriptor_append_via_stream( + filesystem_borrow_descriptor(file->file_handle), write_read, + &file->write_pending_result); + } else { + status = filesystem_method_descriptor_write_via_stream( + filesystem_borrow_descriptor(file->file_handle), write_read, + file->offset, &file->write_pending_result); + } if (WASIP3_SUBTASK_STATE(status) != WASIP3_SUBTASK_RETURNED) file->write_subtask = WASIP3_SUBTASK_HANDLE(status); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bf79303d9..e81a7fe2e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -259,7 +259,7 @@ endfunction() if (NOT TARGET_TRIPLE MATCHES "-threads") add_wasilibc_test(access.c FS) endif() -add_wasilibc_test(append.c FS FAILP3) +add_wasilibc_test(append.c FS) add_wasilibc_test(argv_two_args.c ARGV foo bar) add_wasilibc_test(clock_nanosleep.c) add_wasilibc_test(chdir.c FS FAILP3) From 5d1206d2d233f60dbf6837d18ae6099d888dce85 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Feb 2026 17:38:23 -0800 Subject: [PATCH 06/17] wasip3: Ungate some `stat`-related tests Use the same WASIp2 implementation for WASIp3 --- cmake/bindings.cmake | 1 + .../cloudlibc/src/libc/sys/stat/fstat.c | 8 +---- .../cloudlibc/src/libc/sys/stat/fstatat.c | 12 ++------ .../cloudlibc/src/libc/sys/stat/mkdirat.c | 10 ++----- .../headers/public/wasi/__generated_wasip3.h | 2 +- libc-bottom-half/sources/wasip3.c | 29 ++++++++++++++++--- test/CMakeLists.txt | 16 +++++----- 7 files changed, 40 insertions(+), 38 deletions(-) diff --git a/cmake/bindings.cmake b/cmake/bindings.cmake index e7919a289..7c2c12743 100644 --- a/cmake/bindings.cmake +++ b/cmake/bindings.cmake @@ -117,6 +117,7 @@ add_custom_target( "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.get-flags" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.open-at" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.read-directory" + "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.create-directory-at" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.remove-directory-at" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.unlink-file-at" "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-until" diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstat.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstat.c index 01cc633ae..3a672067e 100644 --- a/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstat.c +++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstat.c @@ -18,18 +18,12 @@ int fstat(int fildes, struct stat *buf) { } to_public_stat(&internal_stat, buf); return 0; -#elif defined(__wasip2__) +#elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle descriptor_table_entry_t *entry = descriptor_table_get_ref(fildes); if (!entry) return -1; return entry->vtable->fstat(entry->data, buf); -#elif defined(__wasip3__) - (void) fildes; - (void) buf; - // TODO(wasip3) - errno = ENOTSUP; - return -1; #else # error "Unsupported WASI version" #endif diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c index 7d8561edb..03d2a1a70 100644 --- a/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c +++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c @@ -35,14 +35,14 @@ int __wasilibc_nocwd_fstatat(int fd, const char *restrict path, struct stat *res to_public_stat(&internal_stat, buf); return 0; -#elif defined(__wasip2__) +#elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; if (fd_to_file_handle(fd, &file_handle) < 0) return -1; // Convert the string into a Wasm string - wasip2_string_t path_wasm_string; + wasi_string_t path_wasm_string; if (wasi_string_from_c(path, &path_wasm_string) < 0) return -1; @@ -80,14 +80,6 @@ int __wasilibc_nocwd_fstatat(int fd, const char *restrict path, struct stat *res // Convert the internal data to an external struct to_public_stat(&metadata, &internal_stat, buf); return 0; -#elif defined(__wasip3__) - (void) fd; - (void) path; - (void) buf; - (void) flag; - // TODO(wasip3) - errno = ENOTSUP; - return -1; #else # error "Unsupported WASI version" #endif diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/mkdirat.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/mkdirat.c index 52f9fef26..d758a9a99 100644 --- a/libc-bottom-half/cloudlibc/src/libc/sys/stat/mkdirat.c +++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/mkdirat.c @@ -21,7 +21,7 @@ int __wasilibc_nocwd_mkdirat_nomode(int fd, const char *path) { return -1; } return 0; -#elif defined(__wasip2__) +#elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; if (fd_to_file_handle(fd, &file_handle) < 0) @@ -29,7 +29,7 @@ int __wasilibc_nocwd_mkdirat_nomode(int fd, const char *path) { // Create the directory filesystem_error_code_t error; - wasip2_string_t path2; + wasi_string_t path2; if (wasi_string_from_c(path, &path2) < 0) return -1; bool ok = filesystem_method_descriptor_create_directory_at(file_handle, @@ -40,12 +40,6 @@ int __wasilibc_nocwd_mkdirat_nomode(int fd, const char *path) { return -1; } return 0; -#elif defined(__wasip3__) - (void) fd; - (void) path; - // TODO(wasip3) - errno = ENOTSUP; - return -1; #else # error "Unsupported WASI version" #endif diff --git a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h index ac7fab491..c47893609 100644 --- a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h +++ b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h @@ -1126,7 +1126,7 @@ extern wasip3_subtask_status_t filesystem_method_descriptor_sync(filesystem_borr // Create a directory. // // Note: This is similar to `mkdirat` in POSIX. -extern wasip3_subtask_status_t filesystem_method_descriptor_create_directory_at(filesystem_borrow_descriptor_t self, wasip3_string_t path, filesystem_result_void_error_code_t *result); +extern bool filesystem_method_descriptor_create_directory_at(filesystem_borrow_descriptor_t self, wasip3_string_t *path, filesystem_error_code_t *err); // Return the attributes of an open file or directory. // // Note: This is similar to `fstat` in POSIX, except that it does not return diff --git a/libc-bottom-half/sources/wasip3.c b/libc-bottom-half/sources/wasip3.c index 14e11467d..941e8e3b9 100644 --- a/libc-bottom-half/sources/wasip3.c +++ b/libc-bottom-half/sources/wasip3.c @@ -106,8 +106,8 @@ extern void __wasm_import_filesystem_method_descriptor_read_directory(int32_t, u __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.sync"))) extern int32_t __wasm_import_filesystem_method_descriptor_sync(int32_t, uint8_t *); -__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.create-directory-at"))) -extern int32_t __wasm_import_filesystem_method_descriptor_create_directory_at(int32_t, uint8_t *, size_t, uint8_t *); +__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.create-directory-at"))) +extern void __wasm_import_filesystem_method_descriptor_create_directory_at(int32_t, uint8_t *, size_t, uint8_t *); __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.stat"))) extern void __wasm_import_filesystem_method_descriptor_stat(int32_t, uint8_t *); @@ -1488,8 +1488,29 @@ wasip3_subtask_status_t filesystem_method_descriptor_sync(filesystem_borrow_desc return __wasm_import_filesystem_method_descriptor_sync((self).__handle, (uint8_t*) result); } -wasip3_subtask_status_t filesystem_method_descriptor_create_directory_at(filesystem_borrow_descriptor_t self, wasip3_string_t path, filesystem_result_void_error_code_t *result) { - return __wasm_import_filesystem_method_descriptor_create_directory_at((self).__handle, (uint8_t *) (path).ptr, (path).len, (uint8_t*) result); +bool filesystem_method_descriptor_create_directory_at(filesystem_borrow_descriptor_t self, wasip3_string_t *path, filesystem_error_code_t *err) { + __attribute__((__aligned__(1))) + uint8_t ret_area[2]; + uint8_t *ptr = (uint8_t *) &ret_area; + __wasm_import_filesystem_method_descriptor_create_directory_at((self).__handle, (uint8_t *) (*path).ptr, (*path).len, ptr); + filesystem_result_void_error_code_t result; + switch ((int32_t) *((uint8_t*) (ptr + 0))) { + case 0: { + result.is_err = false; + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) *((uint8_t*) (ptr + 1)); + break; + } + } + if (!result.is_err) { + return 1; + } else { + *err = result.val.err; + return 0; + } } bool filesystem_method_descriptor_stat(filesystem_borrow_descriptor_t self, filesystem_descriptor_stat_t *ret, filesystem_error_code_t *err) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e81a7fe2e..c189853b6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -262,7 +262,7 @@ endif() add_wasilibc_test(append.c FS) add_wasilibc_test(argv_two_args.c ARGV foo bar) add_wasilibc_test(clock_nanosleep.c) -add_wasilibc_test(chdir.c FS FAILP3) +add_wasilibc_test(chdir.c FS) add_wasilibc_test(close.c FS) add_wasilibc_test(external_env.c ENV VAR1=foo VAR2=bar) add_wasilibc_test(fadvise.c FS FAILP3) @@ -274,7 +274,7 @@ add_wasilibc_test(feof.c FS) add_wasilibc_test(file_permissions.c FS) add_wasilibc_test(file_nonblocking.c FS) add_wasilibc_test(fseek.c FS) -add_wasilibc_test(fstat.c FS FAILP3) +add_wasilibc_test(fstat.c FS) add_wasilibc_test(fsync.c FS FAILP3) add_wasilibc_test(ftruncate.c FS FAILP3) add_wasilibc_test(fts.c FS FAILP3) @@ -288,16 +288,16 @@ add_wasilibc_test(lseek.c FS) add_wasilibc_test(memchr.c LDFLAGS -Wl,--stack-first -Wl,--initial-memory=327680) add_wasilibc_test(memcmp.c LDFLAGS -Wl,--stack-first -Wl,--initial-memory=327680) add_wasilibc_test(opendir.c FS ARGV /) -add_wasilibc_test(open_relative_path.c FS FAILP3 ARGV /) +add_wasilibc_test(open_relative_path.c FS ARGV /) add_wasilibc_test(poll.c FS FAILP3) add_wasilibc_test(preadvwritev.c FS FAILP3) add_wasilibc_test(preadwrite.c FS FAILP3) add_wasilibc_test(readlink.c FS FAILP3) add_wasilibc_test(readv.c FS) add_wasilibc_test(rename.c FS FAILP3) -add_wasilibc_test(rmdir.c FS FAILP3) -add_wasilibc_test(scandir.c FS FAILP3) -add_wasilibc_test(stat.c FS FAILP3) +add_wasilibc_test(rmdir.c FS) +add_wasilibc_test(scandir.c FS) +add_wasilibc_test(stat.c FS) add_wasilibc_test(strchrnul.c LDFLAGS -Wl,--stack-first -Wl,--initial-memory=327680) add_wasilibc_test(strlen.c LDFLAGS -Wl,--stack-first -Wl,--initial-memory=327680) add_wasilibc_test(strptime.c) @@ -308,11 +308,11 @@ add_wasilibc_test(time_and_times.c add_wasilibc_test(time.c) add_wasilibc_test(utime.c FS FAILP3) add_wasilibc_test(rewinddir.c FS FAILP3) -add_wasilibc_test(seekdir.c FS FAILP3) +add_wasilibc_test(seekdir.c FS) add_wasilibc_test(usleep.c) add_wasilibc_test(nanosleep.c) add_wasilibc_test(sleep.c) -add_wasilibc_test(write.c FS FAILP3) +add_wasilibc_test(write.c FS) add_wasilibc_test(wasi-defines.c) add_wasilibc_test(close-stdio.c) From 446adf27b08f447294990389c4fa79ada350772b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Feb 2026 17:39:33 -0800 Subject: [PATCH 07/17] wasip3: Implement `fadvise` Use the wasip2 implementation --- cmake/bindings.cmake | 1 + .../cloudlibc/src/libc/fcntl/posix_fadvise.c | 8 +---- .../headers/public/wasi/__generated_wasip3.h | 2 +- libc-bottom-half/sources/wasip3.c | 29 ++++++++++++++++--- test/CMakeLists.txt | 2 +- 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/cmake/bindings.cmake b/cmake/bindings.cmake index 7c2c12743..853dd96ed 100644 --- a/cmake/bindings.cmake +++ b/cmake/bindings.cmake @@ -120,6 +120,7 @@ add_custom_target( "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.create-directory-at" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.remove-directory-at" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.unlink-file-at" + "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.advise" "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-until" "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-for" ${CMAKE_SOURCE_DIR}/wasi/p3/wit diff --git a/libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fadvise.c b/libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fadvise.c index 7a16be169..c27fc5565 100644 --- a/libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fadvise.c +++ b/libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fadvise.c @@ -30,7 +30,7 @@ int posix_fadvise(int fd, off_t offset, off_t len, int advice) { #if defined(__wasip1__) return __wasi_fd_advise(fd, offset, len, advice); -#elif defined(__wasip2__) +#elif defined(__wasip2__) || defined(__wasip3__) filesystem_borrow_descriptor_t file_handle; if (fd_to_file_handle(fd, &file_handle) < 0) return EBADF; @@ -71,12 +71,6 @@ int posix_fadvise(int fd, off_t offset, off_t len, int advice) { return errno; } return 0; -#elif defined(__wasip3__) - (void) fd; - (void) advice; - // TODO(wasip3) - errno = ENOTSUP; - return ENOTSUP; #else # error "Unsupported WASI version" #endif diff --git a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h index c47893609..acad81cf2 100644 --- a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h +++ b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h @@ -1066,7 +1066,7 @@ extern wasip3_subtask_status_t filesystem_method_descriptor_append_via_stream(fi // Provide file advisory information on a descriptor. // // This is similar to `posix_fadvise` in POSIX. -extern wasip3_subtask_status_t filesystem_method_descriptor_advise(filesystem_borrow_descriptor_t self, filesystem_filesize_t offset, filesystem_filesize_t length, filesystem_advice_t advice, filesystem_result_void_error_code_t *result); +extern bool filesystem_method_descriptor_advise(filesystem_borrow_descriptor_t self, filesystem_filesize_t offset, filesystem_filesize_t length, filesystem_advice_t advice, filesystem_error_code_t *err); // Synchronize the data of a file to disk. // // This function succeeds with no effect if the file descriptor is not diff --git a/libc-bottom-half/sources/wasip3.c b/libc-bottom-half/sources/wasip3.c index 941e8e3b9..2db58e499 100644 --- a/libc-bottom-half/sources/wasip3.c +++ b/libc-bottom-half/sources/wasip3.c @@ -82,8 +82,8 @@ extern int32_t __wasm_import_filesystem_method_descriptor_write_via_stream(int32 __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.append-via-stream"))) extern int32_t __wasm_import_filesystem_method_descriptor_append_via_stream(int32_t, int32_t, uint8_t *); -__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.advise"))) -extern int32_t __wasm_import_filesystem_method_descriptor_advise(int32_t, int64_t, int64_t, int32_t, uint8_t *); +__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.advise"))) +extern void __wasm_import_filesystem_method_descriptor_advise(int32_t, int64_t, int64_t, int32_t, uint8_t *); __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.sync-data"))) extern int32_t __wasm_import_filesystem_method_descriptor_sync_data(int32_t, uint8_t *); @@ -1426,8 +1426,29 @@ wasip3_subtask_status_t filesystem_method_descriptor_append_via_stream(filesyste return __wasm_import_filesystem_method_descriptor_append_via_stream((self).__handle, ((int32_t) data), (uint8_t*) result); } -wasip3_subtask_status_t filesystem_method_descriptor_advise(filesystem_borrow_descriptor_t self, filesystem_filesize_t offset, filesystem_filesize_t length, filesystem_advice_t advice, filesystem_result_void_error_code_t *result) { - return __wasm_import_filesystem_method_descriptor_advise((self).__handle, (int64_t) (offset), (int64_t) (length), (int32_t) advice, (uint8_t*) result); +bool filesystem_method_descriptor_advise(filesystem_borrow_descriptor_t self, filesystem_filesize_t offset, filesystem_filesize_t length, filesystem_advice_t advice, filesystem_error_code_t *err) { + __attribute__((__aligned__(1))) + uint8_t ret_area[2]; + uint8_t *ptr = (uint8_t *) &ret_area; + __wasm_import_filesystem_method_descriptor_advise((self).__handle, (int64_t) (offset), (int64_t) (length), (int32_t) advice, ptr); + filesystem_result_void_error_code_t result; + switch ((int32_t) *((uint8_t*) (ptr + 0))) { + case 0: { + result.is_err = false; + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) *((uint8_t*) (ptr + 1)); + break; + } + } + if (!result.is_err) { + return 1; + } else { + *err = result.val.err; + return 0; + } } wasip3_subtask_status_t filesystem_method_descriptor_sync_data(filesystem_borrow_descriptor_t self, filesystem_result_void_error_code_t *result) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c189853b6..315869d51 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -265,7 +265,7 @@ add_wasilibc_test(clock_nanosleep.c) add_wasilibc_test(chdir.c FS) add_wasilibc_test(close.c FS) add_wasilibc_test(external_env.c ENV VAR1=foo VAR2=bar) -add_wasilibc_test(fadvise.c FS FAILP3) +add_wasilibc_test(fadvise.c FS) add_wasilibc_test(fallocate.c FS) add_wasilibc_test(fcntl.c FS) add_wasilibc_test(fdatasync.c FS FAILP3) From 27133f12dde632b61b5d70a2ea9168e202c5b1dc Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Feb 2026 17:40:16 -0800 Subject: [PATCH 08/17] wasip3: Implement `fdatasync` Use the wasip2 implementation --- cmake/bindings.cmake | 1 + .../cloudlibc/src/libc/unistd/fdatasync.c | 7 +---- .../headers/public/wasi/__generated_wasip3.h | 2 +- libc-bottom-half/sources/wasip3.c | 29 ++++++++++++++++--- test/CMakeLists.txt | 2 +- 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/cmake/bindings.cmake b/cmake/bindings.cmake index 853dd96ed..738befe09 100644 --- a/cmake/bindings.cmake +++ b/cmake/bindings.cmake @@ -121,6 +121,7 @@ add_custom_target( "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.remove-directory-at" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.unlink-file-at" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.advise" + "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.sync-data" "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-until" "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-for" ${CMAKE_SOURCE_DIR}/wasi/p3/wit diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/fdatasync.c b/libc-bottom-half/cloudlibc/src/libc/unistd/fdatasync.c index 66d4d452b..57a13942b 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/fdatasync.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/fdatasync.c @@ -19,7 +19,7 @@ int fdatasync(int fildes) { errno = error == ENOTCAPABLE ? EBADF : error; return -1; } -#elif defined(__wasip2__) +#elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; if (fd_to_file_handle(fildes, &file_handle) < 0) @@ -31,11 +31,6 @@ int fdatasync(int fildes) { translate_error(error_code); return -1; } -#elif defined(__wasip3__) - (void) fildes; - // TODO(wasip3) - errno = ENOTSUP; - return -1; #else # error "Unsupported WASI version" #endif diff --git a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h index acad81cf2..c6654328e 100644 --- a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h +++ b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h @@ -1073,7 +1073,7 @@ extern bool filesystem_method_descriptor_advise(filesystem_borrow_descriptor_t s // opened for writing. // // Note: This is similar to `fdatasync` in POSIX. -extern wasip3_subtask_status_t filesystem_method_descriptor_sync_data(filesystem_borrow_descriptor_t self, filesystem_result_void_error_code_t *result); +extern bool filesystem_method_descriptor_sync_data(filesystem_borrow_descriptor_t self, filesystem_error_code_t *err); // Get flags associated with a descriptor. // // Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX. diff --git a/libc-bottom-half/sources/wasip3.c b/libc-bottom-half/sources/wasip3.c index 2db58e499..cdfd2995b 100644 --- a/libc-bottom-half/sources/wasip3.c +++ b/libc-bottom-half/sources/wasip3.c @@ -85,8 +85,8 @@ extern int32_t __wasm_import_filesystem_method_descriptor_append_via_stream(int3 __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.advise"))) extern void __wasm_import_filesystem_method_descriptor_advise(int32_t, int64_t, int64_t, int32_t, uint8_t *); -__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.sync-data"))) -extern int32_t __wasm_import_filesystem_method_descriptor_sync_data(int32_t, uint8_t *); +__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.sync-data"))) +extern void __wasm_import_filesystem_method_descriptor_sync_data(int32_t, uint8_t *); __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.get-flags"))) extern void __wasm_import_filesystem_method_descriptor_get_flags(int32_t, uint8_t *); @@ -1451,8 +1451,29 @@ bool filesystem_method_descriptor_advise(filesystem_borrow_descriptor_t self, fi } } -wasip3_subtask_status_t filesystem_method_descriptor_sync_data(filesystem_borrow_descriptor_t self, filesystem_result_void_error_code_t *result) { - return __wasm_import_filesystem_method_descriptor_sync_data((self).__handle, (uint8_t*) result); +bool filesystem_method_descriptor_sync_data(filesystem_borrow_descriptor_t self, filesystem_error_code_t *err) { + __attribute__((__aligned__(1))) + uint8_t ret_area[2]; + uint8_t *ptr = (uint8_t *) &ret_area; + __wasm_import_filesystem_method_descriptor_sync_data((self).__handle, ptr); + filesystem_result_void_error_code_t result; + switch ((int32_t) *((uint8_t*) (ptr + 0))) { + case 0: { + result.is_err = false; + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) *((uint8_t*) (ptr + 1)); + break; + } + } + if (!result.is_err) { + return 1; + } else { + *err = result.val.err; + return 0; + } } bool filesystem_method_descriptor_get_flags(filesystem_borrow_descriptor_t self, filesystem_descriptor_flags_t *ret, filesystem_error_code_t *err) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 315869d51..2a80f9c5d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -268,7 +268,7 @@ add_wasilibc_test(external_env.c ENV VAR1=foo VAR2=bar) add_wasilibc_test(fadvise.c FS) add_wasilibc_test(fallocate.c FS) add_wasilibc_test(fcntl.c FS) -add_wasilibc_test(fdatasync.c FS FAILP3) +add_wasilibc_test(fdatasync.c FS) add_wasilibc_test(fdopen.c FS) add_wasilibc_test(feof.c FS) add_wasilibc_test(file_permissions.c FS) From 22a614fb0c11ca9368aa2cebb9842945151e4a98 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Feb 2026 17:41:12 -0800 Subject: [PATCH 09/17] wasip3: Implement `fsync` Use the wasip2 implementation --- cmake/bindings.cmake | 1 + .../cloudlibc/src/libc/unistd/fsync.c | 7 +---- .../headers/public/wasi/__generated_wasip3.h | 2 +- libc-bottom-half/sources/wasip3.c | 29 ++++++++++++++++--- test/CMakeLists.txt | 2 +- 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/cmake/bindings.cmake b/cmake/bindings.cmake index 738befe09..948ede25c 100644 --- a/cmake/bindings.cmake +++ b/cmake/bindings.cmake @@ -122,6 +122,7 @@ add_custom_target( "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.unlink-file-at" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.advise" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.sync-data" + "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.sync" "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-until" "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-for" ${CMAKE_SOURCE_DIR}/wasi/p3/wit diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/fsync.c b/libc-bottom-half/cloudlibc/src/libc/unistd/fsync.c index ef12c2e5d..01e8df443 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/fsync.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/fsync.c @@ -18,7 +18,7 @@ int fsync(int fildes) { errno = error == ENOTCAPABLE ? EINVAL : error; return -1; } -#elif defined(__wasip2__) +#elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; if (fd_to_file_handle(fildes, &file_handle) < 0) @@ -30,11 +30,6 @@ int fsync(int fildes) { translate_error(error_code); return -1; } -#elif defined(__wasip3__) - (void) fildes; - // TODO(wasip3) - errno = ENOTSUP; - return -1; #else # error "Unsupported WASI version" #endif diff --git a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h index c6654328e..c9e84cd08 100644 --- a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h +++ b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h @@ -1122,7 +1122,7 @@ extern void filesystem_method_descriptor_read_directory(filesystem_borrow_descri // opened for writing. // // Note: This is similar to `fsync` in POSIX. -extern wasip3_subtask_status_t filesystem_method_descriptor_sync(filesystem_borrow_descriptor_t self, filesystem_result_void_error_code_t *result); +extern bool filesystem_method_descriptor_sync(filesystem_borrow_descriptor_t self, filesystem_error_code_t *err); // Create a directory. // // Note: This is similar to `mkdirat` in POSIX. diff --git a/libc-bottom-half/sources/wasip3.c b/libc-bottom-half/sources/wasip3.c index cdfd2995b..672637121 100644 --- a/libc-bottom-half/sources/wasip3.c +++ b/libc-bottom-half/sources/wasip3.c @@ -103,8 +103,8 @@ extern int32_t __wasm_import_filesystem_method_descriptor_set_times(uint8_t *, u __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.read-directory"))) extern void __wasm_import_filesystem_method_descriptor_read_directory(int32_t, uint8_t *); -__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.sync"))) -extern int32_t __wasm_import_filesystem_method_descriptor_sync(int32_t, uint8_t *); +__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.sync"))) +extern void __wasm_import_filesystem_method_descriptor_sync(int32_t, uint8_t *); __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.create-directory-at"))) extern void __wasm_import_filesystem_method_descriptor_create_directory_at(int32_t, uint8_t *, size_t, uint8_t *); @@ -1526,8 +1526,29 @@ void filesystem_method_descriptor_read_directory(filesystem_borrow_descriptor_t }; } -wasip3_subtask_status_t filesystem_method_descriptor_sync(filesystem_borrow_descriptor_t self, filesystem_result_void_error_code_t *result) { - return __wasm_import_filesystem_method_descriptor_sync((self).__handle, (uint8_t*) result); +bool filesystem_method_descriptor_sync(filesystem_borrow_descriptor_t self, filesystem_error_code_t *err) { + __attribute__((__aligned__(1))) + uint8_t ret_area[2]; + uint8_t *ptr = (uint8_t *) &ret_area; + __wasm_import_filesystem_method_descriptor_sync((self).__handle, ptr); + filesystem_result_void_error_code_t result; + switch ((int32_t) *((uint8_t*) (ptr + 0))) { + case 0: { + result.is_err = false; + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) *((uint8_t*) (ptr + 1)); + break; + } + } + if (!result.is_err) { + return 1; + } else { + *err = result.val.err; + return 0; + } } bool filesystem_method_descriptor_create_directory_at(filesystem_borrow_descriptor_t self, wasip3_string_t *path, filesystem_error_code_t *err) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2a80f9c5d..2686d7210 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -275,7 +275,7 @@ add_wasilibc_test(file_permissions.c FS) add_wasilibc_test(file_nonblocking.c FS) add_wasilibc_test(fseek.c FS) add_wasilibc_test(fstat.c FS) -add_wasilibc_test(fsync.c FS FAILP3) +add_wasilibc_test(fsync.c FS) add_wasilibc_test(ftruncate.c FS FAILP3) add_wasilibc_test(fts.c FS FAILP3) add_wasilibc_test(fwscanf.c FS) From e7e73410fd74fbde4d407e68df365b9378f84db4 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Feb 2026 17:42:01 -0800 Subject: [PATCH 10/17] wasip3: Implement `ftruncate` Use the wasip2 implementation --- cmake/bindings.cmake | 1 + .../cloudlibc/src/libc/unistd/ftruncate.c | 7 +---- .../headers/public/wasi/__generated_wasip3.h | 2 +- libc-bottom-half/sources/wasip3.c | 29 ++++++++++++++++--- test/CMakeLists.txt | 2 +- 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/cmake/bindings.cmake b/cmake/bindings.cmake index 948ede25c..661d2ffb1 100644 --- a/cmake/bindings.cmake +++ b/cmake/bindings.cmake @@ -123,6 +123,7 @@ add_custom_target( "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.advise" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.sync-data" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.sync" + "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.set-size" "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-until" "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-for" ${CMAKE_SOURCE_DIR}/wasi/p3/wit diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/ftruncate.c b/libc-bottom-half/cloudlibc/src/libc/unistd/ftruncate.c index 2722b95e2..08a1d685e 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/ftruncate.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/ftruncate.c @@ -24,7 +24,7 @@ int ftruncate(int fildes, off_t length) { errno = error; return -1; } -#elif defined(__wasip2__) +#elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal file handle filesystem_borrow_descriptor_t file_handle; if (fd_to_file_handle(fildes, &file_handle) < 0) @@ -35,11 +35,6 @@ int ftruncate(int fildes, off_t length) { translate_error(error_code); return -1; } -#elif defined(__wasip3__) - (void) fildes; - // TODO(wasip3) - errno = ENOTSUP; - return -1; #else # error "Unsupported WASI version" #endif diff --git a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h index c9e84cd08..8a8bdc15b 100644 --- a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h +++ b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h @@ -1096,7 +1096,7 @@ extern wasip3_subtask_status_t filesystem_method_descriptor_get_type(filesystem_ // extra bytes are filled with zeros. // // Note: This was called `fd_filestat_set_size` in earlier versions of WASI. -extern wasip3_subtask_status_t filesystem_method_descriptor_set_size(filesystem_borrow_descriptor_t self, filesystem_filesize_t size, filesystem_result_void_error_code_t *result); +extern bool filesystem_method_descriptor_set_size(filesystem_borrow_descriptor_t self, filesystem_filesize_t size, filesystem_error_code_t *err); // Adjust the timestamps of an open file or directory. // // Note: This is similar to `futimens` in POSIX. diff --git a/libc-bottom-half/sources/wasip3.c b/libc-bottom-half/sources/wasip3.c index 672637121..19eb93816 100644 --- a/libc-bottom-half/sources/wasip3.c +++ b/libc-bottom-half/sources/wasip3.c @@ -94,8 +94,8 @@ extern void __wasm_import_filesystem_method_descriptor_get_flags(int32_t, uint8_ __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.get-type"))) extern int32_t __wasm_import_filesystem_method_descriptor_get_type(int32_t, uint8_t *); -__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.set-size"))) -extern int32_t __wasm_import_filesystem_method_descriptor_set_size(int32_t, int64_t, uint8_t *); +__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.set-size"))) +extern void __wasm_import_filesystem_method_descriptor_set_size(int32_t, int64_t, uint8_t *); __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.set-times"))) extern int32_t __wasm_import_filesystem_method_descriptor_set_times(uint8_t *, uint8_t *); @@ -1507,8 +1507,29 @@ wasip3_subtask_status_t filesystem_method_descriptor_get_type(filesystem_borrow_ return __wasm_import_filesystem_method_descriptor_get_type((self).__handle, (uint8_t*) result); } -wasip3_subtask_status_t filesystem_method_descriptor_set_size(filesystem_borrow_descriptor_t self, filesystem_filesize_t size, filesystem_result_void_error_code_t *result) { - return __wasm_import_filesystem_method_descriptor_set_size((self).__handle, (int64_t) (size), (uint8_t*) result); +bool filesystem_method_descriptor_set_size(filesystem_borrow_descriptor_t self, filesystem_filesize_t size, filesystem_error_code_t *err) { + __attribute__((__aligned__(1))) + uint8_t ret_area[2]; + uint8_t *ptr = (uint8_t *) &ret_area; + __wasm_import_filesystem_method_descriptor_set_size((self).__handle, (int64_t) (size), ptr); + filesystem_result_void_error_code_t result; + switch ((int32_t) *((uint8_t*) (ptr + 0))) { + case 0: { + result.is_err = false; + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) *((uint8_t*) (ptr + 1)); + break; + } + } + if (!result.is_err) { + return 1; + } else { + *err = result.val.err; + return 0; + } } wasip3_subtask_status_t filesystem_method_descriptor_set_times(filesystem_method_descriptor_set_times_args_t *args, filesystem_result_void_error_code_t *result) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2686d7210..cdc07bc85 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -276,7 +276,7 @@ add_wasilibc_test(file_nonblocking.c FS) add_wasilibc_test(fseek.c FS) add_wasilibc_test(fstat.c FS) add_wasilibc_test(fsync.c FS) -add_wasilibc_test(ftruncate.c FS FAILP3) +add_wasilibc_test(ftruncate.c FS) add_wasilibc_test(fts.c FS FAILP3) add_wasilibc_test(fwscanf.c FS) add_wasilibc_test(getentropy.c) From d9d0bff19c072f7f4d497cc560e7b4f757960f2d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Feb 2026 17:42:54 -0800 Subject: [PATCH 11/17] wasip3: Implement `symlinkat` Use the wasip2 implementation --- cmake/bindings.cmake | 1 + .../cloudlibc/src/libc/unistd/symlinkat.c | 11 ++----- .../headers/public/wasi/__generated_wasip3.h | 7 +---- libc-bottom-half/sources/wasip3.c | 29 ++++++++++++++++--- test/CMakeLists.txt | 2 +- 5 files changed, 30 insertions(+), 20 deletions(-) diff --git a/cmake/bindings.cmake b/cmake/bindings.cmake index 661d2ffb1..07a57a0a9 100644 --- a/cmake/bindings.cmake +++ b/cmake/bindings.cmake @@ -124,6 +124,7 @@ add_custom_target( "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.sync-data" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.sync" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.set-size" + "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.symlink-at" "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-until" "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-for" ${CMAKE_SOURCE_DIR}/wasi/p3/wit diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/symlinkat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/symlinkat.c index 96aea65be..5ea2c97dd 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/symlinkat.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/symlinkat.c @@ -19,14 +19,14 @@ int __wasilibc_nocwd_symlinkat(const char *path1, int fd, const char *path2) { errno = error; return -1; } -#elif defined(__wasip2__) +#elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; if (fd_to_file_handle(fd, &file_handle) < 0) return -1; // Convert the paths into WASI paths - wasip2_string_t path1_wasi, path2_wasi; + wasi_string_t path1_wasi, path2_wasi; if (wasi_string_from_c(path1, &path1_wasi) < 0) return -1; if (wasi_string_from_c(path2, &path2_wasi) < 0) @@ -44,13 +44,6 @@ int __wasilibc_nocwd_symlinkat(const char *path1, int fd, const char *path2) { translate_error(error_code); return -1; } -#elif defined(__wasip3__) - (void) path1; - (void) path2; - (void) fd; - // TODO(wasip3) - errno = ENOTSUP; - return -1; #else # error "Unsupported WASI version" #endif diff --git a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h index 8a8bdc15b..ecfbd28f8 100644 --- a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h +++ b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h @@ -599,11 +599,6 @@ typedef struct filesystem_method_descriptor_rename_at_args { filesystem_borrow_descriptor_t new_descriptor; wasip3_string_t new_path; } filesystem_method_descriptor_rename_at_args_t; -typedef struct filesystem_method_descriptor_symlink_at_args { - filesystem_borrow_descriptor_t self; - wasip3_string_t old_path; - wasip3_string_t new_path; -} filesystem_method_descriptor_symlink_at_args_t; typedef filesystem_own_descriptor_t filesystem_preopens_own_descriptor_t; @@ -1196,7 +1191,7 @@ extern wasip3_subtask_status_t filesystem_method_descriptor_rename_at(filesystem // `error-code::not-permitted`. // // Note: This is similar to `symlinkat` in POSIX. -extern wasip3_subtask_status_t filesystem_method_descriptor_symlink_at(filesystem_method_descriptor_symlink_at_args_t *args, filesystem_result_void_error_code_t *result); +extern bool filesystem_method_descriptor_symlink_at(filesystem_borrow_descriptor_t self, wasip3_string_t *old_path, wasip3_string_t *new_path, filesystem_error_code_t *err); // Unlink a filesystem object that is not a directory. // // Return `error-code::is-directory` if the path refers to a directory. diff --git a/libc-bottom-half/sources/wasip3.c b/libc-bottom-half/sources/wasip3.c index 19eb93816..df7922b93 100644 --- a/libc-bottom-half/sources/wasip3.c +++ b/libc-bottom-half/sources/wasip3.c @@ -133,8 +133,8 @@ extern void __wasm_import_filesystem_method_descriptor_remove_directory_at(int32 __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.rename-at"))) extern int32_t __wasm_import_filesystem_method_descriptor_rename_at(uint8_t *, uint8_t *); -__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.symlink-at"))) -extern int32_t __wasm_import_filesystem_method_descriptor_symlink_at(uint8_t *, uint8_t *); +__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.symlink-at"))) +extern void __wasm_import_filesystem_method_descriptor_symlink_at(int32_t, uint8_t *, size_t, uint8_t *, size_t, uint8_t *); __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.unlink-file-at"))) extern void __wasm_import_filesystem_method_descriptor_unlink_file_at(int32_t, uint8_t *, size_t, uint8_t *); @@ -1825,8 +1825,29 @@ wasip3_subtask_status_t filesystem_method_descriptor_rename_at(filesystem_method return __wasm_import_filesystem_method_descriptor_rename_at((uint8_t*) args, (uint8_t*) result); } -wasip3_subtask_status_t filesystem_method_descriptor_symlink_at(filesystem_method_descriptor_symlink_at_args_t *args, filesystem_result_void_error_code_t *result) { - return __wasm_import_filesystem_method_descriptor_symlink_at((uint8_t*) args, (uint8_t*) result); +bool filesystem_method_descriptor_symlink_at(filesystem_borrow_descriptor_t self, wasip3_string_t *old_path, wasip3_string_t *new_path, filesystem_error_code_t *err) { + __attribute__((__aligned__(1))) + uint8_t ret_area[2]; + uint8_t *ptr = (uint8_t *) &ret_area; + __wasm_import_filesystem_method_descriptor_symlink_at((self).__handle, (uint8_t *) (*old_path).ptr, (*old_path).len, (uint8_t *) (*new_path).ptr, (*new_path).len, ptr); + filesystem_result_void_error_code_t result; + switch ((int32_t) *((uint8_t*) (ptr + 0))) { + case 0: { + result.is_err = false; + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) *((uint8_t*) (ptr + 1)); + break; + } + } + if (!result.is_err) { + return 1; + } else { + *err = result.val.err; + return 0; + } } bool filesystem_method_descriptor_unlink_file_at(filesystem_borrow_descriptor_t self, wasip3_string_t *path, filesystem_error_code_t *err) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cdc07bc85..2a98d6984 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -277,7 +277,7 @@ add_wasilibc_test(fseek.c FS) add_wasilibc_test(fstat.c FS) add_wasilibc_test(fsync.c FS) add_wasilibc_test(ftruncate.c FS) -add_wasilibc_test(fts.c FS FAILP3) +add_wasilibc_test(fts.c FS) add_wasilibc_test(fwscanf.c FS) add_wasilibc_test(getentropy.c) add_wasilibc_test(hello.c PASS_REGULAR_EXPRESSION "Hello, World!") From e7ec2e67299ce03ba63f4ed2bd3d4345715d0d79 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Feb 2026 17:43:37 -0800 Subject: [PATCH 12/17] wasip3: Implement `linkat` Use the wasip2 implementation --- cmake/bindings.cmake | 1 + .../cloudlibc/src/libc/unistd/linkat.c | 13 ++------- .../headers/public/wasi/__generated_wasip3.h | 9 +----- libc-bottom-half/sources/wasip3.c | 29 ++++++++++++++++--- test/CMakeLists.txt | 2 +- 5 files changed, 30 insertions(+), 24 deletions(-) diff --git a/cmake/bindings.cmake b/cmake/bindings.cmake index 07a57a0a9..6d3106386 100644 --- a/cmake/bindings.cmake +++ b/cmake/bindings.cmake @@ -125,6 +125,7 @@ add_custom_target( "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.sync" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.set-size" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.symlink-at" + "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.link-at" "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-until" "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-for" ${CMAKE_SOURCE_DIR}/wasi/p3/wit diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/linkat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/linkat.c index 2cbd27dc1..17d54eca8 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/linkat.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/linkat.c @@ -26,7 +26,7 @@ int __wasilibc_nocwd_linkat(int fd1, const char *path1, int fd2, const char *pat errno = error; return -1; } -#elif defined(__wasip2__) +#elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptors to internal handles filesystem_borrow_descriptor_t file_handle1, file_handle2; if (fd_to_file_handle(fd1, &file_handle1) < 0) @@ -35,7 +35,7 @@ int __wasilibc_nocwd_linkat(int fd1, const char *path1, int fd2, const char *pat return -1; // Convert the strings into WASI strings - wasip2_string_t path1_wasi, path2_wasi; + wasi_string_t path1_wasi, path2_wasi; if (wasi_string_from_c(path1, &path1_wasi) < 0) return -1; if (wasi_string_from_c(path2, &path2_wasi) < 0) @@ -56,15 +56,6 @@ int __wasilibc_nocwd_linkat(int fd1, const char *path1, int fd2, const char *pat translate_error(error_code); return -1; } -#elif defined(__wasip3__) - (void) fd1; - (void) path1; - (void) fd2; - (void) path2; - (void) flag; - // TODO(wasip3) - errno = ENOTSUP; - return -1; #else # error "Unsupported WASI version" #endif diff --git a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h index ecfbd28f8..413af18e7 100644 --- a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h +++ b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h @@ -586,13 +586,6 @@ typedef struct filesystem_method_descriptor_set_times_at_args { filesystem_new_timestamp_t data_access_timestamp; filesystem_new_timestamp_t data_modification_timestamp; } filesystem_method_descriptor_set_times_at_args_t; -typedef struct filesystem_method_descriptor_link_at_args { - filesystem_borrow_descriptor_t self; - filesystem_path_flags_t old_path_flags; - wasip3_string_t old_path; - filesystem_borrow_descriptor_t new_descriptor; - wasip3_string_t new_path; -} filesystem_method_descriptor_link_at_args_t; typedef struct filesystem_method_descriptor_rename_at_args { filesystem_borrow_descriptor_t self; wasip3_string_t old_path; @@ -1154,7 +1147,7 @@ extern wasip3_subtask_status_t filesystem_method_descriptor_set_times_at(filesys // `error-code::not-permitted` if the old path is not a file. // // Note: This is similar to `linkat` in POSIX. -extern wasip3_subtask_status_t filesystem_method_descriptor_link_at(filesystem_method_descriptor_link_at_args_t *args, filesystem_result_void_error_code_t *result); +extern bool filesystem_method_descriptor_link_at(filesystem_borrow_descriptor_t self, filesystem_path_flags_t old_path_flags, wasip3_string_t *old_path, filesystem_borrow_descriptor_t new_descriptor, wasip3_string_t *new_path, filesystem_error_code_t *err); // Open a file or directory. // // If `flags` contains `descriptor-flags::mutate-directory`, and the base diff --git a/libc-bottom-half/sources/wasip3.c b/libc-bottom-half/sources/wasip3.c index df7922b93..d384eef32 100644 --- a/libc-bottom-half/sources/wasip3.c +++ b/libc-bottom-half/sources/wasip3.c @@ -118,8 +118,8 @@ extern void __wasm_import_filesystem_method_descriptor_stat_at(int32_t, int32_t, __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.set-times-at"))) extern int32_t __wasm_import_filesystem_method_descriptor_set_times_at(uint8_t *, uint8_t *); -__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.link-at"))) -extern int32_t __wasm_import_filesystem_method_descriptor_link_at(uint8_t *, uint8_t *); +__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.link-at"))) +extern void __wasm_import_filesystem_method_descriptor_link_at(int32_t, int32_t, uint8_t *, size_t, int32_t, uint8_t *, size_t, uint8_t *); __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.open-at"))) extern void __wasm_import_filesystem_method_descriptor_open_at(int32_t, int32_t, uint8_t *, size_t, int32_t, int32_t, uint8_t *); @@ -1761,8 +1761,29 @@ wasip3_subtask_status_t filesystem_method_descriptor_set_times_at(filesystem_met return __wasm_import_filesystem_method_descriptor_set_times_at((uint8_t*) args, (uint8_t*) result); } -wasip3_subtask_status_t filesystem_method_descriptor_link_at(filesystem_method_descriptor_link_at_args_t *args, filesystem_result_void_error_code_t *result) { - return __wasm_import_filesystem_method_descriptor_link_at((uint8_t*) args, (uint8_t*) result); +bool filesystem_method_descriptor_link_at(filesystem_borrow_descriptor_t self, filesystem_path_flags_t old_path_flags, wasip3_string_t *old_path, filesystem_borrow_descriptor_t new_descriptor, wasip3_string_t *new_path, filesystem_error_code_t *err) { + __attribute__((__aligned__(1))) + uint8_t ret_area[2]; + uint8_t *ptr = (uint8_t *) &ret_area; + __wasm_import_filesystem_method_descriptor_link_at((self).__handle, old_path_flags, (uint8_t *) (*old_path).ptr, (*old_path).len, (new_descriptor).__handle, (uint8_t *) (*new_path).ptr, (*new_path).len, ptr); + filesystem_result_void_error_code_t result; + switch ((int32_t) *((uint8_t*) (ptr + 0))) { + case 0: { + result.is_err = false; + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) *((uint8_t*) (ptr + 1)); + break; + } + } + if (!result.is_err) { + return 1; + } else { + *err = result.val.err; + return 0; + } } bool filesystem_method_descriptor_open_at(filesystem_borrow_descriptor_t self, filesystem_path_flags_t path_flags, wasip3_string_t *path, filesystem_open_flags_t open_flags, filesystem_descriptor_flags_t flags, filesystem_own_descriptor_t *ret, filesystem_error_code_t *err) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2a98d6984..a0116486c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -283,7 +283,7 @@ add_wasilibc_test(getentropy.c) add_wasilibc_test(hello.c PASS_REGULAR_EXPRESSION "Hello, World!") add_wasilibc_test(ioctl.c FS) add_wasilibc_test(isatty.c FS) -add_wasilibc_test(link.c FS FAILP3) +add_wasilibc_test(link.c FS) add_wasilibc_test(lseek.c FS) add_wasilibc_test(memchr.c LDFLAGS -Wl,--stack-first -Wl,--initial-memory=327680) add_wasilibc_test(memcmp.c LDFLAGS -Wl,--stack-first -Wl,--initial-memory=327680) From 9f02da0d73f94018ba172b4dd92a7c402201b6ca Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Feb 2026 17:46:38 -0800 Subject: [PATCH 13/17] wasip3: Implement `readlink` Use the wasip2 version --- cmake/bindings.cmake | 1 + .../cloudlibc/src/libc/unistd/readlinkat.c | 16 ++++------ .../headers/public/wasi/__generated_wasip3.h | 2 +- libc-bottom-half/sources/wasip3.c | 31 ++++++++++++++++--- test/CMakeLists.txt | 2 +- 5 files changed, 36 insertions(+), 16 deletions(-) diff --git a/cmake/bindings.cmake b/cmake/bindings.cmake index 6d3106386..f991b6ef5 100644 --- a/cmake/bindings.cmake +++ b/cmake/bindings.cmake @@ -126,6 +126,7 @@ add_custom_target( "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.set-size" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.symlink-at" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.link-at" + "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.readlink-at" "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-until" "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-for" ${CMAKE_SOURCE_DIR}/wasi/p3/wit diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/readlinkat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/readlinkat.c index 656b9c501..f5c93a6eb 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/readlinkat.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/readlinkat.c @@ -23,14 +23,14 @@ ssize_t __wasilibc_nocwd_readlinkat(int fd, const char *restrict path, char *res errno = error; return -1; } -#elif defined(__wasip2__) +#elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; if (fd_to_file_handle(fd, &file_handle) < 0) return -1; // Convert the path into a WASI path - wasip2_string_t wasi_path, link_source; + wasi_string_t wasi_path, link_source; if (wasi_string_from_c(path, &wasi_path) < 0) return -1; @@ -48,15 +48,11 @@ ssize_t __wasilibc_nocwd_readlinkat(int fd, const char *restrict path, char *res // Copy the contents of the output path back into the buffer provided bufused = bufsize < link_source.len ? bufsize : link_source.len; memcpy(buf, link_source.ptr, bufused); +#ifdef __wasip2__ wasip2_string_free(&link_source); -#elif defined(__wasip3__) - (void) fd; - (void) path; - (void) buf; - (void) bufsize; - // TODO(wasip3) - errno = ENOTSUP; - return -1; +#else + wasip3_string_free(&link_source); +#endif #else # error "Unsupported WASI version" #endif diff --git a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h index 413af18e7..b0925c577 100644 --- a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h +++ b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h @@ -1167,7 +1167,7 @@ extern bool filesystem_method_descriptor_open_at(filesystem_borrow_descriptor_t // filesystem, this function fails with `error-code::not-permitted`. // // Note: This is similar to `readlinkat` in POSIX. -extern wasip3_subtask_status_t filesystem_method_descriptor_readlink_at(filesystem_borrow_descriptor_t self, wasip3_string_t path, filesystem_result_string_error_code_t *result); +extern bool filesystem_method_descriptor_readlink_at(filesystem_borrow_descriptor_t self, wasip3_string_t *path, wasip3_string_t *ret, filesystem_error_code_t *err); // Remove a directory. // // Return `error-code::not-empty` if the directory is not empty. diff --git a/libc-bottom-half/sources/wasip3.c b/libc-bottom-half/sources/wasip3.c index d384eef32..43554eae4 100644 --- a/libc-bottom-half/sources/wasip3.c +++ b/libc-bottom-half/sources/wasip3.c @@ -124,8 +124,8 @@ extern void __wasm_import_filesystem_method_descriptor_link_at(int32_t, int32_t, __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.open-at"))) extern void __wasm_import_filesystem_method_descriptor_open_at(int32_t, int32_t, uint8_t *, size_t, int32_t, int32_t, uint8_t *); -__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.readlink-at"))) -extern int32_t __wasm_import_filesystem_method_descriptor_readlink_at(int32_t, uint8_t *, size_t, uint8_t *); +__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.readlink-at"))) +extern void __wasm_import_filesystem_method_descriptor_readlink_at(int32_t, uint8_t *, size_t, uint8_t *); __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.remove-directory-at"))) extern void __wasm_import_filesystem_method_descriptor_remove_directory_at(int32_t, uint8_t *, size_t, uint8_t *); @@ -1813,8 +1813,31 @@ bool filesystem_method_descriptor_open_at(filesystem_borrow_descriptor_t self, f } } -wasip3_subtask_status_t filesystem_method_descriptor_readlink_at(filesystem_borrow_descriptor_t self, wasip3_string_t path, filesystem_result_string_error_code_t *result) { - return __wasm_import_filesystem_method_descriptor_readlink_at((self).__handle, (uint8_t *) (path).ptr, (path).len, (uint8_t*) result); +bool filesystem_method_descriptor_readlink_at(filesystem_borrow_descriptor_t self, wasip3_string_t *path, wasip3_string_t *ret, filesystem_error_code_t *err) { + __attribute__((__aligned__(sizeof(void*)))) + uint8_t ret_area[(3*sizeof(void*))]; + uint8_t *ptr = (uint8_t *) &ret_area; + __wasm_import_filesystem_method_descriptor_readlink_at((self).__handle, (uint8_t *) (*path).ptr, (*path).len, ptr); + filesystem_result_string_error_code_t result; + switch ((int32_t) *((uint8_t*) (ptr + 0))) { + case 0: { + result.is_err = false; + result.val.ok = (wasip3_string_t) { (uint8_t*)(*((uint8_t **) (ptr + sizeof(void*)))), (*((size_t*) (ptr + (2*sizeof(void*))))) }; + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) *((uint8_t*) (ptr + sizeof(void*))); + break; + } + } + if (!result.is_err) { + *ret = result.val.ok; + return 1; + } else { + *err = result.val.err; + return 0; + } } bool filesystem_method_descriptor_remove_directory_at(filesystem_borrow_descriptor_t self, wasip3_string_t *path, filesystem_error_code_t *err) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a0116486c..ac1474a35 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -292,7 +292,7 @@ add_wasilibc_test(open_relative_path.c FS ARGV /) add_wasilibc_test(poll.c FS FAILP3) add_wasilibc_test(preadvwritev.c FS FAILP3) add_wasilibc_test(preadwrite.c FS FAILP3) -add_wasilibc_test(readlink.c FS FAILP3) +add_wasilibc_test(readlink.c FS) add_wasilibc_test(readv.c FS) add_wasilibc_test(rename.c FS FAILP3) add_wasilibc_test(rmdir.c FS) From d3367dc27d3d531702c09d48d10014f69c400606 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Feb 2026 17:47:32 -0800 Subject: [PATCH 14/17] wasip3: Implement `renameat` Use the wasip2 implementation --- cmake/bindings.cmake | 1 + .../cloudlibc/src/libc/stdio/renameat.c | 12 ++------ .../headers/public/wasi/__generated_wasip3.h | 8 +---- libc-bottom-half/sources/wasip3.c | 29 ++++++++++++++++--- test/CMakeLists.txt | 2 +- 5 files changed, 30 insertions(+), 22 deletions(-) diff --git a/cmake/bindings.cmake b/cmake/bindings.cmake index f991b6ef5..4702b15d9 100644 --- a/cmake/bindings.cmake +++ b/cmake/bindings.cmake @@ -127,6 +127,7 @@ add_custom_target( "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.symlink-at" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.link-at" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.readlink-at" + "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.rename-at" "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-until" "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-for" ${CMAKE_SOURCE_DIR}/wasi/p3/wit diff --git a/libc-bottom-half/cloudlibc/src/libc/stdio/renameat.c b/libc-bottom-half/cloudlibc/src/libc/stdio/renameat.c index 39d2730ad..0a576ef71 100644 --- a/libc-bottom-half/cloudlibc/src/libc/stdio/renameat.c +++ b/libc-bottom-half/cloudlibc/src/libc/stdio/renameat.c @@ -19,7 +19,7 @@ int __wasilibc_nocwd_renameat(int oldfd, const char *old, int newfd, const char errno = error; return -1; } -#elif defined(__wasip2__) +#elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptors to internal handles filesystem_borrow_descriptor_t old_file_handle; if (fd_to_file_handle(oldfd, &old_file_handle) < 0) @@ -30,7 +30,7 @@ int __wasilibc_nocwd_renameat(int oldfd, const char *old, int newfd, const char return -1; // Convert the strings into WASI strings - wasip2_string_t old_path, new_path; + wasi_string_t old_path, new_path; if (wasi_string_from_c(old, &old_path) < 0) return -1; if (wasi_string_from_c(new, &new_path) < 0) @@ -47,14 +47,6 @@ int __wasilibc_nocwd_renameat(int oldfd, const char *old, int newfd, const char translate_error(error_code); return -1; } -#elif defined(__wasip3__) - (void) oldfd; - (void) old; - (void) newfd; - (void) new; - // TODO(wasip3) - errno = ENOTSUP; - return -1; #else # error "Unknown WASI version" #endif diff --git a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h index b0925c577..1bc42a1f2 100644 --- a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h +++ b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h @@ -586,12 +586,6 @@ typedef struct filesystem_method_descriptor_set_times_at_args { filesystem_new_timestamp_t data_access_timestamp; filesystem_new_timestamp_t data_modification_timestamp; } filesystem_method_descriptor_set_times_at_args_t; -typedef struct filesystem_method_descriptor_rename_at_args { - filesystem_borrow_descriptor_t self; - wasip3_string_t old_path; - filesystem_borrow_descriptor_t new_descriptor; - wasip3_string_t new_path; -} filesystem_method_descriptor_rename_at_args_t; typedef filesystem_own_descriptor_t filesystem_preopens_own_descriptor_t; @@ -1177,7 +1171,7 @@ extern bool filesystem_method_descriptor_remove_directory_at(filesystem_borrow_d // Rename a filesystem object. // // Note: This is similar to `renameat` in POSIX. -extern wasip3_subtask_status_t filesystem_method_descriptor_rename_at(filesystem_method_descriptor_rename_at_args_t *args, filesystem_result_void_error_code_t *result); +extern bool filesystem_method_descriptor_rename_at(filesystem_borrow_descriptor_t self, wasip3_string_t *old_path, filesystem_borrow_descriptor_t new_descriptor, wasip3_string_t *new_path, filesystem_error_code_t *err); // Create a symbolic link (also known as a "symlink"). // // If `old-path` starts with `/`, the function fails with diff --git a/libc-bottom-half/sources/wasip3.c b/libc-bottom-half/sources/wasip3.c index 43554eae4..882683fb6 100644 --- a/libc-bottom-half/sources/wasip3.c +++ b/libc-bottom-half/sources/wasip3.c @@ -130,8 +130,8 @@ extern void __wasm_import_filesystem_method_descriptor_readlink_at(int32_t, uint __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.remove-directory-at"))) extern void __wasm_import_filesystem_method_descriptor_remove_directory_at(int32_t, uint8_t *, size_t, uint8_t *); -__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.rename-at"))) -extern int32_t __wasm_import_filesystem_method_descriptor_rename_at(uint8_t *, uint8_t *); +__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.rename-at"))) +extern void __wasm_import_filesystem_method_descriptor_rename_at(int32_t, uint8_t *, size_t, int32_t, uint8_t *, size_t, uint8_t *); __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.symlink-at"))) extern void __wasm_import_filesystem_method_descriptor_symlink_at(int32_t, uint8_t *, size_t, uint8_t *, size_t, uint8_t *); @@ -1865,8 +1865,29 @@ bool filesystem_method_descriptor_remove_directory_at(filesystem_borrow_descript } } -wasip3_subtask_status_t filesystem_method_descriptor_rename_at(filesystem_method_descriptor_rename_at_args_t *args, filesystem_result_void_error_code_t *result) { - return __wasm_import_filesystem_method_descriptor_rename_at((uint8_t*) args, (uint8_t*) result); +bool filesystem_method_descriptor_rename_at(filesystem_borrow_descriptor_t self, wasip3_string_t *old_path, filesystem_borrow_descriptor_t new_descriptor, wasip3_string_t *new_path, filesystem_error_code_t *err) { + __attribute__((__aligned__(1))) + uint8_t ret_area[2]; + uint8_t *ptr = (uint8_t *) &ret_area; + __wasm_import_filesystem_method_descriptor_rename_at((self).__handle, (uint8_t *) (*old_path).ptr, (*old_path).len, (new_descriptor).__handle, (uint8_t *) (*new_path).ptr, (*new_path).len, ptr); + filesystem_result_void_error_code_t result; + switch ((int32_t) *((uint8_t*) (ptr + 0))) { + case 0: { + result.is_err = false; + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) *((uint8_t*) (ptr + 1)); + break; + } + } + if (!result.is_err) { + return 1; + } else { + *err = result.val.err; + return 0; + } } bool filesystem_method_descriptor_symlink_at(filesystem_borrow_descriptor_t self, wasip3_string_t *old_path, wasip3_string_t *new_path, filesystem_error_code_t *err) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ac1474a35..a02b1041c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -294,7 +294,7 @@ add_wasilibc_test(preadvwritev.c FS FAILP3) add_wasilibc_test(preadwrite.c FS FAILP3) add_wasilibc_test(readlink.c FS) add_wasilibc_test(readv.c FS) -add_wasilibc_test(rename.c FS FAILP3) +add_wasilibc_test(rename.c FS) add_wasilibc_test(rmdir.c FS) add_wasilibc_test(scandir.c FS) add_wasilibc_test(stat.c FS) From aa6688efaf7f885a13e01084c2289afd756526c5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 27 Feb 2026 17:48:42 -0800 Subject: [PATCH 15/17] wasip3: Get `utime`-related functions working Use the wasip2 implementations --- cmake/bindings.cmake | 2 + .../cloudlibc/src/libc/sys/stat/futimens.c | 8 +- .../cloudlibc/src/libc/sys/stat/utimensat.c | 12 +- .../headers/public/wasi/__generated_wasip3.h | 16 +- libc-bottom-half/sources/wasip3.c | 154 +++++++++++++++++- test/CMakeLists.txt | 2 +- 6 files changed, 154 insertions(+), 40 deletions(-) diff --git a/cmake/bindings.cmake b/cmake/bindings.cmake index 4702b15d9..56cecf973 100644 --- a/cmake/bindings.cmake +++ b/cmake/bindings.cmake @@ -128,6 +128,8 @@ add_custom_target( "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.link-at" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.readlink-at" "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.rename-at" + "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.set-times-at" + "--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.set-times" "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-until" "--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-for" ${CMAKE_SOURCE_DIR}/wasi/p3/wit diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/futimens.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/futimens.c index 4311cf36d..ac1daea06 100644 --- a/libc-bottom-half/cloudlibc/src/libc/sys/stat/futimens.c +++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/futimens.c @@ -29,7 +29,7 @@ int futimens(int fd, const struct timespec *times) { errno = error; return -1; } -#elif defined(__wasip2__) +#elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; if (fd_to_file_handle(fd, &file_handle) < 0) @@ -52,12 +52,6 @@ int futimens(int fd, const struct timespec *times) { translate_error(error); return -1; } -#elif defined(__wasip3__) - (void) fd; - (void) times; - // TODO(wasip3) - errno = ENOTSUP; - return -1; #else # error "Unsupported WASI version" #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 index 4be2d0a1e..4b270b5fe 100644 --- a/libc-bottom-half/cloudlibc/src/libc/sys/stat/utimensat.c +++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/utimensat.c @@ -38,7 +38,7 @@ int __wasilibc_nocwd_utimensat(int fd, const char *path, const struct timespec t errno = error; return -1; } -#elif defined(__wasip2__) +#elif defined(__wasip2__) || defined(__wasip3__) // Translate the file descriptor to an internal handle filesystem_borrow_descriptor_t file_handle; if (fd_to_file_handle(fd, &file_handle) < 0) @@ -58,7 +58,7 @@ int __wasilibc_nocwd_utimensat(int fd, const char *path, const struct timespec t lookup_flags |= FILESYSTEM_PATH_FLAGS_SYMLINK_FOLLOW; // Convert the string into a Wasm string - wasip2_string_t path_wasm_string; + wasi_string_t path_wasm_string; if (wasi_string_from_c(path, &path_wasm_string) < 0) return -1; @@ -74,14 +74,6 @@ int __wasilibc_nocwd_utimensat(int fd, const char *path, const struct timespec t translate_error(error); return -1; } -#elif defined(__wasip3__) - (void) fd; - (void) path; - (void) times; - (void) flag; - // TODO(wasip3) - errno = ENOTSUP; - return -1; #else # error "Unsupported WASI version" #endif diff --git a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h index 1bc42a1f2..b956e3e48 100644 --- a/libc-bottom-half/headers/public/wasi/__generated_wasip3.h +++ b/libc-bottom-half/headers/public/wasi/__generated_wasip3.h @@ -574,18 +574,6 @@ typedef struct { filesystem_error_code_t err; } val; } filesystem_result_metadata_hash_value_error_code_t; -typedef struct filesystem_method_descriptor_set_times_args { - filesystem_borrow_descriptor_t self; - filesystem_new_timestamp_t data_access_timestamp; - filesystem_new_timestamp_t data_modification_timestamp; -} filesystem_method_descriptor_set_times_args_t; -typedef struct filesystem_method_descriptor_set_times_at_args { - filesystem_borrow_descriptor_t self; - filesystem_path_flags_t path_flags; - wasip3_string_t path; - filesystem_new_timestamp_t data_access_timestamp; - filesystem_new_timestamp_t data_modification_timestamp; -} filesystem_method_descriptor_set_times_at_args_t; typedef filesystem_own_descriptor_t filesystem_preopens_own_descriptor_t; @@ -1084,7 +1072,7 @@ extern bool filesystem_method_descriptor_set_size(filesystem_borrow_descriptor_t // Note: This is similar to `futimens` in POSIX. // // Note: This was called `fd_filestat_set_times` in earlier versions of WASI. -extern wasip3_subtask_status_t filesystem_method_descriptor_set_times(filesystem_method_descriptor_set_times_args_t *args, filesystem_result_void_error_code_t *result); +extern bool filesystem_method_descriptor_set_times(filesystem_borrow_descriptor_t self, filesystem_new_timestamp_t *data_access_timestamp, filesystem_new_timestamp_t *data_modification_timestamp, filesystem_error_code_t *err); // Read directory entries from a directory. // // On filesystems where directories contain entries referring to themselves @@ -1133,7 +1121,7 @@ extern bool filesystem_method_descriptor_stat_at(filesystem_borrow_descriptor_t // // Note: This was called `path_filestat_set_times` in earlier versions of // WASI. -extern wasip3_subtask_status_t filesystem_method_descriptor_set_times_at(filesystem_method_descriptor_set_times_at_args_t *args, filesystem_result_void_error_code_t *result); +extern bool filesystem_method_descriptor_set_times_at(filesystem_borrow_descriptor_t self, filesystem_path_flags_t path_flags, wasip3_string_t *path, filesystem_new_timestamp_t *data_access_timestamp, filesystem_new_timestamp_t *data_modification_timestamp, filesystem_error_code_t *err); // Create a hard link. // // Fails with `error-code::no-entry` if the old path does not exist, diff --git a/libc-bottom-half/sources/wasip3.c b/libc-bottom-half/sources/wasip3.c index 882683fb6..6dc0f9521 100644 --- a/libc-bottom-half/sources/wasip3.c +++ b/libc-bottom-half/sources/wasip3.c @@ -97,8 +97,8 @@ extern int32_t __wasm_import_filesystem_method_descriptor_get_type(int32_t, uint __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.set-size"))) extern void __wasm_import_filesystem_method_descriptor_set_size(int32_t, int64_t, uint8_t *); -__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.set-times"))) -extern int32_t __wasm_import_filesystem_method_descriptor_set_times(uint8_t *, uint8_t *); +__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.set-times"))) +extern void __wasm_import_filesystem_method_descriptor_set_times(int32_t, int32_t, int64_t, int32_t, int32_t, int64_t, int32_t, uint8_t *); __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.read-directory"))) extern void __wasm_import_filesystem_method_descriptor_read_directory(int32_t, uint8_t *); @@ -115,8 +115,8 @@ extern void __wasm_import_filesystem_method_descriptor_stat(int32_t, uint8_t *); __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.stat-at"))) extern void __wasm_import_filesystem_method_descriptor_stat_at(int32_t, int32_t, uint8_t *, size_t, uint8_t *); -__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[async-lower][method]descriptor.set-times-at"))) -extern int32_t __wasm_import_filesystem_method_descriptor_set_times_at(uint8_t *, uint8_t *); +__attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.set-times-at"))) +extern void __wasm_import_filesystem_method_descriptor_set_times_at(int32_t, int32_t, uint8_t *, size_t, int32_t, int64_t, int32_t, int32_t, int64_t, int32_t, uint8_t *); __attribute__((__import_module__("wasi:filesystem/types@0.3.0-rc-2026-01-06"), __import_name__("[method]descriptor.link-at"))) extern void __wasm_import_filesystem_method_descriptor_link_at(int32_t, int32_t, uint8_t *, size_t, int32_t, uint8_t *, size_t, uint8_t *); @@ -1532,8 +1532,77 @@ bool filesystem_method_descriptor_set_size(filesystem_borrow_descriptor_t self, } } -wasip3_subtask_status_t filesystem_method_descriptor_set_times(filesystem_method_descriptor_set_times_args_t *args, filesystem_result_void_error_code_t *result) { - return __wasm_import_filesystem_method_descriptor_set_times((uint8_t*) args, (uint8_t*) result); +bool filesystem_method_descriptor_set_times(filesystem_borrow_descriptor_t self, filesystem_new_timestamp_t *data_access_timestamp, filesystem_new_timestamp_t *data_modification_timestamp, filesystem_error_code_t *err) { + __attribute__((__aligned__(1))) + uint8_t ret_area[2]; + int32_t variant; + int64_t variant2; + int32_t variant3; + switch ((int32_t) (*data_access_timestamp).tag) { + case 0: { + variant = 0; + variant2 = 0; + variant3 = 0; + break; + } + case 1: { + variant = 1; + variant2 = 0; + variant3 = 0; + break; + } + case 2: { + const filesystem_instant_t *payload1 = &(*data_access_timestamp).val.timestamp; + variant = 2; + variant2 = (*payload1).seconds; + variant3 = (int32_t) ((*payload1).nanoseconds); + break; + } + } + int32_t variant7; + int64_t variant8; + int32_t variant9; + switch ((int32_t) (*data_modification_timestamp).tag) { + case 0: { + variant7 = 0; + variant8 = 0; + variant9 = 0; + break; + } + case 1: { + variant7 = 1; + variant8 = 0; + variant9 = 0; + break; + } + case 2: { + const filesystem_instant_t *payload6 = &(*data_modification_timestamp).val.timestamp; + variant7 = 2; + variant8 = (*payload6).seconds; + variant9 = (int32_t) ((*payload6).nanoseconds); + break; + } + } + uint8_t *ptr = (uint8_t *) &ret_area; + __wasm_import_filesystem_method_descriptor_set_times((self).__handle, variant, variant2, variant3, variant7, variant8, variant9, ptr); + filesystem_result_void_error_code_t result; + switch ((int32_t) *((uint8_t*) (ptr + 0))) { + case 0: { + result.is_err = false; + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) *((uint8_t*) (ptr + 1)); + break; + } + } + if (!result.is_err) { + return 1; + } else { + *err = result.val.err; + return 0; + } } void filesystem_method_descriptor_read_directory(filesystem_borrow_descriptor_t self, filesystem_tuple2_stream_directory_entry_future_result_void_error_code_t *ret) { @@ -1757,8 +1826,77 @@ bool filesystem_method_descriptor_stat_at(filesystem_borrow_descriptor_t self, f } } -wasip3_subtask_status_t filesystem_method_descriptor_set_times_at(filesystem_method_descriptor_set_times_at_args_t *args, filesystem_result_void_error_code_t *result) { - return __wasm_import_filesystem_method_descriptor_set_times_at((uint8_t*) args, (uint8_t*) result); +bool filesystem_method_descriptor_set_times_at(filesystem_borrow_descriptor_t self, filesystem_path_flags_t path_flags, wasip3_string_t *path, filesystem_new_timestamp_t *data_access_timestamp, filesystem_new_timestamp_t *data_modification_timestamp, filesystem_error_code_t *err) { + __attribute__((__aligned__(1))) + uint8_t ret_area[2]; + int32_t variant; + int64_t variant2; + int32_t variant3; + switch ((int32_t) (*data_access_timestamp).tag) { + case 0: { + variant = 0; + variant2 = 0; + variant3 = 0; + break; + } + case 1: { + variant = 1; + variant2 = 0; + variant3 = 0; + break; + } + case 2: { + const filesystem_instant_t *payload1 = &(*data_access_timestamp).val.timestamp; + variant = 2; + variant2 = (*payload1).seconds; + variant3 = (int32_t) ((*payload1).nanoseconds); + break; + } + } + int32_t variant7; + int64_t variant8; + int32_t variant9; + switch ((int32_t) (*data_modification_timestamp).tag) { + case 0: { + variant7 = 0; + variant8 = 0; + variant9 = 0; + break; + } + case 1: { + variant7 = 1; + variant8 = 0; + variant9 = 0; + break; + } + case 2: { + const filesystem_instant_t *payload6 = &(*data_modification_timestamp).val.timestamp; + variant7 = 2; + variant8 = (*payload6).seconds; + variant9 = (int32_t) ((*payload6).nanoseconds); + break; + } + } + uint8_t *ptr = (uint8_t *) &ret_area; + __wasm_import_filesystem_method_descriptor_set_times_at((self).__handle, path_flags, (uint8_t *) (*path).ptr, (*path).len, variant, variant2, variant3, variant7, variant8, variant9, ptr); + filesystem_result_void_error_code_t result; + switch ((int32_t) *((uint8_t*) (ptr + 0))) { + case 0: { + result.is_err = false; + break; + } + case 1: { + result.is_err = true; + result.val.err = (int32_t) *((uint8_t*) (ptr + 1)); + break; + } + } + if (!result.is_err) { + return 1; + } else { + *err = result.val.err; + return 0; + } } bool filesystem_method_descriptor_link_at(filesystem_borrow_descriptor_t self, filesystem_path_flags_t old_path_flags, wasip3_string_t *old_path, filesystem_borrow_descriptor_t new_descriptor, wasip3_string_t *new_path, filesystem_error_code_t *err) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a02b1041c..7727b967a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -306,7 +306,7 @@ add_wasilibc_test(time_and_times.c CFLAGS -D_WASI_EMULATED_PROCESS_CLOCKS LDFLAGS -lwasi-emulated-process-clocks) add_wasilibc_test(time.c) -add_wasilibc_test(utime.c FS FAILP3) +add_wasilibc_test(utime.c FS) add_wasilibc_test(rewinddir.c FS FAILP3) add_wasilibc_test(seekdir.c FS) add_wasilibc_test(usleep.c) From 8ab06211340ebf7ead45685e952d612adb247693 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 2 Mar 2026 18:07:20 -0800 Subject: [PATCH 16/17] wasip3: Fix `rewinddir` test and reading a finished stream Don't re-read a stream once its termination has been received. --- .../cloudlibc/src/libc/dirent/dirent_impl.h | 2 ++ .../cloudlibc/src/libc/dirent/fdopendir.c | 1 + .../cloudlibc/src/libc/dirent/readdir.c | 15 +++++++++------ test/CMakeLists.txt | 2 +- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/dirent_impl.h b/libc-bottom-half/cloudlibc/src/libc/dirent/dirent_impl.h index 0d835c02d..18675a027 100644 --- a/libc-bottom-half/cloudlibc/src/libc/dirent/dirent_impl.h +++ b/libc-bottom-half/cloudlibc/src/libc/dirent/dirent_impl.h @@ -29,6 +29,7 @@ struct _DIR { size_t offset; #elif defined(__wasip3__) filesystem_tuple2_stream_directory_entry_future_result_void_error_code_t stream; + bool stream_done; size_t skip; size_t offset; #else @@ -52,6 +53,7 @@ static inline void dirent_close_streams(DIR *dirp) { if (dirp->stream.f0 != 0) { filesystem_stream_directory_entry_drop_readable(dirp->stream.f0); dirp->stream.f0 = 0; + dirp->stream_done = false; } if (dirp->stream.f1 != 0) { filesystem_future_result_void_error_code_drop_readable(dirp->stream.f1); diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c index edd0476fd..40bec050d 100644 --- a/libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c +++ b/libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c @@ -73,6 +73,7 @@ DIR *fdopendir(int fd) { dirp->stream = result; #elif defined(__wasip3__) filesystem_method_descriptor_read_directory(file_handle, &dirp->stream); + dirp->stream_done = false; #endif dirp->fd = fd; dirp->skip = 0; diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c index 255c0ad99..0d0d1b460 100644 --- a/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c +++ b/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c @@ -234,21 +234,24 @@ static struct dirent *readdir_next(DIR *dirp) { #elif defined(__wasip3__) filesystem_directory_entry_t dir_entry; + // Don't try to keep reading once the stream is closed. + if (dirp->stream_done) + return NULL; + // Loop until at least one stream entry is read, or until the stream is closed. - bool closed = false; - while (1) { + while (!dirp->stream_done) { size_t amount = wasip3_stream_block_on( filesystem_stream_directory_entry_read(dirp->stream.f0, &dir_entry, 1), dirp->stream.f0, - &closed); + &dirp->stream_done); // If something was read, then break out and process that below. if (amount > 0) break; // If nothing was read and the stream isn't finished yet, try again. - if (!closed) + if (!dirp->stream_done) continue; // If the stream's result future hasn't been read yet, do so here. @@ -263,8 +266,8 @@ static struct dirent *readdir_next(DIR *dirp) { translate_error(result.val.err); } - // The stream is closed, so return NULL. This'll set `errno` based on the - // result of the future above. + // The stream is closed, so return NULL. If `errno` needs to be set it'll + // have been done above with `f1`. return NULL; } #else diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7727b967a..513cbeb16 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -307,7 +307,7 @@ add_wasilibc_test(time_and_times.c LDFLAGS -lwasi-emulated-process-clocks) add_wasilibc_test(time.c) add_wasilibc_test(utime.c FS) -add_wasilibc_test(rewinddir.c FS FAILP3) +add_wasilibc_test(rewinddir.c FS) add_wasilibc_test(seekdir.c FS) add_wasilibc_test(usleep.c) add_wasilibc_test(nanosleep.c) From 50171d38276a042a4767d0743f92f7f3e9b72728 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 2 Mar 2026 18:31:02 -0800 Subject: [PATCH 17/17] wasip3: Get pread/pwrite working This is different than wasip2 so needed some custom implementations. --- .../cloudlibc/src/libc/unistd/pread.c | 42 ++++++++++++++++--- .../cloudlibc/src/libc/unistd/pwrite.c | 38 ++++++++++++++--- test/CMakeLists.txt | 4 +- 3 files changed, 70 insertions(+), 14 deletions(-) diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/pread.c b/libc-bottom-half/cloudlibc/src/libc/unistd/pread.c index 8a501889e..b427a164d 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/pread.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/pread.c @@ -12,6 +12,10 @@ #include #endif +#ifdef __wasip3__ +#include +#endif + ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset) { if (offset < 0) { errno = EINVAL; @@ -66,12 +70,38 @@ ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset) { return bytes_read; #elif defined(__wasip3__) - (void) fildes; - (void) buf; - (void) nbyte; - // TODO(wasip3) - errno = ENOTSUP; - return -1; + filesystem_borrow_descriptor_t file_handle; + if (fd_to_file_handle(fildes, &file_handle) < 0) + return -1; + + // Use `read-via-stream` to acquire a stream of data at this specified file + // offset, then issue a read and wait for it to finish. + filesystem_tuple2_stream_u8_future_result_void_error_code_t io; + filesystem_method_descriptor_read_via_stream(file_handle, offset, &io); + bool closed; + size_t ret = wasip3_stream_block_on( + filesystem_stream_u8_read(io.f0, buf, nbyte), + io.f0, + &closed); + filesystem_stream_u8_drop_readable(io.f0); + + // If zero bytes were written then read the provided future to see + // what happened. Otherwise close the future as we won't be reading the + // result. + if (ret == 0) { + filesystem_result_void_error_code_t result; + wasip3_future_block_on( + filesystem_future_result_void_error_code_read(io.f1, &result), + io.f1); + filesystem_future_result_void_error_code_drop_readable(io.f1); + if (result.is_err) { + translate_error(result.val.err); + return -1; + } + } else { + filesystem_future_result_void_error_code_drop_readable(io.f1); + } + return ret; #else # error "Unknown WASI version" #endif diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/pwrite.c b/libc-bottom-half/cloudlibc/src/libc/unistd/pwrite.c index 53dc39f6f..02c07cd2e 100644 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/pwrite.c +++ b/libc-bottom-half/cloudlibc/src/libc/unistd/pwrite.c @@ -11,6 +11,10 @@ #include #endif +#ifdef __wasip3__ +#include +#endif + ssize_t pwrite(int fildes, const void *buf, size_t nbyte, off_t offset) { if (offset < 0) { errno = EINVAL; @@ -62,12 +66,34 @@ ssize_t pwrite(int fildes, const void *buf, size_t nbyte, off_t offset) { return bytes_written; #elif defined(__wasip3__) - (void) fildes; - (void) buf; - (void) nbyte; - // TODO(wasip3) - errno = ENOTSUP; - return -1; + filesystem_borrow_descriptor_t file_handle; + if (fd_to_file_handle(fildes, &file_handle) < 0) + return -1; + + // Create a read/write stream, use `write-via-stream` to start writing, + // then perform the write to see how much was accepted. + filesystem_stream_u8_writer_t writer; + filesystem_stream_u8_t reader = filesystem_stream_u8_new(&writer); + filesystem_result_void_error_code_t result; + wasip3_subtask_status_t subtask_status = + filesystem_method_descriptor_write_via_stream(file_handle, reader, offset, &result); + bool closed; + size_t ret = wasip3_stream_block_on( + filesystem_stream_u8_write(writer, buf, nbyte), + writer, + &closed); + filesystem_stream_u8_drop_writable(writer); + + // Wait for the subtask to resolve now that the writer half is closed and if + // we failed to write bytes (0 bytes written) and the result is an error we + // can return -1. + if (WASIP3_SUBTASK_STATE(subtask_status) != WASIP3_SUBTASK_RETURNED) + wasip3_subtask_block_on_and_drop(WASIP3_SUBTASK_HANDLE(subtask_status)); + if (ret == 0 && result.is_err) { + translate_error(result.val.err); + return -1; + } + return ret; #else # error "Unsupported WASI version" #endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 513cbeb16..764fe5c11 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -290,8 +290,8 @@ add_wasilibc_test(memcmp.c LDFLAGS -Wl,--stack-first -Wl,--initial-memory=327680 add_wasilibc_test(opendir.c FS ARGV /) add_wasilibc_test(open_relative_path.c FS ARGV /) add_wasilibc_test(poll.c FS FAILP3) -add_wasilibc_test(preadvwritev.c FS FAILP3) -add_wasilibc_test(preadwrite.c FS FAILP3) +add_wasilibc_test(preadvwritev.c FS) +add_wasilibc_test(preadwrite.c FS) add_wasilibc_test(readlink.c FS) add_wasilibc_test(readv.c FS) add_wasilibc_test(rename.c FS)