Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions libc-bottom-half/cloudlibc/src/libc/unistd/read.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ ssize_t read(int fildes, void *buf, size_t nbyte) {
size_t bytes_read;
__wasi_errno_t error = __wasi_fd_read(fildes, &iov, 1, &bytes_read);
if (error != 0) {
__wasi_fdstat_t fds;
if ((error == __WASI_ERRNO_BADF || error == ENOTCAPABLE) &&
__wasi_fd_fdstat_get(fildes, &fds) == 0 &&
fds.fs_filetype == __WASI_FILETYPE_DIRECTORY) {
errno = EISDIR;
return -1;
}
errno = error == ENOTCAPABLE ? EBADF : error;
return -1;
}
Expand Down
39 changes: 39 additions & 0 deletions libc-bottom-half/sources/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,29 @@ static void file_free(void *data) {
free(file);
}

// True when `file` refers to a directory. Used to return POSIX EISDIR
// instead of the host's `bad-descriptor`. On wasip2 this runs only after
// `read-via-stream` fails. On wasip3 that call traps, so the check runs
// before opening the stream.
static bool file_is_directory(file_t *file) {
filesystem_error_code_t error;
#ifdef __wasip2__
filesystem_descriptor_type_t ty;
if (!filesystem_method_descriptor_get_type(
filesystem_borrow_descriptor(file->file_handle), &ty, &error))
return false;
return ty == FILESYSTEM_DESCRIPTOR_TYPE_DIRECTORY;
#else
filesystem_descriptor_stat_t st;
if (!filesystem_method_descriptor_stat(
filesystem_borrow_descriptor(file->file_handle), &st, &error))
return false;
bool is_dir = st.type.tag == FILESYSTEM_DESCRIPTOR_TYPE_DIRECTORY;
filesystem_descriptor_stat_free(&st);
return is_dir;
#endif
}

#ifndef __wasip2__
static int file_read_eof(void *data) {
file_t *file = (file_t *)data;
Expand All @@ -94,6 +117,11 @@ static int file_read_eof(void *data) {
filesystem_future_result_void_error_code_drop_readable(file->read_result);
file->read_result = 0;
if (result.is_err) {
if (result.val.err.tag == FILESYSTEM_ERROR_CODE_BAD_DESCRIPTOR &&
file_is_directory(file)) {
errno = EISDIR;
return -1;
}
translate_error(&result.val.err);
return -1;
}
Expand All @@ -115,6 +143,11 @@ static int file_get_read_stream(void *data, wasi_read_t *read) {
filesystem_borrow_descriptor(file->file_handle), file->offset,
&file->read_stream, &error_code);
if (!ok) {
if (error_code == FILESYSTEM_ERROR_CODE_BAD_DESCRIPTOR &&
file_is_directory(file)) {
errno = EISDIR;
return -1;
}
translate_error(&error_code);
return -1;
}
Expand All @@ -123,6 +156,12 @@ static int file_get_read_stream(void *data, wasi_read_t *read) {
read->pollable = &file->read_pollable;
#else
if (!wasip3_io_state_present(&file->read)) {
// wasip3 `read-via-stream` returns a stream, not a result. Wasmtime
// traps with ErrorCode::BadDescriptor on a directory. Check first.
if (file_is_directory(file)) {
errno = EISDIR;
return -1;
}
assert(!file->read_result);
filesystem_tuple2_stream_u8_future_result_void_error_code_t result;
filesystem_method_descriptor_read_via_stream(
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@ add_wasilibc_test(ppoll.c FS)
add_wasilibc_test(pselect.c FS)
add_wasilibc_test(preadvwritev.c FS)
add_wasilibc_test(preadwrite.c FS)
add_wasilibc_test(read-dir-eisdir.c FS)
add_wasilibc_test(readlink.c FS)
add_wasilibc_test(readv.c FS)
add_wasilibc_test(rename.c FS)
Expand Down
42 changes: 42 additions & 0 deletions test/src/read-dir-eisdir.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include "test.h"
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>

#define TEST(c) \
do { \
errno = 0; \
if (!(c)) \
t_error("%s failed (errno = %d)\n", #c, errno); \
} while (0)

// POSIX read() on a directory fd must fail with EISDIR, not EBADF.
// wasip2 read-via-stream fails with bad-descriptor (mapped to EBADF).
// wasip3 read-via-stream traps with BadDescriptor unless we check type first.
int main(void) {
char buf[16];

TEST(mkdir("read-dir-eisdir", 0755) == 0);
int dirfd = open("read-dir-eisdir", O_RDONLY | O_DIRECTORY);
TEST(dirfd > 2);

TEST(read(dirfd, buf, sizeof buf) == -1 && errno == EISDIR);

TEST(close(dirfd) == 0);

// Genuine bad fds stay EBADF; regular files still read.
TEST(read(-1, buf, 1) == -1 && errno == EBADF);

int fd;
TEST((fd = open("read-dir-eisdir/f", O_RDWR | O_CREAT | O_EXCL, 0600)) > 2);
TEST(write(fd, "x", 1) == 1);
TEST(lseek(fd, 0, SEEK_SET) == 0);
TEST(read(fd, buf, 1) == 1);
TEST(buf[0] == 'x');
TEST(close(fd) == 0);
TEST(unlink("read-dir-eisdir/f") == 0);
TEST(rmdir("read-dir-eisdir") == 0);

return t_status;
}