Implements filesystem, networking and os interactions.
For #58141
Co-authored-by: Richard Musiol <neelance@gmail.com>
Co-authored-by: Achille Roussel <achille.roussel@gmail.com>
Co-authored-by: Julien Fabre <ju.pryz@gmail.com>
Co-authored-by: Evan Phoenix <evan@phx.io>
Change-Id: If5c43ad5bd2955e6d2d4e2822fd68bce89ca786c
Reviewed-on: https://go-review.googlesource.com/c/go/+/479619
Reviewed-by: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Cherry Mui <cherryyz@google.com>
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build unix || (js && wasm)
+//go:build unix || (js && wasm) || wasip1
package syscall
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build unix || (js && wasm) || plan9
+//go:build unix || (js && wasm) || plan9 || wasip1
// Unix environment variables.
--- /dev/null
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build wasip1
+
+package syscall
+
+func JoinPath(dir, file string) string {
+ return joinPath(dir, file)
+}
--- /dev/null
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build wasip1
+
+package syscall
+
+import (
+ "runtime"
+ "unsafe"
+)
+
+type uintptr32 = uint32
+type size = uint32
+type fdflags = uint32
+type filesize = uint64
+type filetype = uint8
+type lookupflags = uint32
+type oflags = uint32
+type rights = uint64
+type timestamp = uint64
+type dircookie = uint64
+type filedelta = int64
+type fstflags = uint32
+
+type iovec struct {
+ buf uintptr32
+ bufLen size
+}
+
+const (
+ LOOKUP_SYMLINK_FOLLOW = 0x00000001
+)
+
+const (
+ OFLAG_CREATE = 0x0001
+ OFLAG_DIRECTORY = 0x0002
+ OFLAG_EXCL = 0x0004
+ OFLAG_TRUNC = 0x0008
+)
+
+const (
+ FDFLAG_APPEND = 0x0001
+ FDFLAG_DSYNC = 0x0002
+ FDFLAG_NONBLOCK = 0x0004
+ FDFLAG_RSYNC = 0x0008
+ FDFLAG_SYNC = 0x0010
+)
+
+const (
+ RIGHT_FD_DATASYNC = 1 << iota
+ RIGHT_FD_READ
+ RIGHT_FD_SEEK
+ RIGHT_FDSTAT_SET_FLAGS
+ RIGHT_FD_SYNC
+ RIGHT_FD_TELL
+ RIGHT_FD_WRITE
+ RIGHT_FD_ADVISE
+ RIGHT_FD_ALLOCATE
+ RIGHT_PATH_CREATE_DIRECTORY
+ RIGHT_PATH_CREATE_FILE
+ RIGHT_PATH_LINK_SOURCE
+ RIGHT_PATH_LINK_TARGET
+ RIGHT_PATH_OPEN
+ RIGHT_FD_READDIR
+ RIGHT_PATH_READLINK
+ RIGHT_PATH_RENAME_SOURCE
+ RIGHT_PATH_RENAME_TARGET
+ RIGHT_PATH_FILESTAT_GET
+ RIGHT_PATH_FILESTAT_SET_SIZE
+ RIGHT_PATH_FILESTAT_SET_TIMES
+ RIGHT_FD_FILESTAT_GET
+ RIGHT_FD_FILESTAT_SET_SIZE
+ RIGHT_FD_FILESTAT_SET_TIMES
+ RIGHT_PATH_SYMLINK
+ RIGHT_PATH_REMOVE_DIRECTORY
+ RIGHT_PATH_UNLINK_FILE
+ RIGHT_POLL_FD_READWRITE
+ RIGHT_SOCK_SHUTDOWN
+ RIGHT_SOCK_ACCEPT
+)
+
+const (
+ WHENCE_SET = 0
+ WHENCE_CUR = 1
+ WHENCE_END = 2
+)
+
+const (
+ FILESTAT_SET_ATIM = 0x0001
+ FILESTAT_SET_ATIM_NOW = 0x0002
+ FILESTAT_SET_MTIM = 0x0004
+ FILESTAT_SET_MTIM_NOW = 0x0008
+)
+
+const (
+ // Despite the rights being defined as a 64 bits integer in the spec,
+ // wasmtime crashes the program if we set any of the upper 32 bits.
+ fullRights = rights(^uint32(0))
+ readRights = rights(RIGHT_FD_READ | RIGHT_FD_READDIR)
+ writeRights = rights(RIGHT_FD_DATASYNC | RIGHT_FD_WRITE | RIGHT_FD_ALLOCATE | RIGHT_PATH_FILESTAT_SET_SIZE)
+)
+
+// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-fd_closefd-fd---result-errno
+//
+//go:wasmimport wasi_snapshot_preview1 fd_close
+//go:noescape
+func fd_close(fd int32) Errno
+
+// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-fd_filestat_set_sizefd-fd-size-filesize---result-errno
+//
+//go:wasmimport wasi_snapshot_preview1 fd_filestat_set_size
+//go:noescape
+func fd_filestat_set_size(fd int32, set_size filesize) Errno
+
+// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-fd_preadfd-fd-iovs-iovec_array-offset-filesize---resultsize-errno
+//
+//go:wasmimport wasi_snapshot_preview1 fd_pread
+//go:noescape
+func fd_pread(fd int32, iovs *iovec, iovsLen size, offset filesize, nread *size) Errno
+
+//go:wasmimport wasi_snapshot_preview1 fd_pwrite
+//go:noescape
+func fd_pwrite(fd int32, iovs *iovec, iovsLen size, offset filesize, nwritten *size) Errno
+
+//go:wasmimport wasi_snapshot_preview1 fd_read
+//go:noescape
+func fd_read(fd int32, iovs *iovec, iovsLen size, nread *size) Errno
+
+//go:wasmimport wasi_snapshot_preview1 fd_readdir
+//go:noescape
+func fd_readdir(fd int32, buf *byte, bufLen size, cookie dircookie, nwritten *size) Errno
+
+//go:wasmimport wasi_snapshot_preview1 fd_seek
+//go:noescape
+func fd_seek(fd int32, offset filedelta, whence uint32, newoffset *filesize) Errno
+
+// 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
+//
+//go:wasmimport wasi_snapshot_preview1 fd_fdstat_set_rights
+//go:noescape
+func fd_fdstat_set_rights(fd int32, rightsBase rights, rightsInheriting rights) Errno
+
+//go:wasmimport wasi_snapshot_preview1 fd_filestat_get
+//go:noescape
+func fd_filestat_get(fd int32, buf *Stat_t) Errno
+
+//go:wasmimport wasi_snapshot_preview1 fd_write
+//go:noescape
+func fd_write(fd int32, iovs *iovec, iovsLen size, nwritten *size) Errno
+
+//go:wasmimport wasi_snapshot_preview1 fd_sync
+//go:noescape
+func fd_sync(fd int32) Errno
+
+//go:wasmimport wasi_snapshot_preview1 path_create_directory
+//go:noescape
+func path_create_directory(fd int32, path *byte, pathLen size) Errno
+
+//go:wasmimport wasi_snapshot_preview1 path_filestat_get
+//go:noescape
+func path_filestat_get(fd int32, flags lookupflags, path *byte, pathLen size, buf *Stat_t) Errno
+
+//go:wasmimport wasi_snapshot_preview1 path_filestat_set_times
+//go:noescape
+func path_filestat_set_times(fd int32, flags lookupflags, path *byte, pathLen size, atim timestamp, mtim timestamp, fstflags fstflags) Errno
+
+//go:wasmimport wasi_snapshot_preview1 path_link
+//go:noescape
+func path_link(oldFd int32, oldFlags lookupflags, oldPath *byte, oldPathLen size, newFd int32, newPath *byte, newPathLen size) Errno
+
+//go:wasmimport wasi_snapshot_preview1 path_readlink
+//go:noescape
+func path_readlink(fd int32, path *byte, pathLen size, buf *byte, bufLen size, nwritten *size) Errno
+
+//go:wasmimport wasi_snapshot_preview1 path_remove_directory
+//go:noescape
+func path_remove_directory(fd int32, path *byte, pathLen size) Errno
+
+//go:wasmimport wasi_snapshot_preview1 path_rename
+//go:noescape
+func path_rename(oldFd int32, oldPath *byte, oldPathLen size, newFd int32, newPath *byte, newPathLen size) Errno
+
+//go:wasmimport wasi_snapshot_preview1 path_symlink
+//go:noescape
+func path_symlink(oldPath *byte, oldPathLen size, fd int32, newPath *byte, newPathLen size) Errno
+
+//go:wasmimport wasi_snapshot_preview1 path_unlink_file
+//go:noescape
+func path_unlink_file(fd int32, path *byte, pathLen size) Errno
+
+//go:wasmimport wasi_snapshot_preview1 path_open
+//go:noescape
+func path_open(rootFD int32, dirflags lookupflags, path *byte, pathLen size, oflags oflags, fsRightsBase rights, fsRightsInheriting rights, fsFlags fdflags, fd *int32) Errno
+
+//go:wasmimport wasi_snapshot_preview1 random_get
+//go:noescape
+func random_get(buf *byte, bufLen size) Errno
+
+type preopentype = uint8
+
+const (
+ preopentypeDir preopentype = iota
+)
+
+type prestatDir struct {
+ prNameLen size
+}
+
+type prestat struct {
+ typ preopentype
+ dir prestatDir
+}
+
+//go:wasmimport wasi_snapshot_preview1 fd_prestat_get
+//go:noescape
+func fd_prestat_get(fd int32, prestat *prestat) Errno
+
+//go:wasmimport wasi_snapshot_preview1 fd_prestat_dir_name
+//go:noescape
+func fd_prestat_dir_name(fd int32, path *byte, pathLen size) Errno
+
+type opendir struct {
+ fd int32
+ name string
+}
+
+// List of preopen directories that were exposed by the runtime. The first one
+// is assumed to the be root directory of the file system, and others are seen
+// as mount points at sub paths of the root.
+var preopens []opendir
+
+// Current working directory. We maintain this as a string and resolve paths in
+// the code because wasmtime does not allow relative path lookups outside of the
+// scope of a directory; a previous approach we tried consisted in maintaining
+// open a file descriptor to the current directory so we could perform relative
+// path lookups from that location, but it resulted in breaking path resolution
+// from the current directory to its parent.
+var cwd string
+
+func init() {
+ dirNameBuf := make([]byte, 256)
+ // We start looking for preopens at fd=3 because 0, 1, and 2 are reserved
+ // for standard input and outputs.
+ for preopenFd := int32(3); ; preopenFd++ {
+ var prestat prestat
+
+ errno := fd_prestat_get(preopenFd, &prestat)
+ if errno == EBADF {
+ break
+ }
+ if errno != 0 {
+ panic("fd_prestat: " + errno.Error())
+ }
+ if prestat.typ != preopentypeDir {
+ continue
+ }
+ if int(prestat.dir.prNameLen) > len(dirNameBuf) {
+ dirNameBuf = make([]byte, prestat.dir.prNameLen)
+ }
+
+ errno = fd_prestat_dir_name(preopenFd, &dirNameBuf[0], prestat.dir.prNameLen)
+ if errno != 0 {
+ panic("fd_prestat_dir_name: " + errno.Error())
+ }
+
+ preopens = append(preopens, opendir{
+ fd: preopenFd,
+ name: string(dirNameBuf[:prestat.dir.prNameLen]),
+ })
+ }
+
+ if cwd, _ = Getenv("PWD"); cwd != "" {
+ cwd = joinPath("/", cwd)
+ } else if len(preopens) > 0 {
+ cwd = preopens[0].name
+ }
+}
+
+// Provided by package runtime.
+func now() (sec int64, nsec int32)
+
+//go:nosplit
+func appendCleanPath(buf []byte, path string, lookupParent bool) ([]byte, bool) {
+ i := 0
+ for i < len(path) {
+ for i < len(path) && path[i] == '/' {
+ i++
+ }
+
+ j := i
+ for j < len(path) && path[j] != '/' {
+ j++
+ }
+
+ s := path[i:j]
+ i = j
+
+ switch s {
+ case "":
+ continue
+ case ".":
+ continue
+ case "..":
+ if !lookupParent {
+ k := len(buf)
+ for k > 0 && buf[k-1] != '/' {
+ k--
+ }
+ for k > 1 && buf[k-1] == '/' {
+ k--
+ }
+ buf = buf[:k]
+ if k == 0 {
+ lookupParent = true
+ } else {
+ s = ""
+ continue
+ }
+ }
+ default:
+ lookupParent = false
+ }
+
+ if len(buf) > 0 && buf[len(buf)-1] != '/' {
+ buf = append(buf, '/')
+ }
+ buf = append(buf, s...)
+ }
+ return buf, lookupParent
+}
+
+// joinPath concatenates dir and file paths, producing a cleaned path where
+// "." and ".." have been removed, unless dir is relative and the references
+// to parent directories in file represented a location relatie to a parent
+// of dir.
+//
+// This function is used for path resolution of all wasi functions expecting
+// a path argument; the returned string is heap allocated, which we may want
+// to optimize in the future. Instead of returning a string, the function
+// could append the result to an output buffer that the functions in this
+// file can manage to have allocated on the stack (e.g. initializing to a
+// fixed capacity). Since it will significantly increase code complexity,
+// we prefer to optimize for readability and maintainability at this time.
+func joinPath(dir, file string) string {
+ buf := make([]byte, 0, len(dir)+len(file)+1)
+ if isAbs(dir) {
+ buf = append(buf, '/')
+ }
+ buf, lookupParent := appendCleanPath(buf, dir, false)
+ buf, _ = appendCleanPath(buf, file, lookupParent)
+ // The appendCleanPath function cleans the path so it does not inject
+ // references to the current directory. If both the dir and file args
+ // were ".", this results in the output buffer being empty so we handle
+ // this condition here.
+ if len(buf) == 0 {
+ buf = append(buf, '.')
+ }
+ // If the file ended with a '/' we make sure that the output also ends
+ // with a '/'. This is needed to ensure that programs have a mechanism
+ // to represent dereferencing symbolic links pointing to directories.
+ if buf[len(buf)-1] != '/' && isDir(file) {
+ buf = append(buf, '/')
+ }
+ return unsafe.String(&buf[0], len(buf))
+}
+
+func isAbs(path string) bool {
+ return hasPrefix(path, "/")
+}
+
+func isDir(path string) bool {
+ return hasSuffix(path, "/")
+}
+
+func hasPrefix(s, p string) bool {
+ return len(s) >= len(p) && s[:len(p)] == p
+}
+
+func hasSuffix(s, x string) bool {
+ return len(s) >= len(x) && s[len(s)-len(x):] == x
+}
+
+// preparePath returns the preopen file descriptor of the directory to perform
+// path resolution from, along with the pair of pointer and length for the
+// relative expression of path from the directory.
+//
+// If the path argument is not absolute, it is first appended to the current
+// working directory before resolution.
+func preparePath(path string) (int32, *byte, size) {
+ var dirFd = int32(-1)
+ var dirName string
+
+ dir := "/"
+ if !isAbs(path) {
+ dir = cwd
+ }
+ path = joinPath(dir, path)
+
+ for _, p := range preopens {
+ if len(p.name) > len(dirName) && hasPrefix(path, p.name) {
+ dirFd, dirName = p.fd, p.name
+ }
+ }
+
+ path = path[len(dirName):]
+ for isAbs(path) {
+ path = path[1:]
+ }
+ if len(path) == 0 {
+ path = "."
+ }
+
+ return dirFd, unsafe.StringData(path), size(len(path))
+}
+
+func Open(path string, openmode int, perm uint32) (int, error) {
+ if path == "" {
+ return -1, EINVAL
+ }
+ dirFd, pathPtr, pathLen := preparePath(path)
+
+ var oflags oflags
+ if (openmode & O_CREATE) != 0 {
+ oflags |= OFLAG_CREATE
+ }
+ if (openmode & O_TRUNC) != 0 {
+ oflags |= OFLAG_TRUNC
+ }
+ if (openmode & O_EXCL) != 0 {
+ oflags |= OFLAG_EXCL
+ }
+
+ // Remove when https://github.com/bytecodealliance/wasmtime/pull/4967 is merged.
+ var fi Stat_t
+ if errno := path_filestat_get(
+ dirFd,
+ LOOKUP_SYMLINK_FOLLOW,
+ pathPtr,
+ pathLen,
+ &fi,
+ ); errno != 0 && errno != ENOENT {
+ return -1, errnoErr(errno)
+ }
+ if fi.Filetype == FILETYPE_DIRECTORY {
+ oflags |= OFLAG_DIRECTORY
+ // WASM runtimes appear to return EINVAL when passing invalid
+ // combination of flags to open directories; however, TestOpenError
+ // in the os package expects EISDIR, so we precheck this condition
+ // here to emulate the expected behavior.
+ const invalidFlags = O_WRONLY | O_RDWR | O_CREATE | O_APPEND | O_TRUNC | O_EXCL
+ if (openmode & invalidFlags) != 0 {
+ return 0, EISDIR
+ }
+ }
+
+ var rights rights
+ switch openmode & (O_RDONLY | O_WRONLY | O_RDWR) {
+ case O_RDONLY:
+ rights = fullRights & ^writeRights
+ case O_WRONLY:
+ rights = fullRights & ^readRights
+ case O_RDWR:
+ rights = fullRights
+ }
+
+ var fdflags fdflags
+ if (openmode & O_APPEND) != 0 {
+ fdflags |= FDFLAG_APPEND
+ }
+ if (openmode & O_SYNC) != 0 {
+ fdflags |= FDFLAG_SYNC
+ }
+
+ var fd int32
+ errno := path_open(
+ dirFd,
+ LOOKUP_SYMLINK_FOLLOW,
+ pathPtr,
+ pathLen,
+ oflags,
+ rights,
+ fullRights,
+ fdflags,
+ &fd,
+ )
+ return int(fd), errnoErr(errno)
+}
+
+func Close(fd int) error {
+ errno := fd_close(int32(fd))
+ return errnoErr(errno)
+}
+
+func CloseOnExec(fd int) {
+ // nothing to do - no exec
+}
+
+func Mkdir(path string, perm uint32) error {
+ if path == "" {
+ return EINVAL
+ }
+ dirFd, pathPtr, pathLen := preparePath(path)
+ errno := path_create_directory(dirFd, pathPtr, pathLen)
+ return errnoErr(errno)
+}
+
+func ReadDir(fd int, buf []byte, cookie dircookie) (int, error) {
+ var nwritten size
+ errno := fd_readdir(int32(fd), &buf[0], size(len(buf)), cookie, &nwritten)
+ return int(nwritten), errnoErr(errno)
+}
+
+type Stat_t struct {
+ Dev uint64
+ Ino uint64
+ Filetype uint8
+ Nlink uint64
+ Size uint64
+ Atime uint64
+ Mtime uint64
+ Ctime uint64
+
+ Mode int
+
+ // Uid and Gid are always zero on wasip1 platforms
+ Uid uint32
+ Gid uint32
+}
+
+func Stat(path string, st *Stat_t) error {
+ if path == "" {
+ return EINVAL
+ }
+ dirFd, pathPtr, pathLen := preparePath(path)
+ errno := path_filestat_get(dirFd, LOOKUP_SYMLINK_FOLLOW, pathPtr, pathLen, st)
+ setDefaultMode(st)
+ return errnoErr(errno)
+}
+
+func Lstat(path string, st *Stat_t) error {
+ if path == "" {
+ return EINVAL
+ }
+ dirFd, pathPtr, pathLen := preparePath(path)
+ errno := path_filestat_get(dirFd, 0, pathPtr, pathLen, st)
+ setDefaultMode(st)
+ return errnoErr(errno)
+}
+
+func Fstat(fd int, st *Stat_t) error {
+ errno := fd_filestat_get(int32(fd), st)
+ setDefaultMode(st)
+ return errnoErr(errno)
+}
+
+func setDefaultMode(st *Stat_t) {
+ // WASI does not support unix-like permissions, but Go programs are likely
+ // to expect the permission bits to not be zero so we set defaults to help
+ // avoid breaking applications that are migrating to WASM.
+ if st.Filetype == FILETYPE_DIRECTORY {
+ st.Mode = 0700
+ } else {
+ st.Mode = 0600
+ }
+}
+
+func Unlink(path string) error {
+ if path == "" {
+ return EINVAL
+ }
+ dirFd, pathPtr, pathLen := preparePath(path)
+ errno := path_unlink_file(dirFd, pathPtr, pathLen)
+ return errnoErr(errno)
+}
+
+func Rmdir(path string) error {
+ if path == "" {
+ return EINVAL
+ }
+ dirFd, pathPtr, pathLen := preparePath(path)
+ errno := path_remove_directory(dirFd, pathPtr, pathLen)
+ return errnoErr(errno)
+}
+
+func Chmod(path string, mode uint32) error {
+ var stat Stat_t
+ return Stat(path, &stat)
+}
+
+func Fchmod(fd int, mode uint32) error {
+ var stat Stat_t
+ return Fstat(fd, &stat)
+}
+
+func Chown(path string, uid, gid int) error {
+ return ENOSYS
+}
+
+func Fchown(fd int, uid, gid int) error {
+ return ENOSYS
+}
+
+func Lchown(path string, uid, gid int) error {
+ return ENOSYS
+}
+
+func UtimesNano(path string, ts []Timespec) error {
+ if path == "" {
+ return EINVAL
+ }
+ dirFd, pathPtr, pathLen := preparePath(path)
+ errno := path_filestat_set_times(
+ dirFd,
+ LOOKUP_SYMLINK_FOLLOW,
+ pathPtr,
+ pathLen,
+ timestamp(TimespecToNsec(ts[0])),
+ timestamp(TimespecToNsec(ts[1])),
+ FILESTAT_SET_ATIM|FILESTAT_SET_MTIM,
+ )
+ return errnoErr(errno)
+}
+
+func Rename(from, to string) error {
+ if from == "" || to == "" {
+ return EINVAL
+ }
+ oldDirFd, oldPathPtr, oldPathLen := preparePath(from)
+ newDirFd, newPathPtr, newPathLen := preparePath(to)
+ errno := path_rename(
+ oldDirFd,
+ oldPathPtr,
+ oldPathLen,
+ newDirFd,
+ newPathPtr,
+ newPathLen,
+ )
+ return errnoErr(errno)
+}
+
+func Truncate(path string, length int64) error {
+ if path == "" {
+ return EINVAL
+ }
+ fd, err := Open(path, O_WRONLY, 0)
+ if err != nil {
+ return err
+ }
+ defer Close(fd)
+ return Ftruncate(fd, length)
+}
+
+func Ftruncate(fd int, length int64) error {
+ errno := fd_filestat_set_size(int32(fd), filesize(length))
+ return errnoErr(errno)
+}
+
+const ImplementsGetwd = true
+
+func Getwd() (string, error) {
+ return cwd, nil
+}
+
+func Chdir(path string) error {
+ if path == "" {
+ return EINVAL
+ }
+
+ dir := "/"
+ if !isAbs(path) {
+ dir = cwd
+ }
+ path = joinPath(dir, path)
+
+ var stat Stat_t
+ dirFd, pathPtr, pathLen := preparePath(path)
+ errno := path_filestat_get(dirFd, LOOKUP_SYMLINK_FOLLOW, pathPtr, pathLen, &stat)
+ if errno != 0 {
+ return errnoErr(errno)
+ }
+ if stat.Filetype != FILETYPE_DIRECTORY {
+ return ENOTDIR
+ }
+ cwd = path
+ return nil
+}
+
+func Readlink(path string, buf []byte) (n int, err error) {
+ if path == "" {
+ return 0, EINVAL
+ }
+ if len(buf) == 0 {
+ return 0, nil
+ }
+ dirFd, pathPtr, pathLen := preparePath(path)
+ var nwritten size
+ errno := path_readlink(
+ dirFd,
+ pathPtr,
+ pathLen,
+ &buf[0],
+ size(len(buf)),
+ &nwritten,
+ )
+ // For some reason wasmtime returns ERANGE when the output buffer is
+ // shorter than the symbolic link value. os.Readlink expects a nil
+ // error and uses the fact that n is greater or equal to the buffer
+ // length to assume that it needs to try again with a larger size.
+ // This condition is handled in os.Readlink.
+ return int(nwritten), errnoErr(errno)
+}
+
+func Link(path, link string) error {
+ if path == "" || link == "" {
+ return EINVAL
+ }
+ oldDirFd, oldPathPtr, oldPathLen := preparePath(path)
+ newDirFd, newPathPtr, newPathLen := preparePath(link)
+ errno := path_link(
+ oldDirFd,
+ 0,
+ oldPathPtr,
+ oldPathLen,
+ newDirFd,
+ newPathPtr,
+ newPathLen,
+ )
+ return errnoErr(errno)
+}
+
+func Symlink(path, link string) error {
+ if path == "" || link == "" {
+ return EINVAL
+ }
+ dirFd, pathPtr, pathlen := preparePath(link)
+ errno := path_symlink(
+ unsafe.StringData(path),
+ size(len(path)),
+ dirFd,
+ pathPtr,
+ pathlen,
+ )
+ return errnoErr(errno)
+}
+
+func Fsync(fd int) error {
+ errno := fd_sync(int32(fd))
+ return errnoErr(errno)
+}
+
+func makeIOVec(b []byte) *iovec {
+ return &iovec{
+ buf: uintptr32(uintptr(unsafe.Pointer(unsafe.SliceData(b)))),
+ bufLen: size(len(b)),
+ }
+}
+
+func Read(fd int, b []byte) (int, error) {
+ var nread size
+ errno := fd_read(int32(fd), makeIOVec(b), 1, &nread)
+ runtime.KeepAlive(b)
+ return int(nread), errnoErr(errno)
+}
+
+func Write(fd int, b []byte) (int, error) {
+ var nwritten size
+ errno := fd_write(int32(fd), makeIOVec(b), 1, &nwritten)
+ runtime.KeepAlive(b)
+ return int(nwritten), errnoErr(errno)
+}
+
+func Pread(fd int, b []byte, offset int64) (int, error) {
+ var nread size
+ errno := fd_pread(int32(fd), makeIOVec(b), 1, filesize(offset), &nread)
+ runtime.KeepAlive(b)
+ return int(nread), errnoErr(errno)
+}
+
+func Pwrite(fd int, b []byte, offset int64) (int, error) {
+ var nwritten size
+ errno := fd_pwrite(int32(fd), makeIOVec(b), 1, filesize(offset), &nwritten)
+ runtime.KeepAlive(b)
+ return int(nwritten), errnoErr(errno)
+}
+
+func Seek(fd int, offset int64, whence int) (int64, error) {
+ var newoffset filesize
+ errno := fd_seek(int32(fd), filedelta(offset), uint32(whence), &newoffset)
+ return int64(newoffset), errnoErr(errno)
+}
+
+func Dup(fd int) (int, error) {
+ return 0, ENOSYS
+}
+
+func Dup2(fd, newfd int) error {
+ return ENOSYS
+}
+
+func Pipe(fd []int) error {
+ return ENOSYS
+}
+
+func RandomGet(b []byte) error {
+ errno := random_get(unsafe.SliceData(b), size(len(b)))
+ return errnoErr(errno)
+}
--- /dev/null
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build wasip1
+
+package syscall_test
+
+import (
+ "syscall"
+ "testing"
+)
+
+var joinPathTests = [...]struct {
+ dir, file, path string
+}{
+ 0: {".", ".", "."},
+ 1: {"./", "./", "./"},
+ 2: {"././././", ".", "."},
+ 3: {".", "./././", "./"},
+ 4: {".", "a", "a"},
+ 5: {".", "a/b", "a/b"},
+ 6: {".", "..", ".."},
+ 7: {".", "../", "../"},
+ 8: {".", "../../", "../../"},
+ 9: {".", "../..", "../.."},
+ 10: {".", "../..//..///", "../../../"},
+ 11: {"/", "/", "/"},
+ 12: {"/", "a", "/a"},
+ 13: {"/", "a/b", "/a/b"},
+ 14: {"/a", "b", "/a/b"},
+ 15: {"/", ".", "/"},
+ 16: {"/", "..", "/"},
+ 17: {"/", "../../", "/"},
+ 18: {"/", "/../a/b/c", "/a/b/c"},
+ 19: {"/", "/../a/b/c", "/a/b/c"},
+ 20: {"/", "./hello/world", "/hello/world"},
+ 21: {"/a", "../", "/"},
+ 22: {"/a/b/c", "..", "/a/b"},
+ 23: {"/a/b/c", "..///..///", "/a/"},
+ 24: {"/a/b/c", "..///..///..", "/"},
+ 25: {"/a/b/c", "..///..///..///..", "/"},
+ 26: {"/a/b/c", "..///..///..///..///..", "/"},
+ 27: {"/a/b/c/", "/d/e/f/", "/a/b/c/d/e/f/"},
+ 28: {"a/b/c/", ".", "a/b/c"},
+ 29: {"a/b/c/", "./d", "a/b/c/d"},
+ 30: {"a/b/c/", "./d/", "a/b/c/d/"},
+ 31: {"a/b/", "./c/d/", "a/b/c/d/"},
+ 32: {"../", "..", "../.."},
+ 33: {"a/b/c/d", "e/../..", "a/b/c"},
+ 34: {"a/b/c/d", "./e/../..", "a/b/c"},
+ 35: {"a/b/c/d", "./e/..//../../f/g//", "a/b/f/g/"},
+ 36: {"../../../", "a/../../b/c", "../../b/c"},
+ 37: {"/a/b/c", "/.././/hey!", "/a/b/hey!"},
+}
+
+func TestJoinPath(t *testing.T) {
+ for _, test := range joinPathTests {
+ t.Run("", func(t *testing.T) {
+ path := syscall.JoinPath(test.dir, test.file)
+ if path != test.path {
+ t.Errorf("join(%q,%q): want=%q got=%q", test.dir, test.file, test.path, path)
+ }
+ })
+ }
+}
+
+func BenchmarkJoinPath(b *testing.B) {
+ for _, test := range joinPathTests {
+ b.Run("", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ syscall.JoinPath(test.dir, test.file)
+ }
+ })
+ }
+}
--- /dev/null
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// wasip1/wasm uses fake networking directly implemented in the net package.
+// This file only exists to make the compiler happy.
+
+//go:build wasip1
+
+package syscall
+
+const (
+ AF_UNSPEC = iota
+ AF_UNIX
+ AF_INET
+ AF_INET6
+)
+
+const (
+ SOCK_STREAM = 1 + iota
+ SOCK_DGRAM
+ SOCK_RAW
+ SOCK_SEQPACKET
+)
+
+const (
+ IPPROTO_IP = 0
+ IPPROTO_IPV4 = 4
+ IPPROTO_IPV6 = 0x29
+ IPPROTO_TCP = 6
+ IPPROTO_UDP = 0x11
+)
+
+const (
+ _ = iota
+ IPV6_V6ONLY
+ SOMAXCONN
+ SO_ERROR
+)
+
+// Misc constants expected by package net but not supported.
+const (
+ _ = iota
+ F_DUPFD_CLOEXEC
+ SYS_FCNTL = 500 // unsupported; same value as net_nacl.go
+)
+
+type Sockaddr interface {
+}
+
+type SockaddrInet4 struct {
+ Port int
+ Addr [4]byte
+}
+
+type SockaddrInet6 struct {
+ Port int
+ ZoneId uint32
+ Addr [16]byte
+}
+
+type SockaddrUnix struct {
+ Name string
+}
+
+func Socket(proto, sotype, unused int) (fd int, err error) {
+ return 0, ENOSYS
+}
+
+func Bind(fd int, sa Sockaddr) error {
+ return ENOSYS
+}
+
+func StopIO(fd int) error {
+ return ENOSYS
+}
+
+func Listen(fd int, backlog int) error {
+ return ENOSYS
+}
+
+func Accept(fd int) (newfd int, sa Sockaddr, err error) {
+ return 0, nil, ENOSYS
+}
+
+func Connect(fd int, sa Sockaddr) error {
+ return ENOSYS
+}
+
+func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) {
+ return 0, nil, ENOSYS
+}
+
+func Sendto(fd int, p []byte, flags int, to Sockaddr) error {
+ return ENOSYS
+}
+
+func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn, recvflags int, from Sockaddr, err error) {
+ return 0, 0, 0, nil, ENOSYS
+}
+
+func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
+ return 0, ENOSYS
+}
+
+func GetsockoptInt(fd, level, opt int) (value int, err error) {
+ return 0, ENOSYS
+}
+
+func SetsockoptInt(fd, level, opt int, value int) error {
+ return ENOSYS
+}
+
+func SetReadDeadline(fd int, t int64) error {
+ return ENOSYS
+}
+
+func SetWriteDeadline(fd int, t int64) error {
+ return ENOSYS
+}
+
+func Shutdown(fd int, how int) error {
+ return ENOSYS
+}
+
+func SetNonblock(fd int, nonblocking bool) error {
+ return ENOSYS
+}
--- /dev/null
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syscall
+
+//go:wasmimport wasi_snapshot_preview1 proc_exit
+func ProcExit(code int32)
--- /dev/null
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build wasip1
+
+package syscall
+
+import (
+ "internal/itoa"
+ "internal/oserror"
+ "unsafe"
+)
+
+type Dircookie = uint64
+
+type Filetype = uint8
+
+const (
+ FILETYPE_UNKNOWN Filetype = iota
+ FILETYPE_BLOCK_DEVICE
+ FILETYPE_CHARACTER_DEVICE
+ FILETYPE_DIRECTORY
+ FILETYPE_REGULAR_FILE
+ FILETYPE_SOCKET_DGRAM
+ FILETYPE_SOCKET_STREAM
+ FILETYPE_SYMBOLIC_LINK
+)
+
+type Dirent struct {
+ // The offset of the next directory entry stored in this directory.
+ Next Dircookie
+ // The serial number of the file referred to by this directory entry.
+ Ino uint64
+ // The length of the name of the directory entry.
+ Namlen uint32
+ // The type of the file referred to by this directory entry.
+ Type Filetype
+ // Name of the directory entry.
+ Name *byte
+}
+
+func direntIno(buf []byte) (uint64, bool) {
+ return readInt(buf, unsafe.Offsetof(Dirent{}.Ino), unsafe.Sizeof(Dirent{}.Ino))
+}
+
+func direntReclen(buf []byte) (uint64, bool) {
+ namelen, ok := direntNamlen(buf)
+ return 24 + namelen, ok
+}
+
+func direntNamlen(buf []byte) (uint64, bool) {
+ return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen))
+}
+
+// An Errno is an unsigned number describing an error condition.
+// It implements the error interface. The zero Errno is by convention
+// a non-error, so code to convert from Errno to error should use:
+//
+// var err = nil
+// if errno != 0 {
+// err = errno
+// }
+type Errno uint32
+
+func (e Errno) Error() string {
+ if 0 <= int(e) && int(e) < len(errorstr) {
+ s := errorstr[e]
+ if s != "" {
+ return s
+ }
+ }
+ return "errno " + itoa.Itoa(int(e))
+}
+
+func (e Errno) Is(target error) bool {
+ switch target {
+ case oserror.ErrPermission:
+ return e == EACCES || e == EPERM
+ case oserror.ErrExist:
+ return e == EEXIST || e == ENOTEMPTY
+ case oserror.ErrNotExist:
+ return e == ENOENT
+ }
+ return false
+}
+
+func (e Errno) Temporary() bool {
+ return e == EINTR || e == EMFILE || e.Timeout()
+}
+
+func (e Errno) Timeout() bool {
+ return e == EAGAIN || e == ETIMEDOUT
+}
+
+// A Signal is a number describing a process signal.
+// It implements the os.Signal interface.
+type Signal uint8
+
+const (
+ SIGNONE Signal = iota
+ SIGHUP
+ SIGINT
+ SIGQUIT
+ SIGILL
+ SIGTRAP
+ SIGABRT
+ SIGBUS
+ SIGFPE
+ SIGKILL
+ SIGUSR1
+ SIGSEGV
+ SIGUSR2
+ SIGPIPE
+ SIGALRM
+ SIGTERM
+ SIGCHLD
+ SIGCONT
+ SIGSTOP
+ SIGTSTP
+ SIGTTIN
+ SIGTTOU
+ SIGURG
+ SIGXCPU
+ SIGXFSZ
+ SIGVTARLM
+ SIGPROF
+ SIGWINCH
+ SIGPOLL
+ SIGPWR
+ SIGSYS
+)
+
+func (s Signal) Signal() {}
+
+func (s Signal) String() string {
+ switch s {
+ case SIGNONE:
+ return "no signal"
+ case SIGHUP:
+ return "hangup"
+ case SIGINT:
+ return "interrupt"
+ case SIGQUIT:
+ return "quit"
+ case SIGILL:
+ return "illegal instruction"
+ case SIGTRAP:
+ return "trace/breakpoint trap"
+ case SIGABRT:
+ return "abort"
+ case SIGBUS:
+ return "bus error"
+ case SIGFPE:
+ return "floating point exception"
+ case SIGKILL:
+ return "killed"
+ case SIGUSR1:
+ return "user defined signal 1"
+ case SIGSEGV:
+ return "segmentation fault"
+ case SIGUSR2:
+ return "user defined signal 2"
+ case SIGPIPE:
+ return "broken pipe"
+ case SIGALRM:
+ return "alarm clock"
+ case SIGTERM:
+ return "terminated"
+ case SIGCHLD:
+ return "child exited"
+ case SIGCONT:
+ return "continued"
+ case SIGSTOP:
+ return "stopped (signal)"
+ case SIGTSTP:
+ return "stopped"
+ case SIGTTIN:
+ return "stopped (tty input)"
+ case SIGTTOU:
+ return "stopped (tty output)"
+ case SIGURG:
+ return "urgent I/O condition"
+ case SIGXCPU:
+ return "CPU time limit exceeded"
+ case SIGXFSZ:
+ return "file size limit exceeded"
+ case SIGVTARLM:
+ return "virtual timer expired"
+ case SIGPROF:
+ return "profiling timer expired"
+ case SIGWINCH:
+ return "window changed"
+ case SIGPOLL:
+ return "I/O possible"
+ case SIGPWR:
+ return "power failure"
+ case SIGSYS:
+ return "bad system call"
+ default:
+ return "signal " + itoa.Itoa(int(s))
+ }
+}
+
+const (
+ Stdin = 0
+ Stdout = 1
+ Stderr = 2
+)
+
+const (
+ O_RDONLY = 0
+ O_WRONLY = 1
+ O_RDWR = 2
+
+ O_CREAT = 0100
+ O_CREATE = O_CREAT
+ O_TRUNC = 01000
+ O_APPEND = 02000
+ O_EXCL = 0200
+ O_SYNC = 010000
+
+ O_CLOEXEC = 0
+)
+
+const (
+ F_DUPFD = 0
+ F_GETFD = 1
+ F_SETFD = 2
+ F_GETFL = 3
+ F_SETFL = 4
+ F_GETOWN = 5
+ F_SETOWN = 6
+ F_GETLK = 7
+ F_SETLK = 8
+ F_SETLKW = 9
+ F_RGETLK = 10
+ F_RSETLK = 11
+ F_CNVT = 12
+ F_RSETLKW = 13
+
+ F_RDLCK = 1
+ F_WRLCK = 2
+ F_UNLCK = 3
+ F_UNLKSYS = 4
+)
+
+const (
+ S_IFMT = 0000370000
+ S_IFSHM_SYSV = 0000300000
+ S_IFSEMA = 0000270000
+ S_IFCOND = 0000260000
+ S_IFMUTEX = 0000250000
+ S_IFSHM = 0000240000
+ S_IFBOUNDSOCK = 0000230000
+ S_IFSOCKADDR = 0000220000
+ S_IFDSOCK = 0000210000
+
+ S_IFSOCK = 0000140000
+ S_IFLNK = 0000120000
+ S_IFREG = 0000100000
+ S_IFBLK = 0000060000
+ S_IFDIR = 0000040000
+ S_IFCHR = 0000020000
+ S_IFIFO = 0000010000
+
+ S_UNSUP = 0000370000
+
+ S_ISUID = 0004000
+ S_ISGID = 0002000
+ S_ISVTX = 0001000
+
+ S_IREAD = 0400
+ S_IWRITE = 0200
+ S_IEXEC = 0100
+
+ S_IRWXU = 0700
+ S_IRUSR = 0400
+ S_IWUSR = 0200
+ S_IXUSR = 0100
+
+ S_IRWXG = 070
+ S_IRGRP = 040
+ S_IWGRP = 020
+ S_IXGRP = 010
+
+ S_IRWXO = 07
+ S_IROTH = 04
+ S_IWOTH = 02
+ S_IXOTH = 01
+)
+
+type WaitStatus uint32
+
+func (w WaitStatus) Exited() bool { return false }
+func (w WaitStatus) ExitStatus() int { return 0 }
+func (w WaitStatus) Signaled() bool { return false }
+func (w WaitStatus) Signal() Signal { return 0 }
+func (w WaitStatus) CoreDump() bool { return false }
+func (w WaitStatus) Stopped() bool { return false }
+func (w WaitStatus) Continued() bool { return false }
+func (w WaitStatus) StopSignal() Signal { return 0 }
+func (w WaitStatus) TrapCause() int { return 0 }
+
+// Rusage is a placeholder to allow compilation of the os/exec package
+// because we need Go programs to be portable across platforms. WASI does
+// not have a mechanism to to spawn processes so there is no reason for an
+// application to take a dependency on this type.
+type Rusage struct {
+ Utime Timeval
+ Stime Timeval
+}
+
+// ProcAttr is a placeholder to allow compilation of the os/exec package
+// because we need Go programs to be portable across platforms. WASI does
+// not have a mechanism to to spawn processes so there is no reason for an
+// application to take a dependency on this type.
+type ProcAttr struct {
+ Dir string
+ Env []string
+ Files []uintptr
+ Sys *SysProcAttr
+}
+
+type SysProcAttr struct {
+}
+
+func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
+ return 0, 0, ENOSYS
+}
+
+func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
+ return 0, 0, ENOSYS
+}
+
+func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
+ return 0, 0, ENOSYS
+}
+
+func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
+ return 0, 0, ENOSYS
+}
+
+func Sysctl(key string) (string, error) {
+ if key == "kern.hostname" {
+ return "wasip1", nil
+ }
+ return "", ENOSYS
+}
+
+func Getuid() int {
+ return 1
+}
+
+func Getgid() int {
+ return 1
+}
+
+func Geteuid() int {
+ return 1
+}
+
+func Getegid() int {
+ return 1
+}
+
+func Getgroups() ([]int, error) {
+ return []int{1}, nil
+}
+
+func Getpid() int {
+ return 3
+}
+
+func Getppid() int {
+ return 2
+}
+
+func Gettimeofday(tv *Timeval) error {
+ var time timestamp
+ if errno := clock_time_get(clockRealtime, 1e3, &time); errno != 0 {
+ return errno
+ }
+ tv.setTimestamp(time)
+ return nil
+}
+
+func Kill(pid int, signum Signal) error {
+ // WASI does not have the notion of processes nor signal handlers.
+ //
+ // Any signal that the application raises to the process itself will
+ // be interpreted as being cause for termination.
+ if pid > 0 && pid != Getpid() {
+ return ESRCH
+ }
+ ProcExit(128 + int32(signum))
+ return nil
+}
+
+func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
+ return 0, ENOSYS
+}
+
+func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {
+ return 0, 0, ENOSYS
+}
+
+func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) {
+ return 0, ENOSYS
+}
+
+func Umask(mask int) int {
+ return 0
+}
+
+type Timespec struct {
+ Sec int64
+ Nsec int64
+}
+
+func (ts *Timespec) timestamp() timestamp {
+ return timestamp(ts.Sec*1e9) + timestamp(ts.Nsec)
+}
+
+func (ts *Timespec) setTimestamp(t timestamp) {
+ ts.Sec = int64(t / 1e9)
+ ts.Nsec = int64(t % 1e9)
+}
+
+type Timeval struct {
+ Sec int64
+ Usec int64
+}
+
+func (tv *Timeval) timestamp() timestamp {
+ return timestamp(tv.Sec*1e9) + timestamp(tv.Usec*1e3)
+}
+
+func (tv *Timeval) setTimestamp(t timestamp) {
+ tv.Sec = int64(t / 1e9)
+ tv.Usec = int64((t % 1e9) / 1e3)
+}
+
+func setTimespec(sec, nsec int64) Timespec {
+ return Timespec{Sec: sec, Nsec: nsec}
+}
+
+func setTimeval(sec, usec int64) Timeval {
+ return Timeval{Sec: sec, Usec: usec}
+}
+
+type clockid = uint32
+
+const (
+ clockRealtime clockid = iota
+ clockMonotonic
+ clockProcessCPUTimeID
+ clockThreadCPUTimeID
+)
+
+//go:wasmimport wasi_snapshot_preview1 clock_time_get
+//go:noescape
+func clock_time_get(id clockid, precision timestamp, time *timestamp) Errno
--- /dev/null
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build wasip1
+
+package syscall
+
+import "runtime"
+
+// TODO: Auto-generate some day. (Hard-coded in binaries so not likely to change.)
+const (
+ E2BIG Errno = 1
+ EACCES Errno = 2
+ EADDRINUSE Errno = 3
+ EADDRNOTAVAIL Errno = 4
+ EAFNOSUPPORT Errno = 5
+ EAGAIN Errno = 6
+ EALREADY Errno = 7
+ EBADF Errno = 8
+ EBADMSG Errno = 9
+ EBUSY Errno = 10
+ ECANCELED Errno = 11
+ ECHILD Errno = 12
+ ECONNABORTED Errno = 13
+ ECONNREFUSED Errno = 14
+ ECONNRESET Errno = 15
+ EDEADLK Errno = 16
+ EDESTADDRREQ Errno = 17
+ EDOM Errno = 18
+ EDQUOT Errno = 19
+ EEXIST Errno = 20
+ EFAULT Errno = 21
+ EFBIG Errno = 22
+ EHOSTUNREACH Errno = 23
+ EIDRM Errno = 24
+ EILSEQ Errno = 25
+ EINPROGRESS Errno = 26
+ EINTR Errno = 27
+ EINVAL Errno = 28
+ EIO Errno = 29
+ EISCONN Errno = 30
+ EISDIR Errno = 31
+ ELOOP Errno = 32
+ EMFILE Errno = 33
+ EMLINK Errno = 34
+ EMSGSIZE Errno = 35
+ EMULTIHOP Errno = 36
+ ENAMETOOLONG Errno = 37
+ ENETDOWN Errno = 38
+ ENETRESET Errno = 39
+ ENETUNREACH Errno = 40
+ ENFILE Errno = 41
+ ENOBUFS Errno = 42
+ ENODEV Errno = 43
+ ENOENT Errno = 44
+ ENOEXEC Errno = 45
+ ENOLCK Errno = 46
+ ENOLINK Errno = 47
+ ENOMEM Errno = 48
+ ENOMSG Errno = 49
+ ENOPROTOOPT Errno = 50
+ ENOSPC Errno = 51
+ ENOSYS Errno = 52
+ ENOTCONN Errno = 53
+ ENOTDIR Errno = 54
+ ENOTEMPTY Errno = 55
+ ENOTRECOVERABLE Errno = 56
+ ENOTSOCK Errno = 57
+ ENOTSUP Errno = 58
+ ENOTTY Errno = 59
+ ENXIO Errno = 60
+ EOVERFLOW Errno = 61
+ EOWNERDEAD Errno = 62
+ EPERM Errno = 63
+ EPIPE Errno = 64
+ EPROTO Errno = 65
+ EPROTONOSUPPORT Errno = 66
+ EPROTOTYPE Errno = 67
+ ERANGE Errno = 68
+ EROFS Errno = 69
+ ESPIPE Errno = 70
+ ESRCH Errno = 71
+ ESTALE Errno = 72
+ ETIMEDOUT Errno = 73
+ ETXTBSY Errno = 74
+ EXDEV Errno = 75
+ ENOTCAPABLE Errno = 76
+ // needed by src/net/error_unix_test.go
+ EOPNOTSUPP = ENOTSUP
+)
+
+// TODO: Auto-generate some day. (Hard-coded in binaries so not likely to change.)
+var errorstr = [...]string{
+ E2BIG: "Argument list too long",
+ EACCES: "Permission denied",
+ EADDRINUSE: "Address already in use",
+ EADDRNOTAVAIL: "Address not available",
+ EAFNOSUPPORT: "Address family not supported by protocol family",
+ EAGAIN: "Try again",
+ EALREADY: "Socket already connected",
+ EBADF: "Bad file number",
+ EBADMSG: "Trying to read unreadable message",
+ EBUSY: "Device or resource busy",
+ ECANCELED: "Operation canceled.",
+ ECHILD: "No child processes",
+ ECONNABORTED: "Connection aborted",
+ ECONNREFUSED: "Connection refused",
+ ECONNRESET: "Connection reset by peer",
+ EDEADLK: "Deadlock condition",
+ EDESTADDRREQ: "Destination address required",
+ EDOM: "Math arg out of domain of func",
+ EDQUOT: "Quota exceeded",
+ EEXIST: "File exists",
+ EFAULT: "Bad address",
+ EFBIG: "File too large",
+ EHOSTUNREACH: "Host is unreachable",
+ EIDRM: "Identifier removed",
+ EILSEQ: "EILSEQ",
+ EINPROGRESS: "Connection already in progress",
+ EINTR: "Interrupted system call",
+ EINVAL: "Invalid argument",
+ EIO: "I/O error",
+ EISCONN: "Socket is already connected",
+ EISDIR: "Is a directory",
+ ELOOP: "Too many symbolic links",
+ EMFILE: "Too many open files",
+ EMLINK: "Too many links",
+ EMSGSIZE: "Message too long",
+ EMULTIHOP: "Multihop attempted",
+ ENAMETOOLONG: "File name too long",
+ ENETDOWN: "Network interface is not configured",
+ ENETRESET: "Network dropped connection on reset",
+ ENETUNREACH: "Network is unreachable",
+ ENFILE: "File table overflow",
+ ENOBUFS: "No buffer space available",
+ ENODEV: "No such device",
+ ENOENT: "No such file or directory",
+ ENOEXEC: "Exec format error",
+ ENOLCK: "No record locks available",
+ ENOLINK: "The link has been severed",
+ ENOMEM: "Out of memory",
+ ENOMSG: "No message of desired type",
+ ENOPROTOOPT: "Protocol not available",
+ ENOSPC: "No space left on device",
+ ENOSYS: "Not implemented on " + runtime.GOOS,
+ ENOTCONN: "Socket is not connected",
+ ENOTDIR: "Not a directory",
+ ENOTEMPTY: "Directory not empty",
+ ENOTRECOVERABLE: "State not recoverable",
+ ENOTSOCK: "Socket operation on non-socket",
+ ENOTSUP: "Not supported",
+ ENOTTY: "Not a typewriter",
+ ENXIO: "No such device or address",
+ EOVERFLOW: "Value too large for defined data type",
+ EOWNERDEAD: "Owner died",
+ EPERM: "Operation not permitted",
+ EPIPE: "Broken pipe",
+ EPROTO: "Protocol error",
+ EPROTONOSUPPORT: "Unknown protocol",
+ EPROTOTYPE: "Protocol wrong type for socket",
+ ERANGE: "Math result not representable",
+ EROFS: "Read-only file system",
+ ESPIPE: "Illegal seek",
+ ESRCH: "No such process",
+ ESTALE: "Stale file handle",
+ ETIMEDOUT: "Connection timed out",
+ ETXTBSY: "Text file busy",
+ EXDEV: "Cross-device link",
+ ENOTCAPABLE: "Capabilities insufficient",
+}
+
+// Do the interface allocations only once for common
+// Errno values.
+var (
+ errEAGAIN error = EAGAIN
+ errEINVAL error = EINVAL
+ errENOENT error = ENOENT
+)
+
+// errnoErr returns common boxed Errno values, to prevent
+// allocations at runtime.
+//
+// We set both noinline and nosplit to reduce code size, this function has many
+// call sites in the syscall package, inlining it causes a significant increase
+// of the compiled code; the function call ultimately does not make a difference
+// in the performance of syscall functions since the time is dominated by calls
+// to the imports and path resolution.
+//
+//go:noinline
+//go:nosplit
+func errnoErr(e Errno) error {
+ switch e {
+ case 0:
+ return nil
+ case EAGAIN:
+ return errEAGAIN
+ case EINVAL:
+ return errEINVAL
+ case ENOENT:
+ return errENOENT
+ }
+ return e
+}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build unix || (js && wasm)
+//go:build unix || (js && wasm) || wasip1
package syscall