Skip to content

Use index rather than telldir/seekdir to represent fd_readdir cookie #298

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 29, 2025
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
30 changes: 19 additions & 11 deletions src/uvwasi.c
Original file line number Diff line number Diff line change
Expand Up @@ -1396,6 +1396,7 @@ uvwasi_errno_t uvwasi_fd_readdir(uvwasi_t* uvwasi,
#if defined(UVWASI_FD_READDIR_SUPPORTED)
/* TODO(cjihrig): Avoid opening and closing the directory on each call. */
struct uvwasi_fd_wrap_t* wrap;
uvwasi_dircookie_t cur_cookie;
uvwasi_dirent_t dirent;
uv_dirent_t dirents[UVWASI__READDIR_NUM_ENTRIES];
uv_dir_t* dir;
Expand All @@ -1404,7 +1405,6 @@ uvwasi_errno_t uvwasi_fd_readdir(uvwasi_t* uvwasi,
size_t name_len;
size_t available;
size_t size_to_cp;
long tell;
int i;
int r;
#endif /* defined(UVWASI_FD_READDIR_SUPPORTED) */
Expand Down Expand Up @@ -1444,8 +1444,22 @@ uvwasi_errno_t uvwasi_fd_readdir(uvwasi_t* uvwasi,
uv_fs_req_cleanup(&req);

/* Seek to the proper location in the directory. */
if (cookie != UVWASI_DIRCOOKIE_START)
seekdir(dir->dir, cookie);
cur_cookie = 0;
while (cur_cookie < cookie) {
r = uv_fs_readdir(NULL, &req, dir, NULL);
if (r < 0) {
err = uvwasi__translate_uv_error(r);
uv_fs_req_cleanup(&req);
goto exit;
}

cur_cookie += (uvwasi_dircookie_t)r;
uv_fs_req_cleanup(&req);

if (r == 0) {
break;
}
}

/* Read the directory entries into the provided buffer. */
err = UVWASI_ESUCCESS;
Expand All @@ -1460,15 +1474,9 @@ uvwasi_errno_t uvwasi_fd_readdir(uvwasi_t* uvwasi,
available = 0;

for (i = 0; i < r; i++) {
tell = telldir(dir->dir);
if (tell < 0) {
err = uvwasi__translate_uv_error(uv_translate_sys_error(errno));
uv_fs_req_cleanup(&req);
goto exit;
}

cur_cookie++;
name_len = strlen(dirents[i].name);
dirent.d_next = (uvwasi_dircookie_t) tell;
dirent.d_next = (uvwasi_dircookie_t) cur_cookie;
/* TODO(cjihrig): libuv doesn't provide d_ino, and d_type is not
supported on all platforms. Use stat()? */
dirent.d_ino = 0;
Expand Down
101 changes: 101 additions & 0 deletions test/test-fd-readdir-cookie.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#include "test-common.h"
#include "uv.h"
#include "uvwasi.h"
#include "wasi_serdes.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TEST_TMP_DIR "./out/tmp"
#define TEST_PATH_READDIR TEST_TMP_DIR "/test_readdir_cookie"

#if !defined(_WIN32) && !defined(__ANDROID__)
static void touch_file(const char *name) {
uv_fs_t req;
int r;

r = uv_fs_open(NULL, &req, name, O_WRONLY | O_CREAT | O_TRUNC,
S_IWUSR | S_IRUSR, NULL);
uv_fs_req_cleanup(&req);
assert(r >= 0);
r = uv_fs_close(NULL, &req, r, NULL);
uv_fs_req_cleanup(&req);
assert(r == 0);
}
#endif /* !defined(_WIN32) && !defined(__ANDROID__) */

/*
* This is a test case for https://github.com/nodejs/node/issues/47193.
*/

int main(void) {
#if !defined(_WIN32) && !defined(__ANDROID__)
uvwasi_t uvwasi;
uvwasi_options_t init_options;
uvwasi_dircookie_t cookie;
uvwasi_dirent_t dirent;
uvwasi_size_t buf_size;
uvwasi_size_t buf_used;
uvwasi_errno_t err;
uv_fs_t req;
uvwasi_fd_t tmp_fd = 3;
char buf[64];
int r;
int cnt;

setup_test_environment();

r = uv_fs_mkdir(NULL, &req, TEST_TMP_DIR, 0777, NULL);
uv_fs_req_cleanup(&req);
assert(r == 0 || r == UV_EEXIST);

r = uv_fs_mkdir(NULL, &req, TEST_PATH_READDIR, 0777, NULL);
uv_fs_req_cleanup(&req);
assert(r == 0 || r == UV_EEXIST);

for (int i = 0; i < 10; i++) {
const char *format = TEST_PATH_READDIR "/test_file_"
"%d";
int len = strlen(format) + 3;
char file_name[len];
snprintf(file_name, len, format, i);
touch_file(file_name);
}

uvwasi_options_init(&init_options);
init_options.preopenc = 1;
init_options.preopens = calloc(1, sizeof(uvwasi_preopen_t));
init_options.preopens[0].mapped_path = "/var";
init_options.preopens[0].real_path = TEST_PATH_READDIR;

err = uvwasi_init(&uvwasi, &init_options);
assert(err == 0);

buf_size = 64;
memset(buf, 0, buf_size);
buf_used = -1;
cookie = UVWASI_DIRCOOKIE_START;
cnt = 0;

// For simplicity, we read entries one by one
while (buf_used == -1 || buf_used == buf_size) {
memset(buf, 0, buf_size);
err = uvwasi_fd_readdir(&uvwasi, tmp_fd, &buf, buf_size, cookie, &buf_used);
uvwasi_serdes_read_dirent_t(buf, 0, &dirent);
assert(err == UVWASI_ESUCCESS);

cookie = dirent.d_next;
cnt += 1;

// There are only 10 files
assert(cnt <= 10);
}

assert(cnt == 10);
uvwasi_destroy(&uvwasi);
free(init_options.preopens);

#endif /* !defined(_WIN32) && !defined(__ANDROID__) */
return 0;
}
Loading