aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/os
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2020-12-23 09:57:37 -0800
committerIan Lance Taylor <iant@golang.org>2020-12-30 15:13:24 -0800
commitcfcbb4227fb20191e04eb8d7766ae6202f526afd (patch)
treee2effea96f6f204451779f044415c2385e45042b /libgo/go/os
parent0696141107d61483f38482b941549959a0d7f613 (diff)
downloadgcc-cfcbb4227fb20191e04eb8d7766ae6202f526afd.zip
gcc-cfcbb4227fb20191e04eb8d7766ae6202f526afd.tar.gz
gcc-cfcbb4227fb20191e04eb8d7766ae6202f526afd.tar.bz2
libgo: update to Go1.16beta1 release
This does not yet include support for the //go:embed directive added in this release. * Makefile.am (check-runtime): Don't create check-runtime-dir. (mostlyclean-local): Don't remove check-runtime-dir. (check-go-tool, check-vet): Copy in go.mod and modules.txt. (check-cgo-test, check-carchive-test): Add go.mod file. * Makefile.in: Regenerate. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/280172
Diffstat (limited to 'libgo/go/os')
-rw-r--r--libgo/go/os/dir.go80
-rw-r--r--libgo/go/os/dir_gccgo.go163
-rw-r--r--libgo/go/os/dir_gccgo_c.c58
-rw-r--r--libgo/go/os/dir_largefile.go4
-rw-r--r--libgo/go/os/dir_plan9.go42
-rw-r--r--libgo/go/os/dir_regfile.go4
-rw-r--r--libgo/go/os/error.go47
-rw-r--r--libgo/go/os/error_test.go64
-rw-r--r--libgo/go/os/error_unix_test.go11
-rw-r--r--libgo/go/os/error_windows_test.go11
-rw-r--r--libgo/go/os/example_test.go101
-rw-r--r--libgo/go/os/exec.go4
-rw-r--r--libgo/go/os/exec/exec_plan9.go4
-rw-r--r--libgo/go/os/exec/exec_test.go25
-rw-r--r--libgo/go/os/exec/exec_unix.go4
-rw-r--r--libgo/go/os/exec/exec_windows.go4
-rw-r--r--libgo/go/os/exec/lp_plan9.go3
-rw-r--r--libgo/go/os/exec/lp_unix.go3
-rw-r--r--libgo/go/os/exec/lp_unix_test.go3
-rw-r--r--libgo/go/os/exec/lp_windows.go5
-rw-r--r--libgo/go/os/exec/read3.go8
-rw-r--r--libgo/go/os/exec_plan9.go5
-rw-r--r--libgo/go/os/exec_posix.go2
-rw-r--r--libgo/go/os/exec_unix.go6
-rw-r--r--libgo/go/os/exec_unix_test.go29
-rw-r--r--libgo/go/os/exec_windows.go2
-rw-r--r--libgo/go/os/executable_dragonfly.go12
-rw-r--r--libgo/go/os/executable_freebsd.go33
-rw-r--r--libgo/go/os/executable_procfs.go4
-rw-r--r--libgo/go/os/executable_sysctl.go35
-rw-r--r--libgo/go/os/export_test.go2
-rw-r--r--libgo/go/os/fifo_test.go3
-rw-r--r--libgo/go/os/file.go113
-rw-r--r--libgo/go/os/file_plan9.go63
-rw-r--r--libgo/go/os/file_posix.go40
-rw-r--r--libgo/go/os/file_unix.go130
-rw-r--r--libgo/go/os/getwd.go19
-rw-r--r--libgo/go/os/getwd_darwin.go15
-rw-r--r--libgo/go/os/os_test.go252
-rw-r--r--libgo/go/os/os_unix_test.go4
-rw-r--r--libgo/go/os/os_windows_test.go40
-rw-r--r--libgo/go/os/path.go2
-rw-r--r--libgo/go/os/path_test.go6
-rw-r--r--libgo/go/os/path_windows_test.go3
-rw-r--r--libgo/go/os/pipe2_illumos.go25
-rw-r--r--libgo/go/os/pipe_bsd.go2
-rw-r--r--libgo/go/os/pipe_test.go20
-rw-r--r--libgo/go/os/proc.go8
-rw-r--r--libgo/go/os/read_test.go131
-rw-r--r--libgo/go/os/readfrom_linux_test.go4
-rw-r--r--libgo/go/os/removeall_at.go14
-rw-r--r--libgo/go/os/removeall_noat.go2
-rw-r--r--libgo/go/os/removeall_test.go29
-rw-r--r--libgo/go/os/signal/example_unix_test.go47
-rw-r--r--libgo/go/os/signal/signal.go75
-rw-r--r--libgo/go/os/signal/signal_cgo_test.go3
-rw-r--r--libgo/go/os/signal/signal_test.go165
-rw-r--r--libgo/go/os/stat_plan9.go8
-rw-r--r--libgo/go/os/stat_test.go28
-rw-r--r--libgo/go/os/stat_unix.go14
-rw-r--r--libgo/go/os/tempfile.go118
-rw-r--r--libgo/go/os/tempfile_test.go193
-rw-r--r--libgo/go/os/testdata/hello1
-rw-r--r--libgo/go/os/timeout_test.go7
-rw-r--r--libgo/go/os/types.go86
-rw-r--r--libgo/go/os/types_windows.go6
-rw-r--r--libgo/go/os/user/lookup_plan9.go3
67 files changed, 1896 insertions, 561 deletions
diff --git a/libgo/go/os/dir.go b/libgo/go/os/dir.go
index 1d7ced8..5306bcb 100644
--- a/libgo/go/os/dir.go
+++ b/libgo/go/os/dir.go
@@ -4,6 +4,19 @@
package os
+import (
+ "io/fs"
+ "sort"
+)
+
+type readdirMode int
+
+const (
+ readdirName readdirMode = iota
+ readdirDirEntry
+ readdirFileInfo
+)
+
// Readdir reads the contents of the directory associated with file and
// returns a slice of up to n FileInfo values, as would be returned
// by Lstat, in directory order. Subsequent calls on the same file will yield
@@ -19,11 +32,20 @@ package os
// nil error. If it encounters an error before the end of the
// directory, Readdir returns the FileInfo read until that point
// and a non-nil error.
+//
+// Most clients are better served by the more efficient ReadDir method.
func (f *File) Readdir(n int) ([]FileInfo, error) {
if f == nil {
return nil, ErrInvalid
}
- return f.readdir(n)
+ _, _, infos, err := f.readdir(n, readdirFileInfo)
+ if infos == nil {
+ // Readdir has historically always returned a non-nil empty slice, never nil,
+ // even on error (except misuse with nil receiver above).
+ // Keep it that way to avoid breaking overly sensitive callers.
+ infos = []FileInfo{}
+ }
+ return infos, err
}
// Readdirnames reads the contents of the directory associated with file
@@ -45,5 +67,59 @@ func (f *File) Readdirnames(n int) (names []string, err error) {
if f == nil {
return nil, ErrInvalid
}
- return f.readdirnames(n)
+ names, _, _, err = f.readdir(n, readdirName)
+ if names == nil {
+ // Readdirnames has historically always returned a non-nil empty slice, never nil,
+ // even on error (except misuse with nil receiver above).
+ // Keep it that way to avoid breaking overly sensitive callers.
+ names = []string{}
+ }
+ return names, err
+}
+
+// A DirEntry is an entry read from a directory
+// (using the ReadDir function or a File's ReadDir method).
+type DirEntry = fs.DirEntry
+
+// ReadDir reads the contents of the directory associated with the file f
+// and returns a slice of DirEntry values in directory order.
+// Subsequent calls on the same file will yield later DirEntry records in the directory.
+//
+// If n > 0, ReadDir returns at most n DirEntry records.
+// In this case, if ReadDir returns an empty slice, it will return an error explaining why.
+// At the end of a directory, the error is io.EOF.
+//
+// If n <= 0, ReadDir returns all the DirEntry records remaining in the directory.
+// When it succeeds, it returns a nil error (not io.EOF).
+func (f *File) ReadDir(n int) ([]DirEntry, error) {
+ if f == nil {
+ return nil, ErrInvalid
+ }
+ _, dirents, _, err := f.readdir(n, readdirDirEntry)
+ if dirents == nil {
+ // Match Readdir and Readdirnames: don't return nil slices.
+ dirents = []DirEntry{}
+ }
+ return dirents, err
+}
+
+// testingForceReadDirLstat forces ReadDir to call Lstat, for testing that code path.
+// This can be difficult to provoke on some Unix systems otherwise.
+var testingForceReadDirLstat bool
+
+// ReadDir reads the named directory,
+// returning all its directory entries sorted by filename.
+// If an error occurs reading the directory,
+// ReadDir returns the entries it was able to read before the error,
+// along with the error.
+func ReadDir(name string) ([]DirEntry, error) {
+ f, err := Open(name)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+
+ dirs, err := f.ReadDir(-1)
+ sort.Slice(dirs, func(i, j int) bool { return dirs[i].Name() < dirs[j].Name() })
+ return dirs, err
}
diff --git a/libgo/go/os/dir_gccgo.go b/libgo/go/os/dir_gccgo.go
index 171dde5..31ee262 100644
--- a/libgo/go/os/dir_gccgo.go
+++ b/libgo/go/os/dir_gccgo.go
@@ -5,9 +5,8 @@
package os
import (
+ "internal/poll"
"io"
- "runtime"
- "sync/atomic"
"syscall"
"unsafe"
)
@@ -16,8 +15,7 @@ import (
//extern pathconf
func libc_pathconf(*byte, int32) int
-//extern dup
-func libc_dup(int32) int32
+func direntType(*syscall.Dirent) byte
func clen(n []byte) int {
for i := 0; i < len(n); i++ {
@@ -28,95 +26,118 @@ func clen(n []byte) int {
return len(n)
}
-var nameMax int32
-
-func (file *File) readdirnames(n int) (names []string, err error) {
- if file.dirinfo == nil {
- p, err := syscall.BytePtrFromString(file.name)
+func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) {
+ // If this file has no dirinfo, create one.
+ if f.dirinfo == nil {
+ fd, call, err := poll.DupCloseOnExec(int(f.pfd.Sysfd))
if err != nil {
- return nil, err
- }
-
- elen := int(atomic.LoadInt32(&nameMax))
- if elen == 0 {
- syscall.Entersyscall()
- plen := libc_pathconf(p, syscall.PC_NAME_MAX)
- syscall.Exitsyscall()
- if plen < 1024 {
- plen = 1024
- }
- var dummy syscall.Dirent
- elen = int(unsafe.Offsetof(dummy.Name)) + plen + 1
- atomic.StoreInt32(&nameMax, int32(elen))
+ return nil, nil, nil, NewSyscallError(call, err)
}
syscall.Entersyscall()
- fd := libc_dup(int32(file.pfd.Sysfd))
+ r := libc_fdopendir(int32(fd))
errno := syscall.GetErrno()
syscall.Exitsyscall()
- if fd < 0 {
- return nil, &PathError{"dup", file.name, errno}
- }
-
- syscall.Entersyscall()
- r := libc_fdopendir(fd)
- errno = syscall.GetErrno()
- syscall.Exitsyscall()
if r == nil {
- return nil, &PathError{"fdopendir", file.name, errno}
+ return nil, nil, nil, &PathError{"fdopendir", f.name, errno}
}
- file.dirinfo = new(dirInfo)
- file.dirinfo.buf = make([]byte, elen)
- file.dirinfo.dir = r
+ f.dirinfo = &dirInfo{r}
}
-
- entryDirent := (*syscall.Dirent)(unsafe.Pointer(&file.dirinfo.buf[0]))
-
- size := n
- if size <= 0 {
- size = 100
+ dir := f.dirinfo.dir
+
+ // Change the meaning of n for the implementation below.
+ //
+ // The n above was for the public interface of "if n <= 0,
+ // Readdir returns all the FileInfo from the directory in a
+ // single slice".
+ //
+ // But below, we use only negative to mean looping until the
+ // end and positive to mean bounded, with positive
+ // terminating at 0.
+ if n == 0 {
n = -1
}
- names = make([]string, 0, size) // Empty with room to grow.
-
for n != 0 {
- var dirent *syscall.Dirent
- pr := &dirent
syscall.Entersyscall()
- i := libc_readdir_r(file.dirinfo.dir, entryDirent, pr)
+ syscall.SetErrno(0)
+ dirent := libc_readdir(dir)
+ errno := syscall.GetErrno()
syscall.Exitsyscall()
- // On AIX when readdir_r hits EOF it sets dirent to nil and returns 9.
- // https://www.ibm.com/support/knowledgecenter/ssw_aix_71/com.ibm.aix.basetrf2/readdir_r.htm
- if runtime.GOOS == "aix" && i == 9 && dirent == nil {
- break
- }
- if i != 0 {
- return names, NewSyscallError("readdir_r", i)
- }
+
if dirent == nil {
+ if errno != 0 {
+ return names, dirents, infos, &PathError{Op: "readdir", Path: f.name, Err: errno}
+ }
break // EOF
}
- bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
- var name = string(bytes[0:clen(bytes[:])])
- if name == "." || name == ".." { // Useless names
+
+ // In some cases the actual name can be longer than
+ // the Name field.
+ name := (*[1 << 16]byte)(unsafe.Pointer(&dirent.Name[0]))[:]
+ for i, c := range name {
+ if c == 0 {
+ name = name[:i]
+ break
+ }
+ }
+ // Check for useless names before allocating a string.
+ if (len(name) == 1 && name[0] == '.') || (len(name) == 2 && name[0] == '.' && name[1] == '.') {
continue
}
- names = append(names, name)
- n--
- }
- if n >= 0 && len(names) == 0 {
- return names, io.EOF
+ if n > 0 { // see 'n == 0' comment above
+ n--
+ }
+ if mode == readdirName {
+ names = append(names, string(name))
+ } else if mode == readdirDirEntry {
+ var typ FileMode
+ switch direntType(dirent) {
+ case 'B':
+ typ = ModeDevice
+ case 'C':
+ typ = ModeDevice | ModeCharDevice
+ case 'D':
+ typ = ModeDir
+ case 'F':
+ typ = ModeNamedPipe
+ case 'L':
+ typ = ModeSymlink
+ case 'R':
+ typ = 0
+ case 'S':
+ typ = ModeSocket
+ case 'U':
+ typ = ^FileMode(0)
+ }
+
+ de, err := newUnixDirent(f.name, string(name), typ)
+ if IsNotExist(err) {
+ // File disappeared between readdir and stat.
+ // Treat as if it didn't exist.
+ continue
+ }
+ if err != nil {
+ return nil, dirents, nil, err
+ }
+ dirents = append(dirents, de)
+ } else {
+ info, err := lstat(f.name + "/" + string(name))
+ if IsNotExist(err) {
+ // File disappeared between readdir + stat.
+ // Treat as if it didn't exist.
+ continue
+ }
+ if err != nil {
+ return nil, nil, infos, err
+ }
+ infos = append(infos, info)
+ }
}
- return names, nil
-}
-func (f *File) seekInvalidate() {
- if f.file.dirinfo != nil {
- syscall.Entersyscall()
- libc_closedir(f.file.dirinfo.dir)
- syscall.Exitsyscall()
- f.file.dirinfo = nil
+ if n > 0 && len(names)+len(dirents)+len(infos) == 0 {
+ return nil, nil, nil, io.EOF
}
+ return names, dirents, infos, nil
}
diff --git a/libgo/go/os/dir_gccgo_c.c b/libgo/go/os/dir_gccgo_c.c
new file mode 100644
index 0000000..66b9be1
--- /dev/null
+++ b/libgo/go/os/dir_gccgo_c.c
@@ -0,0 +1,58 @@
+/* Copyright 2020 The Go Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style
+ license that can be found in the LICENSE file. */
+
+#include <dirent.h>
+
+#include "runtime.h"
+
+unsigned char direntType (struct dirent *)
+ __asm__ (GOSYM_PREFIX "os.direntType");
+
+unsigned char
+direntType (struct dirent *p __attribute__((unused)))
+{
+#ifndef HAVE_STRUCT_DIRENT_D_TYPE
+ return 'U';
+#else
+ switch (p->d_type)
+ {
+#ifdef DT_BLK
+ case DT_BLK:
+ return 'B';
+#endif
+#ifdef DT_CHR
+ case DT_CHR:
+ return 'C';
+#endif
+#ifdef DT_DBF
+ case DT_DBF:
+ // Database record file.
+ // Treat as regular file.
+ return 'R';
+#endif
+#ifdef DT_DIR
+ case DT_DIR:
+ return 'D';
+#endif
+#ifdef DT_FIFO
+ case DT_FIFO:
+ return 'F';
+#endif
+#ifdef DT_LNK
+ case DT_LNK:
+ return 'L';
+#endif
+#ifdef DT_REG
+ case DT_REG:
+ return 'R';
+#endif
+#ifdef DT_SOCK
+ case DT_SOCK:
+ return 'S';
+#endif
+ default:
+ return 'U';
+ }
+#endif /* HAVE_DIRENT_D_TYPE */
+}
diff --git a/libgo/go/os/dir_largefile.go b/libgo/go/os/dir_largefile.go
index e7d30f8..1fc5ee0 100644
--- a/libgo/go/os/dir_largefile.go
+++ b/libgo/go/os/dir_largefile.go
@@ -11,5 +11,5 @@ package os
import "syscall"
-//extern readdir64_r
-func libc_readdir_r(*syscall.DIR, *syscall.Dirent, **syscall.Dirent) syscall.Errno
+//extern readdir64
+func libc_readdir(*syscall.DIR) *syscall.Dirent
diff --git a/libgo/go/os/dir_plan9.go b/libgo/go/os/dir_plan9.go
index 8195c02..8f6b0d6 100644
--- a/libgo/go/os/dir_plan9.go
+++ b/libgo/go/os/dir_plan9.go
@@ -9,7 +9,7 @@ import (
"syscall"
)
-func (file *File) readdir(n int) ([]FileInfo, error) {
+func (file *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) {
// If this file has no dirinfo, create one.
if file.dirinfo == nil {
file.dirinfo = new(dirInfo)
@@ -20,7 +20,6 @@ func (file *File) readdir(n int) ([]FileInfo, error) {
size = 100
n = -1
}
- fi := make([]FileInfo, 0, size) // Empty with room to grow.
for n != 0 {
// Refill the buffer if necessary.
if d.bufp >= d.nbuf {
@@ -33,10 +32,10 @@ func (file *File) readdir(n int) ([]FileInfo, error) {
if err == io.EOF {
break
}
- return fi, &PathError{"readdir", file.name, err}
+ return names, dirents, infos, &PathError{Op: "readdir", Path: file.name, Err: err}
}
if nb < syscall.STATFIXLEN {
- return fi, &PathError{"readdir", file.name, syscall.ErrShortStat}
+ return names, dirents, infos, &PathError{Op: "readdir", Path: file.name, Err: syscall.ErrShortStat}
}
}
@@ -44,30 +43,39 @@ func (file *File) readdir(n int) ([]FileInfo, error) {
b := d.buf[d.bufp:]
m := int(uint16(b[0])|uint16(b[1])<<8) + 2
if m < syscall.STATFIXLEN {
- return fi, &PathError{"readdir", file.name, syscall.ErrShortStat}
+ return names, dirents, infos, &PathError{Op: "readdir", Path: file.name, Err: syscall.ErrShortStat}
}
dir, err := syscall.UnmarshalDir(b[:m])
if err != nil {
- return fi, &PathError{"readdir", file.name, err}
+ return names, dirents, infos, &PathError{Op: "readdir", Path: file.name, Err: err}
}
- fi = append(fi, fileInfoFromStat(dir))
+ if mode == readdirName {
+ names = append(names, dir.Name)
+ } else {
+ f := fileInfoFromStat(dir)
+ if mode == readdirDirEntry {
+ dirents = append(dirents, dirEntry{f})
+ } else {
+ infos = append(infos, f)
+ }
+ }
d.bufp += m
n--
}
- if n >= 0 && len(fi) == 0 {
- return fi, io.EOF
+ if n > 0 && len(names)+len(dirents)+len(infos) == 0 {
+ return nil, nil, nil, io.EOF
}
- return fi, nil
+ return names, dirents, infos, nil
}
-func (file *File) readdirnames(n int) (names []string, err error) {
- fi, err := file.Readdir(n)
- names = make([]string, len(fi))
- for i := range fi {
- names[i] = fi[i].Name()
- }
- return
+type dirEntry struct {
+ fs *fileStat
}
+
+func (de dirEntry) Name() string { return de.fs.Name() }
+func (de dirEntry) IsDir() bool { return de.fs.IsDir() }
+func (de dirEntry) Type() FileMode { return de.fs.Mode().Type() }
+func (de dirEntry) Info() (FileInfo, error) { return de.fs, nil }
diff --git a/libgo/go/os/dir_regfile.go b/libgo/go/os/dir_regfile.go
index b2e6623..3a0cd64 100644
--- a/libgo/go/os/dir_regfile.go
+++ b/libgo/go/os/dir_regfile.go
@@ -15,5 +15,5 @@ package os
import "syscall"
-//extern-sysinfo readdir_r
-func libc_readdir_r(*syscall.DIR, *syscall.Dirent, **syscall.Dirent) syscall.Errno
+//extern-sysinfo readdir
+func libc_readdir(*syscall.DIR) *syscall.Dirent
diff --git a/libgo/go/os/error.go b/libgo/go/os/error.go
index 875cc97..704a6fb 100644
--- a/libgo/go/os/error.go
+++ b/libgo/go/os/error.go
@@ -7,6 +7,7 @@ package os
import (
"internal/oserror"
"internal/poll"
+ "io/fs"
)
// Portable analogs of some common system call errors.
@@ -16,20 +17,17 @@ import (
var (
// ErrInvalid indicates an invalid argument.
// Methods on File will return this error when the receiver is nil.
- ErrInvalid = errInvalid() // "invalid argument"
+ ErrInvalid = fs.ErrInvalid // "invalid argument"
+
+ ErrPermission = fs.ErrPermission // "permission denied"
+ ErrExist = fs.ErrExist // "file already exists"
+ ErrNotExist = fs.ErrNotExist // "file does not exist"
+ ErrClosed = fs.ErrClosed // "file already closed"
- ErrPermission = errPermission() // "permission denied"
- ErrExist = errExist() // "file already exists"
- ErrNotExist = errNotExist() // "file does not exist"
- ErrClosed = errClosed() // "file already closed"
ErrNoDeadline = errNoDeadline() // "file type does not support deadline"
ErrDeadlineExceeded = errDeadlineExceeded() // "i/o timeout"
)
-func errInvalid() error { return oserror.ErrInvalid }
-func errPermission() error { return oserror.ErrPermission }
-func errExist() error { return oserror.ErrExist }
-func errNotExist() error { return oserror.ErrNotExist }
func errClosed() error { return oserror.ErrClosed }
func errNoDeadline() error { return poll.ErrNoDeadline }
@@ -47,21 +45,7 @@ type timeout interface {
}
// PathError records an error and the operation and file path that caused it.
-type PathError struct {
- Op string
- Path string
- Err error
-}
-
-func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }
-
-func (e *PathError) Unwrap() error { return e.Err }
-
-// Timeout reports whether this error represents a timeout.
-func (e *PathError) Timeout() bool {
- t, ok := e.Err.(timeout)
- return ok && t.Timeout()
-}
+type PathError = fs.PathError
// SyscallError records an error from a specific system call.
type SyscallError struct {
@@ -92,6 +76,9 @@ func NewSyscallError(syscall string, err error) error {
// IsExist returns a boolean indicating whether the error is known to report
// that a file or directory already exists. It is satisfied by ErrExist as
// well as some syscall errors.
+//
+// This function predates errors.Is. It only supports errors returned by
+// the os package. New code should use errors.Is(err, os.ErrExist).
func IsExist(err error) bool {
return underlyingErrorIs(err, ErrExist)
}
@@ -99,6 +86,9 @@ func IsExist(err error) bool {
// IsNotExist returns a boolean indicating whether the error is known to
// report that a file or directory does not exist. It is satisfied by
// ErrNotExist as well as some syscall errors.
+//
+// This function predates errors.Is. It only supports errors returned by
+// the os package. New code should use errors.Is(err, os.ErrNotExist).
func IsNotExist(err error) bool {
return underlyingErrorIs(err, ErrNotExist)
}
@@ -106,12 +96,21 @@ func IsNotExist(err error) bool {
// IsPermission returns a boolean indicating whether the error is known to
// report that permission is denied. It is satisfied by ErrPermission as well
// as some syscall errors.
+//
+// This function predates errors.Is. It only supports errors returned by
+// the os package. New code should use errors.Is(err, os.ErrPermission).
func IsPermission(err error) bool {
return underlyingErrorIs(err, ErrPermission)
}
// IsTimeout returns a boolean indicating whether the error is known
// to report that a timeout occurred.
+//
+// This function predates errors.Is, and the notion of whether an
+// error indicates a timeout can be ambiguous. For example, the Unix
+// error EWOULDBLOCK sometimes indicates a timeout and sometimes does not.
+// New code should use errors.Is with a value appropriate to the call
+// returning the error, such as os.ErrDeadlineExceeded.
func IsTimeout(err error) bool {
terr, ok := underlyingError(err).(timeout)
return ok && terr.Timeout()
diff --git a/libgo/go/os/error_test.go b/libgo/go/os/error_test.go
index 3d92157..6264ccc 100644
--- a/libgo/go/os/error_test.go
+++ b/libgo/go/os/error_test.go
@@ -7,14 +7,14 @@ package os_test
import (
"errors"
"fmt"
- "io/ioutil"
+ "io/fs"
"os"
"path/filepath"
"testing"
)
func TestErrIsExist(t *testing.T) {
- f, err := ioutil.TempFile("", "_Go_ErrIsExist")
+ f, err := os.CreateTemp("", "_Go_ErrIsExist")
if err != nil {
t.Fatalf("open ErrIsExist tempfile: %s", err)
return
@@ -27,7 +27,7 @@ func TestErrIsExist(t *testing.T) {
t.Fatal("Open should have failed")
return
}
- if s := checkErrorPredicate("os.IsExist", os.IsExist, err, os.ErrExist); s != "" {
+ if s := checkErrorPredicate("os.IsExist", os.IsExist, err, fs.ErrExist); s != "" {
t.Fatal(s)
return
}
@@ -39,7 +39,7 @@ func testErrNotExist(name string) string {
f.Close()
return "Open should have failed"
}
- if s := checkErrorPredicate("os.IsNotExist", os.IsNotExist, err, os.ErrNotExist); s != "" {
+ if s := checkErrorPredicate("os.IsNotExist", os.IsNotExist, err, fs.ErrNotExist); s != "" {
return s
}
@@ -47,14 +47,14 @@ func testErrNotExist(name string) string {
if err == nil {
return "Chdir should have failed"
}
- if s := checkErrorPredicate("os.IsNotExist", os.IsNotExist, err, os.ErrNotExist); s != "" {
+ if s := checkErrorPredicate("os.IsNotExist", os.IsNotExist, err, fs.ErrNotExist); s != "" {
return s
}
return ""
}
func TestErrIsNotExist(t *testing.T) {
- tmpDir, err := ioutil.TempDir("", "_Go_ErrIsNotExist")
+ tmpDir, err := os.MkdirTemp("", "_Go_ErrIsNotExist")
if err != nil {
t.Fatalf("create ErrIsNotExist tempdir: %s", err)
return
@@ -91,18 +91,18 @@ type isExistTest struct {
}
var isExistTests = []isExistTest{
- {&os.PathError{Err: os.ErrInvalid}, false, false},
- {&os.PathError{Err: os.ErrPermission}, false, false},
- {&os.PathError{Err: os.ErrExist}, true, false},
- {&os.PathError{Err: os.ErrNotExist}, false, true},
- {&os.PathError{Err: os.ErrClosed}, false, false},
- {&os.LinkError{Err: os.ErrInvalid}, false, false},
- {&os.LinkError{Err: os.ErrPermission}, false, false},
- {&os.LinkError{Err: os.ErrExist}, true, false},
- {&os.LinkError{Err: os.ErrNotExist}, false, true},
- {&os.LinkError{Err: os.ErrClosed}, false, false},
- {&os.SyscallError{Err: os.ErrNotExist}, false, true},
- {&os.SyscallError{Err: os.ErrExist}, true, false},
+ {&fs.PathError{Err: fs.ErrInvalid}, false, false},
+ {&fs.PathError{Err: fs.ErrPermission}, false, false},
+ {&fs.PathError{Err: fs.ErrExist}, true, false},
+ {&fs.PathError{Err: fs.ErrNotExist}, false, true},
+ {&fs.PathError{Err: fs.ErrClosed}, false, false},
+ {&os.LinkError{Err: fs.ErrInvalid}, false, false},
+ {&os.LinkError{Err: fs.ErrPermission}, false, false},
+ {&os.LinkError{Err: fs.ErrExist}, true, false},
+ {&os.LinkError{Err: fs.ErrNotExist}, false, true},
+ {&os.LinkError{Err: fs.ErrClosed}, false, false},
+ {&os.SyscallError{Err: fs.ErrNotExist}, false, true},
+ {&os.SyscallError{Err: fs.ErrExist}, true, false},
{nil, false, false},
}
@@ -111,14 +111,14 @@ func TestIsExist(t *testing.T) {
if is := os.IsExist(tt.err); is != tt.is {
t.Errorf("os.IsExist(%T %v) = %v, want %v", tt.err, tt.err, is, tt.is)
}
- if is := errors.Is(tt.err, os.ErrExist); is != tt.is {
- t.Errorf("errors.Is(%T %v, os.ErrExist) = %v, want %v", tt.err, tt.err, is, tt.is)
+ if is := errors.Is(tt.err, fs.ErrExist); is != tt.is {
+ t.Errorf("errors.Is(%T %v, fs.ErrExist) = %v, want %v", tt.err, tt.err, is, tt.is)
}
if isnot := os.IsNotExist(tt.err); isnot != tt.isnot {
t.Errorf("os.IsNotExist(%T %v) = %v, want %v", tt.err, tt.err, isnot, tt.isnot)
}
- if isnot := errors.Is(tt.err, os.ErrNotExist); isnot != tt.isnot {
- t.Errorf("errors.Is(%T %v, os.ErrNotExist) = %v, want %v", tt.err, tt.err, isnot, tt.isnot)
+ if isnot := errors.Is(tt.err, fs.ErrNotExist); isnot != tt.isnot {
+ t.Errorf("errors.Is(%T %v, fs.ErrNotExist) = %v, want %v", tt.err, tt.err, isnot, tt.isnot)
}
}
}
@@ -130,8 +130,8 @@ type isPermissionTest struct {
var isPermissionTests = []isPermissionTest{
{nil, false},
- {&os.PathError{Err: os.ErrPermission}, true},
- {&os.SyscallError{Err: os.ErrPermission}, true},
+ {&fs.PathError{Err: fs.ErrPermission}, true},
+ {&os.SyscallError{Err: fs.ErrPermission}, true},
}
func TestIsPermission(t *testing.T) {
@@ -139,19 +139,19 @@ func TestIsPermission(t *testing.T) {
if got := os.IsPermission(tt.err); got != tt.want {
t.Errorf("os.IsPermission(%#v) = %v; want %v", tt.err, got, tt.want)
}
- if got := errors.Is(tt.err, os.ErrPermission); got != tt.want {
- t.Errorf("errors.Is(%#v, os.ErrPermission) = %v; want %v", tt.err, got, tt.want)
+ if got := errors.Is(tt.err, fs.ErrPermission); got != tt.want {
+ t.Errorf("errors.Is(%#v, fs.ErrPermission) = %v; want %v", tt.err, got, tt.want)
}
}
}
func TestErrPathNUL(t *testing.T) {
- f, err := ioutil.TempFile("", "_Go_ErrPathNUL\x00")
+ f, err := os.CreateTemp("", "_Go_ErrPathNUL\x00")
if err == nil {
f.Close()
t.Fatal("TempFile should have failed")
}
- f, err = ioutil.TempFile("", "_Go_ErrPathNUL")
+ f, err = os.CreateTemp("", "_Go_ErrPathNUL")
if err != nil {
t.Fatalf("open ErrPathNUL tempfile: %s", err)
}
@@ -170,8 +170,8 @@ func TestErrPathNUL(t *testing.T) {
}
func TestPathErrorUnwrap(t *testing.T) {
- pe := &os.PathError{Err: os.ErrInvalid}
- if !errors.Is(pe, os.ErrInvalid) {
+ pe := &fs.PathError{Err: fs.ErrInvalid}
+ if !errors.Is(pe, fs.ErrInvalid) {
t.Error("errors.Is failed, wanted success")
}
}
@@ -181,7 +181,7 @@ type myErrorIs struct{ error }
func (e myErrorIs) Is(target error) bool { return target == e.error }
func TestErrorIsMethods(t *testing.T) {
- if os.IsPermission(myErrorIs{os.ErrPermission}) {
- t.Error("os.IsPermission(err) = true when err.Is(os.ErrPermission), wanted false")
+ if os.IsPermission(myErrorIs{fs.ErrPermission}) {
+ t.Error("os.IsPermission(err) = true when err.Is(fs.ErrPermission), wanted false")
}
}
diff --git a/libgo/go/os/error_unix_test.go b/libgo/go/os/error_unix_test.go
index a01916e..dd9f317 100644
--- a/libgo/go/os/error_unix_test.go
+++ b/libgo/go/os/error_unix_test.go
@@ -7,14 +7,15 @@
package os_test
import (
+ "io/fs"
"os"
"syscall"
)
func init() {
isExistTests = append(isExistTests,
- isExistTest{err: &os.PathError{Err: syscall.EEXIST}, is: true, isnot: false},
- isExistTest{err: &os.PathError{Err: syscall.ENOTEMPTY}, is: true, isnot: false},
+ isExistTest{err: &fs.PathError{Err: syscall.EEXIST}, is: true, isnot: false},
+ isExistTest{err: &fs.PathError{Err: syscall.ENOTEMPTY}, is: true, isnot: false},
isExistTest{err: &os.LinkError{Err: syscall.EEXIST}, is: true, isnot: false},
isExistTest{err: &os.LinkError{Err: syscall.ENOTEMPTY}, is: true, isnot: false},
@@ -23,9 +24,9 @@ func init() {
isExistTest{err: &os.SyscallError{Err: syscall.ENOTEMPTY}, is: true, isnot: false},
)
isPermissionTests = append(isPermissionTests,
- isPermissionTest{err: &os.PathError{Err: syscall.EACCES}, want: true},
- isPermissionTest{err: &os.PathError{Err: syscall.EPERM}, want: true},
- isPermissionTest{err: &os.PathError{Err: syscall.EEXIST}, want: false},
+ isPermissionTest{err: &fs.PathError{Err: syscall.EACCES}, want: true},
+ isPermissionTest{err: &fs.PathError{Err: syscall.EPERM}, want: true},
+ isPermissionTest{err: &fs.PathError{Err: syscall.EEXIST}, want: false},
isPermissionTest{err: &os.LinkError{Err: syscall.EACCES}, want: true},
isPermissionTest{err: &os.LinkError{Err: syscall.EPERM}, want: true},
diff --git a/libgo/go/os/error_windows_test.go b/libgo/go/os/error_windows_test.go
index 1635c10..b8191c5 100644
--- a/libgo/go/os/error_windows_test.go
+++ b/libgo/go/os/error_windows_test.go
@@ -7,6 +7,7 @@
package os_test
import (
+ "io/fs"
"os"
"syscall"
)
@@ -15,24 +16,24 @@ func init() {
const _ERROR_BAD_NETPATH = syscall.Errno(53)
isExistTests = append(isExistTests,
- isExistTest{err: &os.PathError{Err: syscall.ERROR_FILE_NOT_FOUND}, is: false, isnot: true},
+ isExistTest{err: &fs.PathError{Err: syscall.ERROR_FILE_NOT_FOUND}, is: false, isnot: true},
isExistTest{err: &os.LinkError{Err: syscall.ERROR_FILE_NOT_FOUND}, is: false, isnot: true},
isExistTest{err: &os.SyscallError{Err: syscall.ERROR_FILE_NOT_FOUND}, is: false, isnot: true},
- isExistTest{err: &os.PathError{Err: _ERROR_BAD_NETPATH}, is: false, isnot: true},
+ isExistTest{err: &fs.PathError{Err: _ERROR_BAD_NETPATH}, is: false, isnot: true},
isExistTest{err: &os.LinkError{Err: _ERROR_BAD_NETPATH}, is: false, isnot: true},
isExistTest{err: &os.SyscallError{Err: _ERROR_BAD_NETPATH}, is: false, isnot: true},
- isExistTest{err: &os.PathError{Err: syscall.ERROR_PATH_NOT_FOUND}, is: false, isnot: true},
+ isExistTest{err: &fs.PathError{Err: syscall.ERROR_PATH_NOT_FOUND}, is: false, isnot: true},
isExistTest{err: &os.LinkError{Err: syscall.ERROR_PATH_NOT_FOUND}, is: false, isnot: true},
isExistTest{err: &os.SyscallError{Err: syscall.ERROR_PATH_NOT_FOUND}, is: false, isnot: true},
- isExistTest{err: &os.PathError{Err: syscall.ERROR_DIR_NOT_EMPTY}, is: true, isnot: false},
+ isExistTest{err: &fs.PathError{Err: syscall.ERROR_DIR_NOT_EMPTY}, is: true, isnot: false},
isExistTest{err: &os.LinkError{Err: syscall.ERROR_DIR_NOT_EMPTY}, is: true, isnot: false},
isExistTest{err: &os.SyscallError{Err: syscall.ERROR_DIR_NOT_EMPTY}, is: true, isnot: false},
)
isPermissionTests = append(isPermissionTests,
- isPermissionTest{err: &os.PathError{Err: syscall.ERROR_ACCESS_DENIED}, want: true},
+ isPermissionTest{err: &fs.PathError{Err: syscall.ERROR_ACCESS_DENIED}, want: true},
isPermissionTest{err: &os.LinkError{Err: syscall.ERROR_ACCESS_DENIED}, want: true},
isPermissionTest{err: &os.SyscallError{Err: syscall.ERROR_ACCESS_DENIED}, want: true},
)
diff --git a/libgo/go/os/example_test.go b/libgo/go/os/example_test.go
index 822886f..3adce51 100644
--- a/libgo/go/os/example_test.go
+++ b/libgo/go/os/example_test.go
@@ -6,8 +6,10 @@ package os_test
import (
"fmt"
+ "io/fs"
"log"
"os"
+ "path/filepath"
"time"
)
@@ -62,9 +64,9 @@ func ExampleFileMode() {
fmt.Println("regular file")
case mode.IsDir():
fmt.Println("directory")
- case mode&os.ModeSymlink != 0:
+ case mode&fs.ModeSymlink != 0:
fmt.Println("symbolic link")
- case mode&os.ModeNamedPipe != 0:
+ case mode&fs.ModeNamedPipe != 0:
fmt.Println("named pipe")
}
}
@@ -143,3 +145,98 @@ func ExampleUnsetenv() {
os.Setenv("TMPDIR", "/my/tmp")
defer os.Unsetenv("TMPDIR")
}
+
+func ExampleReadDir() {
+ files, err := os.ReadDir(".")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ for _, file := range files {
+ fmt.Println(file.Name())
+ }
+}
+
+func ExampleMkdirTemp() {
+ dir, err := os.MkdirTemp("", "example")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer os.RemoveAll(dir) // clean up
+
+ file := filepath.Join(dir, "tmpfile")
+ if err := os.WriteFile(file, []byte("content"), 0666); err != nil {
+ log.Fatal(err)
+ }
+}
+
+func ExampleMkdirTemp_suffix() {
+ logsDir, err := os.MkdirTemp("", "*-logs")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer os.RemoveAll(logsDir) // clean up
+
+ // Logs can be cleaned out earlier if needed by searching
+ // for all directories whose suffix ends in *-logs.
+ globPattern := filepath.Join(os.TempDir(), "*-logs")
+ matches, err := filepath.Glob(globPattern)
+ if err != nil {
+ log.Fatalf("Failed to match %q: %v", globPattern, err)
+ }
+
+ for _, match := range matches {
+ if err := os.RemoveAll(match); err != nil {
+ log.Printf("Failed to remove %q: %v", match, err)
+ }
+ }
+}
+
+func ExampleCreateTemp() {
+ f, err := os.CreateTemp("", "example")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer os.Remove(f.Name()) // clean up
+
+ if _, err := f.Write([]byte("content")); err != nil {
+ log.Fatal(err)
+ }
+ if err := f.Close(); err != nil {
+ log.Fatal(err)
+ }
+}
+
+func ExampleCreateTemp_suffix() {
+ f, err := os.CreateTemp("", "example.*.txt")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer os.Remove(f.Name()) // clean up
+
+ if _, err := f.Write([]byte("content")); err != nil {
+ f.Close()
+ log.Fatal(err)
+ }
+ if err := f.Close(); err != nil {
+ log.Fatal(err)
+ }
+}
+
+func ExampleReadFile() {
+ data, err := os.ReadFile("testdata/hello")
+ if err != nil {
+ log.Fatal(err)
+ }
+ os.Stdout.Write(data)
+
+ // Output:
+ // Hello, Gophers!
+}
+
+func ExampleWriteFile() {
+ err := os.WriteFile("testdata/hello", []byte("Hello, Gophers!"), 0666)
+ if err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/libgo/go/os/exec.go b/libgo/go/os/exec.go
index cab6a73..edb773a 100644
--- a/libgo/go/os/exec.go
+++ b/libgo/go/os/exec.go
@@ -5,6 +5,7 @@
package os
import (
+ "errors"
"internal/testlog"
"runtime"
"sync"
@@ -13,6 +14,9 @@ import (
"time"
)
+// ErrProcessDone indicates a Process has finished.
+var ErrProcessDone = errors.New("os: process already finished")
+
// Process stores the information about a process created by StartProcess.
type Process struct {
Pid int
diff --git a/libgo/go/os/exec/exec_plan9.go b/libgo/go/os/exec/exec_plan9.go
index d90bd04..21ac7b7 100644
--- a/libgo/go/os/exec/exec_plan9.go
+++ b/libgo/go/os/exec/exec_plan9.go
@@ -4,14 +4,14 @@
package exec
-import "os"
+import "io/fs"
func init() {
skipStdinCopyError = func(err error) bool {
// Ignore hungup errors copying to stdin if the program
// completed successfully otherwise.
// See Issue 35753.
- pe, ok := err.(*os.PathError)
+ pe, ok := err.(*fs.PathError)
return ok &&
pe.Op == "write" && pe.Path == "|1" &&
pe.Err.Error() == "i/o on hungup channel"
diff --git a/libgo/go/os/exec/exec_test.go b/libgo/go/os/exec/exec_test.go
index dafbc64..8b0c93f 100644
--- a/libgo/go/os/exec/exec_test.go
+++ b/libgo/go/os/exec/exec_test.go
@@ -15,7 +15,6 @@ import (
"internal/poll"
"internal/testenv"
"io"
- "io/ioutil"
"log"
"net"
"net/http"
@@ -386,7 +385,7 @@ func TestPipeLookPathLeak(t *testing.T) {
// Reading /proc/self/fd is more reliable than calling lsof, so try that
// first.
numOpenFDs := func() (int, []byte, error) {
- fds, err := ioutil.ReadDir("/proc/self/fd")
+ fds, err := os.ReadDir("/proc/self/fd")
if err != nil {
return 0, nil, err
}
@@ -605,6 +604,10 @@ func TestExtraFiles(t *testing.T) {
testenv.MustHaveExec(t)
testenv.MustHaveGoBuild(t)
+ // This test runs with cgo disabled. External linking needs cgo, so
+ // it doesn't work if external linking is required.
+ testenv.MustInternalLink(t)
+
if runtime.GOOS == "windows" {
t.Skipf("skipping test on %q", runtime.GOOS)
}
@@ -633,7 +636,7 @@ func TestExtraFiles(t *testing.T) {
// cgo), to make sure none of that potential C code leaks fds.
ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
// quiet expected TLS handshake error "remote error: bad certificate"
- ts.Config.ErrorLog = log.New(ioutil.Discard, "", 0)
+ ts.Config.ErrorLog = log.New(io.Discard, "", 0)
ts.StartTLS()
defer ts.Close()
_, err = http.Get(ts.URL)
@@ -641,7 +644,7 @@ func TestExtraFiles(t *testing.T) {
t.Errorf("success trying to fetch %s; want an error", ts.URL)
}
- tf, err := ioutil.TempFile("", "")
+ tf, err := os.CreateTemp("", "")
if err != nil {
t.Fatalf("TempFile: %v", err)
}
@@ -687,6 +690,18 @@ func TestExtraFiles(t *testing.T) {
c.Stdout = &stdout
c.Stderr = &stderr
c.ExtraFiles = []*os.File{tf}
+ if runtime.GOOS == "illumos" {
+ // Some facilities in illumos are implemented via access
+ // to /proc by libc; such accesses can briefly occupy a
+ // low-numbered fd. If this occurs concurrently with the
+ // test that checks for leaked descriptors, the check can
+ // become confused and report a spurious leaked descriptor.
+ // (See issue #42431 for more detailed analysis.)
+ //
+ // Attempt to constrain the use of additional threads in the
+ // child process to make this test less flaky:
+ c.Env = append(os.Environ(), "GOMAXPROCS=1")
+ }
err = c.Run()
if err != nil {
t.Fatalf("Run: %v\n--- stdout:\n%s--- stderr:\n%s", err, stdout.Bytes(), stderr.Bytes())
@@ -826,7 +841,7 @@ func TestHelperProcess(*testing.T) {
}
}
case "stdinClose":
- b, err := ioutil.ReadAll(os.Stdin)
+ b, err := io.ReadAll(os.Stdin)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
diff --git a/libgo/go/os/exec/exec_unix.go b/libgo/go/os/exec/exec_unix.go
index 9c3e17d..51c5242 100644
--- a/libgo/go/os/exec/exec_unix.go
+++ b/libgo/go/os/exec/exec_unix.go
@@ -7,7 +7,7 @@
package exec
import (
- "os"
+ "io/fs"
"syscall"
)
@@ -16,7 +16,7 @@ func init() {
// Ignore EPIPE errors copying to stdin if the program
// completed successfully otherwise.
// See Issue 9173.
- pe, ok := err.(*os.PathError)
+ pe, ok := err.(*fs.PathError)
return ok &&
pe.Op == "write" && pe.Path == "|1" &&
pe.Err == syscall.EPIPE
diff --git a/libgo/go/os/exec/exec_windows.go b/libgo/go/os/exec/exec_windows.go
index af8cd97..bb937f8 100644
--- a/libgo/go/os/exec/exec_windows.go
+++ b/libgo/go/os/exec/exec_windows.go
@@ -5,7 +5,7 @@
package exec
import (
- "os"
+ "io/fs"
"syscall"
)
@@ -15,7 +15,7 @@ func init() {
// to stdin if the program completed successfully otherwise.
// See Issue 20445.
const _ERROR_NO_DATA = syscall.Errno(0xe8)
- pe, ok := err.(*os.PathError)
+ pe, ok := err.(*fs.PathError)
return ok &&
pe.Op == "write" && pe.Path == "|1" &&
(pe.Err == syscall.ERROR_BROKEN_PIPE || pe.Err == _ERROR_NO_DATA)
diff --git a/libgo/go/os/exec/lp_plan9.go b/libgo/go/os/exec/lp_plan9.go
index 5860cbc..e8826a5 100644
--- a/libgo/go/os/exec/lp_plan9.go
+++ b/libgo/go/os/exec/lp_plan9.go
@@ -6,6 +6,7 @@ package exec
import (
"errors"
+ "io/fs"
"os"
"path/filepath"
"strings"
@@ -22,7 +23,7 @@ func findExecutable(file string) error {
if m := d.Mode(); !m.IsDir() && m&0111 != 0 {
return nil
}
- return os.ErrPermission
+ return fs.ErrPermission
}
// LookPath searches for an executable named file in the
diff --git a/libgo/go/os/exec/lp_unix.go b/libgo/go/os/exec/lp_unix.go
index 6a5e877..bb5a3ec 100644
--- a/libgo/go/os/exec/lp_unix.go
+++ b/libgo/go/os/exec/lp_unix.go
@@ -8,6 +8,7 @@ package exec
import (
"errors"
+ "io/fs"
"os"
"path/filepath"
"strings"
@@ -24,7 +25,7 @@ func findExecutable(file string) error {
if m := d.Mode(); !m.IsDir() && m&0111 != 0 {
return nil
}
- return os.ErrPermission
+ return fs.ErrPermission
}
// LookPath searches for an executable named file in the
diff --git a/libgo/go/os/exec/lp_unix_test.go b/libgo/go/os/exec/lp_unix_test.go
index 5c240b3..23d1506 100644
--- a/libgo/go/os/exec/lp_unix_test.go
+++ b/libgo/go/os/exec/lp_unix_test.go
@@ -7,13 +7,12 @@
package exec
import (
- "io/ioutil"
"os"
"testing"
)
func TestLookPathUnixEmptyPath(t *testing.T) {
- tmp, err := ioutil.TempDir("", "TestLookPathUnixEmptyPath")
+ tmp, err := os.MkdirTemp("", "TestLookPathUnixEmptyPath")
if err != nil {
t.Fatal("TempDir failed: ", err)
}
diff --git a/libgo/go/os/exec/lp_windows.go b/libgo/go/os/exec/lp_windows.go
index 9ea3d76..e7a2cdf 100644
--- a/libgo/go/os/exec/lp_windows.go
+++ b/libgo/go/os/exec/lp_windows.go
@@ -6,6 +6,7 @@ package exec
import (
"errors"
+ "io/fs"
"os"
"path/filepath"
"strings"
@@ -20,7 +21,7 @@ func chkStat(file string) error {
return err
}
if d.IsDir() {
- return os.ErrPermission
+ return fs.ErrPermission
}
return nil
}
@@ -47,7 +48,7 @@ func findExecutable(file string, exts []string) (string, error) {
return f, nil
}
}
- return "", os.ErrNotExist
+ return "", fs.ErrNotExist
}
// LookPath searches for an executable named file in the
diff --git a/libgo/go/os/exec/read3.go b/libgo/go/os/exec/read3.go
index 25d732a..8cc24da 100644
--- a/libgo/go/os/exec/read3.go
+++ b/libgo/go/os/exec/read3.go
@@ -15,7 +15,7 @@ package main
import (
"fmt"
"internal/poll"
- "io/ioutil"
+ "io"
"os"
"os/exec"
"runtime"
@@ -24,7 +24,7 @@ import (
func main() {
fd3 := os.NewFile(3, "fd3")
- bs, err := ioutil.ReadAll(fd3)
+ bs, err := io.ReadAll(fd3)
if err != nil {
fmt.Printf("ReadAll from fd 3: %v\n", err)
os.Exit(1)
@@ -56,7 +56,7 @@ func main() {
switch runtime.GOOS {
case "plan9":
args = []string{fmt.Sprintf("/proc/%d/fd", os.Getpid())}
- case "aix":
+ case "aix", "solaris", "illumos":
args = []string{fmt.Sprint(os.Getpid())}
default:
args = []string{"-p", fmt.Sprint(os.Getpid())}
@@ -71,6 +71,8 @@ func main() {
ofcmd = "/bin/cat"
case "aix":
ofcmd = "procfiles"
+ case "solaris", "illumos":
+ ofcmd = "pfiles"
}
cmd := exec.Command(ofcmd, args...)
diff --git a/libgo/go/os/exec_plan9.go b/libgo/go/os/exec_plan9.go
index b0abf74..8580153 100644
--- a/libgo/go/os/exec_plan9.go
+++ b/libgo/go/os/exec_plan9.go
@@ -5,7 +5,6 @@
package os
import (
- "errors"
"runtime"
"syscall"
"time"
@@ -34,7 +33,7 @@ func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
pid, h, e := syscall.StartProcess(name, argv, sysattr)
if e != nil {
- return nil, &PathError{"fork/exec", name, e}
+ return nil, &PathError{Op: "fork/exec", Path: name, Err: e}
}
return newProcess(pid, h), nil
@@ -52,7 +51,7 @@ func (p *Process) writeProcFile(file string, data string) error {
func (p *Process) signal(sig Signal) error {
if p.done() {
- return errors.New("os: process already finished")
+ return ErrProcessDone
}
if e := p.writeProcFile("note", sig.String()); e != nil {
return NewSyscallError("signal", e)
diff --git a/libgo/go/os/exec_posix.go b/libgo/go/os/exec_posix.go
index dc9947c..aa8dfe0 100644
--- a/libgo/go/os/exec_posix.go
+++ b/libgo/go/os/exec_posix.go
@@ -56,7 +56,7 @@ func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
runtime.KeepAlive(attr)
if e != nil {
- return nil, &PathError{"fork/exec", name, e}
+ return nil, &PathError{Op: "fork/exec", Path: name, Err: e}
}
return newProcess(pid, h), nil
diff --git a/libgo/go/os/exec_unix.go b/libgo/go/os/exec_unix.go
index a99b45d..69eb615 100644
--- a/libgo/go/os/exec_unix.go
+++ b/libgo/go/os/exec_unix.go
@@ -59,8 +59,6 @@ func (p *Process) wait() (ps *ProcessState, err error) {
return ps, nil
}
-var errFinished = errors.New("os: process already finished")
-
func (p *Process) signal(sig Signal) error {
if p.Pid == -1 {
return errors.New("os: process already released")
@@ -71,7 +69,7 @@ func (p *Process) signal(sig Signal) error {
p.sigMu.RLock()
defer p.sigMu.RUnlock()
if p.done() {
- return errFinished
+ return ErrProcessDone
}
s, ok := sig.(syscall.Signal)
if !ok {
@@ -79,7 +77,7 @@ func (p *Process) signal(sig Signal) error {
}
if e := syscall.Kill(p.Pid, s); e != nil {
if e == syscall.ESRCH {
- return errFinished
+ return ErrProcessDone
}
return e
}
diff --git a/libgo/go/os/exec_unix_test.go b/libgo/go/os/exec_unix_test.go
new file mode 100644
index 0000000..d942cdb
--- /dev/null
+++ b/libgo/go/os/exec_unix_test.go
@@ -0,0 +1,29 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
+
+package os_test
+
+import (
+ "internal/testenv"
+ . "os"
+ "testing"
+)
+
+func TestErrProcessDone(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ path, err := testenv.GoTool()
+ if err != nil {
+ t.Errorf("finding go tool: %v", err)
+ }
+ p, err := StartProcess(path, []string{"go"}, &ProcAttr{})
+ if err != nil {
+ t.Errorf("starting test process: %v", err)
+ }
+ p.Wait()
+ if got := p.Signal(Kill); got != ErrProcessDone {
+ t.Errorf("got %v want %v", got, ErrProcessDone)
+ }
+}
diff --git a/libgo/go/os/exec_windows.go b/libgo/go/os/exec_windows.go
index 24ddf89..5710401 100644
--- a/libgo/go/os/exec_windows.go
+++ b/libgo/go/os/exec_windows.go
@@ -61,7 +61,7 @@ func (p *Process) signal(sig Signal) error {
return syscall.EINVAL
}
if p.done() {
- return errors.New("os: process already finished")
+ return ErrProcessDone
}
if sig == Kill {
err := terminateProcess(p.Pid, 1)
diff --git a/libgo/go/os/executable_dragonfly.go b/libgo/go/os/executable_dragonfly.go
new file mode 100644
index 0000000..19c2ae8
--- /dev/null
+++ b/libgo/go/os/executable_dragonfly.go
@@ -0,0 +1,12 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+// From DragonFly's <sys/sysctl.h>
+const (
+ _CTL_KERN = 1
+ _KERN_PROC = 14
+ _KERN_PROC_PATHNAME = 9
+)
diff --git a/libgo/go/os/executable_freebsd.go b/libgo/go/os/executable_freebsd.go
index ccaf8e6..95f1a93 100644
--- a/libgo/go/os/executable_freebsd.go
+++ b/libgo/go/os/executable_freebsd.go
@@ -1,33 +1,12 @@
-// Copyright 2016 The Go Authors. All rights reserved.
+// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package os
-import (
- "syscall"
- "unsafe"
+// From FreeBSD's <sys/sysctl.h>
+const (
+ _CTL_KERN = 1
+ _KERN_PROC = 14
+ _KERN_PROC_PATHNAME = 12
)
-
-func executable() (string, error) {
- mib := [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1}
-
- n := uintptr(0)
- // get length
- _, _, err := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
- if err != 0 {
- return "", err
- }
- if n == 0 { // shouldn't happen
- return "", nil
- }
- buf := make([]byte, n)
- _, _, err = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0)
- if err != 0 {
- return "", err
- }
- if n == 0 { // shouldn't happen
- return "", nil
- }
- return string(buf[:n-1]), nil
-}
diff --git a/libgo/go/os/executable_procfs.go b/libgo/go/os/executable_procfs.go
index d2d2e5a..989a220 100644
--- a/libgo/go/os/executable_procfs.go
+++ b/libgo/go/os/executable_procfs.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 hurd linux netbsd dragonfly js,wasm
+// +build hurd linux netbsd js,wasm
package os
@@ -23,8 +23,6 @@ var executablePath, executablePathErr = func() (string, error) {
procfn = "/proc/self/exe"
case "netbsd":
procfn = "/proc/curproc/exe"
- case "dragonfly":
- procfn = "/proc/curproc/file"
}
return Readlink(procfn)
}()
diff --git a/libgo/go/os/executable_sysctl.go b/libgo/go/os/executable_sysctl.go
new file mode 100644
index 0000000..f9a4b18
--- /dev/null
+++ b/libgo/go/os/executable_sysctl.go
@@ -0,0 +1,35 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build freebsd dragonfly
+
+package os
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+func executable() (string, error) {
+ mib := [4]int32{_CTL_KERN, _KERN_PROC, _KERN_PROC_PATHNAME, -1}
+
+ n := uintptr(0)
+ // get length
+ _, _, err := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
+ if err != 0 {
+ return "", err
+ }
+ if n == 0 { // shouldn't happen
+ return "", nil
+ }
+ buf := make([]byte, n)
+ _, _, err = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0)
+ if err != 0 {
+ return "", err
+ }
+ if n == 0 { // shouldn't happen
+ return "", nil
+ }
+ return string(buf[:n-1]), nil
+}
diff --git a/libgo/go/os/export_test.go b/libgo/go/os/export_test.go
index 812432c..f3cb1a2 100644
--- a/libgo/go/os/export_test.go
+++ b/libgo/go/os/export_test.go
@@ -9,3 +9,5 @@ package os
var Atime = atime
var LstatP = &lstat
var ErrWriteAtInAppendMode = errWriteAtInAppendMode
+var TestingForceReadDirLstat = &testingForceReadDirLstat
+var ErrPatternHasSeparator = errPatternHasSeparator
diff --git a/libgo/go/os/fifo_test.go b/libgo/go/os/fifo_test.go
index 3041dcf..2439192 100644
--- a/libgo/go/os/fifo_test.go
+++ b/libgo/go/os/fifo_test.go
@@ -11,7 +11,6 @@ import (
"bytes"
"fmt"
"io"
- "io/ioutil"
"os"
"path/filepath"
"runtime"
@@ -31,7 +30,7 @@ func TestFifoEOF(t *testing.T) {
t.Skip("skipping on OpenBSD; issue 25877")
}
- dir, err := ioutil.TempDir("", "TestFifoEOF")
+ dir, err := os.MkdirTemp("", "TestFifoEOF")
if err != nil {
t.Fatal(err)
}
diff --git a/libgo/go/os/file.go b/libgo/go/os/file.go
index a2b71cb..416bc0e 100644
--- a/libgo/go/os/file.go
+++ b/libgo/go/os/file.go
@@ -45,6 +45,7 @@ import (
"internal/poll"
"internal/testlog"
"io"
+ "io/fs"
"runtime"
"syscall"
"time"
@@ -127,7 +128,7 @@ func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
}
if off < 0 {
- return 0, &PathError{"readat", f.name, errors.New("negative offset")}
+ return 0, &PathError{Op: "readat", Path: f.name, Err: errors.New("negative offset")}
}
for len(b) > 0 {
@@ -203,7 +204,7 @@ func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
}
if off < 0 {
- return 0, &PathError{"writeat", f.name, errors.New("negative offset")}
+ return 0, &PathError{Op: "writeat", Path: f.name, Err: errors.New("negative offset")}
}
for len(b) > 0 {
@@ -253,12 +254,15 @@ func (f *File) WriteString(s string) (n int, err error) {
// If there is an error, it will be of type *PathError.
func Mkdir(name string, perm FileMode) error {
if runtime.GOOS == "windows" && isWindowsNulName(name) {
- return &PathError{"mkdir", name, syscall.ENOTDIR}
+ return &PathError{Op: "mkdir", Path: name, Err: syscall.ENOTDIR}
}
- e := syscall.Mkdir(fixLongPath(name), syscallMode(perm))
+ longName := fixLongPath(name)
+ e := ignoringEINTR(func() error {
+ return syscall.Mkdir(longName, syscallMode(perm))
+ })
if e != nil {
- return &PathError{"mkdir", name, e}
+ return &PathError{Op: "mkdir", Path: name, Err: e}
}
// mkdir(2) itself won't handle the sticky bit on *BSD and Solaris
@@ -288,7 +292,7 @@ func setStickyBit(name string) error {
func Chdir(dir string) error {
if e := syscall.Chdir(dir); e != nil {
testlog.Open(dir) // observe likely non-existent directory
- return &PathError{"chdir", dir, e}
+ return &PathError{Op: "chdir", Path: dir, Err: e}
}
if log := testlog.Logger(); log != nil {
wd, err := Getwd()
@@ -363,7 +367,7 @@ func (f *File) wrapErr(op string, err error) error {
if err == poll.ErrFileClosing {
err = ErrClosed
}
- return &PathError{op, f.name, err}
+ return &PathError{Op: op, Path: f.name, Err: err}
}
// TempDir returns the default directory to use for temporary files.
@@ -402,7 +406,7 @@ func UserCacheDir() (string, error) {
return "", errors.New("%LocalAppData% is not defined")
}
- case "darwin":
+ case "darwin", "ios":
dir = Getenv("HOME")
if dir == "" {
return "", errors.New("$HOME is not defined")
@@ -453,7 +457,7 @@ func UserConfigDir() (string, error) {
return "", errors.New("%AppData% is not defined")
}
- case "darwin":
+ case "darwin", "ios":
dir = Getenv("HOME")
if dir == "" {
return "", errors.New("$HOME is not defined")
@@ -501,10 +505,8 @@ func UserHomeDir() (string, error) {
switch runtime.GOOS {
case "android":
return "/sdcard", nil
- case "darwin":
- if runtime.GOARCH == "arm64" {
- return "/", nil
- }
+ case "ios":
+ return "/", nil
}
return "", errors.New(enverr + " is not defined")
}
@@ -605,3 +607,88 @@ func isWindowsNulName(name string) bool {
}
return true
}
+
+// DirFS returns a file system (an fs.FS) for the tree of files rooted at the directory dir.
+//
+// Note that DirFS("/prefix") only guarantees that the Open calls it makes to the
+// operating system will begin with "/prefix": DirFS("/prefix").Open("file") is the
+// same as os.Open("/prefix/file"). So if /prefix/file is a symbolic link pointing outside
+// the /prefix tree, then using DirFS does not stop the access any more than using
+// os.Open does. DirFS is therefore not a general substitute for a chroot-style security
+// mechanism when the directory tree contains arbitrary content.
+func DirFS(dir string) fs.FS {
+ return dirFS(dir)
+}
+
+type dirFS string
+
+func (dir dirFS) Open(name string) (fs.File, error) {
+ if !fs.ValidPath(name) {
+ return nil, &PathError{Op: "open", Path: name, Err: ErrInvalid}
+ }
+ f, err := Open(string(dir) + "/" + name)
+ if err != nil {
+ return nil, err // nil fs.File
+ }
+ return f, nil
+}
+
+// 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/libgo/go/os/file_plan9.go b/libgo/go/os/file_plan9.go
index eb15890..bbc7328 100644
--- a/libgo/go/os/file_plan9.go
+++ b/libgo/go/os/file_plan9.go
@@ -29,8 +29,13 @@ type file struct {
}
// Fd returns the integer Plan 9 file descriptor referencing the open file.
-// The file descriptor is valid only until f.Close is called or f is garbage collected.
-// On Unix systems this will cause the SetDeadline methods to stop working.
+// If f is closed, the file descriptor becomes invalid.
+// If f is garbage collected, a finalizer may close the file descriptor,
+// making it invalid; see runtime.SetFinalizer for more information on when
+// a finalizer might be run. On Unix systems this will cause the SetDeadline
+// methods to stop working.
+//
+// As an alternative, see the f.SyscallConn method.
func (f *File) Fd() uintptr {
if f == nil {
return ^(uintptr(0))
@@ -114,18 +119,18 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
if IsNotExist(e) && create {
fd, e = syscall.Create(name, flag, syscallMode(perm))
if e != nil {
- return nil, &PathError{"create", name, e}
+ return nil, &PathError{Op: "create", Path: name, Err: e}
}
}
}
if e != nil {
- return nil, &PathError{"open", name, e}
+ return nil, &PathError{Op: "open", Path: name, Err: e}
}
if append {
if _, e = syscall.Seek(fd, 0, io.SeekEnd); e != nil {
- return nil, &PathError{"seek", name, e}
+ return nil, &PathError{Op: "seek", Path: name, Err: e}
}
}
@@ -149,7 +154,7 @@ func (file *file) close() error {
}
var err error
if e := syscall.Close(file.fd); e != nil {
- err = &PathError{"close", file.name, e}
+ err = &PathError{Op: "close", Path: file.name, Err: e}
}
file.fd = badFd // so it can't be closed again
@@ -186,10 +191,10 @@ func (f *File) Truncate(size int64) error {
var buf [syscall.STATFIXLEN]byte
n, err := d.Marshal(buf[:])
if err != nil {
- return &PathError{"truncate", f.name, err}
+ return &PathError{Op: "truncate", Path: f.name, Err: err}
}
if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
- return &PathError{"truncate", f.name, err}
+ return &PathError{Op: "truncate", Path: f.name, Err: err}
}
return nil
}
@@ -204,7 +209,7 @@ func (f *File) chmod(mode FileMode) error {
odir, e := dirstat(f)
if e != nil {
- return &PathError{"chmod", f.name, e}
+ return &PathError{Op: "chmod", Path: f.name, Err: e}
}
d.Null()
d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask
@@ -212,10 +217,10 @@ func (f *File) chmod(mode FileMode) error {
var buf [syscall.STATFIXLEN]byte
n, err := d.Marshal(buf[:])
if err != nil {
- return &PathError{"chmod", f.name, err}
+ return &PathError{Op: "chmod", Path: f.name, Err: err}
}
if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
- return &PathError{"chmod", f.name, err}
+ return &PathError{Op: "chmod", Path: f.name, Err: err}
}
return nil
}
@@ -233,10 +238,10 @@ func (f *File) Sync() error {
var buf [syscall.STATFIXLEN]byte
n, err := d.Marshal(buf[:])
if err != nil {
- return &PathError{"sync", f.name, err}
+ return &PathError{Op: "sync", Path: f.name, Err: err}
}
if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
- return &PathError{"sync", f.name, err}
+ return &PathError{Op: "sync", Path: f.name, Err: err}
}
return nil
}
@@ -309,10 +314,10 @@ func Truncate(name string, size int64) error {
var buf [syscall.STATFIXLEN]byte
n, err := d.Marshal(buf[:])
if err != nil {
- return &PathError{"truncate", name, err}
+ return &PathError{Op: "truncate", Path: name, Err: err}
}
if err = syscall.Wstat(name, buf[:n]); err != nil {
- return &PathError{"truncate", name, err}
+ return &PathError{Op: "truncate", Path: name, Err: err}
}
return nil
}
@@ -321,7 +326,7 @@ func Truncate(name string, size int64) error {
// If there is an error, it will be of type *PathError.
func Remove(name string) error {
if e := syscall.Remove(name); e != nil {
- return &PathError{"remove", name, e}
+ return &PathError{Op: "remove", Path: name, Err: e}
}
return nil
}
@@ -384,7 +389,7 @@ func chmod(name string, mode FileMode) error {
odir, e := dirstat(name)
if e != nil {
- return &PathError{"chmod", name, e}
+ return &PathError{Op: "chmod", Path: name, Err: e}
}
d.Null()
d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask
@@ -392,10 +397,10 @@ func chmod(name string, mode FileMode) error {
var buf [syscall.STATFIXLEN]byte
n, err := d.Marshal(buf[:])
if err != nil {
- return &PathError{"chmod", name, err}
+ return &PathError{Op: "chmod", Path: name, Err: err}
}
if err = syscall.Wstat(name, buf[:n]); err != nil {
- return &PathError{"chmod", name, err}
+ return &PathError{Op: "chmod", Path: name, Err: err}
}
return nil
}
@@ -416,10 +421,10 @@ func Chtimes(name string, atime time.Time, mtime time.Time) error {
var buf [syscall.STATFIXLEN]byte
n, err := d.Marshal(buf[:])
if err != nil {
- return &PathError{"chtimes", name, err}
+ return &PathError{Op: "chtimes", Path: name, Err: err}
}
if err = syscall.Wstat(name, buf[:n]); err != nil {
- return &PathError{"chtimes", name, err}
+ return &PathError{Op: "chtimes", Path: name, Err: err}
}
return nil
}
@@ -453,7 +458,7 @@ func Symlink(oldname, newname string) error {
// Readlink returns the destination of the named symbolic link.
// If there is an error, it will be of type *PathError.
func Readlink(name string) (string, error) {
- return "", &PathError{"readlink", name, syscall.EPLAN9}
+ return "", &PathError{Op: "readlink", Path: name, Err: syscall.EPLAN9}
}
// Chown changes the numeric uid and gid of the named file.
@@ -464,14 +469,14 @@ func Readlink(name string) (string, error) {
// On Windows or Plan 9, Chown always returns the syscall.EWINDOWS or
// EPLAN9 error, wrapped in *PathError.
func Chown(name string, uid, gid int) error {
- return &PathError{"chown", name, syscall.EPLAN9}
+ return &PathError{Op: "chown", Path: name, Err: syscall.EPLAN9}
}
// Lchown changes the numeric uid and gid of the named file.
// If the file is a symbolic link, it changes the uid and gid of the link itself.
// If there is an error, it will be of type *PathError.
func Lchown(name string, uid, gid int) error {
- return &PathError{"lchown", name, syscall.EPLAN9}
+ return &PathError{Op: "lchown", Path: name, Err: syscall.EPLAN9}
}
// Chown changes the numeric uid and gid of the named file.
@@ -480,7 +485,7 @@ func (f *File) Chown(uid, gid int) error {
if f == nil {
return ErrInvalid
}
- return &PathError{"chown", f.name, syscall.EPLAN9}
+ return &PathError{Op: "chown", Path: f.name, Err: syscall.EPLAN9}
}
func tempDir() string {
@@ -500,7 +505,7 @@ func (f *File) Chdir() error {
return err
}
if e := syscall.Fchdir(f.fd); e != nil {
- return &PathError{"chdir", f.name, e}
+ return &PathError{Op: "chdir", Path: f.name, Err: e}
}
return nil
}
@@ -536,7 +541,7 @@ func (f *File) checkValid(op string) error {
return ErrInvalid
}
if f.fd == badFd {
- return &PathError{op, f.name, ErrClosed}
+ return &PathError{Op: op, Path: f.name, Err: ErrClosed}
}
return nil
}
@@ -558,3 +563,7 @@ func (c *rawConn) Write(f func(uintptr) bool) error {
func newRawConn(file *File) (*rawConn, error) {
return nil, syscall.EPLAN9
}
+
+func ignoringEINTR(fn func() error) error {
+ return fn()
+}
diff --git a/libgo/go/os/file_posix.go b/libgo/go/os/file_posix.go
index ee0728b..95bf9e7 100644
--- a/libgo/go/os/file_posix.go
+++ b/libgo/go/os/file_posix.go
@@ -76,8 +76,12 @@ func syscallMode(i FileMode) (o uint32) {
// See docs in file.go:Chmod.
func chmod(name string, mode FileMode) error {
- if e := syscall.Chmod(fixLongPath(name), syscallMode(mode)); e != nil {
- return &PathError{"chmod", name, e}
+ longName := fixLongPath(name)
+ e := ignoringEINTR(func() error {
+ return syscall.Chmod(longName, syscallMode(mode))
+ })
+ if e != nil {
+ return &PathError{Op: "chmod", Path: name, Err: e}
}
return nil
}
@@ -101,8 +105,11 @@ func (f *File) chmod(mode FileMode) error {
// On Windows or Plan 9, Chown always returns the syscall.EWINDOWS or
// EPLAN9 error, wrapped in *PathError.
func Chown(name string, uid, gid int) error {
- if e := syscall.Chown(name, uid, gid); e != nil {
- return &PathError{"chown", name, e}
+ e := ignoringEINTR(func() error {
+ return syscall.Chown(name, uid, gid)
+ })
+ if e != nil {
+ return &PathError{Op: "chown", Path: name, Err: e}
}
return nil
}
@@ -114,8 +121,11 @@ func Chown(name string, uid, gid int) error {
// On Windows, it always returns the syscall.EWINDOWS error, wrapped
// in *PathError.
func Lchown(name string, uid, gid int) error {
- if e := syscall.Lchown(name, uid, gid); e != nil {
- return &PathError{"lchown", name, e}
+ e := ignoringEINTR(func() error {
+ return syscall.Lchown(name, uid, gid)
+ })
+ if e != nil {
+ return &PathError{Op: "lchown", Path: name, Err: e}
}
return nil
}
@@ -172,7 +182,7 @@ func Chtimes(name string, atime time.Time, mtime time.Time) error {
utimes[0] = syscall.NsecToTimespec(atime.UnixNano())
utimes[1] = syscall.NsecToTimespec(mtime.UnixNano())
if e := syscall.UtimesNano(fixLongPath(name), utimes[0:]); e != nil {
- return &PathError{"chtimes", name, e}
+ return &PathError{Op: "chtimes", Path: name, Err: e}
}
return nil
}
@@ -222,3 +232,19 @@ func (f *File) checkValid(op string) error {
}
return nil
}
+
+// ignoringEINTR makes a function call and repeats it if it returns an
+// EINTR error. This appears to be required even though we install all
+// signal handlers with SA_RESTART: see #22838, #38033, #38836, #40846.
+// Also #20400 and #36644 are issues in which a signal handler is
+// installed without setting SA_RESTART. None of these are the common case,
+// but there are enough of them that it seems that we can't avoid
+// an EINTR loop.
+func ignoringEINTR(fn func() error) error {
+ for {
+ err := fn()
+ if err != syscall.EINTR {
+ return err
+ }
+ }
+}
diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go
index aed7713..8315887 100644
--- a/libgo/go/os/file_unix.go
+++ b/libgo/go/os/file_unix.go
@@ -9,7 +9,6 @@ package os
import (
"internal/poll"
"internal/syscall/unix"
- "io"
"runtime"
"syscall"
)
@@ -39,7 +38,9 @@ func rename(oldname, newname string) error {
return &LinkError{"rename", oldname, newname, syscall.EEXIST}
}
}
- err = syscall.Rename(oldname, newname)
+ err = ignoringEINTR(func() error {
+ return syscall.Rename(oldname, newname)
+ })
if err != nil {
return &LinkError{"rename", oldname, newname, err}
}
@@ -60,8 +61,13 @@ type file struct {
}
// Fd returns the integer Unix file descriptor referencing the open file.
-// The file descriptor is valid only until f.Close is called or f is garbage collected.
-// On Unix systems this will cause the SetDeadline methods to stop working.
+// If f is closed, the file descriptor becomes invalid.
+// If f is garbage collected, a finalizer may close the file descriptor,
+// making it invalid; see runtime.SetFinalizer for more information on when
+// a finalizer might be run. On Unix systems this will cause the SetDeadline
+// methods to stop working.
+//
+// As an alternative, see the f.SyscallConn method.
func (f *File) Fd() uintptr {
if f == nil {
return ^(uintptr(0))
@@ -127,9 +133,11 @@ func newFile(fd uintptr, name string, kind newFileKind) *File {
// used with kqueue.
if kind == kindOpenFile {
switch runtime.GOOS {
- case "darwin", "dragonfly", "freebsd", "netbsd", "openbsd":
+ case "darwin", "ios", "dragonfly", "freebsd", "netbsd", "openbsd":
var st syscall.Stat_t
- err := syscall.Fstat(fdi, &st)
+ err := ignoringEINTR(func() error {
+ return syscall.Fstat(fdi, &st)
+ })
typ := st.Mode & syscall.S_IFMT
// Don't try to use kqueue with regular files on *BSDs.
// On FreeBSD a regular file is always
@@ -146,7 +154,7 @@ func newFile(fd uintptr, name string, kind newFileKind) *File {
// on Darwin, kqueue does not work properly with fifos:
// closing the last writer does not cause a kqueue event
// for any readers. See issue #24164.
- if runtime.GOOS == "darwin" && typ == syscall.S_IFIFO {
+ if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && typ == syscall.S_IFIFO {
pollable = false
}
}
@@ -173,7 +181,6 @@ func newFile(fd uintptr, name string, kind newFileKind) *File {
// Auxiliary information if the File describes a directory
type dirInfo struct {
- buf []byte // buffer for directory I/O
dir *syscall.DIR // from opendir
}
@@ -222,7 +229,7 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
continue
}
- return nil, &PathError{"open", name, e}
+ return nil, &PathError{Op: "open", Path: name, Err: e}
}
// open(2) itself won't handle the sticky bit on *BSD and Solaris
@@ -251,7 +258,7 @@ func (file *file) close() error {
if e == poll.ErrFileClosing {
e = ErrClosed
}
- err = &PathError{"close", file.name, e}
+ err = &PathError{Op: "close", Path: file.name, Err: e}
}
// no need for a finalizer anymore
@@ -279,8 +286,11 @@ func (f *File) seek(offset int64, whence int) (ret int64, err error) {
// If the file is a symbolic link, it changes the size of the link's target.
// If there is an error, it will be of type *PathError.
func Truncate(name string, size int64) error {
- if e := syscall.Truncate(name, size); e != nil {
- return &PathError{"truncate", name, e}
+ e := ignoringEINTR(func() error {
+ return syscall.Truncate(name, size)
+ })
+ if e != nil {
+ return &PathError{Op: "truncate", Path: name, Err: e}
}
return nil
}
@@ -292,11 +302,15 @@ func Remove(name string) error {
// whether name is a file or directory.
// Try both: it is cheaper on average than
// doing a Stat plus the right one.
- e := syscall.Unlink(name)
+ e := ignoringEINTR(func() error {
+ return syscall.Unlink(name)
+ })
if e == nil {
return nil
}
- e1 := syscall.Rmdir(name)
+ e1 := ignoringEINTR(func() error {
+ return syscall.Rmdir(name)
+ })
if e1 == nil {
return nil
}
@@ -313,7 +327,7 @@ func Remove(name string) error {
if e1 != syscall.ENOTDIR {
e = e1
}
- return &PathError{"remove", name, e}
+ return &PathError{Op: "remove", Path: name, Err: e}
}
func tempDir() string {
@@ -331,7 +345,9 @@ func tempDir() string {
// Link creates newname as a hard link to the oldname file.
// If there is an error, it will be of type *LinkError.
func Link(oldname, newname string) error {
- e := syscall.Link(oldname, newname)
+ e := ignoringEINTR(func() error {
+ return syscall.Link(oldname, newname)
+ })
if e != nil {
return &LinkError{"link", oldname, newname, e}
}
@@ -341,55 +357,77 @@ func Link(oldname, newname string) error {
// Symlink creates newname as a symbolic link to oldname.
// If there is an error, it will be of type *LinkError.
func Symlink(oldname, newname string) error {
- e := syscall.Symlink(oldname, newname)
+ e := ignoringEINTR(func() error {
+ return syscall.Symlink(oldname, newname)
+ })
if e != nil {
return &LinkError{"symlink", oldname, newname, e}
}
return nil
}
-func (f *File) readdir(n int) (fi []FileInfo, err error) {
- dirname := f.name
- if dirname == "" {
- dirname = "."
- }
- names, err := f.Readdirnames(n)
- fi = make([]FileInfo, 0, len(names))
- for _, filename := range names {
- fip, lerr := lstat(dirname + "/" + filename)
- if IsNotExist(lerr) {
- // File disappeared between readdir + stat.
- // Just treat it as if it didn't exist.
- continue
- }
- if lerr != nil {
- return fi, lerr
- }
- fi = append(fi, fip)
- }
- if len(fi) == 0 && err == nil && n > 0 {
- // Per File.Readdir, the slice must be non-empty or err
- // must be non-nil if n > 0.
- err = io.EOF
- }
- return fi, err
-}
-
// Readlink returns the destination of the named symbolic link.
// If there is an error, it will be of type *PathError.
func Readlink(name string) (string, error) {
for len := 128; ; len *= 2 {
b := make([]byte, len)
- n, e := fixCount(syscall.Readlink(name, b))
+ var (
+ n int
+ e error
+ )
+ for {
+ n, e = fixCount(syscall.Readlink(name, b))
+ if e != syscall.EINTR {
+ break
+ }
+ }
// buffer too small
if runtime.GOOS == "aix" && e == syscall.ERANGE {
continue
}
if e != nil {
- return "", &PathError{"readlink", name, e}
+ return "", &PathError{Op: "readlink", Path: name, Err: e}
}
if n < len {
return string(b[0:n]), nil
}
}
}
+
+type unixDirent struct {
+ parent string
+ name string
+ typ FileMode
+ info FileInfo
+}
+
+func (d *unixDirent) Name() string { return d.name }
+func (d *unixDirent) IsDir() bool { return d.typ.IsDir() }
+func (d *unixDirent) Type() FileMode { return d.typ }
+
+func (d *unixDirent) Info() (FileInfo, error) {
+ if d.info != nil {
+ return d.info, nil
+ }
+ return lstat(d.parent + "/" + d.name)
+}
+
+func newUnixDirent(parent, name string, typ FileMode) (DirEntry, error) {
+ ude := &unixDirent{
+ parent: parent,
+ name: name,
+ typ: typ,
+ }
+ if typ != ^FileMode(0) && !testingForceReadDirLstat {
+ return ude, nil
+ }
+
+ info, err := lstat(parent + "/" + name)
+ if err != nil {
+ return nil, err
+ }
+
+ ude.typ = info.Mode().Type()
+ ude.info = info
+ return ude, nil
+}
diff --git a/libgo/go/os/getwd.go b/libgo/go/os/getwd.go
index 6d25466..90604cf 100644
--- a/libgo/go/os/getwd.go
+++ b/libgo/go/os/getwd.go
@@ -15,10 +15,6 @@ var getwdCache struct {
dir string
}
-// useSyscallwd determines whether to use the return value of
-// syscall.Getwd based on its error.
-var useSyscallwd = func(error) bool { return true }
-
// Getwd returns a rooted path name corresponding to the
// current directory. If the current directory can be
// reached via multiple paths (due to symbolic links),
@@ -45,10 +41,17 @@ func Getwd() (dir string, err error) {
// If the operating system provides a Getwd call, use it.
// Otherwise, we're trying to find our way back to ".".
if syscall.ImplementsGetwd {
- s, e := syscall.Getwd()
- if useSyscallwd(e) {
- return s, NewSyscallError("getwd", e)
+ var (
+ s string
+ e error
+ )
+ for {
+ s, e = syscall.Getwd()
+ if e != syscall.EINTR {
+ break
+ }
}
+ return s, NewSyscallError("getwd", e)
}
// Apply same kludge but to cached dir instead of $PWD.
@@ -103,10 +106,10 @@ func Getwd() (dir string, err error) {
Found:
pd, err := fd.Stat()
+ fd.Close()
if err != nil {
return "", err
}
- fd.Close()
if SameFile(pd, root) {
break
}
diff --git a/libgo/go/os/getwd_darwin.go b/libgo/go/os/getwd_darwin.go
deleted file mode 100644
index e51ffcd..0000000
--- a/libgo/go/os/getwd_darwin.go
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package os
-
-import "syscall"
-
-func init() {
- useSyscallwd = useSyscallwdDarwin
-}
-
-func useSyscallwdDarwin(err error) bool {
- return err != syscall.ENOTSUP
-}
diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go
index 8a2f917..ccef141 100644
--- a/libgo/go/os/os_test.go
+++ b/libgo/go/os/os_test.go
@@ -11,7 +11,7 @@ import (
"fmt"
"internal/testenv"
"io"
- "io/ioutil"
+ "os"
. "os"
osexec "os/exec"
"path/filepath"
@@ -23,6 +23,7 @@ import (
"sync"
"syscall"
"testing"
+ "testing/fstest"
"time"
)
@@ -50,7 +51,7 @@ var sysdir = func() *sysDir {
"libpowermanager.so",
},
}
- case "darwin":
+ case "darwin", "ios":
switch runtime.GOARCH {
case "arm64":
wd, err := syscall.Getwd()
@@ -142,7 +143,7 @@ func localTmp() string {
switch runtime.GOOS {
case "android", "windows":
return TempDir()
- case "darwin":
+ case "darwin", "ios":
switch runtime.GOARCH {
case "arm64":
return TempDir()
@@ -152,7 +153,7 @@ func localTmp() string {
}
func newFile(testName string, t *testing.T) (f *File) {
- f, err := ioutil.TempFile(localTmp(), "_Go_"+testName)
+ f, err := os.CreateTemp(localTmp(), "_Go_"+testName)
if err != nil {
t.Fatalf("TempFile %s: %s", testName, err)
}
@@ -160,7 +161,7 @@ func newFile(testName string, t *testing.T) (f *File) {
}
func newDir(testName string, t *testing.T) (name string) {
- name, err := ioutil.TempDir(localTmp(), "_Go_"+testName)
+ name, err := os.MkdirTemp(localTmp(), "_Go_"+testName)
if err != nil {
t.Fatalf("TempDir %s: %s", testName, err)
}
@@ -307,25 +308,29 @@ func testReaddirnames(dir string, contents []string, t *testing.T) {
defer file.Close()
s, err2 := file.Readdirnames(-1)
if err2 != nil {
- t.Fatalf("readdirnames %q failed: %v", dir, err2)
+ t.Fatalf("Readdirnames %q failed: %v", dir, err2)
}
for _, m := range contents {
found := false
for _, n := range s {
if n == "." || n == ".." {
- t.Errorf("got %s in directory", n)
+ t.Errorf("got %q in directory", n)
}
- if equal(m, n) {
- if found {
- t.Error("present twice:", m)
- }
- found = true
+ if !equal(m, n) {
+ continue
+ }
+ if found {
+ t.Error("present twice:", m)
}
+ found = true
}
if !found {
t.Error("could not find", m)
}
}
+ if s == nil {
+ t.Error("Readdirnames returned nil instead of empty slice")
+ }
}
func testReaddir(dir string, contents []string, t *testing.T) {
@@ -336,32 +341,98 @@ func testReaddir(dir string, contents []string, t *testing.T) {
defer file.Close()
s, err2 := file.Readdir(-1)
if err2 != nil {
- t.Fatalf("readdir %q failed: %v", dir, err2)
+ t.Fatalf("Readdir %q failed: %v", dir, err2)
}
for _, m := range contents {
found := false
for _, n := range s {
- if equal(m, n.Name()) {
- if found {
- t.Error("present twice:", m)
- }
- found = true
+ if n.Name() == "." || n.Name() == ".." {
+ t.Errorf("got %q in directory", n.Name())
}
+ if !equal(m, n.Name()) {
+ continue
+ }
+ if found {
+ t.Error("present twice:", m)
+ }
+ found = true
}
if !found {
t.Error("could not find", m)
}
}
+ if s == nil {
+ t.Error("Readdir returned nil instead of empty slice")
+ }
}
-func TestReaddirnames(t *testing.T) {
+func testReadDir(dir string, contents []string, t *testing.T) {
+ file, err := Open(dir)
+ if err != nil {
+ t.Fatalf("open %q failed: %v", dir, err)
+ }
+ defer file.Close()
+ s, err2 := file.ReadDir(-1)
+ if err2 != nil {
+ t.Fatalf("ReadDir %q failed: %v", dir, err2)
+ }
+ for _, m := range contents {
+ found := false
+ for _, n := range s {
+ if n.Name() == "." || n.Name() == ".." {
+ t.Errorf("got %q in directory", n)
+ }
+ if !equal(m, n.Name()) {
+ continue
+ }
+ if found {
+ t.Error("present twice:", m)
+ }
+ found = true
+ lstat, err := Lstat(dir + "/" + m)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if n.IsDir() != lstat.IsDir() {
+ t.Errorf("%s: IsDir=%v, want %v", m, n.IsDir(), lstat.IsDir())
+ }
+ if n.Type() != lstat.Mode().Type() {
+ t.Errorf("%s: IsDir=%v, want %v", m, n.Type(), lstat.Mode().Type())
+ }
+ info, err := n.Info()
+ if err != nil {
+ t.Errorf("%s: Info: %v", m, err)
+ continue
+ }
+ if !SameFile(info, lstat) {
+ t.Errorf("%s: Info: SameFile(info, lstat) = false", m)
+ }
+ }
+ if !found {
+ t.Error("could not find", m)
+ }
+ }
+ if s == nil {
+ t.Error("ReadDir returned nil instead of empty slice")
+ }
+}
+
+func TestFileReaddirnames(t *testing.T) {
testReaddirnames(".", dot, t)
testReaddirnames(sysdir.name, sysdir.files, t)
+ testReaddirnames(t.TempDir(), nil, t)
}
-func TestReaddir(t *testing.T) {
+func TestFileReaddir(t *testing.T) {
testReaddir(".", dot, t)
testReaddir(sysdir.name, sysdir.files, t)
+ testReaddir(t.TempDir(), nil, t)
+}
+
+func TestFileReadDir(t *testing.T) {
+ testReadDir(".", dot, t)
+ testReadDir(sysdir.name, sysdir.files, t)
+ testReadDir(t.TempDir(), nil, t)
}
func benchmarkReaddirname(path string, b *testing.B) {
@@ -398,6 +469,23 @@ func benchmarkReaddir(path string, b *testing.B) {
b.Logf("benchmarkReaddir %q: %d entries", path, nentries)
}
+func benchmarkReadDir(path string, b *testing.B) {
+ var nentries int
+ for i := 0; i < b.N; i++ {
+ f, err := Open(path)
+ if err != nil {
+ b.Fatalf("open %q failed: %v", path, err)
+ }
+ fs, err := f.ReadDir(-1)
+ f.Close()
+ if err != nil {
+ b.Fatalf("readdir %q failed: %v", path, err)
+ }
+ nentries = len(fs)
+ }
+ b.Logf("benchmarkReadDir %q: %d entries", path, nentries)
+}
+
func BenchmarkReaddirname(b *testing.B) {
benchmarkReaddirname(".", b)
}
@@ -406,6 +494,10 @@ func BenchmarkReaddir(b *testing.B) {
benchmarkReaddir(".", b)
}
+func BenchmarkReadDir(b *testing.B) {
+ benchmarkReadDir(".", b)
+}
+
func benchmarkStat(b *testing.B, path string) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
@@ -479,7 +571,7 @@ func TestReaddirnamesOneAtATime(t *testing.T) {
switch runtime.GOOS {
case "android":
dir = "/system/bin"
- case "darwin":
+ case "darwin", "ios":
switch runtime.GOARCH {
case "arm64":
wd, err := Getwd()
@@ -522,7 +614,7 @@ func TestReaddirNValues(t *testing.T) {
if testing.Short() {
t.Skip("test.short; skipping")
}
- dir, err := ioutil.TempDir("", "")
+ dir, err := os.MkdirTemp("", "")
if err != nil {
t.Fatalf("TempDir: %v", err)
}
@@ -545,7 +637,8 @@ func TestReaddirNValues(t *testing.T) {
}
}
- readDirExpect := func(n, want int, wantErr error) {
+ readdirExpect := func(n, want int, wantErr error) {
+ t.Helper()
fi, err := d.Readdir(n)
if err != wantErr {
t.Fatalf("Readdir of %d got error %v, want %v", n, err, wantErr)
@@ -555,7 +648,19 @@ func TestReaddirNValues(t *testing.T) {
}
}
- readDirNamesExpect := func(n, want int, wantErr error) {
+ readDirExpect := func(n, want int, wantErr error) {
+ t.Helper()
+ de, err := d.ReadDir(n)
+ if err != wantErr {
+ t.Fatalf("ReadDir of %d got error %v, want %v", n, err, wantErr)
+ }
+ if g, e := len(de), want; g != e {
+ t.Errorf("ReadDir of %d got %d files, want %d", n, g, e)
+ }
+ }
+
+ readdirnamesExpect := func(n, want int, wantErr error) {
+ t.Helper()
fi, err := d.Readdirnames(n)
if err != wantErr {
t.Fatalf("Readdirnames of %d got error %v, want %v", n, err, wantErr)
@@ -565,7 +670,7 @@ func TestReaddirNValues(t *testing.T) {
}
}
- for _, fn := range []func(int, int, error){readDirExpect, readDirNamesExpect} {
+ for _, fn := range []func(int, int, error){readdirExpect, readdirnamesExpect, readDirExpect} {
// Test the slurp case
openDir()
fn(0, 105, nil)
@@ -608,7 +713,7 @@ func TestReaddirStatFailures(t *testing.T) {
// testing it wouldn't work.
t.Skipf("skipping test on %v", runtime.GOOS)
}
- dir, err := ioutil.TempDir("", "")
+ dir, err := os.MkdirTemp("", "")
if err != nil {
t.Fatalf("TempDir: %v", err)
}
@@ -669,7 +774,7 @@ func TestReaddirStatFailures(t *testing.T) {
// Readdir on a regular file should fail.
func TestReaddirOfFile(t *testing.T) {
- f, err := ioutil.TempFile("", "_Go_ReaddirOfFile")
+ f, err := os.CreateTemp("", "_Go_ReaddirOfFile")
if err != nil {
t.Fatal(err)
}
@@ -686,6 +791,10 @@ func TestReaddirOfFile(t *testing.T) {
if err == nil {
t.Error("Readdirnames succeeded; want non-nil error")
}
+ var pe *PathError
+ if !errors.As(err, &pe) || pe.Path != f.Name() {
+ t.Errorf("Readdirnames returned %q; want a PathError with path %q", err, f.Name())
+ }
if len(names) > 0 {
t.Errorf("unexpected dir names in regular file: %q", names)
}
@@ -756,7 +865,7 @@ func chtmpdir(t *testing.T) func() {
if err != nil {
t.Fatalf("chtmpdir: %v", err)
}
- d, err := ioutil.TempDir("", "test")
+ d, err := os.MkdirTemp("", "test")
if err != nil {
t.Fatalf("chtmpdir: %v", err)
}
@@ -881,12 +990,12 @@ func TestRenameOverwriteDest(t *testing.T) {
toData := []byte("to")
fromData := []byte("from")
- err := ioutil.WriteFile(to, toData, 0777)
+ err := os.WriteFile(to, toData, 0777)
if err != nil {
t.Fatalf("write file %q failed: %v", to, err)
}
- err = ioutil.WriteFile(from, fromData, 0777)
+ err = os.WriteFile(from, fromData, 0777)
if err != nil {
t.Fatalf("write file %q failed: %v", from, err)
}
@@ -1093,32 +1202,38 @@ func checkMode(t *testing.T, path string, mode FileMode) {
if err != nil {
t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err)
}
- if dir.Mode()&0777 != mode {
+ if dir.Mode()&ModePerm != mode {
t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode(), mode)
}
}
func TestChmod(t *testing.T) {
- // Chmod is not supported under windows.
- if runtime.GOOS == "windows" {
- return
- }
f := newFile("TestChmod", t)
defer Remove(f.Name())
defer f.Close()
+ // Creation mode is read write
- if err := Chmod(f.Name(), 0456); err != nil {
- t.Fatalf("chmod %s 0456: %s", f.Name(), err)
+ fm := FileMode(0456)
+ if runtime.GOOS == "windows" {
+ fm = FileMode(0444) // read-only file
}
- checkMode(t, f.Name(), 0456)
+ if err := Chmod(f.Name(), fm); err != nil {
+ t.Fatalf("chmod %s %#o: %s", f.Name(), fm, err)
+ }
+ checkMode(t, f.Name(), fm)
- if err := f.Chmod(0123); err != nil {
- t.Fatalf("chmod %s 0123: %s", f.Name(), err)
+ fm = FileMode(0123)
+ if runtime.GOOS == "windows" {
+ fm = FileMode(0666) // read-write file
+ }
+ if err := f.Chmod(fm); err != nil {
+ t.Fatalf("chmod %s %#o: %s", f.Name(), fm, err)
}
- checkMode(t, f.Name(), 0123)
+ checkMode(t, f.Name(), fm)
}
func checkSize(t *testing.T, f *File, size int64) {
+ t.Helper()
dir, err := f.Stat()
if err != nil {
t.Fatalf("Stat %q (looking for size %d): %s", f.Name(), size, err)
@@ -1224,7 +1339,7 @@ func testChtimes(t *testing.T, name string) {
// the contents are accessed; also, it is set
// whenever mtime is set.
case "netbsd":
- mounts, _ := ioutil.ReadFile("/proc/mounts")
+ mounts, _ := os.ReadFile("/proc/mounts")
if strings.Contains(string(mounts), "noatime") {
t.Logf("AccessTime didn't go backwards, but see a filesystem mounted noatime; ignoring. Issue 19293.")
} else {
@@ -1293,12 +1408,12 @@ func TestChdirAndGetwd(t *testing.T) {
dirs = []string{"/system/bin"}
case "plan9":
dirs = []string{"/", "/usr"}
- case "darwin":
+ case "darwin", "ios":
switch runtime.GOARCH {
case "arm64":
dirs = nil
for _, d := range []string{"d1", "d2"} {
- dir, err := ioutil.TempDir("", d)
+ dir, err := os.MkdirTemp("", d)
if err != nil {
t.Fatalf("TempDir: %v", err)
}
@@ -1392,7 +1507,7 @@ func TestProgWideChdir(t *testing.T) {
c <- true
t.Fatalf("Getwd: %v", err)
}
- d, err := ioutil.TempDir("", "test")
+ d, err := os.MkdirTemp("", "test")
if err != nil {
c <- true
t.Fatalf("TempDir: %v", err)
@@ -1463,7 +1578,7 @@ func TestSeek(t *testing.T) {
off, err := f.Seek(tt.in, tt.whence)
if off != tt.out || err != nil {
if e, ok := err.(*PathError); ok && e.Err == syscall.EINVAL && tt.out > 1<<32 && runtime.GOOS == "linux" {
- mounts, _ := ioutil.ReadFile("/proc/mounts")
+ mounts, _ := os.ReadFile("/proc/mounts")
if strings.Contains(string(mounts), "reiserfs") {
// Reiserfs rejects the big seeks.
t.Skipf("skipping test known to fail on reiserfs; https://golang.org/issue/91")
@@ -1566,8 +1681,8 @@ func TestOpenError(t *testing.T) {
func TestOpenNoName(t *testing.T) {
f, err := Open("")
if err == nil {
- t.Fatal(`Open("") succeeded`)
f.Close()
+ t.Fatal(`Open("") succeeded`)
}
}
@@ -1745,7 +1860,7 @@ func TestWriteAt(t *testing.T) {
t.Fatalf("WriteAt 7: %d, %v", n, err)
}
- b, err := ioutil.ReadFile(f.Name())
+ b, err := os.ReadFile(f.Name())
if err != nil {
t.Fatalf("ReadFile %s: %v", f.Name(), err)
}
@@ -1793,7 +1908,7 @@ func writeFile(t *testing.T, fname string, flag int, text string) string {
t.Fatalf("WriteString: %d, %v", n, err)
}
f.Close()
- data, err := ioutil.ReadFile(fname)
+ data, err := os.ReadFile(fname)
if err != nil {
t.Fatalf("ReadFile: %v", err)
}
@@ -1835,7 +1950,7 @@ func TestAppend(t *testing.T) {
func TestStatDirWithTrailingSlash(t *testing.T) {
// Create new temporary directory and arrange to clean it up.
- path, err := ioutil.TempDir("", "_TestStatDirWithSlash_")
+ path, err := os.MkdirTemp("", "_TestStatDirWithSlash_")
if err != nil {
t.Fatalf("TempDir: %s", err)
}
@@ -1977,7 +2092,7 @@ func TestLargeWriteToConsole(t *testing.T) {
func TestStatDirModeExec(t *testing.T) {
const mode = 0111
- path, err := ioutil.TempDir("", "go-build")
+ path, err := os.MkdirTemp("", "go-build")
if err != nil {
t.Fatalf("Failed to create temp directory: %v", err)
}
@@ -2046,7 +2161,7 @@ func TestStatStdin(t *testing.T) {
func TestStatRelativeSymlink(t *testing.T) {
testenv.MustHaveSymlink(t)
- tmpdir, err := ioutil.TempDir("", "TestStatRelativeSymlink")
+ tmpdir, err := os.MkdirTemp("", "TestStatRelativeSymlink")
if err != nil {
t.Fatal(err)
}
@@ -2136,8 +2251,8 @@ func TestLongPath(t *testing.T) {
t.Fatalf("MkdirAll failed: %v", err)
}
data := []byte("hello world\n")
- if err := ioutil.WriteFile(sizedTempDir+"/foo.txt", data, 0644); err != nil {
- t.Fatalf("ioutil.WriteFile() failed: %v", err)
+ if err := os.WriteFile(sizedTempDir+"/foo.txt", data, 0644); err != nil {
+ t.Fatalf("os.WriteFile() failed: %v", err)
}
if err := Rename(sizedTempDir+"/foo.txt", sizedTempDir+"/bar.txt"); err != nil {
t.Fatalf("Rename failed: %v", err)
@@ -2321,7 +2436,7 @@ func TestRemoveAllRace(t *testing.T) {
n := runtime.GOMAXPROCS(16)
defer runtime.GOMAXPROCS(n)
- root, err := ioutil.TempDir("", "issue")
+ root, err := os.MkdirTemp("", "issue")
if err != nil {
t.Fatal(err)
}
@@ -2427,7 +2542,7 @@ func testDoubleCloseError(t *testing.T, path string) {
if err := file.Close(); err == nil {
t.Error("second Close did not fail")
} else if pe, ok := err.(*PathError); !ok {
- t.Errorf("second Close returned unexpected error type %T; expected os.PathError", pe)
+ t.Errorf("second Close returned unexpected error type %T; expected fs.PathError", pe)
} else if pe.Err != ErrClosed {
t.Errorf("second Close returned %q, wanted %q", err, ErrClosed)
} else {
@@ -2572,3 +2687,28 @@ func TestOpenFileKeepsPermissions(t *testing.T) {
t.Errorf("Stat after OpenFile is %v, should be writable", fi.Mode())
}
}
+
+func TestDirFS(t *testing.T) {
+ if err := fstest.TestFS(DirFS("./signal"), "signal.go", "internal/pty/pty.go"); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestReadFileProc(t *testing.T) {
+ // Linux files in /proc report 0 size,
+ // but then if ReadFile reads just a single byte at offset 0,
+ // the read at offset 1 returns EOF instead of more data.
+ // ReadFile has a minimum read size of 512 to work around this,
+ // but test explicitly that it's working.
+ name := "/proc/sys/fs/pipe-max-size"
+ if _, err := Stat(name); err != nil {
+ t.Skip(err)
+ }
+ data, err := ReadFile(name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(data) == 0 || data[len(data)-1] != '\n' {
+ t.Fatalf("read %s: not newline-terminated: %q", name, data)
+ }
+}
diff --git a/libgo/go/os/os_unix_test.go b/libgo/go/os/os_unix_test.go
index 7b6fa0c..9a5d7bf 100644
--- a/libgo/go/os/os_unix_test.go
+++ b/libgo/go/os/os_unix_test.go
@@ -8,7 +8,7 @@ package os_test
import (
"io"
- "io/ioutil"
+ "os"
. "os"
"path/filepath"
"runtime"
@@ -190,7 +190,7 @@ func TestReaddirRemoveRace(t *testing.T) {
}
dir := newDir("TestReaddirRemoveRace", t)
defer RemoveAll(dir)
- if err := ioutil.WriteFile(filepath.Join(dir, "some-file"), []byte("hello"), 0644); err != nil {
+ if err := os.WriteFile(filepath.Join(dir, "some-file"), []byte("hello"), 0644); err != nil {
t.Fatal(err)
}
d, err := Open(dir)
diff --git a/libgo/go/os/os_windows_test.go b/libgo/go/os/os_windows_test.go
index f03ec75..8d1d1f6 100644
--- a/libgo/go/os/os_windows_test.go
+++ b/libgo/go/os/os_windows_test.go
@@ -12,7 +12,7 @@ import (
"internal/syscall/windows/registry"
"internal/testenv"
"io"
- "io/ioutil"
+ "io/fs"
"os"
osexec "os/exec"
"path/filepath"
@@ -30,7 +30,7 @@ import (
type syscallDescriptor = syscall.Handle
func TestSameWindowsFile(t *testing.T) {
- temp, err := ioutil.TempDir("", "TestSameWindowsFile")
+ temp, err := os.MkdirTemp("", "TestSameWindowsFile")
if err != nil {
t.Fatal(err)
}
@@ -89,7 +89,7 @@ type dirLinkTest struct {
}
func testDirLinks(t *testing.T, tests []dirLinkTest) {
- tmpdir, err := ioutil.TempDir("", "testDirLinks")
+ tmpdir, err := os.MkdirTemp("", "testDirLinks")
if err != nil {
t.Fatal(err)
}
@@ -114,7 +114,7 @@ func testDirLinks(t *testing.T, tests []dirLinkTest) {
if err != nil {
t.Fatal(err)
}
- err = ioutil.WriteFile(filepath.Join(dir, "abc"), []byte("abc"), 0644)
+ err = os.WriteFile(filepath.Join(dir, "abc"), []byte("abc"), 0644)
if err != nil {
t.Fatal(err)
}
@@ -126,7 +126,7 @@ func testDirLinks(t *testing.T, tests []dirLinkTest) {
continue
}
- data, err := ioutil.ReadFile(filepath.Join(link, "abc"))
+ data, err := os.ReadFile(filepath.Join(link, "abc"))
if err != nil {
t.Errorf("failed to read abc file: %v", err)
continue
@@ -164,11 +164,11 @@ func testDirLinks(t *testing.T, tests []dirLinkTest) {
t.Errorf("failed to lstat link %v: %v", link, err)
continue
}
- if m := fi2.Mode(); m&os.ModeSymlink == 0 {
+ if m := fi2.Mode(); m&fs.ModeSymlink == 0 {
t.Errorf("%q should be a link, but is not (mode=0x%x)", link, uint32(m))
continue
}
- if m := fi2.Mode(); m&os.ModeDir != 0 {
+ if m := fi2.Mode(); m&fs.ModeDir != 0 {
t.Errorf("%q should be a link, not a directory (mode=0x%x)", link, uint32(m))
continue
}
@@ -438,7 +438,7 @@ func TestNetworkSymbolicLink(t *testing.T) {
const _NERR_ServerNotStarted = syscall.Errno(2114)
- dir, err := ioutil.TempDir("", "TestNetworkSymbolicLink")
+ dir, err := os.MkdirTemp("", "TestNetworkSymbolicLink")
if err != nil {
t.Fatal(err)
}
@@ -599,7 +599,7 @@ func TestStatDir(t *testing.T) {
}
func TestOpenVolumeName(t *testing.T) {
- tmpdir, err := ioutil.TempDir("", "TestOpenVolumeName")
+ tmpdir, err := os.MkdirTemp("", "TestOpenVolumeName")
if err != nil {
t.Fatal(err)
}
@@ -618,7 +618,7 @@ func TestOpenVolumeName(t *testing.T) {
want := []string{"file1", "file2", "file3", "gopher.txt"}
sort.Strings(want)
for _, name := range want {
- err := ioutil.WriteFile(filepath.Join(tmpdir, name), nil, 0777)
+ err := os.WriteFile(filepath.Join(tmpdir, name), nil, 0777)
if err != nil {
t.Fatal(err)
}
@@ -642,7 +642,7 @@ func TestOpenVolumeName(t *testing.T) {
}
func TestDeleteReadOnly(t *testing.T) {
- tmpdir, err := ioutil.TempDir("", "TestDeleteReadOnly")
+ tmpdir, err := os.MkdirTemp("", "TestDeleteReadOnly")
if err != nil {
t.Fatal(err)
}
@@ -681,7 +681,7 @@ func TestStatSymlinkLoop(t *testing.T) {
defer os.Remove("x")
_, err = os.Stat("x")
- if _, ok := err.(*os.PathError); !ok {
+ if _, ok := err.(*fs.PathError); !ok {
t.Errorf("expected *PathError, got %T: %v\n", err, err)
}
}
@@ -803,7 +803,7 @@ func compareCommandLineToArgvWithSyscall(t *testing.T, cmd string) {
}
func TestCmdArgs(t *testing.T) {
- tmpdir, err := ioutil.TempDir("", "TestCmdArgs")
+ tmpdir, err := os.MkdirTemp("", "TestCmdArgs")
if err != nil {
t.Fatal(err)
}
@@ -822,7 +822,7 @@ func main() {
}
`
src := filepath.Join(tmpdir, "main.go")
- err = ioutil.WriteFile(src, []byte(prog), 0666)
+ err = os.WriteFile(src, []byte(prog), 0666)
if err != nil {
t.Fatal(err)
}
@@ -970,14 +970,14 @@ func TestSymlinkCreation(t *testing.T) {
}
t.Parallel()
- temp, err := ioutil.TempDir("", "TestSymlinkCreation")
+ temp, err := os.MkdirTemp("", "TestSymlinkCreation")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(temp)
dummyFile := filepath.Join(temp, "file")
- err = ioutil.WriteFile(dummyFile, []byte(""), 0644)
+ err = os.WriteFile(dummyFile, []byte(""), 0644)
if err != nil {
t.Fatal(err)
}
@@ -1206,7 +1206,7 @@ func mklinkd(t *testing.T, link, target string) {
}
func TestWindowsReadlink(t *testing.T) {
- tmpdir, err := ioutil.TempDir("", "TestWindowsReadlink")
+ tmpdir, err := os.MkdirTemp("", "TestWindowsReadlink")
if err != nil {
t.Fatal(err)
}
@@ -1271,7 +1271,7 @@ func TestWindowsReadlink(t *testing.T) {
testReadlink(t, "reldirlink", "dir")
file := filepath.Join(tmpdir, "file")
- err = ioutil.WriteFile(file, []byte(""), 0666)
+ err = os.WriteFile(file, []byte(""), 0666)
if err != nil {
t.Fatal(err)
}
@@ -1291,9 +1291,9 @@ func TestWindowsReadlink(t *testing.T) {
// os.Mkdir(os.DevNull) fails.
func TestMkdirDevNull(t *testing.T) {
err := os.Mkdir(os.DevNull, 777)
- oserr, ok := err.(*os.PathError)
+ oserr, ok := err.(*fs.PathError)
if !ok {
- t.Fatalf("error (%T) is not *os.PathError", err)
+ t.Fatalf("error (%T) is not *fs.PathError", err)
}
errno, ok := oserr.Err.(syscall.Errno)
if !ok {
diff --git a/libgo/go/os/path.go b/libgo/go/os/path.go
index ba43ea3..df87887 100644
--- a/libgo/go/os/path.go
+++ b/libgo/go/os/path.go
@@ -22,7 +22,7 @@ func MkdirAll(path string, perm FileMode) error {
if dir.IsDir() {
return nil
}
- return &PathError{"mkdir", path, syscall.ENOTDIR}
+ return &PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR}
}
// Slow path: make sure parent exists and then call Mkdir for path.
diff --git a/libgo/go/os/path_test.go b/libgo/go/os/path_test.go
index d586daf..b79d958 100644
--- a/libgo/go/os/path_test.go
+++ b/libgo/go/os/path_test.go
@@ -6,7 +6,7 @@ package os_test
import (
"internal/testenv"
- "io/ioutil"
+ "os"
. "os"
"path/filepath"
"runtime"
@@ -78,7 +78,7 @@ func TestMkdirAll(t *testing.T) {
func TestMkdirAllWithSymlink(t *testing.T) {
testenv.MustHaveSymlink(t)
- tmpDir, err := ioutil.TempDir("", "TestMkdirAllWithSymlink-")
+ tmpDir, err := os.MkdirTemp("", "TestMkdirAllWithSymlink-")
if err != nil {
t.Fatal(err)
}
@@ -107,7 +107,7 @@ func TestMkdirAllAtSlash(t *testing.T) {
switch runtime.GOOS {
case "android", "plan9", "windows":
t.Skipf("skipping on %s", runtime.GOOS)
- case "darwin":
+ case "darwin", "ios":
switch runtime.GOARCH {
case "arm64":
t.Skipf("skipping on darwin/arm64, mkdir returns EPERM")
diff --git a/libgo/go/os/path_windows_test.go b/libgo/go/os/path_windows_test.go
index 862b404..869db8f 100644
--- a/libgo/go/os/path_windows_test.go
+++ b/libgo/go/os/path_windows_test.go
@@ -5,7 +5,6 @@
package os_test
import (
- "io/ioutil"
"os"
"strings"
"syscall"
@@ -48,7 +47,7 @@ func TestFixLongPath(t *testing.T) {
}
func TestMkdirAllExtendedLength(t *testing.T) {
- tmpDir, err := ioutil.TempDir("", "TestMkdirAllExtendedLength")
+ tmpDir, err := os.MkdirTemp("", "TestMkdirAllExtendedLength")
if err != nil {
t.Fatal(err)
}
diff --git a/libgo/go/os/pipe2_illumos.go b/libgo/go/os/pipe2_illumos.go
new file mode 100644
index 0000000..026ce62
--- /dev/null
+++ b/libgo/go/os/pipe2_illumos.go
@@ -0,0 +1,25 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build illumos
+
+package os
+
+import (
+ "internal/syscall/unix"
+ "syscall"
+)
+
+// Pipe returns a connected pair of Files; reads from r return bytes written to w.
+// It returns the files and an error, if any.
+func Pipe() (r *File, w *File, err error) {
+ var p [2]int
+
+ e := unix.Pipe2(p[0:], syscall.O_CLOEXEC)
+ if e != nil {
+ return nil, nil, NewSyscallError("pipe", e)
+ }
+
+ return newFile(uintptr(p[0]), "|0", kindPipe), newFile(uintptr(p[1]), "|1", kindPipe), nil
+}
diff --git a/libgo/go/os/pipe_bsd.go b/libgo/go/os/pipe_bsd.go
index 0d2d82f..115d6ba 100644
--- a/libgo/go/os/pipe_bsd.go
+++ b/libgo/go/os/pipe_bsd.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 aix darwin dragonfly js,wasm solaris
+// +build aix darwin dragonfly js,wasm solaris,!illumos
package os
diff --git a/libgo/go/os/pipe_test.go b/libgo/go/os/pipe_test.go
index 429bd81..b98e538 100644
--- a/libgo/go/os/pipe_test.go
+++ b/libgo/go/os/pipe_test.go
@@ -13,7 +13,7 @@ import (
"fmt"
"internal/testenv"
"io"
- "io/ioutil"
+ "io/fs"
"os"
osexec "os/exec"
"os/signal"
@@ -46,7 +46,7 @@ func TestEPIPE(t *testing.T) {
if err == nil {
t.Fatal("unexpected success of Write to broken pipe")
}
- if pe, ok := err.(*os.PathError); ok {
+ if pe, ok := err.(*fs.PathError); ok {
err = pe.Err
}
if se, ok := err.(*os.SyscallError); ok {
@@ -160,7 +160,7 @@ func testClosedPipeRace(t *testing.T, read bool) {
// Get the amount we have to write to overload a pipe
// with no reader.
limit = 131073
- if b, err := ioutil.ReadFile("/proc/sys/fs/pipe-max-size"); err == nil {
+ if b, err := os.ReadFile("/proc/sys/fs/pipe-max-size"); err == nil {
if i, err := strconv.Atoi(strings.TrimSpace(string(b))); err == nil {
limit = i + 1
}
@@ -202,10 +202,10 @@ func testClosedPipeRace(t *testing.T, read bool) {
}
if err == nil {
t.Error("I/O on closed pipe unexpectedly succeeded")
- } else if pe, ok := err.(*os.PathError); !ok {
- t.Errorf("I/O on closed pipe returned unexpected error type %T; expected os.PathError", pe)
- } else if pe.Err != os.ErrClosed {
- t.Errorf("got error %q but expected %q", pe.Err, os.ErrClosed)
+ } else if pe, ok := err.(*fs.PathError); !ok {
+ t.Errorf("I/O on closed pipe returned unexpected error type %T; expected fs.PathError", pe)
+ } else if pe.Err != fs.ErrClosed {
+ t.Errorf("got error %q but expected %q", pe.Err, fs.ErrClosed)
} else {
t.Logf("I/O returned expected error %q", err)
}
@@ -233,7 +233,7 @@ func TestReadNonblockingFd(t *testing.T) {
defer syscall.SetNonblock(fd, false)
_, err := os.Stdin.Read(make([]byte, 1))
if err != nil {
- if perr, ok := err.(*os.PathError); !ok || perr.Err != syscall.EAGAIN {
+ if perr, ok := err.(*fs.PathError); !ok || perr.Err != syscall.EAGAIN {
t.Fatalf("read on nonblocking stdin got %q, should have gotten EAGAIN", err)
}
}
@@ -308,10 +308,10 @@ func testCloseWithBlockingRead(t *testing.T, r, w *os.File) {
if err == nil {
t.Error("I/O on closed pipe unexpectedly succeeded")
}
- if pe, ok := err.(*os.PathError); ok {
+ if pe, ok := err.(*fs.PathError); ok {
err = pe.Err
}
- if err != io.EOF && err != os.ErrClosed {
+ if err != io.EOF && err != fs.ErrClosed {
t.Errorf("got %v, expected EOF or closed", err)
}
}(c2)
diff --git a/libgo/go/os/proc.go b/libgo/go/os/proc.go
index 7364d63..cbd5a6a 100644
--- a/libgo/go/os/proc.go
+++ b/libgo/go/os/proc.go
@@ -7,6 +7,7 @@
package os
import (
+ "internal/testlog"
"runtime"
"syscall"
)
@@ -60,6 +61,13 @@ func Getgroups() ([]int, error) {
// For portability, the status code should be in the range [0, 125].
func Exit(code int) {
if code == 0 {
+ if testlog.PanicOnExit0() {
+ // We were told to panic on calls to os.Exit(0).
+ // This is used to fail tests that make an early
+ // unexpected call to os.Exit(0).
+ panic("unexpected call to os.Exit(0) during test")
+ }
+
// Give race detector a chance to fail the program.
// Racy programs do not have the right to finish successfully.
runtime_beforeExit()
diff --git a/libgo/go/os/read_test.go b/libgo/go/os/read_test.go
new file mode 100644
index 0000000..b42ff36
--- /dev/null
+++ b/libgo/go/os/read_test.go
@@ -0,0 +1,131 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os_test
+
+import (
+ "bytes"
+ . "os"
+ "path/filepath"
+ "runtime"
+ "testing"
+)
+
+func checkNamedSize(t *testing.T, path string, size int64) {
+ dir, err := Stat(path)
+ if err != nil {
+ t.Fatalf("Stat %q (looking for size %d): %s", path, size, err)
+ }
+ if dir.Size() != size {
+ t.Errorf("Stat %q: size %d want %d", path, dir.Size(), size)
+ }
+}
+
+func TestReadFile(t *testing.T) {
+ filename := "rumpelstilzchen"
+ contents, err := ReadFile(filename)
+ if err == nil {
+ t.Fatalf("ReadFile %s: error expected, none found", filename)
+ }
+
+ filename = "read_test.go"
+ contents, err = ReadFile(filename)
+ if err != nil {
+ t.Fatalf("ReadFile %s: %v", filename, err)
+ }
+
+ checkNamedSize(t, filename, int64(len(contents)))
+}
+
+func TestWriteFile(t *testing.T) {
+ f, err := CreateTemp("", "ioutil-test")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+ defer Remove(f.Name())
+
+ msg := "Programming today is a race between software engineers striving to " +
+ "build bigger and better idiot-proof programs, and the Universe trying " +
+ "to produce bigger and better idiots. So far, the Universe is winning."
+
+ if err := WriteFile(f.Name(), []byte(msg), 0644); err != nil {
+ t.Fatalf("WriteFile %s: %v", f.Name(), err)
+ }
+
+ data, err := ReadFile(f.Name())
+ if err != nil {
+ t.Fatalf("ReadFile %s: %v", f.Name(), err)
+ }
+
+ if string(data) != msg {
+ t.Fatalf("ReadFile: wrong data:\nhave %q\nwant %q", string(data), msg)
+ }
+}
+
+func TestReadOnlyWriteFile(t *testing.T) {
+ if Getuid() == 0 {
+ t.Skipf("Root can write to read-only files anyway, so skip the read-only test.")
+ }
+
+ // We don't want to use CreateTemp directly, since that opens a file for us as 0600.
+ tempDir, err := MkdirTemp("", t.Name())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer RemoveAll(tempDir)
+ filename := filepath.Join(tempDir, "blurp.txt")
+
+ shmorp := []byte("shmorp")
+ florp := []byte("florp")
+ err = WriteFile(filename, shmorp, 0444)
+ if err != nil {
+ t.Fatalf("WriteFile %s: %v", filename, err)
+ }
+ err = WriteFile(filename, florp, 0444)
+ if err == nil {
+ t.Fatalf("Expected an error when writing to read-only file %s", filename)
+ }
+ got, err := ReadFile(filename)
+ if err != nil {
+ t.Fatalf("ReadFile %s: %v", filename, err)
+ }
+ if !bytes.Equal(got, shmorp) {
+ t.Fatalf("want %s, got %s", shmorp, got)
+ }
+}
+
+func TestReadDir(t *testing.T) {
+ dirname := "rumpelstilzchen"
+ _, err := ReadDir(dirname)
+ if err == nil {
+ t.Fatalf("ReadDir %s: error expected, none found", dirname)
+ }
+
+ dirname = "."
+ list, err := ReadDir(dirname)
+ if err != nil {
+ t.Fatalf("ReadDir %s: %v", dirname, err)
+ }
+
+ foundFile := false
+ foundSubDir := false
+ for _, dir := range list {
+ switch {
+ case !dir.IsDir() && dir.Name() == "read_test.go":
+ foundFile = true
+ case dir.IsDir() && dir.Name() == "exec":
+ foundSubDir = true
+ }
+ }
+ if !foundFile {
+ t.Fatalf("ReadDir %s: read_test.go file not found", dirname)
+ }
+ if !foundSubDir {
+ // This doesn't work in the gofrontend testsuite framework.
+ if runtime.Compiler == "gc" {
+ t.Fatalf("ReadDir %s: exec directory not found", dirname)
+ }
+ }
+}
diff --git a/libgo/go/os/readfrom_linux_test.go b/libgo/go/os/readfrom_linux_test.go
index 00faf39..3704717 100644
--- a/libgo/go/os/readfrom_linux_test.go
+++ b/libgo/go/os/readfrom_linux_test.go
@@ -8,8 +8,8 @@ import (
"bytes"
"internal/poll"
"io"
- "io/ioutil"
"math/rand"
+ "os"
. "os"
"path/filepath"
"strconv"
@@ -173,7 +173,7 @@ func TestCopyFileRange(t *testing.T) {
})
t.Run("Nil", func(t *testing.T) {
var nilFile *File
- anyFile, err := ioutil.TempFile("", "")
+ anyFile, err := os.CreateTemp("", "")
if err != nil {
t.Fatal(err)
}
diff --git a/libgo/go/os/removeall_at.go b/libgo/go/os/removeall_at.go
index 8bbdcf0..f9eafa1 100644
--- a/libgo/go/os/removeall_at.go
+++ b/libgo/go/os/removeall_at.go
@@ -22,7 +22,7 @@ func removeAll(path string) error {
// The rmdir system call does not permit removing ".",
// so we don't permit it either.
if endsWithDot(path) {
- return &PathError{"RemoveAll", path, syscall.EINVAL}
+ return &PathError{Op: "RemoveAll", Path: path, Err: syscall.EINVAL}
}
// Simple case: if Remove works, we're done.
@@ -70,7 +70,7 @@ func removeAllFrom(parent *File, base string) error {
// whose contents need to be removed.
// Otherwise just return the error.
if err != syscall.EISDIR && err != syscall.EPERM && err != syscall.EACCES {
- return &PathError{"unlinkat", base, err}
+ return &PathError{Op: "unlinkat", Path: base, Err: err}
}
// Is this a directory we need to recurse into?
@@ -80,11 +80,11 @@ func removeAllFrom(parent *File, base string) error {
if IsNotExist(statErr) {
return nil
}
- return &PathError{"fstatat", base, statErr}
+ return &PathError{Op: "fstatat", Path: base, Err: statErr}
}
if statInfo.Mode&syscall.S_IFMT != syscall.S_IFDIR {
// Not a directory; return the error from the unix.Unlinkat.
- return &PathError{"unlinkat", base, err}
+ return &PathError{Op: "unlinkat", Path: base, Err: err}
}
// Remove the directory's entries.
@@ -99,7 +99,7 @@ func removeAllFrom(parent *File, base string) error {
if IsNotExist(err) {
return nil
}
- recurseErr = &PathError{"openfdat", base, err}
+ recurseErr = &PathError{Op: "openfdat", Path: base, Err: err}
break
}
@@ -113,7 +113,7 @@ func removeAllFrom(parent *File, base string) error {
if IsNotExist(readErr) {
return nil
}
- return &PathError{"readdirnames", base, readErr}
+ return &PathError{Op: "readdirnames", Path: base, Err: readErr}
}
respSize = len(names)
@@ -159,7 +159,7 @@ func removeAllFrom(parent *File, base string) error {
if recurseErr != nil {
return recurseErr
}
- return &PathError{"unlinkat", base, unlinkError}
+ return &PathError{Op: "unlinkat", Path: base, Err: unlinkError}
}
// openFdAt opens path relative to the directory in fd.
diff --git a/libgo/go/os/removeall_noat.go b/libgo/go/os/removeall_noat.go
index fb9b45f..a4cde5a 100644
--- a/libgo/go/os/removeall_noat.go
+++ b/libgo/go/os/removeall_noat.go
@@ -23,7 +23,7 @@ func removeAll(path string) error {
// so we don't permit it to remain consistent with the
// "at" implementation of RemoveAll.
if endsWithDot(path) {
- return &PathError{"RemoveAll", path, syscall.EINVAL}
+ return &PathError{Op: "RemoveAll", Path: path, Err: syscall.EINVAL}
}
// Simple case: if Remove works, we're done.
diff --git a/libgo/go/os/removeall_test.go b/libgo/go/os/removeall_test.go
index 8a71f68..3a2f6e3 100644
--- a/libgo/go/os/removeall_test.go
+++ b/libgo/go/os/removeall_test.go
@@ -6,7 +6,7 @@ package os_test
import (
"fmt"
- "io/ioutil"
+ "os"
. "os"
"path/filepath"
"runtime"
@@ -15,7 +15,7 @@ import (
)
func TestRemoveAll(t *testing.T) {
- tmpDir, err := ioutil.TempDir("", "TestRemoveAll-")
+ tmpDir, err := os.MkdirTemp("", "TestRemoveAll-")
if err != nil {
t.Fatal(err)
}
@@ -128,7 +128,7 @@ func TestRemoveAllLarge(t *testing.T) {
t.Skip("skipping in short mode")
}
- tmpDir, err := ioutil.TempDir("", "TestRemoveAll-")
+ tmpDir, err := os.MkdirTemp("", "TestRemoveAll-")
if err != nil {
t.Fatal(err)
}
@@ -158,7 +158,7 @@ func TestRemoveAllLarge(t *testing.T) {
func TestRemoveAllLongPath(t *testing.T) {
switch runtime.GOOS {
- case "aix", "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "illumos", "solaris":
+ case "aix", "darwin", "ios", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "illumos", "solaris":
break
default:
t.Skip("skipping for not implemented platforms")
@@ -169,7 +169,7 @@ func TestRemoveAllLongPath(t *testing.T) {
t.Fatalf("Could not get wd: %s", err)
}
- startPath, err := ioutil.TempDir("", "TestRemoveAllLongPath-")
+ startPath, err := os.MkdirTemp("", "TestRemoveAllLongPath-")
if err != nil {
t.Fatalf("Could not create TempDir: %s", err)
}
@@ -211,7 +211,7 @@ func TestRemoveAllDot(t *testing.T) {
if err != nil {
t.Fatalf("Could not get wd: %s", err)
}
- tempDir, err := ioutil.TempDir("", "TestRemoveAllDot-")
+ tempDir, err := os.MkdirTemp("", "TestRemoveAllDot-")
if err != nil {
t.Fatalf("Could not create TempDir: %s", err)
}
@@ -236,7 +236,7 @@ func TestRemoveAllDot(t *testing.T) {
func TestRemoveAllDotDot(t *testing.T) {
t.Parallel()
- tempDir, err := ioutil.TempDir("", "TestRemoveAllDotDot-")
+ tempDir, err := os.MkdirTemp("", "TestRemoveAllDotDot-")
if err != nil {
t.Fatal(err)
}
@@ -261,7 +261,7 @@ func TestRemoveAllDotDot(t *testing.T) {
func TestRemoveReadOnlyDir(t *testing.T) {
t.Parallel()
- tempDir, err := ioutil.TempDir("", "TestRemoveReadOnlyDir-")
+ tempDir, err := os.MkdirTemp("", "TestRemoveReadOnlyDir-")
if err != nil {
t.Fatal(err)
}
@@ -298,7 +298,7 @@ func TestRemoveAllButReadOnlyAndPathError(t *testing.T) {
t.Parallel()
- tempDir, err := ioutil.TempDir("", "TestRemoveAllButReadOnly-")
+ tempDir, err := os.MkdirTemp("", "TestRemoveAllButReadOnly-")
if err != nil {
t.Fatal(err)
}
@@ -355,11 +355,12 @@ func TestRemoveAllButReadOnlyAndPathError(t *testing.T) {
// The error should be of type *PathError.
// see issue 30491 for details.
if pathErr, ok := err.(*PathError); ok {
- if g, w := pathErr.Path, filepath.Join(tempDir, "b", "y"); g != w {
- t.Errorf("got %q, expected pathErr.path %q", g, w)
+ want := filepath.Join(tempDir, "b", "y")
+ if pathErr.Path != want {
+ t.Errorf("RemoveAll(%q): err.Path=%q, want %q", tempDir, pathErr.Path, want)
}
} else {
- t.Errorf("got %T, expected *os.PathError", err)
+ t.Errorf("RemoveAll(%q): error has type %T, want *fs.PathError", tempDir, err)
}
for _, dir := range dirs {
@@ -388,7 +389,7 @@ func TestRemoveUnreadableDir(t *testing.T) {
t.Parallel()
- tempDir, err := ioutil.TempDir("", "TestRemoveAllButReadOnly-")
+ tempDir, err := os.MkdirTemp("", "TestRemoveAllButReadOnly-")
if err != nil {
t.Fatal(err)
}
@@ -412,7 +413,7 @@ func TestRemoveAllWithMoreErrorThanReqSize(t *testing.T) {
t.Skip("skipping in short mode")
}
- tmpDir, err := ioutil.TempDir("", "TestRemoveAll-")
+ tmpDir, err := os.MkdirTemp("", "TestRemoveAll-")
if err != nil {
t.Fatal(err)
}
diff --git a/libgo/go/os/signal/example_unix_test.go b/libgo/go/os/signal/example_unix_test.go
new file mode 100644
index 0000000..a0af37a
--- /dev/null
+++ b/libgo/go/os/signal/example_unix_test.go
@@ -0,0 +1,47 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
+
+package signal_test
+
+import (
+ "context"
+ "fmt"
+ "log"
+ "os"
+ "os/signal"
+ "time"
+)
+
+// This example passes a context with a signal to tell a blocking function that
+// it should abandon its work after a signal is received.
+func ExampleNotifyContext() {
+ ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
+ defer stop()
+
+ p, err := os.FindProcess(os.Getpid())
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // On a Unix-like system, pressing Ctrl+C on a keyboard sends a
+ // SIGINT signal to the process of the program in execution.
+ //
+ // This example simulates that by sending a SIGINT signal to itself.
+ if err := p.Signal(os.Interrupt); err != nil {
+ log.Fatal(err)
+ }
+
+ select {
+ case <-time.After(time.Second):
+ fmt.Println("missed signal")
+ case <-ctx.Done():
+ fmt.Println(ctx.Err()) // prints "context canceled"
+ stop() // stop receiving signal notifications as soon as possible.
+ }
+
+ // Output:
+ // context canceled
+}
diff --git a/libgo/go/os/signal/signal.go b/libgo/go/os/signal/signal.go
index 8e31aa2..4250a7e 100644
--- a/libgo/go/os/signal/signal.go
+++ b/libgo/go/os/signal/signal.go
@@ -5,6 +5,7 @@
package signal
import (
+ "context"
"os"
"sync"
)
@@ -257,3 +258,77 @@ func process(sig os.Signal) {
}
}
}
+
+// NotifyContext returns a copy of the parent context that is marked done
+// (its Done channel is closed) when one of the listed signals arrives,
+// when the returned stop function is called, or when the parent context's
+// Done channel is closed, whichever happens first.
+//
+// The stop function unregisters the signal behavior, which, like signal.Reset,
+// may restore the default behavior for a given signal. For example, the default
+// behavior of a Go program receiving os.Interrupt is to exit. Calling
+// NotifyContext(parent, os.Interrupt) will change the behavior to cancel
+// the returned context. Future interrupts received will not trigger the default
+// (exit) behavior until the returned stop function is called.
+//
+// The stop function releases resources associated with it, so code should
+// call stop as soon as the operations running in this Context complete and
+// signals no longer need to be diverted to the context.
+func NotifyContext(parent context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc) {
+ ctx, cancel := context.WithCancel(parent)
+ c := &signalCtx{
+ Context: ctx,
+ cancel: cancel,
+ signals: signals,
+ }
+ c.ch = make(chan os.Signal, 1)
+ Notify(c.ch, c.signals...)
+ if ctx.Err() == nil {
+ go func() {
+ select {
+ case <-c.ch:
+ c.cancel()
+ case <-c.Done():
+ }
+ }()
+ }
+ return c, c.stop
+}
+
+type signalCtx struct {
+ context.Context
+
+ cancel context.CancelFunc
+ signals []os.Signal
+ ch chan os.Signal
+}
+
+func (c *signalCtx) stop() {
+ c.cancel()
+ Stop(c.ch)
+}
+
+type stringer interface {
+ String() string
+}
+
+func (c *signalCtx) String() string {
+ var buf []byte
+ // We know that the type of c.Context is context.cancelCtx, and we know that the
+ // String method of cancelCtx returns a string that ends with ".WithCancel".
+ name := c.Context.(stringer).String()
+ name = name[:len(name)-len(".WithCancel")]
+ buf = append(buf, "signal.NotifyContext("+name...)
+ if len(c.signals) != 0 {
+ buf = append(buf, ", ["...)
+ for i, s := range c.signals {
+ buf = append(buf, s.String()...)
+ if i != len(c.signals)-1 {
+ buf = append(buf, ' ')
+ }
+ }
+ buf = append(buf, ']')
+ }
+ buf = append(buf, ')')
+ return string(buf)
+}
diff --git a/libgo/go/os/signal/signal_cgo_test.go b/libgo/go/os/signal/signal_cgo_test.go
index a117221..a8a4856 100644
--- a/libgo/go/os/signal/signal_cgo_test.go
+++ b/libgo/go/os/signal/signal_cgo_test.go
@@ -17,6 +17,7 @@ import (
"context"
"fmt"
"io"
+ "io/fs"
"os"
"os/exec"
ptypkg "os/signal/internal/pty"
@@ -127,7 +128,7 @@ func TestTerminalSignal(t *testing.T) {
if len(line) > 0 || len(handled) > 0 {
t.Logf("%q", append(handled, line...))
}
- if perr, ok := err.(*os.PathError); ok {
+ if perr, ok := err.(*fs.PathError); ok {
err = perr.Err
}
// EOF means pty is closed.
diff --git a/libgo/go/os/signal/signal_test.go b/libgo/go/os/signal/signal_test.go
index 98a1cc1..66b8327 100644
--- a/libgo/go/os/signal/signal_test.go
+++ b/libgo/go/os/signal/signal_test.go
@@ -8,10 +8,10 @@ package signal
import (
"bytes"
+ "context"
"flag"
"fmt"
"internal/testenv"
- "io/ioutil"
"os"
"os/exec"
"runtime"
@@ -303,7 +303,7 @@ func TestDetectNohup(t *testing.T) {
os.Remove("nohup.out")
out, err := exec.Command("/usr/bin/nohup", os.Args[0], "-test.run=TestDetectNohup", "-check_sighup_ignored").CombinedOutput()
- data, _ := ioutil.ReadFile("nohup.out")
+ data, _ := os.ReadFile("nohup.out")
os.Remove("nohup.out")
if err != nil {
t.Errorf("ran test with -check_sighup_ignored under nohup and it failed: expected success.\nError: %v\nOutput:\n%s%s", err, out, data)
@@ -674,3 +674,164 @@ func TestTime(t *testing.T) {
close(stop)
<-done
}
+
+func TestNotifyContext(t *testing.T) {
+ c, stop := NotifyContext(context.Background(), syscall.SIGINT)
+ defer stop()
+
+ if want, got := "signal.NotifyContext(context.Background, [interrupt])", fmt.Sprint(c); want != got {
+ t.Errorf("c.String() = %q, want %q", got, want)
+ }
+
+ syscall.Kill(syscall.Getpid(), syscall.SIGINT)
+ select {
+ case <-c.Done():
+ if got := c.Err(); got != context.Canceled {
+ t.Errorf("c.Err() = %q, want %q", got, context.Canceled)
+ }
+ case <-time.After(time.Second):
+ t.Errorf("timed out waiting for context to be done after SIGINT")
+ }
+}
+
+func TestNotifyContextStop(t *testing.T) {
+ Ignore(syscall.SIGHUP)
+ if !Ignored(syscall.SIGHUP) {
+ t.Errorf("expected SIGHUP to be ignored when explicitly ignoring it.")
+ }
+
+ parent, cancelParent := context.WithCancel(context.Background())
+ defer cancelParent()
+ c, stop := NotifyContext(parent, syscall.SIGHUP)
+ defer stop()
+
+ // If we're being notified, then the signal should not be ignored.
+ if Ignored(syscall.SIGHUP) {
+ t.Errorf("expected SIGHUP to not be ignored.")
+ }
+
+ if want, got := "signal.NotifyContext(context.Background.WithCancel, [hangup])", fmt.Sprint(c); want != got {
+ t.Errorf("c.String() = %q, wanted %q", got, want)
+ }
+
+ stop()
+ select {
+ case <-c.Done():
+ if got := c.Err(); got != context.Canceled {
+ t.Errorf("c.Err() = %q, want %q", got, context.Canceled)
+ }
+ case <-time.After(time.Second):
+ t.Errorf("timed out waiting for context to be done after calling stop")
+ }
+}
+
+func TestNotifyContextCancelParent(t *testing.T) {
+ parent, cancelParent := context.WithCancel(context.Background())
+ defer cancelParent()
+ c, stop := NotifyContext(parent, syscall.SIGINT)
+ defer stop()
+
+ if want, got := "signal.NotifyContext(context.Background.WithCancel, [interrupt])", fmt.Sprint(c); want != got {
+ t.Errorf("c.String() = %q, want %q", got, want)
+ }
+
+ cancelParent()
+ select {
+ case <-c.Done():
+ if got := c.Err(); got != context.Canceled {
+ t.Errorf("c.Err() = %q, want %q", got, context.Canceled)
+ }
+ case <-time.After(time.Second):
+ t.Errorf("timed out waiting for parent context to be canceled")
+ }
+}
+
+func TestNotifyContextPrematureCancelParent(t *testing.T) {
+ parent, cancelParent := context.WithCancel(context.Background())
+ defer cancelParent()
+
+ cancelParent() // Prematurely cancel context before calling NotifyContext.
+ c, stop := NotifyContext(parent, syscall.SIGINT)
+ defer stop()
+
+ if want, got := "signal.NotifyContext(context.Background.WithCancel, [interrupt])", fmt.Sprint(c); want != got {
+ t.Errorf("c.String() = %q, want %q", got, want)
+ }
+
+ select {
+ case <-c.Done():
+ if got := c.Err(); got != context.Canceled {
+ t.Errorf("c.Err() = %q, want %q", got, context.Canceled)
+ }
+ case <-time.After(time.Second):
+ t.Errorf("timed out waiting for parent context to be canceled")
+ }
+}
+
+func TestNotifyContextSimultaneousNotifications(t *testing.T) {
+ c, stop := NotifyContext(context.Background(), syscall.SIGINT)
+ defer stop()
+
+ if want, got := "signal.NotifyContext(context.Background, [interrupt])", fmt.Sprint(c); want != got {
+ t.Errorf("c.String() = %q, want %q", got, want)
+ }
+
+ var wg sync.WaitGroup
+ n := 10
+ wg.Add(n)
+ for i := 0; i < n; i++ {
+ go func() {
+ syscall.Kill(syscall.Getpid(), syscall.SIGINT)
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+ select {
+ case <-c.Done():
+ if got := c.Err(); got != context.Canceled {
+ t.Errorf("c.Err() = %q, want %q", got, context.Canceled)
+ }
+ case <-time.After(time.Second):
+ t.Errorf("expected context to be canceled")
+ }
+}
+
+func TestNotifyContextSimultaneousStop(t *testing.T) {
+ c, stop := NotifyContext(context.Background(), syscall.SIGINT)
+ defer stop()
+
+ if want, got := "signal.NotifyContext(context.Background, [interrupt])", fmt.Sprint(c); want != got {
+ t.Errorf("c.String() = %q, want %q", got, want)
+ }
+
+ var wg sync.WaitGroup
+ n := 10
+ wg.Add(n)
+ for i := 0; i < n; i++ {
+ go func() {
+ stop()
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+ select {
+ case <-c.Done():
+ if got := c.Err(); got != context.Canceled {
+ t.Errorf("c.Err() = %q, want %q", got, context.Canceled)
+ }
+ case <-time.After(time.Second):
+ t.Errorf("expected context to be canceled")
+ }
+}
+
+func TestNotifyContextStringer(t *testing.T) {
+ parent, cancelParent := context.WithCancel(context.Background())
+ defer cancelParent()
+ c, stop := NotifyContext(parent, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
+ defer stop()
+
+ want := `signal.NotifyContext(context.Background.WithCancel, [hangup interrupt terminated])`
+ if got := fmt.Sprint(c); got != want {
+ t.Errorf("c.String() = %q, want %q", got, want)
+ }
+}
diff --git a/libgo/go/os/stat_plan9.go b/libgo/go/os/stat_plan9.go
index b43339a..57ae6fb 100644
--- a/libgo/go/os/stat_plan9.go
+++ b/libgo/go/os/stat_plan9.go
@@ -11,7 +11,7 @@ import (
const bitSize16 = 2
-func fileInfoFromStat(d *syscall.Dir) FileInfo {
+func fileInfoFromStat(d *syscall.Dir) *fileStat {
fs := &fileStat{
name: d.Name,
size: d.Length,
@@ -65,7 +65,7 @@ func dirstat(arg interface{}) (*syscall.Dir, error) {
}
if n < bitSize16 {
- return nil, &PathError{"stat", name, err}
+ return nil, &PathError{Op: "stat", Path: name, Err: err}
}
// Pull the real size out of the stat message.
@@ -76,7 +76,7 @@ func dirstat(arg interface{}) (*syscall.Dir, error) {
if size <= n {
d, err := syscall.UnmarshalDir(buf[:n])
if err != nil {
- return nil, &PathError{"stat", name, err}
+ return nil, &PathError{Op: "stat", Path: name, Err: err}
}
return d, nil
}
@@ -87,7 +87,7 @@ func dirstat(arg interface{}) (*syscall.Dir, error) {
err = syscall.ErrBadStat
}
- return nil, &PathError{"stat", name, err}
+ return nil, &PathError{Op: "stat", Path: name, Err: err}
}
// statNolog implements Stat for Plan 9.
diff --git a/libgo/go/os/stat_test.go b/libgo/go/os/stat_test.go
index 60f3b4c..c409f0f 100644
--- a/libgo/go/os/stat_test.go
+++ b/libgo/go/os/stat_test.go
@@ -6,7 +6,7 @@ package os_test
import (
"internal/testenv"
- "io/ioutil"
+ "io/fs"
"os"
"path/filepath"
"runtime"
@@ -14,7 +14,7 @@ import (
)
// testStatAndLstat verifies that all os.Stat, os.Lstat os.File.Stat and os.Readdir work.
-func testStatAndLstat(t *testing.T, path string, isLink bool, statCheck, lstatCheck func(*testing.T, string, os.FileInfo)) {
+func testStatAndLstat(t *testing.T, path string, isLink bool, statCheck, lstatCheck func(*testing.T, string, fs.FileInfo)) {
// test os.Stat
sfi, err := os.Stat(path)
if err != nil {
@@ -70,7 +70,7 @@ func testStatAndLstat(t *testing.T, path string, isLink bool, statCheck, lstatCh
}
}
- // test os.FileInfo returned by os.Readdir
+ // test fs.FileInfo returned by os.Readdir
if len(path) > 0 && os.IsPathSeparator(path[len(path)-1]) {
// skip os.Readdir test of directories with slash at the end
return
@@ -88,7 +88,7 @@ func testStatAndLstat(t *testing.T, path string, isLink bool, statCheck, lstatCh
t.Error(err)
return
}
- var lsfi2 os.FileInfo
+ var lsfi2 fs.FileInfo
base := filepath.Base(path)
for _, fi2 := range fis {
if fi2.Name() == base {
@@ -108,34 +108,34 @@ func testStatAndLstat(t *testing.T, path string, isLink bool, statCheck, lstatCh
}
// testIsDir verifies that fi refers to directory.
-func testIsDir(t *testing.T, path string, fi os.FileInfo) {
+func testIsDir(t *testing.T, path string, fi fs.FileInfo) {
t.Helper()
if !fi.IsDir() {
t.Errorf("%q should be a directory", path)
}
- if fi.Mode()&os.ModeSymlink != 0 {
+ if fi.Mode()&fs.ModeSymlink != 0 {
t.Errorf("%q should not be a symlink", path)
}
}
// testIsSymlink verifies that fi refers to symlink.
-func testIsSymlink(t *testing.T, path string, fi os.FileInfo) {
+func testIsSymlink(t *testing.T, path string, fi fs.FileInfo) {
t.Helper()
if fi.IsDir() {
t.Errorf("%q should not be a directory", path)
}
- if fi.Mode()&os.ModeSymlink == 0 {
+ if fi.Mode()&fs.ModeSymlink == 0 {
t.Errorf("%q should be a symlink", path)
}
}
// testIsFile verifies that fi refers to file.
-func testIsFile(t *testing.T, path string, fi os.FileInfo) {
+func testIsFile(t *testing.T, path string, fi fs.FileInfo) {
t.Helper()
if fi.IsDir() {
t.Errorf("%q should not be a directory", path)
}
- if fi.Mode()&os.ModeSymlink != 0 {
+ if fi.Mode()&fs.ModeSymlink != 0 {
t.Errorf("%q should not be a symlink", path)
}
}
@@ -185,7 +185,7 @@ func testSymlinkSameFile(t *testing.T, path, link string) {
func TestDirAndSymlinkStats(t *testing.T) {
testenv.MustHaveSymlink(t)
- tmpdir, err := ioutil.TempDir("", "TestDirAndSymlinkStats")
+ tmpdir, err := os.MkdirTemp("", "TestDirAndSymlinkStats")
if err != nil {
t.Fatal(err)
}
@@ -218,14 +218,14 @@ func TestDirAndSymlinkStats(t *testing.T) {
func TestFileAndSymlinkStats(t *testing.T) {
testenv.MustHaveSymlink(t)
- tmpdir, err := ioutil.TempDir("", "TestFileAndSymlinkStats")
+ tmpdir, err := os.MkdirTemp("", "TestFileAndSymlinkStats")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
file := filepath.Join(tmpdir, "file")
- err = ioutil.WriteFile(file, []byte(""), 0644)
+ err = os.WriteFile(file, []byte(""), 0644)
if err != nil {
t.Fatal(err)
}
@@ -252,7 +252,7 @@ func TestFileAndSymlinkStats(t *testing.T) {
func TestSymlinkWithTrailingSlash(t *testing.T) {
testenv.MustHaveSymlink(t)
- tmpdir, err := ioutil.TempDir("", "TestSymlinkWithTrailingSlash")
+ tmpdir, err := os.MkdirTemp("", "TestSymlinkWithTrailingSlash")
if err != nil {
t.Fatal(err)
}
diff --git a/libgo/go/os/stat_unix.go b/libgo/go/os/stat_unix.go
index d489ad1..7873bf9 100644
--- a/libgo/go/os/stat_unix.go
+++ b/libgo/go/os/stat_unix.go
@@ -19,7 +19,7 @@ func (f *File) Stat() (FileInfo, error) {
var fs fileStat
err := f.pfd.Fstat(&fs.sys)
if err != nil {
- return nil, &PathError{"stat", f.name, err}
+ return nil, &PathError{Op: "stat", Path: f.name, Err: err}
}
fillFileStatFromSys(&fs, f.name)
return &fs, nil
@@ -28,9 +28,11 @@ func (f *File) Stat() (FileInfo, error) {
// statNolog stats a file with no test logging.
func statNolog(name string) (FileInfo, error) {
var fs fileStat
- err := syscall.Stat(name, &fs.sys)
+ err := ignoringEINTR(func() error {
+ return syscall.Stat(name, &fs.sys)
+ })
if err != nil {
- return nil, &PathError{"stat", name, err}
+ return nil, &PathError{Op: "stat", Path: name, Err: err}
}
fillFileStatFromSys(&fs, name)
return &fs, nil
@@ -39,9 +41,11 @@ func statNolog(name string) (FileInfo, error) {
// lstatNolog lstats a file with no test logging.
func lstatNolog(name string) (FileInfo, error) {
var fs fileStat
- err := syscall.Lstat(name, &fs.sys)
+ err := ignoringEINTR(func() error {
+ return syscall.Lstat(name, &fs.sys)
+ })
if err != nil {
- return nil, &PathError{"lstat", name, err}
+ return nil, &PathError{Op: "lstat", Path: name, Err: err}
}
fillFileStatFromSys(&fs, name)
return &fs, nil
diff --git a/libgo/go/os/tempfile.go b/libgo/go/os/tempfile.go
new file mode 100644
index 0000000..2728485
--- /dev/null
+++ b/libgo/go/os/tempfile.go
@@ -0,0 +1,118 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+import (
+ "errors"
+ "strings"
+)
+
+// fastrand provided by runtime.
+// We generate random temporary file names so that there's a good
+// chance the file doesn't exist yet - keeps the number of tries in
+// TempFile to a minimum.
+func fastrand() uint32
+
+func nextRandom() string {
+ return uitoa(uint(fastrand()))
+}
+
+// CreateTemp creates a new temporary file in the directory dir,
+// opens the file for reading and writing, and returns the resulting file.
+// The filename is generated by taking pattern and adding a random string to the end.
+// If pattern includes a "*", the random string replaces the last "*".
+// If dir is the empty string, TempFile uses the default directory for temporary files, as returned by TempDir.
+// Multiple programs or goroutines calling CreateTemp simultaneously will not choose the same file.
+// The caller can use the file's Name method to find the pathname of the file.
+// It is the caller's responsibility to remove the file when it is no longer needed.
+func CreateTemp(dir, pattern string) (*File, error) {
+ if dir == "" {
+ dir = TempDir()
+ }
+
+ prefix, suffix, err := prefixAndSuffix(pattern)
+ if err != nil {
+ return nil, &PathError{Op: "createtemp", Path: pattern, Err: err}
+ }
+ prefix = joinPath(dir, prefix)
+
+ try := 0
+ for {
+ name := prefix + nextRandom() + suffix
+ f, err := OpenFile(name, O_RDWR|O_CREATE|O_EXCL, 0600)
+ if IsExist(err) {
+ if try++; try < 10000 {
+ continue
+ }
+ return nil, &PathError{Op: "createtemp", Path: dir + string(PathSeparator) + prefix + "*" + suffix, Err: ErrExist}
+ }
+ return f, err
+ }
+}
+
+var errPatternHasSeparator = errors.New("pattern contains path separator")
+
+// prefixAndSuffix splits pattern by the last wildcard "*", if applicable,
+// returning prefix as the part before "*" and suffix as the part after "*".
+func prefixAndSuffix(pattern string) (prefix, suffix string, err error) {
+ for i := 0; i < len(pattern); i++ {
+ if IsPathSeparator(pattern[i]) {
+ return "", "", errPatternHasSeparator
+ }
+ }
+ if pos := strings.LastIndex(pattern, "*"); pos != -1 {
+ prefix, suffix = pattern[:pos], pattern[pos+1:]
+ } else {
+ prefix = pattern
+ }
+ return prefix, suffix, nil
+}
+
+// MkdirTemp creates a new temporary directory in the directory dir
+// and returns the pathname of the new directory.
+// The new directory's name is generated by adding a random string to the end of pattern.
+// If pattern includes a "*", the random string replaces the last "*" instead.
+// If dir is the empty string, TempFile uses the default directory for temporary files, as returned by TempDir.
+// Multiple programs or goroutines calling MkdirTemp simultaneously will not choose the same directory.
+// It is the caller's responsibility to remove the directory when it is no longer needed.
+func MkdirTemp(dir, pattern string) (string, error) {
+ if dir == "" {
+ dir = TempDir()
+ }
+
+ prefix, suffix, err := prefixAndSuffix(pattern)
+ if err != nil {
+ return "", &PathError{Op: "mkdirtemp", Path: pattern, Err: err}
+ }
+ prefix = joinPath(dir, prefix)
+
+ try := 0
+ for {
+ name := prefix + nextRandom() + suffix
+ err := Mkdir(name, 0700)
+ if err == nil {
+ return name, nil
+ }
+ if IsExist(err) {
+ if try++; try < 10000 {
+ continue
+ }
+ return "", &PathError{Op: "mkdirtemp", Path: dir + string(PathSeparator) + prefix + "*" + suffix, Err: ErrExist}
+ }
+ if IsNotExist(err) {
+ if _, err := Stat(dir); IsNotExist(err) {
+ return "", err
+ }
+ }
+ return "", err
+ }
+}
+
+func joinPath(dir, name string) string {
+ if len(dir) > 0 && IsPathSeparator(dir[len(dir)-1]) {
+ return dir + name
+ }
+ return dir + string(PathSeparator) + name
+}
diff --git a/libgo/go/os/tempfile_test.go b/libgo/go/os/tempfile_test.go
new file mode 100644
index 0000000..e71a244
--- /dev/null
+++ b/libgo/go/os/tempfile_test.go
@@ -0,0 +1,193 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os_test
+
+import (
+ "errors"
+ "io/fs"
+ . "os"
+ "path/filepath"
+ "regexp"
+ "strings"
+ "testing"
+)
+
+func TestCreateTemp(t *testing.T) {
+ dir, err := MkdirTemp("", "TestCreateTempBadDir")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer RemoveAll(dir)
+
+ nonexistentDir := filepath.Join(dir, "_not_exists_")
+ f, err := CreateTemp(nonexistentDir, "foo")
+ if f != nil || err == nil {
+ t.Errorf("CreateTemp(%q, `foo`) = %v, %v", nonexistentDir, f, err)
+ }
+}
+
+func TestCreateTempPattern(t *testing.T) {
+ tests := []struct{ pattern, prefix, suffix string }{
+ {"tempfile_test", "tempfile_test", ""},
+ {"tempfile_test*", "tempfile_test", ""},
+ {"tempfile_test*xyz", "tempfile_test", "xyz"},
+ }
+ for _, test := range tests {
+ f, err := CreateTemp("", test.pattern)
+ if err != nil {
+ t.Errorf("CreateTemp(..., %q) error: %v", test.pattern, err)
+ continue
+ }
+ defer Remove(f.Name())
+ base := filepath.Base(f.Name())
+ f.Close()
+ if !(strings.HasPrefix(base, test.prefix) && strings.HasSuffix(base, test.suffix)) {
+ t.Errorf("CreateTemp pattern %q created bad name %q; want prefix %q & suffix %q",
+ test.pattern, base, test.prefix, test.suffix)
+ }
+ }
+}
+
+func TestCreateTempBadPattern(t *testing.T) {
+ tmpDir, err := MkdirTemp("", t.Name())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer RemoveAll(tmpDir)
+
+ const sep = string(PathSeparator)
+ tests := []struct {
+ pattern string
+ wantErr bool
+ }{
+ {"ioutil*test", false},
+ {"tempfile_test*foo", false},
+ {"tempfile_test" + sep + "foo", true},
+ {"tempfile_test*" + sep + "foo", true},
+ {"tempfile_test" + sep + "*foo", true},
+ {sep + "tempfile_test" + sep + "*foo", true},
+ {"tempfile_test*foo" + sep, true},
+ }
+ for _, tt := range tests {
+ t.Run(tt.pattern, func(t *testing.T) {
+ tmpfile, err := CreateTemp(tmpDir, tt.pattern)
+ if tmpfile != nil {
+ defer tmpfile.Close()
+ }
+ if tt.wantErr {
+ if err == nil {
+ t.Errorf("CreateTemp(..., %#q) succeeded, expected error", tt.pattern)
+ }
+ if !errors.Is(err, ErrPatternHasSeparator) {
+ t.Errorf("CreateTemp(..., %#q): %v, expected ErrPatternHasSeparator", tt.pattern, err)
+ }
+ } else if err != nil {
+ t.Errorf("CreateTemp(..., %#q): %v", tt.pattern, err)
+ }
+ })
+ }
+}
+
+func TestMkdirTemp(t *testing.T) {
+ name, err := MkdirTemp("/_not_exists_", "foo")
+ if name != "" || err == nil {
+ t.Errorf("MkdirTemp(`/_not_exists_`, `foo`) = %v, %v", name, err)
+ }
+
+ tests := []struct {
+ pattern string
+ wantPrefix, wantSuffix string
+ }{
+ {"tempfile_test", "tempfile_test", ""},
+ {"tempfile_test*", "tempfile_test", ""},
+ {"tempfile_test*xyz", "tempfile_test", "xyz"},
+ }
+
+ dir := filepath.Clean(TempDir())
+
+ runTestMkdirTemp := func(t *testing.T, pattern, wantRePat string) {
+ name, err := MkdirTemp(dir, pattern)
+ if name == "" || err != nil {
+ t.Fatalf("MkdirTemp(dir, `tempfile_test`) = %v, %v", name, err)
+ }
+ defer Remove(name)
+
+ re := regexp.MustCompile(wantRePat)
+ if !re.MatchString(name) {
+ t.Errorf("MkdirTemp(%q, %q) created bad name\n\t%q\ndid not match pattern\n\t%q", dir, pattern, name, wantRePat)
+ }
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.pattern, func(t *testing.T) {
+ wantRePat := "^" + regexp.QuoteMeta(filepath.Join(dir, tt.wantPrefix)) + "[0-9]+" + regexp.QuoteMeta(tt.wantSuffix) + "$"
+ runTestMkdirTemp(t, tt.pattern, wantRePat)
+ })
+ }
+
+ // Separately testing "*xyz" (which has no prefix). That is when constructing the
+ // pattern to assert on, as in the previous loop, using filepath.Join for an empty
+ // prefix filepath.Join(dir, ""), produces the pattern:
+ // ^<DIR>[0-9]+xyz$
+ // yet we just want to match
+ // "^<DIR>/[0-9]+xyz"
+ t.Run("*xyz", func(t *testing.T) {
+ wantRePat := "^" + regexp.QuoteMeta(filepath.Join(dir)) + regexp.QuoteMeta(string(filepath.Separator)) + "[0-9]+xyz$"
+ runTestMkdirTemp(t, "*xyz", wantRePat)
+ })
+}
+
+// test that we return a nice error message if the dir argument to TempDir doesn't
+// exist (or that it's empty and TempDir doesn't exist)
+func TestMkdirTempBadDir(t *testing.T) {
+ dir, err := MkdirTemp("", "MkdirTempBadDir")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer RemoveAll(dir)
+
+ badDir := filepath.Join(dir, "not-exist")
+ _, err = MkdirTemp(badDir, "foo")
+ if pe, ok := err.(*fs.PathError); !ok || !IsNotExist(err) || pe.Path != badDir {
+ t.Errorf("TempDir error = %#v; want PathError for path %q satisifying IsNotExist", err, badDir)
+ }
+}
+
+func TestMkdirTempBadPattern(t *testing.T) {
+ tmpDir, err := MkdirTemp("", t.Name())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer RemoveAll(tmpDir)
+
+ const sep = string(PathSeparator)
+ tests := []struct {
+ pattern string
+ wantErr bool
+ }{
+ {"ioutil*test", false},
+ {"tempfile_test*foo", false},
+ {"tempfile_test" + sep + "foo", true},
+ {"tempfile_test*" + sep + "foo", true},
+ {"tempfile_test" + sep + "*foo", true},
+ {sep + "tempfile_test" + sep + "*foo", true},
+ {"tempfile_test*foo" + sep, true},
+ }
+ for _, tt := range tests {
+ t.Run(tt.pattern, func(t *testing.T) {
+ _, err := MkdirTemp(tmpDir, tt.pattern)
+ if tt.wantErr {
+ if err == nil {
+ t.Errorf("MkdirTemp(..., %#q) succeeded, expected error", tt.pattern)
+ }
+ if !errors.Is(err, ErrPatternHasSeparator) {
+ t.Errorf("MkdirTemp(..., %#q): %v, expected ErrPatternHasSeparator", tt.pattern, err)
+ }
+ } else if err != nil {
+ t.Errorf("MkdirTemp(..., %#q): %v", tt.pattern, err)
+ }
+ })
+ }
+}
diff --git a/libgo/go/os/testdata/hello b/libgo/go/os/testdata/hello
new file mode 100644
index 0000000..e47c092
--- /dev/null
+++ b/libgo/go/os/testdata/hello
@@ -0,0 +1 @@
+Hello, Gophers!
diff --git a/libgo/go/os/timeout_test.go b/libgo/go/os/timeout_test.go
index 99b94c2..0a39f46 100644
--- a/libgo/go/os/timeout_test.go
+++ b/libgo/go/os/timeout_test.go
@@ -11,7 +11,6 @@ package os_test
import (
"fmt"
"io"
- "io/ioutil"
"math/rand"
"os"
"os/signal"
@@ -29,7 +28,7 @@ func TestNonpollableDeadline(t *testing.T) {
t.Skipf("skipping on %s", runtime.GOOS)
}
- f, err := ioutil.TempFile("", "ostest")
+ f, err := os.CreateTemp("", "ostest")
if err != nil {
t.Fatal(err)
}
@@ -429,7 +428,7 @@ func testVariousDeadlines(t *testing.T) {
if err := r.SetDeadline(t0.Add(timeout)); err != nil {
t.Error(err)
}
- n, err := io.Copy(ioutil.Discard, r)
+ n, err := io.Copy(io.Discard, r)
dt := time.Since(t0)
r.Close()
actvch <- result{n, err, dt}
@@ -565,7 +564,7 @@ func TestRacyWrite(t *testing.T) {
var wg sync.WaitGroup
defer wg.Wait()
- go io.Copy(ioutil.Discard, r)
+ go io.Copy(io.Discard, r)
w.SetWriteDeadline(time.Now().Add(time.Millisecond))
for i := 0; i < 10; i++ {
diff --git a/libgo/go/os/types.go b/libgo/go/os/types.go
index 4b6c084..d8edd98 100644
--- a/libgo/go/os/types.go
+++ b/libgo/go/os/types.go
@@ -5,8 +5,8 @@
package os
import (
+ "io/fs"
"syscall"
- "time"
)
// Getpagesize returns the underlying system's memory page size.
@@ -18,21 +18,14 @@ type File struct {
}
// 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 FileInfo = fs.FileInfo
// A FileMode represents a file's mode and permission bits.
// The bits have the same definition on all systems, so that
// information about files can be moved from one system
// to another portably. Not all bits apply to all systems.
// The only required bit is ModeDir for directories.
-type FileMode uint32
+type FileMode = fs.FileMode
// The defined file mode bits are the most significant bits of the FileMode.
// The nine least-significant bits are the standard Unix rwxrwxrwx permissions.
@@ -42,69 +35,26 @@ type FileMode uint32
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
+ ModeDir = fs.ModeDir // d: is a directory
+ ModeAppend = fs.ModeAppend // a: append-only
+ ModeExclusive = fs.ModeExclusive // l: exclusive use
+ ModeTemporary = fs.ModeTemporary // T: temporary file; Plan 9 only
+ ModeSymlink = fs.ModeSymlink // L: symbolic link
+ ModeDevice = fs.ModeDevice // D: device file
+ ModeNamedPipe = fs.ModeNamedPipe // p: named pipe (FIFO)
+ ModeSocket = fs.ModeSocket // S: Unix domain socket
+ ModeSetuid = fs.ModeSetuid // u: setuid
+ ModeSetgid = fs.ModeSetgid // g: setgid
+ ModeCharDevice = fs.ModeCharDevice // c: Unix character device, when ModeDevice is set
+ ModeSticky = fs.ModeSticky // t: sticky
+ ModeIrregular = fs.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
+ ModeType = fs.ModeType
- ModePerm FileMode = 0777 // Unix permission bits
+ ModePerm = fs.ModePerm // Unix permission bits, 0o777
)
-func (m FileMode) String() string {
- const str = "dalTLDpSugct?"
- var buf [32]byte // Mode is uint32.
- w := 0
- for i, c := range str {
- if m&(1<<uint(32-1-i)) != 0 {
- buf[w] = byte(c)
- w++
- }
- }
- if w == 0 {
- buf[w] = '-'
- w++
- }
- const rwx = "rwxrwxrwx"
- for i, c := range rwx {
- if m&(1<<uint(9-1-i)) != 0 {
- buf[w] = byte(c)
- } else {
- buf[w] = '-'
- }
- w++
- }
- return string(buf[:w])
-}
-
-// IsDir reports whether m describes a directory.
-// That is, it tests for the ModeDir bit being set in m.
-func (m FileMode) IsDir() bool {
- return m&ModeDir != 0
-}
-
-// IsRegular reports whether m describes a regular file.
-// That is, it tests that no mode type bits are set.
-func (m FileMode) IsRegular() bool {
- return m&ModeType == 0
-}
-
-// Perm returns the Unix permission bits in m.
-func (m FileMode) Perm() FileMode {
- return m & ModePerm
-}
-
func (fs *fileStat) Name() string { return fs.name }
func (fs *fileStat) IsDir() bool { return fs.Mode().IsDir() }
diff --git a/libgo/go/os/types_windows.go b/libgo/go/os/types_windows.go
index 3d1a667..59bf5ca 100644
--- a/libgo/go/os/types_windows.go
+++ b/libgo/go/os/types_windows.go
@@ -45,7 +45,7 @@ func newFileStatFromGetFileInformationByHandle(path string, h syscall.Handle) (f
var d syscall.ByHandleFileInformation
err = syscall.GetFileInformationByHandle(h, &d)
if err != nil {
- return nil, &PathError{"GetFileInformationByHandle", path, err}
+ return nil, &PathError{Op: "GetFileInformationByHandle", Path: path, Err: err}
}
var ti windows.FILE_ATTRIBUTE_TAG_INFO
@@ -58,7 +58,7 @@ func newFileStatFromGetFileInformationByHandle(path string, h syscall.Handle) (f
// instance to indicate no symlinks are possible.
ti.ReparseTag = 0
} else {
- return nil, &PathError{"GetFileInformationByHandleEx", path, err}
+ return nil, &PathError{Op: "GetFileInformationByHandleEx", Path: path, Err: err}
}
}
@@ -197,7 +197,7 @@ func (fs *fileStat) saveInfoFromPath(path string) error {
var err error
fs.path, err = syscall.FullPath(fs.path)
if err != nil {
- return &PathError{"FullPath", path, err}
+ return &PathError{Op: "FullPath", Path: path, Err: err}
}
}
fs.name = basename(path)
diff --git a/libgo/go/os/user/lookup_plan9.go b/libgo/go/os/user/lookup_plan9.go
index ea3ce0b..33ae3a6 100644
--- a/libgo/go/os/user/lookup_plan9.go
+++ b/libgo/go/os/user/lookup_plan9.go
@@ -6,7 +6,6 @@ package user
import (
"fmt"
- "io/ioutil"
"os"
"syscall"
)
@@ -23,7 +22,7 @@ func init() {
}
func current() (*User, error) {
- ubytes, err := ioutil.ReadFile(userFile)
+ ubytes, err := os.ReadFile(userFile)
if err != nil {
return nil, fmt.Errorf("user: %s", err)
}