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