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