aboutsummaryrefslogtreecommitdiff
path: root/src/os
diff options
context:
space:
mode:
Diffstat (limited to 'src/os')
-rw-r--r--src/os/dir.go44
-rw-r--r--src/os/dir_darwin.go62
-rw-r--r--src/os/dir_plan9.go42
-rw-r--r--src/os/dir_unix.go122
-rw-r--r--src/os/dir_windows.go53
-rw-r--r--src/os/dirent_aix.go30
-rw-r--r--src/os/dirent_dragonfly.go55
-rw-r--r--src/os/dirent_freebsd.go47
-rw-r--r--src/os/dirent_js.go30
-rw-r--r--src/os/dirent_linux.go51
-rw-r--r--src/os/dirent_netbsd.go47
-rw-r--r--src/os/dirent_openbsd.go47
-rw-r--r--src/os/dirent_solaris.go30
-rw-r--r--src/os/endian_big.go9
-rw-r--r--src/os/endian_little.go9
-rw-r--r--src/os/error.go32
-rw-r--r--src/os/error_test.go55
-rw-r--r--src/os/error_unix_test.go11
-rw-r--r--src/os/error_windows_test.go11
-rw-r--r--src/os/example_test.go5
-rw-r--r--src/os/exec/example_test.go3
-rw-r--r--src/os/exec/exec_plan9.go4
-rw-r--r--src/os/exec/exec_test.go8
-rw-r--r--src/os/exec/exec_unix.go4
-rw-r--r--src/os/exec/exec_windows.go4
-rw-r--r--src/os/exec/lp_plan9.go3
-rw-r--r--src/os/exec/lp_unix.go3
-rw-r--r--src/os/exec/lp_windows.go5
-rw-r--r--src/os/exec/read3.go4
-rw-r--r--src/os/exec_plan9.go2
-rw-r--r--src/os/exec_posix.go2
-rw-r--r--src/os/export_test.go1
-rw-r--r--src/os/file.go31
-rw-r--r--src/os/file_plan9.go50
-rw-r--r--src/os/file_posix.go8
-rw-r--r--src/os/file_unix.go76
-rw-r--r--src/os/file_windows.go14
-rw-r--r--src/os/os_test.go135
-rw-r--r--src/os/os_windows_test.go11
-rw-r--r--src/os/path.go2
-rw-r--r--src/os/pipe_test.go17
-rw-r--r--src/os/removeall_at.go14
-rw-r--r--src/os/removeall_noat.go2
-rw-r--r--src/os/removeall_test.go2
-rw-r--r--src/os/signal/signal_cgo_test.go3
-rw-r--r--src/os/stat_plan9.go8
-rw-r--r--src/os/stat_test.go19
-rw-r--r--src/os/stat_unix.go6
-rw-r--r--src/os/stat_windows.go10
-rw-r--r--src/os/timeout_test.go4
-rw-r--r--src/os/types.go86
-rw-r--r--src/os/types_windows.go6
52 files changed, 982 insertions, 357 deletions
diff --git a/src/os/dir.go b/src/os/dir.go
index 1d7ced8061..b56d998459 100644
--- a/src/os/dir.go
+++ b/src/os/dir.go
@@ -4,6 +4,16 @@
package os
+import "io/fs"
+
+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 +29,14 @@ 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)
+ return infos, err
}
// Readdirnames reads the contents of the directory associated with file
@@ -45,5 +58,32 @@ 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)
+ 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)
+ 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
diff --git a/src/os/dir_darwin.go b/src/os/dir_darwin.go
index 476af6862e..deba3eb37f 100644
--- a/src/os/dir_darwin.go
+++ b/src/os/dir_darwin.go
@@ -24,11 +24,11 @@ func (d *dirInfo) close() {
d.dir = 0
}
-func (f *File) readdirnames(n int) (names []string, err error) {
+func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) {
if f.dirinfo == nil {
dir, call, errno := f.pfd.OpenDir()
if errno != nil {
- return nil, &PathError{call, f.name, errno}
+ return nil, nil, nil, &PathError{Op: call, Path: f.name, Err: errno}
}
f.dirinfo = &dirInfo{
dir: dir,
@@ -42,15 +42,14 @@ func (f *File) readdirnames(n int) (names []string, err error) {
n = -1
}
- names = make([]string, 0, size)
var dirent syscall.Dirent
var entptr *syscall.Dirent
- for len(names) < size || n == -1 {
+ for len(names)+len(dirents)+len(infos) < size || n == -1 {
if errno := readdir_r(d.dir, &dirent, &entptr); errno != 0 {
if errno == syscall.EINTR {
continue
}
- return names, &PathError{"readdir", f.name, errno}
+ return names, dirents, infos, &PathError{Op: "readdir", Path: f.name, Err: errno}
}
if entptr == nil { // EOF
break
@@ -69,13 +68,58 @@ func (f *File) readdirnames(n int) (names []string, err error) {
if string(name) == "." || string(name) == ".." {
continue
}
- names = append(names, string(name))
+ if mode == readdirName {
+ names = append(names, string(name))
+ } else if mode == readdirDirEntry {
+ de, err := newUnixDirent(f.name, string(name), dtToType(dirent.Type))
+ 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)
+ }
runtime.KeepAlive(f)
}
- if n >= 0 && len(names) == 0 {
- return names, io.EOF
+
+ if n > 0 && len(names)+len(dirents)+len(infos) == 0 {
+ return nil, nil, nil, io.EOF
+ }
+ return names, dirents, infos, nil
+}
+
+func dtToType(typ uint8) FileMode {
+ switch typ {
+ case syscall.DT_BLK:
+ return ModeDevice
+ case syscall.DT_CHR:
+ return ModeDevice | ModeCharDevice
+ case syscall.DT_DIR:
+ return ModeDir
+ case syscall.DT_FIFO:
+ return ModeNamedPipe
+ case syscall.DT_LNK:
+ return ModeSymlink
+ case syscall.DT_REG:
+ return 0
+ case syscall.DT_SOCK:
+ return ModeSocket
}
- return names, nil
+ return ^FileMode(0)
}
// Implemented in syscall/syscall_darwin.go.
diff --git a/src/os/dir_plan9.go b/src/os/dir_plan9.go
index 8195c02a46..8f6b0d6109 100644
--- a/src/os/dir_plan9.go
+++ b/src/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/src/os/dir_unix.go b/src/os/dir_unix.go
index 58ec406ab8..3e5a698350 100644
--- a/src/os/dir_unix.go
+++ b/src/os/dir_unix.go
@@ -10,6 +10,7 @@ import (
"io"
"runtime"
"syscall"
+ "unsafe"
)
// Auxiliary information if the File describes a directory
@@ -26,7 +27,7 @@ const (
func (d *dirInfo) close() {}
-func (f *File) readdirnames(n int) (names []string, err error) {
+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 {
f.dirinfo = new(dirInfo)
@@ -41,7 +42,6 @@ func (f *File) readdirnames(n int) (names []string, err error) {
n = -1
}
- names = make([]string, 0, size) // Empty with room to grow.
for n != 0 {
// Refill the buffer if necessary
if d.bufp >= d.nbuf {
@@ -50,7 +50,7 @@ func (f *File) readdirnames(n int) (names []string, err error) {
d.nbuf, errno = f.pfd.ReadDirent(d.buf)
runtime.KeepAlive(f)
if errno != nil {
- return names, &PathError{"readdirent", f.name, errno}
+ return names, dirents, infos, &PathError{Op: "readdirent", Path: f.name, Err: errno}
}
if d.nbuf <= 0 {
break // EOF
@@ -58,13 +58,115 @@ func (f *File) readdirnames(n int) (names []string, err error) {
}
// Drain the buffer
- var nb, nc int
- nb, nc, names = syscall.ParseDirent(d.buf[d.bufp:d.nbuf], n, names)
- d.bufp += nb
- n -= nc
+ buf := d.buf[d.bufp:d.nbuf]
+ reclen, ok := direntReclen(buf)
+ if !ok || reclen > uint64(len(buf)) {
+ break
+ }
+ rec := buf[:reclen]
+ d.bufp += int(reclen)
+ ino, ok := direntIno(rec)
+ if !ok {
+ break
+ }
+ if ino == 0 {
+ continue
+ }
+ const namoff = uint64(unsafe.Offsetof(syscall.Dirent{}.Name))
+ namlen, ok := direntNamlen(rec)
+ if !ok || namoff+namlen > uint64(len(rec)) {
+ break
+ }
+ name := rec[namoff : namoff+namlen]
+ for i, c := range name {
+ if c == 0 {
+ name = name[:i]
+ break
+ }
+ }
+ // Check for useless names before allocating a string.
+ if string(name) == "." || string(name) == ".." {
+ continue
+ }
+ n--
+ if mode == readdirName {
+ names = append(names, string(name))
+ } else if mode == readdirDirEntry {
+ de, err := newUnixDirent(f.name, string(name), direntType(rec))
+ 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)
+ }
}
- if n >= 0 && len(names) == 0 {
- return names, io.EOF
+
+ if n > 0 && len(names)+len(dirents)+len(infos) == 0 {
+ return nil, nil, nil, io.EOF
+ }
+ return names, dirents, infos, nil
+}
+
+// readInt returns the size-bytes unsigned integer in native byte order at offset off.
+func readInt(b []byte, off, size uintptr) (u uint64, ok bool) {
+ if len(b) < int(off+size) {
+ return 0, false
+ }
+ if isBigEndian {
+ return readIntBE(b[off:], size), true
+ }
+ return readIntLE(b[off:], size), true
+}
+
+func readIntBE(b []byte, size uintptr) uint64 {
+ switch size {
+ case 1:
+ return uint64(b[0])
+ case 2:
+ _ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
+ return uint64(b[1]) | uint64(b[0])<<8
+ case 4:
+ _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
+ return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24
+ case 8:
+ _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
+ return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
+ uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
+ default:
+ panic("syscall: readInt with unsupported size")
+ }
+}
+
+func readIntLE(b []byte, size uintptr) uint64 {
+ switch size {
+ case 1:
+ return uint64(b[0])
+ case 2:
+ _ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
+ return uint64(b[0]) | uint64(b[1])<<8
+ case 4:
+ _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
+ return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24
+ case 8:
+ _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
+ return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
+ uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
+ default:
+ panic("syscall: readInt with unsupported size")
}
- return names, nil
}
diff --git a/src/os/dir_windows.go b/src/os/dir_windows.go
index 9e5d6bd505..253adad0b9 100644
--- a/src/os/dir_windows.go
+++ b/src/os/dir_windows.go
@@ -10,20 +10,14 @@ import (
"syscall"
)
-func (file *File) readdir(n int) (fi []FileInfo, err error) {
- if file == nil {
- return nil, syscall.EINVAL
- }
+func (file *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) {
if !file.isdir() {
- return nil, &PathError{"Readdir", file.name, syscall.ENOTDIR}
+ return nil, nil, nil, &PathError{Op: "readdir", Path: file.name, Err: syscall.ENOTDIR}
}
wantAll := n <= 0
- size := n
if wantAll {
n = -1
- size = 100
}
- fi = make([]FileInfo, 0, size) // Empty with room to grow.
d := &file.dirinfo.data
for n != 0 && !file.dirinfo.isempty {
if file.dirinfo.needdata {
@@ -33,10 +27,7 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) {
if e == syscall.ERROR_NO_MORE_FILES {
break
} else {
- err = &PathError{"FindNextFile", file.name, e}
- if !wantAll {
- fi = nil
- }
+ err = &PathError{Op: "FindNextFile", Path: file.name, Err: e}
return
}
}
@@ -46,24 +37,32 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) {
if name == "." || name == ".." { // Useless names
continue
}
- f := newFileStatFromWin32finddata(d)
- f.name = name
- f.path = file.dirinfo.path
- f.appendNameToPath = true
+ if mode == readdirName {
+ names = append(names, name)
+ } else {
+ f := newFileStatFromWin32finddata(d)
+ f.name = name
+ f.path = file.dirinfo.path
+ f.appendNameToPath = true
+ if mode == readdirDirEntry {
+ dirents = append(dirents, dirEntry{f})
+ } else {
+ infos = append(infos, f)
+ }
+ }
n--
- fi = append(fi, f)
}
- if !wantAll && len(fi) == 0 {
- return fi, io.EOF
+ if !wantAll && 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) {
- fis, err := file.Readdir(n)
- names = make([]string, len(fis))
- for i, fi := range fis {
- names[i] = fi.Name()
- }
- return names, err
+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/src/os/dirent_aix.go b/src/os/dirent_aix.go
new file mode 100644
index 0000000000..5597b8af20
--- /dev/null
+++ b/src/os/dirent_aix.go
@@ -0,0 +1,30 @@
+// 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"
+)
+
+func direntIno(buf []byte) (uint64, bool) {
+ return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Ino), unsafe.Sizeof(syscall.Dirent{}.Ino))
+}
+
+func direntReclen(buf []byte) (uint64, bool) {
+ return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Reclen), unsafe.Sizeof(syscall.Dirent{}.Reclen))
+}
+
+func direntNamlen(buf []byte) (uint64, bool) {
+ reclen, ok := direntReclen(buf)
+ if !ok {
+ return 0, false
+ }
+ return reclen - uint64(unsafe.Offsetof(syscall.Dirent{}.Name)), true
+}
+
+func direntType(buf []byte) FileMode {
+ return ^FileMode(0) // unknown
+}
diff --git a/src/os/dirent_dragonfly.go b/src/os/dirent_dragonfly.go
new file mode 100644
index 0000000000..38cbd61ed3
--- /dev/null
+++ b/src/os/dirent_dragonfly.go
@@ -0,0 +1,55 @@
+// 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"
+)
+
+func direntIno(buf []byte) (uint64, bool) {
+ return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Fileno), unsafe.Sizeof(syscall.Dirent{}.Fileno))
+}
+
+func direntReclen(buf []byte) (uint64, bool) {
+ namlen, ok := direntNamlen(buf)
+ if !ok {
+ return 0, false
+ }
+ return (16 + namlen + 1 + 7) &^ 7, true
+}
+
+func direntNamlen(buf []byte) (uint64, bool) {
+ return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Namlen), unsafe.Sizeof(syscall.Dirent{}.Namlen))
+}
+
+func direntType(buf []byte) FileMode {
+ off := unsafe.Offsetof(syscall.Dirent{}.Type)
+ if off >= uintptr(len(buf)) {
+ return ^FileMode(0) // unknown
+ }
+ typ := buf[off]
+ switch typ {
+ case syscall.DT_BLK:
+ return ModeDevice
+ case syscall.DT_CHR:
+ return ModeDevice | ModeCharDevice
+ case syscall.DT_DBF:
+ // DT_DBF is "database record file".
+ // fillFileStatFromSys treats as regular file.
+ return 0
+ case syscall.DT_DIR:
+ return ModeDir
+ case syscall.DT_FIFO:
+ return ModeNamedPipe
+ case syscall.DT_LNK:
+ return ModeSymlink
+ case syscall.DT_REG:
+ return 0
+ case syscall.DT_SOCK:
+ return ModeSocket
+ }
+ return ^FileMode(0) // unknown
+}
diff --git a/src/os/dirent_freebsd.go b/src/os/dirent_freebsd.go
new file mode 100644
index 0000000000..d600837ebb
--- /dev/null
+++ b/src/os/dirent_freebsd.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.
+
+package os
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+func direntIno(buf []byte) (uint64, bool) {
+ return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Fileno), unsafe.Sizeof(syscall.Dirent{}.Fileno))
+}
+
+func direntReclen(buf []byte) (uint64, bool) {
+ return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Reclen), unsafe.Sizeof(syscall.Dirent{}.Reclen))
+}
+
+func direntNamlen(buf []byte) (uint64, bool) {
+ return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Namlen), unsafe.Sizeof(syscall.Dirent{}.Namlen))
+}
+
+func direntType(buf []byte) FileMode {
+ off := unsafe.Offsetof(syscall.Dirent{}.Type)
+ if off >= uintptr(len(buf)) {
+ return ^FileMode(0) // unknown
+ }
+ typ := buf[off]
+ switch typ {
+ case syscall.DT_BLK:
+ return ModeDevice
+ case syscall.DT_CHR:
+ return ModeDevice | ModeCharDevice
+ case syscall.DT_DIR:
+ return ModeDir
+ case syscall.DT_FIFO:
+ return ModeNamedPipe
+ case syscall.DT_LNK:
+ return ModeSymlink
+ case syscall.DT_REG:
+ return 0
+ case syscall.DT_SOCK:
+ return ModeSocket
+ }
+ return ^FileMode(0) // unknown
+}
diff --git a/src/os/dirent_js.go b/src/os/dirent_js.go
new file mode 100644
index 0000000000..31778c2ad8
--- /dev/null
+++ b/src/os/dirent_js.go
@@ -0,0 +1,30 @@
+// 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"
+)
+
+func direntIno(buf []byte) (uint64, bool) {
+ return 1, true
+}
+
+func direntReclen(buf []byte) (uint64, bool) {
+ return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Reclen), unsafe.Sizeof(syscall.Dirent{}.Reclen))
+}
+
+func direntNamlen(buf []byte) (uint64, bool) {
+ reclen, ok := direntReclen(buf)
+ if !ok {
+ return 0, false
+ }
+ return reclen - uint64(unsafe.Offsetof(syscall.Dirent{}.Name)), true
+}
+
+func direntType(buf []byte) FileMode {
+ return ^FileMode(0) // unknown
+}
diff --git a/src/os/dirent_linux.go b/src/os/dirent_linux.go
new file mode 100644
index 0000000000..74a3431121
--- /dev/null
+++ b/src/os/dirent_linux.go
@@ -0,0 +1,51 @@
+// 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"
+)
+
+func direntIno(buf []byte) (uint64, bool) {
+ return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Ino), unsafe.Sizeof(syscall.Dirent{}.Ino))
+}
+
+func direntReclen(buf []byte) (uint64, bool) {
+ return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Reclen), unsafe.Sizeof(syscall.Dirent{}.Reclen))
+}
+
+func direntNamlen(buf []byte) (uint64, bool) {
+ reclen, ok := direntReclen(buf)
+ if !ok {
+ return 0, false
+ }
+ return reclen - uint64(unsafe.Offsetof(syscall.Dirent{}.Name)), true
+}
+
+func direntType(buf []byte) FileMode {
+ off := unsafe.Offsetof(syscall.Dirent{}.Type)
+ if off >= uintptr(len(buf)) {
+ return ^FileMode(0) // unknown
+ }
+ typ := buf[off]
+ switch typ {
+ case syscall.DT_BLK:
+ return ModeDevice
+ case syscall.DT_CHR:
+ return ModeDevice | ModeCharDevice
+ case syscall.DT_DIR:
+ return ModeDir
+ case syscall.DT_FIFO:
+ return ModeNamedPipe
+ case syscall.DT_LNK:
+ return ModeSymlink
+ case syscall.DT_REG:
+ return 0
+ case syscall.DT_SOCK:
+ return ModeSocket
+ }
+ return ^FileMode(0) // unknown
+}
diff --git a/src/os/dirent_netbsd.go b/src/os/dirent_netbsd.go
new file mode 100644
index 0000000000..d600837ebb
--- /dev/null
+++ b/src/os/dirent_netbsd.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.
+
+package os
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+func direntIno(buf []byte) (uint64, bool) {
+ return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Fileno), unsafe.Sizeof(syscall.Dirent{}.Fileno))
+}
+
+func direntReclen(buf []byte) (uint64, bool) {
+ return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Reclen), unsafe.Sizeof(syscall.Dirent{}.Reclen))
+}
+
+func direntNamlen(buf []byte) (uint64, bool) {
+ return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Namlen), unsafe.Sizeof(syscall.Dirent{}.Namlen))
+}
+
+func direntType(buf []byte) FileMode {
+ off := unsafe.Offsetof(syscall.Dirent{}.Type)
+ if off >= uintptr(len(buf)) {
+ return ^FileMode(0) // unknown
+ }
+ typ := buf[off]
+ switch typ {
+ case syscall.DT_BLK:
+ return ModeDevice
+ case syscall.DT_CHR:
+ return ModeDevice | ModeCharDevice
+ case syscall.DT_DIR:
+ return ModeDir
+ case syscall.DT_FIFO:
+ return ModeNamedPipe
+ case syscall.DT_LNK:
+ return ModeSymlink
+ case syscall.DT_REG:
+ return 0
+ case syscall.DT_SOCK:
+ return ModeSocket
+ }
+ return ^FileMode(0) // unknown
+}
diff --git a/src/os/dirent_openbsd.go b/src/os/dirent_openbsd.go
new file mode 100644
index 0000000000..d600837ebb
--- /dev/null
+++ b/src/os/dirent_openbsd.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.
+
+package os
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+func direntIno(buf []byte) (uint64, bool) {
+ return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Fileno), unsafe.Sizeof(syscall.Dirent{}.Fileno))
+}
+
+func direntReclen(buf []byte) (uint64, bool) {
+ return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Reclen), unsafe.Sizeof(syscall.Dirent{}.Reclen))
+}
+
+func direntNamlen(buf []byte) (uint64, bool) {
+ return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Namlen), unsafe.Sizeof(syscall.Dirent{}.Namlen))
+}
+
+func direntType(buf []byte) FileMode {
+ off := unsafe.Offsetof(syscall.Dirent{}.Type)
+ if off >= uintptr(len(buf)) {
+ return ^FileMode(0) // unknown
+ }
+ typ := buf[off]
+ switch typ {
+ case syscall.DT_BLK:
+ return ModeDevice
+ case syscall.DT_CHR:
+ return ModeDevice | ModeCharDevice
+ case syscall.DT_DIR:
+ return ModeDir
+ case syscall.DT_FIFO:
+ return ModeNamedPipe
+ case syscall.DT_LNK:
+ return ModeSymlink
+ case syscall.DT_REG:
+ return 0
+ case syscall.DT_SOCK:
+ return ModeSocket
+ }
+ return ^FileMode(0) // unknown
+}
diff --git a/src/os/dirent_solaris.go b/src/os/dirent_solaris.go
new file mode 100644
index 0000000000..5597b8af20
--- /dev/null
+++ b/src/os/dirent_solaris.go
@@ -0,0 +1,30 @@
+// 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"
+)
+
+func direntIno(buf []byte) (uint64, bool) {
+ return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Ino), unsafe.Sizeof(syscall.Dirent{}.Ino))
+}
+
+func direntReclen(buf []byte) (uint64, bool) {
+ return readInt(buf, unsafe.Offsetof(syscall.Dirent{}.Reclen), unsafe.Sizeof(syscall.Dirent{}.Reclen))
+}
+
+func direntNamlen(buf []byte) (uint64, bool) {
+ reclen, ok := direntReclen(buf)
+ if !ok {
+ return 0, false
+ }
+ return reclen - uint64(unsafe.Offsetof(syscall.Dirent{}.Name)), true
+}
+
+func direntType(buf []byte) FileMode {
+ return ^FileMode(0) // unknown
+}
diff --git a/src/os/endian_big.go b/src/os/endian_big.go
new file mode 100644
index 0000000000..c98f124782
--- /dev/null
+++ b/src/os/endian_big.go
@@ -0,0 +1,9 @@
+// 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 ppc64 s390x mips mips64
+
+package os
+
+const isBigEndian = true
diff --git a/src/os/endian_little.go b/src/os/endian_little.go
new file mode 100644
index 0000000000..3efc5e0d8d
--- /dev/null
+++ b/src/os/endian_little.go
@@ -0,0 +1,9 @@
+// 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 386 amd64 arm arm64 ppc64le mips64le mipsle riscv64 wasm
+
+package os
+
+const isBigEndian = false
diff --git a/src/os/error.go b/src/os/error.go
index 875cc9711f..7cd9f22bfb 100644
--- a/src/os/error.go
+++ b/src/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 {
diff --git a/src/os/error_test.go b/src/os/error_test.go
index 3d921578fd..060cf59875 100644
--- a/src/os/error_test.go
+++ b/src/os/error_test.go
@@ -7,6 +7,7 @@ package os_test
import (
"errors"
"fmt"
+ "io/fs"
"io/ioutil"
"os"
"path/filepath"
@@ -27,7 +28,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 +40,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,7 +48,7 @@ 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 ""
@@ -91,18 +92,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 +112,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 +131,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,8 +140,8 @@ 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)
}
}
}
@@ -170,8 +171,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 +182,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/src/os/error_unix_test.go b/src/os/error_unix_test.go
index bfc83c9867..18bcf3f4e4 100644
--- a/src/os/error_unix_test.go
+++ b/src/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/src/os/error_windows_test.go b/src/os/error_windows_test.go
index 1635c1088e..b8191c5ebc 100644
--- a/src/os/error_windows_test.go
+++ b/src/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/src/os/example_test.go b/src/os/example_test.go
index 822886f70c..fbb277b6f1 100644
--- a/src/os/example_test.go
+++ b/src/os/example_test.go
@@ -6,6 +6,7 @@ package os_test
import (
"fmt"
+ "io/fs"
"log"
"os"
"time"
@@ -62,9 +63,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")
}
}
diff --git a/src/os/exec/example_test.go b/src/os/exec/example_test.go
index 62866fa710..a66890be69 100644
--- a/src/os/exec/example_test.go
+++ b/src/os/exec/example_test.go
@@ -10,7 +10,6 @@ import (
"encoding/json"
"fmt"
"io"
- "io/ioutil"
"log"
"os"
"os/exec"
@@ -128,7 +127,7 @@ func ExampleCmd_StderrPipe() {
log.Fatal(err)
}
- slurp, _ := ioutil.ReadAll(stderr)
+ slurp, _ := io.ReadAll(stderr)
fmt.Printf("%s\n", slurp)
if err := cmd.Wait(); err != nil {
diff --git a/src/os/exec/exec_plan9.go b/src/os/exec/exec_plan9.go
index d90bd04399..21ac7b765f 100644
--- a/src/os/exec/exec_plan9.go
+++ b/src/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/src/os/exec/exec_test.go b/src/os/exec/exec_test.go
index dafbc64a17..cd3d759ebc 100644
--- a/src/os/exec/exec_test.go
+++ b/src/os/exec/exec_test.go
@@ -605,6 +605,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 +637,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)
@@ -826,7 +830,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/src/os/exec/exec_unix.go b/src/os/exec/exec_unix.go
index 9c3e17d23a..51c52427c2 100644
--- a/src/os/exec/exec_unix.go
+++ b/src/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/src/os/exec/exec_windows.go b/src/os/exec/exec_windows.go
index af8cd97218..bb937f8aed 100644
--- a/src/os/exec/exec_windows.go
+++ b/src/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/src/os/exec/lp_plan9.go b/src/os/exec/lp_plan9.go
index 5860cbca4d..e8826a5083 100644
--- a/src/os/exec/lp_plan9.go
+++ b/src/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/src/os/exec/lp_unix.go b/src/os/exec/lp_unix.go
index 93793e0eee..66c1df76fb 100644
--- a/src/os/exec/lp_unix.go
+++ b/src/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/src/os/exec/lp_windows.go b/src/os/exec/lp_windows.go
index 9ea3d76575..e7a2cdf142 100644
--- a/src/os/exec/lp_windows.go
+++ b/src/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/src/os/exec/read3.go b/src/os/exec/read3.go
index 25d732a991..8852023e77 100644
--- a/src/os/exec/read3.go
+++ b/src/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)
diff --git a/src/os/exec_plan9.go b/src/os/exec_plan9.go
index b0abf743dd..ef8dad11b6 100644
--- a/src/os/exec_plan9.go
+++ b/src/os/exec_plan9.go
@@ -34,7 +34,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
diff --git a/src/os/exec_posix.go b/src/os/exec_posix.go
index 45b47a542d..7ecddaed37 100644
--- a/src/os/exec_posix.go
+++ b/src/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/src/os/export_test.go b/src/os/export_test.go
index 812432cee4..d66264a68f 100644
--- a/src/os/export_test.go
+++ b/src/os/export_test.go
@@ -9,3 +9,4 @@ package os
var Atime = atime
var LstatP = &lstat
var ErrWriteAtInAppendMode = errWriteAtInAppendMode
+var TestingForceReadDirLstat = &testingForceReadDirLstat
diff --git a/src/os/file.go b/src/os/file.go
index 05d2f83283..835d44ab8c 100644
--- a/src/os/file.go
+++ b/src/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,7 +254,7 @@ 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}
}
longName := fixLongPath(name)
e := ignoringEINTR(func() error {
@@ -261,7 +262,7 @@ func Mkdir(name string, perm FileMode) error {
})
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
@@ -291,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()
@@ -366,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.
@@ -608,3 +609,21 @@ 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.
+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
+}
diff --git a/src/os/file_plan9.go b/src/os/file_plan9.go
index a1a51a1c06..bbc732838a 100644
--- a/src/os/file_plan9.go
+++ b/src/os/file_plan9.go
@@ -119,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}
}
}
@@ -154,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
@@ -191,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
}
@@ -209,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
@@ -217,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
}
@@ -238,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
}
@@ -314,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
}
@@ -326,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
}
@@ -389,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
@@ -397,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
}
@@ -421,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
}
@@ -458,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.
@@ -469,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.
@@ -485,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 {
@@ -505,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
}
@@ -541,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
}
diff --git a/src/os/file_posix.go b/src/os/file_posix.go
index ae23d22d0a..795c547856 100644
--- a/src/os/file_posix.go
+++ b/src/os/file_posix.go
@@ -81,7 +81,7 @@ func chmod(name string, mode FileMode) error {
return syscall.Chmod(longName, syscallMode(mode))
})
if e != nil {
- return &PathError{"chmod", name, e}
+ return &PathError{Op: "chmod", Path: name, Err: e}
}
return nil
}
@@ -109,7 +109,7 @@ func Chown(name string, uid, gid int) error {
return syscall.Chown(name, uid, gid)
})
if e != nil {
- return &PathError{"chown", name, e}
+ return &PathError{Op: "chown", Path: name, Err: e}
}
return nil
}
@@ -125,7 +125,7 @@ func Lchown(name string, uid, gid int) error {
return syscall.Lchown(name, uid, gid)
})
if e != nil {
- return &PathError{"lchown", name, e}
+ return &PathError{Op: "lchown", Path: name, Err: e}
}
return nil
}
@@ -182,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
}
diff --git a/src/os/file_unix.go b/src/os/file_unix.go
index e0f16d809d..0dc7a5a0a2 100644
--- a/src/os/file_unix.go
+++ b/src/os/file_unix.go
@@ -9,7 +9,6 @@ package os
import (
"internal/poll"
"internal/syscall/unix"
- "io"
"runtime"
"syscall"
)
@@ -216,7 +215,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
@@ -245,7 +244,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
@@ -277,7 +276,7 @@ func Truncate(name string, size int64) error {
return syscall.Truncate(name, size)
})
if e != nil {
- return &PathError{"truncate", name, e}
+ return &PathError{Op: "truncate", Path: name, Err: e}
}
return nil
}
@@ -314,7 +313,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 {
@@ -353,33 +352,6 @@ func Symlink(oldname, newname string) error {
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) {
@@ -400,10 +372,48 @@ func Readlink(name string) (string, error) {
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/src/os/file_windows.go b/src/os/file_windows.go
index f744a35023..dfc5fc6ce6 100644
--- a/src/os/file_windows.go
+++ b/src/os/file_windows.go
@@ -168,7 +168,7 @@ func openDir(name string) (file *File, err error) {
// openFileNolog is the Windows implementation of OpenFile.
func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
if name == "" {
- return nil, &PathError{"open", name, syscall.ENOENT}
+ return nil, &PathError{Op: "open", Path: name, Err: syscall.ENOENT}
}
r, errf := openFile(name, flag, perm)
if errf == nil {
@@ -178,11 +178,11 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
if errd == nil {
if flag&O_WRONLY != 0 || flag&O_RDWR != 0 {
r.Close()
- return nil, &PathError{"open", name, syscall.EISDIR}
+ return nil, &PathError{Op: "open", Path: name, Err: syscall.EISDIR}
}
return r, nil
}
- return nil, &PathError{"open", name, errf}
+ return nil, &PathError{Op: "open", Path: name, Err: errf}
}
func (file *file) close() error {
@@ -198,7 +198,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
@@ -236,7 +236,7 @@ func Truncate(name string, size int64) error {
func Remove(name string) error {
p, e := syscall.UTF16PtrFromString(fixLongPath(name))
if e != nil {
- return &PathError{"remove", name, e}
+ return &PathError{Op: "remove", Path: name, Err: e}
}
// Go file interface forces us to know whether
@@ -267,7 +267,7 @@ func Remove(name string) error {
}
}
}
- return &PathError{"remove", name, e}
+ return &PathError{Op: "remove", Path: name, Err: e}
}
func rename(oldname, newname string) error {
@@ -493,7 +493,7 @@ func readlink(path string) (string, error) {
func Readlink(name string) (string, error) {
s, err := readlink(fixLongPath(name))
if err != nil {
- return "", &PathError{"readlink", name, err}
+ return "", &PathError{Op: "readlink", Path: name, Err: err}
}
return s, nil
}
diff --git a/src/os/os_test.go b/src/os/os_test.go
index 2bb57d866f..378ddf58dd 100644
--- a/src/os/os_test.go
+++ b/src/os/os_test.go
@@ -23,6 +23,7 @@ import (
"sync"
"syscall"
"testing"
+ "testing/fstest"
"time"
)
@@ -309,20 +310,21 @@ 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)
@@ -338,16 +340,68 @@ 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)
+ }
+ }
+}
+
+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 {
@@ -366,6 +420,11 @@ func TestReaddir(t *testing.T) {
testReaddir(sysdir.name, sysdir.files, t)
}
+func TestReadDir(t *testing.T) {
+ testReadDir(".", dot, t)
+ testReadDir(sysdir.name, sysdir.files, t)
+}
+
func benchmarkReaddirname(path string, b *testing.B) {
var nentries int
for i := 0; i < b.N; i++ {
@@ -400,6 +459,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)
}
@@ -408,6 +484,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++ {
@@ -547,7 +627,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)
@@ -557,7 +638,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)
@@ -567,7 +660,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)
@@ -1573,8 +1666,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`)
}
}
@@ -2434,7 +2527,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 {
@@ -2579,3 +2672,9 @@ 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)
+ }
+}
diff --git a/src/os/os_windows_test.go b/src/os/os_windows_test.go
index f03ec750d0..e002774844 100644
--- a/src/os/os_windows_test.go
+++ b/src/os/os_windows_test.go
@@ -12,6 +12,7 @@ import (
"internal/syscall/windows/registry"
"internal/testenv"
"io"
+ "io/fs"
"io/ioutil"
"os"
osexec "os/exec"
@@ -164,11 +165,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
}
@@ -681,7 +682,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)
}
}
@@ -1291,9 +1292,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/src/os/path.go b/src/os/path.go
index ba43ea3525..df87887b9b 100644
--- a/src/os/path.go
+++ b/src/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/src/os/pipe_test.go b/src/os/pipe_test.go
index 429bd813c2..0593efec75 100644
--- a/src/os/pipe_test.go
+++ b/src/os/pipe_test.go
@@ -13,6 +13,7 @@ import (
"fmt"
"internal/testenv"
"io"
+ "io/fs"
"io/ioutil"
"os"
osexec "os/exec"
@@ -46,7 +47,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 {
@@ -202,10 +203,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 +234,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 +309,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/src/os/removeall_at.go b/src/os/removeall_at.go
index 37bf1b8f2f..c1a1b726af 100644
--- a/src/os/removeall_at.go
+++ b/src/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/src/os/removeall_noat.go b/src/os/removeall_noat.go
index c1b43e3807..7c888baaa9 100644
--- a/src/os/removeall_noat.go
+++ b/src/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/src/os/removeall_test.go b/src/os/removeall_test.go
index 1e5c650fe1..bc9c468ce3 100644
--- a/src/os/removeall_test.go
+++ b/src/os/removeall_test.go
@@ -359,7 +359,7 @@ func TestRemoveAllButReadOnlyAndPathError(t *testing.T) {
t.Errorf("got %q, expected pathErr.path %q", g, w)
}
} else {
- t.Errorf("got %T, expected *os.PathError", err)
+ t.Errorf("got %T, expected *fs.PathError", err)
}
for _, dir := range dirs {
diff --git a/src/os/signal/signal_cgo_test.go b/src/os/signal/signal_cgo_test.go
index a117221400..a8a485613f 100644
--- a/src/os/signal/signal_cgo_test.go
+++ b/src/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/src/os/stat_plan9.go b/src/os/stat_plan9.go
index b43339afa4..57ae6fb0bb 100644
--- a/src/os/stat_plan9.go
+++ b/src/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/src/os/stat_test.go b/src/os/stat_test.go
index 60f3b4c587..88b789080e 100644
--- a/src/os/stat_test.go
+++ b/src/os/stat_test.go
@@ -6,6 +6,7 @@ package os_test
import (
"internal/testenv"
+ "io/fs"
"io/ioutil"
"os"
"path/filepath"
@@ -14,7 +15,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 +71,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 +89,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 +109,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)
}
}
diff --git a/src/os/stat_unix.go b/src/os/stat_unix.go
index ef74a43758..66c356fc62 100644
--- a/src/os/stat_unix.go
+++ b/src/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
@@ -32,7 +32,7 @@ func statNolog(name string) (FileInfo, 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
@@ -45,7 +45,7 @@ func lstatNolog(name string) (FileInfo, 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/src/os/stat_windows.go b/src/os/stat_windows.go
index 3e0e0a59ed..da4c49090e 100644
--- a/src/os/stat_windows.go
+++ b/src/os/stat_windows.go
@@ -27,7 +27,7 @@ func (file *File) Stat() (FileInfo, error) {
ft, err := file.pfd.GetFileType()
if err != nil {
- return nil, &PathError{"GetFileType", file.name, err}
+ return nil, &PathError{Op: "GetFileType", Path: file.name, Err: err}
}
switch ft {
case syscall.FILE_TYPE_PIPE, syscall.FILE_TYPE_CHAR:
@@ -45,14 +45,14 @@ func (file *File) Stat() (FileInfo, error) {
// stat implements both Stat and Lstat of a file.
func stat(funcname, name string, createFileAttrs uint32) (FileInfo, error) {
if len(name) == 0 {
- return nil, &PathError{funcname, name, syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)}
+ return nil, &PathError{Op: funcname, Path: name, Err: syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)}
}
if isWindowsNulName(name) {
return &devNullStat, nil
}
namep, err := syscall.UTF16PtrFromString(fixLongPath(name))
if err != nil {
- return nil, &PathError{funcname, name, err}
+ return nil, &PathError{Op: funcname, Path: name, Err: err}
}
// Try GetFileAttributesEx first, because it is faster than CreateFile.
@@ -80,7 +80,7 @@ func stat(funcname, name string, createFileAttrs uint32) (FileInfo, error) {
var fd syscall.Win32finddata
sh, err := syscall.FindFirstFile(namep, &fd)
if err != nil {
- return nil, &PathError{"FindFirstFile", name, err}
+ return nil, &PathError{Op: "FindFirstFile", Path: name, Err: err}
}
syscall.FindClose(sh)
fs := newFileStatFromWin32finddata(&fd)
@@ -94,7 +94,7 @@ func stat(funcname, name string, createFileAttrs uint32) (FileInfo, error) {
h, err := syscall.CreateFile(namep, 0, 0, nil,
syscall.OPEN_EXISTING, createFileAttrs, 0)
if err != nil {
- return nil, &PathError{"CreateFile", name, err}
+ return nil, &PathError{Op: "CreateFile", Path: name, Err: err}
}
defer syscall.CloseHandle(h)
diff --git a/src/os/timeout_test.go b/src/os/timeout_test.go
index 99b94c2e4c..d848e41642 100644
--- a/src/os/timeout_test.go
+++ b/src/os/timeout_test.go
@@ -429,7 +429,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 +565,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/src/os/types.go b/src/os/types.go
index 4b6c084838..d8edd98b68 100644
--- a/src/os/types.go
+++ b/src/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/src/os/types_windows.go b/src/os/types_windows.go
index 3d1a6674b1..59bf5ca381 100644
--- a/src/os/types_windows.go
+++ b/src/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)