]> git.proxmox.com Git - wasi-libc.git/blob - libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c
2b843457c03789de1c7d10ecf6f1fd53db7d3ce0
[wasi-libc.git] / libc-bottom-half / cloudlibc / src / libc / dirent / readdir.c
1 // Copyright (c) 2015-2017 Nuxi, https://nuxi.nl/
2 //
3 // SPDX-License-Identifier: BSD-2-Clause
4
5 #include <sys/stat.h>
6
7 #include <assert.h>
8 #include <wasi/core.h>
9 #include <dirent.h>
10 #include <errno.h>
11 #include <stddef.h>
12 #include <stdlib.h>
13 #include <string.h>
14
15 #include "dirent_impl.h"
16
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");
24
25 // Grows a buffer to be large enough to hold a certain amount of data.
26 #define GROW(buffer, buffer_size, target_size) \
27 do { \
28 if ((buffer_size) < (target_size)) { \
29 size_t new_size = (buffer_size); \
30 while (new_size < (target_size)) \
31 new_size *= 2; \
32 void *new_buffer = realloc(buffer, new_size); \
33 if (new_buffer == NULL) \
34 return NULL; \
35 (buffer) = new_buffer; \
36 (buffer_size) = new_size; \
37 } \
38 } while (0)
39
40 struct dirent *readdir(DIR *dirp) {
41 for (;;) {
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)) {
45 // End-of-file.
46 if (dirp->buffer_used < dirp->buffer_size)
47 return NULL;
48 goto read_entries;
49 }
50 __wasi_dirent_t entry;
51 memcpy(&entry, dirp->buffer + dirp->buffer_processed, sizeof(entry));
52
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;
57 continue;
58 }
59
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);
65 goto read_entries;
66 }
67
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;
72 continue;
73 }
74
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;
86 return dirent;
87
88 read_entries:
89 // Discard data currently stored in the input buffer.
90 dirp->buffer_used = dirp->buffer_processed = dirp->buffer_size;
91
92 // Load more directory entries and continue.
93 __wasi_errno_t error =
94 #ifdef __wasilibc_unmodified_upstream
95 __wasi_file_readdir(dirp->fd, dirp->buffer, dirp->buffer_size,
96 #else
97 __wasi_fd_readdir(dirp->fd, dirp->buffer, dirp->buffer_size,
98 #endif
99 dirp->cookie, &dirp->buffer_used);
100 if (error != 0) {
101 errno = error;
102 return NULL;
103 }
104 dirp->buffer_processed = 0;
105 }
106 }