Skip to content

Commit 1101f81

Browse files
authored
wasip2: Flesh out GETFL and SETFL (WebAssembly#668)
This commit implements `fcntl` with `F_GETFL` and `F_SETFL` in WASIp2. Notably blocking flags are now maintained for files to get Python's test suite passing.
1 parent d5b3d56 commit 1101f81

File tree

7 files changed

+153
-60
lines changed

7 files changed

+153
-60
lines changed

libc-bottom-half/cloudlibc/src/libc/fcntl/fcntl.c

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414
#include <stdarg.h>
1515

1616
int fcntl(int fildes, int cmd, ...) {
17+
#ifdef __wasilibc_use_wasip2
18+
descriptor_table_entry_t *entry = descriptor_table_get_ref(fildes);
19+
if (entry == NULL)
20+
return -1;
21+
#endif
1722
switch (cmd) {
1823
case F_GETFD:
1924
// Act as if the close-on-exec flag is always set.
@@ -23,31 +28,11 @@ int fcntl(int fildes, int cmd, ...) {
2328
return 0;
2429
case F_GETFL: {
2530
#ifdef __wasilibc_use_wasip2
26-
// Translate the file descriptor to an internal handle
27-
filesystem_borrow_descriptor_t file_handle;
28-
if (fd_to_file_handle(fildes, &file_handle) < 0)
29-
return -1;
30-
31-
// Get the flags of the descriptor
32-
filesystem_descriptor_flags_t flags;
33-
filesystem_error_code_t error_code;
34-
if (!filesystem_method_descriptor_get_flags(file_handle, &flags, &error_code)) {
35-
translate_error(error_code);
36-
return -1;
37-
}
38-
39-
int oflags = 0;
40-
if (flags & FILESYSTEM_DESCRIPTOR_FLAGS_READ) {
41-
if (flags & FILESYSTEM_DESCRIPTOR_FLAGS_WRITE)
42-
oflags |= O_RDWR;
43-
else
44-
oflags |= O_RDONLY;
45-
} else if (flags & FILESYSTEM_DESCRIPTOR_FLAGS_WRITE)
46-
oflags |= O_WRONLY;
47-
else
48-
oflags |= O_SEARCH;
49-
50-
return oflags;
31+
if (!entry->vtable->fcntl_getfl) {
32+
errno = EINVAL;
33+
return -1;
34+
}
35+
return entry->vtable->fcntl_getfl(entry->data);
5136
#else
5237
// Obtain the flags and the rights of the descriptor.
5338
__wasi_fdstat_t fds;
@@ -81,12 +66,11 @@ int fcntl(int fildes, int cmd, ...) {
8166
va_end(ap);
8267

8368
#ifdef __wasilibc_use_wasip2
84-
// Translate the file descriptor to an internal handle
85-
filesystem_borrow_descriptor_t file_handle;
86-
if (fd_to_file_handle(fildes, &file_handle) < 0)
87-
return -1;
88-
89-
// This is a no-op -- not supported in wasip2
69+
if (!entry->vtable->fcntl_setfl) {
70+
errno = EINVAL;
71+
return -1;
72+
}
73+
return entry->vtable->fcntl_setfl(entry->data, flags);
9074
#else
9175
__wasi_fdflags_t fs_flags = flags & 0xfff;
9276
__wasi_errno_t error =

libc-bottom-half/cloudlibc/src/libc/sys/ioctl/ioctl.c

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,6 @@
1414
#endif
1515

1616
int ioctl(int fildes, int request, ...) {
17-
#ifdef __wasilibc_use_wasip2
18-
descriptor_table_entry_t *entry = descriptor_table_get_ref(fildes);
19-
if (!entry)
20-
return -1;
21-
switch (request) {
22-
case FIONBIO: {
23-
va_list ap;
24-
va_start(ap, request);
25-
bool blocking = *va_arg(ap, const int *) == 0;
26-
va_end(ap);
27-
return entry->vtable->set_blocking(entry->data, blocking);
28-
}
29-
30-
default:
31-
// TODO wasi-sockets: anything else we should support?
32-
errno = EINVAL;
33-
return -1;
34-
}
35-
#endif // __wasilibc_use_wasip2
36-
3717
switch (request) {
3818
case FIONREAD: {
3919
#ifdef __wasilibc_use_wasip2
@@ -87,9 +67,17 @@ int ioctl(int fildes, int request, ...) {
8767
}
8868
case FIONBIO: {
8969
#ifdef __wasilibc_use_wasip2
90-
// wasip2 doesn't support setting the non-blocking flag
91-
errno = ENOTSUP;
92-
return -1;
70+
descriptor_table_entry_t *entry = descriptor_table_get_ref(fildes);
71+
va_list ap;
72+
va_start(ap, request);
73+
bool blocking = *va_arg(ap, const int *) == 0;
74+
va_end(ap);
75+
76+
if (!entry->vtable->set_blocking) {
77+
errno = EINVAL;
78+
return -1;
79+
}
80+
return entry->vtable->set_blocking(entry->data, blocking);
9381
#else
9482
// Obtain the current file descriptor flags.
9583
__wasi_fdstat_t fds;

libc-bottom-half/headers/private/wasi/descriptor_table.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ typedef struct descriptor_vtable_t {
5353
/// closed before deleting the file to ensure there are no open references to
5454
/// it.
5555
void (*close_streams)(void*);
56+
/// Implementation of `fnctl(fd, F_GETFL)`.
57+
int (*fcntl_getfl)(void*);
58+
/// Implementation of `fnctl(fd, F_SETFL)`.
59+
int (*fcntl_setfl)(void*, int);
5660

5761
// =====================================================================
5862
// Sockets-related APIs

libc-bottom-half/sources/wasip2_file.c

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,7 @@ typedef struct {
2424
// and initially set to 0.
2525
streams_own_pollable_t read_pollable;
2626
streams_own_pollable_t write_pollable;
27-
// File was opened for reading
28-
bool readable;
29-
// File was opened for writing
30-
bool writable;
27+
int oflag;
3128
} file_t;
3229

3330
static void file_close_streams(void *data) {
@@ -172,14 +169,60 @@ static off_t file_seek(void *data, off_t offset, int whence) {
172169
return file->offset;
173170
}
174171

172+
static int file_set_blocking(void *data, bool blocking) {
173+
file_t *file = (file_t *)data;
174+
if (blocking)
175+
file->oflag &= ~O_NONBLOCK;
176+
else
177+
file->oflag |= O_NONBLOCK;
178+
return 0;
179+
}
180+
181+
static int file_fcntl_getfl(void *data) {
182+
file_t *file = (file_t *)data;
183+
184+
// Get the flags of the descriptor
185+
filesystem_descriptor_flags_t flags;
186+
filesystem_error_code_t error_code;
187+
188+
filesystem_borrow_descriptor_t file_handle = filesystem_borrow_descriptor(file->file_handle);
189+
if (!filesystem_method_descriptor_get_flags(file_handle, &flags, &error_code)) {
190+
translate_error(error_code);
191+
return -1;
192+
}
193+
194+
int oflags = file->oflag & (O_NONBLOCK | O_APPEND);
195+
if (flags & FILESYSTEM_DESCRIPTOR_FLAGS_READ) {
196+
if (flags & FILESYSTEM_DESCRIPTOR_FLAGS_WRITE)
197+
oflags |= O_RDWR;
198+
else
199+
oflags |= O_RDONLY;
200+
} else if (flags & FILESYSTEM_DESCRIPTOR_FLAGS_WRITE) {
201+
oflags |= O_WRONLY;
202+
} else {
203+
oflags |= O_SEARCH;
204+
}
205+
return oflags;
206+
}
207+
208+
static int file_fcntl_setfl(void *data, int flags) {
209+
file_t *file = (file_t *)data;
210+
flags &= O_NONBLOCK | O_APPEND;
211+
file->oflag = (file->oflag & ~(O_NONBLOCK | O_APPEND)) | flags;
212+
return 0;
213+
}
214+
175215
static descriptor_vtable_t file_vtable = {
176216
.free = file_free,
177217
.get_read_stream = file_get_read_stream,
178218
.get_write_stream = file_get_write_stream,
179219
.get_file = file_get_file,
220+
.set_blocking = file_set_blocking,
180221
.fstat = file_fstat,
181222
.seek = file_seek,
182223
.close_streams = file_close_streams,
224+
.fcntl_getfl = file_fcntl_getfl,
225+
.fcntl_setfl = file_fcntl_setfl,
183226
};
184227

185228
int __wasilibc_add_file(filesystem_own_descriptor_t file_handle, int oflag) {
@@ -191,8 +234,7 @@ int __wasilibc_add_file(filesystem_own_descriptor_t file_handle, int oflag) {
191234
}
192235
assert(file_handle.__handle != 0);
193236
file->file_handle = file_handle;
194-
file->readable = oflag & O_RDONLY;
195-
file->writable = oflag & O_WRONLY;
237+
file->oflag = oflag;
196238

197239
descriptor_table_entry_t entry;
198240
entry.vtable = &file_vtable;

libc-bottom-half/sources/wasip2_stdio.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#ifdef __wasilibc_use_wasip2
22

33
#include <errno.h>
4+
#include <fcntl.h>
45
#include <string.h>
56
#include <wasi/descriptor_table.h>
67
#include <wasi/stdio.h>
@@ -81,11 +82,21 @@ static int stdio_fstat(void *data, struct stat *buf) {
8182
return 0;
8283
}
8384

85+
static int stdio_fcntl_getfl(void *data) {
86+
stdio_t *stdio = (stdio_t *)data;
87+
if (stdio->fd == 0) {
88+
return O_RDONLY;
89+
} else {
90+
return O_WRONLY;
91+
}
92+
}
93+
8494
static descriptor_vtable_t stdio_vtable = {
8595
.free = stdio_free,
8696
.get_read_stream = stdio_get_read_stream,
8797
.get_write_stream = stdio_get_write_stream,
8898
.fstat = stdio_fstat,
99+
.fcntl_getfl = stdio_fcntl_getfl,
89100
};
90101

91102
static int stdio_add(int fd) {

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ add_wasilibc_test(fdatasync.c FS)
275275
add_wasilibc_test(fdopen.c FS)
276276
add_wasilibc_test(feof.c FS)
277277
add_wasilibc_test(file_permissions.c FS)
278+
add_wasilibc_test(file_nonblocking.c FS)
278279
add_wasilibc_test(fseek.c FS)
279280
add_wasilibc_test(fstat.c FS)
280281
add_wasilibc_test(fsync.c FS)

test/src/file_nonblocking.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#include <errno.h>
2+
#include <fcntl.h>
3+
#include <sys/ioctl.h>
4+
#include <stdio.h>
5+
#include <stdlib.h>
6+
#include <string.h>
7+
#include <unistd.h>
8+
#include <sys/stat.h>
9+
#include "test.h"
10+
11+
#define TEST(c) do { \
12+
errno = 0; \
13+
if (!(c)) \
14+
t_error("%s failed (errno = %d)\n", #c, errno); \
15+
} while(0)
16+
17+
int main(void)
18+
{
19+
char tmp[] = "testsuite-XXXXXX";
20+
int fd;
21+
int enabled = 1;
22+
int disabled = 0;
23+
24+
TEST((fd = open(tmp, O_RDWR | O_CREAT, 0600)) > 2);
25+
26+
// O_NONBLOCK
27+
TEST(fcntl(fd, F_GETFL) == O_RDWR);
28+
TEST(ioctl(fd, FIONBIO, &enabled) == 0);
29+
TEST(fcntl(fd, F_GETFL) == (O_RDWR | O_NONBLOCK));
30+
TEST(ioctl(fd, FIONBIO, &disabled) == 0);
31+
TEST(fcntl(fd, F_GETFL) == O_RDWR);
32+
TEST(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
33+
TEST(fcntl(fd, F_GETFL) == (O_RDWR | O_NONBLOCK));
34+
TEST(fcntl(fd, F_SETFL, 0) == 0);
35+
TEST(fcntl(fd, F_GETFL) == O_RDWR);
36+
37+
// O_APPEND
38+
TEST(fcntl(fd, F_SETFL, O_APPEND) == 0);
39+
TEST(fcntl(fd, F_GETFL) == (O_RDWR | O_APPEND));
40+
TEST(fcntl(fd, F_SETFL, O_APPEND | O_NONBLOCK) == 0);
41+
TEST(fcntl(fd, F_GETFL) == (O_RDWR | O_APPEND | O_NONBLOCK));
42+
TEST(fcntl(fd, F_SETFL, 0) == 0);
43+
TEST(fcntl(fd, F_GETFL) == O_RDWR);
44+
45+
TEST(close(fd) == 0);
46+
47+
// stdio - not that wasmtime's implementation for wasip1-threads is
48+
// broken so some tests are skipped there.
49+
TEST(fcntl(0, F_GETFL) == O_RDONLY);
50+
#ifndef __wasm_atomics__
51+
TEST(fcntl(1, F_GETFL) == O_WRONLY);
52+
TEST(fcntl(2, F_GETFL) == O_WRONLY);
53+
#endif
54+
TEST(fcntl(0, F_SETFL, O_APPEND) == -1);
55+
TEST(fcntl(1, F_SETFL, O_APPEND) == -1);
56+
TEST(fcntl(2, F_SETFL, O_APPEND) == -1);
57+
TEST(fcntl(0, F_SETFL, O_NONBLOCK) == -1);
58+
TEST(fcntl(1, F_SETFL, O_NONBLOCK) == -1);
59+
TEST(fcntl(2, F_SETFL, O_NONBLOCK) == -1);
60+
61+
return t_status;
62+
}
63+

0 commit comments

Comments
 (0)