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}