]> git.proxmox.com Git - wasi-libc.git/blob - libc-bottom-half/libpreopen/lib/po_libc_wrappers.c
Rename __wasilibc_rmfileat to __wasilibc_unlinkat.
[wasi-libc.git] / libc-bottom-half / libpreopen / lib / po_libc_wrappers.c
1 /*-
2 * Copyright (c) 2016 Stanley Uche Godfrey
3 * Copyright (c) 2018 Jonathan Anderson
4 * All rights reserved.
5 *
6 * This software was developed at Memorial University under the
7 * NSERC Discovery program (RGPIN-2015-06048).
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 /**
33 * @file po_libc_wrappers.c
34 * @brief Wrappers of libc functions that access global variables.
35 */
36
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <sys/un.h>
41
42 #include <fcntl.h>
43 #ifdef __wasilibc_unmodified_upstream // dlfcn
44 #include <dlfcn.h>
45 #endif
46 #include <stdarg.h>
47 #include <stdlib.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <unistd.h>
51 #ifdef __wasilibc_unmodified_upstream
52 #else
53 #include <errno.h>
54 #include <wasi/libc.h>
55 #endif
56
57 #include "internal.h"
58 #ifdef __wasilibc_unmodified_upstream
59 #else
60 // Make all of the po_* implementation details private.
61 #include "libpreopen.c"
62 #include "po_map.c"
63 #include "po_err.c"
64 #endif
65
66 /**
67 * A default po_map that can be used implicitly by libc wrappers.
68 *
69 * @internal
70 */
71 static struct po_map *global_map;
72
73 /**
74 * Find a relative path within the po_map given by SHARED_MEMORYFD (if it
75 * exists).
76 *
77 * @returns a struct po_relpath with dirfd and relative_path as set by po_find
78 * if there is an available po_map, or AT_FDCWD/path otherwise
79 */
80 #ifdef __wasilibc_unmodified_upstream
81 static struct po_relpath find_relative(const char *path, cap_rights_t *);
82 #else
83 static struct po_relpath find_relative(const char *path,
84 __wasi_rights_t rights_base,
85 __wasi_rights_t rights_inheriting);
86 #endif
87
88 /**
89 * Get the map that was handed into the process via `SHARED_MEMORYFD`
90 * (if it exists).
91 */
92 static struct po_map* get_shared_map(void);
93
94
95 /*
96 * Wrappers around system calls:
97 */
98
99 /**
100 * Capability-safe wrapper around the `_open(2)` system call.
101 *
102 * `_open(2)` accepts a path argument that can reference the global filesystem
103 * namespace. This is not a capability-safe operation, so this wrapper function
104 * attempts to look up the path (or a prefix of it) within the current global
105 * po_map and converts the call into the capability-safe `openat(2)` if
106 * possible. If the current po_map does not contain the sought-after path,
107 * this wrapper will call `openat(AT_FDCWD, original_path, ...)`, which is
108 * the same as the unwrapped `open(2)` call (i.e., will fail with `ECAPMODE`).
109 */
110 int
111 #ifdef __wasilibc_unmodified_upstream
112 _open(const char *path, int flags, ...)
113 #else
114 /* We just need a plain open definition. */
115 open(const char *path, int flags, ...)
116 #endif
117 {
118 struct po_relpath rel;
119 va_list args;
120 int mode;
121
122 #ifdef __wasilibc_unmodified_upstream
123 va_start(args, flags);
124 mode = va_arg(args, int);
125 rel = find_relative(path, NULL);
126 #else
127 if (flags & O_CREAT) {
128 va_start(args, flags);
129 mode = va_arg(args, int);
130 va_end(args);
131 } else {
132 mode = 0;
133 }
134 rel = find_relative(path, __WASI_RIGHT_PATH_OPEN, 0);
135
136 // If we can't find a preopened directory handle to open this file with,
137 // indicate that the program lacks the capabilities.
138 if (rel.dirfd == -1) {
139 errno = ENOTCAPABLE;
140 return -1;
141 }
142 #endif
143
144 #ifdef __wasilibc_unmodified_upstream
145 // If the file is already opened, no need of relative opening!
146 if( strcmp(rel.relative_path,".") == 0 )
147 return dup(rel.dirfd);
148 else
149 return openat(rel.dirfd, rel.relative_path, flags, mode);
150 #else
151 return openat(rel.dirfd, rel.relative_path, flags, mode);
152 #endif
153 }
154
155 /**
156 * Capability-safe wrapper around the `access(2)` system call.
157 *
158 * `access(2)` accepts a path argument that can reference the global filesystem
159 * namespace. This is not a capability-safe operation, so this wrapper function
160 * attempts to look up the path (or a prefix of it) within the current global
161 * po_map and converts the call into the capability-safe `faccessat(2)` if
162 * possible. If the current po_map does not contain the sought-after path,
163 * this wrapper will call `faccessat(AT_FDCWD, original_path, ...)`, which is
164 * the same as the unwrapped `access(2)` call (i.e., will fail with `ECAPMODE`).
165 */
166 int
167 access(const char *path, int mode)
168 {
169 #ifdef __wasilibc_unmodified_upstream
170 struct po_relpath rel = find_relative(path, NULL);
171 #else
172 struct po_relpath rel = find_relative(path, __WASI_RIGHT_PATH_FILESTAT_GET, 0);
173
174 // If we can't find a preopened directory handle to open this file with,
175 // indicate that the program lacks the capabilities.
176 if (rel.dirfd == -1) {
177 errno = ENOTCAPABLE;
178 return -1;
179 }
180 #endif
181
182 return faccessat(rel.dirfd, rel.relative_path, mode,0);
183 }
184
185 #ifdef __wasilibc_unmodified_upstream
186 /**
187 * Capability-safe wrapper around the `connect(2)` system call.
188 *
189 * `connect(2)` accepts a path argument that can reference the global filesystem
190 * namespace. This is not a capability-safe operation, so this wrapper function
191 * attempts to look up the path (or a prefix of it) within the current global
192 * po_map and converts the call into the capability-safe `connectat(2)` if
193 * possible. If the current po_map does not contain the sought-after path, this
194 * wrapper will call `connectat(AT_FDCWD, original_path, ...)`, which is the
195 * same as the unwrapped `connect(2)` call (i.e., will fail with `ECAPMODE`).
196 */
197 int
198 connect(int s, const struct sockaddr *name, socklen_t namelen)
199 {
200 struct po_relpath rel;
201
202 if (name->sa_family == AF_UNIX) {
203 struct sockaddr_un *usock = (struct sockaddr_un *)name;
204 #ifdef __wasilibc_unmodified_upstream
205 rel = find_relative(usock->sun_path, NULL);
206 strlcpy(usock->sun_path, rel.relative_path, sizeof(usock->sun_path));
207 #else
208 rel = find_relative(usock->sun_path, __WASI_RIGHT_CONNECT, 0);
209 if (strlen(rel.relative_path) + 1 > sizeof(usock->sun_path)) {
210 errno = ENOMEM;
211 return -1;
212 }
213
214 // If we can't find a preopened directory handle to open this file with,
215 // indicate that the program lacks the capabilities.
216 if (rel.dirfd == -1) {
217 errno = ENOTCAPABLE;
218 return -1;
219 }
220
221 strcpy(usock->sun_path, rel.relative_path);
222 #endif
223 return connectat(rel.dirfd, s, name, namelen);
224 }
225
226 return connectat(AT_FDCWD, s, name, namelen);
227 }
228 #endif
229
230 /**
231 * Capability-safe wrapper around the `eaccess(2)` system call.
232 *
233 * `eaccess(2)` accepts a path argument that can reference the global filesystem
234 * namespace. This is not a capability-safe operation, so this wrapper function
235 * attempts to look up the path (or a prefix of it) within the current global
236 * po_map and converts the call into the capability-safe `faccessat(2)` if
237 * possible. If the current po_map does not contain the sought-after path, this
238 * wrapper will call `faccessat(AT_FDCWD, original_path, ...)`, which is the
239 * same as the unwrapped `eaccess(2)` call (i.e., will fail with `ECAPMODE`).
240 */
241 int
242 eaccess(const char *path, int mode)
243 {
244 #ifdef __wasilibc_unmodified_upstream
245 struct po_relpath rel = find_relative(path, NULL);
246 #else
247 struct po_relpath rel = find_relative(path, __WASI_RIGHT_PATH_FILESTAT_GET, 0);
248
249 // If we can't find a preopened directory handle to open this file with,
250 // indicate that the program lacks the capabilities.
251 if (rel.dirfd == -1) {
252 errno = ENOTCAPABLE;
253 return -1;
254 }
255 #endif
256
257 return faccessat(rel.dirfd, rel.relative_path, mode, 0);
258 }
259
260 /**
261 * Capability-safe wrapper around the `lstat(2)` system call.
262 *
263 * `lstat(2)` accepts a path argument that can reference the global filesystem
264 * namespace. This is not a capability-safe operation, so this wrapper function
265 * attempts to look up the path (or a prefix of it) within the current global
266 * po_map and converts the call into the capability-safe `fstatat(2)` if
267 * possible. If the current po_map does not contain the sought-after path,
268 * this wrapper will call `fstatat(AT_FDCWD, original_path, ...)`, which is
269 * the same as the unwrapped `lstat(2)` call (i.e., will fail with `ECAPMODE`).
270 */
271 int
272 lstat(const char *path, struct stat *st)
273 {
274 #ifdef __wasilibc_unmodified_upstream
275 struct po_relpath rel = find_relative(path, NULL);
276 #else
277 struct po_relpath rel = find_relative(path, __WASI_RIGHT_PATH_FILESTAT_GET, 0);
278
279 // If we can't find a preopened directory handle to open this file with,
280 // indicate that the program lacks the capabilities.
281 if (rel.dirfd == -1) {
282 errno = ENOTCAPABLE;
283 return -1;
284 }
285 #endif
286
287 return fstatat(rel.dirfd, rel.relative_path,st,AT_SYMLINK_NOFOLLOW);
288 }
289
290 #ifdef __wasilibc_unmodified_upstream
291 /**
292 * Capability-safe wrapper around the `open(2)` system call.
293 *
294 * `open(2)` will behave just like `_open(2)` if the varargs are unpacked and
295 * passed.
296 */
297 int
298 open(const char *path, int flags, ...)
299 {
300 va_list args;
301 int mode;
302
303 va_start(args, flags);
304 mode = va_arg(args, int);
305 return _open(path, flags, mode);
306 }
307 #endif
308
309 /**
310 * Capability-safe wrapper around the `rename(2)` system call.
311 *
312 * `rename(2)` accepts a path argument that can reference the global filesystem
313 * namespace. This is not a capability-safe operation, so this wrapper function
314 * attempts to look up the path (or a prefix of it) within the current global
315 * po_map and converts the call into the capability-safe `renameat(2)` if
316 * possible. If the current po_map does not contain the sought-after path,
317 * this wrapper will call `renameat(AT_FDCWD, original_path, ...)`, which is
318 * the same as the unwrapped `rename(2)` call (i.e., will fail with `ECAPMODE`).
319 */
320 int
321 rename(const char *from, const char *to)
322 {
323 #ifdef __wasilibc_unmodified_upstream
324 struct po_relpath rel_from = find_relative(from, NULL);
325 struct po_relpath rel_to = find_relative(to, NULL);
326 #else
327 struct po_relpath rel_from = find_relative(from, __WASI_RIGHT_PATH_RENAME_SOURCE, 0);
328 struct po_relpath rel_to = find_relative(to, __WASI_RIGHT_PATH_RENAME_TARGET, 0);
329
330 // If we can't find a preopened directory handle to open this file with,
331 // indicate that the program lacks the capabilities.
332 if (rel_from.dirfd == -1 || rel_to.dirfd == -1) {
333 errno = ENOTCAPABLE;
334 return -1;
335 }
336 #endif
337
338 return renameat(rel_from.dirfd, rel_from.relative_path, rel_to.dirfd,
339 rel_to.relative_path);
340 }
341
342 /**
343 * Capability-safe wrapper around the `stat(2)` system call.
344 *
345 * `stat(2)` accepts a path argument that can reference the global filesystem
346 * namespace. This is not a capability-safe operation, so this wrapper function
347 * attempts to look up the path (or a prefix of it) within the current global
348 * po_map and converts the call into the capability-safe `fstatat(2)` if
349 * possible. If the current po_map does not contain the sought-after path,
350 * this wrapper will call `fstatat(AT_FDCWD, original_path, ...)`, which is
351 * the same as the unwrapped `stat(2)` call (i.e., will fail with `ECAPMODE`).
352 */
353 int
354 stat(const char *path, struct stat *st)
355 {
356 #ifdef __wasilibc_unmodified_upstream
357 struct po_relpath rel = find_relative(path, NULL);
358 #else
359 struct po_relpath rel = find_relative(path, __WASI_RIGHT_PATH_FILESTAT_GET, 0);
360
361 // If we can't find a preopened directory handle to open this file with,
362 // indicate that the program lacks the capabilities.
363 if (rel.dirfd == -1) {
364 errno = ENOTCAPABLE;
365 return -1;
366 }
367 #endif
368
369 return fstatat(rel.dirfd, rel.relative_path,st, AT_SYMLINK_NOFOLLOW);
370 }
371
372 /*
373 * Wrappers around other libc calls:
374 */
375
376 #ifdef __wasilibc_unmodified_upstream
377 /**
378 * Capability-safe wrapper around the `dlopen(3)` libc function.
379 *
380 * `dlopen(3)` accepts a path argument that can reference the global filesystem
381 * namespace. This is not a capability-safe operation, so this wrapper function
382 * attempts to look up the path (or a prefix of it) within the current global
383 * po_map and converts the call into the capability-safe `fdlopen(3)` if
384 * possible. If the current po_map does not contain the sought-after path, this
385 * wrapper will call `fdlopen(openat(AT_FDCWD, original_path), ...)`, which is
386 * the same as the unwrapped `dlopen(3)` call (i.e., will fail with `ECAPMODE`).
387 */
388 void *
389 dlopen(const char *path, int mode)
390 {
391 struct po_relpath rel = find_relative(path, NULL);
392
393 return fdlopen(openat(rel.dirfd, rel.relative_path, 0, mode), mode);
394 }
395 #endif
396 #ifdef __wasilibc_unmodified_upstream
397 #else
398 #include <dirent.h>
399
400 int
401 unlink(const char *pathname)
402 {
403 struct po_relpath rel_pathname = find_relative(pathname, __WASI_RIGHT_PATH_UNLINK_FILE, 0);
404
405 // If we can't find a preopened directory handle to open this file with,
406 // indicate that the program lacks the capabilities.
407 if (rel_pathname.dirfd == -1) {
408 errno = ENOTCAPABLE;
409 return -1;
410 }
411
412 return __wasilibc_unlinkat(rel_pathname.dirfd, rel_pathname.relative_path);
413 }
414
415 int
416 rmdir(const char *pathname)
417 {
418 struct po_relpath rel_pathname = find_relative(pathname, __WASI_RIGHT_PATH_REMOVE_DIRECTORY, 0);
419
420 // If we can't find a preopened directory handle to open this file with,
421 // indicate that the program lacks the capabilities.
422 if (rel_pathname.dirfd == -1) {
423 errno = ENOTCAPABLE;
424 return -1;
425 }
426
427 return __wasilibc_rmdirat(rel_pathname.dirfd, rel_pathname.relative_path);
428 }
429
430 int
431 remove(const char *pathname)
432 {
433 struct po_relpath rel_pathname = find_relative(pathname,
434 __WASI_RIGHT_PATH_UNLINK_FILE |
435 __WASI_RIGHT_PATH_REMOVE_DIRECTORY,
436 0);
437
438 // If searching for both file and directory rights failed, try searching
439 // for either individually.
440 if (rel_pathname.dirfd == -1) {
441 rel_pathname = find_relative(pathname, __WASI_RIGHT_PATH_UNLINK_FILE, 0);
442 if (rel_pathname.dirfd == -1) {
443 rel_pathname = find_relative(pathname, __WASI_RIGHT_PATH_REMOVE_DIRECTORY, 0);
444 }
445 }
446
447 // If we can't find a preopened directory handle to open this file with,
448 // indicate that the program lacks the capabilities.
449 if (rel_pathname.dirfd == -1) {
450 errno = ENOTCAPABLE;
451 return -1;
452 }
453
454 int r = __wasilibc_unlinkat(rel_pathname.dirfd, rel_pathname.relative_path);
455 if (r != 0 && (errno == EISDIR || errno == ENOTCAPABLE))
456 r = __wasilibc_rmdirat(rel_pathname.dirfd, rel_pathname.relative_path);
457 return r;
458 }
459
460 int
461 link(const char *oldpath, const char *newpath)
462 {
463 struct po_relpath rel_oldpath = find_relative(oldpath, __WASI_RIGHT_PATH_LINK_SOURCE, 0);
464 struct po_relpath rel_newpath = find_relative(newpath, __WASI_RIGHT_PATH_LINK_TARGET, 0);
465
466 // If we can't find a preopened directory handle to open this file with,
467 // indicate that the program lacks the capabilities.
468 if (rel_oldpath.dirfd == -1 || rel_newpath.dirfd == -1) {
469 errno = ENOTCAPABLE;
470 return -1;
471 }
472
473 return linkat(rel_oldpath.dirfd, rel_oldpath.relative_path,
474 rel_newpath.dirfd, rel_newpath.relative_path,
475 0);
476 }
477
478 int
479 mkdir(const char *pathname, mode_t mode)
480 {
481 struct po_relpath rel_pathname = find_relative(pathname, __WASI_RIGHT_PATH_CREATE_DIRECTORY, 0);
482
483 // If we can't find a preopened directory handle to open this file with,
484 // indicate that the program lacks the capabilities.
485 if (rel_pathname.dirfd == -1) {
486 errno = ENOTCAPABLE;
487 return -1;
488 }
489
490 return mkdirat(rel_pathname.dirfd, rel_pathname.relative_path, mode);
491 }
492
493 DIR *
494 opendir(const char *name)
495 {
496 struct po_relpath rel_name = find_relative(name, __WASI_RIGHT_PATH_OPEN, 0);
497
498 // If we can't find a preopened directory handle to open this file with,
499 // indicate that the program lacks the capabilities.
500 if (rel_name.dirfd == -1) {
501 errno = ENOTCAPABLE;
502 return NULL;
503 }
504
505 return opendirat(rel_name.dirfd, rel_name.relative_path);
506 }
507
508 ssize_t
509 readlink(const char *pathname, char *buf, size_t bufsiz)
510 {
511 struct po_relpath rel_pathname = find_relative(pathname, __WASI_RIGHT_PATH_READLINK, 0);
512
513 // If we can't find a preopened directory handle to open this file with,
514 // indicate that the program lacks the capabilities.
515 if (rel_pathname.dirfd == -1) {
516 errno = ENOTCAPABLE;
517 return -1;
518 }
519
520 return readlinkat(rel_pathname.dirfd, rel_pathname.relative_path,
521 buf, bufsiz);
522 }
523
524 int
525 scandir(const char *dirp, struct dirent ***namelist,
526 int (*filter)(const struct dirent *),
527 int (*compar)(const struct dirent **, const struct dirent **))
528 {
529 struct po_relpath rel_dirp = find_relative(dirp,
530 __WASI_RIGHT_PATH_OPEN,
531 __WASI_RIGHT_FD_READDIR);
532
533 // If we can't find a preopened directory handle to open this file with,
534 // indicate that the program lacks the capabilities.
535 if (rel_dirp.dirfd == -1) {
536 errno = ENOTCAPABLE;
537 return -1;
538 }
539
540 return scandirat(rel_dirp.dirfd, rel_dirp.relative_path,
541 namelist, filter, compar);
542 }
543
544 int
545 symlink(const char *target, const char *linkpath)
546 {
547 struct po_relpath rel_linkpath = find_relative(linkpath, __WASI_RIGHT_PATH_SYMLINK, 0);
548
549 // If we can't find a preopened directory handle to open this file with,
550 // indicate that the program lacks the capabilities.
551 if (rel_linkpath.dirfd == -1) {
552 errno = ENOTCAPABLE;
553 return -1;
554 }
555
556 return symlinkat(target, rel_linkpath.dirfd, rel_linkpath.relative_path);
557 }
558 #endif
559
560 #ifdef __wasilibc_unmodified_upstream
561 #else
562 static
563 #endif
564 /* Provide tests with mechanism to set our static po_map */
565 void
566 po_set_libc_map(struct po_map *map)
567 {
568 po_map_assertvalid(map);
569
570 map->refcount += 1;
571
572 if (global_map != NULL) {
573 po_map_release(global_map);
574 }
575
576 global_map = map;
577 }
578
579 static struct po_relpath
580 #ifdef __wasilibc_unmodified_upstream
581 find_relative(const char *path, cap_rights_t *rights)
582 #else
583 find_relative(const char *path,
584 __wasi_rights_t rights_base,
585 __wasi_rights_t rights_inheriting)
586 #endif
587 {
588 struct po_relpath rel;
589 struct po_map *map;
590
591 map = get_shared_map();
592 #ifdef __wasilibc_unmodified_upstream
593 if (map == NULL) {
594 rel.dirfd = AT_FDCWD;
595 rel.relative_path = path;
596 } else {
597 rel = po_find(map, path, NULL);
598 }
599 #else
600 rel = po_find(map, path, rights_base, rights_inheriting);
601 #endif
602
603 return (rel);
604 }
605
606 static struct po_map*
607 get_shared_map()
608 {
609 #ifdef __wasilibc_unmodified_upstream
610 struct po_map *map;
611 char *end, *env;
612 long fd;
613
614 // Do we already have a default map?
615 if (global_map) {
616 po_map_assertvalid(global_map);
617 return (global_map);
618 }
619
620 // Attempt to unwrap po_map from a shared memory segment specified by
621 // SHARED_MEMORYFD
622 env = getenv("SHARED_MEMORYFD");
623 if (env == NULL || *env == '\0') {
624 return (NULL);
625 }
626
627 // We expect this environment variable to be an integer and nothing but
628 // an integer.
629 fd = strtol(env, &end, 10);
630 if (*end != '\0') {
631 return (NULL);
632 }
633
634 map = po_unpack(fd);
635 if (map == NULL) {
636 return (NULL);
637 }
638
639 global_map = map;
640
641 return (map);
642 #else
643 assert(global_map);
644 po_map_assertvalid(global_map);
645 return global_map;
646 #endif
647 }
648 #ifdef __wasilibc_unmodified_upstream
649 #else
650 void
651 __wasilibc_init_preopen(void)
652 {
653 global_map = po_map_create(4);
654 if (global_map == NULL)
655 __builtin_trap();
656 po_map_assertvalid(global_map);
657 }
658
659 /*
660 * Register the given pre-opened file descriptor under the given path.
661 */
662 int
663 __wasilibc_register_preopened_fd(int fd, const char *path)
664 {
665 po_map_assertvalid(global_map);
666
667 if (path == NULL) {
668 return -1;
669 }
670
671 #ifdef _REENTRANT
672 #error "__wasilibc_register_preopened_fd doesn't yet support multiple threads"
673 #endif
674
675 struct po_map *map = po_add(global_map, path, fd);
676 if (map == NULL) {
677 return -1;
678 }
679
680 po_map_assertvalid(map);
681 global_map = map;
682
683 return 0;
684 }
685
686 int __wasilibc_find_relpath(const char *path,
687 __wasi_rights_t rights_base,
688 __wasi_rights_t rights_inheriting,
689 const char **relpath)
690 {
691 struct po_relpath rel = find_relative(path, rights_base, rights_inheriting);
692 *relpath = rel.relative_path;
693 return rel.dirfd;
694 }
695 #endif