]> git.proxmox.com Git - wasi-libc.git/blame - libc-bottom-half/sources/posix.c
Use bulk memory opcodes when possible
[wasi-libc.git] / libc-bottom-half / sources / posix.c
CommitLineData
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
15static 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.
31static 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
41static 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
50int 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
57int __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
70int 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
83ssize_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
100int 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
113int 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
126int 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
146int 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 168int 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
184int 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
197int 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
220int 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
233DIR *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
246int 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
264int 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
277int 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
295int 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.
314int
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.
331int
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.
348int
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.
364int
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.
384int
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.
402int
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.
420int
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.
436int
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}