Skip to content

Commit 392e1f1

Browse files
authored
Use index rather than telldir/seekdir to represent fd_readdir cookie (#298)
* Use index rather than tell/seek to represent reddir cookie * Avoid inifite loop * Avoid inifite loop * Add uv_fs_req_cleanup
1 parent 98da5ad commit 392e1f1

File tree

2 files changed

+120
-11
lines changed

2 files changed

+120
-11
lines changed

src/uvwasi.c

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,6 +1396,7 @@ uvwasi_errno_t uvwasi_fd_readdir(uvwasi_t* uvwasi,
13961396
#if defined(UVWASI_FD_READDIR_SUPPORTED)
13971397
/* TODO(cjihrig): Avoid opening and closing the directory on each call. */
13981398
struct uvwasi_fd_wrap_t* wrap;
1399+
uvwasi_dircookie_t cur_cookie;
13991400
uvwasi_dirent_t dirent;
14001401
uv_dirent_t dirents[UVWASI__READDIR_NUM_ENTRIES];
14011402
uv_dir_t* dir;
@@ -1404,7 +1405,6 @@ uvwasi_errno_t uvwasi_fd_readdir(uvwasi_t* uvwasi,
14041405
size_t name_len;
14051406
size_t available;
14061407
size_t size_to_cp;
1407-
long tell;
14081408
int i;
14091409
int r;
14101410
#endif /* defined(UVWASI_FD_READDIR_SUPPORTED) */
@@ -1444,8 +1444,22 @@ uvwasi_errno_t uvwasi_fd_readdir(uvwasi_t* uvwasi,
14441444
uv_fs_req_cleanup(&req);
14451445

14461446
/* Seek to the proper location in the directory. */
1447-
if (cookie != UVWASI_DIRCOOKIE_START)
1448-
seekdir(dir->dir, cookie);
1447+
cur_cookie = 0;
1448+
while (cur_cookie < cookie) {
1449+
r = uv_fs_readdir(NULL, &req, dir, NULL);
1450+
if (r < 0) {
1451+
err = uvwasi__translate_uv_error(r);
1452+
uv_fs_req_cleanup(&req);
1453+
goto exit;
1454+
}
1455+
1456+
cur_cookie += (uvwasi_dircookie_t)r;
1457+
uv_fs_req_cleanup(&req);
1458+
1459+
if (r == 0) {
1460+
break;
1461+
}
1462+
}
14491463

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

14621476
for (i = 0; i < r; i++) {
1463-
tell = telldir(dir->dir);
1464-
if (tell < 0) {
1465-
err = uvwasi__translate_uv_error(uv_translate_sys_error(errno));
1466-
uv_fs_req_cleanup(&req);
1467-
goto exit;
1468-
}
1469-
1477+
cur_cookie++;
14701478
name_len = strlen(dirents[i].name);
1471-
dirent.d_next = (uvwasi_dircookie_t) tell;
1479+
dirent.d_next = (uvwasi_dircookie_t) cur_cookie;
14721480
/* TODO(cjihrig): libuv doesn't provide d_ino, and d_type is not
14731481
supported on all platforms. Use stat()? */
14741482
dirent.d_ino = 0;

test/test-fd-readdir-cookie.c

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#include "test-common.h"
2+
#include "uv.h"
3+
#include "uvwasi.h"
4+
#include "wasi_serdes.h"
5+
#include <assert.h>
6+
#include <stdio.h>
7+
#include <stdlib.h>
8+
#include <string.h>
9+
10+
#define TEST_TMP_DIR "./out/tmp"
11+
#define TEST_PATH_READDIR TEST_TMP_DIR "/test_readdir_cookie"
12+
13+
#if !defined(_WIN32) && !defined(__ANDROID__)
14+
static void touch_file(const char *name) {
15+
uv_fs_t req;
16+
int r;
17+
18+
r = uv_fs_open(NULL, &req, name, O_WRONLY | O_CREAT | O_TRUNC,
19+
S_IWUSR | S_IRUSR, NULL);
20+
uv_fs_req_cleanup(&req);
21+
assert(r >= 0);
22+
r = uv_fs_close(NULL, &req, r, NULL);
23+
uv_fs_req_cleanup(&req);
24+
assert(r == 0);
25+
}
26+
#endif /* !defined(_WIN32) && !defined(__ANDROID__) */
27+
28+
/*
29+
* This is a test case for https://github.com/nodejs/node/issues/47193.
30+
*/
31+
32+
int main(void) {
33+
#if !defined(_WIN32) && !defined(__ANDROID__)
34+
uvwasi_t uvwasi;
35+
uvwasi_options_t init_options;
36+
uvwasi_dircookie_t cookie;
37+
uvwasi_dirent_t dirent;
38+
uvwasi_size_t buf_size;
39+
uvwasi_size_t buf_used;
40+
uvwasi_errno_t err;
41+
uv_fs_t req;
42+
uvwasi_fd_t tmp_fd = 3;
43+
char buf[64];
44+
int r;
45+
int cnt;
46+
47+
setup_test_environment();
48+
49+
r = uv_fs_mkdir(NULL, &req, TEST_TMP_DIR, 0777, NULL);
50+
uv_fs_req_cleanup(&req);
51+
assert(r == 0 || r == UV_EEXIST);
52+
53+
r = uv_fs_mkdir(NULL, &req, TEST_PATH_READDIR, 0777, NULL);
54+
uv_fs_req_cleanup(&req);
55+
assert(r == 0 || r == UV_EEXIST);
56+
57+
for (int i = 0; i < 10; i++) {
58+
const char *format = TEST_PATH_READDIR "/test_file_"
59+
"%d";
60+
int len = strlen(format) + 3;
61+
char file_name[len];
62+
snprintf(file_name, len, format, i);
63+
touch_file(file_name);
64+
}
65+
66+
uvwasi_options_init(&init_options);
67+
init_options.preopenc = 1;
68+
init_options.preopens = calloc(1, sizeof(uvwasi_preopen_t));
69+
init_options.preopens[0].mapped_path = "/var";
70+
init_options.preopens[0].real_path = TEST_PATH_READDIR;
71+
72+
err = uvwasi_init(&uvwasi, &init_options);
73+
assert(err == 0);
74+
75+
buf_size = 64;
76+
memset(buf, 0, buf_size);
77+
buf_used = -1;
78+
cookie = UVWASI_DIRCOOKIE_START;
79+
cnt = 0;
80+
81+
// For simplicity, we read entries one by one
82+
while (buf_used == -1 || buf_used == buf_size) {
83+
memset(buf, 0, buf_size);
84+
err = uvwasi_fd_readdir(&uvwasi, tmp_fd, &buf, buf_size, cookie, &buf_used);
85+
uvwasi_serdes_read_dirent_t(buf, 0, &dirent);
86+
assert(err == UVWASI_ESUCCESS);
87+
88+
cookie = dirent.d_next;
89+
cnt += 1;
90+
91+
// There are only 10 files
92+
assert(cnt <= 10);
93+
}
94+
95+
assert(cnt == 10);
96+
uvwasi_destroy(&uvwasi);
97+
free(init_options.preopens);
98+
99+
#endif /* !defined(_WIN32) && !defined(__ANDROID__) */
100+
return 0;
101+
}

0 commit comments

Comments
 (0)