From 14064533507b0734a2903eb9aaf5a4b6cdcfec25 Mon Sep 17 00:00:00 2001 From: Takeshi Yoneda Date: Thu, 18 Mar 2021 12:53:32 +0900 Subject: [PATCH] WASI & darwin: support basic file io based on libc Signed-off-by: Takeshi Yoneda --- main_test.go | 15 ++-- src/os/errors.go | 42 +++++++++- src/os/file.go | 71 +++-------------- src/os/file_go_116.go | 81 +++++++++++++++++++ src/os/file_go_other.go | 46 +++++++++++ src/os/file_other.go | 2 +- src/os/file_unix.go | 6 +- src/runtime/runtime_wasm_wasi.go | 5 ++ src/syscall/errno_other.go | 5 ++ src/syscall/syscall_libc.go | 37 +++++++-- src/syscall/syscall_libc_darwin.go | 17 ++++ src/syscall/syscall_libc_nintendoswitch.go | 8 +- src/syscall/syscall_libc_wasi.go | 93 +++++++++++++++++++++- src/syscall/tables_baremetal.go | 2 +- testdata/libc/filesystem.go | 38 +++++++++ testdata/libc/filesystem.txt | 6 ++ 16 files changed, 390 insertions(+), 84 deletions(-) create mode 100644 src/os/file_go_116.go create mode 100644 src/os/file_go_other.go create mode 100644 src/syscall/errno_other.go create mode 100644 testdata/libc/filesystem.go create mode 100644 testdata/libc/filesystem.txt diff --git a/main_test.go b/main_test.go index 31ee8c5d..de09f5b4 100644 --- a/main_test.go +++ b/main_test.go @@ -59,7 +59,10 @@ func TestCompiler(t *testing.T) { t.Run("Host", func(t *testing.T) { runPlatTests("", matches, t) 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) { 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 t.Run(filepath.Base(path), func(t *testing.T) { 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) } -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. txtpath := path[:len(path)-3] + ".txt" 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) } else { 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" { diff --git a/src/os/errors.go b/src/os/errors.go index 9b346554..fc8693eb 100644 --- a/src/os/errors.go +++ b/src/os/errors.go @@ -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 +} diff --git a/src/os/file.go b/src/os/file.go index a059a421..99977fa4 100644 --- a/src/os/file.go +++ b/src/os/file.go @@ -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 diff --git a/src/os/file_go_116.go b/src/os/file_go_116.go new file mode 100644 index 00000000..6627c5df --- /dev/null +++ b/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 +} diff --git a/src/os/file_go_other.go b/src/os/file_go_other.go new file mode 100644 index 00000000..d8d680ff --- /dev/null +++ b/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 +} diff --git a/src/os/file_other.go b/src/os/file_other.go index be98cc69..af885edd 100644 --- a/src/os/file_other.go +++ b/src/os/file_other.go @@ -1,4 +1,4 @@ -// +build baremetal wasm +// +build baremetal wasm,!wasi package os diff --git a/src/os/file_unix.go b/src/os/file_unix.go index 84d738b0..bb77c85a 100644 --- a/src/os/file_unix.go +++ b/src/os/file_unix.go @@ -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 } diff --git a/src/runtime/runtime_wasm_wasi.go b/src/runtime/runtime_wasm_wasi.go index 51cb75cf..97659a10 100644 --- a/src/runtime/runtime_wasm_wasi.go +++ b/src/runtime/runtime_wasm_wasi.go @@ -8,11 +8,16 @@ import ( type timeUnit int64 +// libc constructors +//export __wasm_call_ctors +func __wasm_call_ctors() + //export _start func _start() { // These need to be initialized early so that the heap can be initialized. heapStart = uintptr(unsafe.Pointer(&heapStartSymbol)) heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize) + __wasm_call_ctors() run() } diff --git a/src/syscall/errno_other.go b/src/syscall/errno_other.go new file mode 100644 index 00000000..c18aa6cd --- /dev/null +++ b/src/syscall/errno_other.go @@ -0,0 +1,5 @@ +// +build !wasi,!darwin + +package syscall + +func (e Errno) Is(target error) bool { return false } diff --git a/src/syscall/syscall_libc.go b/src/syscall/syscall_libc.go index cf9efa0c..2612938d 100644 --- a/src/syscall/syscall_libc.go +++ b/src/syscall/syscall_libc.go @@ -13,12 +13,15 @@ type sliceHeader struct { } 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) { buf, count := splitSlice(p) - n = libc_write(int32(fd), buf, uint(count)) + n = libc_write(fd, buf, uint(count)) if n < 0 { 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) { - 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) { return 0, ENOSYS // TODO } -func Open(path string, mode int, perm uint32) (fd int, err error) { - return 0, ENOSYS // TODO +func Open(path string, flag int, mode uint32) (fd int, err error) { + 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) { @@ -78,8 +91,20 @@ func splitSlice(p []byte) (buf *byte, len uintptr) { // ssize_t write(int fd, const void *buf, size_t count) //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); //export getenv 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 diff --git a/src/syscall/syscall_libc_darwin.go b/src/syscall/syscall_libc_darwin.go index e2e7f408..308a4a0c 100644 --- a/src/syscall/syscall_libc_darwin.go +++ b/src/syscall/syscall_libc_darwin.go @@ -1,3 +1,5 @@ +// +build darwin + package syscall // This file defines errno and constants to match the darwin libsystem ABI. @@ -22,10 +24,25 @@ func getErrno() Errno { 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 ( + EPERM Errno = 0x1 ENOENT Errno = 0x2 + EACCES Errno = 0xd EEXIST Errno = 0x11 EINTR Errno = 0x4 + ENOTDIR Errno = 0x14 EMFILE Errno = 0x18 EAGAIN Errno = 0x23 ETIMEDOUT Errno = 0x3c diff --git a/src/syscall/syscall_libc_nintendoswitch.go b/src/syscall/syscall_libc_nintendoswitch.go index 8a81de1e..2ff8de04 100644 --- a/src/syscall/syscall_libc_nintendoswitch.go +++ b/src/syscall/syscall_libc_nintendoswitch.go @@ -2,8 +2,6 @@ package syscall -import "errors" - // A Signal is a number describing a process signal. // It implements the os.Signal interface. type Signal int @@ -32,7 +30,6 @@ const ( O_RDWR = 2 O_CREAT = 0100 - O_CREATE = O_CREAT O_TRUNC = 01000 O_APPEND = 02000 O_EXCL = 0200 @@ -41,8 +38,9 @@ const ( O_CLOEXEC = 0 ) -var dummyError = errors.New("unknown syscall error") +//go:extern errno +var libcErrno uintptr func getErrno() error { - return dummyError + return Errno(libcErrno) } diff --git a/src/syscall/syscall_libc_wasi.go b/src/syscall/syscall_libc_wasi.go index e6bed08c..7a71c35f 100644 --- a/src/syscall/syscall_libc_wasi.go +++ b/src/syscall/syscall_libc_wasi.go @@ -33,7 +33,6 @@ const ( O_RDWR = O_RDONLY | O_WRONLY O_CREAT = __WASI_OFLAGS_CREAT << 12 - O_CREATE = O_CREAT O_TRUNC = __WASI_OFLAGS_TRUNC << 12 O_APPEND = __WASI_FDFLAGS_APPEND O_EXCL = __WASI_OFLAGS_EXCL << 12 @@ -48,3 +47,95 @@ var libcErrno uintptr func getErrno() error { 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. */ +) diff --git a/src/syscall/tables_baremetal.go b/src/syscall/tables_baremetal.go index 9677d77a..47a536bf 100644 --- a/src/syscall/tables_baremetal.go +++ b/src/syscall/tables_baremetal.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build baremetal nintendoswitch wasi +// +build baremetal nintendoswitch package syscall diff --git a/testdata/libc/filesystem.go b/testdata/libc/filesystem.go new file mode 100644 index 00000000..d196b9c8 --- /dev/null +++ b/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)) + +} diff --git a/testdata/libc/filesystem.txt b/testdata/libc/filesystem.txt new file mode 100644 index 00000000..83612330 --- /dev/null +++ b/testdata/libc/filesystem.txt @@ -0,0 +1,6 @@ +abcdefg +1 +2 +3 +4 +5