mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-09 05:23:40 +00:00
WASI & darwin: support basic file io based on libc
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
This commit is contained in:
committed by
Ron Evans
parent
6d3c11627c
commit
1406453350
+39
-3
@@ -2,6 +2,7 @@ package os
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -17,9 +18,8 @@ var (
|
||||
ErrExist = errors.New("file exists")
|
||||
)
|
||||
|
||||
func IsPermission(err error) bool {
|
||||
return err == ErrPermission
|
||||
}
|
||||
// The following code is copied from the official implementation.
|
||||
// https://github.com/golang/go/blob/4ce6a8e89668b87dce67e2f55802903d6eb9110a/src/os/error.go#L65-L104
|
||||
|
||||
func NewSyscallError(syscall string, err error) error {
|
||||
if err == nil {
|
||||
@@ -37,3 +37,39 @@ type SyscallError struct {
|
||||
func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Err.Error() }
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
+10
-61
@@ -6,6 +6,7 @@
|
||||
package os
|
||||
|
||||
import (
|
||||
"io"
|
||||
"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.
|
||||
func (f *File) Read(b []byte) (n int, err error) {
|
||||
n, err = f.handle.Read(b)
|
||||
if err != nil {
|
||||
if err != nil && err != io.EOF {
|
||||
err = &PathError{"read", f.name, err}
|
||||
}
|
||||
return
|
||||
@@ -155,59 +156,17 @@ func (e *PathError) Error() string {
|
||||
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 (
|
||||
// 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
|
||||
O_RDONLY int = syscall.O_RDONLY
|
||||
O_WRONLY int = syscall.O_WRONLY
|
||||
O_RDWR int = syscall.O_RDWR
|
||||
O_APPEND int = syscall.O_APPEND
|
||||
O_CREATE int = syscall.O_CREAT
|
||||
O_EXCL int = syscall.O_EXCL
|
||||
O_SYNC int = syscall.O_SYNC
|
||||
O_TRUNC int = syscall.O_TRUNC
|
||||
)
|
||||
|
||||
// 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
|
||||
func Stat(name string) (FileInfo, error) {
|
||||
return nil, &PathError{"stat", name, ErrNotImplemented}
|
||||
@@ -233,16 +192,6 @@ func TempDir() string {
|
||||
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
|
||||
func Getpid() int {
|
||||
return 1
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
+5
-1
@@ -1,8 +1,9 @@
|
||||
// +build darwin linux,!baremetal,!wasi freebsd,!baremetal
|
||||
// +build darwin linux,!baremetal freebsd,!baremetal
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"io"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
@@ -77,6 +78,9 @@ type unixFileHandle uintptr
|
||||
func (f unixFileHandle) Read(b []byte) (n int, err error) {
|
||||
n, err = syscall.Read(int(f), b)
|
||||
err = handleSyscallError(err)
|
||||
if n == 0 && err == nil {
|
||||
err = io.EOF
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user