master
1#include <stdbool.h>
2#include <stdint.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <time.h>
7
8#include "panic.h"
9
10#define LOG_TRACE 0
11
12enum wasi_errno {
13 wasi_errno_success = 0,
14 wasi_errno_2big = 1,
15 wasi_errno_acces = 2,
16 wasi_errno_addrinuse = 3,
17 wasi_errno_addrnotavail = 4,
18 wasi_errno_afnosupport = 5,
19 wasi_errno_again = 6,
20 wasi_errno_already = 7,
21 wasi_errno_badf = 8,
22 wasi_errno_badmsg = 9,
23 wasi_errno_busy = 10,
24 wasi_errno_canceled = 11,
25 wasi_errno_child = 12,
26 wasi_errno_connaborted = 13,
27 wasi_errno_connrefused = 14,
28 wasi_errno_connreset = 15,
29 wasi_errno_deadlk = 16,
30 wasi_errno_destaddrreq = 17,
31 wasi_errno_dom = 18,
32 wasi_errno_dquot = 19,
33 wasi_errno_exist = 20,
34 wasi_errno_fault = 21,
35 wasi_errno_fbig = 22,
36 wasi_errno_hostunreach = 23,
37 wasi_errno_idrm = 24,
38 wasi_errno_ilseq = 25,
39 wasi_errno_inprogress = 26,
40 wasi_errno_intr = 27,
41 wasi_errno_inval = 28,
42 wasi_errno_io = 29,
43 wasi_errno_isconn = 30,
44 wasi_errno_isdir = 31,
45 wasi_errno_loop = 32,
46 wasi_errno_mfile = 33,
47 wasi_errno_mlink = 34,
48 wasi_errno_msgsize = 35,
49 wasi_errno_multihop = 36,
50 wasi_errno_nametoolong = 37,
51 wasi_errno_netdown = 38,
52 wasi_errno_netreset = 39,
53 wasi_errno_netunreach = 40,
54 wasi_errno_nfile = 41,
55 wasi_errno_nobufs = 42,
56 wasi_errno_nodev = 43,
57 wasi_errno_noent = 44,
58 wasi_errno_noexec = 45,
59 wasi_errno_nolck = 46,
60 wasi_errno_nolink = 47,
61 wasi_errno_nomem = 48,
62 wasi_errno_nomsg = 49,
63 wasi_errno_noprotoopt = 50,
64 wasi_errno_nospc = 51,
65 wasi_errno_nosys = 52,
66 wasi_errno_notconn = 53,
67 wasi_errno_notdir = 54,
68 wasi_errno_notempty = 55,
69 wasi_errno_notrecoverable = 56,
70 wasi_errno_notsock = 57,
71 wasi_errno_opnotsupp = 58,
72 wasi_errno_notty = 59,
73 wasi_errno_nxio = 60,
74 wasi_errno_overflow = 61,
75 wasi_errno_ownerdead = 62,
76 wasi_errno_perm = 63,
77 wasi_errno_pipe = 64,
78 wasi_errno_proto = 65,
79 wasi_errno_protonosupport = 66,
80 wasi_errno_prototype = 67,
81 wasi_errno_range = 68,
82 wasi_errno_rofs = 69,
83 wasi_errno_spipe = 70,
84 wasi_errno_srch = 71,
85 wasi_errno_stale = 72,
86 wasi_errno_timedout = 73,
87 wasi_errno_txtbsy = 74,
88 wasi_errno_xdev = 75,
89 wasi_errno_notcapable = 76,
90};
91
92enum wasi_oflags {
93 wasi_oflags_creat = 1 << 0,
94 wasi_oflags_directory = 1 << 1,
95 wasi_oflags_excl = 1 << 2,
96 wasi_oflags_trunc = 1 << 3,
97};
98
99enum wasi_rights {
100 wasi_rights_fd_datasync = 1ull << 0,
101 wasi_rights_fd_read = 1ull << 1,
102 wasi_rights_fd_seek = 1ull << 2,
103 wasi_rights_fd_fdstat_set_flags = 1ull << 3,
104 wasi_rights_fd_sync = 1ull << 4,
105 wasi_rights_fd_tell = 1ull << 5,
106 wasi_rights_fd_write = 1ull << 6,
107 wasi_rights_fd_advise = 1ull << 7,
108 wasi_rights_fd_allocate = 1ull << 8,
109 wasi_rights_path_create_directory = 1ull << 9,
110 wasi_rights_path_create_file = 1ull << 10,
111 wasi_rights_path_link_source = 1ull << 11,
112 wasi_rights_path_link_target = 1ull << 12,
113 wasi_rights_path_open = 1ull << 13,
114 wasi_rights_fd_readdir = 1ull << 14,
115 wasi_rights_path_readlink = 1ull << 15,
116 wasi_rights_path_rename_source = 1ull << 16,
117 wasi_rights_path_rename_target = 1ull << 17,
118 wasi_rights_path_filestat_get = 1ull << 18,
119 wasi_rights_path_filestat_set_size = 1ull << 19,
120 wasi_rights_path_filestat_set_times = 1ull << 20,
121 wasi_rights_fd_filestat_get = 1ull << 21,
122 wasi_rights_fd_filestat_set_size = 1ull << 22,
123 wasi_rights_fd_filestat_set_times = 1ull << 23,
124 wasi_rights_path_symlink = 1ull << 24,
125 wasi_rights_path_remove_directory = 1ull << 25,
126 wasi_rights_path_unlink_file = 1ull << 26,
127 wasi_rights_poll_fd_readwrite = 1ull << 27,
128 wasi_rights_sock_shutdown = 1ull << 28,
129 wasi_rights_sock_accept = 1ull << 29,
130};
131
132enum wasi_clockid {
133 wasi_clockid_realtime = 0,
134 wasi_clockid_monotonic = 1,
135 wasi_clockid_process_cputime_id = 2,
136 wasi_clockid_thread_cputime_id = 3,
137};
138
139enum wasi_filetype {
140 wasi_filetype_unknown = 0,
141 wasi_filetype_block_device = 1,
142 wasi_filetype_character_device = 2,
143 wasi_filetype_directory = 3,
144 wasi_filetype_regular_file = 4,
145 wasi_filetype_socket_dgram = 5,
146 wasi_filetype_socket_stream = 6,
147 wasi_filetype_symbolic_link = 7,
148};
149
150enum wasi_fdflags {
151 wasi_fdflags_append = 1 << 0,
152 wasi_fdflags_dsync = 1 << 1,
153 wasi_fdflags_nonblock = 1 << 2,
154 wasi_fdflags_rsync = 1 << 3,
155 wasi_fdflags_sync = 1 << 4,
156};
157
158struct wasi_filestat {
159 uint64_t dev;
160 uint64_t ino;
161 uint64_t filetype;
162 uint64_t nlink;
163 uint64_t size;
164 uint64_t atim;
165 uint64_t mtim;
166 uint64_t ctim;
167};
168
169struct wasi_fdstat {
170 uint16_t fs_filetype;
171 uint16_t fs_flags;
172 uint32_t padding;
173 uint64_t fs_rights_inheriting;
174};
175
176struct wasi_ciovec {
177 uint32_t ptr;
178 uint32_t len;
179};
180
181enum wasi_whence {
182 wasi_whence_set = 0,
183 wasi_whence_cur = 1,
184 wasi_whence_end = 2,
185};
186
187extern uint16_t load16_align0(const uint8_t *ptr);
188extern uint16_t load16_align1(const uint16_t *ptr);
189extern uint32_t load32_align0(const uint8_t *ptr);
190extern uint32_t load32_align1(const uint16_t *ptr);
191extern uint32_t load32_align2(const uint32_t *ptr);
192extern uint64_t load64_align0(const uint8_t *ptr);
193extern uint64_t load64_align1(const uint16_t *ptr);
194extern uint64_t load64_align2(const uint32_t *ptr);
195extern uint64_t load64_align3(const uint64_t *ptr);
196
197extern void store16_align0(uint8_t *ptr, uint16_t val);
198extern void store16_align1(uint16_t *ptr, uint16_t val);
199extern void store32_align0(uint8_t *ptr, uint32_t val);
200extern void store32_align1(uint16_t *ptr, uint32_t val);
201extern void store32_align2(uint32_t *ptr, uint32_t val);
202extern void store64_align0(uint8_t *ptr, uint64_t val);
203extern void store64_align1(uint16_t *ptr, uint64_t val);
204extern void store64_align2(uint32_t *ptr, uint64_t val);
205extern void store64_align3(uint64_t *ptr, uint64_t val);
206
207extern uint8_t **const wasm_memory;
208extern void wasm__start(void);
209
210static int global_argc;
211static char **global_argv;
212
213static uint32_t de_len;
214struct DirEntry {
215 enum wasi_filetype filetype;
216 time_t atim;
217 time_t mtim;
218 time_t ctim;
219 char *guest_path;
220 char *host_path;
221} *des;
222
223static uint32_t fd_len;
224static struct FileDescriptor {
225 uint32_t de;
226 enum wasi_fdflags fdflags;
227 FILE *stream;
228 uint64_t fs_rights_inheriting;
229} *fds;
230
231static void *dupe(const void *data, size_t len) {
232 void *copy = malloc(len);
233 if (copy == NULL) panic("out of memory");
234 memcpy(copy, data, len);
235 return copy;
236}
237
238int main(int argc, char **argv) {
239 if (argc < 2) {
240 fprintf(stderr, "usage: %s <zig-lib-path> <args...>\n", argv[0]);
241 return 1;
242 }
243
244 global_argc = argc;
245 global_argv = argv;
246
247 time_t now = time(NULL);
248 srand((unsigned)now);
249
250 de_len = 4;
251 des = calloc(de_len, sizeof(struct DirEntry));
252 if (des == NULL) panic("out of memory");
253
254 des[0].filetype = wasi_filetype_character_device;
255
256 des[1].filetype = wasi_filetype_directory;
257 des[1].guest_path = dupe(".", sizeof("."));
258 des[1].host_path = dupe(".", sizeof("."));
259
260 des[2].filetype = wasi_filetype_directory;
261 des[2].guest_path = dupe("/cache", sizeof("/cache"));
262 des[2].atim = now;
263 des[2].mtim = now;
264 des[2].ctim = now;
265
266 des[3].filetype = wasi_filetype_directory;
267 des[3].guest_path = dupe("/lib", sizeof("/lib"));
268 des[3].host_path = dupe(argv[1], strlen(argv[1]) + 1);
269
270 fd_len = 6;
271 fds = calloc(sizeof(struct FileDescriptor), fd_len);
272 if (fds == NULL) panic("out of memory");
273 fds[0].stream = stdin;
274 fds[1].stream = stdout;
275 fds[2].stream = stderr;
276 fds[3].de = 1;
277 fds[4].de = 2;
278 fds[5].de = 3;
279
280 wasm__start();
281}
282
283static bool isLetter(char c) {
284 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
285}
286static bool isPathSep(char c) {
287 return c == '/' || c == '\\';
288}
289static bool isAbsPath(const char *path, uint32_t path_len) {
290 if (path_len >= 1 && isPathSep(path[0])) return true;
291 if (path_len >= 3 && isLetter(path[0]) && path[1] == ':' && isPathSep(path[2])) return true;
292 return false;
293}
294static bool isSamePath(const char *a, const char *b, uint32_t len) {
295 for (uint32_t i = 0; i < len; i += 1) {
296 if (isPathSep(a[i]) && isPathSep(b[i])) continue;
297 if (a[i] != b[i]) return false;
298 }
299 return true;
300}
301
302static enum wasi_errno DirEntry_create(uint32_t dir_fd, const char *path, uint32_t path_len, enum wasi_filetype filetype, time_t tim, uint32_t *res_de) {
303 if (isAbsPath(path, path_len)) {
304 if (dir_fd >= fd_len || fds[dir_fd].de >= de_len) return wasi_errno_badf;
305 if (des[fds[dir_fd].de].filetype != wasi_filetype_directory) return wasi_errno_notdir;
306 }
307
308 struct DirEntry *new_des = realloc(des, (de_len + 1) * sizeof(struct DirEntry));
309 if (new_des == NULL) return wasi_errno_nomem;
310 des = new_des;
311
312 struct DirEntry *de = &des[de_len];
313 de->filetype = filetype;
314 de->atim = tim;
315 de->mtim = tim;
316 de->ctim = tim;
317 if (isAbsPath(path, path_len)) {
318 de->guest_path = malloc(path_len + 1);
319 if (de->guest_path == NULL) return wasi_errno_nomem;
320 memcpy(&de->guest_path[0], path, path_len);
321 de->guest_path[path_len] = '\0';
322
323 de->host_path = malloc(path_len + 1);
324 if (de->host_path == NULL) return wasi_errno_nomem;
325 memcpy(&de->host_path[0], path, path_len);
326 de->host_path[path_len] = '\0';
327 } else {
328 const struct DirEntry *dir_de = &des[fds[dir_fd].de];
329 if (dir_de->guest_path != NULL) {
330 size_t dir_guest_path_len = strlen(dir_de->guest_path);
331 de->guest_path = malloc(dir_guest_path_len + 1 + path_len + 1);
332 if (de->guest_path == NULL) return wasi_errno_nomem;
333 memcpy(&de->guest_path[0], dir_de->guest_path, dir_guest_path_len);
334 de->guest_path[dir_guest_path_len] = '/';
335 memcpy(&de->guest_path[dir_guest_path_len + 1], path, path_len);
336 de->guest_path[dir_guest_path_len + 1 + path_len] = '\0';
337 } else de->guest_path = NULL;
338
339 if (dir_de->host_path != NULL) {
340 size_t dir_host_path_len = strlen(dir_de->host_path);
341 de->host_path = malloc(dir_host_path_len + 1 + path_len + 1);
342 if (de->host_path == NULL) { free(de->guest_path); return wasi_errno_nomem; }
343 memcpy(&de->host_path[0], dir_de->host_path, dir_host_path_len);
344 de->host_path[dir_host_path_len] = '/';
345 memcpy(&de->host_path[dir_host_path_len + 1], path, path_len);
346 de->host_path[dir_host_path_len + 1 + path_len] = '\0';
347 } else de->host_path = NULL;
348 }
349
350 if (res_de != NULL) *res_de = de_len;
351 de_len += 1;
352 return wasi_errno_success;
353}
354
355static enum wasi_errno DirEntry_lookup(uint32_t dir_fd, uint32_t flags, const char *path, uint32_t path_len, uint32_t *res_de) {
356 (void)flags;
357 if (isAbsPath(path, path_len)) {
358 for (uint32_t de = 0; de < de_len; de += 1) {
359 if (des[de].guest_path == NULL) continue;
360 if (!isSamePath(&des[de].guest_path[0], path, path_len)) continue;
361 if (des[de].guest_path[path_len] != '\0') continue;
362 if (res_de != NULL) *res_de = de;
363 return wasi_errno_success;
364 }
365 } else {
366 if (dir_fd >= fd_len || fds[dir_fd].de >= de_len) return wasi_errno_badf;
367 const struct DirEntry *dir_de = &des[fds[dir_fd].de];
368 if (dir_de->filetype != wasi_filetype_directory) return wasi_errno_notdir;
369
370 size_t dir_guest_path_len = strlen(dir_de->guest_path);
371 for (uint32_t de = 0; de < de_len; de += 1) {
372 if (des[de].guest_path == NULL) continue;
373 if (!isSamePath(&des[de].guest_path[0], dir_de->guest_path, dir_guest_path_len)) continue;
374 if (!isPathSep(des[de].guest_path[dir_guest_path_len])) continue;
375 if (!isSamePath(&des[de].guest_path[dir_guest_path_len + 1], path, path_len)) continue;
376 if (des[de].guest_path[dir_guest_path_len + 1 + path_len] != '\0') continue;
377 if (res_de != NULL) *res_de = de;
378 return wasi_errno_success;
379 }
380 }
381 return wasi_errno_noent;
382}
383
384static void DirEntry_filestat(uint32_t de, struct wasi_filestat *res_filestat) {
385 store64_align3(&res_filestat->dev, 0);
386 store64_align3(&res_filestat->ino, de);
387 store64_align3(&res_filestat->filetype, des[de].filetype);
388 store64_align3(&res_filestat->nlink, 1);
389 store64_align3(&res_filestat->size, 0);
390 store64_align3(&res_filestat->atim, des[de].atim * UINT64_C(1000000000));
391 store64_align3(&res_filestat->mtim, des[de].mtim * UINT64_C(1000000000));
392 store64_align3(&res_filestat->ctim, des[de].ctim * UINT64_C(1000000000));
393}
394
395static void DirEntry_unlink(uint32_t de) {
396 free(des[de].guest_path);
397 des[de].guest_path = NULL;
398 free(des[de].host_path);
399 des[de].host_path = NULL;
400}
401
402uint32_t wasi_snapshot_preview1_args_sizes_get(uint32_t argv_size, uint32_t argv_buf_size) {
403 uint8_t *const m = *wasm_memory;
404 uint32_t *argv_size_ptr = (uint32_t *)&m[argv_size];
405 uint32_t *argv_buf_size_ptr = (uint32_t *)&m[argv_buf_size];
406#if LOG_TRACE
407 fprintf(stderr, "wasi_snapshot_preview1_args_sizes_get()\n");
408#endif
409
410 int c_argc = global_argc;
411 char **c_argv = global_argv;
412 uint32_t size = 0;
413 for (int i = 0; i < c_argc; i += 1) {
414 if (i == 1) continue;
415 size += strlen(c_argv[i]) + 1;
416 }
417 store32_align2(argv_size_ptr, c_argc - 1);
418 store32_align2(argv_buf_size_ptr, size);
419 return wasi_errno_success;
420}
421
422uint32_t wasi_snapshot_preview1_args_get(uint32_t argv, uint32_t argv_buf) {
423 uint8_t *const m = *wasm_memory;
424 uint32_t *argv_ptr = (uint32_t *)&m[argv];
425 char *argv_buf_ptr = (char *)&m[argv_buf];
426#if LOG_TRACE
427 fprintf(stderr, "wasi_snapshot_preview1_args_get()\n");
428#endif
429
430 int c_argc = global_argc;
431 char **c_argv = global_argv;
432 uint32_t dst_i = 0;
433 uint32_t argv_buf_i = 0;
434 for (int src_i = 0; src_i < c_argc; src_i += 1) {
435 if (src_i == 1) continue;
436 store32_align2(&argv_ptr[dst_i], argv_buf + argv_buf_i);
437 dst_i += 1;
438 strcpy(&argv_buf_ptr[argv_buf_i], c_argv[src_i]);
439 argv_buf_i += strlen(c_argv[src_i]) + 1;
440 }
441 return wasi_errno_success;
442}
443
444uint32_t wasi_snapshot_preview1_fd_prestat_get(uint32_t fd, uint32_t res_prestat) {
445 uint8_t *const m = *wasm_memory;
446 uint32_t *res_prestat_ptr = (uint32_t *)&m[res_prestat];
447#if LOG_TRACE
448 fprintf(stderr, "wasi_snapshot_preview1_fd_prestat_get(%u)\n", fd);
449#endif
450
451 if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf;
452
453 store32_align2(&res_prestat_ptr[0], 0);
454 store32_align2(&res_prestat_ptr[1], strlen(des[fds[fd].de].guest_path));
455 return wasi_errno_success;
456}
457
458uint32_t wasi_snapshot_preview1_fd_prestat_dir_name(uint32_t fd, uint32_t path, uint32_t path_len) {
459 uint8_t *const m = *wasm_memory;
460 char *path_ptr = (char *)&m[path];
461#if LOG_TRACE
462 fprintf(stderr, "wasi_snapshot_preview1_fd_prestat_dir_name(%u, \"%.*s\")\n", fd, (int)path_len, path_ptr);
463#endif
464
465 if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf;
466 strncpy(path_ptr, des[fds[fd].de].guest_path, path_len);
467 return wasi_errno_success;
468}
469
470void wasi_snapshot_preview1_proc_exit(uint32_t rval) {
471#if LOG_TRACE
472 fprintf(stderr, "wasi_snapshot_preview1_proc_exit(%u)\n", rval);
473#endif
474
475 exit(rval);
476}
477
478uint32_t wasi_snapshot_preview1_fd_close(uint32_t fd) {
479#if LOG_TRACE
480 fprintf(stderr, "wasi_snapshot_preview1_fd_close(%u)\n", fd);
481#endif
482
483 if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf;
484 if (fds[fd].stream != NULL) fclose(fds[fd].stream);
485
486 fds[fd].de = ~0;
487 fds[fd].stream = NULL;
488 return wasi_errno_success;
489}
490
491uint32_t wasi_snapshot_preview1_path_create_directory(uint32_t fd, uint32_t path, uint32_t path_len) {
492 uint8_t *const m = *wasm_memory;
493 const char *path_ptr = (const char *)&m[path];
494#if LOG_TRACE
495 fprintf(stderr, "wasi_snapshot_preview1_path_create_directory(%u, \"%.*s\")\n", fd, (int)path_len, path_ptr);
496#endif
497
498 enum wasi_errno lookup_errno = DirEntry_lookup(fd, 0, path_ptr, path_len, NULL);
499 switch (lookup_errno) {
500 case wasi_errno_success: return wasi_errno_exist;
501 case wasi_errno_noent: break;
502 default: return lookup_errno;
503 }
504 return DirEntry_create(fd, path_ptr, path_len, wasi_filetype_directory, time(NULL), NULL);
505}
506
507uint32_t wasi_snapshot_preview1_fd_read(uint32_t fd, uint32_t iovs, uint32_t iovs_len, uint32_t res_size) {
508 uint8_t *const m = *wasm_memory;
509 struct wasi_ciovec *iovs_ptr = (struct wasi_ciovec *)&m[iovs];
510 uint32_t *res_size_ptr = (uint32_t *)&m[res_size];
511#if LOG_TRACE
512 fprintf(stderr, "wasi_snapshot_preview1_fd_read(%u, 0x%X, %u)\n", fd, iovs, iovs_len);
513#endif
514
515 if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf;
516 switch (des[fds[fd].de].filetype) {
517 case wasi_filetype_character_device: break;
518 case wasi_filetype_regular_file: break;
519 case wasi_filetype_directory: return wasi_errno_inval;
520 default: panic("unimplemented: fd_read special file");
521 }
522
523 if (fds[fd].stream == NULL) {
524 store32_align2(res_size_ptr, 0);
525 return wasi_errno_success;
526 }
527
528 size_t size = 0;
529 for (uint32_t i = 0; i < iovs_len; i += 1) {
530 uint32_t len = load32_align2(&iovs_ptr[i].len);
531 size_t read_size = fread(&m[load32_align2(&iovs_ptr[i].ptr)], 1, len, fds[fd].stream);
532 size += read_size;
533 if (read_size < len) break;
534 }
535
536 if (size > 0) des[fds[fd].de].atim = time(NULL);
537 store32_align2(res_size_ptr, size);
538 return wasi_errno_success;
539}
540
541uint32_t wasi_snapshot_preview1_fd_filestat_get(uint32_t fd, uint32_t res_filestat) {
542 uint8_t *const m = *wasm_memory;
543 struct wasi_filestat *res_filestat_ptr = (struct wasi_filestat *)&m[res_filestat];
544#if LOG_TRACE
545 fprintf(stderr, "wasi_snapshot_preview1_fd_filestat_get(%u)\n", fd);
546#endif
547
548 if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf;
549 DirEntry_filestat(fds[fd].de, res_filestat_ptr);
550 if (des[fds[fd].de].filetype != wasi_filetype_regular_file) return wasi_errno_success;
551 if (fds[fd].stream == NULL) return wasi_errno_success;
552 fpos_t pos;
553 if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
554 if (fseek(fds[fd].stream, 0, SEEK_END) < 0) return wasi_errno_io;
555 long size = ftell(fds[fd].stream);
556 if (size < 0) return wasi_errno_io;
557 store64_align3(&res_filestat_ptr->size, size);
558 if (fsetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
559 return wasi_errno_success;
560}
561
562uint32_t wasi_snapshot_preview1_path_rename(uint32_t fd, uint32_t old_path, uint32_t old_path_len, uint32_t new_fd, uint32_t new_path, uint32_t new_path_len) {
563 uint8_t *const m = *wasm_memory;
564 const char *old_path_ptr = (const char *)&m[old_path];
565 const char *new_path_ptr = (const char *)&m[new_path];
566#if LOG_TRACE
567 fprintf(stderr, "wasi_snapshot_preview1_path_rename(%u, \"%.*s\", %u, \"%.*s\")\n", fd, (int)old_path_len, old_path_ptr, new_fd, (int)new_path_len, new_path_ptr);
568#endif
569
570 uint32_t old_de;
571 enum wasi_errno old_lookup_errno = DirEntry_lookup(fd, 0, old_path_ptr, old_path_len, &old_de);
572 if (old_lookup_errno != wasi_errno_success) return old_lookup_errno;
573 DirEntry_unlink(old_de);
574
575 uint32_t de;
576 enum wasi_errno new_lookup_errno = DirEntry_lookup(new_fd, 0, new_path_ptr, new_path_len, &de);
577 switch (new_lookup_errno) {
578 case wasi_errno_success: DirEntry_unlink(de); break;
579 case wasi_errno_noent: break;
580 default: return new_lookup_errno;
581 }
582
583 uint32_t new_de;
584 enum wasi_errno create_errno =
585 DirEntry_create(new_fd, new_path_ptr, new_path_len, des[old_de].filetype, 0, &new_de);
586 if (create_errno != wasi_errno_success) return create_errno;
587 des[new_de].atim = des[old_de].atim;
588 des[new_de].mtim = des[old_de].mtim;
589 des[new_de].ctim = time(NULL);
590 return wasi_errno_success;
591}
592
593uint32_t wasi_snapshot_preview1_fd_filestat_set_size(uint32_t fd, uint64_t size) {
594#if LOG_TRACE
595 fprintf(stderr, "wasi_snapshot_preview1_fd_filestat_set_size(%u, %llu)\n", fd, (unsigned long long)size);
596#endif
597
598 if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf;
599 if (des[fds[fd].de].filetype != wasi_filetype_regular_file) return wasi_errno_inval;
600
601 if (fds[fd].stream == NULL) return wasi_errno_success;
602 fpos_t pos;
603 if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
604 if (fseek(fds[fd].stream, 0, SEEK_END) < 0) return wasi_errno_io;
605 long old_size = ftell(fds[fd].stream);
606 if (old_size < 0) return wasi_errno_io;
607 if (size != (unsigned long)old_size) {
608 if (size > 0 && fseek(fds[fd].stream, size - 1, SEEK_SET) < 0) return wasi_errno_io;
609 if (size < (unsigned long)old_size) {
610 // Note that this destroys the contents on resize might have to save truncated
611 // file in memory if this becomes an issue.
612 FILE *trunc = fopen(des[fds[fd].de].host_path, "wb");
613 if (trunc == NULL) return wasi_errno_io;
614 fclose(trunc);
615 }
616 if (size > 0) fputc(0, fds[fd].stream);
617 }
618 if (fsetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
619 return wasi_errno_success;
620}
621
622uint32_t wasi_snapshot_preview1_fd_pwrite(uint32_t fd, uint32_t iovs, uint32_t iovs_len, uint64_t offset, uint32_t res_size) {
623 uint8_t *const m = *wasm_memory;
624 struct wasi_ciovec *iovs_ptr = (struct wasi_ciovec *)&m[iovs];
625 uint32_t *res_size_ptr = (uint32_t *)&m[res_size];
626#if LOG_TRACE
627 fprintf(stderr, "wasi_snapshot_preview1_fd_pwrite(%u, 0x%X, %u)\n", fd, iovs, iovs_len);
628#endif
629
630 if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf;
631 switch (des[fds[fd].de].filetype) {
632 case wasi_filetype_character_device: break;
633 case wasi_filetype_regular_file: break;
634 case wasi_filetype_directory: return wasi_errno_inval;
635 default: panic("unimplemented: fd_pwrite special file");
636 }
637
638 fpos_t pos;
639 if (fds[fd].stream != NULL) {
640 if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
641 if (fseek(fds[fd].stream, offset, SEEK_SET) < 0) return wasi_errno_io;
642 }
643
644 size_t size = 0;
645 for (uint32_t i = 0; i < iovs_len; i += 1) {
646 uint32_t len = load32_align2(&iovs_ptr[i].len);
647 size_t written_size = 0;
648 if (fds[fd].stream != NULL)
649 written_size = fwrite(&m[load32_align2(&iovs_ptr[i].ptr)], 1, len, fds[fd].stream);
650 else
651 written_size = len;
652 size += written_size;
653 if (written_size < len) break;
654 }
655
656 if (fds[fd].stream != NULL) {
657 if (fsetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
658 }
659
660 if (size > 0) {
661 time_t now = time(NULL);
662 des[fds[fd].de].atim = now;
663 des[fds[fd].de].mtim = now;
664 }
665 store32_align2(res_size_ptr, size);
666 return wasi_errno_success;
667}
668
669uint32_t wasi_snapshot_preview1_random_get(uint32_t buf, uint32_t buf_len) {
670 uint8_t *const m = *wasm_memory;
671 uint8_t *buf_ptr = (uint8_t *)&m[buf];
672#if LOG_TRACE
673 fprintf(stderr, "wasi_snapshot_preview1_random_get(%u)\n", buf_len);
674#endif
675
676 for (uint32_t i = 0; i < buf_len; i += 1) buf_ptr[i] = (uint8_t)rand();
677 return wasi_errno_success;
678}
679
680uint32_t wasi_snapshot_preview1_fd_filestat_set_times(uint32_t fd, uint64_t atim, uint64_t mtim, uint32_t fst_flags) {
681 (void)fd;
682 (void)atim;
683 (void)mtim;
684 (void)fst_flags;
685#if LOG_TRACE
686 fprintf(stderr, "wasi_snapshot_preview1_fd_filestat_set_times(%u, %llu, %llu, 0x%X)\n", fd, (unsigned long long)atim, (unsigned long long)mtim, fst_flags);
687#endif
688
689 panic("unimplemented: fd_filestat_set_times");
690 return wasi_errno_success;
691}
692
693uint32_t wasi_snapshot_preview1_environ_sizes_get(uint32_t environ_size, uint32_t environ_buf_size) {
694 uint8_t *const m = *wasm_memory;
695 uint32_t *environ_size_ptr = (uint32_t *)&m[environ_size];
696 uint32_t *environ_buf_size_ptr = (uint32_t *)&m[environ_buf_size];
697#if LOG_TRACE
698 fprintf(stderr, "wasi_snapshot_preview1_environ_sizes_get()\n");
699#endif
700
701 store32_align2(environ_size_ptr, 0);
702 store32_align2(environ_buf_size_ptr, 0);
703 return wasi_errno_success;
704}
705
706uint32_t wasi_snapshot_preview1_environ_get(uint32_t environ, uint32_t environ_buf) {
707 (void)environ;
708 (void)environ_buf;
709#if LOG_TRACE
710 fprintf(stderr, "wasi_snapshot_preview1_environ_get()\n");
711#endif
712
713 panic("unimplemented: environ_get");
714 return wasi_errno_success;
715}
716
717uint32_t wasi_snapshot_preview1_path_filestat_get(uint32_t fd, uint32_t flags, uint32_t path, uint32_t path_len, uint32_t res_filestat) {
718 uint8_t *const m = *wasm_memory;
719 const char *path_ptr = (const char *)&m[path];
720 struct wasi_filestat *res_filestat_ptr = (struct wasi_filestat *)&m[res_filestat];
721#if LOG_TRACE
722 fprintf(stderr, "wasi_snapshot_preview1_path_filestat_get(%u, 0x%X, \"%.*s\")\n", fd, flags, (int)path_len, path_ptr);
723#endif
724
725 uint32_t de;
726 enum wasi_errno lookup_errno = DirEntry_lookup(fd, flags, path_ptr, path_len, &de);
727 if (lookup_errno != wasi_errno_success) return lookup_errno;
728 DirEntry_filestat(de, res_filestat_ptr);
729 if (des[de].filetype == wasi_filetype_regular_file && des[de].host_path != NULL) {
730 FILE *stream = fopen(des[de].host_path, "rb");
731 if (stream != NULL) {
732 if (fseek(stream, 0, SEEK_END) >= 0) {
733 long size = ftell(stream);
734 if (size >= 0) store64_align3(&res_filestat_ptr->size, size);
735 }
736 fclose(stream);
737 }
738 }
739 return wasi_errno_success;
740}
741
742uint32_t wasi_snapshot_preview1_fd_fdstat_get(uint32_t fd, uint32_t res_fdstat) {
743 uint8_t *const m = *wasm_memory;
744 struct wasi_fdstat *res_fdstat_ptr = (struct wasi_fdstat *)&m[res_fdstat];
745#if LOG_TRACE
746 fprintf(stderr, "wasi_snapshot_preview1_fd_fdstat_get(%u)\n", fd);
747#endif
748
749 if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf;
750 store16_align1(&res_fdstat_ptr->fs_filetype, des[fds[fd].de].filetype);
751 store16_align1(&res_fdstat_ptr->fs_flags, fds[fd].fdflags);
752 store32_align2(&res_fdstat_ptr->padding, 0);
753 store64_align3(&res_fdstat_ptr->fs_rights_inheriting, fds[fd].fs_rights_inheriting);
754 return wasi_errno_success;
755}
756
757uint32_t wasi_snapshot_preview1_fd_readdir(uint32_t fd, uint32_t buf, uint32_t buf_len, uint64_t cookie, uint32_t res_size) {
758 (void)fd;
759 (void)buf;
760 (void)buf_len;
761 (void)cookie;
762 (void)res_size;
763#if LOG_TRACE
764 fprintf(stderr, "wasi_snapshot_preview1_fd_readdir(%u, 0x%X, %u, %llu)\n", fd, buf, buf_len, (unsigned long long)cookie);
765#endif
766
767 panic("unimplemented: fd_readdir");
768 return wasi_errno_success;
769}
770
771uint32_t wasi_snapshot_preview1_fd_write(uint32_t fd, uint32_t iovs, uint32_t iovs_len, uint32_t res_size) {
772 uint8_t *const m = *wasm_memory;
773 struct wasi_ciovec *iovs_ptr = (struct wasi_ciovec *)&m[iovs];
774 uint32_t *res_size_ptr = (uint32_t *)&m[res_size];
775#if LOG_TRACE
776 fprintf(stderr, "wasi_snapshot_preview1_fd_write(%u, 0x%X, %u)\n", fd, iovs, iovs_len);
777#endif
778
779 if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf;
780 switch (des[fds[fd].de].filetype) {
781 case wasi_filetype_character_device: break;
782 case wasi_filetype_regular_file: break;
783 case wasi_filetype_directory: return wasi_errno_inval;
784 default: panic("unimplemented: fd_write special file");
785 }
786
787 size_t size = 0;
788 for (uint32_t i = 0; i < iovs_len; i += 1) {
789 uint32_t len = load32_align2(&iovs_ptr[i].len);
790 size_t written_size = 0;
791 if (fds[fd].stream != NULL)
792 written_size = fwrite(&m[load32_align2(&iovs_ptr[i].ptr)], 1, len, fds[fd].stream);
793 else
794 written_size = len;
795 size += written_size;
796 if (written_size < len) break;
797 }
798
799 if (size > 0) {
800 time_t now = time(NULL);
801 des[fds[fd].de].atim = now;
802 des[fds[fd].de].mtim = now;
803 }
804 store32_align2(res_size_ptr, size);
805 return wasi_errno_success;
806}
807
808uint32_t wasi_snapshot_preview1_path_open(uint32_t fd, uint32_t dirflags, uint32_t path, uint32_t path_len, uint32_t oflags, uint64_t fs_rights_base, uint64_t fs_rights_inheriting, uint32_t fdflags, uint32_t res_fd) {
809 uint8_t *const m = *wasm_memory;
810 const char *path_ptr = (const char *)&m[path];
811 uint32_t *res_fd_ptr = (uint32_t *)&m[res_fd];
812#if LOG_TRACE
813 fprintf(stderr, "wasi_snapshot_preview1_path_open(%u, 0x%X, \"%.*s\", 0x%X, 0x%llX, 0x%llX, 0x%X)\n", fd, dirflags, (int)path_len, path_ptr, oflags, (unsigned long long)fs_rights_base, (unsigned long long)fs_rights_inheriting, fdflags);
814#endif
815
816 bool creat = (oflags & wasi_oflags_creat) != 0;
817 bool directory = (oflags & wasi_oflags_directory) != 0;
818 bool excl = (oflags & wasi_oflags_excl) != 0;
819 bool trunc = (oflags & wasi_oflags_trunc) != 0;
820 bool append = (fdflags & wasi_fdflags_append) != 0;
821
822 uint32_t de;
823 enum wasi_errno lookup_errno = DirEntry_lookup(fd, dirflags, path_ptr, path_len, &de);
824 if (lookup_errno == wasi_errno_success) {
825 if (directory && des[de].filetype != wasi_filetype_directory) return wasi_errno_notdir;
826
827 struct FileDescriptor *new_fds = realloc(fds, (fd_len + 1) * sizeof(struct FileDescriptor));
828 if (new_fds == NULL) return wasi_errno_nomem;
829 fds = new_fds;
830
831 fds[fd_len].de = de;
832 fds[fd_len].fdflags = fdflags;
833 switch (des[de].filetype) {
834 case wasi_filetype_directory: fds[fd_len].stream = NULL; break;
835 default: panic("unimplemented: path_open non-directory DirEntry");
836 }
837 fds[fd_len].fs_rights_inheriting = fs_rights_inheriting;
838
839#if LOG_TRACE
840 fprintf(stderr, "fd = %u\n", fd_len);
841#endif
842 store32_align2(res_fd_ptr, fd_len);
843 fd_len += 1;
844 }
845 if (lookup_errno != wasi_errno_noent) return lookup_errno;
846
847 struct FileDescriptor *new_fds = realloc(fds, (fd_len + 1) * sizeof(struct FileDescriptor));
848 if (new_fds == NULL) return wasi_errno_nomem;
849 fds = new_fds;
850
851 enum wasi_filetype filetype = directory ? wasi_filetype_directory : wasi_filetype_regular_file;
852 enum wasi_errno create_errno = DirEntry_create(fd, path_ptr, path_len, filetype, 0, &de);
853 if (create_errno != wasi_errno_success) return create_errno;
854 FILE *stream;
855 if (!directory) {
856 if (des[de].host_path == NULL) {
857 if (!creat) { DirEntry_unlink(de); de_len -= 1; return wasi_errno_noent; }
858 time_t now = time(NULL);
859 des[de].atim = now;
860 des[de].mtim = now;
861 des[de].ctim = now;
862 stream = NULL;
863 } else {
864 if (oflags != (append ? wasi_oflags_creat : wasi_oflags_creat | wasi_oflags_trunc)) {
865 char mode[] = "rb+";
866 if ((fs_rights_base & wasi_rights_fd_write) == 0) mode[2] = '\0';
867 stream = fopen(des[de].host_path, mode);
868 if (stream != NULL) {
869 if (append || excl || trunc) fclose(stream);
870 if (excl) {
871 DirEntry_unlink(de);
872 de_len -= 1;
873 return wasi_errno_exist;
874 }
875 } else if (!creat) { DirEntry_unlink(de); de_len -= 1; return wasi_errno_noent; }
876 }
877 if (append || trunc || stream == NULL) {
878 char mode[] = "wb+";
879 if ((fs_rights_base & wasi_rights_fd_read) == 0) mode[2] = '\0';
880 if (trunc || !append) {
881 stream = fopen(des[de].host_path, mode);
882 if (append && stream != NULL) fclose(stream);
883 }
884 if (append) {
885 mode[0] = 'a';
886 stream = fopen(des[de].host_path, mode);
887 }
888 }
889 if (stream == NULL) { DirEntry_unlink(de); de_len -= 1; return wasi_errno_isdir; }
890 }
891 } else stream = NULL;
892
893#if LOG_TRACE
894 fprintf(stderr, "fd = %u\n", fd_len);
895#endif
896 fds[fd_len].de = de;
897 fds[fd_len].fdflags = fdflags;
898 fds[fd_len].stream = stream;
899 fds[fd_len].fs_rights_inheriting = fs_rights_inheriting;
900 store32_align2(res_fd_ptr, fd_len);
901 fd_len += 1;
902 return wasi_errno_success;
903}
904
905uint32_t wasi_snapshot_preview1_clock_time_get(uint32_t id, uint64_t precision, uint32_t res_timestamp) {
906 uint8_t *const m = *wasm_memory;
907 (void)precision;
908 uint64_t *res_timestamp_ptr = (uint64_t *)&m[res_timestamp];
909#if LOG_TRACE
910 fprintf(stderr, "wasi_snapshot_preview1_clock_time_get(%u, %llu)\n", id, (unsigned long long)precision);
911#endif
912
913 switch (id) {
914 case wasi_clockid_realtime:
915 store64_align3(res_timestamp_ptr, time(NULL) * UINT64_C(1000000000));
916 break;
917 case wasi_clockid_monotonic:
918 case wasi_clockid_process_cputime_id:
919 case wasi_clockid_thread_cputime_id:
920 store64_align3(res_timestamp_ptr, clock() * (UINT64_C(1000000000) / CLOCKS_PER_SEC));
921 break;
922 default: return wasi_errno_inval;
923 }
924 return wasi_errno_success;
925}
926
927uint32_t wasi_snapshot_preview1_path_remove_directory(uint32_t fd, uint32_t path, uint32_t path_len) {
928 uint8_t *const m = *wasm_memory;
929 const char *path_ptr = (const char *)&m[path];
930#if LOG_TRACE
931 fprintf(stderr, "wasi_snapshot_preview1_path_remove_directory(%u, \"%.*s\")\n", fd, (int)path_len, path_ptr);
932#endif
933
934 uint32_t de;
935 enum wasi_errno lookup_errno = DirEntry_lookup(fd, 0, path_ptr, path_len, &de);
936 if (lookup_errno != wasi_errno_success) return lookup_errno;
937 if (des[de].filetype != wasi_filetype_directory) return wasi_errno_notdir;
938 DirEntry_unlink(de);
939 return wasi_errno_success;
940}
941
942uint32_t wasi_snapshot_preview1_path_unlink_file(uint32_t fd, uint32_t path, uint32_t path_len) {
943 uint8_t *const m = *wasm_memory;
944 const char *path_ptr = (const char *)&m[path];
945#if LOG_TRACE
946 fprintf(stderr, "wasi_snapshot_preview1_path_unlink_file(%u, \"%.*s\")\n", fd, (int)path_len, path_ptr);
947#endif
948
949 uint32_t de;
950 enum wasi_errno lookup_errno = DirEntry_lookup(fd, 0, path_ptr, path_len, &de);
951 if (lookup_errno != wasi_errno_success) return lookup_errno;
952 if (des[de].filetype == wasi_filetype_directory) return wasi_errno_isdir;
953 if (des[de].filetype != wasi_filetype_regular_file) panic("unimplemented: path_unlink_file special file");
954 DirEntry_unlink(de);
955 return wasi_errno_success;
956}
957
958uint32_t wasi_snapshot_preview1_fd_pread(uint32_t fd, uint32_t iovs, uint32_t iovs_len, uint64_t offset, uint32_t res_size) {
959 uint8_t *const m = *wasm_memory;
960 struct wasi_ciovec *iovs_ptr = (struct wasi_ciovec *)&m[iovs];
961 uint32_t *res_size_ptr = (uint32_t *)&m[res_size];
962#if LOG_TRACE
963 fprintf(stderr, "wasi_snapshot_preview1_fd_pread(%u, 0x%X, %u)\n", fd, iovs, iovs_len);
964#endif
965
966 if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf;
967 switch (des[fds[fd].de].filetype) {
968 case wasi_filetype_character_device: break;
969 case wasi_filetype_regular_file: break;
970 case wasi_filetype_directory: return wasi_errno_inval;
971 default: panic("unimplemented: fd_pread special file");
972 }
973
974 if (fds[fd].stream == NULL) {
975 store32_align2(res_size_ptr, 0);
976 return wasi_errno_success;
977 }
978
979 fpos_t pos;
980 if (fgetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
981 if (fseek(fds[fd].stream, offset, SEEK_SET) < 0) return wasi_errno_io;
982
983 size_t size = 0;
984 for (uint32_t i = 0; i < iovs_len; i += 1) {
985 uint32_t len = load32_align2(&iovs_ptr[i].len);
986 size_t read_size = 0;
987 if (fds[fd].stream != NULL)
988 read_size = fread(&m[load32_align2(&iovs_ptr[i].ptr)], 1, len, fds[fd].stream);
989 else
990 panic("unimplemented: fd_pread stream=NULL");
991 size += read_size;
992 if (read_size < len) break;
993 }
994
995 if (fsetpos(fds[fd].stream, &pos) < 0) return wasi_errno_io;
996
997 if (size > 0) des[fds[fd].de].atim = time(NULL);
998 store32_align2(res_size_ptr, size);
999 return wasi_errno_success;
1000}
1001
1002uint32_t wasi_snapshot_preview1_fd_seek(uint32_t fd, uint64_t in_offset, uint32_t whence, uint32_t res_filesize) {
1003 uint8_t *const m = *wasm_memory;
1004 int64_t offset = (int64_t)in_offset;
1005 uint64_t *res_filesize_ptr = (uint64_t *)&m[res_filesize];
1006#if LOG_TRACE
1007 fprintf(stderr, "wasi_snapshot_preview1_fd_seek(%u, 0x%lld, %u)\n", fd, (long long)offset, whence);
1008#endif
1009
1010 if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf;
1011 switch (des[fds[fd].de].filetype) {
1012 case wasi_filetype_character_device: break;
1013 case wasi_filetype_regular_file: break;
1014 case wasi_filetype_directory: return wasi_errno_inval;
1015 default: panic("unimplemented: fd_seek special file");
1016 }
1017
1018 if (fds[fd].stream == NULL) return wasi_errno_success;
1019
1020 int seek_whence;
1021 switch (whence) {
1022 case wasi_whence_set:
1023 seek_whence = SEEK_SET;
1024 break;
1025 case wasi_whence_cur:
1026 seek_whence = SEEK_CUR;
1027 break;
1028 case wasi_whence_end:
1029 seek_whence = SEEK_END;
1030 break;
1031 default:
1032 return wasi_errno_inval;
1033 }
1034 if (fseek(fds[fd].stream, offset, seek_whence) < 0) return wasi_errno_io;
1035 long res_offset = ftell(fds[fd].stream);
1036 if (res_offset < 0) return wasi_errno_io;
1037 store64_align3(res_filesize_ptr, res_offset);
1038 return wasi_errno_success;
1039}
1040
1041uint32_t wasi_snapshot_preview1_poll_oneoff(uint32_t in, uint32_t out, uint32_t nsubscriptions, uint32_t res_nevents) {
1042 (void)in;
1043 (void)out;
1044 (void)nsubscriptions;
1045 (void)res_nevents;
1046#if LOG_TRACE
1047 fprintf(stderr, "wasi_snapshot_preview1_poll_oneoff(%u)\n", nsubscriptions);
1048#endif
1049
1050 panic("unimplemented: poll_oneoff");
1051 return wasi_errno_success;
1052}
1053
1054void wasi_snapshot_preview1_debug(uint32_t string, uint64_t x) {
1055 uint8_t *const m = *wasm_memory;
1056 const char *string_ptr = (const char *)&m[string];
1057#if LOG_TRACE
1058 fprintf(stderr, "wasi_snapshot_preview1_debug(\"%s\", %llu, 0x%llX)\n", string_ptr, (unsigned long long)x, (unsigned long long)x);
1059#endif
1060
1061 (void)string_ptr;
1062 (void)x;
1063}