1 //
2 //  m3_api_wasi.c
3 //
4 //  Created by Volodymyr Shymanskyy on 11/20/19.
5 //  Copyright © 2019 Volodymyr Shymanskyy. All rights reserved.
6 //
7 
8 #define _POSIX_C_SOURCE 200809L
9 
10 #include "m3_api_wasi.h"
11 
12 #include "m3_env.h"
13 #include "m3_exception.h"
14 
15 #if defined(d_m3HasWASI)
16 
17 // Fixup wasi_core.h
18 #if defined (M3_COMPILER_MSVC)
19 #  define _Static_assert(...)
20 #  define __attribute__(...)
21 #  define _Noreturn
22 #endif
23 
24 #include "extra/wasi_core.h"
25 
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <time.h>
29 #include <errno.h>
30 #include <stdio.h>
31 #include <fcntl.h>
32 
33 #if defined(APE)
34 // Actually Portable Executable
35 // All functions are already included in cosmopolitan.h
36 #elif defined(__wasi__) || defined(__APPLE__) || defined(__ANDROID_API__) || defined(__OpenBSD__) || defined(__linux__) || defined(__EMSCRIPTEN__)
37 #  include <unistd.h>
38 #  include <sys/uio.h>
39 #  if defined(__APPLE__)
40 #      include <TargetConditionals.h>
41 #      if TARGET_OS_OSX // TARGET_OS_MAC includes iOS
42 #          include <sys/random.h>
43 #      else // iOS / Simulator
44 #          include <Security/Security.h>
45 #      endif
46 #  else
47 #      include <sys/random.h>
48 #  endif
49 #  define HAS_IOVEC
50 #elif defined(_WIN32)
51 #  include <Windows.h>
52 #  include <io.h>
53 // See http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx
54 #  define SystemFunction036 NTAPI SystemFunction036
55 #  include <NTSecAPI.h>
56 #  undef SystemFunction036
57 #  define ssize_t SSIZE_T
58 
59 #  define open  _open
60 #  define read  _read
61 #  define write _write
62 #  define close _close
63 #endif
64 
65 static m3_wasi_context_t* wasi_context;
66 
67 typedef struct wasi_iovec_t
68 {
69     __wasi_size_t buf;
70     __wasi_size_t buf_len;
71 } wasi_iovec_t;
72 
73 #define PREOPEN_CNT   5
74 
75 typedef struct Preopen {
76     int         fd;
77     const char* path;
78     const char* real_path;
79 } Preopen;
80 
81 Preopen preopen[PREOPEN_CNT] = {
82     {  0, "<stdin>" , "" },
83     {  1, "<stdout>", "" },
84     {  2, "<stderr>", "" },
85     { -1, "/"       , "." },
86     { -1, "./"      , "." },
87 };
88 
89 #if defined(APE)
90 #  define APE_SWITCH_BEG
91 #  define APE_SWITCH_END          {}
92 #  define APE_CASE_RET(e1,e2)     if (errnum == e1)    return e2;   else
93 #else
94 #  define APE_SWITCH_BEG          switch (errnum) {
95 #  define APE_SWITCH_END          }
96 #  define APE_CASE_RET(e1,e2)     case e1:   return e2;   break;
97 #endif
98 
99 static
errno_to_wasi(int errnum)100 __wasi_errno_t errno_to_wasi(int errnum) {
101     APE_SWITCH_BEG
102     APE_CASE_RET( EPERM   , __WASI_ERRNO_PERM   )
103     APE_CASE_RET( ENOENT  , __WASI_ERRNO_NOENT  )
104     APE_CASE_RET( ESRCH   , __WASI_ERRNO_SRCH   )
105     APE_CASE_RET( EINTR   , __WASI_ERRNO_INTR   )
106     APE_CASE_RET( EIO     , __WASI_ERRNO_IO     )
107     APE_CASE_RET( ENXIO   , __WASI_ERRNO_NXIO   )
108     APE_CASE_RET( E2BIG   , __WASI_ERRNO_2BIG   )
109     APE_CASE_RET( ENOEXEC , __WASI_ERRNO_NOEXEC )
110     APE_CASE_RET( EBADF   , __WASI_ERRNO_BADF   )
111     APE_CASE_RET( ECHILD  , __WASI_ERRNO_CHILD  )
112     APE_CASE_RET( EAGAIN  , __WASI_ERRNO_AGAIN  )
113     APE_CASE_RET( ENOMEM  , __WASI_ERRNO_NOMEM  )
114     APE_CASE_RET( EACCES  , __WASI_ERRNO_ACCES  )
115     APE_CASE_RET( EFAULT  , __WASI_ERRNO_FAULT  )
116     APE_CASE_RET( EBUSY   , __WASI_ERRNO_BUSY   )
117     APE_CASE_RET( EEXIST  , __WASI_ERRNO_EXIST  )
118     APE_CASE_RET( EXDEV   , __WASI_ERRNO_XDEV   )
119     APE_CASE_RET( ENODEV  , __WASI_ERRNO_NODEV  )
120     APE_CASE_RET( ENOTDIR , __WASI_ERRNO_NOTDIR )
121     APE_CASE_RET( EISDIR  , __WASI_ERRNO_ISDIR  )
122     APE_CASE_RET( EINVAL  , __WASI_ERRNO_INVAL  )
123     APE_CASE_RET( ENFILE  , __WASI_ERRNO_NFILE  )
124     APE_CASE_RET( EMFILE  , __WASI_ERRNO_MFILE  )
125     APE_CASE_RET( ENOTTY  , __WASI_ERRNO_NOTTY  )
126     APE_CASE_RET( ETXTBSY , __WASI_ERRNO_TXTBSY )
127     APE_CASE_RET( EFBIG   , __WASI_ERRNO_FBIG   )
128     APE_CASE_RET( ENOSPC  , __WASI_ERRNO_NOSPC  )
129     APE_CASE_RET( ESPIPE  , __WASI_ERRNO_SPIPE  )
130     APE_CASE_RET( EROFS   , __WASI_ERRNO_ROFS   )
131     APE_CASE_RET( EMLINK  , __WASI_ERRNO_MLINK  )
132     APE_CASE_RET( EPIPE   , __WASI_ERRNO_PIPE   )
133     APE_CASE_RET( EDOM    , __WASI_ERRNO_DOM    )
134     APE_CASE_RET( ERANGE  , __WASI_ERRNO_RANGE  )
135     APE_SWITCH_END
136     return __WASI_ERRNO_INVAL;
137 }
138 
139 #if defined(_WIN32)
140 
141 #if !defined(__MINGW32__)
142 
143 static inline
clock_gettime(int clk_id,struct timespec * spec)144 int clock_gettime(int clk_id, struct timespec *spec)
145 {
146     __int64 wintime; GetSystemTimeAsFileTime((FILETIME*)&wintime);
147     wintime      -= 116444736000000000i64;           //1jan1601 to 1jan1970
148     spec->tv_sec  = wintime / 10000000i64;           //seconds
149     spec->tv_nsec = wintime % 10000000i64 *100;      //nano-seconds
150     return 0;
151 }
152 
153 static inline
clock_getres(int clk_id,struct timespec * spec)154 int clock_getres(int clk_id, struct timespec *spec) {
155     return -1; // Defaults to 1000000
156 }
157 
158 #endif
159 
160 static inline
convert_clockid(__wasi_clockid_t in)161 int convert_clockid(__wasi_clockid_t in) {
162     return 0;
163 }
164 
165 #else // _WIN32
166 
167 static inline
convert_clockid(__wasi_clockid_t in)168 int convert_clockid(__wasi_clockid_t in) {
169     switch (in) {
170     case __WASI_CLOCKID_MONOTONIC:            return CLOCK_MONOTONIC;
171     case __WASI_CLOCKID_PROCESS_CPUTIME_ID:   return CLOCK_PROCESS_CPUTIME_ID;
172     case __WASI_CLOCKID_REALTIME:             return CLOCK_REALTIME;
173     case __WASI_CLOCKID_THREAD_CPUTIME_ID:    return CLOCK_THREAD_CPUTIME_ID;
174     default: return -1;
175     }
176 }
177 
178 #endif // _WIN32
179 
180 static inline
convert_timespec(const struct timespec * ts)181 __wasi_timestamp_t convert_timespec(const struct timespec *ts) {
182     if (ts->tv_sec < 0)
183         return 0;
184     if ((__wasi_timestamp_t)ts->tv_sec >= UINT64_MAX / 1000000000)
185         return UINT64_MAX;
186     return (__wasi_timestamp_t)ts->tv_sec * 1000000000 + ts->tv_nsec;
187 }
188 
189 #if defined(HAS_IOVEC)
190 
191 static inline
copy_iov_to_host(void * _mem,struct iovec * host_iov,wasi_iovec_t * wasi_iov,int32_t iovs_len)192 void copy_iov_to_host(void* _mem, struct iovec* host_iov, wasi_iovec_t* wasi_iov, int32_t iovs_len)
193 {
194     // Convert wasi memory offsets to host addresses
195     for (int i = 0; i < iovs_len; i++) {
196         host_iov[i].iov_base = m3ApiOffsetToPtr(m3ApiReadMem32(&wasi_iov[i].buf));
197         host_iov[i].iov_len  = m3ApiReadMem32(&wasi_iov[i].buf_len);
198     }
199 }
200 
201 #endif
202 
203 /*
204  * WASI API implementation
205  */
206 
m3ApiRawFunction(m3_wasi_generic_args_get)207 m3ApiRawFunction(m3_wasi_generic_args_get)
208 {
209     m3ApiReturnType  (uint32_t)
210     m3ApiGetArgMem   (uint32_t *           , argv)
211     m3ApiGetArgMem   (char *               , argv_buf)
212 
213     m3_wasi_context_t* context = (m3_wasi_context_t*)(_ctx->userdata);
214 
215     if (context == NULL) { m3ApiReturn(__WASI_ERRNO_INVAL); }
216 
217     m3ApiCheckMem(argv, context->argc * sizeof(uint32_t));
218 
219     for (u32 i = 0; i < context->argc; ++i)
220     {
221         m3ApiWriteMem32(&argv[i], m3ApiPtrToOffset(argv_buf));
222 
223         size_t len = strlen (context->argv[i]);
224 
225         m3ApiCheckMem(argv_buf, len);
226         memcpy (argv_buf, context->argv[i], len);
227         argv_buf += len;
228         * argv_buf++ = 0;
229     }
230 
231     m3ApiReturn(__WASI_ERRNO_SUCCESS);
232 }
233 
m3ApiRawFunction(m3_wasi_generic_args_sizes_get)234 m3ApiRawFunction(m3_wasi_generic_args_sizes_get)
235 {
236     m3ApiReturnType  (uint32_t)
237     m3ApiGetArgMem   (__wasi_size_t *      , argc)
238     m3ApiGetArgMem   (__wasi_size_t *      , argv_buf_size)
239 
240     m3ApiCheckMem(argc,             sizeof(__wasi_size_t));
241     m3ApiCheckMem(argv_buf_size,    sizeof(__wasi_size_t));
242 
243     m3_wasi_context_t* context = (m3_wasi_context_t*)(_ctx->userdata);
244 
245     if (context == NULL) { m3ApiReturn(__WASI_ERRNO_INVAL); }
246 
247     __wasi_size_t buf_len = 0;
248     for (u32 i = 0; i < context->argc; ++i)
249     {
250         buf_len += strlen (context->argv[i]) + 1;
251     }
252 
253     m3ApiWriteMem32(argc, context->argc);
254     m3ApiWriteMem32(argv_buf_size, buf_len);
255 
256     m3ApiReturn(__WASI_ERRNO_SUCCESS);
257 }
258 
m3ApiRawFunction(m3_wasi_generic_environ_get)259 m3ApiRawFunction(m3_wasi_generic_environ_get)
260 {
261     m3ApiReturnType  (uint32_t)
262     m3ApiGetArgMem   (uint32_t *           , env)
263     m3ApiGetArgMem   (char *               , env_buf)
264 
265     // TODO
266     m3ApiReturn(__WASI_ERRNO_SUCCESS);
267 }
268 
m3ApiRawFunction(m3_wasi_generic_environ_sizes_get)269 m3ApiRawFunction(m3_wasi_generic_environ_sizes_get)
270 {
271     m3ApiReturnType  (uint32_t)
272     m3ApiGetArgMem   (__wasi_size_t *      , env_count)
273     m3ApiGetArgMem   (__wasi_size_t *      , env_buf_size)
274 
275     m3ApiCheckMem(env_count,    sizeof(__wasi_size_t));
276     m3ApiCheckMem(env_buf_size, sizeof(__wasi_size_t));
277 
278     // TODO
279     m3ApiWriteMem32(env_count,    0);
280     m3ApiWriteMem32(env_buf_size, 0);
281 
282     m3ApiReturn(__WASI_ERRNO_SUCCESS);
283 }
284 
m3ApiRawFunction(m3_wasi_generic_fd_prestat_dir_name)285 m3ApiRawFunction(m3_wasi_generic_fd_prestat_dir_name)
286 {
287     m3ApiReturnType  (uint32_t)
288     m3ApiGetArg      (__wasi_fd_t          , fd)
289     m3ApiGetArgMem   (char *               , path)
290     m3ApiGetArg      (__wasi_size_t        , path_len)
291 
292     m3ApiCheckMem(path, path_len);
293 
294     if (fd < 3 || fd >= PREOPEN_CNT) { m3ApiReturn(__WASI_ERRNO_BADF); }
295     size_t slen = strlen(preopen[fd].path) + 1;
296     memcpy(path, preopen[fd].path, M3_MIN(slen, path_len));
297     m3ApiReturn(__WASI_ERRNO_SUCCESS);
298 }
299 
m3ApiRawFunction(m3_wasi_generic_fd_prestat_get)300 m3ApiRawFunction(m3_wasi_generic_fd_prestat_get)
301 {
302     m3ApiReturnType  (uint32_t)
303     m3ApiGetArg      (__wasi_fd_t          , fd)
304     m3ApiGetArgMem   (uint8_t *            , buf)
305 
306     m3ApiCheckMem(buf, 8);
307 
308     if (fd < 3 || fd >= PREOPEN_CNT) { m3ApiReturn(__WASI_ERRNO_BADF); }
309 
310     m3ApiWriteMem32(buf+0, __WASI_PREOPENTYPE_DIR);
311     m3ApiWriteMem32(buf+4, strlen(preopen[fd].path) + 1);
312     m3ApiReturn(__WASI_ERRNO_SUCCESS);
313 }
314 
m3ApiRawFunction(m3_wasi_generic_fd_fdstat_get)315 m3ApiRawFunction(m3_wasi_generic_fd_fdstat_get)
316 {
317     m3ApiReturnType  (uint32_t)
318     m3ApiGetArg      (__wasi_fd_t          , fd)
319     m3ApiGetArgMem   (__wasi_fdstat_t *    , fdstat)
320 
321     m3ApiCheckMem(fdstat, sizeof(__wasi_fdstat_t));
322 
323 #ifdef _WIN32
324 
325     // TODO: This needs a proper implementation
326     if (fd < PREOPEN_CNT) {
327         fdstat->fs_filetype= __WASI_FILETYPE_DIRECTORY;
328     } else {
329         fdstat->fs_filetype= __WASI_FILETYPE_REGULAR_FILE;
330     }
331 
332     fdstat->fs_flags = 0;
333     fdstat->fs_rights_base = (uint64_t)-1; // all rights
334     fdstat->fs_rights_inheriting = (uint64_t)-1; // all rights
335     m3ApiReturn(__WASI_ERRNO_SUCCESS);
336 #else
337     struct stat fd_stat;
338 
339 #if !defined(APE) // TODO: not implemented in Cosmopolitan
340     int fl = fcntl(fd, F_GETFL);
341     if (fl < 0) { m3ApiReturn(errno_to_wasi(errno)); }
342 #endif
343 
344     fstat(fd, &fd_stat);
345     int mode = fd_stat.st_mode;
346     fdstat->fs_filetype = (S_ISBLK(mode)   ? __WASI_FILETYPE_BLOCK_DEVICE     : 0) |
347                           (S_ISCHR(mode)   ? __WASI_FILETYPE_CHARACTER_DEVICE : 0) |
348                           (S_ISDIR(mode)   ? __WASI_FILETYPE_DIRECTORY        : 0) |
349                           (S_ISREG(mode)   ? __WASI_FILETYPE_REGULAR_FILE     : 0) |
350                           //(S_ISSOCK(mode)  ? __WASI_FILETYPE_SOCKET_STREAM    : 0) |
351                           (S_ISLNK(mode)   ? __WASI_FILETYPE_SYMBOLIC_LINK    : 0);
352 #if !defined(APE)
353     m3ApiWriteMem16(&fdstat->fs_flags,
354                        ((fl & O_APPEND)    ? __WASI_FDFLAGS_APPEND    : 0) |
355                        ((fl & O_DSYNC)     ? __WASI_FDFLAGS_DSYNC     : 0) |
356                        ((fl & O_NONBLOCK)  ? __WASI_FDFLAGS_NONBLOCK  : 0) |
357                        //((fl & O_RSYNC)     ? __WASI_FDFLAGS_RSYNC     : 0) |
358                        ((fl & O_SYNC)      ? __WASI_FDFLAGS_SYNC      : 0));
359 #endif // APE
360 
361     fdstat->fs_rights_base = (uint64_t)-1; // all rights
362 
363     // Make descriptors 0,1,2 look like a TTY
364     if (fd <= 2) {
365         fdstat->fs_rights_base &= ~(__WASI_RIGHTS_FD_SEEK | __WASI_RIGHTS_FD_TELL);
366     }
367 
368     fdstat->fs_rights_inheriting = (uint64_t)-1; // all rights
369     m3ApiReturn(__WASI_ERRNO_SUCCESS);
370 #endif
371 }
372 
m3ApiRawFunction(m3_wasi_generic_fd_fdstat_set_flags)373 m3ApiRawFunction(m3_wasi_generic_fd_fdstat_set_flags)
374 {
375     m3ApiReturnType  (uint32_t)
376     m3ApiGetArg      (__wasi_fd_t          , fd)
377     m3ApiGetArg      (__wasi_fdflags_t     , flags)
378 
379     // TODO
380 
381     m3ApiReturn(__WASI_ERRNO_SUCCESS);
382 }
383 
m3ApiRawFunction(m3_wasi_unstable_fd_seek)384 m3ApiRawFunction(m3_wasi_unstable_fd_seek)
385 {
386     m3ApiReturnType  (uint32_t)
387     m3ApiGetArg      (__wasi_fd_t          , fd)
388     m3ApiGetArg      (__wasi_filedelta_t   , offset)
389     m3ApiGetArg      (uint32_t             , wasi_whence)
390     m3ApiGetArgMem   (__wasi_filesize_t *  , result)
391 
392     m3ApiCheckMem(result, sizeof(__wasi_filesize_t));
393 
394     int whence;
395 
396     switch (wasi_whence) {
397     case 0: whence = SEEK_CUR; break;
398     case 1: whence = SEEK_END; break;
399     case 2: whence = SEEK_SET; break;
400     default:                m3ApiReturn(__WASI_ERRNO_INVAL);
401     }
402 
403     int64_t ret;
404 #if defined(M3_COMPILER_MSVC) || defined(__MINGW32__)
405     ret = _lseeki64(fd, offset, whence);
406 #else
407     ret = lseek(fd, offset, whence);
408 #endif
409     if (ret < 0) { m3ApiReturn(errno_to_wasi(errno)); }
410     m3ApiWriteMem64(result, ret);
411     m3ApiReturn(__WASI_ERRNO_SUCCESS);
412 }
413 
m3ApiRawFunction(m3_wasi_snapshot_preview1_fd_seek)414 m3ApiRawFunction(m3_wasi_snapshot_preview1_fd_seek)
415 {
416     m3ApiReturnType  (uint32_t)
417     m3ApiGetArg      (__wasi_fd_t          , fd)
418     m3ApiGetArg      (__wasi_filedelta_t   , offset)
419     m3ApiGetArg      (uint32_t             , wasi_whence)
420     m3ApiGetArgMem   (__wasi_filesize_t *  , result)
421 
422     m3ApiCheckMem(result, sizeof(__wasi_filesize_t));
423 
424     int whence;
425 
426     switch (wasi_whence) {
427     case 0: whence = SEEK_SET; break;
428     case 1: whence = SEEK_CUR; break;
429     case 2: whence = SEEK_END; break;
430     default:                m3ApiReturn(__WASI_ERRNO_INVAL);
431     }
432 
433     int64_t ret;
434 #if defined(M3_COMPILER_MSVC) || defined(__MINGW32__)
435     ret = _lseeki64(fd, offset, whence);
436 #else
437     ret = lseek(fd, offset, whence);
438 #endif
439     if (ret < 0) { m3ApiReturn(errno_to_wasi(errno)); }
440     m3ApiWriteMem64(result, ret);
441     m3ApiReturn(__WASI_ERRNO_SUCCESS);
442 }
443 
444 
m3ApiRawFunction(m3_wasi_generic_path_open)445 m3ApiRawFunction(m3_wasi_generic_path_open)
446 {
447     m3ApiReturnType  (uint32_t)
448     m3ApiGetArg      (__wasi_fd_t          , dirfd)
449     m3ApiGetArg      (__wasi_lookupflags_t , dirflags)
450     m3ApiGetArgMem   (const char *         , path)
451     m3ApiGetArg      (__wasi_size_t        , path_len)
452     m3ApiGetArg      (__wasi_oflags_t      , oflags)
453     m3ApiGetArg      (__wasi_rights_t      , fs_rights_base)
454     m3ApiGetArg      (__wasi_rights_t      , fs_rights_inheriting)
455     m3ApiGetArg      (__wasi_fdflags_t     , fs_flags)
456     m3ApiGetArgMem   (__wasi_fd_t *        , fd)
457 
458     m3ApiCheckMem(path, path_len);
459     m3ApiCheckMem(fd,   sizeof(__wasi_fd_t));
460 
461     if (path_len >= 512)
462         m3ApiReturn(__WASI_ERRNO_INVAL);
463 
464     // copy path so we can ensure it is NULL terminated
465 #if defined(M3_COMPILER_MSVC)
466     char host_path[512];
467 #else
468     char host_path[path_len+1];
469 #endif
470     memcpy (host_path, path, path_len);
471     host_path[path_len] = '\0'; // NULL terminator
472 
473 #if defined(APE)
474     // TODO: This all needs a proper implementation
475 
476     int flags = ((oflags & __WASI_OFLAGS_CREAT)             ? O_CREAT     : 0) |
477                 ((oflags & __WASI_OFLAGS_EXCL)              ? O_EXCL      : 0) |
478                 ((oflags & __WASI_OFLAGS_TRUNC)             ? O_TRUNC     : 0) |
479                 ((fs_flags & __WASI_FDFLAGS_APPEND)     ? O_APPEND    : 0);
480 
481     if ((fs_rights_base & __WASI_RIGHTS_FD_READ) &&
482         (fs_rights_base & __WASI_RIGHTS_FD_WRITE)) {
483         flags |= O_RDWR;
484     } else if ((fs_rights_base & __WASI_RIGHTS_FD_WRITE)) {
485         flags |= O_WRONLY;
486     } else if ((fs_rights_base & __WASI_RIGHTS_FD_READ)) {
487         flags |= O_RDONLY; // no-op because O_RDONLY is 0
488     }
489     int mode = 0644;
490 
491     int host_fd = open (host_path, flags, mode);
492 
493     if (host_fd < 0)
494     {
495         m3ApiReturn(errno_to_wasi (errno));
496     }
497     else
498     {
499         m3ApiWriteMem32(fd, host_fd);
500         m3ApiReturn(__WASI_ERRNO_SUCCESS);
501     }
502 #elif defined(_WIN32)
503     // TODO: This all needs a proper implementation
504 
505     int flags = ((oflags & __WASI_OFLAGS_CREAT)             ? _O_CREAT     : 0) |
506                 ((oflags & __WASI_OFLAGS_EXCL)              ? _O_EXCL      : 0) |
507                 ((oflags & __WASI_OFLAGS_TRUNC)             ? _O_TRUNC     : 0) |
508                 ((fs_flags & __WASI_FDFLAGS_APPEND)         ? _O_APPEND    : 0) |
509                 _O_BINARY;
510 
511     if ((fs_rights_base & __WASI_RIGHTS_FD_READ) &&
512         (fs_rights_base & __WASI_RIGHTS_FD_WRITE)) {
513         flags |= _O_RDWR;
514     } else if ((fs_rights_base & __WASI_RIGHTS_FD_WRITE)) {
515         flags |= _O_WRONLY;
516     } else if ((fs_rights_base & __WASI_RIGHTS_FD_READ)) {
517         flags |= _O_RDONLY; // no-op because O_RDONLY is 0
518     }
519     int mode = 0644;
520 
521     int host_fd = open (host_path, flags, mode);
522 
523     if (host_fd < 0)
524     {
525         m3ApiReturn(errno_to_wasi (errno));
526     }
527     else
528     {
529         m3ApiWriteMem32(fd, host_fd);
530         m3ApiReturn(__WASI_ERRNO_SUCCESS);
531     }
532 #else
533     // translate o_flags and fs_flags into flags and mode
534     int flags = ((oflags & __WASI_OFLAGS_CREAT)             ? O_CREAT     : 0) |
535                 //((oflags & __WASI_OFLAGS_DIRECTORY)         ? O_DIRECTORY : 0) |
536                 ((oflags & __WASI_OFLAGS_EXCL)              ? O_EXCL      : 0) |
537                 ((oflags & __WASI_OFLAGS_TRUNC)             ? O_TRUNC     : 0) |
538                 ((fs_flags & __WASI_FDFLAGS_APPEND)     ? O_APPEND    : 0) |
539                 ((fs_flags & __WASI_FDFLAGS_DSYNC)      ? O_DSYNC     : 0) |
540                 ((fs_flags & __WASI_FDFLAGS_NONBLOCK)   ? O_NONBLOCK  : 0) |
541                 //((fs_flags & __WASI_FDFLAGS_RSYNC)      ? O_RSYNC     : 0) |
542                 ((fs_flags & __WASI_FDFLAGS_SYNC)       ? O_SYNC      : 0);
543     if ((fs_rights_base & __WASI_RIGHTS_FD_READ) &&
544         (fs_rights_base & __WASI_RIGHTS_FD_WRITE)) {
545         flags |= O_RDWR;
546     } else if ((fs_rights_base & __WASI_RIGHTS_FD_WRITE)) {
547         flags |= O_WRONLY;
548     } else if ((fs_rights_base & __WASI_RIGHTS_FD_READ)) {
549         flags |= O_RDONLY; // no-op because O_RDONLY is 0
550     }
551     int mode = 0644;
552     int host_fd = openat (preopen[dirfd].fd, host_path, flags, mode);
553 
554     if (host_fd < 0)
555     {
556         m3ApiReturn(errno_to_wasi (errno));
557     }
558     else
559     {
560         m3ApiWriteMem32(fd, host_fd);
561         m3ApiReturn(__WASI_ERRNO_SUCCESS);
562     }
563 #endif
564 }
565 
m3ApiRawFunction(m3_wasi_generic_fd_read)566 m3ApiRawFunction(m3_wasi_generic_fd_read)
567 {
568     m3ApiReturnType  (uint32_t)
569     m3ApiGetArg      (__wasi_fd_t          , fd)
570     m3ApiGetArgMem   (wasi_iovec_t *       , wasi_iovs)
571     m3ApiGetArg      (__wasi_size_t        , iovs_len)
572     m3ApiGetArgMem   (__wasi_size_t *      , nread)
573 
574     m3ApiCheckMem(wasi_iovs,    iovs_len * sizeof(wasi_iovec_t));
575     m3ApiCheckMem(nread,        sizeof(__wasi_size_t));
576 
577 #if defined(HAS_IOVEC)
578     struct iovec iovs[iovs_len];
579     copy_iov_to_host(_mem, iovs, wasi_iovs, iovs_len);
580 
581     ssize_t ret = readv(fd, iovs, iovs_len);
582     if (ret < 0) { m3ApiReturn(errno_to_wasi(errno)); }
583     m3ApiWriteMem32(nread, ret);
584     m3ApiReturn(__WASI_ERRNO_SUCCESS);
585 #else
586     ssize_t res = 0;
587     for (__wasi_size_t i = 0; i < iovs_len; i++) {
588         void* addr = m3ApiOffsetToPtr(m3ApiReadMem32(&wasi_iovs[i].buf));
589         size_t len = m3ApiReadMem32(&wasi_iovs[i].buf_len);
590         if (len == 0) continue;
591 
592         int ret = read (fd, addr, len);
593         if (ret < 0) m3ApiReturn(errno_to_wasi(errno));
594         res += ret;
595         if ((size_t)ret < len) break;
596     }
597     m3ApiWriteMem32(nread, res);
598     m3ApiReturn(__WASI_ERRNO_SUCCESS);
599 #endif
600 }
601 
m3ApiRawFunction(m3_wasi_generic_fd_write)602 m3ApiRawFunction(m3_wasi_generic_fd_write)
603 {
604     m3ApiReturnType  (uint32_t)
605     m3ApiGetArg      (__wasi_fd_t          , fd)
606     m3ApiGetArgMem   (wasi_iovec_t *       , wasi_iovs)
607     m3ApiGetArg      (__wasi_size_t        , iovs_len)
608     m3ApiGetArgMem   (__wasi_size_t *      , nwritten)
609 
610     m3ApiCheckMem(wasi_iovs,    iovs_len * sizeof(wasi_iovec_t));
611     m3ApiCheckMem(nwritten,     sizeof(__wasi_size_t));
612 
613 #if defined(HAS_IOVEC)
614     struct iovec iovs[iovs_len];
615     copy_iov_to_host(_mem, iovs, wasi_iovs, iovs_len);
616 
617     ssize_t ret = writev(fd, iovs, iovs_len);
618     if (ret < 0) { m3ApiReturn(errno_to_wasi(errno)); }
619     m3ApiWriteMem32(nwritten, ret);
620     m3ApiReturn(__WASI_ERRNO_SUCCESS);
621 #else
622     ssize_t res = 0;
623     for (__wasi_size_t i = 0; i < iovs_len; i++) {
624         void* addr = m3ApiOffsetToPtr(m3ApiReadMem32(&wasi_iovs[i].buf));
625         size_t len = m3ApiReadMem32(&wasi_iovs[i].buf_len);
626         if (len == 0) continue;
627 
628         int ret = write (fd, addr, len);
629         if (ret < 0) m3ApiReturn(errno_to_wasi(errno));
630         res += ret;
631         if ((size_t)ret < len) break;
632     }
633     m3ApiWriteMem32(nwritten, res);
634     m3ApiReturn(__WASI_ERRNO_SUCCESS);
635 #endif
636 }
637 
m3ApiRawFunction(m3_wasi_generic_fd_close)638 m3ApiRawFunction(m3_wasi_generic_fd_close)
639 {
640     m3ApiReturnType  (uint32_t)
641     m3ApiGetArg      (__wasi_fd_t, fd)
642 
643     int ret = close(fd);
644     m3ApiReturn(ret == 0 ? __WASI_ERRNO_SUCCESS : ret);
645 }
646 
m3ApiRawFunction(m3_wasi_generic_fd_datasync)647 m3ApiRawFunction(m3_wasi_generic_fd_datasync)
648 {
649     m3ApiReturnType  (uint32_t)
650     m3ApiGetArg      (__wasi_fd_t, fd)
651 
652 #if defined(_WIN32)
653     int ret = _commit(fd);
654 #elif defined(__APPLE__)
655     int ret = fsync(fd);
656 #elif defined(__ANDROID_API__) || defined(__OpenBSD__) || defined(__linux__) || defined(__EMSCRIPTEN__)
657     int ret = fdatasync(fd);
658 #else
659     int ret = __WASI_ERRNO_NOSYS;
660 #endif
661     m3ApiReturn(ret == 0 ? __WASI_ERRNO_SUCCESS : ret);
662 }
663 
m3ApiRawFunction(m3_wasi_generic_random_get)664 m3ApiRawFunction(m3_wasi_generic_random_get)
665 {
666     m3ApiReturnType  (uint32_t)
667     m3ApiGetArgMem   (uint8_t *            , buf)
668     m3ApiGetArg      (__wasi_size_t        , buf_len)
669 
670     m3ApiCheckMem(buf, buf_len);
671 
672     while (1) {
673         ssize_t retlen = 0;
674 
675 #if defined(__wasi__) || defined(__APPLE__) || defined(__ANDROID_API__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
676         size_t reqlen = M3_MIN (buf_len, 256);
677 #   if defined(__APPLE__) && (TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR)
678         retlen = SecRandomCopyBytes(kSecRandomDefault, reqlen, buf) < 0 ? -1 : reqlen;
679 #   else
680         retlen = getentropy(buf, reqlen) < 0 ? -1 : reqlen;
681 #   endif
682 #elif defined(__FreeBSD__) || defined(__linux__) || defined(__DragonFly__)
683         retlen = getrandom(buf, buf_len, 0);
684 #elif defined(_WIN32)
685         if (RtlGenRandom(buf, buf_len) == TRUE) {
686             m3ApiReturn(__WASI_ERRNO_SUCCESS);
687         }
688 #else
689         m3ApiReturn(__WASI_ERRNO_NOSYS);
690 #endif
691         if (retlen < 0) {
692             if (errno == EINTR || errno == EAGAIN) {
693                 continue;
694             }
695             m3ApiReturn(errno_to_wasi(errno));
696         } else if (retlen == buf_len) {
697             m3ApiReturn(__WASI_ERRNO_SUCCESS);
698         } else {
699             buf     += retlen;
700             buf_len -= retlen;
701         }
702     }
703 }
704 
m3ApiRawFunction(m3_wasi_generic_clock_res_get)705 m3ApiRawFunction(m3_wasi_generic_clock_res_get)
706 {
707     m3ApiReturnType  (uint32_t)
708     m3ApiGetArg      (__wasi_clockid_t     , wasi_clk_id)
709     m3ApiGetArgMem   (__wasi_timestamp_t * , resolution)
710 
711     m3ApiCheckMem(resolution, sizeof(__wasi_timestamp_t));
712 
713     int clk = convert_clockid(wasi_clk_id);
714     if (clk < 0) m3ApiReturn(__WASI_ERRNO_INVAL);
715 
716     struct timespec tp;
717     if (clock_getres(clk, &tp) != 0) {
718         m3ApiWriteMem64(resolution, 1000000);
719     } else {
720         m3ApiWriteMem64(resolution, convert_timespec(&tp));
721     }
722 
723     m3ApiReturn(__WASI_ERRNO_SUCCESS);
724 }
725 
m3ApiRawFunction(m3_wasi_generic_clock_time_get)726 m3ApiRawFunction(m3_wasi_generic_clock_time_get)
727 {
728     m3ApiReturnType  (uint32_t)
729     m3ApiGetArg      (__wasi_clockid_t     , wasi_clk_id)
730     m3ApiGetArg      (__wasi_timestamp_t   , precision)
731     m3ApiGetArgMem   (__wasi_timestamp_t * , time)
732 
733     m3ApiCheckMem(time, sizeof(__wasi_timestamp_t));
734 
735     int clk = convert_clockid(wasi_clk_id);
736     if (clk < 0) m3ApiReturn(__WASI_ERRNO_INVAL);
737 
738     struct timespec tp;
739     if (clock_gettime(clk, &tp) != 0) {
740         m3ApiReturn(errno_to_wasi(errno));
741     }
742 
743     m3ApiWriteMem64(time, convert_timespec(&tp));
744     m3ApiReturn(__WASI_ERRNO_SUCCESS);
745 }
746 
m3ApiRawFunction(m3_wasi_generic_proc_exit)747 m3ApiRawFunction(m3_wasi_generic_proc_exit)
748 {
749     m3ApiGetArg      (uint32_t, code)
750 
751     m3_wasi_context_t* context = (m3_wasi_context_t*)(_ctx->userdata);
752 
753     if (context) {
754         context->exit_code = code;
755     }
756 
757     m3ApiTrap(m3Err_trapExit);
758 }
759 
760 
761 static
SuppressLookupFailure(M3Result i_result)762 M3Result SuppressLookupFailure(M3Result i_result)
763 {
764     if (i_result == m3Err_functionLookupFailed)
765         return m3Err_none;
766     else
767         return i_result;
768 }
769 
m3_GetWasiContext()770 m3_wasi_context_t* m3_GetWasiContext()
771 {
772     return wasi_context;
773 }
774 
775 
m3_LinkWASI(IM3Module module)776 M3Result  m3_LinkWASI  (IM3Module module)
777 {
778     M3Result result = m3Err_none;
779 
780 #ifdef _WIN32
781     setmode(fileno(stdin),  O_BINARY);
782     setmode(fileno(stdout), O_BINARY);
783     setmode(fileno(stderr), O_BINARY);
784 
785 #else
786     // Preopen dirs
787     for (int i = 3; i < PREOPEN_CNT; i++) {
788         preopen[i].fd = open(preopen[i].real_path, O_RDONLY);
789     }
790 #endif
791 
792     if (!wasi_context) {
793         wasi_context = (m3_wasi_context_t*)malloc(sizeof(m3_wasi_context_t));
794         wasi_context->exit_code = 0;
795         wasi_context->argc = 0;
796         wasi_context->argv = 0;
797     }
798 
799     static const char* namespaces[2] = { "wasi_unstable", "wasi_snapshot_preview1" };
800 
801     // fd_seek is incompatible
802 _   (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_unstable",          "fd_seek",     "i(iIi*)", &m3_wasi_unstable_fd_seek)));
803 _   (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_snapshot_preview1", "fd_seek",     "i(iIi*)", &m3_wasi_snapshot_preview1_fd_seek)));
804 
805     for (int i=0; i<2; i++)
806     {
807         const char* wasi = namespaces[i];
808 
809 _       (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_get",           "i(**)",   &m3_wasi_generic_args_get, wasi_context)));
810 _       (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_sizes_get",     "i(**)",   &m3_wasi_generic_args_sizes_get, wasi_context)));
811 _       (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_res_get",        "i(i*)",   &m3_wasi_generic_clock_res_get)));
812 _       (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_time_get",       "i(iI*)",  &m3_wasi_generic_clock_time_get)));
813 _       (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_get",          "i(**)",   &m3_wasi_generic_environ_get)));
814 _       (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_sizes_get",    "i(**)",   &m3_wasi_generic_environ_sizes_get)));
815 
816 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_advise",            "i(iIIi)", )));
817 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_allocate",          "i(iII)",  )));
818 _       (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_close",             "i(i)",    &m3_wasi_generic_fd_close)));
819 _       (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_datasync",          "i(i)",    &m3_wasi_generic_fd_datasync)));
820 _       (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_get",        "i(i*)",   &m3_wasi_generic_fd_fdstat_get)));
821 _       (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_set_flags",  "i(ii)",   &m3_wasi_generic_fd_fdstat_set_flags)));
822 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_set_rights", "i(iII)",  )));
823 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_get",      "i(i*)",   )));
824 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_set_size", "i(iI)",   )));
825 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_set_times","i(iIIi)", )));
826 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_pread",             "i(i*iI*)",)));
827 _       (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_get",       "i(i*)",   &m3_wasi_generic_fd_prestat_get)));
828 _       (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_dir_name",  "i(i*i)",  &m3_wasi_generic_fd_prestat_dir_name)));
829 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_pwrite",            "i(i*iI*)",)));
830 _       (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_read",              "i(i*i*)", &m3_wasi_generic_fd_read)));
831 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_readdir",           "i(i*iI*)",)));
832 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_renumber",          "i(ii)",   )));
833 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_sync",              "i(i)",    )));
834 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_tell",              "i(i*)",   )));
835 _       (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_write",             "i(i*i*)", &m3_wasi_generic_fd_write)));
836 
837 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_create_directory",    "i(i*i)",       )));
838 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_filestat_get",        "i(ii*i*)",     &m3_wasi_generic_path_filestat_get)));
839 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_filestat_set_times",  "i(ii*iIIi)",   )));
840 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_link",                "i(ii*ii*i)",   )));
841 _       (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_open",                "i(ii*iiIIi*)", &m3_wasi_generic_path_open)));
842 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_readlink",            "i(i*i*i*)",    )));
843 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_remove_directory",    "i(i*i)",       )));
844 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_rename",              "i(i*ii*i)",    )));
845 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_symlink",             "i(*ii*i)",     )));
846 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_unlink_file",         "i(i*i)",       )));
847 
848 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "poll_oneoff",          "i(**i*)", &m3_wasi_generic_poll_oneoff)));
849 _       (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "proc_exit",          "v(i)",    &m3_wasi_generic_proc_exit, wasi_context)));
850 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "proc_raise",           "i(i)",    )));
851 _       (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "random_get",           "i(*i)",   &m3_wasi_generic_random_get)));
852 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sched_yield",          "i()",     )));
853 
854 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sock_recv",            "i(i*ii**)",        )));
855 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sock_send",            "i(i*ii*)",         )));
856 //_     (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sock_shutdown",        "i(ii)",            )));
857     }
858 
859 _catch:
860     return result;
861 }
862 
863 #endif // d_m3HasWASI
864