1 package gojs
2
3 import (
4 "context"
5 "fmt"
6
7 "github.com/tetratelabs/wazero/api"
8 experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
9 "github.com/tetratelabs/wazero/internal/gojs/custom"
10 "github.com/tetratelabs/wazero/internal/gojs/goos"
11 "github.com/tetratelabs/wazero/internal/gojs/util"
12 internalsys "github.com/tetratelabs/wazero/internal/sys"
13 "github.com/tetratelabs/wazero/internal/wasm"
14 "github.com/tetratelabs/wazero/sys"
15 )
16
17 var (
18
19 jsfsConstants = newJsVal(goos.RefJsfsConstants, "constants").
20 addProperties(map[string]interface{}{
21 "O_WRONLY": oWRONLY,
22 "O_RDWR": oRDWR,
23 "O_CREAT": oCREAT,
24 "O_TRUNC": oTRUNC,
25 "O_APPEND": oAPPEND,
26 "O_EXCL": oEXCL,
27 })
28
29
30 oWRONLY = float64(experimentalsys.O_WRONLY)
31
32
33 oRDWR = float64(experimentalsys.O_RDWR)
34
35
36 oCREAT = float64(experimentalsys.O_CREAT)
37
38
39 oTRUNC = float64(experimentalsys.O_TRUNC)
40
41
42 oAPPEND = float64(experimentalsys.O_APPEND)
43
44
45 oEXCL = float64(experimentalsys.O_EXCL)
46 )
47
48
49
50
51
52
53 func newJsFs(proc *processState) *jsVal {
54 return newJsVal(goos.RefJsfs, custom.NameFs).
55 addProperties(map[string]interface{}{
56 "constants": jsfsConstants,
57 }).
58 addFunction(custom.NameFsOpen, &jsfsOpen{proc: proc}).
59 addFunction(custom.NameFsStat, &jsfsStat{proc: proc}).
60 addFunction(custom.NameFsFstat, jsfsFstat{}).
61 addFunction(custom.NameFsLstat, &jsfsLstat{proc: proc}).
62 addFunction(custom.NameFsClose, jsfsClose{}).
63 addFunction(custom.NameFsRead, jsfsRead{}).
64 addFunction(custom.NameFsWrite, jsfsWrite{}).
65 addFunction(custom.NameFsReaddir, &jsfsReaddir{proc: proc}).
66 addFunction(custom.NameFsMkdir, &jsfsMkdir{proc: proc}).
67 addFunction(custom.NameFsRmdir, &jsfsRmdir{proc: proc}).
68 addFunction(custom.NameFsRename, &jsfsRename{proc: proc}).
69 addFunction(custom.NameFsUnlink, &jsfsUnlink{proc: proc}).
70 addFunction(custom.NameFsUtimes, &jsfsUtimes{proc: proc}).
71 addFunction(custom.NameFsChmod, &jsfsChmod{proc: proc}).
72 addFunction(custom.NameFsFchmod, jsfsFchmod{}).
73 addFunction(custom.NameFsChown, &jsfsChown{proc: proc}).
74 addFunction(custom.NameFsFchown, jsfsFchown{}).
75 addFunction(custom.NameFsLchown, &jsfsLchown{proc: proc}).
76 addFunction(custom.NameFsTruncate, &jsfsTruncate{proc: proc}).
77 addFunction(custom.NameFsFtruncate, jsfsFtruncate{}).
78 addFunction(custom.NameFsReadlink, &jsfsReadlink{proc: proc}).
79 addFunction(custom.NameFsLink, &jsfsLink{proc: proc}).
80 addFunction(custom.NameFsSymlink, &jsfsSymlink{proc: proc}).
81 addFunction(custom.NameFsFsync, jsfsFsync{})
82 }
83
84
85
86
87 type jsfsOpen struct {
88 proc *processState
89 }
90
91 func (o *jsfsOpen) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
92 path := util.ResolvePath(o.proc.cwd, args[0].(string))
93
94
95 flags := experimentalsys.Oflag(toUint64(args[1]))
96 perm := custom.FromJsMode(goos.ValueToUint32(args[2]), o.proc.umask)
97 callback := args[3].(funcWrapper)
98
99 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
100
101 fd, errno := fsc.OpenFile(fsc.RootFS(), path, flags, perm)
102
103 return callback.invoke(ctx, mod, goos.RefJsfs, maybeError(errno), fd)
104 }
105
106
107
108
109 type jsfsStat struct {
110 proc *processState
111 }
112
113 func (s *jsfsStat) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
114 path := util.ResolvePath(s.proc.cwd, args[0].(string))
115 callback := args[1].(funcWrapper)
116
117 stat, err := syscallStat(mod, path)
118 return callback.invoke(ctx, mod, goos.RefJsfs, err, stat)
119 }
120
121
122 func syscallStat(mod api.Module, path string) (*jsSt, error) {
123 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
124
125 if st, errno := fsc.RootFS().Stat(path); errno != 0 {
126 return nil, errno
127 } else {
128 return newJsSt(st), nil
129 }
130 }
131
132
133
134
135 type jsfsLstat struct {
136 proc *processState
137 }
138
139 func (l *jsfsLstat) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
140 path := util.ResolvePath(l.proc.cwd, args[0].(string))
141 callback := args[1].(funcWrapper)
142
143 lstat, err := syscallLstat(mod, path)
144
145 return callback.invoke(ctx, mod, goos.RefJsfs, err, lstat)
146 }
147
148
149 func syscallLstat(mod api.Module, path string) (*jsSt, error) {
150 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
151
152 if st, errno := fsc.RootFS().Lstat(path); errno != 0 {
153 return nil, errno
154 } else {
155 return newJsSt(st), nil
156 }
157 }
158
159
160
161
162 type jsfsFstat struct{}
163
164 func (jsfsFstat) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
165 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
166
167 fd := goos.ValueToInt32(args[0])
168 callback := args[1].(funcWrapper)
169
170 fstat, err := syscallFstat(fsc, fd)
171 return callback.invoke(ctx, mod, goos.RefJsfs, err, fstat)
172 }
173
174
175 func syscallFstat(fsc *internalsys.FSContext, fd int32) (*jsSt, error) {
176 f, ok := fsc.LookupFile(fd)
177 if !ok {
178 return nil, experimentalsys.EBADF
179 }
180
181 if st, errno := f.File.Stat(); errno != 0 {
182 return nil, errno
183 } else {
184 return newJsSt(st), nil
185 }
186 }
187
188 func newJsSt(st sys.Stat_t) *jsSt {
189 ret := &jsSt{}
190 ret.isDir = st.Mode.IsDir()
191 ret.dev = st.Dev
192 ret.ino = st.Ino
193 ret.mode = custom.ToJsMode(st.Mode)
194 ret.nlink = uint32(st.Nlink)
195 ret.size = st.Size
196 ret.atimeMs = st.Atim / 1e6
197 ret.mtimeMs = st.Mtim / 1e6
198 ret.ctimeMs = st.Ctim / 1e6
199 return ret
200 }
201
202
203 type jsfsClose struct{}
204
205 func (jsfsClose) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
206 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
207
208 fd := goos.ValueToInt32(args[0])
209 callback := args[1].(funcWrapper)
210
211 errno := fsc.CloseFile(fd)
212
213 return jsfsInvoke(ctx, mod, callback, errno)
214 }
215
216
217
218
219
220 type jsfsRead struct{}
221
222 func (jsfsRead) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
223 fd := goos.ValueToInt32(args[0])
224 buf, ok := args[1].(*goos.ByteArray)
225 if !ok {
226 return nil, fmt.Errorf("arg[1] is %v not a []byte", args[1])
227 }
228 offset := goos.ValueToUint32(args[2])
229 byteCount := goos.ValueToUint32(args[3])
230 fOffset := args[4]
231 callback := args[5].(funcWrapper)
232
233 var err error
234 n, errno := syscallRead(mod, fd, fOffset, buf.Unwrap()[offset:offset+byteCount])
235 if errno != 0 {
236 err = errno
237 }
238
239 return callback.invoke(ctx, mod, goos.RefJsfs, err, uint32(n))
240 }
241
242
243 func syscallRead(mod api.Module, fd int32, offset interface{}, buf []byte) (n int, errno experimentalsys.Errno) {
244 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
245
246 if f, ok := fsc.LookupFile(fd); !ok {
247 return 0, experimentalsys.EBADF
248 } else if offset != nil {
249 return f.File.Pread(buf, toInt64(offset))
250 } else {
251 return f.File.Read(buf)
252 }
253 }
254
255
256
257
258
259
260 type jsfsWrite struct{}
261
262 func (jsfsWrite) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
263 fd := goos.ValueToInt32(args[0])
264 buf, ok := args[1].(*goos.ByteArray)
265 if !ok {
266 return nil, fmt.Errorf("arg[1] is %v not a []byte", args[1])
267 }
268 offset := goos.ValueToUint32(args[2])
269 byteCount := goos.ValueToUint32(args[3])
270 fOffset := args[4]
271 callback := args[5].(funcWrapper)
272
273 if byteCount > 0 {
274 n, errno := syscallWrite(mod, fd, fOffset, buf.Unwrap()[offset:offset+byteCount])
275 var err error
276 if errno != 0 {
277 err = errno
278 }
279
280 return callback.invoke(ctx, mod, goos.RefJsfs, err, uint32(n))
281 }
282 return callback.invoke(ctx, mod, goos.RefJsfs, nil, goos.RefValueZero)
283 }
284
285
286 func syscallWrite(mod api.Module, fd int32, offset interface{}, buf []byte) (n int, errno experimentalsys.Errno) {
287 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
288 if f, ok := fsc.LookupFile(fd); !ok {
289 errno = experimentalsys.EBADF
290 } else if offset != nil {
291 n, errno = f.File.Pwrite(buf, toInt64(offset))
292 } else {
293 n, errno = f.File.Write(buf)
294 }
295 if errno == experimentalsys.ENOSYS {
296 errno = experimentalsys.EBADF
297 }
298 return
299 }
300
301
302
303
304
305 type jsfsReaddir struct {
306 proc *processState
307 }
308
309 func (r *jsfsReaddir) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
310 path := util.ResolvePath(r.proc.cwd, args[0].(string))
311 callback := args[1].(funcWrapper)
312
313 stat, err := syscallReaddir(ctx, mod, path)
314 return callback.invoke(ctx, mod, goos.RefJsfs, err, stat)
315 }
316
317 func syscallReaddir(_ context.Context, mod api.Module, name string) (*objectArray, error) {
318 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
319
320
321 f, errno := fsc.RootFS().OpenFile(name, experimentalsys.O_RDONLY, 0)
322 if errno != 0 {
323 return nil, errno
324 }
325 defer f.Close()
326
327 if dirents, errno := f.Readdir(-1); errno != 0 {
328 return nil, errno
329 } else {
330 entries := make([]interface{}, 0, len(dirents))
331 for _, e := range dirents {
332 entries = append(entries, e.Name)
333 }
334 return &objectArray{entries}, nil
335 }
336 }
337
338
339
340
341 type jsfsMkdir struct {
342 proc *processState
343 }
344
345 func (m *jsfsMkdir) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
346 path := util.ResolvePath(m.proc.cwd, args[0].(string))
347 perm := custom.FromJsMode(goos.ValueToUint32(args[1]), m.proc.umask)
348 callback := args[2].(funcWrapper)
349
350 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
351 root := fsc.RootFS()
352
353 var fd int32
354 var errno experimentalsys.Errno
355
356 if perm == 0 {
357 perm = 0o0500
358 }
359 if errno = root.Mkdir(path, perm); errno == 0 {
360 fd, errno = fsc.OpenFile(root, path, experimentalsys.O_RDONLY, 0)
361 }
362
363 return callback.invoke(ctx, mod, goos.RefJsfs, maybeError(errno), fd)
364 }
365
366
367
368
369 type jsfsRmdir struct {
370 proc *processState
371 }
372
373 func (r *jsfsRmdir) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
374 path := util.ResolvePath(r.proc.cwd, args[0].(string))
375 callback := args[1].(funcWrapper)
376
377 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
378 errno := fsc.RootFS().Rmdir(path)
379
380 return jsfsInvoke(ctx, mod, callback, errno)
381 }
382
383
384
385
386 type jsfsRename struct {
387 proc *processState
388 }
389
390 func (r *jsfsRename) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
391 cwd := r.proc.cwd
392 from := util.ResolvePath(cwd, args[0].(string))
393 to := util.ResolvePath(cwd, args[1].(string))
394 callback := args[2].(funcWrapper)
395
396 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
397 errno := fsc.RootFS().Rename(from, to)
398
399 return jsfsInvoke(ctx, mod, callback, errno)
400 }
401
402
403
404
405 type jsfsUnlink struct {
406 proc *processState
407 }
408
409 func (u *jsfsUnlink) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
410 path := util.ResolvePath(u.proc.cwd, args[0].(string))
411 callback := args[1].(funcWrapper)
412
413 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
414 errno := fsc.RootFS().Unlink(path)
415
416 return jsfsInvoke(ctx, mod, callback, errno)
417 }
418
419
420
421
422 type jsfsUtimes struct {
423 proc *processState
424 }
425
426 func (u *jsfsUtimes) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
427 path := util.ResolvePath(u.proc.cwd, args[0].(string))
428 atimeSec := toInt64(args[1])
429 mtimeSec := toInt64(args[2])
430 callback := args[3].(funcWrapper)
431
432 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
433 errno := fsc.RootFS().Utimens(path, atimeSec*1e9, mtimeSec*1e9)
434
435 return jsfsInvoke(ctx, mod, callback, errno)
436 }
437
438
439
440
441 type jsfsChmod struct {
442 proc *processState
443 }
444
445 func (c *jsfsChmod) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
446 path := util.ResolvePath(c.proc.cwd, args[0].(string))
447 mode := custom.FromJsMode(goos.ValueToUint32(args[1]), 0)
448 callback := args[2].(funcWrapper)
449
450 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
451 errno := fsc.RootFS().Chmod(path, mode)
452
453 return jsfsInvoke(ctx, mod, callback, errno)
454 }
455
456
457
458
459 type jsfsFchmod struct{}
460
461 func (jsfsFchmod) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
462 fd := goos.ValueToInt32(args[0])
463 _ = args[1]
464 callback := args[2].(funcWrapper)
465
466
467 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
468 var errno experimentalsys.Errno
469 if _, ok := fsc.LookupFile(fd); !ok {
470 errno = experimentalsys.EBADF
471 } else {
472 errno = experimentalsys.ENOSYS
473 }
474
475 return jsfsInvoke(ctx, mod, callback, errno)
476 }
477
478
479
480
481 type jsfsChown struct {
482 proc *processState
483 }
484
485 func (c *jsfsChown) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
486 _ = args[0]
487 _ = args[1]
488 _ = args[2]
489 callback := args[3].(funcWrapper)
490
491 errno := experimentalsys.ENOSYS
492
493 return jsfsInvoke(ctx, mod, callback, errno)
494 }
495
496
497
498
499 type jsfsFchown struct{}
500
501 func (jsfsFchown) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
502 fd := goos.ValueToInt32(args[0])
503 _ = args[1]
504 _ = args[2]
505 callback := args[3].(funcWrapper)
506
507
508 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
509 var errno experimentalsys.Errno
510 if _, ok := fsc.LookupFile(fd); !ok {
511 errno = experimentalsys.EBADF
512 } else {
513 errno = experimentalsys.ENOSYS
514 }
515
516 return jsfsInvoke(ctx, mod, callback, errno)
517 }
518
519
520
521
522 type jsfsLchown struct {
523 proc *processState
524 }
525
526 func (l *jsfsLchown) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
527 _ = args[0]
528 _ = args[1]
529 _ = args[2]
530 callback := args[3].(funcWrapper)
531
532 errno := experimentalsys.ENOSYS
533
534 return jsfsInvoke(ctx, mod, callback, errno)
535 }
536
537
538
539
540 type jsfsTruncate struct {
541 proc *processState
542 }
543
544 func (t *jsfsTruncate) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
545 _ = args[0]
546 _ = args[1]
547 callback := args[2].(funcWrapper)
548
549 errno := experimentalsys.ENOSYS
550
551 return jsfsInvoke(ctx, mod, callback, errno)
552 }
553
554
555
556
557 type jsfsFtruncate struct{}
558
559 func (jsfsFtruncate) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
560 fd := goos.ValueToInt32(args[0])
561 length := toInt64(args[1])
562 callback := args[2].(funcWrapper)
563
564
565 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
566 var errno experimentalsys.Errno
567 if f, ok := fsc.LookupFile(fd); !ok {
568 errno = experimentalsys.EBADF
569 } else {
570 errno = f.File.Truncate(length)
571 }
572
573 return jsfsInvoke(ctx, mod, callback, errno)
574 }
575
576
577
578
579 type jsfsReadlink struct {
580 proc *processState
581 }
582
583 func (r *jsfsReadlink) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
584 path := util.ResolvePath(r.proc.cwd, args[0].(string))
585 callback := args[1].(funcWrapper)
586
587 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
588 dst, errno := fsc.RootFS().Readlink(path)
589
590 return callback.invoke(ctx, mod, goos.RefJsfs, maybeError(errno), dst)
591 }
592
593
594
595
596 type jsfsLink struct {
597 proc *processState
598 }
599
600 func (l *jsfsLink) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
601 cwd := l.proc.cwd
602 path := util.ResolvePath(cwd, args[0].(string))
603 link := util.ResolvePath(cwd, args[1].(string))
604 callback := args[2].(funcWrapper)
605
606 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
607 errno := fsc.RootFS().Link(path, link)
608
609 return jsfsInvoke(ctx, mod, callback, errno)
610 }
611
612
613
614
615 type jsfsSymlink struct {
616 proc *processState
617 }
618
619 func (s *jsfsSymlink) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
620 dst := args[0].(string)
621 link := util.ResolvePath(s.proc.cwd, args[1].(string))
622 callback := args[2].(funcWrapper)
623
624 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
625 errno := fsc.RootFS().Symlink(dst, link)
626
627 return jsfsInvoke(ctx, mod, callback, errno)
628 }
629
630
631
632
633 type jsfsFsync struct{}
634
635 func (jsfsFsync) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
636 fd := goos.ValueToInt32(args[0])
637 callback := args[1].(funcWrapper)
638
639
640 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
641 var errno experimentalsys.Errno
642 if f, ok := fsc.LookupFile(fd); !ok {
643 errno = experimentalsys.EBADF
644 } else {
645 errno = f.File.Sync()
646 }
647
648 return jsfsInvoke(ctx, mod, callback, errno)
649 }
650
651
652 type jsSt struct {
653 isDir bool
654 dev uint64
655 ino uint64
656 mode uint32
657 nlink uint32
658 uid uint32
659 gid uint32
660 rdev int64
661 size int64
662 blksize int32
663 blocks int32
664 atimeMs int64
665 mtimeMs int64
666 ctimeMs int64
667 }
668
669
670 func (s *jsSt) String() string {
671 return fmt.Sprintf("{isDir=%v,mode=%s,size=%d,mtimeMs=%d}", s.isDir, custom.FromJsMode(s.mode, 0), s.size, s.mtimeMs)
672 }
673
674
675 func (s *jsSt) Get(propertyKey string) interface{} {
676 switch propertyKey {
677 case "dev":
678 return s.dev
679 case "ino":
680 return s.ino
681 case "mode":
682 return s.mode
683 case "nlink":
684 return s.nlink
685 case "uid":
686 return s.uid
687 case "gid":
688 return s.gid
689 case "rdev":
690 return s.rdev
691 case "size":
692 return s.size
693 case "blksize":
694 return s.blksize
695 case "blocks":
696 return s.blocks
697 case "atimeMs":
698 return s.atimeMs
699 case "mtimeMs":
700 return s.mtimeMs
701 case "ctimeMs":
702 return s.ctimeMs
703 }
704 panic(fmt.Sprintf("TODO: stat.%s", propertyKey))
705 }
706
707
708 func (s *jsSt) call(_ context.Context, _ api.Module, _ goos.Ref, method string, _ ...interface{}) (interface{}, error) {
709 if method == "isDirectory" {
710 return s.isDir, nil
711 }
712 panic(fmt.Sprintf("TODO: stat.%s", method))
713 }
714
715 func jsfsInvoke(ctx context.Context, mod api.Module, callback funcWrapper, err experimentalsys.Errno) (interface{}, error) {
716 return callback.invoke(ctx, mod, goos.RefJsfs, maybeError(err), err == 0)
717 }
718
719 func maybeError(errno experimentalsys.Errno) error {
720 if errno != 0 {
721 return errno
722 }
723 return nil
724 }
725
View as plain text