github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/gojs/fs.go (about) 1 package gojs 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/bananabytelabs/wazero/api" 8 experimentalsys "github.com/bananabytelabs/wazero/experimental/sys" 9 "github.com/bananabytelabs/wazero/internal/gojs/custom" 10 "github.com/bananabytelabs/wazero/internal/gojs/goos" 11 "github.com/bananabytelabs/wazero/internal/gojs/util" 12 internalsys "github.com/bananabytelabs/wazero/internal/sys" 13 "github.com/bananabytelabs/wazero/internal/wasm" 14 "github.com/bananabytelabs/wazero/sys" 15 ) 16 17 var ( 18 // jsfsConstants = jsfs Get("constants") // fs_js.go init 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 // oWRONLY = jsfsConstants Get("O_WRONLY").Int() // fs_js.go init 30 oWRONLY = float64(experimentalsys.O_WRONLY) 31 32 // oRDWR = jsfsConstants Get("O_RDWR").Int() // fs_js.go init 33 oRDWR = float64(experimentalsys.O_RDWR) 34 35 // o CREAT = jsfsConstants Get("O_CREAT").Int() // fs_js.go init 36 oCREAT = float64(experimentalsys.O_CREAT) 37 38 // oTRUNC = jsfsConstants Get("O_TRUNC").Int() // fs_js.go init 39 oTRUNC = float64(experimentalsys.O_TRUNC) 40 41 // oAPPEND = jsfsConstants Get("O_APPEND").Int() // fs_js.go init 42 oAPPEND = float64(experimentalsys.O_APPEND) 43 44 // oEXCL = jsfsConstants Get("O_EXCL").Int() // fs_js.go init 45 oEXCL = float64(experimentalsys.O_EXCL) 46 ) 47 48 // jsfs = js.Global().Get("fs") // fs_js.go init 49 // 50 // js.fsCall conventions: 51 // * funcWrapper callback is the last parameter 52 // - arg0 is error and up to one result in arg1 53 func newJsFs(proc *processState) *jsVal { 54 return newJsVal(goos.RefJsfs, custom.NameFs). 55 addProperties(map[string]interface{}{ 56 "constants": jsfsConstants, // = jsfs.Get("constants") // init 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 // jsfsOpen implements implements jsFn for syscall.Open 85 // 86 // jsFD /* Int */, err := fsCall("open", path, flags, perm) 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 // Note: these are already sys.Flag because Go uses constants we define: 94 // https://github.com/golang/go/blob/go1.20/src/syscall/fs_js.go#L24-L31 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) // note: error first 104 } 105 106 // jsfsStat implements jsFn for syscall.Stat 107 // 108 // jsSt, err := fsCall("stat", path) 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) // note: error first 119 } 120 121 // syscallStat is like syscall.Stat 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 // jsfsLstat implements jsFn for syscall.Lstat 133 // 134 // jsSt, err := fsCall("lstat", path) 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) // note: error first 146 } 147 148 // syscallLstat is like syscall.Lstat 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 // jsfsFstat implements jsFn for syscall.Open 160 // 161 // stat, err := fsCall("fstat", fd); err == nil && stat.Call("isDirectory").Bool() 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) // note: error first 172 } 173 174 // syscallFstat is like syscall.Fstat 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 // jsfsClose implements jsFn for syscall.Close 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 // jsfsRead implements jsFn for syscall.Read and syscall.Pread, called by 217 // src/internal/poll/fd_unix.go poll.Read. 218 // 219 // n, err := fsCall("read", fd, buf, 0, len(b), nil) 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] // nil unless Pread 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 // It is safe to cast to uint32 because n <= uint32(byteCount). 239 return callback.invoke(ctx, mod, goos.RefJsfs, err, uint32(n)) // note: error first 240 } 241 242 // syscallRead is like syscall.Read 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 // jsfsWrite implements jsFn for syscall.Write and syscall.Pwrite. 256 // 257 // Notably, offset is non-nil in Pwrite. 258 // 259 // n, err := fsCall("write", fd, buf, 0, len(b), nil) 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] // nil unless Pwrite 271 callback := args[5].(funcWrapper) 272 273 if byteCount > 0 { // empty is possible on EOF 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 // It is safe to cast to uint32 because n <= uint32(byteCount). 280 return callback.invoke(ctx, mod, goos.RefJsfs, err, uint32(n)) // note: error first 281 } 282 return callback.invoke(ctx, mod, goos.RefJsfs, nil, goos.RefValueZero) 283 } 284 285 // syscallWrite is like syscall.Write 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 // e.g. unimplemented for write 297 } 298 return 299 } 300 301 // jsfsReaddir implements jsFn for syscall.Open 302 // 303 // dir, err := fsCall("readdir", path) 304 // dir.Length(), dir.Index(i).String() 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) // note: error first 315 } 316 317 func syscallReaddir(_ context.Context, mod api.Module, name string) (*objectArray, error) { 318 fsc := mod.(*wasm.ModuleInstance).Sys.FS() 319 320 // don't allocate a file descriptor 321 f, errno := fsc.RootFS().OpenFile(name, experimentalsys.O_RDONLY, 0) 322 if errno != 0 { 323 return nil, errno 324 } 325 defer f.Close() //nolint 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 // jsfsMkdir implements implements jsFn for fs.Mkdir 339 // 340 // jsFD /* Int */, err := fsCall("mkdir", path, perm) 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 // We need at least read access to open the file descriptor 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) // note: error first 364 } 365 366 // jsfsRmdir implements jsFn for the following 367 // 368 // _, err := fsCall("rmdir", path) // syscall.Rmdir 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 // jsfsRename implements jsFn for the following 384 // 385 // _, err := fsCall("rename", from, to) // syscall.Rename 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 // jsfsUnlink implements jsFn for the following 403 // 404 // _, err := fsCall("unlink", path) // syscall.Unlink 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 // jsfsUtimes implements jsFn for the following 420 // 421 // _, err := fsCall("utimes", path, atime, mtime) // syscall.Utimens 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 // jsfsChmod implements jsFn for the following 439 // 440 // _, err := fsCall("chmod", path, mode) // syscall.Chmod 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 // jsfsFchmod implements jsFn for the following 457 // 458 // _, err := fsCall("fchmod", fd, mode) // syscall.Fchmod 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] // mode 464 callback := args[2].(funcWrapper) 465 466 // Check to see if the file descriptor is available 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 // We only support functions used in wasip1 473 } 474 475 return jsfsInvoke(ctx, mod, callback, errno) 476 } 477 478 // jsfsChown implements jsFn for the following 479 // 480 // _, err := fsCall("chown", path, uint32(uid), uint32(gid)) // syscall.Chown 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] // path 487 _ = args[1] // uid 488 _ = args[2] // gid 489 callback := args[3].(funcWrapper) 490 491 errno := experimentalsys.ENOSYS // We only support functions used in wasip1 492 493 return jsfsInvoke(ctx, mod, callback, errno) 494 } 495 496 // jsfsFchown implements jsFn for the following 497 // 498 // _, err := fsCall("fchown", fd, uint32(uid), uint32(gid)) // syscall.Fchown 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] // uid 504 _ = args[2] // gid 505 callback := args[3].(funcWrapper) 506 507 // Check to see if the file descriptor is available 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 // We only support functions used in wasip1 514 } 515 516 return jsfsInvoke(ctx, mod, callback, errno) 517 } 518 519 // jsfsLchown implements jsFn for the following 520 // 521 // _, err := fsCall("lchown", path, uint32(uid), uint32(gid)) // syscall.Lchown 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] // path 528 _ = args[1] // uid 529 _ = args[2] // gid 530 callback := args[3].(funcWrapper) 531 532 errno := experimentalsys.ENOSYS // We only support functions used in wasip1 533 534 return jsfsInvoke(ctx, mod, callback, errno) 535 } 536 537 // jsfsTruncate implements jsFn for the following 538 // 539 // _, err := fsCall("truncate", path, length) // syscall.Truncate 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] // path 546 _ = args[1] // length 547 callback := args[2].(funcWrapper) 548 549 errno := experimentalsys.ENOSYS // We only support functions used in wasip1 550 551 return jsfsInvoke(ctx, mod, callback, errno) 552 } 553 554 // jsfsFtruncate implements jsFn for the following 555 // 556 // _, err := fsCall("ftruncate", fd, length) // syscall.Ftruncate 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 // Check to see if the file descriptor is available 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 // jsfsReadlink implements jsFn for syscall.Readlink 577 // 578 // dst, err := fsCall("readlink", path) // syscall.Readlink 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) // note: error first 591 } 592 593 // jsfsLink implements jsFn for the following 594 // 595 // _, err := fsCall("link", path, link) // syscall.Link 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 // jsfsSymlink implements jsFn for the following 613 // 614 // _, err := fsCall("symlink", path, link) // syscall.Symlink 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) // The dst of a symlink must not be resolved, as it should be resolved during readLink. 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 // jsfsFsync implements jsFn for the following 631 // 632 // _, err := fsCall("fsync", fd) // syscall.Fsync 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 // Check to see if the file descriptor is available 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 // jsSt is pre-parsed from fs_js.go setStat to avoid thrashing 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 // String implements fmt.Stringer 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 // Get implements the same method as documented on goos.GetFunction 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 // call implements jsCall.call 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) // note: error first 717 } 718 719 func maybeError(errno experimentalsys.Errno) error { 720 if errno != 0 { 721 return errno 722 } 723 return nil 724 }