1 // Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
3 // SPDX-License-Identifier: BSD-2-Clause
14 #include "dirent_impl.h"
16 static int sel_true(const struct dirent
*de
) {
20 int scandirat(int dirfd
, const char *dir
, struct dirent
***namelist
,
21 int (*sel
)(const struct dirent
*),
22 int (*compar
)(const struct dirent
**, const struct dirent
**)) {
23 // Match all files if no select function is provided.
27 // Open the directory.
28 #ifdef __wasilibc_unmodified_upstream // avoid making a varargs call
29 int fd
= openat(dirfd
, dir
, O_RDONLY
| O_NONBLOCK
| O_DIRECTORY
);
31 int fd
= __wasilibc_openat_nomode(dirfd
, dir
, O_RDONLY
| O_NONBLOCK
| O_DIRECTORY
);
36 // Allocate a read buffer for the directory entries.
37 size_t buffer_size
= DIRENT_DEFAULT_BUFFER_SIZE
;
38 char *buffer
= malloc(buffer_size
);
43 size_t buffer_processed
= buffer_size
;
44 size_t buffer_used
= buffer_size
;
46 // Space for the array to return to the caller.
47 struct dirent
**dirents
= NULL
;
48 size_t dirents_size
= 0;
49 size_t dirents_used
= 0;
51 __wasi_dircookie_t cookie
= __WASI_DIRCOOKIE_START
;
53 // Extract the next dirent header.
54 size_t buffer_left
= buffer_used
- buffer_processed
;
55 if (buffer_left
< sizeof(__wasi_dirent_t
)) {
57 if (buffer_used
< buffer_size
)
61 __wasi_dirent_t entry
;
62 memcpy(&entry
, buffer
+ buffer_processed
, sizeof(entry
));
64 size_t entry_size
= sizeof(__wasi_dirent_t
) + entry
.d_namlen
;
65 if (entry
.d_namlen
== 0) {
66 // Invalid pathname length. Skip the entry.
67 buffer_processed
+= entry_size
;
71 // The entire entry must be present in buffer space. If not, read
72 // the entry another time. Ensure that the read buffer is large
73 // enough to fit at least this single entry.
74 if (buffer_left
< entry_size
) {
75 while (buffer_size
< entry_size
)
77 char *new_buffer
= realloc(buffer
, buffer_size
);
78 if (new_buffer
== NULL
)
84 // Skip entries having null bytes in the filename.
85 const char *name
= buffer
+ buffer_processed
+ sizeof(entry
);
86 buffer_processed
+= entry_size
;
87 if (memchr(name
, '\0', entry
.d_namlen
) != NULL
)
90 // Create the new directory entry.
91 struct dirent
*dirent
=
92 malloc(offsetof(struct dirent
, d_name
) + entry
.d_namlen
+ 1);
95 dirent
->d_ino
= entry
.d_ino
;
96 dirent
->d_type
= entry
.d_type
;
97 memcpy(dirent
->d_name
, name
, entry
.d_namlen
);
98 dirent
->d_name
[entry
.d_namlen
] = '\0';
99 cookie
= entry
.d_next
;
102 // Add the entry to the results.
103 if (dirents_used
== dirents_size
) {
104 dirents_size
= dirents_size
< 8 ? 8 : dirents_size
* 2;
105 struct dirent
**new_dirents
=
106 realloc(dirents
, dirents_size
* sizeof(*dirents
));
107 if (new_dirents
== NULL
) {
111 dirents
= new_dirents
;
113 dirents
[dirents_used
++] = dirent
;
115 // Discard the entry.
121 // Load more directory entries and continue.
122 #ifdef __wasilibc_unmodified_upstream
123 __wasi_errno_t error
= __wasi_file_readdir(fd
, buffer
, buffer_size
,
125 // TODO: Remove the cast on `buffer` once the witx is updated with char8 support.
126 __wasi_errno_t error
= __wasi_fd_readdir(fd
, (uint8_t *)buffer
, buffer_size
,
128 cookie
, &buffer_used
);
133 buffer_processed
= 0;
136 // Sort results and return them.
139 (qsort
)(dirents
, dirents_used
, sizeof(*dirents
),
140 (int (*)(const void *, const void *))compar
);
145 // Deallocate partially created results.
146 for (size_t i
= 0; i
< dirents_used
; ++i
)