runtime: wasm compatible with js build tag
This commit is contained in:
323
runtime/internal/clite/syscall/fs_wasm.go
Normal file
323
runtime/internal/clite/syscall/fs_wasm.go
Normal file
@@ -0,0 +1,323 @@
|
||||
// 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 wasm
|
||||
|
||||
package syscall
|
||||
|
||||
import (
|
||||
"structs"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Try to set stdio to non-blocking mode before the os package
|
||||
// calls NewFile for each fd. NewFile queries the non-blocking flag
|
||||
// but doesn't change it, even if the runtime supports non-blocking
|
||||
// stdio. Since WebAssembly modules are single-threaded, blocking
|
||||
// system calls temporarily halt execution of the module. If the
|
||||
// runtime supports non-blocking stdio, the Go runtime is able to
|
||||
// use the WASI net poller to poll for read/write readiness and is
|
||||
// able to schedule goroutines while waiting.
|
||||
SetNonblock(0, true)
|
||||
SetNonblock(1, true)
|
||||
SetNonblock(2, true)
|
||||
}
|
||||
|
||||
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 {
|
||||
_ structs.HostLayout
|
||||
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)
|
||||
|
||||
// Some runtimes have very strict expectations when it comes to which
|
||||
// rights can be enabled on files opened by path_open. The fileRights
|
||||
// constant is used as a mask to retain only bits for operations that
|
||||
// are supported on files.
|
||||
fileRights rights = RIGHT_FD_DATASYNC |
|
||||
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
|
||||
|
||||
// Runtimes like wasmtime and wasmedge will refuse to open directories
|
||||
// if the rights requested by the application exceed the operations that
|
||||
// can be performed on a directory.
|
||||
dirRights rights = RIGHT_FD_SEEK |
|
||||
RIGHT_FDSTAT_SET_FLAGS |
|
||||
RIGHT_FD_SYNC |
|
||||
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_TIMES |
|
||||
RIGHT_PATH_SYMLINK |
|
||||
RIGHT_PATH_REMOVE_DIRECTORY |
|
||||
RIGHT_PATH_UNLINK_FILE
|
||||
)
|
||||
|
||||
// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-fdstat-record
|
||||
// fdflags must be at offset 2, hence the uint16 type rather than the
|
||||
// fdflags (uint32) type.
|
||||
type fdstat struct {
|
||||
_ structs.HostLayout
|
||||
filetype filetype
|
||||
fdflags uint16
|
||||
rightsBase rights
|
||||
rightsInheriting rights
|
||||
}
|
||||
|
||||
type preopentype = uint8
|
||||
|
||||
const (
|
||||
preopentypeDir preopentype = iota
|
||||
)
|
||||
|
||||
type prestatDir struct {
|
||||
_ structs.HostLayout
|
||||
prNameLen size
|
||||
}
|
||||
|
||||
type prestat struct {
|
||||
_ structs.HostLayout
|
||||
typ preopentype
|
||||
dir prestatDir
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
//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 relative 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, prefix string) bool {
|
||||
return len(s) >= len(prefix) && s[:len(prefix)] == prefix
|
||||
}
|
||||
|
||||
func hasSuffix(s, suffix string) bool {
|
||||
return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
|
||||
}
|
||||
|
||||
type Stat_t struct {
|
||||
Dev uint64
|
||||
Ino uint64
|
||||
Filetype uint8
|
||||
Nlink uint64
|
||||
Size uint64
|
||||
Atime uint64
|
||||
Mtime uint64
|
||||
Ctime uint64
|
||||
}
|
||||
Reference in New Issue
Block a user