master
1//! POSIX-like functions supporting absolute path arguments, implemented in
2//! terms of `__wasilibc_find_relpath` and `*at`-style functions.
3
4#include <errno.h>
5#include <dirent.h>
6#include <fcntl.h>
7#include <stdlib.h>
8#include <sys/stat.h>
9#include <sys/statvfs.h>
10#include <unistd.h>
11#include <utime.h>
12#include <wasi/libc.h>
13#include <wasi/libc-find-relpath.h>
14#include <wasi/libc-nocwd.h>
15
16static int find_relpath2(
17 const char *path,
18 char **relative,
19 size_t *relative_len
20) {
21 // See comments in `preopens.c` for what this trick is doing.
22 const char *abs;
23 if (__wasilibc_find_relpath_alloc)
24 return __wasilibc_find_relpath_alloc(path, &abs, relative, relative_len, 1);
25 return __wasilibc_find_relpath(path, &abs, relative, *relative_len);
26}
27
28// Helper to call `__wasilibc_find_relpath` and return an already-managed
29// pointer for the `relative` path. This function is not reentrant since the
30// `relative` pointer will point to static data that cannot be reused until
31// `relative` is no longer used.
32static int find_relpath(const char *path, char **relative) {
33 static __thread char *relative_buf = NULL;
34 static __thread size_t relative_buf_len = 0;
35 int fd = find_relpath2(path, &relative_buf, &relative_buf_len);
36 // find_relpath2 can update relative_buf, so assign it after the call
37 *relative = relative_buf;
38 return fd;
39}
40
41// same as `find_relpath`, but uses another set of static variables to cache
42static int find_relpath_alt(const char *path, char **relative) {
43 static __thread char *relative_buf = NULL;
44 static __thread size_t relative_buf_len = 0;
45 int fd = find_relpath2(path, &relative_buf, &relative_buf_len);
46 // find_relpath2 can update relative_buf, so assign it after the call
47 *relative = relative_buf;
48 return fd;
49}
50
51int open(const char *path, int oflag, ...) {
52 // WASI libc's `openat` ignores the mode argument, so call a special
53 // entrypoint which avoids the varargs calling convention.
54 return __wasilibc_open_nomode(path, oflag);
55}
56
57// See the documentation in libc.h
58int __wasilibc_open_nomode(const char *path, int oflag) {
59 char *relative_path;
60 int dirfd = find_relpath(path, &relative_path);
61
62 // If we can't find a preopen for it, fail as if we can't find the path.
63 if (dirfd == -1) {
64 errno = ENOENT;
65 return -1;
66 }
67
68 return __wasilibc_nocwd_openat_nomode(dirfd, relative_path, oflag);
69}
70
71int access(const char *path, int amode) {
72 char *relative_path;
73 int dirfd = find_relpath(path, &relative_path);
74
75 // If we can't find a preopen for it, fail as if we can't find the path.
76 if (dirfd == -1) {
77 errno = ENOENT;
78 return -1;
79 }
80
81 return __wasilibc_nocwd_faccessat(dirfd, relative_path, amode, 0);
82}
83
84ssize_t readlink(
85 const char *restrict path,
86 char *restrict buf,
87 size_t bufsize)
88{
89 char *relative_path;
90 int dirfd = find_relpath(path, &relative_path);
91
92 // If we can't find a preopen for it, fail as if we can't find the path.
93 if (dirfd == -1) {
94 errno = ENOENT;
95 return -1;
96 }
97
98 return __wasilibc_nocwd_readlinkat(dirfd, relative_path, buf, bufsize);
99}
100
101int stat(const char *restrict path, struct stat *restrict buf) {
102 char *relative_path;
103 int dirfd = find_relpath(path, &relative_path);
104
105 // If we can't find a preopen for it, fail as if we can't find the path.
106 if (dirfd == -1) {
107 errno = ENOENT;
108 return -1;
109 }
110
111 return __wasilibc_nocwd_fstatat(dirfd, relative_path, buf, 0);
112}
113
114int lstat(const char *restrict path, struct stat *restrict buf) {
115 char *relative_path;
116 int dirfd = find_relpath(path, &relative_path);
117
118 // If we can't find a preopen for it, fail as if we can't find the path.
119 if (dirfd == -1) {
120 errno = ENOENT;
121 return -1;
122 }
123
124 return __wasilibc_nocwd_fstatat(dirfd, relative_path, buf, AT_SYMLINK_NOFOLLOW);
125}
126
127int utime(const char *path, const struct utimbuf *times) {
128 char *relative_path;
129 int dirfd = find_relpath(path, &relative_path);
130
131 // If we can't find a preopen for it, fail as if we can't find the path.
132 if (dirfd == -1) {
133 errno = ENOENT;
134 return -1;
135 }
136
137 return __wasilibc_nocwd_utimensat(
138 dirfd, relative_path,
139 times ? ((struct timespec [2]) {
140 { .tv_sec = times->actime },
141 { .tv_sec = times->modtime }
142 })
143 : NULL,
144 0);
145}
146
147int utimes(const char *path, const struct timeval times[2]) {
148 char *relative_path;
149 int dirfd = find_relpath(path, &relative_path);
150
151 // If we can't find a preopen for it, fail as if we can't find the path.
152 if (dirfd == -1) {
153 errno = ENOENT;
154 return -1;
155 }
156
157 return __wasilibc_nocwd_utimensat(
158 dirfd, relative_path,
159 times ? ((struct timespec [2]) {
160 { .tv_sec = times[0].tv_sec,
161 .tv_nsec = times[0].tv_usec * 1000 },
162 { .tv_sec = times[1].tv_sec,
163 .tv_nsec = times[1].tv_usec * 1000 },
164 })
165 : NULL,
166 0);
167}
168
169int unlink(const char *path) {
170 char *relative_path;
171 int dirfd = find_relpath(path, &relative_path);
172
173 // If we can't find a preopen for it, fail as if we can't find the path.
174 if (dirfd == -1) {
175 errno = ENOENT;
176 return -1;
177 }
178
179 // `unlinkat` imports `__wasi_path_remove_directory` even when
180 // `AT_REMOVEDIR` isn't passed. Instead, use a specialized function which
181 // just imports `__wasi_path_unlink_file`.
182 return __wasilibc_nocwd___wasilibc_unlinkat(dirfd, relative_path);
183}
184
185int rmdir(const char *path) {
186 char *relative_path;
187 int dirfd = find_relpath(path, &relative_path);
188
189 // If we can't find a preopen for it, fail as if we can't find the path.
190 if (dirfd == -1) {
191 errno = ENOENT;
192 return -1;
193 }
194
195 return __wasilibc_nocwd___wasilibc_rmdirat(dirfd, relative_path);
196}
197
198int remove(const char *path) {
199 char *relative_path;
200 int dirfd = find_relpath(path, &relative_path);
201
202 // If we can't find a preopen for it, fail as if we can't find the path.
203 if (dirfd == -1) {
204 errno = ENOENT;
205 return -1;
206 }
207
208 // First try to remove it as a file.
209 int r = __wasilibc_nocwd___wasilibc_unlinkat(dirfd, relative_path);
210 if (r != 0 && (errno == EISDIR || errno == ENOENT)) {
211 // That failed, but it might be a directory.
212 r = __wasilibc_nocwd___wasilibc_rmdirat(dirfd, relative_path);
213
214 // If it isn't a directory, we lack capabilities to remove it as a file.
215 if (errno == ENOTDIR)
216 errno = ENOENT;
217 }
218 return r;
219}
220
221int mkdir(const char *path, mode_t mode) {
222 char *relative_path;
223 int dirfd = find_relpath(path, &relative_path);
224
225 // If we can't find a preopen for it, fail as if we can't find the path.
226 if (dirfd == -1) {
227 errno = ENOENT;
228 return -1;
229 }
230
231 return __wasilibc_nocwd_mkdirat_nomode(dirfd, relative_path);
232}
233
234DIR *opendir(const char *dirname) {
235 char *relative_path;
236 int dirfd = find_relpath(dirname, &relative_path);
237
238 // If we can't find a preopen for it, fail as if we can't find the path.
239 if (dirfd == -1) {
240 errno = ENOENT;
241 return NULL;
242 }
243
244 return __wasilibc_nocwd_opendirat(dirfd, relative_path);
245}
246
247int scandir(
248 const char *restrict dir,
249 struct dirent ***restrict namelist,
250 int (*filter)(const struct dirent *),
251 int (*compar)(const struct dirent **, const struct dirent **)
252) {
253 char *relative_path;
254 int dirfd = find_relpath(dir, &relative_path);
255
256 // If we can't find a preopen for it, fail as if we can't find the path.
257 if (dirfd == -1) {
258 errno = ENOENT;
259 return -1;
260 }
261
262 return __wasilibc_nocwd_scandirat(dirfd, relative_path, namelist, filter, compar);
263}
264
265int symlink(const char *target, const char *linkpath) {
266 char *relative_path;
267 int dirfd = find_relpath(linkpath, &relative_path);
268
269 // If we can't find a preopen for it, fail as if we can't find the path.
270 if (dirfd == -1) {
271 errno = ENOENT;
272 return -1;
273 }
274
275 return __wasilibc_nocwd_symlinkat(target, dirfd, relative_path);
276}
277
278int link(const char *old, const char *new) {
279 char *old_relative_path;
280 int old_dirfd = find_relpath_alt(old, &old_relative_path);
281
282 if (old_dirfd != -1) {
283 char *new_relative_path;
284 int new_dirfd = find_relpath(new, &new_relative_path);
285
286 if (new_dirfd != -1)
287 return __wasilibc_nocwd_linkat(old_dirfd, old_relative_path,
288 new_dirfd, new_relative_path, 0);
289 }
290
291 // We couldn't find a preopen for it; fail as if we can't find the path.
292 errno = ENOENT;
293 return -1;
294}
295
296int rename(const char *old, const char *new) {
297 char *old_relative_path;
298 int old_dirfd = find_relpath_alt(old, &old_relative_path);
299
300 if (old_dirfd != -1) {
301 char *new_relative_path;
302 int new_dirfd = find_relpath(new, &new_relative_path);
303
304 if (new_dirfd != -1)
305 return __wasilibc_nocwd_renameat(old_dirfd, old_relative_path,
306 new_dirfd, new_relative_path);
307 }
308
309 // We couldn't find a preopen for it; fail as if we can't find the path.
310 errno = ENOENT;
311 return -1;
312}
313
314int chmod(const char *path, mode_t mode) {
315 // TODO: We plan to support this eventually in WASI, but not yet.
316 // Meanwhile, we provide a stub so that libc++'s `<filesystem>`
317 // implementation will build unmodified.
318 errno = ENOSYS;
319 return -1;
320}
321
322int fchmod(int fd, mode_t mode) {
323 // TODO: We plan to support this eventually in WASI, but not yet.
324 // Meanwhile, we provide a stub so that libc++'s `<filesystem>`
325 // implementation will build unmodified.
326 errno = ENOSYS;
327 return -1;
328}
329
330int fchmodat(int fd, const char *path, mode_t mode, int flag) {
331 // TODO: We plan to support this eventually in WASI, but not yet.
332 // Meanwhile, we provide a stub so that libc++'s `<filesystem>`
333 // implementation will build unmodified.
334 errno = ENOSYS;
335 return -1;
336}
337
338int statvfs(const char *__restrict path, struct statvfs *__restrict buf) {
339 // TODO: We plan to support this eventually in WASI, but not yet.
340 // Meanwhile, we provide a stub so that libc++'s `<filesystem>`
341 // implementation will build unmodified.
342 errno = ENOSYS;
343 return -1;
344}
345
346int fstatvfs(int fd, struct statvfs *buf) {
347 // TODO: We plan to support this eventually in WASI, but not yet.
348 // Meanwhile, we provide a stub so that libc++'s `<filesystem>`
349 // implementation will build unmodified.
350 errno = ENOSYS;
351 return -1;
352}
353
354// Like `access`, but with `faccessat`'s flags argument.
355int
356__wasilibc_access(const char *path, int mode, int flags)
357{
358 char *relative_path;
359 int dirfd = find_relpath(path, &relative_path);
360
361 // If we can't find a preopen for it, fail as if we can't find the path.
362 if (dirfd == -1) {
363 errno = ENOENT;
364 return -1;
365 }
366
367 return __wasilibc_nocwd_faccessat(dirfd, relative_path,
368 mode, flags);
369}
370
371// Like `utimensat`, but without the `at` part.
372int
373__wasilibc_utimens(const char *path, const struct timespec times[2], int flags)
374{
375 char *relative_path;
376 int dirfd = find_relpath(path, &relative_path);
377
378 // If we can't find a preopen for it, fail as if we can't find the path.
379 if (dirfd == -1) {
380 errno = ENOENT;
381 return -1;
382 }
383
384 return __wasilibc_nocwd_utimensat(dirfd, relative_path,
385 times, flags);
386}
387
388// Like `stat`, but with `fstatat`'s flags argument.
389int
390__wasilibc_stat(const char *__restrict path, struct stat *__restrict st, int flags)
391{
392 char *relative_path;
393 int dirfd = find_relpath(path, &relative_path);
394
395 // If we can't find a preopen for it, fail as if we can't find the path.
396 if (dirfd == -1) {
397 errno = ENOENT;
398 return -1;
399 }
400
401 return __wasilibc_nocwd_fstatat(dirfd, relative_path, st, flags);
402}
403
404// Like `link`, but with `linkat`'s flags argument.
405int
406__wasilibc_link(const char *oldpath, const char *newpath, int flags)
407{
408 char *old_relative_path;
409 char *new_relative_path;
410 int old_dirfd = find_relpath(oldpath, &old_relative_path);
411 int new_dirfd = find_relpath(newpath, &new_relative_path);
412
413 // If we can't find a preopen for it, fail as if we can't find the path.
414 if (old_dirfd == -1 || new_dirfd == -1) {
415 errno = ENOENT;
416 return -1;
417 }
418
419 return __wasilibc_nocwd_linkat(old_dirfd, old_relative_path,
420 new_dirfd, new_relative_path,
421 flags);
422}
423
424// Like `__wasilibc_link`, but oldpath is relative to olddirfd.
425int
426__wasilibc_link_oldat(int olddirfd, const char *oldpath, const char *newpath, int flags)
427{
428 char *new_relative_path;
429 int new_dirfd = find_relpath(newpath, &new_relative_path);
430
431 // If we can't find a preopen for it, fail as if we can't find the path.
432 if (new_dirfd == -1) {
433 errno = ENOENT;
434 return -1;
435 }
436
437 return __wasilibc_nocwd_linkat(olddirfd, oldpath,
438 new_dirfd, new_relative_path,
439 flags);
440}
441
442// Like `__wasilibc_link`, but newpath is relative to newdirfd.
443int
444__wasilibc_link_newat(const char *oldpath, int newdirfd, const char *newpath, int flags)
445{
446 char *old_relative_path;
447 int old_dirfd = find_relpath(oldpath, &old_relative_path);
448
449 // If we can't find a preopen for it, fail as if we can't find the path.
450 if (old_dirfd == -1) {
451 errno = ENOENT;
452 return -1;
453 }
454
455 return __wasilibc_nocwd_linkat(old_dirfd, old_relative_path,
456 newdirfd, newpath,
457 flags);
458}
459
460// Like `rename`, but from is relative to fromdirfd.
461int
462__wasilibc_rename_oldat(int fromdirfd, const char *from, const char *to)
463{
464 char *to_relative_path;
465 int to_dirfd = find_relpath(to, &to_relative_path);
466
467 // If we can't find a preopen for it, fail as if we can't find the path.
468 if (to_dirfd == -1) {
469 errno = ENOENT;
470 return -1;
471 }
472
473 return __wasilibc_nocwd_renameat(fromdirfd, from, to_dirfd, to_relative_path);
474}
475
476// Like `rename`, but to is relative to todirfd.
477int
478__wasilibc_rename_newat(const char *from, int todirfd, const char *to)
479{
480 char *from_relative_path;
481 int from_dirfd = find_relpath(from, &from_relative_path);
482
483 // If we can't find a preopen for it, fail as if we can't find the path.
484 if (from_dirfd == -1) {
485 errno = ENOENT;
486 return -1;
487 }
488
489 return __wasilibc_nocwd_renameat(from_dirfd, from_relative_path, todirfd, to);
490}