mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2025-06-05 21:59:39 +02:00
[experiment] add alternative wasm sqlite3 implementation available via build-tag (#2863)
This allows for building GoToSocial with [SQLite transpiled to WASM](https://github.com/ncruces/go-sqlite3) and accessed through [Wazero](https://wazero.io/).
This commit is contained in:
105
vendor/github.com/tetratelabs/wazero/internal/sysfs/adapter.go
generated
vendored
Normal file
105
vendor/github.com/tetratelabs/wazero/internal/sysfs/adapter.go
generated
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"path"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
type AdaptFS struct {
|
||||
FS fs.FS
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer
|
||||
func (a *AdaptFS) String() string {
|
||||
return fmt.Sprintf("%v", a.FS)
|
||||
}
|
||||
|
||||
// OpenFile implements the same method as documented on sys.FS
|
||||
func (a *AdaptFS) OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) {
|
||||
return OpenFSFile(a.FS, cleanPath(path), flag, perm)
|
||||
}
|
||||
|
||||
// Lstat implements the same method as documented on sys.FS
|
||||
func (a *AdaptFS) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) {
|
||||
// At this time, we make the assumption sys.FS instances do not support
|
||||
// symbolic links, therefore Lstat is the same as Stat. This is obviously
|
||||
// not true, but until FS.FS has a solid story for how to handle symlinks,
|
||||
// we are better off not making a decision that would be difficult to
|
||||
// revert later on.
|
||||
//
|
||||
// For further discussions on the topic, see:
|
||||
// https://github.com/golang/go/issues/49580
|
||||
return a.Stat(path)
|
||||
}
|
||||
|
||||
// Stat implements the same method as documented on sys.FS
|
||||
func (a *AdaptFS) Stat(path string) (sys.Stat_t, experimentalsys.Errno) {
|
||||
f, errno := a.OpenFile(path, experimentalsys.O_RDONLY, 0)
|
||||
if errno != 0 {
|
||||
return sys.Stat_t{}, errno
|
||||
}
|
||||
defer f.Close()
|
||||
return f.Stat()
|
||||
}
|
||||
|
||||
// Readlink implements the same method as documented on sys.FS
|
||||
func (a *AdaptFS) Readlink(string) (string, experimentalsys.Errno) {
|
||||
return "", experimentalsys.ENOSYS
|
||||
}
|
||||
|
||||
// Mkdir implements the same method as documented on sys.FS
|
||||
func (a *AdaptFS) Mkdir(string, fs.FileMode) experimentalsys.Errno {
|
||||
return experimentalsys.ENOSYS
|
||||
}
|
||||
|
||||
// Chmod implements the same method as documented on sys.FS
|
||||
func (a *AdaptFS) Chmod(string, fs.FileMode) experimentalsys.Errno {
|
||||
return experimentalsys.ENOSYS
|
||||
}
|
||||
|
||||
// Rename implements the same method as documented on sys.FS
|
||||
func (a *AdaptFS) Rename(string, string) experimentalsys.Errno {
|
||||
return experimentalsys.ENOSYS
|
||||
}
|
||||
|
||||
// Rmdir implements the same method as documented on sys.FS
|
||||
func (a *AdaptFS) Rmdir(string) experimentalsys.Errno {
|
||||
return experimentalsys.ENOSYS
|
||||
}
|
||||
|
||||
// Link implements the same method as documented on sys.FS
|
||||
func (a *AdaptFS) Link(string, string) experimentalsys.Errno {
|
||||
return experimentalsys.ENOSYS
|
||||
}
|
||||
|
||||
// Symlink implements the same method as documented on sys.FS
|
||||
func (a *AdaptFS) Symlink(string, string) experimentalsys.Errno {
|
||||
return experimentalsys.ENOSYS
|
||||
}
|
||||
|
||||
// Unlink implements the same method as documented on sys.FS
|
||||
func (a *AdaptFS) Unlink(string) experimentalsys.Errno {
|
||||
return experimentalsys.ENOSYS
|
||||
}
|
||||
|
||||
// Utimens implements the same method as documented on sys.FS
|
||||
func (a *AdaptFS) Utimens(string, int64, int64) experimentalsys.Errno {
|
||||
return experimentalsys.ENOSYS
|
||||
}
|
||||
|
||||
func cleanPath(name string) string {
|
||||
if len(name) == 0 {
|
||||
return name
|
||||
}
|
||||
// fs.ValidFile cannot be rooted (start with '/')
|
||||
cleaned := name
|
||||
if name[0] == '/' {
|
||||
cleaned = name[1:]
|
||||
}
|
||||
cleaned = path.Clean(cleaned) // e.g. "sub/." -> "sub"
|
||||
return cleaned
|
||||
}
|
14
vendor/github.com/tetratelabs/wazero/internal/sysfs/datasync_linux.go
generated
vendored
Normal file
14
vendor/github.com/tetratelabs/wazero/internal/sysfs/datasync_linux.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
//go:build linux && !tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
func datasync(f *os.File) sys.Errno {
|
||||
return sys.UnwrapOSError(syscall.Fdatasync(int(f.Fd())))
|
||||
}
|
13
vendor/github.com/tetratelabs/wazero/internal/sysfs/datasync_tinygo.go
generated
vendored
Normal file
13
vendor/github.com/tetratelabs/wazero/internal/sysfs/datasync_tinygo.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
//go:build tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
func datasync(f *os.File) sys.Errno {
|
||||
return sys.ENOSYS
|
||||
}
|
14
vendor/github.com/tetratelabs/wazero/internal/sysfs/datasync_unsupported.go
generated
vendored
Normal file
14
vendor/github.com/tetratelabs/wazero/internal/sysfs/datasync_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
//go:build !linux
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
func datasync(f *os.File) sys.Errno {
|
||||
// Attempt to sync everything, even if we only need to sync the data.
|
||||
return fsync(f)
|
||||
}
|
24
vendor/github.com/tetratelabs/wazero/internal/sysfs/dir.go
generated
vendored
Normal file
24
vendor/github.com/tetratelabs/wazero/internal/sysfs/dir.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
func adjustReaddirErr(f sys.File, isClosed bool, err error) sys.Errno {
|
||||
if err == io.EOF {
|
||||
return 0 // e.g. Readdir on darwin returns io.EOF, but linux doesn't.
|
||||
} else if errno := sys.UnwrapOSError(err); errno != 0 {
|
||||
errno = dirError(f, isClosed, errno)
|
||||
// Comply with errors allowed on sys.File Readdir
|
||||
switch errno {
|
||||
case sys.EINVAL: // os.File Readdir can return this
|
||||
return sys.EBADF
|
||||
case sys.ENOTDIR: // dirError can return this
|
||||
return sys.EBADF
|
||||
}
|
||||
return errno
|
||||
}
|
||||
return 0
|
||||
}
|
99
vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs.go
generated
vendored
Normal file
99
vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs.go
generated
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/platform"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
func DirFS(dir string) experimentalsys.FS {
|
||||
return &dirFS{
|
||||
dir: dir,
|
||||
cleanedDir: ensureTrailingPathSeparator(dir),
|
||||
}
|
||||
}
|
||||
|
||||
func ensureTrailingPathSeparator(dir string) string {
|
||||
if !os.IsPathSeparator(dir[len(dir)-1]) {
|
||||
return dir + string(os.PathSeparator)
|
||||
}
|
||||
return dir
|
||||
}
|
||||
|
||||
// dirFS is not exported because the input fields must be maintained together.
|
||||
// This is likely why os.DirFS doesn't, either!
|
||||
type dirFS struct {
|
||||
experimentalsys.UnimplementedFS
|
||||
|
||||
dir string
|
||||
// cleanedDir is for easier OS-specific concatenation, as it always has
|
||||
// a trailing path separator.
|
||||
cleanedDir string
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer
|
||||
func (d *dirFS) String() string {
|
||||
return d.dir
|
||||
}
|
||||
|
||||
// OpenFile implements the same method as documented on sys.FS
|
||||
func (d *dirFS) OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) {
|
||||
return OpenOSFile(d.join(path), flag, perm)
|
||||
}
|
||||
|
||||
// Lstat implements the same method as documented on sys.FS
|
||||
func (d *dirFS) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) {
|
||||
return lstat(d.join(path))
|
||||
}
|
||||
|
||||
// Stat implements the same method as documented on sys.FS
|
||||
func (d *dirFS) Stat(path string) (sys.Stat_t, experimentalsys.Errno) {
|
||||
return stat(d.join(path))
|
||||
}
|
||||
|
||||
// Mkdir implements the same method as documented on sys.FS
|
||||
func (d *dirFS) Mkdir(path string, perm fs.FileMode) (errno experimentalsys.Errno) {
|
||||
err := os.Mkdir(d.join(path), perm)
|
||||
if errno = experimentalsys.UnwrapOSError(err); errno == experimentalsys.ENOTDIR {
|
||||
errno = experimentalsys.ENOENT
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Readlink implements the same method as documented on sys.FS
|
||||
func (d *dirFS) Readlink(path string) (string, experimentalsys.Errno) {
|
||||
// Note: do not use syscall.Readlink as that causes race on Windows.
|
||||
// In any case, syscall.Readlink does almost the same logic as os.Readlink.
|
||||
dst, err := os.Readlink(d.join(path))
|
||||
if err != nil {
|
||||
return "", experimentalsys.UnwrapOSError(err)
|
||||
}
|
||||
return platform.ToPosixPath(dst), 0
|
||||
}
|
||||
|
||||
// Rmdir implements the same method as documented on sys.FS
|
||||
func (d *dirFS) Rmdir(path string) experimentalsys.Errno {
|
||||
return rmdir(d.join(path))
|
||||
}
|
||||
|
||||
// Utimens implements the same method as documented on sys.FS
|
||||
func (d *dirFS) Utimens(path string, atim, mtim int64) experimentalsys.Errno {
|
||||
return utimens(d.join(path), atim, mtim)
|
||||
}
|
||||
|
||||
func (d *dirFS) join(path string) string {
|
||||
switch path {
|
||||
case "", ".", "/":
|
||||
if d.cleanedDir == "/" {
|
||||
return "/"
|
||||
}
|
||||
// cleanedDir includes an unnecessary delimiter for the root path.
|
||||
return d.cleanedDir[:len(d.cleanedDir)-1]
|
||||
}
|
||||
// TODO: Enforce similar to safefilepath.FromFS(path), but be careful as
|
||||
// relative path inputs are allowed. e.g. dir or path == ../
|
||||
return d.cleanedDir + path
|
||||
}
|
42
vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs_supported.go
generated
vendored
Normal file
42
vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs_supported.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
//go:build !tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
// Link implements the same method as documented on sys.FS
|
||||
func (d *dirFS) Link(oldName, newName string) experimentalsys.Errno {
|
||||
err := os.Link(d.join(oldName), d.join(newName))
|
||||
return experimentalsys.UnwrapOSError(err)
|
||||
}
|
||||
|
||||
// Unlink implements the same method as documented on sys.FS
|
||||
func (d *dirFS) Unlink(path string) (err experimentalsys.Errno) {
|
||||
return unlink(d.join(path))
|
||||
}
|
||||
|
||||
// Rename implements the same method as documented on sys.FS
|
||||
func (d *dirFS) Rename(from, to string) experimentalsys.Errno {
|
||||
from, to = d.join(from), d.join(to)
|
||||
return rename(from, to)
|
||||
}
|
||||
|
||||
// Chmod implements the same method as documented on sys.FS
|
||||
func (d *dirFS) Chmod(path string, perm fs.FileMode) experimentalsys.Errno {
|
||||
err := os.Chmod(d.join(path), perm)
|
||||
return experimentalsys.UnwrapOSError(err)
|
||||
}
|
||||
|
||||
// Symlink implements the same method as documented on sys.FS
|
||||
func (d *dirFS) Symlink(oldName, link string) experimentalsys.Errno {
|
||||
// Note: do not resolve `oldName` relative to this dirFS. The link result is always resolved
|
||||
// when dereference the `link` on its usage (e.g. readlink, read, etc).
|
||||
// https://github.com/bytecodealliance/cap-std/blob/v1.0.4/cap-std/src/fs/dir.rs#L404-L409
|
||||
err := os.Symlink(oldName, d.join(link))
|
||||
return experimentalsys.UnwrapOSError(err)
|
||||
}
|
34
vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs_unsupported.go
generated
vendored
Normal file
34
vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
//go:build tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
// Link implements the same method as documented on sys.FS
|
||||
func (d *dirFS) Link(oldName, newName string) experimentalsys.Errno {
|
||||
return experimentalsys.ENOSYS
|
||||
}
|
||||
|
||||
// Unlink implements the same method as documented on sys.FS
|
||||
func (d *dirFS) Unlink(path string) (err experimentalsys.Errno) {
|
||||
return experimentalsys.ENOSYS
|
||||
}
|
||||
|
||||
// Rename implements the same method as documented on sys.FS
|
||||
func (d *dirFS) Rename(from, to string) experimentalsys.Errno {
|
||||
return experimentalsys.ENOSYS
|
||||
}
|
||||
|
||||
// Chmod implements the same method as documented on sys.FS
|
||||
func (d *dirFS) Chmod(path string, perm fs.FileMode) experimentalsys.Errno {
|
||||
return experimentalsys.ENOSYS
|
||||
}
|
||||
|
||||
// Symlink implements the same method as documented on sys.FS
|
||||
func (d *dirFS) Symlink(oldName, link string) experimentalsys.Errno {
|
||||
return experimentalsys.ENOSYS
|
||||
}
|
520
vendor/github.com/tetratelabs/wazero/internal/sysfs/file.go
generated
vendored
Normal file
520
vendor/github.com/tetratelabs/wazero/internal/sysfs/file.go
generated
vendored
Normal file
@ -0,0 +1,520 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/fsapi"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
func NewStdioFile(stdin bool, f fs.File) (fsapi.File, error) {
|
||||
// Return constant stat, which has fake times, but keep the underlying
|
||||
// file mode. Fake times are needed to pass wasi-testsuite.
|
||||
// https://github.com/WebAssembly/wasi-testsuite/blob/af57727/tests/rust/src/bin/fd_filestat_get.rs#L1-L19
|
||||
var mode fs.FileMode
|
||||
if st, err := f.Stat(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
mode = st.Mode()
|
||||
}
|
||||
var flag experimentalsys.Oflag
|
||||
if stdin {
|
||||
flag = experimentalsys.O_RDONLY
|
||||
} else {
|
||||
flag = experimentalsys.O_WRONLY
|
||||
}
|
||||
var file fsapi.File
|
||||
if of, ok := f.(*os.File); ok {
|
||||
// This is ok because functions that need path aren't used by stdioFile
|
||||
file = newOsFile("", flag, 0, of)
|
||||
} else {
|
||||
file = &fsFile{file: f}
|
||||
}
|
||||
return &stdioFile{File: file, st: sys.Stat_t{Mode: mode, Nlink: 1}}, nil
|
||||
}
|
||||
|
||||
func OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (*os.File, experimentalsys.Errno) {
|
||||
if flag&experimentalsys.O_DIRECTORY != 0 && flag&(experimentalsys.O_WRONLY|experimentalsys.O_RDWR) != 0 {
|
||||
return nil, experimentalsys.EISDIR // invalid to open a directory writeable
|
||||
}
|
||||
return openFile(path, flag, perm)
|
||||
}
|
||||
|
||||
func OpenOSFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) {
|
||||
f, errno := OpenFile(path, flag, perm)
|
||||
if errno != 0 {
|
||||
return nil, errno
|
||||
}
|
||||
return newOsFile(path, flag, perm, f), 0
|
||||
}
|
||||
|
||||
func OpenFSFile(fs fs.FS, path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) {
|
||||
if flag&experimentalsys.O_DIRECTORY != 0 && flag&(experimentalsys.O_WRONLY|experimentalsys.O_RDWR) != 0 {
|
||||
return nil, experimentalsys.EISDIR // invalid to open a directory writeable
|
||||
}
|
||||
f, err := fs.Open(path)
|
||||
if errno := experimentalsys.UnwrapOSError(err); errno != 0 {
|
||||
return nil, errno
|
||||
}
|
||||
// Don't return an os.File because the path is not absolute. osFile needs
|
||||
// the path to be real and certain FS.File impls are subrooted.
|
||||
return &fsFile{fs: fs, name: path, file: f}, 0
|
||||
}
|
||||
|
||||
type stdioFile struct {
|
||||
fsapi.File
|
||||
st sys.Stat_t
|
||||
}
|
||||
|
||||
// SetAppend implements File.SetAppend
|
||||
func (f *stdioFile) SetAppend(bool) experimentalsys.Errno {
|
||||
// Ignore for stdio.
|
||||
return 0
|
||||
}
|
||||
|
||||
// IsAppend implements File.SetAppend
|
||||
func (f *stdioFile) IsAppend() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Stat implements File.Stat
|
||||
func (f *stdioFile) Stat() (sys.Stat_t, experimentalsys.Errno) {
|
||||
return f.st, 0
|
||||
}
|
||||
|
||||
// Close implements File.Close
|
||||
func (f *stdioFile) Close() experimentalsys.Errno {
|
||||
return 0
|
||||
}
|
||||
|
||||
// fsFile is used for wrapped fs.File, like os.Stdin or any fs.File
|
||||
// implementation. Notably, this does not have access to the full file path.
|
||||
// so certain operations can't be supported, such as inode lookups on Windows.
|
||||
type fsFile struct {
|
||||
experimentalsys.UnimplementedFile
|
||||
|
||||
// fs is the file-system that opened the file, or nil when wrapped for
|
||||
// pre-opens like stdio.
|
||||
fs fs.FS
|
||||
|
||||
// name is what was used in fs for Open, so it may not be the actual path.
|
||||
name string
|
||||
|
||||
// file is always set, possibly an os.File like os.Stdin.
|
||||
file fs.File
|
||||
|
||||
// reopenDir is true if reopen should be called before Readdir. This flag
|
||||
// is deferred until Readdir to prevent redundant rewinds. This could
|
||||
// happen if Seek(0) was called twice, or if in Windows, Seek(0) was called
|
||||
// before Readdir.
|
||||
reopenDir bool
|
||||
|
||||
// closed is true when closed was called. This ensures proper sys.EBADF
|
||||
closed bool
|
||||
|
||||
// cachedStat includes fields that won't change while a file is open.
|
||||
cachedSt *cachedStat
|
||||
}
|
||||
|
||||
type cachedStat struct {
|
||||
// dev is the same as sys.Stat_t Dev.
|
||||
dev uint64
|
||||
|
||||
// dev is the same as sys.Stat_t Ino.
|
||||
ino sys.Inode
|
||||
|
||||
// isDir is sys.Stat_t Mode masked with fs.ModeDir
|
||||
isDir bool
|
||||
}
|
||||
|
||||
// cachedStat returns the cacheable parts of sys.Stat_t or an error if they
|
||||
// couldn't be retrieved.
|
||||
func (f *fsFile) cachedStat() (dev uint64, ino sys.Inode, isDir bool, errno experimentalsys.Errno) {
|
||||
if f.cachedSt == nil {
|
||||
if _, errno = f.Stat(); errno != 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
return f.cachedSt.dev, f.cachedSt.ino, f.cachedSt.isDir, 0
|
||||
}
|
||||
|
||||
// Dev implements the same method as documented on sys.File
|
||||
func (f *fsFile) Dev() (uint64, experimentalsys.Errno) {
|
||||
dev, _, _, errno := f.cachedStat()
|
||||
return dev, errno
|
||||
}
|
||||
|
||||
// Ino implements the same method as documented on sys.File
|
||||
func (f *fsFile) Ino() (sys.Inode, experimentalsys.Errno) {
|
||||
_, ino, _, errno := f.cachedStat()
|
||||
return ino, errno
|
||||
}
|
||||
|
||||
// IsDir implements the same method as documented on sys.File
|
||||
func (f *fsFile) IsDir() (bool, experimentalsys.Errno) {
|
||||
_, _, isDir, errno := f.cachedStat()
|
||||
return isDir, errno
|
||||
}
|
||||
|
||||
// IsAppend implements the same method as documented on sys.File
|
||||
func (f *fsFile) IsAppend() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SetAppend implements the same method as documented on sys.File
|
||||
func (f *fsFile) SetAppend(bool) (errno experimentalsys.Errno) {
|
||||
return fileError(f, f.closed, experimentalsys.ENOSYS)
|
||||
}
|
||||
|
||||
// Stat implements the same method as documented on sys.File
|
||||
func (f *fsFile) Stat() (sys.Stat_t, experimentalsys.Errno) {
|
||||
if f.closed {
|
||||
return sys.Stat_t{}, experimentalsys.EBADF
|
||||
}
|
||||
|
||||
st, errno := statFile(f.file)
|
||||
switch errno {
|
||||
case 0:
|
||||
f.cachedSt = &cachedStat{dev: st.Dev, ino: st.Ino, isDir: st.Mode&fs.ModeDir == fs.ModeDir}
|
||||
case experimentalsys.EIO:
|
||||
errno = experimentalsys.EBADF
|
||||
}
|
||||
return st, errno
|
||||
}
|
||||
|
||||
// Read implements the same method as documented on sys.File
|
||||
func (f *fsFile) Read(buf []byte) (n int, errno experimentalsys.Errno) {
|
||||
if n, errno = read(f.file, buf); errno != 0 {
|
||||
// Defer validation overhead until we've already had an error.
|
||||
errno = fileError(f, f.closed, errno)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pread implements the same method as documented on sys.File
|
||||
func (f *fsFile) Pread(buf []byte, off int64) (n int, errno experimentalsys.Errno) {
|
||||
if ra, ok := f.file.(io.ReaderAt); ok {
|
||||
if n, errno = pread(ra, buf, off); errno != 0 {
|
||||
// Defer validation overhead until we've already had an error.
|
||||
errno = fileError(f, f.closed, errno)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// See /RATIONALE.md "fd_pread: io.Seeker fallback when io.ReaderAt is not supported"
|
||||
if rs, ok := f.file.(io.ReadSeeker); ok {
|
||||
// Determine the current position in the file, as we need to revert it.
|
||||
currentOffset, err := rs.Seek(0, io.SeekCurrent)
|
||||
if err != nil {
|
||||
return 0, fileError(f, f.closed, experimentalsys.UnwrapOSError(err))
|
||||
}
|
||||
|
||||
// Put the read position back when complete.
|
||||
defer func() { _, _ = rs.Seek(currentOffset, io.SeekStart) }()
|
||||
|
||||
// If the current offset isn't in sync with this reader, move it.
|
||||
if off != currentOffset {
|
||||
if _, err = rs.Seek(off, io.SeekStart); err != nil {
|
||||
return 0, fileError(f, f.closed, experimentalsys.UnwrapOSError(err))
|
||||
}
|
||||
}
|
||||
|
||||
n, err = rs.Read(buf)
|
||||
if errno = experimentalsys.UnwrapOSError(err); errno != 0 {
|
||||
// Defer validation overhead until we've already had an error.
|
||||
errno = fileError(f, f.closed, errno)
|
||||
}
|
||||
} else {
|
||||
errno = experimentalsys.ENOSYS // unsupported
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Seek implements the same method as documented on sys.File
|
||||
func (f *fsFile) Seek(offset int64, whence int) (newOffset int64, errno experimentalsys.Errno) {
|
||||
// If this is a directory, and we're attempting to seek to position zero,
|
||||
// we have to re-open the file to ensure the directory state is reset.
|
||||
var isDir bool
|
||||
if offset == 0 && whence == io.SeekStart {
|
||||
if isDir, errno = f.IsDir(); errno == 0 && isDir {
|
||||
f.reopenDir = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if s, ok := f.file.(io.Seeker); ok {
|
||||
if newOffset, errno = seek(s, offset, whence); errno != 0 {
|
||||
// Defer validation overhead until we've already had an error.
|
||||
errno = fileError(f, f.closed, errno)
|
||||
}
|
||||
} else {
|
||||
errno = experimentalsys.ENOSYS // unsupported
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Readdir implements the same method as documented on sys.File
|
||||
//
|
||||
// Notably, this uses readdirFile or fs.ReadDirFile if available. This does not
|
||||
// return inodes on windows.
|
||||
func (f *fsFile) Readdir(n int) (dirents []experimentalsys.Dirent, errno experimentalsys.Errno) {
|
||||
// Windows lets you Readdir after close, FS.File also may not implement
|
||||
// close in a meaningful way. read our closed field to return consistent
|
||||
// results.
|
||||
if f.closed {
|
||||
errno = experimentalsys.EBADF
|
||||
return
|
||||
}
|
||||
|
||||
if f.reopenDir { // re-open the directory if needed.
|
||||
f.reopenDir = false
|
||||
if errno = adjustReaddirErr(f, f.closed, f.reopen()); errno != 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if of, ok := f.file.(readdirFile); ok {
|
||||
// We can't use f.name here because it is the path up to the sys.FS,
|
||||
// not necessarily the real path. For this reason, Windows may not be
|
||||
// able to populate inodes. However, Darwin and Linux will.
|
||||
if dirents, errno = readdir(of, "", n); errno != 0 {
|
||||
errno = adjustReaddirErr(f, f.closed, errno)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Try with FS.ReadDirFile which is available on api.FS implementations
|
||||
// like embed:FS.
|
||||
if rdf, ok := f.file.(fs.ReadDirFile); ok {
|
||||
entries, e := rdf.ReadDir(n)
|
||||
if errno = adjustReaddirErr(f, f.closed, e); errno != 0 {
|
||||
return
|
||||
}
|
||||
dirents = make([]experimentalsys.Dirent, 0, len(entries))
|
||||
for _, e := range entries {
|
||||
// By default, we don't attempt to read inode data
|
||||
dirents = append(dirents, experimentalsys.Dirent{Name: e.Name(), Type: e.Type()})
|
||||
}
|
||||
} else {
|
||||
errno = experimentalsys.EBADF // not a directory
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Write implements the same method as documented on sys.File.
|
||||
func (f *fsFile) Write(buf []byte) (n int, errno experimentalsys.Errno) {
|
||||
if w, ok := f.file.(io.Writer); ok {
|
||||
if n, errno = write(w, buf); errno != 0 {
|
||||
// Defer validation overhead until we've already had an error.
|
||||
errno = fileError(f, f.closed, errno)
|
||||
}
|
||||
} else {
|
||||
errno = experimentalsys.ENOSYS // unsupported
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pwrite implements the same method as documented on sys.File.
|
||||
func (f *fsFile) Pwrite(buf []byte, off int64) (n int, errno experimentalsys.Errno) {
|
||||
if wa, ok := f.file.(io.WriterAt); ok {
|
||||
if n, errno = pwrite(wa, buf, off); errno != 0 {
|
||||
// Defer validation overhead until we've already had an error.
|
||||
errno = fileError(f, f.closed, errno)
|
||||
}
|
||||
} else {
|
||||
errno = experimentalsys.ENOSYS // unsupported
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Close implements the same method as documented on sys.File.
|
||||
func (f *fsFile) Close() experimentalsys.Errno {
|
||||
if f.closed {
|
||||
return 0
|
||||
}
|
||||
f.closed = true
|
||||
return f.close()
|
||||
}
|
||||
|
||||
func (f *fsFile) close() experimentalsys.Errno {
|
||||
return experimentalsys.UnwrapOSError(f.file.Close())
|
||||
}
|
||||
|
||||
// IsNonblock implements the same method as documented on fsapi.File
|
||||
func (f *fsFile) IsNonblock() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SetNonblock implements the same method as documented on fsapi.File
|
||||
func (f *fsFile) SetNonblock(bool) experimentalsys.Errno {
|
||||
return experimentalsys.ENOSYS
|
||||
}
|
||||
|
||||
// Poll implements the same method as documented on fsapi.File
|
||||
func (f *fsFile) Poll(fsapi.Pflag, int32) (ready bool, errno experimentalsys.Errno) {
|
||||
return false, experimentalsys.ENOSYS
|
||||
}
|
||||
|
||||
// dirError is used for commands that work against a directory, but not a file.
|
||||
func dirError(f experimentalsys.File, isClosed bool, errno experimentalsys.Errno) experimentalsys.Errno {
|
||||
if vErrno := validate(f, isClosed, false, true); vErrno != 0 {
|
||||
return vErrno
|
||||
}
|
||||
return errno
|
||||
}
|
||||
|
||||
// fileError is used for commands that work against a file, but not a directory.
|
||||
func fileError(f experimentalsys.File, isClosed bool, errno experimentalsys.Errno) experimentalsys.Errno {
|
||||
if vErrno := validate(f, isClosed, true, false); vErrno != 0 {
|
||||
return vErrno
|
||||
}
|
||||
return errno
|
||||
}
|
||||
|
||||
// validate is used to making syscalls which will fail.
|
||||
func validate(f experimentalsys.File, isClosed, wantFile, wantDir bool) experimentalsys.Errno {
|
||||
if isClosed {
|
||||
return experimentalsys.EBADF
|
||||
}
|
||||
|
||||
isDir, errno := f.IsDir()
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
|
||||
if wantFile && isDir {
|
||||
return experimentalsys.EISDIR
|
||||
} else if wantDir && !isDir {
|
||||
return experimentalsys.ENOTDIR
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func read(r io.Reader, buf []byte) (n int, errno experimentalsys.Errno) {
|
||||
if len(buf) == 0 {
|
||||
return 0, 0 // less overhead on zero-length reads.
|
||||
}
|
||||
|
||||
n, err := r.Read(buf)
|
||||
return n, experimentalsys.UnwrapOSError(err)
|
||||
}
|
||||
|
||||
func pread(ra io.ReaderAt, buf []byte, off int64) (n int, errno experimentalsys.Errno) {
|
||||
if len(buf) == 0 {
|
||||
return 0, 0 // less overhead on zero-length reads.
|
||||
}
|
||||
|
||||
n, err := ra.ReadAt(buf, off)
|
||||
return n, experimentalsys.UnwrapOSError(err)
|
||||
}
|
||||
|
||||
func seek(s io.Seeker, offset int64, whence int) (int64, experimentalsys.Errno) {
|
||||
if uint(whence) > io.SeekEnd {
|
||||
return 0, experimentalsys.EINVAL // negative or exceeds the largest valid whence
|
||||
}
|
||||
|
||||
newOffset, err := s.Seek(offset, whence)
|
||||
return newOffset, experimentalsys.UnwrapOSError(err)
|
||||
}
|
||||
|
||||
// reopenFile allows re-opening a file for reasons such as applying flags or
|
||||
// directory iteration.
|
||||
type reopenFile func() experimentalsys.Errno
|
||||
|
||||
// compile-time check to ensure fsFile.reopen implements reopenFile.
|
||||
var _ reopenFile = (*fsFile)(nil).reopen
|
||||
|
||||
// reopen implements the same method as documented on reopenFile.
|
||||
func (f *fsFile) reopen() experimentalsys.Errno {
|
||||
_ = f.close()
|
||||
var err error
|
||||
f.file, err = f.fs.Open(f.name)
|
||||
return experimentalsys.UnwrapOSError(err)
|
||||
}
|
||||
|
||||
// readdirFile allows masking the `Readdir` function on os.File.
|
||||
type readdirFile interface {
|
||||
Readdir(n int) ([]fs.FileInfo, error)
|
||||
}
|
||||
|
||||
// readdir uses readdirFile.Readdir, special casing windows when path !="".
|
||||
func readdir(f readdirFile, path string, n int) (dirents []experimentalsys.Dirent, errno experimentalsys.Errno) {
|
||||
fis, e := f.Readdir(n)
|
||||
if errno = experimentalsys.UnwrapOSError(e); errno != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
dirents = make([]experimentalsys.Dirent, 0, len(fis))
|
||||
|
||||
// linux/darwin won't have to fan out to lstat, but windows will.
|
||||
var ino sys.Inode
|
||||
for fi := range fis {
|
||||
t := fis[fi]
|
||||
// inoFromFileInfo is more efficient than sys.NewStat_t, as it gets the
|
||||
// inode without allocating an instance and filling other fields.
|
||||
if ino, errno = inoFromFileInfo(path, t); errno != 0 {
|
||||
return
|
||||
}
|
||||
dirents = append(dirents, experimentalsys.Dirent{Name: t.Name(), Ino: ino, Type: t.Mode().Type()})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func write(w io.Writer, buf []byte) (n int, errno experimentalsys.Errno) {
|
||||
if len(buf) == 0 {
|
||||
return 0, 0 // less overhead on zero-length writes.
|
||||
}
|
||||
|
||||
n, err := w.Write(buf)
|
||||
return n, experimentalsys.UnwrapOSError(err)
|
||||
}
|
||||
|
||||
func pwrite(w io.WriterAt, buf []byte, off int64) (n int, errno experimentalsys.Errno) {
|
||||
if len(buf) == 0 {
|
||||
return 0, 0 // less overhead on zero-length writes.
|
||||
}
|
||||
|
||||
n, err := w.WriteAt(buf, off)
|
||||
return n, experimentalsys.UnwrapOSError(err)
|
||||
}
|
||||
|
||||
func chtimes(path string, atim, mtim int64) (errno experimentalsys.Errno) { //nolint:unused
|
||||
// When both inputs are omitted, there is nothing to change.
|
||||
if atim == experimentalsys.UTIME_OMIT && mtim == experimentalsys.UTIME_OMIT {
|
||||
return
|
||||
}
|
||||
|
||||
// UTIME_OMIT is expensive until progress is made in Go, as it requires a
|
||||
// stat to read-back the value to re-apply.
|
||||
// - https://github.com/golang/go/issues/32558.
|
||||
// - https://go-review.googlesource.com/c/go/+/219638 (unmerged)
|
||||
var st sys.Stat_t
|
||||
if atim == experimentalsys.UTIME_OMIT || mtim == experimentalsys.UTIME_OMIT {
|
||||
if st, errno = stat(path); errno != 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var atime, mtime time.Time
|
||||
if atim == experimentalsys.UTIME_OMIT {
|
||||
atime = epochNanosToTime(st.Atim)
|
||||
mtime = epochNanosToTime(mtim)
|
||||
} else if mtim == experimentalsys.UTIME_OMIT {
|
||||
atime = epochNanosToTime(atim)
|
||||
mtime = epochNanosToTime(st.Mtim)
|
||||
} else {
|
||||
atime = epochNanosToTime(atim)
|
||||
mtime = epochNanosToTime(mtim)
|
||||
}
|
||||
return experimentalsys.UnwrapOSError(os.Chtimes(path, atime, mtime))
|
||||
}
|
||||
|
||||
func epochNanosToTime(epochNanos int64) time.Time { //nolint:unused
|
||||
seconds := epochNanos / 1e9
|
||||
nanos := epochNanos % 1e9
|
||||
return time.Unix(seconds, nanos)
|
||||
}
|
39
vendor/github.com/tetratelabs/wazero/internal/sysfs/file_unix.go
generated
vendored
Normal file
39
vendor/github.com/tetratelabs/wazero/internal/sysfs/file_unix.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
//go:build unix && !tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
const (
|
||||
nonBlockingFileReadSupported = true
|
||||
nonBlockingFileWriteSupported = true
|
||||
)
|
||||
|
||||
func rmdir(path string) sys.Errno {
|
||||
err := syscall.Rmdir(path)
|
||||
return sys.UnwrapOSError(err)
|
||||
}
|
||||
|
||||
// readFd exposes syscall.Read.
|
||||
func readFd(fd uintptr, buf []byte) (int, sys.Errno) {
|
||||
if len(buf) == 0 {
|
||||
return 0, 0 // Short-circuit 0-len reads.
|
||||
}
|
||||
n, err := syscall.Read(int(fd), buf)
|
||||
errno := sys.UnwrapOSError(err)
|
||||
return n, errno
|
||||
}
|
||||
|
||||
// writeFd exposes syscall.Write.
|
||||
func writeFd(fd uintptr, buf []byte) (int, sys.Errno) {
|
||||
if len(buf) == 0 {
|
||||
return 0, 0 // Short-circuit 0-len writes.
|
||||
}
|
||||
n, err := syscall.Write(int(fd), buf)
|
||||
errno := sys.UnwrapOSError(err)
|
||||
return n, errno
|
||||
}
|
28
vendor/github.com/tetratelabs/wazero/internal/sysfs/file_unsupported.go
generated
vendored
Normal file
28
vendor/github.com/tetratelabs/wazero/internal/sysfs/file_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
//go:build !(unix || windows) || tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
const (
|
||||
nonBlockingFileReadSupported = false
|
||||
nonBlockingFileWriteSupported = false
|
||||
)
|
||||
|
||||
func rmdir(path string) sys.Errno {
|
||||
return sys.UnwrapOSError(os.Remove(path))
|
||||
}
|
||||
|
||||
// readFd returns ENOSYS on unsupported platforms.
|
||||
func readFd(fd uintptr, buf []byte) (int, sys.Errno) {
|
||||
return -1, sys.ENOSYS
|
||||
}
|
||||
|
||||
// writeFd returns ENOSYS on unsupported platforms.
|
||||
func writeFd(fd uintptr, buf []byte) (int, sys.Errno) {
|
||||
return -1, sys.ENOSYS
|
||||
}
|
175
vendor/github.com/tetratelabs/wazero/internal/sysfs/file_windows.go
generated
vendored
Normal file
175
vendor/github.com/tetratelabs/wazero/internal/sysfs/file_windows.go
generated
vendored
Normal file
@ -0,0 +1,175 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
const (
|
||||
nonBlockingFileReadSupported = true
|
||||
nonBlockingFileWriteSupported = false
|
||||
|
||||
_ERROR_IO_INCOMPLETE = syscall.Errno(996)
|
||||
)
|
||||
|
||||
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
// procPeekNamedPipe is the syscall.LazyProc in kernel32 for PeekNamedPipe
|
||||
var (
|
||||
// procPeekNamedPipe is the syscall.LazyProc in kernel32 for PeekNamedPipe
|
||||
procPeekNamedPipe = kernel32.NewProc("PeekNamedPipe")
|
||||
// procGetOverlappedResult is the syscall.LazyProc in kernel32 for GetOverlappedResult
|
||||
procGetOverlappedResult = kernel32.NewProc("GetOverlappedResult")
|
||||
// procCreateEventW is the syscall.LazyProc in kernel32 for CreateEventW
|
||||
procCreateEventW = kernel32.NewProc("CreateEventW")
|
||||
)
|
||||
|
||||
// readFd returns ENOSYS on unsupported platforms.
|
||||
//
|
||||
// PeekNamedPipe: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-peeknamedpipe
|
||||
// "GetFileType can assist in determining what device type the handle refers to. A console handle presents as FILE_TYPE_CHAR."
|
||||
// https://learn.microsoft.com/en-us/windows/console/console-handles
|
||||
func readFd(fd uintptr, buf []byte) (int, sys.Errno) {
|
||||
handle := syscall.Handle(fd)
|
||||
fileType, err := syscall.GetFileType(handle)
|
||||
if err != nil {
|
||||
return 0, sys.UnwrapOSError(err)
|
||||
}
|
||||
if fileType&syscall.FILE_TYPE_CHAR == 0 {
|
||||
return -1, sys.ENOSYS
|
||||
}
|
||||
n, errno := peekNamedPipe(handle)
|
||||
if errno == syscall.ERROR_BROKEN_PIPE {
|
||||
return 0, 0
|
||||
}
|
||||
if n == 0 {
|
||||
return -1, sys.EAGAIN
|
||||
}
|
||||
un, err := syscall.Read(handle, buf[0:n])
|
||||
return un, sys.UnwrapOSError(err)
|
||||
}
|
||||
|
||||
func writeFd(fd uintptr, buf []byte) (int, sys.Errno) {
|
||||
return -1, sys.ENOSYS
|
||||
}
|
||||
|
||||
func readSocket(h uintptr, buf []byte) (int, sys.Errno) {
|
||||
// Poll the socket to ensure that we never perform a blocking/overlapped Read.
|
||||
//
|
||||
// When the socket is closed by the remote peer, wsaPoll will return n=1 and
|
||||
// errno=0, and syscall.ReadFile will return n=0 and errno=0 -- which indicates
|
||||
// io.EOF.
|
||||
if n, errno := wsaPoll(
|
||||
[]pollFd{newPollFd(h, _POLLIN, 0)}, 0); !errors.Is(errno, sys.Errno(0)) {
|
||||
return 0, sys.UnwrapOSError(errno)
|
||||
} else if n <= 0 {
|
||||
return 0, sys.EAGAIN
|
||||
}
|
||||
|
||||
// Properly use overlapped result.
|
||||
//
|
||||
// If hFile was opened with FILE_FLAG_OVERLAPPED, the following conditions are in effect:
|
||||
// - The lpOverlapped parameter must point to a valid and unique OVERLAPPED structure,
|
||||
// otherwise the function can incorrectly report that the read operation is complete.
|
||||
// - The lpNumberOfBytesRead parameter should be set to NULL. Use the GetOverlappedResult
|
||||
// function to get the actual number of bytes read. If the hFile parameter is associated
|
||||
// with an I/O completion port, you can also get the number of bytes read by calling the
|
||||
// GetQueuedCompletionStatus function.
|
||||
//
|
||||
// We are currently skipping checking if hFile was opened with FILE_FLAG_OVERLAPPED but using
|
||||
// both lpOverlapped and lpNumberOfBytesRead.
|
||||
var overlapped syscall.Overlapped
|
||||
|
||||
// Create an event to wait on.
|
||||
if hEvent, err := createEventW(nil, true, false, nil); err != 0 {
|
||||
return 0, sys.UnwrapOSError(err)
|
||||
} else {
|
||||
overlapped.HEvent = syscall.Handle(hEvent)
|
||||
}
|
||||
|
||||
var done uint32
|
||||
errno := syscall.ReadFile(syscall.Handle(h), buf, &done, &overlapped)
|
||||
if errors.Is(errno, syscall.ERROR_IO_PENDING) {
|
||||
errno = syscall.CancelIo(syscall.Handle(h))
|
||||
if errno != nil {
|
||||
return 0, sys.UnwrapOSError(errno) // This is a fatal error. CancelIo failed.
|
||||
}
|
||||
|
||||
done, errno = getOverlappedResult(syscall.Handle(h), &overlapped, true) // wait for I/O to complete(cancel or finish). Overwrite done and errno.
|
||||
if errors.Is(errno, syscall.ERROR_OPERATION_ABORTED) {
|
||||
return int(done), sys.EAGAIN // This is one of the expected behavior, I/O was cancelled(completed) before finished.
|
||||
}
|
||||
}
|
||||
|
||||
return int(done), sys.UnwrapOSError(errno)
|
||||
}
|
||||
|
||||
func writeSocket(fd uintptr, buf []byte) (int, sys.Errno) {
|
||||
var done uint32
|
||||
var overlapped syscall.Overlapped
|
||||
errno := syscall.WriteFile(syscall.Handle(fd), buf, &done, &overlapped)
|
||||
if errors.Is(errno, syscall.ERROR_IO_PENDING) {
|
||||
errno = syscall.EAGAIN
|
||||
}
|
||||
return int(done), sys.UnwrapOSError(errno)
|
||||
}
|
||||
|
||||
// peekNamedPipe partially exposes PeekNamedPipe from the Win32 API
|
||||
// see https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-peeknamedpipe
|
||||
func peekNamedPipe(handle syscall.Handle) (uint32, syscall.Errno) {
|
||||
var totalBytesAvail uint32
|
||||
totalBytesPtr := unsafe.Pointer(&totalBytesAvail)
|
||||
_, _, errno := syscall.SyscallN(
|
||||
procPeekNamedPipe.Addr(),
|
||||
uintptr(handle), // [in] HANDLE hNamedPipe,
|
||||
0, // [out, optional] LPVOID lpBuffer,
|
||||
0, // [in] DWORD nBufferSize,
|
||||
0, // [out, optional] LPDWORD lpBytesRead
|
||||
uintptr(totalBytesPtr), // [out, optional] LPDWORD lpTotalBytesAvail,
|
||||
0) // [out, optional] LPDWORD lpBytesLeftThisMessage
|
||||
return totalBytesAvail, errno
|
||||
}
|
||||
|
||||
func rmdir(path string) sys.Errno {
|
||||
err := syscall.Rmdir(path)
|
||||
return sys.UnwrapOSError(err)
|
||||
}
|
||||
|
||||
func getOverlappedResult(handle syscall.Handle, overlapped *syscall.Overlapped, wait bool) (uint32, syscall.Errno) {
|
||||
var totalBytesAvail uint32
|
||||
var bwait uintptr
|
||||
if wait {
|
||||
bwait = 0xFFFFFFFF
|
||||
}
|
||||
totalBytesPtr := unsafe.Pointer(&totalBytesAvail)
|
||||
_, _, errno := syscall.SyscallN(
|
||||
procGetOverlappedResult.Addr(),
|
||||
uintptr(handle), // [in] HANDLE hFile,
|
||||
uintptr(unsafe.Pointer(overlapped)), // [in] LPOVERLAPPED lpOverlapped,
|
||||
uintptr(totalBytesPtr), // [out] LPDWORD lpNumberOfBytesTransferred,
|
||||
bwait) // [in] BOOL bWait
|
||||
return totalBytesAvail, errno
|
||||
}
|
||||
|
||||
func createEventW(lpEventAttributes *syscall.SecurityAttributes, bManualReset bool, bInitialState bool, lpName *uint16) (uintptr, syscall.Errno) {
|
||||
var manualReset uintptr
|
||||
var initialState uintptr
|
||||
if bManualReset {
|
||||
manualReset = 1
|
||||
}
|
||||
if bInitialState {
|
||||
initialState = 1
|
||||
}
|
||||
handle, _, errno := syscall.SyscallN(
|
||||
procCreateEventW.Addr(),
|
||||
uintptr(unsafe.Pointer(lpEventAttributes)), // [in] LPSECURITY_ATTRIBUTES lpEventAttributes,
|
||||
manualReset, // [in] BOOL bManualReset,
|
||||
initialState, // [in] BOOL bInitialState,
|
||||
uintptr(unsafe.Pointer(lpName)), // [in, opt]LPCWSTR lpName,
|
||||
)
|
||||
|
||||
return handle, errno
|
||||
}
|
37
vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens.go
generated
vendored
Normal file
37
vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens.go
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
//go:build (linux || darwin) && !tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
func timesToPtr(times *[2]syscall.Timespec) unsafe.Pointer { //nolint:unused
|
||||
if times != nil {
|
||||
return unsafe.Pointer(×[0])
|
||||
}
|
||||
return unsafe.Pointer(nil)
|
||||
}
|
||||
|
||||
func timesToTimespecs(atim int64, mtim int64) (times *[2]syscall.Timespec) {
|
||||
// When both inputs are omitted, there is nothing to change.
|
||||
if atim == sys.UTIME_OMIT && mtim == sys.UTIME_OMIT {
|
||||
return
|
||||
}
|
||||
|
||||
times = &[2]syscall.Timespec{}
|
||||
if atim == sys.UTIME_OMIT {
|
||||
times[0] = syscall.Timespec{Nsec: _UTIME_OMIT}
|
||||
times[1] = syscall.NsecToTimespec(mtim)
|
||||
} else if mtim == sys.UTIME_OMIT {
|
||||
times[0] = syscall.NsecToTimespec(atim)
|
||||
times[1] = syscall.Timespec{Nsec: _UTIME_OMIT}
|
||||
} else {
|
||||
times[0] = syscall.NsecToTimespec(atim)
|
||||
times[1] = syscall.NsecToTimespec(mtim)
|
||||
}
|
||||
return
|
||||
}
|
51
vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_darwin.go
generated
vendored
Normal file
51
vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_darwin.go
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
_ "unsafe"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
const (
|
||||
_AT_FDCWD = -0x2
|
||||
_AT_SYMLINK_NOFOLLOW = 0x0020
|
||||
_UTIME_OMIT = -2
|
||||
)
|
||||
|
||||
//go:noescape
|
||||
//go:linkname utimensat syscall.utimensat
|
||||
func utimensat(dirfd int, path string, times *[2]syscall.Timespec, flags int) error
|
||||
|
||||
func utimens(path string, atim, mtim int64) experimentalsys.Errno {
|
||||
times := timesToTimespecs(atim, mtim)
|
||||
if times == nil {
|
||||
return 0
|
||||
}
|
||||
var flags int
|
||||
return experimentalsys.UnwrapOSError(utimensat(_AT_FDCWD, path, times, flags))
|
||||
}
|
||||
|
||||
func futimens(fd uintptr, atim, mtim int64) experimentalsys.Errno {
|
||||
times := timesToTimespecs(atim, mtim)
|
||||
if times == nil {
|
||||
return 0
|
||||
}
|
||||
_p0 := timesToPtr(times)
|
||||
|
||||
// Warning: futimens only exists since High Sierra (10.13).
|
||||
_, _, e1 := syscall_syscall6(libc_futimens_trampoline_addr, fd, uintptr(_p0), 0, 0, 0, 0)
|
||||
return experimentalsys.UnwrapOSError(e1)
|
||||
}
|
||||
|
||||
// libc_futimens_trampoline_addr is the address of the
|
||||
// `libc_futimens_trampoline` symbol, defined in `futimens_darwin.s`.
|
||||
//
|
||||
// We use this to invoke the syscall through syscall_syscall6 imported below.
|
||||
var libc_futimens_trampoline_addr uintptr
|
||||
|
||||
// Imports the futimens symbol from libc as `libc_futimens`.
|
||||
//
|
||||
// Note: CGO mechanisms are used in darwin regardless of the CGO_ENABLED value
|
||||
// or the "cgo" build flag. See /RATIONALE.md for why.
|
||||
//go:cgo_import_dynamic libc_futimens futimens "/usr/lib/libSystem.B.dylib"
|
8
vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_darwin.s
generated
vendored
Normal file
8
vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_darwin.s
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
// lifted from golang.org/x/sys unix
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT libc_futimens_trampoline<>(SB), NOSPLIT, $0-0
|
||||
JMP libc_futimens(SB)
|
||||
|
||||
GLOBL ·libc_futimens_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_futimens_trampoline_addr(SB)/8, $libc_futimens_trampoline<>(SB)
|
49
vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_linux.go
generated
vendored
Normal file
49
vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_linux.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
//go:build !tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
_ "unsafe"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
const (
|
||||
_AT_FDCWD = -0x64
|
||||
_UTIME_OMIT = (1 << 30) - 2
|
||||
)
|
||||
|
||||
func utimens(path string, atim, mtim int64) experimentalsys.Errno {
|
||||
times := timesToTimespecs(atim, mtim)
|
||||
if times == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
var flags int
|
||||
var _p0 *byte
|
||||
_p0, err := syscall.BytePtrFromString(path)
|
||||
if err == nil {
|
||||
err = utimensat(_AT_FDCWD, uintptr(unsafe.Pointer(_p0)), times, flags)
|
||||
}
|
||||
return experimentalsys.UnwrapOSError(err)
|
||||
}
|
||||
|
||||
// On linux, implement futimens via utimensat with the NUL path.
|
||||
func futimens(fd uintptr, atim, mtim int64) experimentalsys.Errno {
|
||||
times := timesToTimespecs(atim, mtim)
|
||||
if times == nil {
|
||||
return 0
|
||||
}
|
||||
return experimentalsys.UnwrapOSError(utimensat(int(fd), 0 /* NUL */, times, 0))
|
||||
}
|
||||
|
||||
// utimensat is like syscall.utimensat special-cased to accept a NUL string for the path value.
|
||||
func utimensat(dirfd int, strPtr uintptr, times *[2]syscall.Timespec, flags int) (err error) {
|
||||
_, _, e1 := syscall.Syscall6(syscall.SYS_UTIMENSAT, uintptr(dirfd), strPtr, uintptr(unsafe.Pointer(times)), uintptr(flags), 0, 0)
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
18
vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_unsupported.go
generated
vendored
Normal file
18
vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
//go:build (!windows && !linux && !darwin) || tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
func utimens(path string, atim, mtim int64) sys.Errno {
|
||||
return chtimes(path, atim, mtim)
|
||||
}
|
||||
|
||||
func futimens(fd uintptr, atim, mtim int64) error {
|
||||
// Go exports syscall.Futimes, which is microsecond granularity, and
|
||||
// WASI tests expect nanosecond. We don't yet have a way to invoke the
|
||||
// futimens syscall portably.
|
||||
return sys.ENOSYS
|
||||
}
|
42
vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_windows.go
generated
vendored
Normal file
42
vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_windows.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
func utimens(path string, atim, mtim int64) sys.Errno {
|
||||
return chtimes(path, atim, mtim)
|
||||
}
|
||||
|
||||
func futimens(fd uintptr, atim, mtim int64) error {
|
||||
// Per docs, zero isn't a valid timestamp as it cannot be differentiated
|
||||
// from nil. In both cases, it is a marker like sys.UTIME_OMIT.
|
||||
// See https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfiletime
|
||||
a, w := timespecToFiletime(atim, mtim)
|
||||
|
||||
if a == nil && w == nil {
|
||||
return nil // both omitted, so nothing to change
|
||||
}
|
||||
|
||||
// Attempt to get the stat by handle, which works for normal files
|
||||
h := syscall.Handle(fd)
|
||||
|
||||
// Note: This returns ERROR_ACCESS_DENIED when the input is a directory.
|
||||
return syscall.SetFileTime(h, nil, a, w)
|
||||
}
|
||||
|
||||
func timespecToFiletime(atim, mtim int64) (a, w *syscall.Filetime) {
|
||||
a = timespecToFileTime(atim)
|
||||
w = timespecToFileTime(mtim)
|
||||
return
|
||||
}
|
||||
|
||||
func timespecToFileTime(tim int64) *syscall.Filetime {
|
||||
if tim == sys.UTIME_OMIT {
|
||||
return nil
|
||||
}
|
||||
ft := syscall.NsecToFiletime(tim)
|
||||
return &ft
|
||||
}
|
22
vendor/github.com/tetratelabs/wazero/internal/sysfs/ino.go
generated
vendored
Normal file
22
vendor/github.com/tetratelabs/wazero/internal/sysfs/ino.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
//go:build !windows && !plan9 && !tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"syscall"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, experimentalsys.Errno) {
|
||||
switch v := info.Sys().(type) {
|
||||
case *sys.Stat_t:
|
||||
return v.Ino, 0
|
||||
case *syscall.Stat_t:
|
||||
return v.Ino, 0
|
||||
default:
|
||||
return 0, 0
|
||||
}
|
||||
}
|
15
vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_plan9.go
generated
vendored
Normal file
15
vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_plan9.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, experimentalsys.Errno) {
|
||||
if v, ok := info.Sys().(*sys.Stat_t); ok {
|
||||
return v.Ino, 0
|
||||
}
|
||||
return 0, 0
|
||||
}
|
14
vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_tinygo.go
generated
vendored
Normal file
14
vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_tinygo.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
//go:build tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, experimentalsys.Errno) {
|
||||
return 0, experimentalsys.ENOTSUP
|
||||
}
|
28
vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_windows.go
generated
vendored
Normal file
28
vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_windows.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"path"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
// inoFromFileInfo uses stat to get the inode information of the file.
|
||||
func inoFromFileInfo(dirPath string, info fs.FileInfo) (ino sys.Inode, errno experimentalsys.Errno) {
|
||||
if v, ok := info.Sys().(*sys.Stat_t); ok {
|
||||
return v.Ino, 0
|
||||
}
|
||||
if dirPath == "" {
|
||||
// This is a FS.File backed implementation which doesn't have access to
|
||||
// the original file path.
|
||||
return
|
||||
}
|
||||
// Ino is no not in Win32FileAttributeData
|
||||
inoPath := path.Clean(path.Join(dirPath, info.Name()))
|
||||
var st sys.Stat_t
|
||||
if st, errno = lstat(inoPath); errno == 0 {
|
||||
ino = st.Ino
|
||||
}
|
||||
return
|
||||
}
|
17
vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_unix.go
generated
vendored
Normal file
17
vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_unix.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
//go:build !windows && !plan9 && !tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
func setNonblock(fd uintptr, enable bool) sys.Errno {
|
||||
return sys.UnwrapOSError(syscall.SetNonblock(int(fd), enable))
|
||||
}
|
||||
|
||||
func isNonblock(f *osFile) bool {
|
||||
return f.flag&sys.O_NONBLOCK == sys.O_NONBLOCK
|
||||
}
|
13
vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_unsupported.go
generated
vendored
Normal file
13
vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
//go:build plan9 || tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import "github.com/tetratelabs/wazero/experimental/sys"
|
||||
|
||||
func setNonblock(fd uintptr, enable bool) sys.Errno {
|
||||
return sys.ENOSYS
|
||||
}
|
||||
|
||||
func isNonblock(f *osFile) bool {
|
||||
return false
|
||||
}
|
23
vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_windows.go
generated
vendored
Normal file
23
vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_windows.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
func setNonblock(fd uintptr, enable bool) sys.Errno {
|
||||
// We invoke the syscall, but this is currently no-op.
|
||||
return sys.UnwrapOSError(syscall.SetNonblock(syscall.Handle(fd), enable))
|
||||
}
|
||||
|
||||
func isNonblock(f *osFile) bool {
|
||||
// On Windows, we support non-blocking reads only on named pipes.
|
||||
isValid := false
|
||||
st, errno := f.Stat()
|
||||
if errno == 0 {
|
||||
isValid = st.Mode&fs.ModeNamedPipe != 0
|
||||
}
|
||||
return isValid && f.flag&sys.O_NONBLOCK == sys.O_NONBLOCK
|
||||
}
|
38
vendor/github.com/tetratelabs/wazero/internal/sysfs/oflag.go
generated
vendored
Normal file
38
vendor/github.com/tetratelabs/wazero/internal/sysfs/oflag.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
// toOsOpenFlag converts the input to the flag parameter of os.OpenFile
|
||||
func toOsOpenFlag(oflag sys.Oflag) (flag int) {
|
||||
// First flags are exclusive
|
||||
switch oflag & (sys.O_RDONLY | sys.O_RDWR | sys.O_WRONLY) {
|
||||
case sys.O_RDONLY:
|
||||
flag |= os.O_RDONLY
|
||||
case sys.O_RDWR:
|
||||
flag |= os.O_RDWR
|
||||
case sys.O_WRONLY:
|
||||
flag |= os.O_WRONLY
|
||||
}
|
||||
|
||||
// Run down the flags defined in the os package
|
||||
if oflag&sys.O_APPEND != 0 {
|
||||
flag |= os.O_APPEND
|
||||
}
|
||||
if oflag&sys.O_CREAT != 0 {
|
||||
flag |= os.O_CREATE
|
||||
}
|
||||
if oflag&sys.O_EXCL != 0 {
|
||||
flag |= os.O_EXCL
|
||||
}
|
||||
if oflag&sys.O_SYNC != 0 {
|
||||
flag |= os.O_SYNC
|
||||
}
|
||||
if oflag&sys.O_TRUNC != 0 {
|
||||
flag |= os.O_TRUNC
|
||||
}
|
||||
return withSyscallOflag(oflag, flag)
|
||||
}
|
26
vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_darwin.go
generated
vendored
Normal file
26
vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_darwin.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
const supportedSyscallOflag = sys.O_DIRECTORY | sys.O_DSYNC | sys.O_NOFOLLOW | sys.O_NONBLOCK
|
||||
|
||||
func withSyscallOflag(oflag sys.Oflag, flag int) int {
|
||||
if oflag&sys.O_DIRECTORY != 0 {
|
||||
flag |= syscall.O_DIRECTORY
|
||||
}
|
||||
if oflag&sys.O_DSYNC != 0 {
|
||||
flag |= syscall.O_DSYNC
|
||||
}
|
||||
if oflag&sys.O_NOFOLLOW != 0 {
|
||||
flag |= syscall.O_NOFOLLOW
|
||||
}
|
||||
if oflag&sys.O_NONBLOCK != 0 {
|
||||
flag |= syscall.O_NONBLOCK
|
||||
}
|
||||
// syscall.O_RSYNC not defined on darwin
|
||||
return flag
|
||||
}
|
24
vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_freebsd.go
generated
vendored
Normal file
24
vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_freebsd.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
const supportedSyscallOflag = sys.O_DIRECTORY | sys.O_NOFOLLOW | sys.O_NONBLOCK
|
||||
|
||||
func withSyscallOflag(oflag sys.Oflag, flag int) int {
|
||||
if oflag&sys.O_DIRECTORY != 0 {
|
||||
flag |= syscall.O_DIRECTORY
|
||||
}
|
||||
// syscall.O_DSYNC not defined on darwin
|
||||
if oflag&sys.O_NOFOLLOW != 0 {
|
||||
flag |= syscall.O_NOFOLLOW
|
||||
}
|
||||
if oflag&sys.O_NONBLOCK != 0 {
|
||||
flag |= syscall.O_NONBLOCK
|
||||
}
|
||||
// syscall.O_RSYNC not defined on darwin
|
||||
return flag
|
||||
}
|
30
vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_linux.go
generated
vendored
Normal file
30
vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_linux.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
//go:build !tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
const supportedSyscallOflag = sys.O_DIRECTORY | sys.O_DSYNC | sys.O_NOFOLLOW | sys.O_NONBLOCK | sys.O_RSYNC
|
||||
|
||||
func withSyscallOflag(oflag sys.Oflag, flag int) int {
|
||||
if oflag&sys.O_DIRECTORY != 0 {
|
||||
flag |= syscall.O_DIRECTORY
|
||||
}
|
||||
if oflag&sys.O_DSYNC != 0 {
|
||||
flag |= syscall.O_DSYNC
|
||||
}
|
||||
if oflag&sys.O_NOFOLLOW != 0 {
|
||||
flag |= syscall.O_NOFOLLOW
|
||||
}
|
||||
if oflag&sys.O_NONBLOCK != 0 {
|
||||
flag |= syscall.O_NONBLOCK
|
||||
}
|
||||
if oflag&sys.O_RSYNC != 0 {
|
||||
flag |= syscall.O_RSYNC
|
||||
}
|
||||
return flag
|
||||
}
|
20
vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_notwindows.go
generated
vendored
Normal file
20
vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_notwindows.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
//go:build !windows && !tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
// openFile is like os.OpenFile except it accepts a sys.Oflag and returns
|
||||
// sys.Errno. A zero sys.Errno is success.
|
||||
func openFile(path string, oflag sys.Oflag, perm fs.FileMode) (*os.File, sys.Errno) {
|
||||
f, err := os.OpenFile(path, toOsOpenFlag(oflag), perm)
|
||||
// Note: This does not return a sys.File because sys.FS that returns
|
||||
// one may want to hide the real OS path. For example, this is needed for
|
||||
// pre-opens.
|
||||
return f, sys.UnwrapOSError(err)
|
||||
}
|
31
vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_sun.go
generated
vendored
Normal file
31
vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_sun.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
//go:build illumos || solaris
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
const supportedSyscallOflag = sys.O_DIRECTORY | sys.O_DSYNC | sys.O_NOFOLLOW | sys.O_NONBLOCK | sys.O_RSYNC
|
||||
|
||||
func withSyscallOflag(oflag sys.Oflag, flag int) int {
|
||||
if oflag&sys.O_DIRECTORY != 0 {
|
||||
// See https://github.com/illumos/illumos-gate/blob/edd580643f2cf1434e252cd7779e83182ea84945/usr/src/uts/common/sys/fcntl.h#L90
|
||||
flag |= 0x1000000
|
||||
}
|
||||
if oflag&sys.O_DSYNC != 0 {
|
||||
flag |= syscall.O_DSYNC
|
||||
}
|
||||
if oflag&sys.O_NOFOLLOW != 0 {
|
||||
flag |= syscall.O_NOFOLLOW
|
||||
}
|
||||
if oflag&sys.O_NONBLOCK != 0 {
|
||||
flag |= syscall.O_NONBLOCK
|
||||
}
|
||||
if oflag&sys.O_RSYNC != 0 {
|
||||
flag |= syscall.O_RSYNC
|
||||
}
|
||||
return flag
|
||||
}
|
25
vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_tinygo.go
generated
vendored
Normal file
25
vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_tinygo.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
//go:build tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
const supportedSyscallOflag = sys.Oflag(0)
|
||||
|
||||
func withSyscallOflag(oflag sys.Oflag, flag int) int {
|
||||
// O_DIRECTORY not defined
|
||||
// O_DSYNC not defined
|
||||
// O_NOFOLLOW not defined
|
||||
// O_NONBLOCK not defined
|
||||
// O_RSYNC not defined
|
||||
return flag
|
||||
}
|
||||
|
||||
func openFile(path string, oflag sys.Oflag, perm fs.FileMode) (*os.File, sys.Errno) {
|
||||
return nil, sys.ENOSYS
|
||||
}
|
18
vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_unsupported.go
generated
vendored
Normal file
18
vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
//go:build !darwin && !linux && !windows && !illumos && !solaris && !freebsd
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
const supportedSyscallOflag = sys.Oflag(0)
|
||||
|
||||
func withSyscallOflag(oflag sys.Oflag, flag int) int {
|
||||
// O_DIRECTORY not defined
|
||||
// O_DSYNC not defined
|
||||
// O_NOFOLLOW not defined
|
||||
// O_NONBLOCK not defined
|
||||
// O_RSYNC not defined
|
||||
return flag
|
||||
}
|
161
vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_windows.go
generated
vendored
Normal file
161
vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_windows.go
generated
vendored
Normal file
@ -0,0 +1,161 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
func openFile(path string, oflag sys.Oflag, perm fs.FileMode) (*os.File, sys.Errno) {
|
||||
isDir := oflag&sys.O_DIRECTORY > 0
|
||||
flag := toOsOpenFlag(oflag)
|
||||
|
||||
// TODO: document why we are opening twice
|
||||
fd, err := open(path, flag|syscall.O_CLOEXEC, uint32(perm))
|
||||
if err == nil {
|
||||
return os.NewFile(uintptr(fd), path), 0
|
||||
}
|
||||
|
||||
// TODO: Set FILE_SHARE_DELETE for directory as well.
|
||||
f, err := os.OpenFile(path, flag, perm)
|
||||
errno := sys.UnwrapOSError(err)
|
||||
if errno == 0 {
|
||||
return f, 0
|
||||
}
|
||||
|
||||
switch errno {
|
||||
case sys.EINVAL:
|
||||
// WASI expects ENOTDIR for a file path with a trailing slash.
|
||||
if strings.HasSuffix(path, "/") {
|
||||
errno = sys.ENOTDIR
|
||||
}
|
||||
// To match expectations of WASI, e.g. TinyGo TestStatBadDir, return
|
||||
// ENOENT, not ENOTDIR.
|
||||
case sys.ENOTDIR:
|
||||
errno = sys.ENOENT
|
||||
case sys.ENOENT:
|
||||
if isSymlink(path) {
|
||||
// Either symlink or hard link not found. We change the returned
|
||||
// errno depending on if it is symlink or not to have consistent
|
||||
// behavior across OSes.
|
||||
if isDir {
|
||||
// Dangling symlink dir must raise ENOTDIR.
|
||||
errno = sys.ENOTDIR
|
||||
} else {
|
||||
errno = sys.ELOOP
|
||||
}
|
||||
}
|
||||
}
|
||||
return f, errno
|
||||
}
|
||||
|
||||
const supportedSyscallOflag = sys.O_NONBLOCK
|
||||
|
||||
// Map to synthetic values here https://github.com/golang/go/blob/go1.20/src/syscall/types_windows.go#L34-L48
|
||||
func withSyscallOflag(oflag sys.Oflag, flag int) int {
|
||||
// O_DIRECTORY not defined in windows
|
||||
// O_DSYNC not defined in windows
|
||||
// O_NOFOLLOW not defined in windows
|
||||
if oflag&sys.O_NONBLOCK != 0 {
|
||||
flag |= syscall.O_NONBLOCK
|
||||
}
|
||||
// O_RSYNC not defined in windows
|
||||
return flag
|
||||
}
|
||||
|
||||
func isSymlink(path string) bool {
|
||||
if st, e := os.Lstat(path); e == nil && st.Mode()&os.ModeSymlink != 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// # Differences from syscall.Open
|
||||
//
|
||||
// This code is based on syscall.Open from the below link with some differences
|
||||
// https://github.com/golang/go/blame/go1.20/src/syscall/syscall_windows.go#L308-L379
|
||||
//
|
||||
// - syscall.O_CREAT doesn't imply syscall.GENERIC_WRITE as that breaks
|
||||
// flag expectations in wasi.
|
||||
// - add support for setting FILE_SHARE_DELETE.
|
||||
func open(path string, mode int, perm uint32) (fd syscall.Handle, err error) {
|
||||
if len(path) == 0 {
|
||||
return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND
|
||||
}
|
||||
pathp, err := syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return syscall.InvalidHandle, err
|
||||
}
|
||||
var access uint32
|
||||
switch mode & (syscall.O_RDONLY | syscall.O_WRONLY | syscall.O_RDWR) {
|
||||
case syscall.O_RDONLY:
|
||||
access = syscall.GENERIC_READ
|
||||
case syscall.O_WRONLY:
|
||||
access = syscall.GENERIC_WRITE
|
||||
case syscall.O_RDWR:
|
||||
access = syscall.GENERIC_READ | syscall.GENERIC_WRITE
|
||||
}
|
||||
if mode&syscall.O_APPEND != 0 {
|
||||
access &^= syscall.GENERIC_WRITE
|
||||
access |= syscall.FILE_APPEND_DATA
|
||||
}
|
||||
sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE | syscall.FILE_SHARE_DELETE)
|
||||
var sa *syscall.SecurityAttributes
|
||||
if mode&syscall.O_CLOEXEC == 0 {
|
||||
var _sa syscall.SecurityAttributes
|
||||
_sa.Length = uint32(unsafe.Sizeof(sa))
|
||||
_sa.InheritHandle = 1
|
||||
sa = &_sa
|
||||
}
|
||||
var createmode uint32
|
||||
switch {
|
||||
case mode&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL):
|
||||
createmode = syscall.CREATE_NEW
|
||||
case mode&(syscall.O_CREAT|syscall.O_TRUNC) == (syscall.O_CREAT | syscall.O_TRUNC):
|
||||
createmode = syscall.CREATE_ALWAYS
|
||||
case mode&syscall.O_CREAT == syscall.O_CREAT:
|
||||
createmode = syscall.OPEN_ALWAYS
|
||||
case mode&syscall.O_TRUNC == syscall.O_TRUNC:
|
||||
createmode = syscall.TRUNCATE_EXISTING
|
||||
default:
|
||||
createmode = syscall.OPEN_EXISTING
|
||||
}
|
||||
var attrs uint32 = syscall.FILE_ATTRIBUTE_NORMAL
|
||||
if perm&syscall.S_IWRITE == 0 {
|
||||
attrs = syscall.FILE_ATTRIBUTE_READONLY
|
||||
if createmode == syscall.CREATE_ALWAYS {
|
||||
// We have been asked to create a read-only file.
|
||||
// If the file already exists, the semantics of
|
||||
// the Unix open system call is to preserve the
|
||||
// existing permissions. If we pass CREATE_ALWAYS
|
||||
// and FILE_ATTRIBUTE_READONLY to CreateFile,
|
||||
// and the file already exists, CreateFile will
|
||||
// change the file permissions.
|
||||
// Avoid that to preserve the Unix semantics.
|
||||
h, e := syscall.CreateFile(pathp, access, sharemode, sa, syscall.TRUNCATE_EXISTING, syscall.FILE_ATTRIBUTE_NORMAL, 0)
|
||||
switch e {
|
||||
case syscall.ERROR_FILE_NOT_FOUND, syscall.ERROR_PATH_NOT_FOUND:
|
||||
// File does not exist. These are the same
|
||||
// errors as Errno.Is checks for ErrNotExist.
|
||||
// Carry on to create the file.
|
||||
default:
|
||||
// Success or some different error.
|
||||
return h, e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This shouldn't be included before 1.20 to have consistent behavior.
|
||||
// https://github.com/golang/go/commit/0f0aa5d8a6a0253627d58b3aa083b24a1091933f
|
||||
if createmode == syscall.OPEN_EXISTING && access == syscall.GENERIC_READ {
|
||||
// Necessary for opening directory handles.
|
||||
attrs |= syscall.FILE_FLAG_BACKUP_SEMANTICS
|
||||
}
|
||||
|
||||
h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0)
|
||||
return h, e
|
||||
}
|
295
vendor/github.com/tetratelabs/wazero/internal/sysfs/osfile.go
generated
vendored
Normal file
295
vendor/github.com/tetratelabs/wazero/internal/sysfs/osfile.go
generated
vendored
Normal file
@ -0,0 +1,295 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/fsapi"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
func newOsFile(path string, flag experimentalsys.Oflag, perm fs.FileMode, f *os.File) fsapi.File {
|
||||
// Windows cannot read files written to a directory after it was opened.
|
||||
// This was noticed in #1087 in zig tests. Use a flag instead of a
|
||||
// different type.
|
||||
reopenDir := runtime.GOOS == "windows"
|
||||
return &osFile{path: path, flag: flag, perm: perm, reopenDir: reopenDir, file: f, fd: f.Fd()}
|
||||
}
|
||||
|
||||
// osFile is a file opened with this package, and uses os.File or syscalls to
|
||||
// implement api.File.
|
||||
type osFile struct {
|
||||
path string
|
||||
flag experimentalsys.Oflag
|
||||
perm fs.FileMode
|
||||
file *os.File
|
||||
fd uintptr
|
||||
|
||||
// reopenDir is true if reopen should be called before Readdir. This flag
|
||||
// is deferred until Readdir to prevent redundant rewinds. This could
|
||||
// happen if Seek(0) was called twice, or if in Windows, Seek(0) was called
|
||||
// before Readdir.
|
||||
reopenDir bool
|
||||
|
||||
// closed is true when closed was called. This ensures proper sys.EBADF
|
||||
closed bool
|
||||
|
||||
// cachedStat includes fields that won't change while a file is open.
|
||||
cachedSt *cachedStat
|
||||
}
|
||||
|
||||
// cachedStat returns the cacheable parts of sys.Stat_t or an error if they
|
||||
// couldn't be retrieved.
|
||||
func (f *osFile) cachedStat() (dev uint64, ino sys.Inode, isDir bool, errno experimentalsys.Errno) {
|
||||
if f.cachedSt == nil {
|
||||
if _, errno = f.Stat(); errno != 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
return f.cachedSt.dev, f.cachedSt.ino, f.cachedSt.isDir, 0
|
||||
}
|
||||
|
||||
// Dev implements the same method as documented on sys.File
|
||||
func (f *osFile) Dev() (uint64, experimentalsys.Errno) {
|
||||
dev, _, _, errno := f.cachedStat()
|
||||
return dev, errno
|
||||
}
|
||||
|
||||
// Ino implements the same method as documented on sys.File
|
||||
func (f *osFile) Ino() (sys.Inode, experimentalsys.Errno) {
|
||||
_, ino, _, errno := f.cachedStat()
|
||||
return ino, errno
|
||||
}
|
||||
|
||||
// IsDir implements the same method as documented on sys.File
|
||||
func (f *osFile) IsDir() (bool, experimentalsys.Errno) {
|
||||
_, _, isDir, errno := f.cachedStat()
|
||||
return isDir, errno
|
||||
}
|
||||
|
||||
// IsAppend implements File.IsAppend
|
||||
func (f *osFile) IsAppend() bool {
|
||||
return f.flag&experimentalsys.O_APPEND == experimentalsys.O_APPEND
|
||||
}
|
||||
|
||||
// SetAppend implements the same method as documented on sys.File
|
||||
func (f *osFile) SetAppend(enable bool) (errno experimentalsys.Errno) {
|
||||
if enable {
|
||||
f.flag |= experimentalsys.O_APPEND
|
||||
} else {
|
||||
f.flag &= ^experimentalsys.O_APPEND
|
||||
}
|
||||
|
||||
// Clear any create or trunc flag, as we are re-opening, not re-creating.
|
||||
f.flag &= ^(experimentalsys.O_CREAT | experimentalsys.O_TRUNC)
|
||||
|
||||
// appendMode (bool) cannot be changed later, so we have to re-open the
|
||||
// file. https://github.com/golang/go/blob/go1.20/src/os/file_unix.go#L60
|
||||
return fileError(f, f.closed, f.reopen())
|
||||
}
|
||||
|
||||
// compile-time check to ensure osFile.reopen implements reopenFile.
|
||||
var _ reopenFile = (*osFile)(nil).reopen
|
||||
|
||||
func (f *osFile) reopen() (errno experimentalsys.Errno) {
|
||||
// Clear any create flag, as we are re-opening, not re-creating.
|
||||
f.flag &= ^experimentalsys.O_CREAT
|
||||
|
||||
var (
|
||||
isDir bool
|
||||
offset int64
|
||||
err error
|
||||
)
|
||||
|
||||
isDir, errno = f.IsDir()
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
|
||||
if !isDir {
|
||||
offset, err = f.file.Seek(0, io.SeekCurrent)
|
||||
if err != nil {
|
||||
return experimentalsys.UnwrapOSError(err)
|
||||
}
|
||||
}
|
||||
|
||||
_ = f.close()
|
||||
f.file, errno = OpenFile(f.path, f.flag, f.perm)
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
|
||||
if !isDir {
|
||||
_, err = f.file.Seek(offset, io.SeekStart)
|
||||
if err != nil {
|
||||
return experimentalsys.UnwrapOSError(err)
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// IsNonblock implements the same method as documented on fsapi.File
|
||||
func (f *osFile) IsNonblock() bool {
|
||||
return isNonblock(f)
|
||||
}
|
||||
|
||||
// SetNonblock implements the same method as documented on fsapi.File
|
||||
func (f *osFile) SetNonblock(enable bool) (errno experimentalsys.Errno) {
|
||||
if enable {
|
||||
f.flag |= experimentalsys.O_NONBLOCK
|
||||
} else {
|
||||
f.flag &= ^experimentalsys.O_NONBLOCK
|
||||
}
|
||||
if errno = setNonblock(f.fd, enable); errno != 0 {
|
||||
return fileError(f, f.closed, errno)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Stat implements the same method as documented on sys.File
|
||||
func (f *osFile) Stat() (sys.Stat_t, experimentalsys.Errno) {
|
||||
if f.closed {
|
||||
return sys.Stat_t{}, experimentalsys.EBADF
|
||||
}
|
||||
|
||||
st, errno := statFile(f.file)
|
||||
switch errno {
|
||||
case 0:
|
||||
f.cachedSt = &cachedStat{dev: st.Dev, ino: st.Ino, isDir: st.Mode&fs.ModeDir == fs.ModeDir}
|
||||
case experimentalsys.EIO:
|
||||
errno = experimentalsys.EBADF
|
||||
}
|
||||
return st, errno
|
||||
}
|
||||
|
||||
// Read implements the same method as documented on sys.File
|
||||
func (f *osFile) Read(buf []byte) (n int, errno experimentalsys.Errno) {
|
||||
if len(buf) == 0 {
|
||||
return 0, 0 // Short-circuit 0-len reads.
|
||||
}
|
||||
if nonBlockingFileReadSupported && f.IsNonblock() {
|
||||
n, errno = readFd(f.fd, buf)
|
||||
} else {
|
||||
n, errno = read(f.file, buf)
|
||||
}
|
||||
if errno != 0 {
|
||||
// Defer validation overhead until we've already had an error.
|
||||
errno = fileError(f, f.closed, errno)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pread implements the same method as documented on sys.File
|
||||
func (f *osFile) Pread(buf []byte, off int64) (n int, errno experimentalsys.Errno) {
|
||||
if n, errno = pread(f.file, buf, off); errno != 0 {
|
||||
// Defer validation overhead until we've already had an error.
|
||||
errno = fileError(f, f.closed, errno)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Seek implements the same method as documented on sys.File
|
||||
func (f *osFile) Seek(offset int64, whence int) (newOffset int64, errno experimentalsys.Errno) {
|
||||
if newOffset, errno = seek(f.file, offset, whence); errno != 0 {
|
||||
// Defer validation overhead until we've already had an error.
|
||||
errno = fileError(f, f.closed, errno)
|
||||
|
||||
// If the error was trying to rewind a directory, re-open it. Notably,
|
||||
// seeking to zero on a directory doesn't work on Windows with Go 1.19.
|
||||
if errno == experimentalsys.EISDIR && offset == 0 && whence == io.SeekStart {
|
||||
errno = 0
|
||||
f.reopenDir = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Poll implements the same method as documented on fsapi.File
|
||||
func (f *osFile) Poll(flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno experimentalsys.Errno) {
|
||||
return poll(f.fd, flag, timeoutMillis)
|
||||
}
|
||||
|
||||
// Readdir implements File.Readdir. Notably, this uses "Readdir", not
|
||||
// "ReadDir", from os.File.
|
||||
func (f *osFile) Readdir(n int) (dirents []experimentalsys.Dirent, errno experimentalsys.Errno) {
|
||||
if f.reopenDir { // re-open the directory if needed.
|
||||
f.reopenDir = false
|
||||
if errno = adjustReaddirErr(f, f.closed, f.reopen()); errno != 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if dirents, errno = readdir(f.file, f.path, n); errno != 0 {
|
||||
errno = adjustReaddirErr(f, f.closed, errno)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Write implements the same method as documented on sys.File
|
||||
func (f *osFile) Write(buf []byte) (n int, errno experimentalsys.Errno) {
|
||||
if len(buf) == 0 {
|
||||
return 0, 0 // Short-circuit 0-len writes.
|
||||
}
|
||||
if nonBlockingFileWriteSupported && f.IsNonblock() {
|
||||
n, errno = writeFd(f.fd, buf)
|
||||
} else if n, errno = write(f.file, buf); errno != 0 {
|
||||
// Defer validation overhead until we've already had an error.
|
||||
errno = fileError(f, f.closed, errno)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pwrite implements the same method as documented on sys.File
|
||||
func (f *osFile) Pwrite(buf []byte, off int64) (n int, errno experimentalsys.Errno) {
|
||||
if n, errno = pwrite(f.file, buf, off); errno != 0 {
|
||||
// Defer validation overhead until we've already had an error.
|
||||
errno = fileError(f, f.closed, errno)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Truncate implements the same method as documented on sys.File
|
||||
func (f *osFile) Truncate(size int64) (errno experimentalsys.Errno) {
|
||||
if errno = experimentalsys.UnwrapOSError(f.file.Truncate(size)); errno != 0 {
|
||||
// Defer validation overhead until we've already had an error.
|
||||
errno = fileError(f, f.closed, errno)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Sync implements the same method as documented on sys.File
|
||||
func (f *osFile) Sync() experimentalsys.Errno {
|
||||
return fsync(f.file)
|
||||
}
|
||||
|
||||
// Datasync implements the same method as documented on sys.File
|
||||
func (f *osFile) Datasync() experimentalsys.Errno {
|
||||
return datasync(f.file)
|
||||
}
|
||||
|
||||
// Utimens implements the same method as documented on sys.File
|
||||
func (f *osFile) Utimens(atim, mtim int64) experimentalsys.Errno {
|
||||
if f.closed {
|
||||
return experimentalsys.EBADF
|
||||
}
|
||||
|
||||
err := futimens(f.fd, atim, mtim)
|
||||
return experimentalsys.UnwrapOSError(err)
|
||||
}
|
||||
|
||||
// Close implements the same method as documented on sys.File
|
||||
func (f *osFile) Close() experimentalsys.Errno {
|
||||
if f.closed {
|
||||
return 0
|
||||
}
|
||||
f.closed = true
|
||||
return f.close()
|
||||
}
|
||||
|
||||
func (f *osFile) close() experimentalsys.Errno {
|
||||
return experimentalsys.UnwrapOSError(f.file.Close())
|
||||
}
|
18
vendor/github.com/tetratelabs/wazero/internal/sysfs/poll.go
generated
vendored
Normal file
18
vendor/github.com/tetratelabs/wazero/internal/sysfs/poll.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
//go:build windows || (linux && !tinygo) || darwin
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/fsapi"
|
||||
)
|
||||
|
||||
// poll implements `Poll` as documented on sys.File via a file descriptor.
|
||||
func poll(fd uintptr, flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno sys.Errno) {
|
||||
if flag != fsapi.POLLIN {
|
||||
return false, sys.ENOTSUP
|
||||
}
|
||||
fds := []pollFd{newPollFd(fd, _POLLIN, 0)}
|
||||
count, errno := _poll(fds, timeoutMillis)
|
||||
return count > 0, errno
|
||||
}
|
55
vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_darwin.go
generated
vendored
Normal file
55
vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_darwin.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
// pollFd is the struct to query for file descriptor events using poll.
|
||||
type pollFd struct {
|
||||
// fd is the file descriptor.
|
||||
fd int32
|
||||
// events is a bitmap containing the requested events.
|
||||
events int16
|
||||
// revents is a bitmap containing the returned events.
|
||||
revents int16
|
||||
}
|
||||
|
||||
// newPollFd is a constructor for pollFd that abstracts the platform-specific type of file descriptors.
|
||||
func newPollFd(fd uintptr, events, revents int16) pollFd {
|
||||
return pollFd{fd: int32(fd), events: events, revents: revents}
|
||||
}
|
||||
|
||||
// _POLLIN subscribes a notification when any readable data is available.
|
||||
const _POLLIN = 0x0001
|
||||
|
||||
// _poll implements poll on Darwin via the corresponding libc function.
|
||||
func _poll(fds []pollFd, timeoutMillis int32) (n int, errno sys.Errno) {
|
||||
var fdptr *pollFd
|
||||
nfds := len(fds)
|
||||
if nfds > 0 {
|
||||
fdptr = &fds[0]
|
||||
}
|
||||
n1, _, err := syscall_syscall6(
|
||||
libc_poll_trampoline_addr,
|
||||
uintptr(unsafe.Pointer(fdptr)),
|
||||
uintptr(nfds),
|
||||
uintptr(int(timeoutMillis)),
|
||||
uintptr(unsafe.Pointer(nil)),
|
||||
uintptr(unsafe.Pointer(nil)),
|
||||
uintptr(unsafe.Pointer(nil)))
|
||||
return int(n1), sys.UnwrapOSError(err)
|
||||
}
|
||||
|
||||
// libc_poll_trampoline_addr is the address of the
|
||||
// `libc_poll_trampoline` symbol, defined in `poll_darwin.s`.
|
||||
//
|
||||
// We use this to invoke the syscall through syscall_syscall6 imported below.
|
||||
var libc_poll_trampoline_addr uintptr
|
||||
|
||||
// Imports the select symbol from libc as `libc_poll`.
|
||||
//
|
||||
// Note: CGO mechanisms are used in darwin regardless of the CGO_ENABLED value
|
||||
// or the "cgo" build flag. See /RATIONALE.md for why.
|
||||
//go:cgo_import_dynamic libc_poll poll "/usr/lib/libSystem.B.dylib"
|
8
vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_darwin.s
generated
vendored
Normal file
8
vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_darwin.s
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
// lifted from golang.org/x/sys unix
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT libc_poll_trampoline<>(SB), NOSPLIT, $0-0
|
||||
JMP libc_poll(SB)
|
||||
|
||||
GLOBL ·libc_poll_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_poll_trampoline_addr(SB)/8, $libc_poll_trampoline<>(SB)
|
59
vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_linux.go
generated
vendored
Normal file
59
vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_linux.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
//go:build !tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
// pollFd is the struct to query for file descriptor events using poll.
|
||||
type pollFd struct {
|
||||
// fd is the file descriptor.
|
||||
fd int32
|
||||
// events is a bitmap containing the requested events.
|
||||
events int16
|
||||
// revents is a bitmap containing the returned events.
|
||||
revents int16
|
||||
}
|
||||
|
||||
// newPollFd is a constructor for pollFd that abstracts the platform-specific type of file descriptors.
|
||||
func newPollFd(fd uintptr, events, revents int16) pollFd {
|
||||
return pollFd{fd: int32(fd), events: events, revents: revents}
|
||||
}
|
||||
|
||||
// _POLLIN subscribes a notification when any readable data is available.
|
||||
const _POLLIN = 0x0001
|
||||
|
||||
// _poll implements poll on Linux via ppoll.
|
||||
func _poll(fds []pollFd, timeoutMillis int32) (n int, errno sys.Errno) {
|
||||
var ts syscall.Timespec
|
||||
if timeoutMillis >= 0 {
|
||||
ts = syscall.NsecToTimespec(int64(time.Duration(timeoutMillis) * time.Millisecond))
|
||||
}
|
||||
return ppoll(fds, &ts)
|
||||
}
|
||||
|
||||
// ppoll is a poll variant that allows to subscribe to a mask of signals.
|
||||
// However, we do not need such mask, so the corresponding argument is always nil.
|
||||
func ppoll(fds []pollFd, timespec *syscall.Timespec) (n int, err sys.Errno) {
|
||||
var fdptr *pollFd
|
||||
nfd := len(fds)
|
||||
if nfd != 0 {
|
||||
fdptr = &fds[0]
|
||||
}
|
||||
|
||||
n1, _, errno := syscall.Syscall6(
|
||||
uintptr(syscall.SYS_PPOLL),
|
||||
uintptr(unsafe.Pointer(fdptr)),
|
||||
uintptr(nfd),
|
||||
uintptr(unsafe.Pointer(timespec)),
|
||||
uintptr(unsafe.Pointer(nil)), // sigmask is currently always ignored
|
||||
uintptr(unsafe.Pointer(nil)),
|
||||
uintptr(unsafe.Pointer(nil)))
|
||||
|
||||
return int(n1), sys.UnwrapOSError(errno)
|
||||
}
|
13
vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_unsupported.go
generated
vendored
Normal file
13
vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
//go:build !(linux || darwin || windows) || tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/fsapi"
|
||||
)
|
||||
|
||||
// poll implements `Poll` as documented on fsapi.File via a file descriptor.
|
||||
func poll(uintptr, fsapi.Pflag, int32) (bool, sys.Errno) {
|
||||
return false, sys.ENOSYS
|
||||
}
|
224
vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_windows.go
generated
vendored
Normal file
224
vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_windows.go
generated
vendored
Normal file
@ -0,0 +1,224 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
var (
|
||||
procWSAPoll = modws2_32.NewProc("WSAPoll")
|
||||
procGetNamedPipeInfo = kernel32.NewProc("GetNamedPipeInfo")
|
||||
)
|
||||
|
||||
const (
|
||||
// _POLLRDNORM subscribes to normal data for read.
|
||||
_POLLRDNORM = 0x0100
|
||||
// _POLLRDBAND subscribes to priority band (out-of-band) data for read.
|
||||
_POLLRDBAND = 0x0200
|
||||
// _POLLIN subscribes a notification when any readable data is available.
|
||||
_POLLIN = (_POLLRDNORM | _POLLRDBAND)
|
||||
)
|
||||
|
||||
// pollFd is the struct to query for file descriptor events using poll.
|
||||
type pollFd struct {
|
||||
// fd is the file descriptor.
|
||||
fd uintptr
|
||||
// events is a bitmap containing the requested events.
|
||||
events int16
|
||||
// revents is a bitmap containing the returned events.
|
||||
revents int16
|
||||
}
|
||||
|
||||
// newPollFd is a constructor for pollFd that abstracts the platform-specific type of file descriptors.
|
||||
func newPollFd(fd uintptr, events, revents int16) pollFd {
|
||||
return pollFd{fd: fd, events: events, revents: revents}
|
||||
}
|
||||
|
||||
// pollInterval is the interval between each calls to peekNamedPipe in selectAllHandles
|
||||
const pollInterval = 100 * time.Millisecond
|
||||
|
||||
// _poll implements poll on Windows, for a subset of cases.
|
||||
//
|
||||
// fds may contain any number of file handles, but regular files and pipes are only processed for _POLLIN.
|
||||
// Stdin is a pipe, thus it is checked for readiness when present. Pipes are checked using PeekNamedPipe.
|
||||
// Regular files always immediately reported as ready, regardless their actual state and timeouts.
|
||||
//
|
||||
// If n==0 it will wait for the given timeout duration, but it will return sys.ENOSYS if timeout is nil,
|
||||
// i.e. it won't block indefinitely. The given ctx is used to allow for cancellation,
|
||||
// and it is currently used only in tests.
|
||||
//
|
||||
// The implementation actually polls every 100 milliseconds (pollInterval) until it reaches the
|
||||
// given timeout (in millis).
|
||||
//
|
||||
// The duration may be negative, in which case it will wait indefinitely. The given ctx is
|
||||
// used to allow for cancellation, and it is currently used only in tests.
|
||||
func _poll(fds []pollFd, timeoutMillis int32) (n int, errno sys.Errno) {
|
||||
if fds == nil {
|
||||
return -1, sys.ENOSYS
|
||||
}
|
||||
|
||||
regular, pipes, sockets, errno := partionByFtype(fds)
|
||||
nregular := len(regular)
|
||||
if errno != 0 {
|
||||
return -1, errno
|
||||
}
|
||||
|
||||
// Ticker that emits at every pollInterval.
|
||||
tick := time.NewTicker(pollInterval)
|
||||
tickCh := tick.C
|
||||
defer tick.Stop()
|
||||
|
||||
// Timer that expires after the given duration.
|
||||
// Initialize afterCh as nil: the select below will wait forever.
|
||||
var afterCh <-chan time.Time
|
||||
if timeoutMillis >= 0 {
|
||||
// If duration is not nil, instantiate the timer.
|
||||
after := time.NewTimer(time.Duration(timeoutMillis) * time.Millisecond)
|
||||
defer after.Stop()
|
||||
afterCh = after.C
|
||||
}
|
||||
|
||||
npipes, nsockets, errno := peekAll(pipes, sockets)
|
||||
if errno != 0 {
|
||||
return -1, errno
|
||||
}
|
||||
count := nregular + npipes + nsockets
|
||||
if count > 0 {
|
||||
return count, 0
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-afterCh:
|
||||
return 0, 0
|
||||
case <-tickCh:
|
||||
npipes, nsockets, errno := peekAll(pipes, sockets)
|
||||
if errno != 0 {
|
||||
return -1, errno
|
||||
}
|
||||
count = nregular + npipes + nsockets
|
||||
if count > 0 {
|
||||
return count, 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func peekAll(pipes, sockets []pollFd) (npipes, nsockets int, errno sys.Errno) {
|
||||
npipes, errno = peekPipes(pipes)
|
||||
if errno != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Invoke wsaPoll with a 0-timeout to avoid blocking.
|
||||
// Timeouts are handled in pollWithContext instead.
|
||||
nsockets, errno = wsaPoll(sockets, 0)
|
||||
if errno != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
count := npipes + nsockets
|
||||
if count > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func peekPipes(fds []pollFd) (n int, errno sys.Errno) {
|
||||
for _, fd := range fds {
|
||||
bytes, errno := peekNamedPipe(syscall.Handle(fd.fd))
|
||||
if errno != 0 {
|
||||
return -1, sys.UnwrapOSError(errno)
|
||||
}
|
||||
if bytes > 0 {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// wsaPoll is the WSAPoll function from winsock2.
|
||||
//
|
||||
// See https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsapoll
|
||||
func wsaPoll(fds []pollFd, timeout int) (n int, errno sys.Errno) {
|
||||
if len(fds) > 0 {
|
||||
sockptr := &fds[0]
|
||||
ns, _, e := syscall.SyscallN(
|
||||
procWSAPoll.Addr(),
|
||||
uintptr(unsafe.Pointer(sockptr)),
|
||||
uintptr(len(fds)),
|
||||
uintptr(timeout))
|
||||
if e != 0 {
|
||||
return -1, sys.UnwrapOSError(e)
|
||||
}
|
||||
n = int(ns)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ftype is a type of file that can be handled by poll.
|
||||
type ftype uint8
|
||||
|
||||
const (
|
||||
ftype_regular ftype = iota
|
||||
ftype_pipe
|
||||
ftype_socket
|
||||
)
|
||||
|
||||
// partionByFtype checks the type of each fd in fds and returns 3 distinct partitions
|
||||
// for regular files, named pipes and sockets.
|
||||
func partionByFtype(fds []pollFd) (regular, pipe, socket []pollFd, errno sys.Errno) {
|
||||
for _, pfd := range fds {
|
||||
t, errno := ftypeOf(pfd.fd)
|
||||
if errno != 0 {
|
||||
return nil, nil, nil, errno
|
||||
}
|
||||
switch t {
|
||||
case ftype_regular:
|
||||
regular = append(regular, pfd)
|
||||
case ftype_pipe:
|
||||
pipe = append(pipe, pfd)
|
||||
case ftype_socket:
|
||||
socket = append(socket, pfd)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ftypeOf checks the type of fd and return the corresponding ftype.
|
||||
func ftypeOf(fd uintptr) (ftype, sys.Errno) {
|
||||
h := syscall.Handle(fd)
|
||||
t, err := syscall.GetFileType(h)
|
||||
if err != nil {
|
||||
return 0, sys.UnwrapOSError(err)
|
||||
}
|
||||
switch t {
|
||||
case syscall.FILE_TYPE_CHAR, syscall.FILE_TYPE_DISK:
|
||||
return ftype_regular, 0
|
||||
case syscall.FILE_TYPE_PIPE:
|
||||
if isSocket(h) {
|
||||
return ftype_socket, 0
|
||||
} else {
|
||||
return ftype_pipe, 0
|
||||
}
|
||||
default:
|
||||
return ftype_regular, 0
|
||||
}
|
||||
}
|
||||
|
||||
// isSocket returns true if the given file handle
|
||||
// is a pipe.
|
||||
func isSocket(fd syscall.Handle) bool {
|
||||
r, _, errno := syscall.SyscallN(
|
||||
procGetNamedPipeInfo.Addr(),
|
||||
uintptr(fd),
|
||||
uintptr(unsafe.Pointer(nil)),
|
||||
uintptr(unsafe.Pointer(nil)),
|
||||
uintptr(unsafe.Pointer(nil)),
|
||||
uintptr(unsafe.Pointer(nil)))
|
||||
return r == 0 || errno != 0
|
||||
}
|
117
vendor/github.com/tetratelabs/wazero/internal/sysfs/readfs.go
generated
vendored
Normal file
117
vendor/github.com/tetratelabs/wazero/internal/sysfs/readfs.go
generated
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
type ReadFS struct {
|
||||
experimentalsys.FS
|
||||
}
|
||||
|
||||
// OpenFile implements the same method as documented on sys.FS
|
||||
func (r *ReadFS) OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) {
|
||||
// Mask the mutually exclusive bits as they determine write mode.
|
||||
switch flag & (experimentalsys.O_RDONLY | experimentalsys.O_WRONLY | experimentalsys.O_RDWR) {
|
||||
case experimentalsys.O_WRONLY, experimentalsys.O_RDWR:
|
||||
// Return the correct error if a directory was opened for write.
|
||||
if flag&experimentalsys.O_DIRECTORY != 0 {
|
||||
return nil, experimentalsys.EISDIR
|
||||
}
|
||||
return nil, experimentalsys.ENOSYS
|
||||
default: // sys.O_RDONLY (integer zero) so we are ok!
|
||||
}
|
||||
|
||||
f, errno := r.FS.OpenFile(path, flag, perm)
|
||||
if errno != 0 {
|
||||
return nil, errno
|
||||
}
|
||||
return &readFile{f}, 0
|
||||
}
|
||||
|
||||
// Mkdir implements the same method as documented on sys.FS
|
||||
func (r *ReadFS) Mkdir(path string, perm fs.FileMode) experimentalsys.Errno {
|
||||
return experimentalsys.EROFS
|
||||
}
|
||||
|
||||
// Chmod implements the same method as documented on sys.FS
|
||||
func (r *ReadFS) Chmod(path string, perm fs.FileMode) experimentalsys.Errno {
|
||||
return experimentalsys.EROFS
|
||||
}
|
||||
|
||||
// Rename implements the same method as documented on sys.FS
|
||||
func (r *ReadFS) Rename(from, to string) experimentalsys.Errno {
|
||||
return experimentalsys.EROFS
|
||||
}
|
||||
|
||||
// Rmdir implements the same method as documented on sys.FS
|
||||
func (r *ReadFS) Rmdir(path string) experimentalsys.Errno {
|
||||
return experimentalsys.EROFS
|
||||
}
|
||||
|
||||
// Link implements the same method as documented on sys.FS
|
||||
func (r *ReadFS) Link(_, _ string) experimentalsys.Errno {
|
||||
return experimentalsys.EROFS
|
||||
}
|
||||
|
||||
// Symlink implements the same method as documented on sys.FS
|
||||
func (r *ReadFS) Symlink(_, _ string) experimentalsys.Errno {
|
||||
return experimentalsys.EROFS
|
||||
}
|
||||
|
||||
// Unlink implements the same method as documented on sys.FS
|
||||
func (r *ReadFS) Unlink(path string) experimentalsys.Errno {
|
||||
return experimentalsys.EROFS
|
||||
}
|
||||
|
||||
// Utimens implements the same method as documented on sys.FS
|
||||
func (r *ReadFS) Utimens(path string, atim, mtim int64) experimentalsys.Errno {
|
||||
return experimentalsys.EROFS
|
||||
}
|
||||
|
||||
// compile-time check to ensure readFile implements api.File.
|
||||
var _ experimentalsys.File = (*readFile)(nil)
|
||||
|
||||
type readFile struct {
|
||||
experimentalsys.File
|
||||
}
|
||||
|
||||
// Write implements the same method as documented on sys.File.
|
||||
func (r *readFile) Write([]byte) (int, experimentalsys.Errno) {
|
||||
return 0, r.writeErr()
|
||||
}
|
||||
|
||||
// Pwrite implements the same method as documented on sys.File.
|
||||
func (r *readFile) Pwrite([]byte, int64) (n int, errno experimentalsys.Errno) {
|
||||
return 0, r.writeErr()
|
||||
}
|
||||
|
||||
// Truncate implements the same method as documented on sys.File.
|
||||
func (r *readFile) Truncate(int64) experimentalsys.Errno {
|
||||
return r.writeErr()
|
||||
}
|
||||
|
||||
// Sync implements the same method as documented on sys.File.
|
||||
func (r *readFile) Sync() experimentalsys.Errno {
|
||||
return experimentalsys.EBADF
|
||||
}
|
||||
|
||||
// Datasync implements the same method as documented on sys.File.
|
||||
func (r *readFile) Datasync() experimentalsys.Errno {
|
||||
return experimentalsys.EBADF
|
||||
}
|
||||
|
||||
// Utimens implements the same method as documented on sys.File.
|
||||
func (r *readFile) Utimens(int64, int64) experimentalsys.Errno {
|
||||
return experimentalsys.EBADF
|
||||
}
|
||||
|
||||
func (r *readFile) writeErr() experimentalsys.Errno {
|
||||
if isDir, errno := r.IsDir(); errno != 0 {
|
||||
return errno
|
||||
} else if isDir {
|
||||
return experimentalsys.EISDIR
|
||||
}
|
||||
return experimentalsys.EBADF
|
||||
}
|
16
vendor/github.com/tetratelabs/wazero/internal/sysfs/rename.go
generated
vendored
Normal file
16
vendor/github.com/tetratelabs/wazero/internal/sysfs/rename.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
//go:build !windows && !plan9 && !tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
func rename(from, to string) sys.Errno {
|
||||
if from == to {
|
||||
return 0
|
||||
}
|
||||
return sys.UnwrapOSError(syscall.Rename(from, to))
|
||||
}
|
14
vendor/github.com/tetratelabs/wazero/internal/sysfs/rename_plan9.go
generated
vendored
Normal file
14
vendor/github.com/tetratelabs/wazero/internal/sysfs/rename_plan9.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
func rename(from, to string) sys.Errno {
|
||||
if from == to {
|
||||
return 0
|
||||
}
|
||||
return sys.UnwrapOSError(os.Rename(from, to))
|
||||
}
|
55
vendor/github.com/tetratelabs/wazero/internal/sysfs/rename_windows.go
generated
vendored
Normal file
55
vendor/github.com/tetratelabs/wazero/internal/sysfs/rename_windows.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
func rename(from, to string) sys.Errno {
|
||||
if from == to {
|
||||
return 0
|
||||
}
|
||||
|
||||
var fromIsDir, toIsDir bool
|
||||
if fromStat, errno := stat(from); errno != 0 {
|
||||
return errno // failed to stat from
|
||||
} else {
|
||||
fromIsDir = fromStat.Mode.IsDir()
|
||||
}
|
||||
if toStat, errno := stat(to); errno == sys.ENOENT {
|
||||
return syscallRename(from, to) // file or dir to not-exist is ok
|
||||
} else if errno != 0 {
|
||||
return errno // failed to stat to
|
||||
} else {
|
||||
toIsDir = toStat.Mode.IsDir()
|
||||
}
|
||||
|
||||
// Now, handle known cases
|
||||
switch {
|
||||
case !fromIsDir && toIsDir: // file to dir
|
||||
return sys.EISDIR
|
||||
case !fromIsDir && !toIsDir: // file to file
|
||||
// Use os.Rename instead of syscall.Rename to overwrite a file.
|
||||
// This uses MoveFileEx instead of MoveFile (used by syscall.Rename).
|
||||
return sys.UnwrapOSError(os.Rename(from, to))
|
||||
case fromIsDir && !toIsDir: // dir to file
|
||||
return sys.ENOTDIR
|
||||
default: // dir to dir
|
||||
|
||||
// We can't tell if a directory is empty or not, via stat information.
|
||||
// Reading the directory is expensive, as it can buffer large amounts
|
||||
// of data on fail. Instead, speculatively try to remove the directory.
|
||||
// This is only one syscall and won't buffer anything.
|
||||
if errno := rmdir(to); errno == 0 || errno == sys.ENOENT {
|
||||
return syscallRename(from, to)
|
||||
} else {
|
||||
return errno
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func syscallRename(from string, to string) sys.Errno {
|
||||
return sys.UnwrapOSError(syscall.Rename(from, to))
|
||||
}
|
187
vendor/github.com/tetratelabs/wazero/internal/sysfs/sock.go
generated
vendored
Normal file
187
vendor/github.com/tetratelabs/wazero/internal/sysfs/sock.go
generated
vendored
Normal file
@ -0,0 +1,187 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/fsapi"
|
||||
socketapi "github.com/tetratelabs/wazero/internal/sock"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
// NewTCPListenerFile creates a socketapi.TCPSock for a given *net.TCPListener.
|
||||
func NewTCPListenerFile(tl *net.TCPListener) socketapi.TCPSock {
|
||||
return newTCPListenerFile(tl)
|
||||
}
|
||||
|
||||
// baseSockFile implements base behavior for all TCPSock, TCPConn files,
|
||||
// regardless the platform.
|
||||
type baseSockFile struct {
|
||||
experimentalsys.UnimplementedFile
|
||||
}
|
||||
|
||||
var _ experimentalsys.File = (*baseSockFile)(nil)
|
||||
|
||||
// IsDir implements the same method as documented on File.IsDir
|
||||
func (*baseSockFile) IsDir() (bool, experimentalsys.Errno) {
|
||||
// We need to override this method because WASI-libc prestats the FD
|
||||
// and the default impl returns ENOSYS otherwise.
|
||||
return false, 0
|
||||
}
|
||||
|
||||
// Stat implements the same method as documented on File.Stat
|
||||
func (f *baseSockFile) Stat() (fs sys.Stat_t, errno experimentalsys.Errno) {
|
||||
// The mode is not really important, but it should be neither a regular file nor a directory.
|
||||
fs.Mode = os.ModeIrregular
|
||||
return
|
||||
}
|
||||
|
||||
var _ socketapi.TCPSock = (*tcpListenerFile)(nil)
|
||||
|
||||
type tcpListenerFile struct {
|
||||
baseSockFile
|
||||
|
||||
tl *net.TCPListener
|
||||
closed bool
|
||||
nonblock bool
|
||||
}
|
||||
|
||||
// newTCPListenerFile is a constructor for a socketapi.TCPSock.
|
||||
//
|
||||
// The current strategy is to wrap a net.TCPListener
|
||||
// and invoking raw syscalls using syscallConnControl:
|
||||
// this internal calls RawConn.Control(func(fd)), making sure
|
||||
// that the underlying file descriptor is valid throughout
|
||||
// the duration of the syscall.
|
||||
func newDefaultTCPListenerFile(tl *net.TCPListener) socketapi.TCPSock {
|
||||
return &tcpListenerFile{tl: tl}
|
||||
}
|
||||
|
||||
// Close implements the same method as documented on experimentalsys.File
|
||||
func (f *tcpListenerFile) Close() experimentalsys.Errno {
|
||||
if !f.closed {
|
||||
return experimentalsys.UnwrapOSError(f.tl.Close())
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Addr is exposed for testing.
|
||||
func (f *tcpListenerFile) Addr() *net.TCPAddr {
|
||||
return f.tl.Addr().(*net.TCPAddr)
|
||||
}
|
||||
|
||||
// IsNonblock implements the same method as documented on fsapi.File
|
||||
func (f *tcpListenerFile) IsNonblock() bool {
|
||||
return f.nonblock
|
||||
}
|
||||
|
||||
// Poll implements the same method as documented on fsapi.File
|
||||
func (f *tcpListenerFile) Poll(flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno experimentalsys.Errno) {
|
||||
return false, experimentalsys.ENOSYS
|
||||
}
|
||||
|
||||
var _ socketapi.TCPConn = (*tcpConnFile)(nil)
|
||||
|
||||
type tcpConnFile struct {
|
||||
baseSockFile
|
||||
|
||||
tc *net.TCPConn
|
||||
|
||||
// nonblock is true when the underlying connection is flagged as non-blocking.
|
||||
// This ensures that reads and writes return experimentalsys.EAGAIN without blocking the caller.
|
||||
nonblock bool
|
||||
// closed is true when closed was called. This ensures proper experimentalsys.EBADF
|
||||
closed bool
|
||||
}
|
||||
|
||||
func newTcpConn(tc *net.TCPConn) socketapi.TCPConn {
|
||||
return &tcpConnFile{tc: tc}
|
||||
}
|
||||
|
||||
// Read implements the same method as documented on experimentalsys.File
|
||||
func (f *tcpConnFile) Read(buf []byte) (n int, errno experimentalsys.Errno) {
|
||||
if len(buf) == 0 {
|
||||
return 0, 0 // Short-circuit 0-len reads.
|
||||
}
|
||||
if nonBlockingFileReadSupported && f.IsNonblock() {
|
||||
n, errno = syscallConnControl(f.tc, func(fd uintptr) (int, experimentalsys.Errno) {
|
||||
n, err := readSocket(fd, buf)
|
||||
errno = experimentalsys.UnwrapOSError(err)
|
||||
errno = fileError(f, f.closed, errno)
|
||||
return n, errno
|
||||
})
|
||||
} else {
|
||||
n, errno = read(f.tc, buf)
|
||||
}
|
||||
if errno != 0 {
|
||||
// Defer validation overhead until we've already had an error.
|
||||
errno = fileError(f, f.closed, errno)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Write implements the same method as documented on experimentalsys.File
|
||||
func (f *tcpConnFile) Write(buf []byte) (n int, errno experimentalsys.Errno) {
|
||||
if nonBlockingFileWriteSupported && f.IsNonblock() {
|
||||
return syscallConnControl(f.tc, func(fd uintptr) (int, experimentalsys.Errno) {
|
||||
n, err := writeSocket(fd, buf)
|
||||
errno = experimentalsys.UnwrapOSError(err)
|
||||
errno = fileError(f, f.closed, errno)
|
||||
return n, errno
|
||||
})
|
||||
} else {
|
||||
n, errno = write(f.tc, buf)
|
||||
}
|
||||
if errno != 0 {
|
||||
// Defer validation overhead until we've already had an error.
|
||||
errno = fileError(f, f.closed, errno)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Recvfrom implements the same method as documented on socketapi.TCPConn
|
||||
func (f *tcpConnFile) Recvfrom(p []byte, flags int) (n int, errno experimentalsys.Errno) {
|
||||
if flags != MSG_PEEK {
|
||||
errno = experimentalsys.EINVAL
|
||||
return
|
||||
}
|
||||
return syscallConnControl(f.tc, func(fd uintptr) (int, experimentalsys.Errno) {
|
||||
n, err := recvfrom(fd, p, MSG_PEEK)
|
||||
errno = experimentalsys.UnwrapOSError(err)
|
||||
errno = fileError(f, f.closed, errno)
|
||||
return n, errno
|
||||
})
|
||||
}
|
||||
|
||||
// Close implements the same method as documented on experimentalsys.File
|
||||
func (f *tcpConnFile) Close() experimentalsys.Errno {
|
||||
return f.close()
|
||||
}
|
||||
|
||||
func (f *tcpConnFile) close() experimentalsys.Errno {
|
||||
if f.closed {
|
||||
return 0
|
||||
}
|
||||
f.closed = true
|
||||
return f.Shutdown(socketapi.SHUT_RDWR)
|
||||
}
|
||||
|
||||
// SetNonblock implements the same method as documented on fsapi.File
|
||||
func (f *tcpConnFile) SetNonblock(enabled bool) (errno experimentalsys.Errno) {
|
||||
f.nonblock = enabled
|
||||
_, errno = syscallConnControl(f.tc, func(fd uintptr) (int, experimentalsys.Errno) {
|
||||
return 0, experimentalsys.UnwrapOSError(setNonblockSocket(fd, enabled))
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// IsNonblock implements the same method as documented on fsapi.File
|
||||
func (f *tcpConnFile) IsNonblock() bool {
|
||||
return f.nonblock
|
||||
}
|
||||
|
||||
// Poll implements the same method as documented on fsapi.File
|
||||
func (f *tcpConnFile) Poll(flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno experimentalsys.Errno) {
|
||||
return false, experimentalsys.ENOSYS
|
||||
}
|
77
vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_supported.go
generated
vendored
Normal file
77
vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_supported.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
//go:build (linux || darwin || windows) && !tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/fsapi"
|
||||
socketapi "github.com/tetratelabs/wazero/internal/sock"
|
||||
)
|
||||
|
||||
// Accept implements the same method as documented on socketapi.TCPSock
|
||||
func (f *tcpListenerFile) Accept() (socketapi.TCPConn, experimentalsys.Errno) {
|
||||
// Ensure we have an incoming connection, otherwise return immediately.
|
||||
if f.nonblock {
|
||||
if ready, errno := _pollSock(f.tl, fsapi.POLLIN, 0); !ready || errno != 0 {
|
||||
return nil, experimentalsys.EAGAIN
|
||||
}
|
||||
}
|
||||
|
||||
// Accept normally blocks goroutines, but we
|
||||
// made sure that we have an incoming connection,
|
||||
// so we should be safe.
|
||||
if conn, err := f.tl.Accept(); err != nil {
|
||||
return nil, experimentalsys.UnwrapOSError(err)
|
||||
} else {
|
||||
return newTcpConn(conn.(*net.TCPConn)), 0
|
||||
}
|
||||
}
|
||||
|
||||
// SetNonblock implements the same method as documented on fsapi.File
|
||||
func (f *tcpListenerFile) SetNonblock(enabled bool) (errno experimentalsys.Errno) {
|
||||
f.nonblock = enabled
|
||||
_, errno = syscallConnControl(f.tl, func(fd uintptr) (int, experimentalsys.Errno) {
|
||||
return 0, setNonblockSocket(fd, enabled)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Shutdown implements the same method as documented on experimentalsys.Conn
|
||||
func (f *tcpConnFile) Shutdown(how int) experimentalsys.Errno {
|
||||
// FIXME: can userland shutdown listeners?
|
||||
var err error
|
||||
switch how {
|
||||
case socketapi.SHUT_RD:
|
||||
err = f.tc.CloseRead()
|
||||
case socketapi.SHUT_WR:
|
||||
err = f.tc.CloseWrite()
|
||||
case socketapi.SHUT_RDWR:
|
||||
return f.close()
|
||||
default:
|
||||
return experimentalsys.EINVAL
|
||||
}
|
||||
return experimentalsys.UnwrapOSError(err)
|
||||
}
|
||||
|
||||
// syscallConnControl extracts a syscall.RawConn from the given syscall.Conn and applies
|
||||
// the given fn to a file descriptor, returning an integer or a nonzero syscall.Errno on failure.
|
||||
//
|
||||
// syscallConnControl streamlines the pattern of extracting the syscall.Rawconn,
|
||||
// invoking its syscall.RawConn.Control method, then handling properly the errors that may occur
|
||||
// within fn or returned by syscall.RawConn.Control itself.
|
||||
func syscallConnControl(conn syscall.Conn, fn func(fd uintptr) (int, experimentalsys.Errno)) (n int, errno experimentalsys.Errno) {
|
||||
syscallConn, err := conn.SyscallConn()
|
||||
if err != nil {
|
||||
return 0, experimentalsys.UnwrapOSError(err)
|
||||
}
|
||||
// Prioritize the inner errno over Control
|
||||
if controlErr := syscallConn.Control(func(fd uintptr) {
|
||||
n, errno = fn(fd)
|
||||
}); errno == 0 {
|
||||
errno = experimentalsys.UnwrapOSError(controlErr)
|
||||
}
|
||||
return
|
||||
}
|
49
vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_unix.go
generated
vendored
Normal file
49
vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_unix.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
//go:build (linux || darwin) && !tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/fsapi"
|
||||
socketapi "github.com/tetratelabs/wazero/internal/sock"
|
||||
)
|
||||
|
||||
// MSG_PEEK is the constant syscall.MSG_PEEK
|
||||
const MSG_PEEK = syscall.MSG_PEEK
|
||||
|
||||
func newTCPListenerFile(tl *net.TCPListener) socketapi.TCPSock {
|
||||
return newDefaultTCPListenerFile(tl)
|
||||
}
|
||||
|
||||
func _pollSock(conn syscall.Conn, flag fsapi.Pflag, timeoutMillis int32) (bool, sys.Errno) {
|
||||
n, errno := syscallConnControl(conn, func(fd uintptr) (int, sys.Errno) {
|
||||
if ready, errno := poll(fd, fsapi.POLLIN, 0); !ready || errno != 0 {
|
||||
return -1, errno
|
||||
} else {
|
||||
return 0, errno
|
||||
}
|
||||
})
|
||||
return n >= 0, errno
|
||||
}
|
||||
|
||||
func setNonblockSocket(fd uintptr, enabled bool) sys.Errno {
|
||||
return sys.UnwrapOSError(setNonblock(fd, enabled))
|
||||
}
|
||||
|
||||
func readSocket(fd uintptr, buf []byte) (int, sys.Errno) {
|
||||
n, err := syscall.Read(int(fd), buf)
|
||||
return n, sys.UnwrapOSError(err)
|
||||
}
|
||||
|
||||
func writeSocket(fd uintptr, buf []byte) (int, sys.Errno) {
|
||||
n, err := syscall.Write(int(fd), buf)
|
||||
return n, sys.UnwrapOSError(err)
|
||||
}
|
||||
|
||||
func recvfrom(fd uintptr, buf []byte, flags int32) (n int, errno sys.Errno) {
|
||||
n, _, err := syscall.Recvfrom(int(fd), buf, int(flags))
|
||||
return n, sys.UnwrapOSError(err)
|
||||
}
|
81
vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_unsupported.go
generated
vendored
Normal file
81
vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
//go:build (!linux && !darwin && !windows) || tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/fsapi"
|
||||
socketapi "github.com/tetratelabs/wazero/internal/sock"
|
||||
)
|
||||
|
||||
// MSG_PEEK is a filler value.
|
||||
const MSG_PEEK = 0x2
|
||||
|
||||
func newTCPListenerFile(tl *net.TCPListener) socketapi.TCPSock {
|
||||
return &unsupportedSockFile{}
|
||||
}
|
||||
|
||||
type unsupportedSockFile struct {
|
||||
baseSockFile
|
||||
}
|
||||
|
||||
// Accept implements the same method as documented on socketapi.TCPSock
|
||||
func (f *unsupportedSockFile) Accept() (socketapi.TCPConn, sys.Errno) {
|
||||
return nil, sys.ENOSYS
|
||||
}
|
||||
|
||||
func _pollSock(conn syscall.Conn, flag fsapi.Pflag, timeoutMillis int32) (bool, sys.Errno) {
|
||||
return false, sys.ENOTSUP
|
||||
}
|
||||
|
||||
func setNonblockSocket(fd uintptr, enabled bool) sys.Errno {
|
||||
return sys.ENOTSUP
|
||||
}
|
||||
|
||||
func readSocket(fd uintptr, buf []byte) (int, sys.Errno) {
|
||||
return -1, sys.ENOTSUP
|
||||
}
|
||||
|
||||
func writeSocket(fd uintptr, buf []byte) (int, sys.Errno) {
|
||||
return -1, sys.ENOTSUP
|
||||
}
|
||||
|
||||
func recvfrom(fd uintptr, buf []byte, flags int32) (n int, errno sys.Errno) {
|
||||
return -1, sys.ENOTSUP
|
||||
}
|
||||
|
||||
// syscallConnControl extracts a syscall.RawConn from the given syscall.Conn and applies
|
||||
// the given fn to a file descriptor, returning an integer or a nonzero syscall.Errno on failure.
|
||||
//
|
||||
// syscallConnControl streamlines the pattern of extracting the syscall.Rawconn,
|
||||
// invoking its syscall.RawConn.Control method, then handling properly the errors that may occur
|
||||
// within fn or returned by syscall.RawConn.Control itself.
|
||||
func syscallConnControl(conn syscall.Conn, fn func(fd uintptr) (int, experimentalsys.Errno)) (n int, errno sys.Errno) {
|
||||
return -1, sys.ENOTSUP
|
||||
}
|
||||
|
||||
// Accept implements the same method as documented on socketapi.TCPSock
|
||||
func (f *tcpListenerFile) Accept() (socketapi.TCPConn, experimentalsys.Errno) {
|
||||
return nil, experimentalsys.ENOSYS
|
||||
}
|
||||
|
||||
// Shutdown implements the same method as documented on experimentalsys.Conn
|
||||
func (f *tcpConnFile) Shutdown(how int) experimentalsys.Errno {
|
||||
// FIXME: can userland shutdown listeners?
|
||||
var err error
|
||||
switch how {
|
||||
case socketapi.SHUT_RD:
|
||||
err = f.tc.Close()
|
||||
case socketapi.SHUT_WR:
|
||||
err = f.tc.Close()
|
||||
case socketapi.SHUT_RDWR:
|
||||
return f.close()
|
||||
default:
|
||||
return experimentalsys.EINVAL
|
||||
}
|
||||
return experimentalsys.UnwrapOSError(err)
|
||||
}
|
80
vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_windows.go
generated
vendored
Normal file
80
vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_windows.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
//go:build windows
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/internal/fsapi"
|
||||
socketapi "github.com/tetratelabs/wazero/internal/sock"
|
||||
)
|
||||
|
||||
const (
|
||||
// MSG_PEEK is the flag PEEK for syscall.Recvfrom on Windows.
|
||||
// This constant is not exported on this platform.
|
||||
MSG_PEEK = 0x2
|
||||
// _FIONBIO is the flag to set the O_NONBLOCK flag on socket handles using ioctlsocket.
|
||||
_FIONBIO = 0x8004667e
|
||||
)
|
||||
|
||||
var (
|
||||
// modws2_32 is WinSock.
|
||||
modws2_32 = syscall.NewLazyDLL("ws2_32.dll")
|
||||
// procrecvfrom exposes recvfrom from WinSock.
|
||||
procrecvfrom = modws2_32.NewProc("recvfrom")
|
||||
// procioctlsocket exposes ioctlsocket from WinSock.
|
||||
procioctlsocket = modws2_32.NewProc("ioctlsocket")
|
||||
)
|
||||
|
||||
func newTCPListenerFile(tl *net.TCPListener) socketapi.TCPSock {
|
||||
return newDefaultTCPListenerFile(tl)
|
||||
}
|
||||
|
||||
// recvfrom exposes the underlying syscall in Windows.
|
||||
//
|
||||
// Note: since we are only using this to expose MSG_PEEK,
|
||||
// we do not need really need all the parameters that are actually
|
||||
// allowed in WinSock.
|
||||
// We ignore `from *sockaddr` and `fromlen *int`.
|
||||
func recvfrom(s uintptr, buf []byte, flags int32) (n int, errno sys.Errno) {
|
||||
var _p0 *byte
|
||||
if len(buf) > 0 {
|
||||
_p0 = &buf[0]
|
||||
}
|
||||
r0, _, e1 := syscall.SyscallN(
|
||||
procrecvfrom.Addr(),
|
||||
s,
|
||||
uintptr(unsafe.Pointer(_p0)),
|
||||
uintptr(len(buf)),
|
||||
uintptr(flags),
|
||||
0, // from *sockaddr (optional)
|
||||
0) // fromlen *int (optional)
|
||||
return int(r0), sys.UnwrapOSError(e1)
|
||||
}
|
||||
|
||||
func setNonblockSocket(fd uintptr, enabled bool) sys.Errno {
|
||||
opt := uint64(0)
|
||||
if enabled {
|
||||
opt = 1
|
||||
}
|
||||
// ioctlsocket(fd, FIONBIO, &opt)
|
||||
_, _, errno := syscall.SyscallN(
|
||||
procioctlsocket.Addr(),
|
||||
uintptr(fd),
|
||||
uintptr(_FIONBIO),
|
||||
uintptr(unsafe.Pointer(&opt)))
|
||||
return sys.UnwrapOSError(errno)
|
||||
}
|
||||
|
||||
func _pollSock(conn syscall.Conn, flag fsapi.Pflag, timeoutMillis int32) (bool, sys.Errno) {
|
||||
if flag != fsapi.POLLIN {
|
||||
return false, sys.ENOTSUP
|
||||
}
|
||||
n, errno := syscallConnControl(conn, func(fd uintptr) (int, sys.Errno) {
|
||||
return _poll([]pollFd{newPollFd(fd, _POLLIN, 0)}, timeoutMillis)
|
||||
})
|
||||
return n > 0, errno
|
||||
}
|
16
vendor/github.com/tetratelabs/wazero/internal/sysfs/stat.go
generated
vendored
Normal file
16
vendor/github.com/tetratelabs/wazero/internal/sysfs/stat.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
func defaultStatFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) {
|
||||
if info, err := f.Stat(); err != nil {
|
||||
return sys.Stat_t{}, experimentalsys.UnwrapOSError(err)
|
||||
} else {
|
||||
return sys.NewStat_t(info), 0
|
||||
}
|
||||
}
|
37
vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_bsd.go
generated
vendored
Normal file
37
vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_bsd.go
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
//go:build (amd64 || arm64) && (darwin || freebsd)
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
// dirNlinkIncludesDot is true because even though os.File filters out dot
|
||||
// entries, the underlying syscall.Stat includes them.
|
||||
//
|
||||
// Note: this is only used in tests
|
||||
const dirNlinkIncludesDot = true
|
||||
|
||||
func lstat(path string) (sys.Stat_t, experimentalsys.Errno) {
|
||||
if info, err := os.Lstat(path); err != nil {
|
||||
return sys.Stat_t{}, experimentalsys.UnwrapOSError(err)
|
||||
} else {
|
||||
return sys.NewStat_t(info), 0
|
||||
}
|
||||
}
|
||||
|
||||
func stat(path string) (sys.Stat_t, experimentalsys.Errno) {
|
||||
if info, err := os.Stat(path); err != nil {
|
||||
return sys.Stat_t{}, experimentalsys.UnwrapOSError(err)
|
||||
} else {
|
||||
return sys.NewStat_t(info), 0
|
||||
}
|
||||
}
|
||||
|
||||
func statFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) {
|
||||
return defaultStatFile(f)
|
||||
}
|
40
vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_linux.go
generated
vendored
Normal file
40
vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_linux.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
//go:build (amd64 || arm64 || riscv64) && linux
|
||||
|
||||
// Note: This expression is not the same as compiler support, even if it looks
|
||||
// similar. Platform functions here are used in interpreter mode as well.
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
// dirNlinkIncludesDot is true because even though os.File filters out dot
|
||||
// entries, the underlying syscall.Stat includes them.
|
||||
//
|
||||
// Note: this is only used in tests
|
||||
const dirNlinkIncludesDot = true
|
||||
|
||||
func lstat(path string) (sys.Stat_t, experimentalsys.Errno) {
|
||||
if info, err := os.Lstat(path); err != nil {
|
||||
return sys.Stat_t{}, experimentalsys.UnwrapOSError(err)
|
||||
} else {
|
||||
return sys.NewStat_t(info), 0
|
||||
}
|
||||
}
|
||||
|
||||
func stat(path string) (sys.Stat_t, experimentalsys.Errno) {
|
||||
if info, err := os.Stat(path); err != nil {
|
||||
return sys.Stat_t{}, experimentalsys.UnwrapOSError(err)
|
||||
} else {
|
||||
return sys.NewStat_t(info), 0
|
||||
}
|
||||
}
|
||||
|
||||
func statFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) {
|
||||
return defaultStatFile(f)
|
||||
}
|
40
vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_unsupported.go
generated
vendored
Normal file
40
vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
//go:build (!((amd64 || arm64 || riscv64) && linux) && !((amd64 || arm64) && (darwin || freebsd)) && !((amd64 || arm64) && windows)) || js
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
// Note: go:build constraints must be the same as /sys.stat_unsupported.go for
|
||||
// the same reasons.
|
||||
|
||||
// dirNlinkIncludesDot might be true for some operating systems, which can have
|
||||
// new stat_XX.go files as necessary.
|
||||
//
|
||||
// Note: this is only used in tests
|
||||
const dirNlinkIncludesDot = false
|
||||
|
||||
func lstat(path string) (sys.Stat_t, experimentalsys.Errno) {
|
||||
if info, err := os.Lstat(path); err != nil {
|
||||
return sys.Stat_t{}, experimentalsys.UnwrapOSError(err)
|
||||
} else {
|
||||
return sys.NewStat_t(info), 0
|
||||
}
|
||||
}
|
||||
|
||||
func stat(path string) (sys.Stat_t, experimentalsys.Errno) {
|
||||
if info, err := os.Stat(path); err != nil {
|
||||
return sys.Stat_t{}, experimentalsys.UnwrapOSError(err)
|
||||
} else {
|
||||
return sys.NewStat_t(info), 0
|
||||
}
|
||||
}
|
||||
|
||||
func statFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) {
|
||||
return defaultStatFile(f)
|
||||
}
|
120
vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_windows.go
generated
vendored
Normal file
120
vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_windows.go
generated
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
//go:build (amd64 || arm64) && windows
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"syscall"
|
||||
|
||||
experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
// dirNlinkIncludesDot is false because Windows does not return dot entries.
|
||||
//
|
||||
// Note: this is only used in tests
|
||||
const dirNlinkIncludesDot = false
|
||||
|
||||
func lstat(path string) (sys.Stat_t, experimentalsys.Errno) {
|
||||
attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
|
||||
// Use FILE_FLAG_OPEN_REPARSE_POINT, otherwise CreateFile will follow symlink.
|
||||
// See https://docs.microsoft.com/en-us/windows/desktop/FileIO/symbolic-link-effects-on-file-systems-functions#createfile-and-createfiletransacted
|
||||
attrs |= syscall.FILE_FLAG_OPEN_REPARSE_POINT
|
||||
return statPath(attrs, path)
|
||||
}
|
||||
|
||||
func stat(path string) (sys.Stat_t, experimentalsys.Errno) {
|
||||
attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
|
||||
return statPath(attrs, path)
|
||||
}
|
||||
|
||||
func statPath(createFileAttrs uint32, path string) (sys.Stat_t, experimentalsys.Errno) {
|
||||
if len(path) == 0 {
|
||||
return sys.Stat_t{}, experimentalsys.ENOENT
|
||||
}
|
||||
pathp, err := syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return sys.Stat_t{}, experimentalsys.EINVAL
|
||||
}
|
||||
|
||||
// open the file handle
|
||||
h, err := syscall.CreateFile(pathp, 0, 0, nil,
|
||||
syscall.OPEN_EXISTING, createFileAttrs, 0)
|
||||
if err != nil {
|
||||
errno := experimentalsys.UnwrapOSError(err)
|
||||
// To match expectations of WASI, e.g. TinyGo TestStatBadDir, return
|
||||
// ENOENT, not ENOTDIR.
|
||||
if errno == experimentalsys.ENOTDIR {
|
||||
errno = experimentalsys.ENOENT
|
||||
}
|
||||
return sys.Stat_t{}, errno
|
||||
}
|
||||
defer syscall.CloseHandle(h)
|
||||
|
||||
return statHandle(h)
|
||||
}
|
||||
|
||||
// fdFile allows masking the `Fd` function on os.File.
|
||||
type fdFile interface {
|
||||
Fd() uintptr
|
||||
}
|
||||
|
||||
func statFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) {
|
||||
if osF, ok := f.(fdFile); ok {
|
||||
// Attempt to get the stat by handle, which works for normal files
|
||||
st, err := statHandle(syscall.Handle(osF.Fd()))
|
||||
|
||||
// ERROR_INVALID_HANDLE happens before Go 1.20. Don't fail as we only
|
||||
// use that approach to fill in inode data, which is not critical.
|
||||
//
|
||||
// Note: statHandle uses UnwrapOSError which coerces
|
||||
// ERROR_INVALID_HANDLE to EBADF.
|
||||
if err != experimentalsys.EBADF {
|
||||
return st, err
|
||||
}
|
||||
}
|
||||
return defaultStatFile(f)
|
||||
}
|
||||
|
||||
func statHandle(h syscall.Handle) (sys.Stat_t, experimentalsys.Errno) {
|
||||
winFt, err := syscall.GetFileType(h)
|
||||
if err != nil {
|
||||
return sys.Stat_t{}, experimentalsys.UnwrapOSError(err)
|
||||
}
|
||||
|
||||
var fi syscall.ByHandleFileInformation
|
||||
if err = syscall.GetFileInformationByHandle(h, &fi); err != nil {
|
||||
return sys.Stat_t{}, experimentalsys.UnwrapOSError(err)
|
||||
}
|
||||
|
||||
var m fs.FileMode
|
||||
if fi.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 {
|
||||
m |= 0o444
|
||||
} else {
|
||||
m |= 0o666
|
||||
}
|
||||
|
||||
switch { // check whether this is a symlink first
|
||||
case fi.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0:
|
||||
m |= fs.ModeSymlink
|
||||
case winFt == syscall.FILE_TYPE_PIPE:
|
||||
m |= fs.ModeNamedPipe
|
||||
case winFt == syscall.FILE_TYPE_CHAR:
|
||||
m |= fs.ModeDevice | fs.ModeCharDevice
|
||||
case fi.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0:
|
||||
m |= fs.ModeDir | 0o111 // e.g. 0o444 -> 0o555
|
||||
}
|
||||
|
||||
st := sys.Stat_t{}
|
||||
// FileIndex{High,Low} can be combined and used as a unique identifier like inode.
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/ns-fileapi-by_handle_file_information
|
||||
st.Dev = uint64(fi.VolumeSerialNumber)
|
||||
st.Ino = (uint64(fi.FileIndexHigh) << 32) | uint64(fi.FileIndexLow)
|
||||
st.Mode = m
|
||||
st.Nlink = uint64(fi.NumberOfLinks)
|
||||
st.Size = int64(fi.FileSizeHigh)<<32 + int64(fi.FileSizeLow)
|
||||
st.Atim = fi.LastAccessTime.Nanoseconds()
|
||||
st.Mtim = fi.LastWriteTime.Nanoseconds()
|
||||
st.Ctim = fi.CreationTime.Nanoseconds()
|
||||
return st, 0
|
||||
}
|
13
vendor/github.com/tetratelabs/wazero/internal/sysfs/sync.go
generated
vendored
Normal file
13
vendor/github.com/tetratelabs/wazero/internal/sysfs/sync.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
//go:build !windows
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
func fsync(f *os.File) sys.Errno {
|
||||
return sys.UnwrapOSError(f.Sync())
|
||||
}
|
20
vendor/github.com/tetratelabs/wazero/internal/sysfs/sync_windows.go
generated
vendored
Normal file
20
vendor/github.com/tetratelabs/wazero/internal/sysfs/sync_windows.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
func fsync(f *os.File) sys.Errno {
|
||||
errno := sys.UnwrapOSError(f.Sync())
|
||||
// Coerce error performing stat on a directory to 0, as it won't work
|
||||
// on Windows.
|
||||
switch errno {
|
||||
case sys.EACCES /* Go 1.20 */, sys.EBADF /* Go 1.19 */ :
|
||||
if st, err := f.Stat(); err == nil && st.IsDir() {
|
||||
errno = 0
|
||||
}
|
||||
}
|
||||
return errno
|
||||
}
|
13
vendor/github.com/tetratelabs/wazero/internal/sysfs/syscall6_darwin.go
generated
vendored
Normal file
13
vendor/github.com/tetratelabs/wazero/internal/sysfs/syscall6_darwin.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
_ "unsafe"
|
||||
)
|
||||
|
||||
// syscall_syscall6 is a private symbol that we link below. We need to use this
|
||||
// instead of syscall.Syscall6 because the public syscall.Syscall6 won't work
|
||||
// when fn is an address.
|
||||
//
|
||||
//go:linkname syscall_syscall6 syscall.syscall6
|
||||
func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)
|
6
vendor/github.com/tetratelabs/wazero/internal/sysfs/sysfs.go
generated
vendored
Normal file
6
vendor/github.com/tetratelabs/wazero/internal/sysfs/sysfs.go
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
// Package sysfs includes a low-level filesystem interface and utilities needed
|
||||
// for WebAssembly host functions (ABI) such as WASI.
|
||||
//
|
||||
// The name sysfs was chosen because wazero's public API has a "sys" package,
|
||||
// which was named after https://github.com/golang/sys.
|
||||
package sysfs
|
17
vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink.go
generated
vendored
Normal file
17
vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
//go:build !windows && !plan9 && !tinygo
|
||||
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
func unlink(name string) (errno sys.Errno) {
|
||||
err := syscall.Unlink(name)
|
||||
if errno = sys.UnwrapOSError(err); errno == sys.EPERM {
|
||||
errno = sys.EISDIR
|
||||
}
|
||||
return errno
|
||||
}
|
12
vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink_plan9.go
generated
vendored
Normal file
12
vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink_plan9.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
func unlink(name string) sys.Errno {
|
||||
err := syscall.Remove(name)
|
||||
return sys.UnwrapOSError(err)
|
||||
}
|
25
vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink_windows.go
generated
vendored
Normal file
25
vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink_windows.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
package sysfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"github.com/tetratelabs/wazero/experimental/sys"
|
||||
)
|
||||
|
||||
func unlink(name string) sys.Errno {
|
||||
err := syscall.Unlink(name)
|
||||
if err == nil {
|
||||
return 0
|
||||
}
|
||||
errno := sys.UnwrapOSError(err)
|
||||
if errno == sys.EBADF {
|
||||
lstat, errLstat := os.Lstat(name)
|
||||
if errLstat == nil && lstat.Mode()&os.ModeSymlink != 0 {
|
||||
errno = sys.UnwrapOSError(os.Remove(name))
|
||||
} else {
|
||||
errno = sys.EISDIR
|
||||
}
|
||||
}
|
||||
return errno
|
||||
}
|
Reference in New Issue
Block a user