Skip to content

Commit 7523499

Browse files
committed
src: improve uvwasi_fd_readdir() implementation
This commit updates the uvwasi_fd_readdir() implementation to work, including a regression test. The function is not supported on Windows and Android yet. Fixes: #148 Fixes: #137
1 parent 76e0196 commit 7523499

5 files changed

Lines changed: 155 additions & 20 deletions

File tree

include/wasi_serdes.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ IOVS_STRUCT(ciovec_t)
103103
#define UVWASI_SERDES_SIZE_iovec_t 8
104104
IOVS_STRUCT(iovec_t)
105105

106+
#define UVWASI_SERDES_SIZE_dirent_t 24
107+
STRUCT(dirent_t)
108+
106109
#define UVWASI_SERDES_SIZE_fdstat_t 24
107110
STRUCT(fdstat_t)
108111

src/uvwasi.c

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313

1414
#define UVWASI__READDIR_NUM_ENTRIES 1
1515

16+
#if !defined(_WIN32) && !defined(__ANDROID__)
17+
# define UVWASI_FD_READDIR_SUPPORTED 1
18+
#endif
19+
1620
#include "uvwasi.h"
1721
#include "uvwasi_alloc.h"
1822
#include "uv.h"
@@ -22,6 +26,7 @@
2226
#include "path_resolver.h"
2327
#include "poll_oneoff.h"
2428
#include "wasi_rights.h"
29+
#include "wasi_serdes.h"
2530
#include "debug.h"
2631

