WASI & darwin: support basic file io based on libc
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
Этот коммит содержится в:
родитель
6d3c11627c
коммит
1406453350
16 изменённых файлов: 390 добавлений и 84 удалений
15
main_test.go
15
main_test.go
|
@ -59,7 +59,10 @@ func TestCompiler(t *testing.T) {
|
||||||
t.Run("Host", func(t *testing.T) {
|
t.Run("Host", func(t *testing.T) {
|
||||||
runPlatTests("", matches, t)
|
runPlatTests("", matches, t)
|
||||||
if runtime.GOOS == "darwin" {
|
if runtime.GOOS == "darwin" {
|
||||||
runTest("testdata/libc/env.go", "", t, []string{"ENV1=VALUE1", "ENV2=VALUE2"}...)
|
runTest("testdata/libc/filesystem.go", "", t,
|
||||||
|
nil, nil)
|
||||||
|
runTest("testdata/libc/env.go", "", t,
|
||||||
|
[]string{"ENV1=VALUE1", "ENV2=VALUE2"}, nil)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -107,7 +110,9 @@ func TestCompiler(t *testing.T) {
|
||||||
|
|
||||||
t.Run("WASI", func(t *testing.T) {
|
t.Run("WASI", func(t *testing.T) {
|
||||||
runPlatTests("wasi", matches, t)
|
runPlatTests("wasi", matches, t)
|
||||||
runTest("testdata/libc/env.go", "wasi", t, []string{"ENV1=VALUE1", "ENV2=VALUE2"}...)
|
runTest("testdata/libc/env.go", "wasi", t,
|
||||||
|
[]string{"--env", "ENV1=VALUE1", "--env", "ENV2=VALUE2"}, nil)
|
||||||
|
runTest("testdata/libc/filesystem.go", "wasi", t, nil, []string{"--dir=."})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,7 +124,7 @@ func runPlatTests(target string, matches []string, t *testing.T) {
|
||||||
path := path // redefine to avoid race condition
|
path := path // redefine to avoid race condition
|
||||||
t.Run(filepath.Base(path), func(t *testing.T) {
|
t.Run(filepath.Base(path), func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
runTest(path, target, t)
|
runTest(path, target, t, nil, nil)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,7 +141,7 @@ func runBuild(src, out string, opts *compileopts.Options) error {
|
||||||
return Build(src, out, opts)
|
return Build(src, out, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runTest(path, target string, t *testing.T, environmentVars ...string) {
|
func runTest(path, target string, t *testing.T, environmentVars []string, additionalArgs []string) {
|
||||||
// Get the expected output for this test.
|
// Get the expected output for this test.
|
||||||
txtpath := path[:len(path)-3] + ".txt"
|
txtpath := path[:len(path)-3] + ".txt"
|
||||||
if path[len(path)-1] == os.PathSeparator {
|
if path[len(path)-1] == os.PathSeparator {
|
||||||
|
@ -195,7 +200,7 @@ func runTest(path, target string, t *testing.T, environmentVars ...string) {
|
||||||
cmd = exec.Command(binary)
|
cmd = exec.Command(binary)
|
||||||
} else {
|
} else {
|
||||||
args := append(spec.Emulator[1:], binary)
|
args := append(spec.Emulator[1:], binary)
|
||||||
cmd = exec.Command(spec.Emulator[0], args...)
|
cmd = exec.Command(spec.Emulator[0], append(args, additionalArgs...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(spec.Emulator) != 0 && spec.Emulator[0] == "wasmtime" {
|
if len(spec.Emulator) != 0 && spec.Emulator[0] == "wasmtime" {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package os
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -17,9 +18,8 @@ var (
|
||||||
ErrExist = errors.New("file exists")
|
ErrExist = errors.New("file exists")
|
||||||
)
|
)
|
||||||
|
|
||||||
func IsPermission(err error) bool {
|
// The following code is copied from the official implementation.
|
||||||
return err == ErrPermission
|
// https://github.com/golang/go/blob/4ce6a8e89668b87dce67e2f55802903d6eb9110a/src/os/error.go#L65-L104
|
||||||
}
|
|
||||||
|
|
||||||
func NewSyscallError(syscall string, err error) error {
|
func NewSyscallError(syscall string, err error) error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -37,3 +37,39 @@ type SyscallError struct {
|
||||||
func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Err.Error() }
|
func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Err.Error() }
|
||||||
|
|
||||||
func (e *SyscallError) Unwrap() error { return e.Err }
|
func (e *SyscallError) Unwrap() error { return e.Err }
|
||||||
|
|
||||||
|
func IsExist(err error) bool {
|
||||||
|
return underlyingErrorIs(err, ErrExist)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsNotExist(err error) bool {
|
||||||
|
return underlyingErrorIs(err, ErrNotExist)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsPermission(err error) bool {
|
||||||
|
return underlyingErrorIs(err, ErrPermission)
|
||||||
|
}
|
||||||
|
|
||||||
|
func underlyingErrorIs(err, target error) bool {
|
||||||
|
// Note that this function is not errors.Is:
|
||||||
|
// underlyingError only unwraps the specific error-wrapping types
|
||||||
|
// that it historically did, not all errors implementing Unwrap().
|
||||||
|
err = underlyingError(err)
|
||||||
|
if err == target {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// To preserve prior behavior, only examine syscall errors.
|
||||||
|
e, ok := err.(syscall.Errno)
|
||||||
|
return ok && e.Is(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
// underlyingError returns the underlying error for known os error types.
|
||||||
|
func underlyingError(err error) error {
|
||||||
|
switch err := err.(type) {
|
||||||
|
case *PathError:
|
||||||
|
return err.Err
|
||||||
|
case *SyscallError:
|
||||||
|
return err.Err
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
package os
|
package os
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -76,7 +77,7 @@ func Create(name string) (*File, error) {
|
||||||
// read and any error encountered. At end of file, Read returns 0, io.EOF.
|
// read and any error encountered. At end of file, Read returns 0, io.EOF.
|
||||||
func (f *File) Read(b []byte) (n int, err error) {
|
func (f *File) Read(b []byte) (n int, err error) {
|
||||||
n, err = f.handle.Read(b)
|
n, err = f.handle.Read(b)
|
||||||
if err != nil {
|
if err != nil && err != io.EOF {
|
||||||
err = &PathError{"read", f.name, err}
|
err = &PathError{"read", f.name, err}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -155,59 +156,17 @@ func (e *PathError) Error() string {
|
||||||
return e.Op + " " + e.Path + ": " + e.Err.Error()
|
return e.Op + " " + e.Path + ": " + e.Err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileMode uint32
|
|
||||||
|
|
||||||
// Mode constants, copied from the mainline Go source
|
|
||||||
// https://github.com/golang/go/blob/4ce6a8e89668b87dce67e2f55802903d6eb9110a/src/os/types.go#L35-L63
|
|
||||||
const (
|
const (
|
||||||
// The single letters are the abbreviations used by the String method's formatting.
|
O_RDONLY int = syscall.O_RDONLY
|
||||||
ModeDir FileMode = 1 << (32 - 1 - iota) // d: is a directory
|
O_WRONLY int = syscall.O_WRONLY
|
||||||
ModeAppend // a: append-only
|
O_RDWR int = syscall.O_RDWR
|
||||||
ModeExclusive // l: exclusive use
|
O_APPEND int = syscall.O_APPEND
|
||||||
ModeTemporary // T: temporary file; Plan 9 only
|
O_CREATE int = syscall.O_CREAT
|
||||||
ModeSymlink // L: symbolic link
|
O_EXCL int = syscall.O_EXCL
|
||||||
ModeDevice // D: device file
|
O_SYNC int = syscall.O_SYNC
|
||||||
ModeNamedPipe // p: named pipe (FIFO)
|
O_TRUNC int = syscall.O_TRUNC
|
||||||
ModeSocket // S: Unix domain socket
|
|
||||||
ModeSetuid // u: setuid
|
|
||||||
ModeSetgid // g: setgid
|
|
||||||
ModeCharDevice // c: Unix character device, when ModeDevice is set
|
|
||||||
ModeSticky // t: sticky
|
|
||||||
ModeIrregular // ?: non-regular file; nothing else is known about this file
|
|
||||||
|
|
||||||
// Mask for the type bits. For regular files, none will be set.
|
|
||||||
ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice | ModeCharDevice | ModeIrregular
|
|
||||||
|
|
||||||
ModePerm FileMode = 0777 // Unix permission bits
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsDir is a stub, always returning false
|
|
||||||
func (m FileMode) IsDir() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stub constants
|
|
||||||
const (
|
|
||||||
O_RDONLY int = 1
|
|
||||||
O_WRONLY int = 2
|
|
||||||
O_RDWR int = 4
|
|
||||||
O_APPEND int = 8
|
|
||||||
O_CREATE int = 16
|
|
||||||
O_EXCL int = 32
|
|
||||||
O_SYNC int = 64
|
|
||||||
O_TRUNC int = 128
|
|
||||||
)
|
|
||||||
|
|
||||||
// A FileInfo describes a file and is returned by Stat and Lstat.
|
|
||||||
type FileInfo interface {
|
|
||||||
Name() string // base name of the file
|
|
||||||
Size() int64 // length in bytes for regular files; system-dependent for others
|
|
||||||
Mode() FileMode // file mode bits
|
|
||||||
// TODO ModTime() time.Time // modification time
|
|
||||||
IsDir() bool // abbreviation for Mode().IsDir()
|
|
||||||
Sys() interface{} // underlying data source (can return nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stat is a stub, not yet implemented
|
// Stat is a stub, not yet implemented
|
||||||
func Stat(name string) (FileInfo, error) {
|
func Stat(name string) (FileInfo, error) {
|
||||||
return nil, &PathError{"stat", name, ErrNotImplemented}
|
return nil, &PathError{"stat", name, ErrNotImplemented}
|
||||||
|
@ -233,16 +192,6 @@ func TempDir() string {
|
||||||
return "/tmp"
|
return "/tmp"
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsExist is a stub (for now), always returning false
|
|
||||||
func IsExist(err error) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNotExist is a stub (for now), always returning false
|
|
||||||
func IsNotExist(err error) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getpid is a stub (for now), always returning 1
|
// Getpid is a stub (for now), always returning 1
|
||||||
func Getpid() int {
|
func Getpid() int {
|
||||||
return 1
|
return 1
|
||||||
|
|
81
src/os/file_go_116.go
Обычный файл
81
src/os/file_go_116.go
Обычный файл
|
@ -0,0 +1,81 @@
|
||||||
|
// +build go1.16
|
||||||
|
|
||||||
|
package os
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
DirEntry = fs.DirEntry
|
||||||
|
FileMode = fs.FileMode
|
||||||
|
FileInfo = fs.FileInfo
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *File) ReadDir(n int) ([]DirEntry, error) {
|
||||||
|
return nil, &PathError{"ReadDir", f.name, ErrNotImplemented}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The followings are copied from Go 1.16 official implementation:
|
||||||
|
// https://github.com/golang/go/blob/go1.16/src/os/file.go
|
||||||
|
|
||||||
|
// ReadFile reads the named file and returns the contents.
|
||||||
|
// A successful call returns err == nil, not err == EOF.
|
||||||
|
// Because ReadFile reads the whole file, it does not treat an EOF from Read
|
||||||
|
// as an error to be reported.
|
||||||
|
func ReadFile(name string) ([]byte, error) {
|
||||||
|
f, err := Open(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
var size int
|
||||||
|
if info, err := f.Stat(); err == nil {
|
||||||
|
size64 := info.Size()
|
||||||
|
if int64(int(size64)) == size64 {
|
||||||
|
size = int(size64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size++ // one byte for final read at EOF
|
||||||
|
|
||||||
|
// If a file claims a small size, read at least 512 bytes.
|
||||||
|
// In particular, files in Linux's /proc claim size 0 but
|
||||||
|
// then do not work right if read in small pieces,
|
||||||
|
// so an initial read of 1 byte would not work correctly.
|
||||||
|
if size < 512 {
|
||||||
|
size = 512
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make([]byte, 0, size)
|
||||||
|
for {
|
||||||
|
if len(data) >= cap(data) {
|
||||||
|
d := append(data[:cap(data)], 0)
|
||||||
|
data = d[:len(data)]
|
||||||
|
}
|
||||||
|
n, err := f.Read(data[len(data):cap(data)])
|
||||||
|
data = data[:len(data)+n]
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteFile writes data to the named file, creating it if necessary.
|
||||||
|
// If the file does not exist, WriteFile creates it with permissions perm (before umask);
|
||||||
|
// otherwise WriteFile truncates it before writing, without changing permissions.
|
||||||
|
func WriteFile(name string, data []byte, perm FileMode) error {
|
||||||
|
f, err := OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, perm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = f.Write(data)
|
||||||
|
if err1 := f.Close(); err1 != nil && err == nil {
|
||||||
|
err = err1
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
46
src/os/file_go_other.go
Обычный файл
46
src/os/file_go_other.go
Обычный файл
|
@ -0,0 +1,46 @@
|
||||||
|
// +build !go1.16
|
||||||
|
|
||||||
|
package os
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// A FileInfo describes a file and is returned by Stat and Lstat.
|
||||||
|
type FileInfo interface {
|
||||||
|
Name() string // base name of the file
|
||||||
|
Size() int64 // length in bytes for regular files; system-dependent for others
|
||||||
|
Mode() FileMode // file mode bits
|
||||||
|
ModTime() time.Time // modification time
|
||||||
|
IsDir() bool // abbreviation for Mode().IsDir()
|
||||||
|
Sys() interface{} // underlying data source (can return nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileMode uint32
|
||||||
|
|
||||||
|
// Mode constants, copied from the mainline Go source
|
||||||
|
// https://github.com/golang/go/blob/4ce6a8e89668b87dce67e2f55802903d6eb9110a/src/os/types.go#L35-L63
|
||||||
|
const (
|
||||||
|
// The single letters are the abbreviations used by the String method's formatting.
|
||||||
|
ModeDir FileMode = 1 << (32 - 1 - iota) // d: is a directory
|
||||||
|
ModeAppend // a: append-only
|
||||||
|
ModeExclusive // l: exclusive use
|
||||||
|
ModeTemporary // T: temporary file; Plan 9 only
|
||||||
|
ModeSymlink // L: symbolic link
|
||||||
|
ModeDevice // D: device file
|
||||||
|
ModeNamedPipe // p: named pipe (FIFO)
|
||||||
|
ModeSocket // S: Unix domain socket
|
||||||
|
ModeSetuid // u: setuid
|
||||||
|
ModeSetgid // g: setgid
|
||||||
|
ModeCharDevice // c: Unix character device, when ModeDevice is set
|
||||||
|
ModeSticky // t: sticky
|
||||||
|
ModeIrregular // ?: non-regular file; nothing else is known about this file
|
||||||
|
|
||||||
|
// Mask for the type bits. For regular files, none will be set.
|
||||||
|
ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice | ModeCharDevice | ModeIrregular
|
||||||
|
|
||||||
|
ModePerm FileMode = 0777 // Unix permission bits
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsDir is a stub, always returning false
|
||||||
|
func (m FileMode) IsDir() bool {
|
||||||
|
return false
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
// +build baremetal wasm
|
// +build baremetal wasm,!wasi
|
||||||
|
|
||||||
package os
|
package os
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
// +build darwin linux,!baremetal,!wasi freebsd,!baremetal
|
// +build darwin linux,!baremetal freebsd,!baremetal
|
||||||
|
|
||||||
package os
|
package os
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -77,6 +78,9 @@ type unixFileHandle uintptr
|
||||||
func (f unixFileHandle) Read(b []byte) (n int, err error) {
|
func (f unixFileHandle) Read(b []byte) (n int, err error) {
|
||||||
n, err = syscall.Read(int(f), b)
|
n, err = syscall.Read(int(f), b)
|
||||||
err = handleSyscallError(err)
|
err = handleSyscallError(err)
|
||||||
|
if n == 0 && err == nil {
|
||||||
|
err = io.EOF
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,16 @@ import (
|
||||||
|
|
||||||
type timeUnit int64
|
type timeUnit int64
|
||||||
|
|
||||||
|
// libc constructors
|
||||||
|
//export __wasm_call_ctors
|
||||||
|
func __wasm_call_ctors()
|
||||||
|
|
||||||
//export _start
|
//export _start
|
||||||
func _start() {
|
func _start() {
|
||||||
// These need to be initialized early so that the heap can be initialized.
|
// These need to be initialized early so that the heap can be initialized.
|
||||||
heapStart = uintptr(unsafe.Pointer(&heapStartSymbol))
|
heapStart = uintptr(unsafe.Pointer(&heapStartSymbol))
|
||||||
heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize)
|
heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize)
|
||||||
|
__wasm_call_ctors()
|
||||||
run()
|
run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
5
src/syscall/errno_other.go
Обычный файл
5
src/syscall/errno_other.go
Обычный файл
|
@ -0,0 +1,5 @@
|
||||||
|
// +build !wasi,!darwin
|
||||||
|
|
||||||
|
package syscall
|
||||||
|
|
||||||
|
func (e Errno) Is(target error) bool { return false }
|
|
@ -13,12 +13,15 @@ type sliceHeader struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Close(fd int) (err error) {
|
func Close(fd int) (err error) {
|
||||||
return ENOSYS // TODO
|
if libc_close(fd) < 0 {
|
||||||
|
err = getErrno()
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func Write(fd int, p []byte) (n int, err error) {
|
func Write(fd int, p []byte) (n int, err error) {
|
||||||
buf, count := splitSlice(p)
|
buf, count := splitSlice(p)
|
||||||
n = libc_write(int32(fd), buf, uint(count))
|
n = libc_write(fd, buf, uint(count))
|
||||||
if n < 0 {
|
if n < 0 {
|
||||||
err = getErrno()
|
err = getErrno()
|
||||||
}
|
}
|
||||||
|
@ -26,15 +29,25 @@ func Write(fd int, p []byte) (n int, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Read(fd int, p []byte) (n int, err error) {
|
func Read(fd int, p []byte) (n int, err error) {
|
||||||
return 0, ENOSYS // TODO
|
buf, count := splitSlice(p)
|
||||||
|
n = libc_read(fd, buf, uint(count))
|
||||||
|
if n < 0 {
|
||||||
|
err = getErrno()
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func Seek(fd int, offset int64, whence int) (off int64, err error) {
|
func Seek(fd int, offset int64, whence int) (off int64, err error) {
|
||||||
return 0, ENOSYS // TODO
|
return 0, ENOSYS // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
func Open(path string, mode int, perm uint32) (fd int, err error) {
|
func Open(path string, flag int, mode uint32) (fd int, err error) {
|
||||||
return 0, ENOSYS // TODO
|
data := append([]byte(path), 0)
|
||||||
|
fd = libc_open(&data[0], flag, mode)
|
||||||
|
if fd < 0 {
|
||||||
|
err = getErrno()
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func Mkdir(path string, mode uint32) (err error) {
|
func Mkdir(path string, mode uint32) (err error) {
|
||||||
|
@ -78,8 +91,20 @@ func splitSlice(p []byte) (buf *byte, len uintptr) {
|
||||||
|
|
||||||
// ssize_t write(int fd, const void *buf, size_t count)
|
// ssize_t write(int fd, const void *buf, size_t count)
|
||||||
//export write
|
//export write
|
||||||
func libc_write(fd int32, buf *byte, count uint) int
|
func libc_write(fd int, buf *byte, count uint) int
|
||||||
|
|
||||||
// char *getenv(const char *name);
|
// char *getenv(const char *name);
|
||||||
//export getenv
|
//export getenv
|
||||||
func libc_getenv(name *byte) *byte
|
func libc_getenv(name *byte) *byte
|
||||||
|
|
||||||
|
// ssize_t read(int fd, void *buf, size_t count);
|
||||||
|
//export read
|
||||||
|
func libc_read(fd int, buf *byte, count uint) int
|
||||||
|
|
||||||
|
// int open(const char *pathname, int flags, mode_t mode);
|
||||||
|
//export open
|
||||||
|
func libc_open(pathname *byte, flags int, mode uint32) int
|
||||||
|
|
||||||
|
// int close(int fd)
|
||||||
|
//export close
|
||||||
|
func libc_close(fd int) int
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build darwin
|
||||||
|
|
||||||
package syscall
|
package syscall
|
||||||
|
|
||||||
// This file defines errno and constants to match the darwin libsystem ABI.
|
// This file defines errno and constants to match the darwin libsystem ABI.
|
||||||
|
@ -22,10 +24,25 @@ func getErrno() Errno {
|
||||||
return Errno(uintptr(*errptr))
|
return Errno(uintptr(*errptr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e Errno) Is(target error) bool {
|
||||||
|
switch target.Error() {
|
||||||
|
case "permission denied":
|
||||||
|
return e == EACCES || e == EPERM
|
||||||
|
case "file already exists":
|
||||||
|
return e == EEXIST
|
||||||
|
case "file does not exist":
|
||||||
|
return e == ENOENT
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
EPERM Errno = 0x1
|
||||||
ENOENT Errno = 0x2
|
ENOENT Errno = 0x2
|
||||||
|
EACCES Errno = 0xd
|
||||||
EEXIST Errno = 0x11
|
EEXIST Errno = 0x11
|
||||||
EINTR Errno = 0x4
|
EINTR Errno = 0x4
|
||||||
|
ENOTDIR Errno = 0x14
|
||||||
EMFILE Errno = 0x18
|
EMFILE Errno = 0x18
|
||||||
EAGAIN Errno = 0x23
|
EAGAIN Errno = 0x23
|
||||||
ETIMEDOUT Errno = 0x3c
|
ETIMEDOUT Errno = 0x3c
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
package syscall
|
package syscall
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
// A Signal is a number describing a process signal.
|
// A Signal is a number describing a process signal.
|
||||||
// It implements the os.Signal interface.
|
// It implements the os.Signal interface.
|
||||||
type Signal int
|
type Signal int
|
||||||
|
@ -32,7 +30,6 @@ const (
|
||||||
O_RDWR = 2
|
O_RDWR = 2
|
||||||
|
|
||||||
O_CREAT = 0100
|
O_CREAT = 0100
|
||||||
O_CREATE = O_CREAT
|
|
||||||
O_TRUNC = 01000
|
O_TRUNC = 01000
|
||||||
O_APPEND = 02000
|
O_APPEND = 02000
|
||||||
O_EXCL = 0200
|
O_EXCL = 0200
|
||||||
|
@ -41,8 +38,9 @@ const (
|
||||||
O_CLOEXEC = 0
|
O_CLOEXEC = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
var dummyError = errors.New("unknown syscall error")
|
//go:extern errno
|
||||||
|
var libcErrno uintptr
|
||||||
|
|
||||||
func getErrno() error {
|
func getErrno() error {
|
||||||
return dummyError
|
return Errno(libcErrno)
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,6 @@ const (
|
||||||
O_RDWR = O_RDONLY | O_WRONLY
|
O_RDWR = O_RDONLY | O_WRONLY
|
||||||
|
|
||||||
O_CREAT = __WASI_OFLAGS_CREAT << 12
|
O_CREAT = __WASI_OFLAGS_CREAT << 12
|
||||||
O_CREATE = O_CREAT
|
|
||||||
O_TRUNC = __WASI_OFLAGS_TRUNC << 12
|
O_TRUNC = __WASI_OFLAGS_TRUNC << 12
|
||||||
O_APPEND = __WASI_FDFLAGS_APPEND
|
O_APPEND = __WASI_FDFLAGS_APPEND
|
||||||
O_EXCL = __WASI_OFLAGS_EXCL << 12
|
O_EXCL = __WASI_OFLAGS_EXCL << 12
|
||||||
|
@ -48,3 +47,95 @@ var libcErrno uintptr
|
||||||
func getErrno() error {
|
func getErrno() error {
|
||||||
return Errno(libcErrno)
|
return Errno(libcErrno)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e Errno) Is(target error) bool {
|
||||||
|
switch target.Error() {
|
||||||
|
case "permission denied":
|
||||||
|
return e == EACCES || e == EPERM || e == ENOTCAPABLE // ENOTCAPABLE is unique in WASI
|
||||||
|
case "file already exists":
|
||||||
|
return e == EEXIST || e == ENOTEMPTY
|
||||||
|
case "file does not exist":
|
||||||
|
return e == ENOENT
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/headers/public/__errno.h
|
||||||
|
const (
|
||||||
|
E2BIG Errno = 1 /* Argument list too long */
|
||||||
|
EACCES Errno = 2 /* Permission denied */
|
||||||
|
EADDRINUSE Errno = 3 /* Address already in use */
|
||||||
|
EADDRNOTAVAIL Errno = 4 /* Address not available */
|
||||||
|
EAFNOSUPPORT Errno = 5 /* Address family not supported by protocol family */
|
||||||
|
EAGAIN Errno = 6 /* Try again */
|
||||||
|
EWOULDBLOCK Errno = EAGAIN /* Operation would block */
|
||||||
|
EALREADY Errno = 7 /* Socket already connected */
|
||||||
|
EBADF Errno = 8 /* Bad file number */
|
||||||
|
EBADMSG Errno = 9 /* Trying to read unreadable message */
|
||||||
|
EBUSY Errno = 10 /* Device or resource busy */
|
||||||
|
ECANCELED Errno = 11 /* Operation canceled. */
|
||||||
|
ECHILD Errno = 12 /* No child processes */
|
||||||
|
ECONNABORTED Errno = 13 /* Connection aborted */
|
||||||
|
ECONNREFUSED Errno = 14 /* Connection refused */
|
||||||
|
ECONNRESET Errno = 15 /* Connection reset by peer */
|
||||||
|
EDEADLK Errno = 16 /* Deadlock condition */
|
||||||
|
EDESTADDRREQ Errno = 17 /* Destination address required */
|
||||||
|
EDOM Errno = 18 /* Math arg out of domain of func */
|
||||||
|
EDQUOT Errno = 19 /* Quota exceeded */
|
||||||
|
EEXIST Errno = 20 /* File exists */
|
||||||
|
EFAULT Errno = 21 /* Bad address */
|
||||||
|
EFBIG Errno = 22 /* File too large */
|
||||||
|
EHOSTUNREACH Errno = 23 /* Host is unreachable */
|
||||||
|
EIDRM Errno = 24 /* Identifier removed */
|
||||||
|
EILSEQ Errno = 25
|
||||||
|
EINPROGRESS Errno = 26 /* Connection already in progress */
|
||||||
|
EINTR Errno = 27 /* Interrupted system call */
|
||||||
|
EINVAL Errno = 28 /* Invalid argument */
|
||||||
|
EIO Errno = 29 /* I/O error */
|
||||||
|
EISCONN Errno = 30 /* Socket is already connected */
|
||||||
|
EISDIR Errno = 31 /* Is a directory */
|
||||||
|
ELOOP Errno = 32 /* Too many symbolic links */
|
||||||
|
EMFILE Errno = 33 /* Too many open files */
|
||||||
|
EMLINK Errno = 34 /* Too many links */
|
||||||
|
EMSGSIZE Errno = 35 /* Message too long */
|
||||||
|
EMULTIHOP Errno = 36 /* Multihop attempted */
|
||||||
|
ENAMETOOLONG Errno = 37 /* File name too long */
|
||||||
|
ENETDOWN Errno = 38 /* Network interface is not configured */
|
||||||
|
ENETRESET Errno = 39
|
||||||
|
ENETUNREACH Errno = 40 /* Network is unreachable */
|
||||||
|
ENFILE Errno = 41 /* File table overflow */
|
||||||
|
ENOBUFS Errno = 42 /* No buffer space available */
|
||||||
|
ENODEV Errno = 43 /* No such device */
|
||||||
|
ENOENT Errno = 44 /* No such file or directory */
|
||||||
|
ENOEXEC Errno = 45 /* Exec format error */
|
||||||
|
ENOLCK Errno = 46 /* No record locks available */
|
||||||
|
ENOLINK Errno = 47 /* The link has been severed */
|
||||||
|
ENOMEM Errno = 48 /* Out of memory */
|
||||||
|
ENOMSG Errno = 49 /* No message of desired type */
|
||||||
|
ENOPROTOOPT Errno = 50 /* Protocol not available */
|
||||||
|
ENOSPC Errno = 51 /* No space left on device */
|
||||||
|
ENOSYS Errno = 52 /* Function not implemented */
|
||||||
|
ENOTCONN Errno = 53 /* Socket is not connected */
|
||||||
|
ENOTDIR Errno = 54 /* Not a directory */
|
||||||
|
ENOTEMPTY Errno = 55 /* Directory not empty */
|
||||||
|
ENOTSOCK Errno = 57 /* Socket operation on non-socket */
|
||||||
|
ESOCKTNOSUPPORT Errno = 58 /* Socket type not supported */
|
||||||
|
EOPNOTSUPP Errno = 58 /* Operation not supported on transport endpoint */
|
||||||
|
ENOTSUP Errno = EOPNOTSUPP /* Not supported */
|
||||||
|
ENOTTY Errno = 59 /* Not a typewriter */
|
||||||
|
ENXIO Errno = 60 /* No such device or address */
|
||||||
|
EOVERFLOW Errno = 61 /* Value too large for defined data type */
|
||||||
|
EPERM Errno = 63 /* Operation not permitted */
|
||||||
|
EPIPE Errno = 64 /* Broken pipe */
|
||||||
|
EPROTO Errno = 65 /* Protocol error */
|
||||||
|
EPROTONOSUPPORT Errno = 66 /* Unknown protocol */
|
||||||
|
EPROTOTYPE Errno = 67 /* Protocol wrong type for socket */
|
||||||
|
ERANGE Errno = 68 /* Math result not representable */
|
||||||
|
EROFS Errno = 69 /* Read-only file system */
|
||||||
|
ESPIPE Errno = 70 /* Illegal seek */
|
||||||
|
ESRCH Errno = 71 /* No such process */
|
||||||
|
ESTALE Errno = 72
|
||||||
|
ETIMEDOUT Errno = 73 /* Connection timed out */
|
||||||
|
EXDEV Errno = 75 /* Cross-device link */
|
||||||
|
ENOTCAPABLE Errno = 76 /* Extension: Capabilities insufficient. */
|
||||||
|
)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build baremetal nintendoswitch wasi
|
// +build baremetal nintendoswitch
|
||||||
|
|
||||||
package syscall
|
package syscall
|
||||||
|
|
||||||
|
|
38
testdata/libc/filesystem.go
предоставленный
Обычный файл
38
testdata/libc/filesystem.go
предоставленный
Обычный файл
|
@ -0,0 +1,38 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
_, err := os.Open("non-exist")
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
panic("should be non exist error")
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open("testdata/libc/filesystem.txt")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// read after close: error should be returned
|
||||||
|
_, err := f.Read(make([]byte, 10))
|
||||||
|
if err == nil {
|
||||||
|
panic("error expected for reading after closing files")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(f)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
print(string(data))
|
||||||
|
|
||||||
|
}
|
6
testdata/libc/filesystem.txt
предоставленный
Обычный файл
6
testdata/libc/filesystem.txt
предоставленный
Обычный файл
|
@ -0,0 +1,6 @@
|
||||||
|
abcdefg
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
4
|
||||||
|
5
|
Загрузка…
Создание таблицы
Сослаться в новой задаче