Skip to content

Commit 58fa18a

Browse files
alexcrichtoncpetig
andauthored
Wasip3: File handing implementation (WebAssembly#757)
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. Note that much of this PR is primarily authored by @cpetig. I've rebased, squashed, and applied my own review comments from WebAssembly#720 to this. Notably some small edge cases here and there are handled and like with other files `wasip{2,3}_file.c` has now simply become `file.c` with both versions living in the same file. Co-authored-by: Christof Petig <christof.petig@arcor.de>
1 parent 3f7371f commit 58fa18a

37 files changed

Lines changed: 624 additions & 281 deletions

cmake/bindings.cmake

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,17 @@ add_custom_target(
107107
--rename wasi:cli/terminal-stdin@${wasip3-version}=terminal_stdin
108108
--rename wasi:cli/terminal-stdout@${wasip3-version}=terminal_stdout
109109
--rename wasi:cli/terminal-stderr@${wasip3-version}=terminal_stderr
110+
111+
# Disable async bindings generation for some functions which are only
112+
# ever called synchronously within libc.
113+
"--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.metadata-hash"
114+
"--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.metadata-hash-at"
115+
"--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.stat"
116+
"--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.get-flags"
117+
"--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.open-at"
118+
"--async=-wasi:filesystem/types@${wasip3-version}#[method]descriptor.read-directory"
119+
"--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-until"
120+
"--async=-wasi:clocks/monotonic-clock@${wasip3-version}#wait-for"
110121
${CMAKE_SOURCE_DIR}/wasi/p3/wit
111122
COMMAND cmake -E copy wasip3.h ${bottom_half}/headers/public/wasi/__generated_wasip3.h
112123
COMMAND cmake -E copy wasip3_component_type.o ${bottom_half}/sources

expected/wasm32-wasip2/defined-symbols.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1494,6 +1494,7 @@ vwprintf
14941494
vwscanf
14951495
wall_clock_now
14961496
wall_clock_resolution
1497+
wasi_string_from_c
14971498
wasip2_list_string_free
14981499
wasip2_list_tuple2_string_string_free
14991500
wasip2_list_u32_free
@@ -1502,7 +1503,6 @@ wasip2_option_string_free
15021503
wasip2_string_dup
15031504
wasip2_string_dup_n
15041505
wasip2_string_free
1505-
wasip2_string_from_c
15061506
wasip2_string_set
15071507
wasip2_tuple2_string_string_free
15081508
wcpcpy

expected/wasm32-wasip3/defined-symbols.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ __wasilibc_fd_renumber
302302
__wasilibc_find_abspath
303303
__wasilibc_find_relpath
304304
__wasilibc_find_relpath_alloc
305+
__wasilibc_future_block_on
305306
__wasilibc_get_environ
306307
__wasilibc_get_service_entry_by_name
307308
__wasilibc_get_service_entry_by_port
@@ -339,6 +340,8 @@ __wasilibc_rmdirat
339340
__wasilibc_sockaddr_to_wasi
340341
__wasilibc_sockaddr_validate
341342
__wasilibc_stat
343+
__wasilibc_stream_block_on
344+
__wasilibc_subtask_block_on_and_drop
342345
__wasilibc_tell
343346
__wasilibc_unlinkat
344347
__wasilibc_unspecified_addr
@@ -1463,6 +1466,7 @@ vswprintf
14631466
vswscanf
14641467
vwprintf
14651468
vwscanf
1469+
wasi_string_from_c
14661470
wasip3_backpressure_dec
14671471
wasip3_backpressure_inc
14681472
wasip3_context_get_0
@@ -1477,7 +1481,6 @@ wasip3_string_dup
14771481
wasip3_string_dup_n
14781482
wasip3_string_free
14791483
wasip3_string_set
1480-
wasip3_subtask_block_on
14811484
wasip3_subtask_cancel
14821485
wasip3_subtask_drop
14831486
wasip3_task_cancel
@@ -1495,7 +1498,6 @@ wasip3_thread_yield_cancellable
14951498
wasip3_thread_yield_to_suspended
14961499
wasip3_thread_yield_to_suspended_cancellable
14971500
wasip3_tuple2_string_string_free
1498-
wasip3_waitable_block_on
14991501
wasip3_waitable_join
15001502
wasip3_waitable_set_drop
15011503
wasip3_waitable_set_new

libc-bottom-half/CMakeLists.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ else()
140140
sources/getsockpeername.c
141141
sources/listen.c
142142
sources/file_utils.c
143+
sources/file.c
143144
sources/recv.c
144145
sources/send.c
145146
sources/shutdown.c
@@ -155,7 +156,6 @@ if(WASI STREQUAL "p2")
155156
list(APPEND bottom_half_sources
156157
sources/netdb.c
157158
sources/wasip2.c
158-
sources/wasip2_file.c
159159
sources/wasip2_stdio.c
160160
)
161161
endif()
@@ -164,7 +164,6 @@ if (WASI STREQUAL "p3")
164164
list(APPEND bottom_half_sources
165165
sources/wasip3.c
166166
sources/wasip3_block_on.c
167-
sources/wasip3_file.c
168167
sources/wasip3_stdio.c
169168
)
170169
endif()

libc-bottom-half/cloudlibc/src/libc/dirent/dirent_impl.h

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ struct _DIR {
2828
size_t skip;
2929
size_t offset;
3030
#elif defined(__wasip3__)
31-
filesystem_stream_directory_entry_t stream;
31+
filesystem_tuple2_stream_directory_entry_future_result_void_error_code_t stream;
3232
size_t skip;
3333
size_t offset;
3434
#else
@@ -40,4 +40,27 @@ struct _DIR {
4040
size_t dirent_size;
4141
};
4242

43+
static inline void dirent_close_streams(DIR *dirp) {
44+
#if defined(__wasip1__)
45+
(void) dirp;
46+
// nothing to close ...
47+
#elif defined(__wasip2__)
48+
if (dirp->stream.__handle) {
49+
filesystem_directory_entry_stream_drop_own(dirp->stream);
50+
dirp->stream.__handle = 0;
51+
}
52+
#elif defined(__wasip3__)
53+
if (dirp->stream.f0 != 0) {
54+
filesystem_stream_directory_entry_drop_readable(dirp->stream.f0);
55+
dirp->stream.f0 = 0;
56+
}
57+
if (dirp->stream.f1 != 0) {
58+
filesystem_future_result_void_error_code_drop_readable(dirp->stream.f1);
59+
dirp->stream.f1 = 0;
60+
}
61+
#else
62+
# error "Unknown WASI version"
63+
#endif
64+
}
65+
4366
#endif

libc-bottom-half/cloudlibc/src/libc/dirent/fdclosedir.c

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,10 @@
99

1010
int fdclosedir(DIR *dirp) {
1111
int fd = dirp->fd;
12-
#if defined(__wasip1__)
12+
#ifdef __wasip1__
1313
free(dirp->buffer);
14-
#elif defined(__wasip2__)
15-
if (dirp->stream.__handle != 0)
16-
filesystem_directory_entry_stream_drop_own(dirp->stream);
17-
#elif defined(__wasip3__)
18-
if (dirp->stream != 0)
19-
filesystem_stream_directory_entry_drop_readable(dirp->stream);
20-
#else
21-
# error "Unsupported WASI version"
2214
#endif
15+
dirent_close_streams(dirp);
2316
free(dirp->dirent);
2417
free(dirp);
2518
return fd;

libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ DIR *fdopendir(int fd) {
4949
dirp->dirent = NULL;
5050
dirp->dirent_size = 1;
5151
return dirp;
52-
#elif defined(__wasip2__)
52+
#elif defined(__wasip2__) || defined(__wasip3__)
5353
// Translate the file descriptor to an internal handle
5454
filesystem_borrow_descriptor_t file_handle;
5555
if (fd_to_file_handle(fd, &file_handle) < 0) {
@@ -58,6 +58,7 @@ DIR *fdopendir(int fd) {
5858
}
5959

6060
// Read the directory
61+
#if defined(__wasip2__)
6162
filesystem_own_directory_entry_stream_t result;
6263
filesystem_error_code_t error_code;
6364
bool ok = filesystem_method_descriptor_read_directory(file_handle,
@@ -69,19 +70,16 @@ DIR *fdopendir(int fd) {
6970
return NULL;
7071
}
7172

72-
dirp->fd = fd;
7373
dirp->stream = result;
74+
#elif defined(__wasip3__)
75+
filesystem_method_descriptor_read_directory(file_handle, &dirp->stream);
76+
#endif
77+
dirp->fd = fd;
7478
dirp->skip = 0;
7579
dirp->offset = 0;
7680
dirp->dirent = NULL;
7781
dirp->dirent_size = 1;
7882
return dirp;
79-
#elif defined(__wasip3__)
80-
(void) fd;
81-
// TODO(wasip3)
82-
errno = ENOTSUP;
83-
free(dirp);
84-
return NULL;
8583
#else
8684
# error "Unsupported WASI version"
8785
#endif

libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,11 @@ static_assert(DT_REG == __WASI_FILETYPE_REGULAR_FILE, "Value mismatch");
3131
static_assert(DT_UNKNOWN == __WASI_FILETYPE_UNKNOWN, "Value mismatch");
3232
#endif
3333

34+
#ifdef __wasip3__
35+
#include <wasi/wasip3_block.h>
36+
#endif
37+
3438
// Grows a buffer to be large enough to hold a certain amount of data.
35-
#ifndef __wasip3__
3639
static struct dirent* grow(struct dirent **buffer, size_t *buffer_size, size_t target_size) {
3740
if (*buffer_size < target_size) {
3841
size_t new_size = *buffer_size;
@@ -48,7 +51,6 @@ static struct dirent* grow(struct dirent **buffer, size_t *buffer_size, size_t t
4851
}
4952
return *buffer;
5053
}
51-
#endif
5254

5355
#if defined(__wasip1__)
5456
struct dirent *readdir(DIR *dirp) {
@@ -144,12 +146,13 @@ struct dirent *readdir(DIR *dirp) {
144146
}
145147
}
146148

147-
#elif defined(__wasip2__)
149+
#elif defined(__wasip2__) || defined(__wasip3__)
148150

149151
static int ensure_has_directory_stream(DIR *dirp, filesystem_borrow_descriptor_t *handle) {
150152
if (fd_to_file_handle(dirp->fd, handle) < 0)
151153
return -1;
152154

155+
#ifdef __wasip2__
153156
if (dirp->stream.__handle != 0)
154157
return 0;
155158

@@ -161,10 +164,15 @@ static int ensure_has_directory_stream(DIR *dirp, filesystem_borrow_descriptor_t
161164
translate_error(error_code);
162165
return -1;
163166
}
167+
#elif defined(__wasip3__)
168+
if (dirp->stream.f0 == 0)
169+
filesystem_method_descriptor_read_directory(*handle, &dirp->stream);
170+
#endif
164171
return 0;
165172
}
166173

167174
static struct dirent *readdir_next(DIR *dirp) {
175+
bool ok;
168176
filesystem_metadata_hash_value_t metadata;
169177
filesystem_error_code_t error_code;
170178
filesystem_borrow_descriptor_t dir_handle;
@@ -178,9 +186,9 @@ static struct dirent *readdir_next(DIR *dirp) {
178186
dirp->offset += 1;
179187
if (grow(&dirp->dirent, &dirp->dirent_size, offsetof(struct dirent, d_name) + 2) == NULL)
180188
return NULL;
181-
bool ok = filesystem_method_descriptor_metadata_hash(dir_handle,
182-
&metadata,
183-
&error_code);
189+
ok = filesystem_method_descriptor_metadata_hash(dir_handle,
190+
&metadata,
191+
&error_code);
184192
if (!ok) {
185193
translate_error(error_code);
186194
return NULL;
@@ -206,11 +214,12 @@ static struct dirent *readdir_next(DIR *dirp) {
206214
return dirp->dirent;
207215
}
208216

217+
#if defined(__wasip2__)
209218
filesystem_borrow_directory_entry_stream_t stream = filesystem_borrow_directory_entry_stream(dirp->stream);
210219
filesystem_option_directory_entry_t dir_entry_optional;
211-
bool ok = filesystem_method_directory_entry_stream_read_directory_entry(stream,
212-
&dir_entry_optional,
213-
&error_code);
220+
ok = filesystem_method_directory_entry_stream_read_directory_entry(stream,
221+
&dir_entry_optional,
222+
&error_code);
214223
if (!ok) {
215224
translate_error(error_code);
216225
return NULL;
@@ -222,10 +231,54 @@ static struct dirent *readdir_next(DIR *dirp) {
222231

223232
filesystem_directory_entry_t dir_entry = dir_entry_optional.val;
224233

234+
#elif defined(__wasip3__)
235+
filesystem_directory_entry_t dir_entry;
236+
237+
// Loop until at least one stream entry is read, or until the stream is closed.
238+
bool closed = false;
239+
while (1) {
240+
size_t amount =
241+
__wasilibc_stream_block_on(
242+
filesystem_stream_directory_entry_read(dirp->stream.f0, &dir_entry, 1),
243+
dirp->stream.f0,
244+
&closed);
245+
246+
// If something was read, then break out and process that below.
247+
if (amount > 0)
248+
break;
249+
250+
// If nothing was read and the stream isn't finished yet, try again.
251+
if (!closed)
252+
continue;
253+
254+
// If the stream's result future hasn't been read yet, do so here.
255+
if (dirp->stream.f1) {
256+
filesystem_result_void_error_code_t result;
257+
__wasilibc_future_block_on(
258+
filesystem_future_result_void_error_code_read(dirp->stream.f1, &result),
259+
dirp->stream.f1);
260+
filesystem_future_result_void_error_code_drop_readable(dirp->stream.f1);
261+
dirp->stream.f1 = 0;
262+
if (result.is_err)
263+
translate_error(result.val.err);
264+
}
265+
266+
// The stream is closed, so return NULL. This'll set `errno` based on the
267+
// result of the future above.
268+
return NULL;
269+
}
270+
#else
271+
#error "Unknown WASI version"
272+
#endif
273+
225274
// Ensure that the dirent is large enough to fit the filename
226275
size_t the_size = offsetof(struct dirent, d_name);
227276
if (grow(&dirp->dirent, &dirp->dirent_size, the_size + dir_entry.name.len + 1) == NULL) {
277+
#ifdef __wasip2__
228278
wasip2_string_free(&dir_entry.name);
279+
#else
280+
wasip3_string_free(&dir_entry.name);
281+
#endif
229282
return NULL;
230283
}
231284

@@ -241,7 +294,11 @@ static struct dirent *readdir_next(DIR *dirp) {
241294
&dir_entry.name,
242295
&metadata,
243296
&error_code);
297+
#ifdef __wasip2__
244298
wasip2_string_free(&dir_entry.name);
299+
#else
300+
wasip3_string_free(&dir_entry.name);
301+
#endif
245302
if (!ok) {
246303
translate_error(error_code);
247304
return NULL;
@@ -260,13 +317,6 @@ struct dirent *readdir(DIR *dirp) {
260317
}
261318
return result;
262319
}
263-
#elif defined(__wasip3__)
264-
struct dirent *readdir(DIR *dirp) {
265-
// TODO(wasip3)
266-
errno = ENOTSUP;
267-
free(dirp);
268-
return NULL;
269-
}
270320
#else
271321
# error "Unknown WASI version"
272322
#endif

libc-bottom-half/cloudlibc/src/libc/dirent/rewinddir.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,8 @@ void rewinddir(DIR *dirp) {
1313
dirp->cookie = __WASI_DIRCOOKIE_START;
1414
// Mark entire buffer as processed to force a read of new data.
1515
dirp->buffer_used = dirp->buffer_processed = dirp->buffer_size;
16-
#elif defined(__wasip2__)
17-
dirp->stream.__handle = 0;
18-
dirp->skip = 0;
19-
dirp->offset = 0;
20-
#elif defined(__wasip3__)
21-
dirp->stream = 0;
16+
#elif defined(__wasip2__) || defined(__wasip3__)
17+
dirent_close_streams(dirp);
2218
dirp->skip = 0;
2319
dirp->offset = 0;
2420
#else

libc-bottom-half/cloudlibc/src/libc/dirent/seekdir.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,8 @@ void seekdir(DIR *dirp, long loc) {
1313
// Mark entire buffer as processed to force a read of new data.
1414
// TODO(ed): We could prevent a read if the offset is in the buffer.
1515
dirp->buffer_used = dirp->buffer_processed = dirp->buffer_size;
16-
#elif defined(__wasip2__)
17-
dirp->stream.__handle = 0;
18-
dirp->skip = loc;
19-
dirp->offset = 0;
20-
#elif defined(__wasip3__)
21-
dirp->stream = 0;
16+
#elif defined(__wasip2__) || defined(__wasip3__)
17+
dirent_close_streams(dirp);
2218
dirp->skip = loc;
2319
dirp->offset = 0;
2420
#else

0 commit comments

Comments
 (0)