Skip to content
Merged
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
10 changes: 10 additions & 0 deletions libc-bottom-half/cloudlibc/src/libc/dirent/dirent_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,30 @@
#include <wasi/api.h>
#include <stddef.h>

#ifdef __wasilibc_use_wasip2
#include <wasi/wasip2.h>
#endif

struct dirent;

#define DIRENT_DEFAULT_BUFFER_SIZE 4096

struct _DIR {
// Directory file descriptor and cookie.
int fd;
#ifdef __wasilibc_use_wasip2
filesystem_own_directory_entry_stream_t stream;
size_t skip;
size_t offset;
#else
__wasi_dircookie_t cookie;

// Read buffer.
char *buffer;
size_t buffer_processed;
size_t buffer_size;
size_t buffer_used;
#endif

// Object returned by readdir().
struct dirent *dirent;
Expand Down
5 changes: 5 additions & 0 deletions libc-bottom-half/cloudlibc/src/libc/dirent/fdclosedir.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@

int fdclosedir(DIR *dirp) {
int fd = dirp->fd;
#ifdef __wasilibc_use_wasip2
if (dirp->stream.__handle != 0)
filesystem_directory_entry_stream_drop_own(dirp->stream);
#else
free(dirp->buffer);
#endif
free(dirp->dirent);
free(dirp);
return fd;
Expand Down
34 changes: 11 additions & 23 deletions libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,12 @@ DIR *fdopendir(int fd) {
DIR *dirp = malloc(sizeof(*dirp));
if (dirp == NULL)
return NULL;
dirp->buffer = malloc(DIRENT_DEFAULT_BUFFER_SIZE);
if (dirp->buffer == NULL) {
free(dirp);
return NULL;
}

#ifdef __wasilibc_use_wasip2

// Translate the file descriptor to an internal handle
filesystem_borrow_descriptor_t file_handle;
if (!fd_to_file_handle(fd, &file_handle)) {
free(dirp);
errno = EBADF;
return NULL;
}
Expand All @@ -41,34 +37,26 @@ DIR *fdopendir(int fd) {
&result,
&error_code);
if (!ok) {
free(dirp->buffer);
free(dirp);
translate_error(error_code);
return NULL;
}

dirp->fd = fd;
// Add an internal handle for the buffer
descriptor_table_entry_t new_entry;
new_entry.tag = DESCRIPTOR_TABLE_ENTRY_DIRECTORY_STREAM;
directory_stream_entry_t stream_info;
stream_info.directory_stream = result;
stream_info.directory_file_handle = file_handle;
stream_info.directory_state = DIRECTORY_STATE_FILE;
new_entry.directory_stream_info = stream_info;
int new_fd = -1;
if (!descriptor_table_update(dirp->fd, new_entry)) {
errno = EBADF;
return NULL;
}
dirp->cookie = __WASI_DIRCOOKIE_START;
dirp->buffer_processed = 0;
dirp->buffer_size = DIRENT_DEFAULT_BUFFER_SIZE;
dirp->stream = result;
dirp->skip = 0;
dirp->offset = 0;
dirp->dirent = NULL;
dirp->dirent_size = 1;
return dirp;

#else
dirp->buffer = malloc(DIRENT_DEFAULT_BUFFER_SIZE);
if (dirp->buffer == NULL) {
free(dirp);
return NULL;
}

// Ensure that this is really a directory by already loading the first
// chunk of data.
__wasi_errno_t error =
Expand Down
163 changes: 96 additions & 67 deletions libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,79 @@ static_assert(DT_UNKNOWN == __WASI_FILETYPE_UNKNOWN, "Value mismatch");
while (new_size < (target_size)) \
new_size *= 2; \
void *new_buffer = realloc(buffer, new_size); \
if (new_buffer == NULL) \
if (new_buffer == NULL) { \
errno = ENOMEM; \
return NULL; \
} \
(buffer) = new_buffer; \
(buffer_size) = new_size; \
} \
} while (0)

#ifdef __wasilibc_use_wasip2
struct dirent *readdir(DIR *dirp) {
// Translate the file descriptor to an internal handle
filesystem_borrow_directory_entry_stream_t stream;
filesystem_borrow_descriptor_t parent_handle;
read_directory_state_t state;
if (!fd_to_directory_stream(dirp->fd, &stream, &parent_handle, &state)) {

static int ensure_has_directory_stream(DIR *dirp, filesystem_borrow_descriptor_t *handle) {
if (!fd_to_file_handle(dirp->fd, handle)) {
errno = EBADF;
return -1;
}

if (dirp->stream.__handle != 0)
return 0;

filesystem_error_code_t error_code;
bool ok = filesystem_method_descriptor_read_directory(*handle,
&dirp->stream,
&error_code);
if (!ok) {
translate_error(error_code);
return -1;
}
return 0;
}

static struct dirent *readdir_next(DIR *dirp) {
filesystem_metadata_hash_value_t metadata;
filesystem_error_code_t error_code;
filesystem_borrow_descriptor_t dir_handle;

if (ensure_has_directory_stream(dirp, &dir_handle) < 0)
return NULL;

// Yield '.' first if the offset is 0. Note that `d_ino` is from the metadata
// hash of the directory itself.
if (dirp->offset == 0) {
dirp->offset += 1;
GROW(dirp->dirent, dirp->dirent_size, offsetof(struct dirent, d_name) + 2);
bool ok = filesystem_method_descriptor_metadata_hash(dir_handle,
&metadata,
&error_code);
if (!ok) {
translate_error(error_code);
return NULL;
}
dirp->dirent->d_ino = metadata.lower;
dirp->dirent->d_type = DT_DIR;
dirp->dirent->d_name[0] = '.';
dirp->dirent->d_name[1] = 0;
return dirp->dirent;
}

// Yield '..' next if the offset is 1. Note that `d_ino` is set to 0 to
// avoid opening the parent directory here.
if (dirp->offset == 1) {
dirp->offset += 1;
GROW(dirp->dirent, dirp->dirent_size, offsetof(struct dirent, d_name) + 3);
dirp->dirent->d_ino = 0;
dirp->dirent->d_type = DT_DIR;
dirp->dirent->d_name[0] = '.';
dirp->dirent->d_name[1] = '.';
dirp->dirent->d_name[2] = 0;
return dirp->dirent;
}

filesystem_borrow_directory_entry_stream_t stream = filesystem_borrow_directory_entry_stream(dirp->stream);
filesystem_option_directory_entry_t dir_entry_optional;
filesystem_error_code_t error_code;
bool ok = filesystem_method_directory_entry_stream_read_directory_entry(stream,
&dir_entry_optional,
&error_code);
Expand All @@ -67,72 +120,48 @@ struct dirent *readdir(DIR *dirp) {
return NULL;
}

bool return_dot = false;
bool return_dot_dot = false;
if (!dir_entry_optional.is_some) {
// End-of-file; check if we should return '.' or '..'
if (state == DIRECTORY_STATE_FILE) {
return_dot = true;
directory_stream_enter_state(dirp->fd, DIRECTORY_STATE_RETURNED_DOT);
}
else if (state == DIRECTORY_STATE_RETURNED_DOT) {
return_dot_dot = true;
directory_stream_enter_state(dirp->fd, DIRECTORY_STATE_RETURNED_DOT_DOT);
}
else {
remove_and_drop_directory_stream(dirp->fd);
return NULL;
}
}
// Reached end-of-directory? Return null.
if (!dir_entry_optional.is_some)
return NULL;

filesystem_directory_entry_t dir_entry = dir_entry_optional.val;

struct dirent *dirent;
if (!(return_dot || return_dot_dot)) {
// Ensure that the dirent is large enough to fit the filename
size_t the_size = offsetof(struct dirent, d_name);
GROW(dirp->dirent, dirp->dirent_size,
the_size + dir_entry.name.len + 1);
dirent = dirp->dirent;
} else {
dirent = malloc(sizeof(dirent));
if (dirent == NULL) {
errno = ENOMEM;
return NULL;
}
size_t the_size = offsetof(struct dirent, d_name);
int name_len = return_dot ? 1 : 2;
int32_t dirent_size = sizeof(dirent);
GROW(dirent, dirent_size, the_size + name_len + 1);
strcpy(dirent->d_name, return_dot ? "." : "..");
dirent->d_type = DT_DIR;
}
// Ensure that the dirent is large enough to fit the filename
size_t the_size = offsetof(struct dirent, d_name);
GROW(dirp->dirent, dirp->dirent_size, the_size + dir_entry.name.len + 1);

// Get the inode number
if (return_dot || return_dot_dot)
dirent->d_ino = -1;
else {
filesystem_path_flags_t path_flags = 0; // Don't follow symlinks
filesystem_metadata_hash_value_t metadata;
wasip2_string_t name_to_use;
ok = filesystem_method_descriptor_metadata_hash_at(parent_handle,
path_flags,
&dir_entry.name,
&metadata,
&error_code);
if (!ok) {
translate_error(error_code);
remove_and_drop_directory_stream(dirp->fd);
return NULL;
}
dirent->d_ino = metadata.lower;
dirent->d_type = dir_entry_type_to_d_type(dir_entry.type);
memcpy(dirent->d_name, dir_entry.name.ptr, dir_entry.name.len);
dirent->d_name[dir_entry.name.len] = '\0';
// Fill out `d_type` and `d_name`
dirp->dirent->d_type = dir_entry_type_to_d_type(dir_entry.type);
memcpy(dirp->dirent->d_name, dir_entry.name.ptr, dir_entry.name.len);
dirp->dirent->d_name[dir_entry.name.len] = '\0';

// Fill out `d_ino` with the metadata hash.
filesystem_path_flags_t path_flags = 0; // Don't follow symlinks
ok = filesystem_method_descriptor_metadata_hash_at(dir_handle,
path_flags,
&dir_entry.name,
&metadata,
&error_code);
wasip2_string_free(&dir_entry.name);
if (!ok) {
translate_error(error_code);
return NULL;
}
dirp->dirent->d_ino = metadata.lower;
dirp->offset += 1;

return dirent;
return dirp->dirent;
}

struct dirent *readdir(DIR *dirp) {
struct dirent *result = readdir_next(dirp);
while (result != NULL && dirp->skip > 0) {
dirp->skip -= 1;
result = readdir_next(dirp);
}
return result;
}

#else
struct dirent *readdir(DIR *dirp) {
for (;;) {
Expand Down
6 changes: 6 additions & 0 deletions libc-bottom-half/cloudlibc/src/libc/dirent/rewinddir.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@
#include "dirent_impl.h"

void rewinddir(DIR *dirp) {
#if __wasilibc_use_wasip2
dirp->stream.__handle = 0;
dirp->skip = 0;
dirp->offset = 0;
#else
// Update cookie.
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;
#endif
}
Loading