2732
/* IBMi PASE does not support posix_fadvise() */
@@ -1268,7 +1273,7 @@ uvwasi_errno_t uvwasi_fd_readdir(uvwasi_t* uvwasi,
12681273
uvwasi_size_t buf_len,
12691274
uvwasi_dircookie_t cookie,
12701275
uvwasi_size_t* bufused) {
1271-
/* TODO(cjihrig): Support Windows where seekdir() and telldir() are used. */
1276+
#if defined(UVWASI_FD_READDIR_SUPPORTED)
12721277
/* TODO(cjihrig): Avoid opening and closing the directory on each call. */
12731278
struct uvwasi_fd_wrap_t* wrap;
12741279
uvwasi_dirent_t dirent;
@@ -1282,6 +1287,7 @@ uvwasi_errno_t uvwasi_fd_readdir(uvwasi_t* uvwasi,
12821287
long tell;
12831288
int i;
12841289
int r;
1290+
#endif /* defined(UVWASI_FD_READDIR_SUPPORTED) */
12851291

12861292
UVWASI_DEBUG("uvwasi_fd_readdir(uvwasi=%p, fd=%d, buf=%p, buf_len=%d, "
12871293
"cookie=%"PRIu64", bufused=%p)\n",
@@ -1295,6 +1301,7 @@ uvwasi_errno_t uvwasi_fd_readdir(uvwasi_t* uvwasi,
12951301
if (uvwasi == NULL || buf == NULL || bufused == NULL)
12961302
return UVWASI_EINVAL;
12971303

1304+
#if defined(UVWASI_FD_READDIR_SUPPORTED)
12981305
err = uvwasi_fd_table_get(uvwasi->fds,
12991306
fd,
13001307
&wrap,
@@ -1316,12 +1323,9 @@ uvwasi_errno_t uvwasi_fd_readdir(uvwasi_t* uvwasi,
13161323
dir->nentries = UVWASI__READDIR_NUM_ENTRIES;
13171324
uv_fs_req_cleanup(&req);
13181325

1319-
#ifndef _WIN32
1320-
/* TODO(cjihrig): Need a Windows equivalent of this logic. */
13211326
/* Seek to the proper location in the directory. */
13221327
if (cookie != UVWASI_DIRCOOKIE_START)
13231328
seekdir(dir->dir, cookie);
1324-
#endif
13251329

13261330
/* Read the directory entries into the provided buffer. */
13271331
err = UVWASI_ESUCCESS;
@@ -1333,21 +1337,15 @@ uvwasi_errno_t uvwasi_fd_readdir(uvwasi_t* uvwasi,
13331337
goto exit;
13341338
}
13351339

1340+
available = 0;
1341+
13361342
for (i = 0; i < r; i++) {
1337-
/* TODO(cjihrig): This should probably be serialized to the buffer
1338-
consistently across platforms. In other words, d_next should always
1339-
be 8 bytes, d_ino should always be 8 bytes, d_namlen should always be
1340-
4 bytes, and d_type should always be 1 byte. */
1341-
#ifndef _WIN32
13421343
tell = telldir(dir->dir);
13431344
if (tell < 0) {
13441345
err = uvwasi__translate_uv_error(uv_translate_sys_error(errno));
13451346
uv_fs_req_cleanup(&req);
13461347
goto exit;
13471348
}
1348-
#else
1349-
tell = 0; /* TODO(cjihrig): Need to support Windows. */
1350-
#endif /* _WIN32 */
13511349

13521350
name_len = strlen(dirents[i].name);
13531351
dirent.d_next = (uvwasi_dircookie_t) tell;
@@ -1381,21 +1379,24 @@ uvwasi_errno_t uvwasi_fd_readdir(uvwasi_t* uvwasi,
13811379
break;
13821380
}
13831381

1384-
/* Write dirent to the buffer. */
1385-
available = buf_len - *bufused;
1386-
size_to_cp = sizeof(dirent) > available ? available : sizeof(dirent);
1387-
memcpy((char*)buf + *bufused, &dirent, size_to_cp);
1388-
*bufused += size_to_cp;
1389-
/* Write the entry name to the buffer. */
1382+
/* Write dirent to the buffer if it will fit. */
1383+
if (UVWASI_SERDES_SIZE_dirent_t + *bufused > buf_len)
1384+
break;
1385+
1386+
uvwasi_serdes_write_dirent_t(buf, *bufused, &dirent);
1387+
*bufused += UVWASI_SERDES_SIZE_dirent_t;
13901388
available = buf_len - *bufused;
1389+
1390+
/* Write as much of the entry name to the buffer as possible. */
13911391
size_to_cp = name_len > available ? available : name_len;
1392-
memcpy((char*)buf + *bufused, &dirents[i].name, size_to_cp);
1392+
memcpy((char*)buf + *bufused, dirents[i].name, size_to_cp);
13931393
*bufused += size_to_cp;
1394+
available = buf_len - *bufused;
13941395
}
13951396

13961397
uv_fs_req_cleanup(&req);
13971398

1398-
if (*bufused >= buf_len)
1399+
if (available == 0)
13991400
break;
14001401
}
14011402

@@ -1408,6 +1409,10 @@ uvwasi_errno_t uvwasi_fd_readdir(uvwasi_t* uvwasi,
14081409
return uvwasi__translate_uv_error(r);
14091410

14101411
return err;
1412+
#else
1413+
/* TODO(cjihrig): Need a solution for Windows and Android. */
1414+
return UVWASI_ENOSYS;
1415+
#endif /* defined(UVWASI_FD_READDIR_SUPPORTED) */
14111416
}
14121417

14131418

src/wasi_serdes.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,13 @@ uint8_t uvwasi_serdes_read_uint8_t(const void* ptr, size_t offset) {
8484
ALIAS(userdata_t, uint64_t) \
8585
ALIAS(whence_t, uint8_t) \
8686
\
87+
STRUCT(dirent_t) { \
88+
FIELD( 0, dircookie_t, d_next); \
89+
FIELD( 8, inode_t, d_ino); \
90+
FIELD(16, uint32_t, d_namlen); \
91+
FIELD(20, filetype_t, d_type); \
92+
} \
93+
\
8794
STRUCT(fdstat_t) { \
8895
FIELD( 0, filetype_t, fs_filetype); \
8996
FIELD( 2, fdflags_t, fs_flags); \

test/test-ebadf-input-validation.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,12 @@ int main(void) {
4343
CHECK(uvwasi_fd_prestat_dir_name(&uvw, 100, test_str, 10));
4444
CHECK(uvwasi_fd_pwrite(&uvw, 100, &test_ciovec, 2, 10, &test_size));
4545
CHECK(uvwasi_fd_read(&uvw, 100, &test_iovec, 2, &test_size));
46+
#if !defined(_WIN32) && !defined(__ANDROID__)
4647
CHECK(uvwasi_fd_readdir(&uvw, 100, test_void, 3, test_dircookie, &test_size));
48+
#else
49+
assert(UVWASI_ENOSYS ==
50+
uvwasi_fd_readdir(&uvw, 100, test_void, 3, test_dircookie, &test_size));
51+
#endif /* !defined(_WIN32) && !defined(__ANDROID__) */
4752
CHECK(uvwasi_fd_renumber(&uvw, 100, 2));
4853
CHECK(uvwasi_fd_seek(&uvw, 100, 10, UVWASI_WHENCE_CUR, &test_filesize));
4954
CHECK(uvwasi_fd_sync(&uvw, 100));

test/test-fd-readdir.c

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
#include <string.h>
4+
#include "uv.h"
5+
#include "uvwasi.h"
6+
#include "wasi_serdes.h"
7+
8+
#define TEST_TMP_DIR "./out/tmp"
9+
#define TEST_PATH_READDIR TEST_TMP_DIR "/test_readdir"
10+
#define TEST_PATH_FILE_1 TEST_PATH_READDIR "/test_file_1"
11+
#define TEST_PATH_FILE_2 TEST_PATH_READDIR "/test_file_2"
12+
13+
14+
#if !defined(_WIN32) && !defined(__ANDROID__)
15+
static void touch_file(const char* name) {
16+
uv_fs_t req;
17+
int r;
18+
19+
r = uv_fs_open(NULL,
20+
&req,
21+
name,
22+
O_WRONLY | O_CREAT | O_TRUNC,
23+
S_IWUSR | S_IRUSR,
24+
NULL);
25+
uv_fs_req_cleanup(&req);
26+
assert(r >= 0);
27+
r = uv_fs_close(NULL, &req, r, NULL);
28+
uv_fs_req_cleanup(&req);
29+
assert(r == 0);
30+
}
31+
#endif /* !defined(_WIN32) && !defined(__ANDROID__) */
32+
33+
34+
int main(void) {
35+
#if !defined(_WIN32) && !defined(__ANDROID__)
36+
uvwasi_t uvwasi;
37+
uvwasi_options_t init_options;
38+
uvwasi_dircookie_t cookie;
39+
uvwasi_dirent_t dirent;
40+
uvwasi_size_t buf_size;
41+
uvwasi_size_t buf_used;
42+
uvwasi_errno_t err;
43+
uv_fs_t req;
44+
char buf[1024];
45+
char* name;
46+
int r;
47+
48+
r = uv_fs_mkdir(NULL, &req, TEST_TMP_DIR, 0777, NULL);
49+
uv_fs_req_cleanup(&req);
50+
assert(r == 0 || r == UV_EEXIST);
51+
52+
r = uv_fs_mkdir(NULL, &req, TEST_PATH_READDIR, 0777, NULL);
53+
uv_fs_req_cleanup(&req);
54+
assert(r == 0 || r == UV_EEXIST);
55+
56+
touch_file(TEST_PATH_FILE_1);
57+
touch_file(TEST_PATH_FILE_2);
58+
59+
uvwasi_options_init(&init_options);
60+
init_options.preopenc = 1;
61+
init_options.preopens = calloc(1, sizeof(uvwasi_preopen_t));
62+
init_options.preopens[0].mapped_path = "/var";
63+
init_options.preopens[0].real_path = TEST_PATH_READDIR;
64+
65+
err = uvwasi_init(&uvwasi, &init_options);
66+
assert(err == 0);
67+
68+
buf_size = 1024;
69+
70+
/* Verify uvwasi_fd_readdir() behavior with insufficient buffer space. */
71+
memset(buf, 0, buf_size);
72+
buf_used = 0;
73+
cookie = UVWASI_DIRCOOKIE_START;
74+
err = uvwasi_fd_readdir(&uvwasi,
75+
3,
76+
&buf,
77+
UVWASI_SERDES_SIZE_dirent_t + 10,
78+
cookie,
79+
&buf_used);
80+
assert(err == 0);
81+
assert(buf_used == UVWASI_SERDES_SIZE_dirent_t + 10);
82+
uvwasi_serdes_read_dirent_t(buf, 0, &dirent);
83+
assert(dirent.d_ino == 0);
84+
assert(dirent.d_namlen == 11);
85+
assert(dirent.d_type == UVWASI_FILETYPE_REGULAR_FILE);
86+
name = &buf[UVWASI_SERDES_SIZE_dirent_t];
87+
assert(strcmp(name, "test_file_") == 0);
88+
89+
/* Verify uvwasi_fd_readdir() happy path. */
90+
memset(buf, 0, buf_size);
91+
buf_used = 0;
92+
cookie = UVWASI_DIRCOOKIE_START;
93+
err = uvwasi_fd_readdir(&uvwasi, 3, &buf, buf_size, cookie, &buf_used);
94+
assert(err == 0);
95+
assert(buf_used == 2 * (UVWASI_SERDES_SIZE_dirent_t + 11));
96+
uvwasi_serdes_read_dirent_t(buf, 0, &dirent);
97+
assert(dirent.d_ino == 0);
98+
assert(dirent.d_namlen == 11);
99+
assert(dirent.d_type == UVWASI_FILETYPE_REGULAR_FILE);
100+
name = &buf[UVWASI_SERDES_SIZE_dirent_t];
101+
assert(strncmp(name, "test_file_1", 11) == 0 ||
102+
strncmp(name, "test_file_2", 11) == 0);
103+
uvwasi_serdes_read_dirent_t(buf, UVWASI_SERDES_SIZE_dirent_t + 11, &dirent);
104+
assert(dirent.d_ino == 0);
105+
assert(dirent.d_namlen == 11);
106+
assert(dirent.d_type == UVWASI_FILETYPE_REGULAR_FILE);
107+
name = &buf[(2 * UVWASI_SERDES_SIZE_dirent_t) + 11];
108+
assert(strncmp(name, "test_file_1", 11) == 0 ||
109+
strncmp(name, "test_file_2", 11) == 0);
110+
111+
uvwasi_destroy(&uvwasi);
112+
free(init_options.preopens);
113+
#endif /* !defined(_WIN32) && !defined(__ANDROID__) */
114+
return 0;
115+
}

0 commit comments

Comments
 (0)