github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/syscall/fs_wasip1.go (about) 1 // Copyright 2023 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build wasip1 6 7 package syscall 8 9 import ( 10 "runtime" 11 "unsafe" 12 ) 13 14 func init() { 15 // Try to set stdio to non-blocking mode before the os package 16 // calls NewFile for each fd. NewFile queries the non-blocking flag 17 // but doesn't change it, even if the runtime supports non-blocking 18 // stdio. Since WebAssembly modules are single-threaded, blocking 19 // system calls temporarily halt execution of the module. If the 20 // runtime supports non-blocking stdio, the Go runtime is able to 21 // use the WASI net poller to poll for read/write readiness and is 22 // able to schedule goroutines while waiting. 23 SetNonblock(0, true) 24 SetNonblock(1, true) 25 SetNonblock(2, true) 26 } 27 28 type uintptr32 = uint32 29 type size = uint32 30 type fdflags = uint32 31 type filesize = uint64 32 type filetype = uint8 33 type lookupflags = uint32 34 type oflags = uint32 35 type rights = uint64 36 type timestamp = uint64 37 type dircookie = uint64 38 type filedelta = int64 39 type fstflags = uint32 40 41 type iovec struct { 42 buf uintptr32 43 bufLen size 44 } 45 46 const ( 47 LOOKUP_SYMLINK_FOLLOW = 0x00000001 48 ) 49 50 const ( 51 OFLAG_CREATE = 0x0001 52 OFLAG_DIRECTORY = 0x0002 53 OFLAG_EXCL = 0x0004 54 OFLAG_TRUNC = 0x0008 55 ) 56 57 const ( 58 FDFLAG_APPEND = 0x0001 59 FDFLAG_DSYNC = 0x0002 60 FDFLAG_NONBLOCK = 0x0004 61 FDFLAG_RSYNC = 0x0008 62 FDFLAG_SYNC = 0x0010 63 ) 64 65 const ( 66 RIGHT_FD_DATASYNC = 1 << iota 67 RIGHT_FD_READ 68 RIGHT_FD_SEEK 69 RIGHT_FDSTAT_SET_FLAGS 70 RIGHT_FD_SYNC 71 RIGHT_FD_TELL 72 RIGHT_FD_WRITE 73 RIGHT_FD_ADVISE 74 RIGHT_FD_ALLOCATE 75 RIGHT_PATH_CREATE_DIRECTORY 76 RIGHT_PATH_CREATE_FILE 77 RIGHT_PATH_LINK_SOURCE 78 RIGHT_PATH_LINK_TARGET 79 RIGHT_PATH_OPEN 80 RIGHT_FD_READDIR 81 RIGHT_PATH_READLINK 82 RIGHT_PATH_RENAME_SOURCE 83 RIGHT_PATH_RENAME_TARGET 84 RIGHT_PATH_FILESTAT_GET 85 RIGHT_PATH_FILESTAT_SET_SIZE 86 RIGHT_PATH_FILESTAT_SET_TIMES 87 RIGHT_FD_FILESTAT_GET 88 RIGHT_FD_FILESTAT_SET_SIZE 89 RIGHT_FD_FILESTAT_SET_TIMES 90 RIGHT_PATH_SYMLINK 91 RIGHT_PATH_REMOVE_DIRECTORY 92 RIGHT_PATH_UNLINK_FILE 93 RIGHT_POLL_FD_READWRITE 94 RIGHT_SOCK_SHUTDOWN 95 RIGHT_SOCK_ACCEPT 96 ) 97 98 const ( 99 WHENCE_SET = 0 100 WHENCE_CUR = 1 101 WHENCE_END = 2 102 ) 103 104 const ( 105 FILESTAT_SET_ATIM = 0x0001 106 FILESTAT_SET_ATIM_NOW = 0x0002 107 FILESTAT_SET_MTIM = 0x0004 108 FILESTAT_SET_MTIM_NOW = 0x0008 109 ) 110 111 const ( 112 // Despite the rights being defined as a 64 bits integer in the spec, 113 // wasmtime crashes the program if we set any of the upper 32 bits. 114 fullRights = rights(^uint32(0)) 115 readRights = rights(RIGHT_FD_READ | RIGHT_FD_READDIR) 116 writeRights = rights(RIGHT_FD_DATASYNC | RIGHT_FD_WRITE | RIGHT_FD_ALLOCATE | RIGHT_PATH_FILESTAT_SET_SIZE) 117 118 // Some runtimes have very strict expectations when it comes to which 119 // rights can be enabled on files opened by path_open. The fileRights 120 // constant is used as a mask to retain only bits for operations that 121 // are supported on files. 122 fileRights rights = RIGHT_FD_DATASYNC | 123 RIGHT_FD_READ | 124 RIGHT_FD_SEEK | 125 RIGHT_FDSTAT_SET_FLAGS | 126 RIGHT_FD_SYNC | 127 RIGHT_FD_TELL | 128 RIGHT_FD_WRITE | 129 RIGHT_FD_ADVISE | 130 RIGHT_FD_ALLOCATE | 131 RIGHT_PATH_CREATE_DIRECTORY | 132 RIGHT_PATH_CREATE_FILE | 133 RIGHT_PATH_LINK_SOURCE | 134 RIGHT_PATH_LINK_TARGET | 135 RIGHT_PATH_OPEN | 136 RIGHT_FD_READDIR | 137 RIGHT_PATH_READLINK | 138 RIGHT_PATH_RENAME_SOURCE | 139 RIGHT_PATH_RENAME_TARGET | 140 RIGHT_PATH_FILESTAT_GET | 141 RIGHT_PATH_FILESTAT_SET_SIZE | 142 RIGHT_PATH_FILESTAT_SET_TIMES | 143 RIGHT_FD_FILESTAT_GET | 144 RIGHT_FD_FILESTAT_SET_SIZE | 145 RIGHT_FD_FILESTAT_SET_TIMES | 146 RIGHT_PATH_SYMLINK | 147 RIGHT_PATH_REMOVE_DIRECTORY | 148 RIGHT_PATH_UNLINK_FILE | 149 RIGHT_POLL_FD_READWRITE 150 151 // Runtimes like wasmtime and wasmedge will refuse to open directories 152 // if the rights requested by the application exceed the operations that 153 // can be performed on a directory. 154 dirRights rights = RIGHT_FD_SEEK | 155 RIGHT_FDSTAT_SET_FLAGS | 156 RIGHT_FD_SYNC | 157 RIGHT_PATH_CREATE_DIRECTORY | 158 RIGHT_PATH_CREATE_FILE | 159 RIGHT_PATH_LINK_SOURCE | 160 RIGHT_PATH_LINK_TARGET | 161 RIGHT_PATH_OPEN | 162 RIGHT_FD_READDIR | 163 RIGHT_PATH_READLINK | 164 RIGHT_PATH_RENAME_SOURCE | 165 RIGHT_PATH_RENAME_TARGET | 166 RIGHT_PATH_FILESTAT_GET | 167 RIGHT_PATH_FILESTAT_SET_SIZE | 168 RIGHT_PATH_FILESTAT_SET_TIMES | 169 RIGHT_FD_FILESTAT_GET | 170 RIGHT_FD_FILESTAT_SET_TIMES | 171 RIGHT_PATH_SYMLINK | 172 RIGHT_PATH_REMOVE_DIRECTORY | 173 RIGHT_PATH_UNLINK_FILE 174 ) 175 176 // https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-fd_closefd-fd---result-errno 177 // 178 //go:wasmimport wasi_snapshot_preview1 fd_close 179 //go:noescape 180 func fd_close(fd int32) Errno 181 182 // https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-fd_filestat_set_sizefd-fd-size-filesize---result-errno 183 // 184 //go:wasmimport wasi_snapshot_preview1 fd_filestat_set_size 185 //go:noescape 186 func fd_filestat_set_size(fd int32, set_size filesize) Errno 187 188 // https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-fd_preadfd-fd-iovs-iovec_array-offset-filesize---resultsize-errno 189 // 190 //go:wasmimport wasi_snapshot_preview1 fd_pread 191 //go:noescape 192 func fd_pread(fd int32, iovs unsafe.Pointer, iovsLen size, offset filesize, nread unsafe.Pointer) Errno 193 194 //go:wasmimport wasi_snapshot_preview1 fd_pwrite 195 //go:noescape 196 func fd_pwrite(fd int32, iovs unsafe.Pointer, iovsLen size, offset filesize, nwritten unsafe.Pointer) Errno 197 198 //go:wasmimport wasi_snapshot_preview1 fd_read 199 //go:noescape 200 func fd_read(fd int32, iovs unsafe.Pointer, iovsLen size, nread unsafe.Pointer) Errno 201 202 //go:wasmimport wasi_snapshot_preview1 fd_readdir 203 //go:noescape 204 func fd_readdir(fd int32, buf unsafe.Pointer, bufLen size, cookie dircookie, nwritten unsafe.Pointer) Errno 205 206 //go:wasmimport wasi_snapshot_preview1 fd_seek 207 //go:noescape 208 func fd_seek(fd int32, offset filedelta, whence uint32, newoffset unsafe.Pointer) Errno 209 210 // https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-fd_fdstat_set_rightsfd-fd-fs_rights_base-rights-fs_rights_inheriting-rights---result-errno 211 // 212 //go:wasmimport wasi_snapshot_preview1 fd_fdstat_set_rights 213 //go:noescape 214 func fd_fdstat_set_rights(fd int32, rightsBase rights, rightsInheriting rights) Errno 215 216 //go:wasmimport wasi_snapshot_preview1 fd_filestat_get 217 //go:noescape 218 func fd_filestat_get(fd int32, buf unsafe.Pointer) Errno 219 220 //go:wasmimport wasi_snapshot_preview1 fd_write 221 //go:noescape 222 func fd_write(fd int32, iovs unsafe.Pointer, iovsLen size, nwritten unsafe.Pointer) Errno 223 224 //go:wasmimport wasi_snapshot_preview1 fd_sync 225 //go:noescape 226 func fd_sync(fd int32) Errno 227 228 //go:wasmimport wasi_snapshot_preview1 path_create_directory 229 //go:noescape 230 func path_create_directory(fd int32, path unsafe.Pointer, pathLen size) Errno 231 232 //go:wasmimport wasi_snapshot_preview1 path_filestat_get 233 //go:noescape 234 func path_filestat_get(fd int32, flags lookupflags, path unsafe.Pointer, pathLen size, buf unsafe.Pointer) Errno 235 236 //go:wasmimport wasi_snapshot_preview1 path_filestat_set_times 237 //go:noescape 238 func path_filestat_set_times(fd int32, flags lookupflags, path unsafe.Pointer, pathLen size, atim timestamp, mtim timestamp, fstflags fstflags) Errno 239 240 //go:wasmimport wasi_snapshot_preview1 path_link 241 //go:noescape 242 func path_link(oldFd int32, oldFlags lookupflags, oldPath unsafe.Pointer, oldPathLen size, newFd int32, newPath unsafe.Pointer, newPathLen size) Errno 243 244 //go:wasmimport wasi_snapshot_preview1 path_readlink 245 //go:noescape 246 func path_readlink(fd int32, path unsafe.Pointer, pathLen size, buf unsafe.Pointer, bufLen size, nwritten unsafe.Pointer) Errno 247 248 //go:wasmimport wasi_snapshot_preview1 path_remove_directory 249 //go:noescape 250 func path_remove_directory(fd int32, path unsafe.Pointer, pathLen size) Errno 251 252 //go:wasmimport wasi_snapshot_preview1 path_rename 253 //go:noescape 254 func path_rename(oldFd int32, oldPath unsafe.Pointer, oldPathLen size, newFd int32, newPath unsafe.Pointer, newPathLen size) Errno 255 256 //go:wasmimport wasi_snapshot_preview1 path_symlink 257 //go:noescape 258 func path_symlink(oldPath unsafe.Pointer, oldPathLen size, fd int32, newPath unsafe.Pointer, newPathLen size) Errno 259 260 //go:wasmimport wasi_snapshot_preview1 path_unlink_file 261 //go:noescape 262 func path_unlink_file(fd int32, path unsafe.Pointer, pathLen size) Errno 263 264 //go:wasmimport wasi_snapshot_preview1 path_open 265 //go:noescape 266 func path_open(rootFD int32, dirflags lookupflags, path unsafe.Pointer, pathLen size, oflags oflags, fsRightsBase rights, fsRightsInheriting rights, fsFlags fdflags, fd unsafe.Pointer) Errno 267 268 //go:wasmimport wasi_snapshot_preview1 random_get 269 //go:noescape 270 func random_get(buf unsafe.Pointer, bufLen size) Errno 271 272 // https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-fdstat-record 273 // fdflags must be at offset 2, hence the uint16 type rather than the 274 // fdflags (uint32) type. 275 type fdstat struct { 276 filetype filetype 277 fdflags uint16 278 rightsBase rights 279 rightsInheriting rights 280 } 281 282 //go:wasmimport wasi_snapshot_preview1 fd_fdstat_get 283 //go:noescape 284 func fd_fdstat_get(fd int32, buf unsafe.Pointer) Errno 285 286 //go:wasmimport wasi_snapshot_preview1 fd_fdstat_set_flags 287 //go:noescape 288 func fd_fdstat_set_flags(fd int32, flags fdflags) Errno 289 290 func fd_fdstat_get_flags(fd int) (uint32, error) { 291 var stat fdstat 292 errno := fd_fdstat_get(int32(fd), unsafe.Pointer(&stat)) 293 return uint32(stat.fdflags), errnoErr(errno) 294 } 295 296 func fd_fdstat_get_type(fd int) (uint8, error) { 297 var stat fdstat 298 errno := fd_fdstat_get(int32(fd), unsafe.Pointer(&stat)) 299 return stat.filetype, errnoErr(errno) 300 } 301 302 type preopentype = uint8 303 304 const ( 305 preopentypeDir preopentype = iota 306 ) 307 308 type prestatDir struct { 309 prNameLen size 310 } 311 312 type prestat struct { 313 typ preopentype 314 dir prestatDir 315 } 316 317 //go:wasmimport wasi_snapshot_preview1 fd_prestat_get 318 //go:noescape 319 func fd_prestat_get(fd int32, prestat unsafe.Pointer) Errno 320 321 //go:wasmimport wasi_snapshot_preview1 fd_prestat_dir_name 322 //go:noescape 323 func fd_prestat_dir_name(fd int32, path unsafe.Pointer, pathLen size) Errno 324 325 type opendir struct { 326 fd int32 327 name string 328 } 329 330 // List of preopen directories that were exposed by the runtime. The first one 331 // is assumed to the be root directory of the file system, and others are seen 332 // as mount points at sub paths of the root. 333 var preopens []opendir 334 335 // Current working directory. We maintain this as a string and resolve paths in 336 // the code because wasmtime does not allow relative path lookups outside of the 337 // scope of a directory; a previous approach we tried consisted in maintaining 338 // open a file descriptor to the current directory so we could perform relative 339 // path lookups from that location, but it resulted in breaking path resolution 340 // from the current directory to its parent. 341 var cwd string 342 343 func init() { 344 dirNameBuf := make([]byte, 256) 345 // We start looking for preopens at fd=3 because 0, 1, and 2 are reserved 346 // for standard input and outputs. 347 for preopenFd := int32(3); ; preopenFd++ { 348 var prestat prestat 349 350 errno := fd_prestat_get(preopenFd, unsafe.Pointer(&prestat)) 351 if errno == EBADF { 352 break 353 } 354 if errno == ENOTDIR || prestat.typ != preopentypeDir { 355 continue 356 } 357 if errno != 0 { 358 panic("fd_prestat: " + errno.Error()) 359 } 360 if int(prestat.dir.prNameLen) > len(dirNameBuf) { 361 dirNameBuf = make([]byte, prestat.dir.prNameLen) 362 } 363 364 errno = fd_prestat_dir_name(preopenFd, unsafe.Pointer(&dirNameBuf[0]), prestat.dir.prNameLen) 365 if errno != 0 { 366 panic("fd_prestat_dir_name: " + errno.Error()) 367 } 368 369 preopens = append(preopens, opendir{ 370 fd: preopenFd, 371 name: string(dirNameBuf[:prestat.dir.prNameLen]), 372 }) 373 } 374 375 if cwd, _ = Getenv("PWD"); cwd != "" { 376 cwd = joinPath("/", cwd) 377 } else if len(preopens) > 0 { 378 cwd = preopens[0].name 379 } 380 } 381 382 // Provided by package runtime. 383 func now() (sec int64, nsec int32) 384 385 //go:nosplit 386 func appendCleanPath(buf []byte, path string, lookupParent bool) ([]byte, bool) { 387 i := 0 388 for i < len(path) { 389 for i < len(path) && path[i] == '/' { 390 i++ 391 } 392 393 j := i 394 for j < len(path) && path[j] != '/' { 395 j++ 396 } 397 398 s := path[i:j] 399 i = j 400 401 switch s { 402 case "": 403 continue 404 case ".": 405 continue 406 case "..": 407 if !lookupParent { 408 k := len(buf) 409 for k > 0 && buf[k-1] != '/' { 410 k-- 411 } 412 for k > 1 && buf[k-1] == '/' { 413 k-- 414 } 415 buf = buf[:k] 416 if k == 0 { 417 lookupParent = true 418 } else { 419 s = "" 420 continue 421 } 422 } 423 default: 424 lookupParent = false 425 } 426 427 if len(buf) > 0 && buf[len(buf)-1] != '/' { 428 buf = append(buf, '/') 429 } 430 buf = append(buf, s...) 431 } 432 return buf, lookupParent 433 } 434 435 // joinPath concatenates dir and file paths, producing a cleaned path where 436 // "." and ".." have been removed, unless dir is relative and the references 437 // to parent directories in file represented a location relative to a parent 438 // of dir. 439 // 440 // This function is used for path resolution of all wasi functions expecting 441 // a path argument; the returned string is heap allocated, which we may want 442 // to optimize in the future. Instead of returning a string, the function 443 // could append the result to an output buffer that the functions in this 444 // file can manage to have allocated on the stack (e.g. initializing to a 445 // fixed capacity). Since it will significantly increase code complexity, 446 // we prefer to optimize for readability and maintainability at this time. 447 func joinPath(dir, file string) string { 448 buf := make([]byte, 0, len(dir)+len(file)+1) 449 if isAbs(dir) { 450 buf = append(buf, '/') 451 } 452 buf, lookupParent := appendCleanPath(buf, dir, false) 453 buf, _ = appendCleanPath(buf, file, lookupParent) 454 // The appendCleanPath function cleans the path so it does not inject 455 // references to the current directory. If both the dir and file args 456 // were ".", this results in the output buffer being empty so we handle 457 // this condition here. 458 if len(buf) == 0 { 459 buf = append(buf, '.') 460 } 461 // If the file ended with a '/' we make sure that the output also ends 462 // with a '/'. This is needed to ensure that programs have a mechanism 463 // to represent dereferencing symbolic links pointing to directories. 464 if buf[len(buf)-1] != '/' && isDir(file) { 465 buf = append(buf, '/') 466 } 467 return unsafe.String(&buf[0], len(buf)) 468 } 469 470 func isAbs(path string) bool { 471 return hasPrefix(path, "/") 472 } 473 474 func isDir(path string) bool { 475 return hasSuffix(path, "/") 476 } 477 478 func hasPrefix(s, p string) bool { 479 return len(s) >= len(p) && s[:len(p)] == p 480 } 481 482 func hasSuffix(s, x string) bool { 483 return len(s) >= len(x) && s[len(s)-len(x):] == x 484 } 485 486 // preparePath returns the preopen file descriptor of the directory to perform 487 // path resolution from, along with the pair of pointer and length for the 488 // relative expression of path from the directory. 489 // 490 // If the path argument is not absolute, it is first appended to the current 491 // working directory before resolution. 492 func preparePath(path string) (int32, unsafe.Pointer, size) { 493 var dirFd = int32(-1) 494 var dirName string 495 496 dir := "/" 497 if !isAbs(path) { 498 dir = cwd 499 } 500 path = joinPath(dir, path) 501 502 for _, p := range preopens { 503 if len(p.name) > len(dirName) && hasPrefix(path, p.name) { 504 dirFd, dirName = p.fd, p.name 505 } 506 } 507 508 path = path[len(dirName):] 509 for isAbs(path) { 510 path = path[1:] 511 } 512 if len(path) == 0 { 513 path = "." 514 } 515 516 return dirFd, stringPointer(path), size(len(path)) 517 } 518 519 func Open(path string, openmode int, perm uint32) (int, error) { 520 if path == "" { 521 return -1, EINVAL 522 } 523 dirFd, pathPtr, pathLen := preparePath(path) 524 525 var oflags oflags 526 if (openmode & O_CREATE) != 0 { 527 oflags |= OFLAG_CREATE 528 } 529 if (openmode & O_TRUNC) != 0 { 530 oflags |= OFLAG_TRUNC 531 } 532 if (openmode & O_EXCL) != 0 { 533 oflags |= OFLAG_EXCL 534 } 535 536 var rights rights 537 switch openmode & (O_RDONLY | O_WRONLY | O_RDWR) { 538 case O_RDONLY: 539 rights = fileRights & ^writeRights 540 case O_WRONLY: 541 rights = fileRights & ^readRights 542 case O_RDWR: 543 rights = fileRights 544 } 545 546 var fdflags fdflags 547 if (openmode & O_APPEND) != 0 { 548 fdflags |= FDFLAG_APPEND 549 } 550 if (openmode & O_SYNC) != 0 { 551 fdflags |= FDFLAG_SYNC 552 } 553 554 var fd int32 555 errno := path_open( 556 dirFd, 557 LOOKUP_SYMLINK_FOLLOW, 558 pathPtr, 559 pathLen, 560 oflags, 561 rights, 562 fileRights, 563 fdflags, 564 unsafe.Pointer(&fd), 565 ) 566 if errno == EISDIR && oflags == 0 && fdflags == 0 && ((rights & writeRights) == 0) { 567 // wasmtime and wasmedge will error if attempting to open a directory 568 // because we are asking for too many rights. However, we cannot 569 // determine ahead of time if the path we are about to open is a 570 // directory, so instead we fallback to a second call to path_open with 571 // a more limited set of rights. 572 // 573 // This approach is subject to a race if the file system is modified 574 // concurrently, so we also inject OFLAG_DIRECTORY to ensure that we do 575 // not accidentally open a file which is not a directory. 576 errno = path_open( 577 dirFd, 578 LOOKUP_SYMLINK_FOLLOW, 579 pathPtr, 580 pathLen, 581 oflags|OFLAG_DIRECTORY, 582 rights&dirRights, 583 fileRights, 584 fdflags, 585 unsafe.Pointer(&fd), 586 ) 587 } 588 return int(fd), errnoErr(errno) 589 } 590 591 func Close(fd int) error { 592 errno := fd_close(int32(fd)) 593 return errnoErr(errno) 594 } 595 596 func CloseOnExec(fd int) { 597 // nothing to do - no exec 598 } 599 600 func Mkdir(path string, perm uint32) error { 601 if path == "" { 602 return EINVAL 603 } 604 dirFd, pathPtr, pathLen := preparePath(path) 605 errno := path_create_directory(dirFd, pathPtr, pathLen) 606 return errnoErr(errno) 607 } 608 609 func ReadDir(fd int, buf []byte, cookie dircookie) (int, error) { 610 var nwritten size 611 errno := fd_readdir(int32(fd), unsafe.Pointer(&buf[0]), size(len(buf)), cookie, unsafe.Pointer(&nwritten)) 612 return int(nwritten), errnoErr(errno) 613 } 614 615 type Stat_t struct { 616 Dev uint64 617 Ino uint64 618 Filetype uint8 619 Nlink uint64 620 Size uint64 621 Atime uint64 622 Mtime uint64 623 Ctime uint64 624 625 Mode int 626 627 // Uid and Gid are always zero on wasip1 platforms 628 Uid uint32 629 Gid uint32 630 } 631 632 func Stat(path string, st *Stat_t) error { 633 if path == "" { 634 return EINVAL 635 } 636 dirFd, pathPtr, pathLen := preparePath(path) 637 errno := path_filestat_get(dirFd, LOOKUP_SYMLINK_FOLLOW, pathPtr, pathLen, unsafe.Pointer(st)) 638 setDefaultMode(st) 639 return errnoErr(errno) 640 } 641 642 func Lstat(path string, st *Stat_t) error { 643 if path == "" { 644 return EINVAL 645 } 646 dirFd, pathPtr, pathLen := preparePath(path) 647 errno := path_filestat_get(dirFd, 0, pathPtr, pathLen, unsafe.Pointer(st)) 648 setDefaultMode(st) 649 return errnoErr(errno) 650 } 651 652 func Fstat(fd int, st *Stat_t) error { 653 errno := fd_filestat_get(int32(fd), unsafe.Pointer(st)) 654 setDefaultMode(st) 655 return errnoErr(errno) 656 } 657 658 func setDefaultMode(st *Stat_t) { 659 // WASI does not support unix-like permissions, but Go programs are likely 660 // to expect the permission bits to not be zero so we set defaults to help 661 // avoid breaking applications that are migrating to WASM. 662 if st.Filetype == FILETYPE_DIRECTORY { 663 st.Mode = 0700 664 } else { 665 st.Mode = 0600 666 } 667 } 668 669 func Unlink(path string) error { 670 if path == "" { 671 return EINVAL 672 } 673 dirFd, pathPtr, pathLen := preparePath(path) 674 errno := path_unlink_file(dirFd, pathPtr, pathLen) 675 return errnoErr(errno) 676 } 677 678 func Rmdir(path string) error { 679 if path == "" { 680 return EINVAL 681 } 682 dirFd, pathPtr, pathLen := preparePath(path) 683 errno := path_remove_directory(dirFd, pathPtr, pathLen) 684 return errnoErr(errno) 685 } 686 687 func Chmod(path string, mode uint32) error { 688 var stat Stat_t 689 return Stat(path, &stat) 690 } 691 692 func Fchmod(fd int, mode uint32) error { 693 var stat Stat_t 694 return Fstat(fd, &stat) 695 } 696 697 func Chown(path string, uid, gid int) error { 698 return ENOSYS 699 } 700 701 func Fchown(fd int, uid, gid int) error { 702 return ENOSYS 703 } 704 705 func Lchown(path string, uid, gid int) error { 706 return ENOSYS 707 } 708 709 func UtimesNano(path string, ts []Timespec) error { 710 // UTIME_OMIT value must match internal/syscall/unix/at_wasip1.go 711 const UTIME_OMIT = -0x2 712 if path == "" { 713 return EINVAL 714 } 715 dirFd, pathPtr, pathLen := preparePath(path) 716 atime := TimespecToNsec(ts[0]) 717 mtime := TimespecToNsec(ts[1]) 718 if ts[0].Nsec == UTIME_OMIT || ts[1].Nsec == UTIME_OMIT { 719 var st Stat_t 720 if err := Stat(path, &st); err != nil { 721 return err 722 } 723 if ts[0].Nsec == UTIME_OMIT { 724 atime = int64(st.Atime) 725 } 726 if ts[1].Nsec == UTIME_OMIT { 727 mtime = int64(st.Mtime) 728 } 729 } 730 errno := path_filestat_set_times( 731 dirFd, 732 LOOKUP_SYMLINK_FOLLOW, 733 pathPtr, 734 pathLen, 735 timestamp(atime), 736 timestamp(mtime), 737 FILESTAT_SET_ATIM|FILESTAT_SET_MTIM, 738 ) 739 return errnoErr(errno) 740 } 741 742 func Rename(from, to string) error { 743 if from == "" || to == "" { 744 return EINVAL 745 } 746 oldDirFd, oldPathPtr, oldPathLen := preparePath(from) 747 newDirFd, newPathPtr, newPathLen := preparePath(to) 748 errno := path_rename( 749 oldDirFd, 750 oldPathPtr, 751 oldPathLen, 752 newDirFd, 753 newPathPtr, 754 newPathLen, 755 ) 756 return errnoErr(errno) 757 } 758 759 func Truncate(path string, length int64) error { 760 if path == "" { 761 return EINVAL 762 } 763 fd, err := Open(path, O_WRONLY, 0) 764 if err != nil { 765 return err 766 } 767 defer Close(fd) 768 return Ftruncate(fd, length) 769 } 770 771 func Ftruncate(fd int, length int64) error { 772 errno := fd_filestat_set_size(int32(fd), filesize(length)) 773 return errnoErr(errno) 774 } 775 776 const ImplementsGetwd = true 777 778 func Getwd() (string, error) { 779 return cwd, nil 780 } 781 782 func Chdir(path string) error { 783 if path == "" { 784 return EINVAL 785 } 786 787 dir := "/" 788 if !isAbs(path) { 789 dir = cwd 790 } 791 path = joinPath(dir, path) 792 793 var stat Stat_t 794 dirFd, pathPtr, pathLen := preparePath(path) 795 errno := path_filestat_get(dirFd, LOOKUP_SYMLINK_FOLLOW, pathPtr, pathLen, unsafe.Pointer(&stat)) 796 if errno != 0 { 797 return errnoErr(errno) 798 } 799 if stat.Filetype != FILETYPE_DIRECTORY { 800 return ENOTDIR 801 } 802 cwd = path 803 return nil 804 } 805 806 func Readlink(path string, buf []byte) (n int, err error) { 807 if path == "" { 808 return 0, EINVAL 809 } 810 if len(buf) == 0 { 811 return 0, nil 812 } 813 dirFd, pathPtr, pathLen := preparePath(path) 814 var nwritten size 815 errno := path_readlink( 816 dirFd, 817 pathPtr, 818 pathLen, 819 unsafe.Pointer(&buf[0]), 820 size(len(buf)), 821 unsafe.Pointer(&nwritten), 822 ) 823 // For some reason wasmtime returns ERANGE when the output buffer is 824 // shorter than the symbolic link value. os.Readlink expects a nil 825 // error and uses the fact that n is greater or equal to the buffer 826 // length to assume that it needs to try again with a larger size. 827 // This condition is handled in os.Readlink. 828 return int(nwritten), errnoErr(errno) 829 } 830 831 func Link(path, link string) error { 832 if path == "" || link == "" { 833 return EINVAL 834 } 835 oldDirFd, oldPathPtr, oldPathLen := preparePath(path) 836 newDirFd, newPathPtr, newPathLen := preparePath(link) 837 errno := path_link( 838 oldDirFd, 839 0, 840 oldPathPtr, 841 oldPathLen, 842 newDirFd, 843 newPathPtr, 844 newPathLen, 845 ) 846 return errnoErr(errno) 847 } 848 849 func Symlink(path, link string) error { 850 if path == "" || link == "" { 851 return EINVAL 852 } 853 dirFd, pathPtr, pathlen := preparePath(link) 854 errno := path_symlink( 855 stringPointer(path), 856 size(len(path)), 857 dirFd, 858 pathPtr, 859 pathlen, 860 ) 861 return errnoErr(errno) 862 } 863 864 func Fsync(fd int) error { 865 errno := fd_sync(int32(fd)) 866 return errnoErr(errno) 867 } 868 869 func bytesPointer(b []byte) unsafe.Pointer { 870 return unsafe.Pointer(unsafe.SliceData(b)) 871 } 872 873 func stringPointer(s string) unsafe.Pointer { 874 return unsafe.Pointer(unsafe.StringData(s)) 875 } 876 877 func makeIOVec(b []byte) unsafe.Pointer { 878 return unsafe.Pointer(&iovec{ 879 buf: uintptr32(uintptr(bytesPointer(b))), 880 bufLen: size(len(b)), 881 }) 882 } 883 884 func Read(fd int, b []byte) (int, error) { 885 var nread size 886 errno := fd_read(int32(fd), makeIOVec(b), 1, unsafe.Pointer(&nread)) 887 runtime.KeepAlive(b) 888 return int(nread), errnoErr(errno) 889 } 890 891 func Write(fd int, b []byte) (int, error) { 892 var nwritten size 893 errno := fd_write(int32(fd), makeIOVec(b), 1, unsafe.Pointer(&nwritten)) 894 runtime.KeepAlive(b) 895 return int(nwritten), errnoErr(errno) 896 } 897 898 func Pread(fd int, b []byte, offset int64) (int, error) { 899 var nread size 900 errno := fd_pread(int32(fd), makeIOVec(b), 1, filesize(offset), unsafe.Pointer(&nread)) 901 runtime.KeepAlive(b) 902 return int(nread), errnoErr(errno) 903 } 904 905 func Pwrite(fd int, b []byte, offset int64) (int, error) { 906 var nwritten size 907 errno := fd_pwrite(int32(fd), makeIOVec(b), 1, filesize(offset), unsafe.Pointer(&nwritten)) 908 runtime.KeepAlive(b) 909 return int(nwritten), errnoErr(errno) 910 } 911 912 func Seek(fd int, offset int64, whence int) (int64, error) { 913 var newoffset filesize 914 errno := fd_seek(int32(fd), filedelta(offset), uint32(whence), unsafe.Pointer(&newoffset)) 915 return int64(newoffset), errnoErr(errno) 916 } 917 918 func Dup(fd int) (int, error) { 919 return 0, ENOSYS 920 } 921 922 func Dup2(fd, newfd int) error { 923 return ENOSYS 924 } 925 926 func Pipe(fd []int) error { 927 return ENOSYS 928 } 929 930 func RandomGet(b []byte) error { 931 errno := random_get(bytesPointer(b), size(len(b))) 932 return errnoErr(errno) 933 }