1 // Copyright (c) 2015-2017 Nuxi, https://nuxi.nl/
3 // SPDX-License-Identifier: BSD-2-Clause
15 #include "dirent_impl.h"
17 static_assert(DT_BLK
== __WASI_FILETYPE_BLOCK_DEVICE
, "Value mismatch");
18 static_assert(DT_CHR
== __WASI_FILETYPE_CHARACTER_DEVICE
, "Value mismatch");
19 static_assert(DT_DIR
== __WASI_FILETYPE_DIRECTORY
, "Value mismatch");
20 static_assert(DT_FIFO
== __WASI_FILETYPE_SOCKET_STREAM
, "Value mismatch");
21 static_assert(DT_LNK
== __WASI_FILETYPE_SYMBOLIC_LINK
, "Value mismatch");
22 static_assert(DT_REG
== __WASI_FILETYPE_REGULAR_FILE
, "Value mismatch");
23 static_assert(DT_UNKNOWN
== __WASI_FILETYPE_UNKNOWN
, "Value mismatch");
25 // Grows a buffer to be large enough to hold a certain amount of data.
26 #define GROW(buffer, buffer_size, target_size) \
28 if ((buffer_size) < (target_size)) { \
29 size_t new_size = (buffer_size); \
30 while (new_size < (target_size)) \
32 void *new_buffer = realloc(buffer, new_size); \
33 if (new_buffer == NULL) \
35 (buffer) = new_buffer; \
36 (buffer_size) = new_size; \
40 struct dirent
*readdir(DIR *dirp
) {
42 // Extract the next dirent header.
43 size_t buffer_left
= dirp
->buffer_used
- dirp
->buffer_processed
;
44 if (buffer_left
< sizeof(__wasi_dirent_t
)) {
46 if (dirp
->buffer_used
< dirp
->buffer_size
)
50 __wasi_dirent_t entry
;
51 memcpy(&entry
, dirp
->buffer
+ dirp
->buffer_processed
, sizeof(entry
));
53 size_t entry_size
= sizeof(__wasi_dirent_t
) + entry
.d_namlen
;
54 if (entry
.d_namlen
== 0) {
55 // Invalid pathname length. Skip the entry.
56 dirp
->buffer_processed
+= entry_size
;
60 // The entire entry must be present in buffer space. If not, read
61 // the entry another time. Ensure that the read buffer is large
62 // enough to fit at least this single entry.
63 if (buffer_left
< entry_size
) {
64 GROW(dirp
->buffer
, dirp
->buffer_size
, entry_size
);
68 // Skip entries having null bytes in the filename.
69 const char *name
= dirp
->buffer
+ dirp
->buffer_processed
+ sizeof(entry
);
70 if (memchr(name
, '\0', entry
.d_namlen
) != NULL
) {
71 dirp
->buffer_processed
+= entry_size
;
75 // Return the next directory entry. Ensure that the dirent is large
76 // enough to fit the filename.
77 GROW(dirp
->dirent
, dirp
->dirent_size
,
78 offsetof(struct dirent
, d_name
) + entry
.d_namlen
+ 1);
79 struct dirent
*dirent
= dirp
->dirent
;
80 dirent
->d_ino
= entry
.d_ino
;
81 dirent
->d_type
= entry
.d_type
;
82 memcpy(dirent
->d_name
, name
, entry
.d_namlen
);
83 dirent
->d_name
[entry
.d_namlen
] = '\0';
84 dirp
->cookie
= entry
.d_next
;
85 dirp
->buffer_processed
+= entry_size
;
89 // Discard data currently stored in the input buffer.
90 dirp
->buffer_used
= dirp
->buffer_processed
= dirp
->buffer_size
;
92 // Load more directory entries and continue.
93 __wasi_errno_t error
=
94 // TODO: Remove the cast on `dirp->buffer` once the witx is updated with char8 support.
95 __wasi_fd_readdir(dirp
->fd
, (uint8_t *)dirp
->buffer
, dirp
->buffer_size
,
96 dirp
->cookie
, &dirp
->buffer_used
);
101 dirp
->buffer_processed
= 0;