| ... | @@ -25,6 +25,7 @@ typedef struct preopen { | ... | @@ -25,6 +25,7 @@ typedef struct preopen { |
| 25 | } preopen; | 25 | } preopen; |
| 26 | | 26 | |
| 27 | /// A simple growable array of `preopen`. | 27 | /// A simple growable array of `preopen`. |
| | 28 | static _Atomic _Bool preopens_populated = false; |
| 28 | static preopen *preopens; | 29 | static preopen *preopens; |
| 29 | static size_t num_preopens; | 30 | static size_t num_preopens; |
| 30 | static size_t preopen_capacity; | 31 | static size_t preopen_capacity; |
| ... | @@ -100,12 +101,9 @@ static const char *strip_prefixes(const char *path) { | ... | @@ -100,12 +101,9 @@ static const char *strip_prefixes(const char *path) { |
| 100 | return path; | 101 | return path; |
| 101 | } | 102 | } |
| 102 | | 103 | |
| 103 | /// Register the given preopened file descriptor under the given path. | 104 | /// Similar to `internal_register_preopened_fd_unlocked` but does not |
| 104 | /// | 105 | /// take a lock. |
| 105 | /// This function takes ownership of `prefix`. | 106 | static int internal_register_preopened_fd_unlocked(__wasi_fd_t fd, const char *relprefix) { |
| 106 | static int internal_register_preopened_fd(__wasi_fd_t fd, const char *relprefix) { | | |
| 107 | LOCK(lock); | | |
| 108 | | | |
| 109 | // Check preconditions. | 107 | // Check preconditions. |
| 110 | assert_invariants(); | 108 | assert_invariants(); |
| 111 | assert(fd != AT_FDCWD); | 109 | assert(fd != AT_FDCWD); |
| ... | @@ -113,22 +111,32 @@ static int internal_register_preopened_fd(__wasi_fd_t fd, const char *relprefix) | ... | @@ -113,22 +111,32 @@ static int internal_register_preopened_fd(__wasi_fd_t fd, const char *relprefix) |
| 113 | assert(relprefix != NULL); | 111 | assert(relprefix != NULL); |
| 114 | | 112 | |
| 115 | if (num_preopens == preopen_capacity && resize() != 0) { | 113 | if (num_preopens == preopen_capacity && resize() != 0) { |
| 116 | UNLOCK(lock); | | |
| 117 | return -1; | 114 | return -1; |
| 118 | } | 115 | } |
| 119 | | 116 | |
| 120 | char *prefix = strdup(strip_prefixes(relprefix)); | 117 | char *prefix = strdup(strip_prefixes(relprefix)); |
| 121 | if (prefix == NULL) { | 118 | if (prefix == NULL) { |
| 122 | UNLOCK(lock); | | |
| 123 | return -1; | 119 | return -1; |
| 124 | } | 120 | } |
| 125 | preopens[num_preopens++] = (preopen) { prefix, fd, }; | 121 | preopens[num_preopens++] = (preopen) { prefix, fd, }; |
| 126 | | 122 | |
| 127 | assert_invariants(); | 123 | assert_invariants(); |
| 128 | UNLOCK(lock); | | |
| 129 | return 0; | 124 | return 0; |
| 130 | } | 125 | } |
| 131 | | 126 | |
| | 127 | /// Register the given preopened file descriptor under the given path. |
| | 128 | /// |
| | 129 | /// This function takes ownership of `prefix`. |
| | 130 | static int internal_register_preopened_fd(__wasi_fd_t fd, const char *relprefix) { |
| | 131 | LOCK(lock); |
| | 132 | |
| | 133 | int r = internal_register_preopened_fd_unlocked(fd, relprefix); |
| | 134 | |
| | 135 | UNLOCK(lock); |
| | 136 | |
| | 137 | return r; |
| | 138 | } |
| | 139 | |
| 132 | /// Are the `prefix_len` bytes pointed to by `prefix` a prefix of `path`? | 140 | /// Are the `prefix_len` bytes pointed to by `prefix` a prefix of `path`? |
| 133 | static bool prefix_matches(const char *prefix, size_t prefix_len, const char *path) { | 141 | static bool prefix_matches(const char *prefix, size_t prefix_len, const char *path) { |
| 134 | // Allow an empty string as a prefix of any relative path. | 142 | // Allow an empty string as a prefix of any relative path. |
| ... | @@ -152,6 +160,8 @@ static bool prefix_matches(const char *prefix, size_t prefix_len, const char *pa | ... | @@ -152,6 +160,8 @@ static bool prefix_matches(const char *prefix, size_t prefix_len, const char *pa |
| 152 | | 160 | |
| 153 | // See the documentation in libc.h | 161 | // See the documentation in libc.h |
| 154 | int __wasilibc_register_preopened_fd(int fd, const char *prefix) { | 162 | int __wasilibc_register_preopened_fd(int fd, const char *prefix) { |
| | 163 | __wasilibc_populate_preopens(); |
| | 164 | |
| 155 | return internal_register_preopened_fd((__wasi_fd_t)fd, prefix); | 165 | return internal_register_preopened_fd((__wasi_fd_t)fd, prefix); |
| 156 | } | 166 | } |
| 157 | | 167 | |
| ... | @@ -172,6 +182,8 @@ int __wasilibc_find_relpath(const char *path, | ... | @@ -172,6 +182,8 @@ int __wasilibc_find_relpath(const char *path, |
| 172 | int __wasilibc_find_abspath(const char *path, | 182 | int __wasilibc_find_abspath(const char *path, |
| 173 | const char **abs_prefix, | 183 | const char **abs_prefix, |
| 174 | const char **relative_path) { | 184 | const char **relative_path) { |
| | 185 | __wasilibc_populate_preopens(); |
| | 186 | |
| 175 | // Strip leading `/` characters, the prefixes we're mataching won't have | 187 | // Strip leading `/` characters, the prefixes we're mataching won't have |
| 176 | // them. | 188 | // them. |
| 177 | while (*path == '/') | 189 | while (*path == '/') |
| ... | @@ -218,3 +230,84 @@ int __wasilibc_find_abspath(const char *path, | ... | @@ -218,3 +230,84 @@ int __wasilibc_find_abspath(const char *path, |
| 218 | *relative_path = computed; | 230 | *relative_path = computed; |
| 219 | return fd; | 231 | return fd; |
| 220 | } | 232 | } |
| | 233 | |
| | 234 | __attribute__((constructor(51))) |
| | 235 | void __wasilibc_populate_preopens(void) { |
| | 236 | // Fast path: If the preopens are already initialized, do nothing. |
| | 237 | if (preopens_populated) { |
| | 238 | return; |
| | 239 | } |
| | 240 | |
| | 241 | LOCK(lock); |
| | 242 | |
| | 243 | // Check whether another thread initialized the preopens already. |
| | 244 | if (preopens_populated) { |
| | 245 | UNLOCK(lock); |
| | 246 | return; |
| | 247 | } |
| | 248 | |
| | 249 | // Skip stdin, stdout, and stderr, and count up until we reach an invalid |
| | 250 | // file descriptor. |
| | 251 | for (__wasi_fd_t fd = 3; fd != 0; ++fd) { |
| | 252 | __wasi_prestat_t prestat; |
| | 253 | __wasi_errno_t ret = __wasi_fd_prestat_get(fd, &prestat); |
| | 254 | if (ret == __WASI_ERRNO_BADF) |
| | 255 | break; |
| | 256 | if (ret != __WASI_ERRNO_SUCCESS) |
| | 257 | goto oserr; |
| | 258 | switch (prestat.tag) { |
| | 259 | case __WASI_PREOPENTYPE_DIR: { |
| | 260 | char *prefix = malloc(prestat.u.dir.pr_name_len + 1); |
| | 261 | if (prefix == NULL) |
| | 262 | goto software; |
| | 263 | |
| | 264 | // TODO: Remove the cast on `prefix` once the witx is updated with |
| | 265 | // char8 support. |
| | 266 | ret = __wasi_fd_prestat_dir_name(fd, (uint8_t *)prefix, |
| | 267 | prestat.u.dir.pr_name_len); |
| | 268 | if (ret != __WASI_ERRNO_SUCCESS) |
| | 269 | goto oserr; |
| | 270 | prefix[prestat.u.dir.pr_name_len] = '\0'; |
| | 271 | |
| | 272 | if (internal_register_preopened_fd_unlocked(fd, prefix) != 0) |
| | 273 | goto software; |
| | 274 | free(prefix); |
| | 275 | |
| | 276 | break; |
| | 277 | } |
| | 278 | default: |
| | 279 | break; |
| | 280 | } |
| | 281 | } |
| | 282 | |
| | 283 | // Preopens are now initialized. |
| | 284 | preopens_populated = true; |
| | 285 | |
| | 286 | UNLOCK(lock); |
| | 287 | |
| | 288 | return; |
| | 289 | oserr: |
| | 290 | _Exit(EX_OSERR); |
| | 291 | software: |
| | 292 | _Exit(EX_SOFTWARE); |
| | 293 | } |
| | 294 | |
| | 295 | void __wasilibc_reset_preopens(void) { |
| | 296 | LOCK(lock); |
| | 297 | |
| | 298 | if (num_preopens) { |
| | 299 | for (int i = 0; i < num_preopens; ++i) { |
| | 300 | free((void*) preopens[i].prefix); |
| | 301 | } |
| | 302 | free(preopens); |
| | 303 | } |
| | 304 | |
| | 305 | preopens_populated = false; |
| | 306 | preopens = NULL; |
| | 307 | num_preopens = 0; |
| | 308 | preopen_capacity = 0; |
| | 309 | |
| | 310 | assert_invariants(); |
| | 311 | |
| | 312 | UNLOCK(lock); |
| | 313 | } |