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 642400a05..764615f25 100644 --- a/libc-bottom-half/cloudlibc/src/libc/dirent/dirent_impl.h +++ b/libc-bottom-half/cloudlibc/src/libc/dirent/dirent_impl.h @@ -8,6 +8,10 @@ #include #include +#ifdef __wasilibc_use_wasip2 +#include +#endif + struct dirent; #define DIRENT_DEFAULT_BUFFER_SIZE 4096 @@ -15,6 +19,11 @@ struct dirent; 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. @@ -22,6 +31,7 @@ struct _DIR { size_t buffer_processed; size_t buffer_size; size_t buffer_used; +#endif // Object returned by readdir(). struct dirent *dirent; diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/fdclosedir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/fdclosedir.c index cabe70232..912c84c2c 100644 --- a/libc-bottom-half/cloudlibc/src/libc/dirent/fdclosedir.c +++ b/libc-bottom-half/cloudlibc/src/libc/dirent/fdclosedir.c @@ -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; diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c index 0d28888c0..34e983737 100644 --- a/libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c +++ b/libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c @@ -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; } @@ -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 = diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c index 518a92a16..a546d92d4 100644 --- a/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c +++ b/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c @@ -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); @@ -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 (;;) { diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/rewinddir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/rewinddir.c index 8dd58827a..a3fa3d442 100644 --- a/libc-bottom-half/cloudlibc/src/libc/dirent/rewinddir.c +++ b/libc-bottom-half/cloudlibc/src/libc/dirent/rewinddir.c @@ -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 } diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/scandirat.c b/libc-bottom-half/cloudlibc/src/libc/dirent/scandirat.c index 38dfbb673..8301c30d6 100644 --- a/libc-bottom-half/cloudlibc/src/libc/dirent/scandirat.c +++ b/libc-bottom-half/cloudlibc/src/libc/dirent/scandirat.c @@ -25,271 +25,48 @@ static int sel_true(const struct dirent *de) { return 1; } -int __wasilibc_nocwd_scandirat(int dirfd, const char *dir, struct dirent ***namelist, +int __wasilibc_nocwd_scandirat(int dirfd, const char *dir, struct dirent ***res, int (*sel)(const struct dirent *), - int (*compar)(const struct dirent **, const struct dirent **)) { - struct stat statbuf; - - // Match all files if no select function is provided. - if (sel == NULL) - sel = sel_true; - -#ifdef __wasilibc_use_wasip2 - // Open the directory. - int fd = __wasilibc_nocwd_openat_nomode(dirfd, dir, O_RDONLY | O_NONBLOCK | O_DIRECTORY); - if (fd == -1) - return -1; - DIR *dirp = fdopendir(fd); - if (!dirp) { - close(fd); - return -1; - } - fd = dirp->fd; - - // Translate the file descriptor to an internal handle - filesystem_borrow_directory_entry_stream_t stream; - filesystem_borrow_descriptor_t parent_file_handle; - read_directory_state_t state; - if (!fd_to_directory_stream(fd, &stream, &parent_file_handle, &state)) { - errno = EBADF; - close(fd); - return -1; - } - - // Space for the array to return to the caller. - struct dirent **dirents = NULL; - size_t dirents_size = 0; - size_t dirents_used = 0; - - bool ok = true; - filesystem_option_directory_entry_t dir_entry_optional; - filesystem_error_code_t error_code; - bool handle_dot = false; - bool handle_dot_dot = false; - while (true) { - ok = filesystem_method_directory_entry_stream_read_directory_entry(stream, - &dir_entry_optional, - &error_code); - if (!ok) { - translate_error(error_code); - return -1; - } - if (!dir_entry_optional.is_some) { - // All directory entries have been read; handle . and .. - if (!handle_dot && !handle_dot_dot) { - handle_dot = true; - } else if (handle_dot && !handle_dot_dot) { - handle_dot_dot = true; - handle_dot = false; - } else - break; - } - - // Create the new directory entry - size_t name_len = handle_dot ? 1 : handle_dot_dot ? 2 : dir_entry_optional.val.name.len; - struct dirent *dirent = - malloc(offsetof(struct dirent, d_name) + name_len + 1); - if (dirent == NULL) { - errno = EINVAL; - remove_and_drop_directory_stream(fd); - close(fd); - return -1; - } - dirent->d_type = (handle_dot || handle_dot_dot) ? DT_DIR : dir_entry_type_to_d_type(dir_entry_optional.val.type); - if (handle_dot) - memcpy(dirent->d_name, ".", name_len); - else if (handle_dot_dot) - memcpy(dirent->d_name, "..", name_len); - else - memcpy(dirent->d_name, dir_entry_optional.val.name.ptr, name_len); - dirent->d_name[name_len] = '\0'; - - if (!(handle_dot || handle_dot_dot)) { - // Do an `fstatat` to get the inode number. - if (fstatat(fd, dirent->d_name, &statbuf, AT_SYMLINK_NOFOLLOW) != 0) { - errno = EBADF; - remove_and_drop_directory_stream(fd); - close(fd); - return -1; - } - // Fill in the inode. - dirent->d_ino = statbuf.st_ino; - - // In case someone raced with us and replaced the object with this name - // with another of a different type, update the type too. - dirent->d_type = __wasilibc_iftodt(statbuf.st_mode & S_IFMT); - } else { - dirent->d_ino = -1; - } - - if (sel(dirent)) { - // Add the entry to the results - if (dirents_used == dirents_size) { - dirents_size = dirents_size < 8 ? 8 : dirents_size * 2; - struct dirent **new_dirents = - realloc(dirents, dirents_size * sizeof(*dirents)); - if (new_dirents == NULL) { - free(dirent); - free(dirents); - remove_and_drop_directory_stream(fd); - close(fd); - errno = EBADF; + int (*cmp)(const struct dirent **, const struct dirent **)) { + dirfd = openat(dirfd, dir, O_RDONLY | O_DIRECTORY); + if (dirfd < 0) { return -1; - } - dirents = new_dirents; } - dirents[dirents_used++] = dirent; - } else { - // Discard the entry. - free(dirent); - } - } - - // Sort results and return them - remove_and_drop_directory_stream(fd); - (qsort)(dirents, dirents_used, sizeof(*dirents), - (int (*)(const void *, const void *))compar); - *namelist = dirents; - close(fd); - return dirents_used; -#else - // Open the directory. - int fd = __wasilibc_nocwd_openat_nomode(dirfd, dir, O_RDONLY | O_NONBLOCK | O_DIRECTORY); - if (fd == -1) - return -1; - - // Allocate a read buffer for the directory entries. - size_t buffer_size = DIRENT_DEFAULT_BUFFER_SIZE; - char *buffer = malloc(buffer_size); - if (buffer == NULL) { - close(fd); - return -1; - } - size_t buffer_processed = buffer_size; - size_t buffer_used = buffer_size; - - // Space for the array to return to the caller. - struct dirent **dirents = NULL; - size_t dirents_size = 0; - size_t dirents_used = 0; - - __wasi_dircookie_t cookie = __WASI_DIRCOOKIE_START; - for (;;) { - // Extract the next dirent header. - size_t buffer_left = buffer_used - buffer_processed; - if (buffer_left < sizeof(__wasi_dirent_t)) { - // End-of-file. - if (buffer_used < buffer_size) - break; - goto read_entries; - } - __wasi_dirent_t entry; - memcpy(&entry, buffer + buffer_processed, sizeof(entry)); - - size_t entry_size = sizeof(__wasi_dirent_t) + entry.d_namlen; - if (entry.d_namlen == 0) { - // Invalid pathname length. Skip the entry. - buffer_processed += entry_size; - continue; - } - - // The entire entry must be present in buffer space. If not, read - // the entry another time. Ensure that the read buffer is large - // enough to fit at least this single entry. - if (buffer_left < entry_size) { - while (buffer_size < entry_size) - buffer_size *= 2; - char *new_buffer = realloc(buffer, buffer_size); - if (new_buffer == NULL) - goto bad; - buffer = new_buffer; - goto read_entries; - } - - // Skip entries having null bytes in the filename. - const char *name = buffer + buffer_processed + sizeof(entry); - buffer_processed += entry_size; - if (memchr(name, '\0', entry.d_namlen) != NULL) - continue; - - // Create the new directory entry. - struct dirent *dirent = - malloc(offsetof(struct dirent, d_name) + entry.d_namlen + 1); - if (dirent == NULL) - goto bad; - dirent->d_type = entry.d_type; - memcpy(dirent->d_name, name, entry.d_namlen); - dirent->d_name[entry.d_namlen] = '\0'; - - // `fd_readdir` implementations may set the inode field to zero if the - // the inode number is unknown. In that case, do an `fstatat` to get the - // inode number. - off_t d_ino = entry.d_ino; - unsigned char d_type = entry.d_type; - if (d_ino == 0) { - if (fstatat(fd, dirent->d_name, &statbuf, AT_SYMLINK_NOFOLLOW) != 0) { - return -1; - } - - // Fill in the inode. - d_ino = statbuf.st_ino; - - // In case someone raced with us and replaced the object with this name - // with another of a different type, update the type too. - d_type = __wasilibc_iftodt(statbuf.st_mode & S_IFMT); - } - dirent->d_ino = d_ino; - dirent->d_type = d_type; - - cookie = entry.d_next; - - if (sel(dirent)) { - // Add the entry to the results. - if (dirents_used == dirents_size) { - dirents_size = dirents_size < 8 ? 8 : dirents_size * 2; - struct dirent **new_dirents = - realloc(dirents, dirents_size * sizeof(*dirents)); - if (new_dirents == NULL) { - free(dirent); - goto bad; - } - dirents = new_dirents; - } - dirents[dirents_used++] = dirent; - } else { - // Discard the entry. - free(dirent); - } - continue; - - read_entries:; - // Load more directory entries and continue. - // TODO: Remove the cast on `buffer` once the witx is updated with char8 support. - __wasi_errno_t error = __wasi_fd_readdir(fd, (uint8_t *)buffer, buffer_size, - cookie, &buffer_used); - if (error != 0) { - errno = error; - goto bad; - } - buffer_processed = 0; - } - - // Sort results and return them. - free(buffer); - close(fd); - (qsort)(dirents, dirents_used, sizeof(*dirents), - (int (*)(const void *, const void *))compar); - *namelist = dirents; - return dirents_used; - -bad: - // Deallocate partially created results. - for (size_t i = 0; i < dirents_used; ++i) - free(dirents[i]); - free(dirents); - free(buffer); - close(fd); - return -1; -#endif + // Note that below this is a copy of `scandir.c` located in the top-half + // of this library. + DIR *d = fdopendir(dirfd); + struct dirent *de, **names=0, **tmp; + size_t cnt=0, len=0; + int old_errno = errno; + + if (!d) return -1; + + while ((errno=0), (de = readdir(d))) { + if (sel && !sel(de)) continue; + if (cnt >= len) { + len = 2*len+1; + if (len > SIZE_MAX/sizeof *names) break; + tmp = realloc(names, len * sizeof *names); + if (!tmp) break; + names = tmp; + } + size_t size = sizeof(struct dirent) + strlen(de->d_name) + 1; + names[cnt] = malloc(size); + if (!names[cnt]) break; + memcpy(names[cnt++], de, size); + } + + closedir(d); + + if (errno) { + if (names) while (cnt-->0) free(names[cnt]); + free(names); + return -1; + } + errno = old_errno; + + if (cmp) qsort(names, cnt, sizeof *names, (int (*)(const void *, const void *))cmp); + *res = names; + return cnt; } diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/seekdir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/seekdir.c index a865d92c7..72625cc65 100644 --- a/libc-bottom-half/cloudlibc/src/libc/dirent/seekdir.c +++ b/libc-bottom-half/cloudlibc/src/libc/dirent/seekdir.c @@ -7,9 +7,15 @@ #include "dirent_impl.h" void seekdir(DIR *dirp, long loc) { +#if __wasilibc_use_wasip2 + dirp->stream.__handle = 0; + dirp->skip = loc; + dirp->offset = 0; +#else // Update cookie. dirp->cookie = (unsigned 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; +#endif } diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/telldir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/telldir.c index 05687eef8..008448566 100644 --- a/libc-bottom-half/cloudlibc/src/libc/dirent/telldir.c +++ b/libc-bottom-half/cloudlibc/src/libc/dirent/telldir.c @@ -7,5 +7,9 @@ #include "dirent_impl.h" long telldir(DIR *dirp) { +#if __wasilibc_use_wasip2 + return dirp->offset + dirp->skip; +#else return dirp->cookie; +#endif } diff --git a/libc-bottom-half/headers/private/wasi/descriptor_table.h b/libc-bottom-half/headers/private/wasi/descriptor_table.h index 98218d397..5b7a0bbb4 100644 --- a/libc-bottom-half/headers/private/wasi/descriptor_table.h +++ b/libc-bottom-half/headers/private/wasi/descriptor_table.h @@ -108,25 +108,6 @@ typedef struct { udp_socket_state_t state; } udp_socket_t; -typedef enum read_directory_state_t { - DIRECTORY_STATE_FILE, - DIRECTORY_STATE_RETURNED_DOT, - DIRECTORY_STATE_RETURNED_DOT_DOT -} read_directory_state_t; - -typedef struct { -// Stream for contents of directory - filesystem_own_directory_entry_stream_t directory_stream; -// File handle for the directory being listed -// (so that metadata hashes can be accessed). - filesystem_borrow_descriptor_t directory_file_handle; -// State that reflects whether . and .. have been handled yet; -// this is used by readdir() since each call handles a single -// directory entry, and this state needs to be maintained across -// calls - read_directory_state_t directory_state; -} directory_stream_entry_t; - // Stream representing an open file. typedef struct { // File was opened for reading @@ -155,14 +136,12 @@ typedef struct { DESCRIPTOR_TABLE_ENTRY_TCP_SOCKET, DESCRIPTOR_TABLE_ENTRY_UDP_SOCKET, DESCRIPTOR_TABLE_ENTRY_FILE_HANDLE, - DESCRIPTOR_TABLE_ENTRY_DIRECTORY_STREAM, DESCRIPTOR_TABLE_ENTRY_FILE_STREAM } tag; union { tcp_socket_t tcp_socket; udp_socket_t udp_socket; file_t file; - directory_stream_entry_t directory_stream_info; file_stream_t stream; }; } descriptor_table_entry_t; diff --git a/libc-bottom-half/headers/private/wasi/file_utils.h b/libc-bottom-half/headers/private/wasi/file_utils.h index 06857b934..eff29cc40 100644 --- a/libc-bottom-half/headers/private/wasi/file_utils.h +++ b/libc-bottom-half/headers/private/wasi/file_utils.h @@ -37,55 +37,11 @@ static bool fd_to_file_handle_allow_open(int fd, filesystem_borrow_descriptor_t* *result = entry->stream.file_info.file_handle; else if (entry->tag == DESCRIPTOR_TABLE_ENTRY_FILE_HANDLE) *result = entry->file.file_handle; - else if (entry->tag == DESCRIPTOR_TABLE_ENTRY_DIRECTORY_STREAM) - *result = entry->directory_stream_info.directory_file_handle; else return false; return true; } -// Succeed only if fd is bound to a directory stream in the descriptor table -static bool fd_to_directory_stream(int fd, filesystem_borrow_directory_entry_stream_t* result_stream, - filesystem_borrow_descriptor_t* result_fd, - read_directory_state_t* result_state) { - descriptor_table_entry_t *entry = 0; - if (!descriptor_table_get_ref(fd, &entry)) - return false; - if (entry->tag != DESCRIPTOR_TABLE_ENTRY_DIRECTORY_STREAM) { - return false; - } - *result_stream = filesystem_borrow_directory_entry_stream(entry->directory_stream_info.directory_stream); - *result_fd = entry->directory_stream_info.directory_file_handle; - *result_state = entry->directory_stream_info.directory_state; - return true; -} - -// Does nothing if fd is not in the descriptor table or is not bound to a directory stream -static void directory_stream_enter_state(int fd, read_directory_state_t state) { - descriptor_table_entry_t *entry = 0; - if (!descriptor_table_get_ref(fd, &entry)) - return; - - if (entry->tag == DESCRIPTOR_TABLE_ENTRY_DIRECTORY_STREAM) - entry->directory_stream_info.directory_state = state; -} - -// Does nothing if fd is not in the descriptor table or is not bound to a directory stream -static void remove_and_drop_directory_stream(int fd) { - descriptor_table_entry_t *entry = 0; - if (!descriptor_table_get_ref(fd, &entry)) - return; - - if (entry->tag == DESCRIPTOR_TABLE_ENTRY_DIRECTORY_STREAM) { - filesystem_directory_entry_stream_drop_own(entry->directory_stream_info.directory_stream); - filesystem_borrow_descriptor_t file_handle = entry->directory_stream_info.directory_file_handle; - entry->tag = DESCRIPTOR_TABLE_ENTRY_FILE_HANDLE; - entry->file.file_handle = file_handle; - entry->file.readable = true; - entry->file.writable = false; - } -} - // The following three functions are used for lazily initializing stdin/stdout/stderr in // the descriptor table. These file descriptors never need to be removed from the // descriptor table. diff --git a/libc-bottom-half/sources/__wasilibc_fd_renumber.c b/libc-bottom-half/sources/__wasilibc_fd_renumber.c index 1b5592f71..2953bc8c5 100644 --- a/libc-bottom-half/sources/__wasilibc_fd_renumber.c +++ b/libc-bottom-half/sources/__wasilibc_fd_renumber.c @@ -118,9 +118,6 @@ int close(int fd) { case DESCRIPTOR_TABLE_ENTRY_FILE_HANDLE: drop_file_handle(entry.file.file_handle); break; - case DESCRIPTOR_TABLE_ENTRY_DIRECTORY_STREAM: - drop_directory_stream(entry.directory_stream_info.directory_stream); - break; case DESCRIPTOR_TABLE_ENTRY_FILE_STREAM: if (entry.stream.read_pollable.__handle != 0) poll_pollable_drop_own(entry.stream.read_pollable); diff --git a/libc-bottom-half/sources/__wasilibc_rmdirat.c b/libc-bottom-half/sources/__wasilibc_rmdirat.c index ae661d4c5..465b30860 100644 --- a/libc-bottom-half/sources/__wasilibc_rmdirat.c +++ b/libc-bottom-half/sources/__wasilibc_rmdirat.c @@ -19,9 +19,7 @@ int __wasilibc_nocwd___wasilibc_rmdirat(int fd, const char *path) { errno = EBADF; return EBADF; } - if (entry->tag == DESCRIPTOR_TABLE_ENTRY_DIRECTORY_STREAM) - file_handle = entry->directory_stream_info.directory_file_handle; - else if (entry->tag == DESCRIPTOR_TABLE_ENTRY_FILE_HANDLE) + if (entry->tag == DESCRIPTOR_TABLE_ENTRY_FILE_HANDLE) file_handle = entry->file.file_handle; else { errno = EINVAL; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a45eb5549..ce666914c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -304,6 +304,8 @@ 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) +add_wasilibc_test(seekdir.c FS) if (TARGET_TRIPLE MATCHES "-threads") add_wasilibc_test(busywait.c) diff --git a/test/src/rewinddir.c b/test/src/rewinddir.c new file mode 100644 index 000000000..d83bb5733 --- /dev/null +++ b/test/src/rewinddir.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "test.h" + +#define TEST(c) do { \ + errno = 0; \ + if (!(c)) \ + t_error("%s failed (errno = %d)\n", #c, errno); \ +} while(0) + +static int countdir(DIR *dir) +{ + int count = 0; + while (readdir(dir) != NULL) { + int is_error = errno != 0; + TEST(!is_error); + count++; + } + return count; +} + +int main(void) +{ + int fd; + int flags = 0; + + // Create the directory + TEST(mkdir("test", 0755)==0); + TEST((fd = open("test/a", flags | O_RDWR | O_CREAT | O_EXCL, 0600)) > 2); + TEST(write(fd, 0, 1)==1); + close(fd); + TEST((fd = open("test/b", flags | O_RDWR | O_CREAT | O_EXCL, 0600)) > 2); + TEST(write(fd, 0, 1)==1); + close(fd); + TEST((fd = open("test/c", flags | O_RDWR | O_CREAT | O_EXCL, 0600)) > 2); + TEST(write(fd, 0, 1)==1); + close(fd); + + DIR* dir = opendir("test"); + TEST(dir != NULL); + TEST(countdir(dir) == 5); + TEST(countdir(dir) == 0); + rewinddir(dir); + TEST(countdir(dir) == 5); + TEST(countdir(dir) == 0); + + return t_status; +} + diff --git a/test/src/seekdir.c b/test/src/seekdir.c new file mode 100644 index 000000000..9b3a6a2fc --- /dev/null +++ b/test/src/seekdir.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "test.h" + +#define TEST(c) do { \ + errno = 0; \ + if (!(c)) \ + t_error("%s failed (errno = %d)\n", #c, errno); \ +} while(0) + +static int countdir(DIR *dir) +{ + int count = 0; + while (readdir(dir) != NULL) { + int is_error = errno != 0; + TEST(!is_error); + count++; + } + return count; +} + +int main(void) +{ + int fd; + int flags = 0; + + // Create the directory + TEST(mkdir("test", 0755)==0); + TEST((fd = open("test/a", flags | O_RDWR | O_CREAT | O_EXCL, 0600)) > 2); + TEST(write(fd, 0, 1)==1); + close(fd); + TEST((fd = open("test/b", flags | O_RDWR | O_CREAT | O_EXCL, 0600)) > 2); + TEST(write(fd, 0, 1)==1); + close(fd); + TEST((fd = open("test/c", flags | O_RDWR | O_CREAT | O_EXCL, 0600)) > 2); + TEST(write(fd, 0, 1)==1); + close(fd); + + int positions[5]; + int count = 0; + + DIR* dir = opendir("test"); + TEST(dir != NULL); + struct dirent *entry; + + int is_end; + while(1) { + positions[count] = telldir(dir); + if (readdir(dir) == NULL) + break; + count++; + } + TEST(count == 5); + + for (int i = 0; i < count; i++) { + seekdir(dir, positions[i]); + TEST(telldir(dir) == positions[i]); + int c = countdir(dir); + TEST(c == (5 - i)); + } + + return t_status; +} + +