aboutsummaryrefslogtreecommitdiff
path: root/src/os
diff options
context:
space:
mode:
Diffstat (limited to 'src/os')
-rw-r--r--src/os/dir.go22
-rw-r--r--src/os/error.go15
-rw-r--r--src/os/error_test.go9
-rw-r--r--src/os/example_test.go96
-rw-r--r--src/os/exec/exec_test.go17
-rw-r--r--src/os/exec/lp_unix_test.go3
-rw-r--r--src/os/exec/lp_windows_test.go7
-rw-r--r--src/os/executable_dragonfly.go6
-rw-r--r--src/os/executable_freebsd.go6
-rw-r--r--src/os/export_test.go1
-rw-r--r--src/os/fifo_test.go3
-rw-r--r--src/os/file.go90
-rw-r--r--src/os/file_plan9.go10
-rw-r--r--src/os/file_unix.go8
-rw-r--r--src/os/os_test.go147
-rw-r--r--src/os/os_unix_test.go4
-rw-r--r--src/os/os_windows_test.go40
-rw-r--r--src/os/path_test.go4
-rw-r--r--src/os/path_windows_test.go3
-rw-r--r--src/os/pipe_test.go3
-rw-r--r--src/os/read_test.go127
-rw-r--r--src/os/readfrom_linux_test.go36
-rw-r--r--src/os/removeall_test.go27
-rw-r--r--src/os/signal/signal_linux_test.go42
-rw-r--r--src/os/signal/signal_test.go103
-rw-r--r--src/os/signal/signal_windows_test.go3
-rw-r--r--src/os/stat_test.go9
-rw-r--r--src/os/tempfile.go125
-rw-r--r--src/os/tempfile_test.go193
-rw-r--r--src/os/testdata/dirfs/a0
-rw-r--r--src/os/testdata/dirfs/b0
-rw-r--r--src/os/testdata/dirfs/dir/x0
-rw-r--r--src/os/testdata/hello1
-rw-r--r--src/os/timeout_test.go3
-rw-r--r--src/os/user/lookup_plan9.go3
35 files changed, 1001 insertions, 165 deletions
diff --git a/src/os/dir.go b/src/os/dir.go
index 1d90b970e7..5306bcb3ba 100644
--- a/src/os/dir.go
+++ b/src/os/dir.go
@@ -4,7 +4,10 @@
package os
-import "io/fs"
+import (
+ "io/fs"
+ "sort"
+)
type readdirMode int
@@ -103,3 +106,20 @@ func (f *File) ReadDir(n int) ([]DirEntry, error) {
// 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/src/os/error.go b/src/os/error.go
index 7cd9f22bfb..704a6fb29e 100644
--- a/src/os/error.go
+++ b/src/os/error.go
@@ -76,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)
}
@@ -83,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)
}
@@ -90,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/src/os/error_test.go b/src/os/error_test.go
index 060cf59875..6264ccc966 100644
--- a/src/os/error_test.go
+++ b/src/os/error_test.go
@@ -8,14 +8,13 @@ import (
"errors"
"fmt"
"io/fs"
- "io/ioutil"
"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
@@ -55,7 +54,7 @@ func testErrNotExist(name string) string {
}
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
@@ -147,12 +146,12 @@ func TestIsPermission(t *testing.T) {
}
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)
}
diff --git a/src/os/example_test.go b/src/os/example_test.go
index fbb277b6f1..3adce51784 100644
--- a/src/os/example_test.go
+++ b/src/os/example_test.go
@@ -9,6 +9,7 @@ import (
"io/fs"
"log"
"os"
+ "path/filepath"
"time"
)
@@ -144,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/src/os/exec/exec_test.go b/src/os/exec/exec_test.go
index cd3d759ebc..8b0c93f382 100644
--- a/src/os/exec/exec_test.go
+++ b/src/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
}
@@ -645,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)
}
@@ -691,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())
diff --git a/src/os/exec/lp_unix_test.go b/src/os/exec/lp_unix_test.go
index e4656cafb8..296480fd04 100644
--- a/src/os/exec/lp_unix_test.go
+++ b/src/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/src/os/exec/lp_windows_test.go b/src/os/exec/lp_windows_test.go
index 59b5f1c2c7..c6f3d5d406 100644
--- a/src/os/exec/lp_windows_test.go
+++ b/src/os/exec/lp_windows_test.go
@@ -11,7 +11,6 @@ import (
"fmt"
"internal/testenv"
"io"
- "io/ioutil"
"os"
"os/exec"
"path/filepath"
@@ -307,7 +306,7 @@ var lookPathTests = []lookPathTest{
}
func TestLookPath(t *testing.T) {
- tmp, err := ioutil.TempDir("", "TestLookPath")
+ tmp, err := os.MkdirTemp("", "TestLookPath")
if err != nil {
t.Fatal("TempDir failed: ", err)
}
@@ -504,7 +503,7 @@ var commandTests = []commandTest{
}
func TestCommand(t *testing.T) {
- tmp, err := ioutil.TempDir("", "TestCommand")
+ tmp, err := os.MkdirTemp("", "TestCommand")
if err != nil {
t.Fatal("TempDir failed: ", err)
}
@@ -529,7 +528,7 @@ func TestCommand(t *testing.T) {
func buildPrintPathExe(t *testing.T, dir string) string {
const name = "printpath"
srcname := name + ".go"
- err := ioutil.WriteFile(filepath.Join(dir, srcname), []byte(printpathSrc), 0644)
+ err := os.WriteFile(filepath.Join(dir, srcname), []byte(printpathSrc), 0644)
if err != nil {
t.Fatalf("failed to create source: %v", err)
}
diff --git a/src/os/executable_dragonfly.go b/src/os/executable_dragonfly.go
index b0deb7bbe5..19c2ae890f 100644
--- a/src/os/executable_dragonfly.go
+++ b/src/os/executable_dragonfly.go
@@ -6,7 +6,7 @@ package os
// From DragonFly's <sys/sysctl.h>
const (
- _CTL_KERN = 1
- _KERN_PROC = 14
- _KERN_PROC_PATHNAME = 9
+ _CTL_KERN = 1
+ _KERN_PROC = 14
+ _KERN_PROC_PATHNAME = 9
)
diff --git a/src/os/executable_freebsd.go b/src/os/executable_freebsd.go
index 57930b1b16..95f1a93cb9 100644
--- a/src/os/executable_freebsd.go
+++ b/src/os/executable_freebsd.go
@@ -6,7 +6,7 @@ package os
// From FreeBSD's <sys/sysctl.h>
const (
- _CTL_KERN = 1
- _KERN_PROC = 14
- _KERN_PROC_PATHNAME = 12
+ _CTL_KERN = 1
+ _KERN_PROC = 14
+ _KERN_PROC_PATHNAME = 12
)
diff --git a/src/os/export_test.go b/src/os/export_test.go
index d66264a68f..f3cb1a2bef 100644
--- a/src/os/export_test.go
+++ b/src/os/export_test.go
@@ -10,3 +10,4 @@ var Atime = atime
var LstatP = &lstat
var ErrWriteAtInAppendMode = errWriteAtInAppendMode
var TestingForceReadDirLstat = &testingForceReadDirLstat
+var ErrPatternHasSeparator = errPatternHasSeparator
diff --git a/src/os/fifo_test.go b/src/os/fifo_test.go
index 3041dcfa02..2439192a9d 100644
--- a/src/os/fifo_test.go
+++ b/src/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/src/os/file.go b/src/os/file.go
index 835d44ab8c..52dd94339b 100644
--- a/src/os/file.go
+++ b/src/os/file.go
@@ -406,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")
@@ -457,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")
@@ -505,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")
}
@@ -611,14 +609,32 @@ func isWindowsNulName(name string) bool {
}
// 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)
}
+func containsAny(s, chars string) bool {
+ for i := 0; i < len(s); i++ {
+ for j := 0; j < len(chars); j++ {
+ if s[i] == chars[j] {
+ return true
+ }
+ }
+ }
+ return false
+}
+
type dirFS string
func (dir dirFS) Open(name string) (fs.File, error) {
- if !fs.ValidPath(name) {
+ if !fs.ValidPath(name) || runtime.GOOS == "windows" && containsAny(name, `\:`) {
return nil, &PathError{Op: "open", Path: name, Err: ErrInvalid}
}
f, err := Open(string(dir) + "/" + name)
@@ -627,3 +643,63 @@ func (dir dirFS) Open(name string) (fs.File, error) {
}
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/src/os/file_plan9.go b/src/os/file_plan9.go
index bbc732838a..4f384e9211 100644
--- a/src/os/file_plan9.go
+++ b/src/os/file_plan9.go
@@ -336,16 +336,6 @@ func hasPrefix(s, prefix string) bool {
return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
}
-// LastIndexByte from the strings package.
-func lastIndex(s string, sep byte) int {
- for i := len(s) - 1; i >= 0; i-- {
- if s[i] == sep {
- return i
- }
- }
- return -1
-}
-
func rename(oldname, newname string) error {
dirname := oldname[:lastIndex(oldname, '/')+1]
if hasPrefix(newname, dirname) {
diff --git a/src/os/file_unix.go b/src/os/file_unix.go
index 0dc7a5a0a2..f88450018e 100644
--- a/src/os/file_unix.go
+++ b/src/os/file_unix.go
@@ -66,6 +66,10 @@ type file struct {
// 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.
+// Because file descriptors can be reused, the returned file descriptor may
+// only be closed through the Close method of f, or by its finalizer during
+// garbage collection. Otherwise, during garbage collection the finalizer
+// may close an unrelated file descriptor with the same (reused) number.
//
// As an alternative, see the f.SyscallConn method.
func (f *File) Fd() uintptr {
@@ -90,6 +94,10 @@ func (f *File) Fd() uintptr {
// descriptor. On Unix systems, if the file descriptor is in
// non-blocking mode, NewFile will attempt to return a pollable File
// (one for which the SetDeadline methods work).
+//
+// After passing it to NewFile, fd may become invalid under the same
+// conditions described in the comments of the Fd method, and the same
+// constraints apply.
func NewFile(fd uintptr, name string) *File {
kind := kindNewFile
if nb, err := unix.IsNonblock(int(fd)); err == nil && nb {
diff --git a/src/os/os_test.go b/src/os/os_test.go
index a1c0578887..a32e5fc11e 100644
--- a/src/os/os_test.go
+++ b/src/os/os_test.go
@@ -11,7 +11,8 @@ import (
"fmt"
"internal/testenv"
"io"
- "io/ioutil"
+ "io/fs"
+ "os"
. "os"
osexec "os/exec"
"path/filepath"
@@ -155,7 +156,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)
}
@@ -163,7 +164,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)
}
@@ -419,19 +420,19 @@ func testReadDir(dir string, contents []string, t *testing.T) {
}
}
-func TestReaddirnames(t *testing.T) {
+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 TestReadDir(t *testing.T) {
+func TestFileReadDir(t *testing.T) {
testReadDir(".", dot, t)
testReadDir(sysdir.name, sysdir.files, t)
testReadDir(t.TempDir(), nil, t)
@@ -616,7 +617,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)
}
@@ -715,7 +716,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)
}
@@ -776,7 +777,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)
}
@@ -867,7 +868,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)
}
@@ -992,12 +993,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)
}
@@ -1235,6 +1236,7 @@ func TestChmod(t *testing.T) {
}
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)
@@ -1340,7 +1342,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 {
@@ -1414,7 +1416,7 @@ func TestChdirAndGetwd(t *testing.T) {
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)
}
@@ -1508,7 +1510,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)
@@ -1575,7 +1577,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")
@@ -1857,7 +1859,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)
}
@@ -1905,7 +1907,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)
}
@@ -1947,7 +1949,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)
}
@@ -2089,7 +2091,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)
}
@@ -2158,7 +2160,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)
}
@@ -2248,8 +2250,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)
@@ -2297,6 +2299,7 @@ func TestLongPath(t *testing.T) {
func testKillProcess(t *testing.T, processKiller func(p *Process)) {
testenv.MustHaveExec(t)
+ t.Parallel()
// Re-exec the test binary itself to emulate "sleep 1".
cmd := osexec.Command(Args[0], "-test.run", "TestSleep")
@@ -2304,14 +2307,15 @@ func testKillProcess(t *testing.T, processKiller func(p *Process)) {
if err != nil {
t.Fatalf("Failed to start test process: %v", err)
}
- go func() {
- time.Sleep(100 * time.Millisecond)
- processKiller(cmd.Process)
+
+ defer func() {
+ if err := cmd.Wait(); err == nil {
+ t.Errorf("Test process succeeded, but expected to fail")
+ }
}()
- err = cmd.Wait()
- if err == nil {
- t.Errorf("Test process succeeded, but expected to fail")
- }
+
+ time.Sleep(100 * time.Millisecond)
+ processKiller(cmd.Process)
}
// TestSleep emulates "sleep 1". It is a helper for testKillProcess, so we
@@ -2433,7 +2437,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)
}
@@ -2686,7 +2690,86 @@ func TestOpenFileKeepsPermissions(t *testing.T) {
}
func TestDirFS(t *testing.T) {
- if err := fstest.TestFS(DirFS("./signal"), "signal.go", "internal/pty/pty.go"); err != nil {
+ // On Windows, we force the MFT to update by reading the actual metadata from GetFileInformationByHandle and then
+ // explicitly setting that. Otherwise it might get out of sync with FindFirstFile. See golang.org/issues/42637.
+ if runtime.GOOS == "windows" {
+ if err := filepath.WalkDir("./testdata/dirfs", func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ t.Fatal(err)
+ }
+ info, err := d.Info()
+ if err != nil {
+ t.Fatal(err)
+ }
+ stat, err := Stat(path) // This uses GetFileInformationByHandle internally.
+ if err != nil {
+ t.Fatal(err)
+ }
+ if stat.ModTime() == info.ModTime() {
+ return nil
+ }
+ if err := Chtimes(path, stat.ModTime(), stat.ModTime()); err != nil {
+ t.Log(err) // We only log, not die, in case the test directory is not writable.
+ }
+ return nil
+ }); err != nil {
+ t.Fatal(err)
+ }
+ }
+ if err := fstest.TestFS(DirFS("./testdata/dirfs"), "a", "b", "dir/x"); err != nil {
t.Fatal(err)
}
+
+ // Test that Open does not accept backslash as separator.
+ d := DirFS(".")
+ _, err := d.Open(`testdata\dirfs`)
+ if err == nil {
+ t.Fatalf(`Open testdata\dirfs succeeded`)
+ }
+}
+
+func TestDirFSPathsValid(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Skipf("skipping on Windows")
+ }
+
+ d := t.TempDir()
+ if err := os.WriteFile(filepath.Join(d, "control.txt"), []byte(string("Hello, world!")), 0644); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.WriteFile(filepath.Join(d, `e:xperi\ment.txt`), []byte(string("Hello, colon and backslash!")), 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ fsys := os.DirFS(d)
+ err := fs.WalkDir(fsys, ".", func(path string, e fs.DirEntry, err error) error {
+ if fs.ValidPath(e.Name()) {
+ t.Logf("%q ok", e.Name())
+ } else {
+ t.Errorf("%q INVALID", e.Name())
+ }
+ return nil
+ })
+ if 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/src/os/os_unix_test.go b/src/os/os_unix_test.go
index 0bce2989c4..51693fd82a 100644
--- a/src/os/os_unix_test.go
+++ b/src/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/src/os/os_windows_test.go b/src/os/os_windows_test.go
index e002774844..b0929b4f30 100644
--- a/src/os/os_windows_test.go
+++ b/src/os/os_windows_test.go
@@ -13,7 +13,6 @@ import (
"internal/testenv"
"io"
"io/fs"
- "io/ioutil"
"os"
osexec "os/exec"
"path/filepath"
@@ -31,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)
}
@@ -90,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)
}
@@ -115,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)
}
@@ -127,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
@@ -439,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)
}
@@ -600,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)
}
@@ -619,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)
}
@@ -643,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)
}
@@ -693,7 +692,16 @@ func TestReadStdin(t *testing.T) {
poll.ReadConsole = old
}()
- testConsole := os.NewConsoleFile(syscall.Stdin, "test")
+ p, err := syscall.GetCurrentProcess()
+ if err != nil {
+ t.Fatalf("Unable to get handle to current process: %v", err)
+ }
+ var stdinDuplicate syscall.Handle
+ err = syscall.DuplicateHandle(p, syscall.Handle(syscall.Stdin), p, &stdinDuplicate, 0, false, syscall.DUPLICATE_SAME_ACCESS)
+ if err != nil {
+ t.Fatalf("Unable to duplicate stdin: %v", err)
+ }
+ testConsole := os.NewConsoleFile(stdinDuplicate, "test")
var tests = []string{
"abc",
@@ -804,7 +812,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)
}
@@ -823,7 +831,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)
}
@@ -971,14 +979,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)
}
@@ -1207,7 +1215,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)
}
@@ -1272,7 +1280,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)
}
diff --git a/src/os/path_test.go b/src/os/path_test.go
index 3fe9c5ffa3..b79d958711 100644
--- a/src/os/path_test.go
+++ b/src/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)
}
diff --git a/src/os/path_windows_test.go b/src/os/path_windows_test.go
index 862b404362..869db8fd6c 100644
--- a/src/os/path_windows_test.go
+++ b/src/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/src/os/pipe_test.go b/src/os/pipe_test.go
index 0593efec75..b98e53845c 100644
--- a/src/os/pipe_test.go
+++ b/src/os/pipe_test.go
@@ -14,7 +14,6 @@ import (
"internal/testenv"
"io"
"io/fs"
- "io/ioutil"
"os"
osexec "os/exec"
"os/signal"
@@ -161,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
}
diff --git a/src/os/read_test.go b/src/os/read_test.go
new file mode 100644
index 0000000000..5c58d7d7df
--- /dev/null
+++ b/src/os/read_test.go
@@ -0,0 +1,127 @@
+// 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"
+ "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 {
+ t.Fatalf("ReadDir %s: exec directory not found", dirname)
+ }
+}
diff --git a/src/os/readfrom_linux_test.go b/src/os/readfrom_linux_test.go
index 00faf39fe5..1d145dadb0 100644
--- a/src/os/readfrom_linux_test.go
+++ b/src/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)
}
@@ -361,3 +361,35 @@ func (h *copyFileRangeHook) install() {
func (h *copyFileRangeHook) uninstall() {
*PollCopyFileRangeP = h.original
}
+
+// On some kernels copy_file_range fails on files in /proc.
+func TestProcCopy(t *testing.T) {
+ const cmdlineFile = "/proc/self/cmdline"
+ cmdline, err := os.ReadFile(cmdlineFile)
+ if err != nil {
+ t.Skipf("can't read /proc file: %v", err)
+ }
+ in, err := os.Open(cmdlineFile)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer in.Close()
+ outFile := filepath.Join(t.TempDir(), "cmdline")
+ out, err := os.Create(outFile)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if _, err := io.Copy(out, in); err != nil {
+ t.Fatal(err)
+ }
+ if err := out.Close(); err != nil {
+ t.Fatal(err)
+ }
+ copy, err := os.ReadFile(outFile)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(cmdline, copy) {
+ t.Errorf("copy of %q got %q want %q\n", cmdlineFile, copy, cmdline)
+ }
+}
diff --git a/src/os/removeall_test.go b/src/os/removeall_test.go
index bc9c468ce3..3a2f6e3759 100644
--- a/src/os/removeall_test.go
+++ b/src/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)
}
@@ -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 *fs.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/src/os/signal/signal_linux_test.go b/src/os/signal/signal_linux_test.go
new file mode 100644
index 0000000000..2e553d0b0f
--- /dev/null
+++ b/src/os/signal/signal_linux_test.go
@@ -0,0 +1,42 @@
+// 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 linux
+
+package signal
+
+import (
+ "os"
+ "syscall"
+ "testing"
+ "time"
+)
+
+const prSetKeepCaps = 8
+
+// This test validates that syscall.AllThreadsSyscall() can reliably
+// reach all 'm' (threads) of the nocgo runtime even when one thread
+// is blocked waiting to receive signals from the kernel. This monitors
+// for a regression vs. the fix for #43149.
+func TestAllThreadsSyscallSignals(t *testing.T) {
+ if _, _, err := syscall.AllThreadsSyscall(syscall.SYS_PRCTL, prSetKeepCaps, 0, 0); err == syscall.ENOTSUP {
+ t.Skip("AllThreadsSyscall disabled with cgo")
+ }
+
+ sig := make(chan os.Signal, 1)
+ Notify(sig, os.Interrupt)
+
+ for i := 0; i <= 100; i++ {
+ if _, _, errno := syscall.AllThreadsSyscall(syscall.SYS_PRCTL, prSetKeepCaps, uintptr(i&1), 0); errno != 0 {
+ t.Fatalf("[%d] failed to set KEEP_CAPS=%d: %v", i, i&1, errno)
+ }
+ }
+
+ select {
+ case <-time.After(10 * time.Millisecond):
+ case <-sig:
+ t.Fatal("unexpected signal")
+ }
+ Stop(sig)
+}
diff --git a/src/os/signal/signal_test.go b/src/os/signal/signal_test.go
index 23e33fe82b..bbc68af9fb 100644
--- a/src/os/signal/signal_test.go
+++ b/src/os/signal/signal_test.go
@@ -12,7 +12,6 @@ import (
"flag"
"fmt"
"internal/testenv"
- "io/ioutil"
"os"
"os/exec"
"runtime"
@@ -304,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)
@@ -676,22 +675,68 @@ func TestTime(t *testing.T) {
<-done
}
-func TestNotifyContext(t *testing.T) {
- c, stop := NotifyContext(context.Background(), syscall.SIGINT)
- defer stop()
+var (
+ checkNotifyContext = flag.Bool("check_notify_ctx", false, "if true, TestNotifyContext will fail if SIGINT is not received.")
+ ctxNotifyTimes = flag.Int("ctx_notify_times", 1, "number of times a SIGINT signal should be received")
+)
- if want, got := "signal.NotifyContext(context.Background, [interrupt])", fmt.Sprint(c); want != got {
- t.Errorf("c.String() = %q, want %q", got, want)
+func TestNotifyContextNotifications(t *testing.T) {
+ if *checkNotifyContext {
+ ctx, _ := NotifyContext(context.Background(), syscall.SIGINT)
+ // We want to make sure not to be calling Stop() internally on NotifyContext() when processing a received signal.
+ // Being able to wait for a number of received system signals allows us to do so.
+ var wg sync.WaitGroup
+ n := *ctxNotifyTimes
+ wg.Add(n)
+ for i := 0; i < n; i++ {
+ go func() {
+ syscall.Kill(syscall.Getpid(), syscall.SIGINT)
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+ <-ctx.Done()
+ fmt.Print("received SIGINT")
+ // Sleep to give time to simultaneous signals to reach the process.
+ // These signals must be ignored given stop() is not called on this code.
+ // We want to guarantee a SIGINT doesn't cause a premature termination of the program.
+ time.Sleep(settleTime)
+ return
}
- 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")
+ t.Parallel()
+ testCases := []struct {
+ name string
+ n int // number of times a SIGINT should be notified.
+ }{
+ {"once", 1},
+ {"multiple", 10},
+ }
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ var subTimeout time.Duration
+ if deadline, ok := t.Deadline(); ok {
+ subTimeout := time.Until(deadline)
+ subTimeout -= subTimeout / 10 // Leave 10% headroom for cleaning up subprocess.
+ }
+
+ args := []string{
+ "-test.v",
+ "-test.run=TestNotifyContextNotifications$",
+ "-check_notify_ctx",
+ fmt.Sprintf("-ctx_notify_times=%d", tc.n),
+ }
+ if subTimeout != 0 {
+ args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout))
+ }
+ out, err := exec.Command(os.Args[0], args...).CombinedOutput()
+ if err != nil {
+ t.Errorf("ran test with -check_notify_ctx_notification and it failed with %v.\nOutput:\n%s", err, out)
+ }
+ if want := []byte("received SIGINT"); !bytes.Contains(out, want) {
+ t.Errorf("got %q, wanted %q", out, want)
+ }
+ })
}
}
@@ -769,34 +814,6 @@ func TestNotifyContextPrematureCancelParent(t *testing.T) {
}
}
-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()
diff --git a/src/os/signal/signal_windows_test.go b/src/os/signal/signal_windows_test.go
index c2b59010fc..4640428587 100644
--- a/src/os/signal/signal_windows_test.go
+++ b/src/os/signal/signal_windows_test.go
@@ -7,7 +7,6 @@ package signal
import (
"bytes"
"internal/testenv"
- "io/ioutil"
"os"
"os/exec"
"path/filepath"
@@ -57,7 +56,7 @@ func main() {
}
}
`
- tmp, err := ioutil.TempDir("", "TestCtrlBreak")
+ tmp, err := os.MkdirTemp("", "TestCtrlBreak")
if err != nil {
t.Fatal("TempDir failed: ", err)
}
diff --git a/src/os/stat_test.go b/src/os/stat_test.go
index 88b789080e..c409f0ff18 100644
--- a/src/os/stat_test.go
+++ b/src/os/stat_test.go
@@ -7,7 +7,6 @@ package os_test
import (
"internal/testenv"
"io/fs"
- "io/ioutil"
"os"
"path/filepath"
"runtime"
@@ -186,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)
}
@@ -219,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)
}
@@ -253,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/src/os/tempfile.go b/src/os/tempfile.go
new file mode 100644
index 0000000000..1ad44f1163
--- /dev/null
+++ b/src/os/tempfile.go
@@ -0,0 +1,125 @@
+// 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"
+
+// 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, CreateTemp 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 := 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, MkdirTemp 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
+}
+
+// LastIndexByte from the strings package.
+func lastIndex(s string, sep byte) int {
+ for i := len(s) - 1; i >= 0; i-- {
+ if s[i] == sep {
+ return i
+ }
+ }
+ return -1
+}
diff --git a/src/os/tempfile_test.go b/src/os/tempfile_test.go
new file mode 100644
index 0000000000..e71a2444c9
--- /dev/null
+++ b/src/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/src/os/testdata/dirfs/a b/src/os/testdata/dirfs/a
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/os/testdata/dirfs/a
diff --git a/src/os/testdata/dirfs/b b/src/os/testdata/dirfs/b
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/os/testdata/dirfs/b
diff --git a/src/os/testdata/dirfs/dir/x b/src/os/testdata/dirfs/dir/x
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/os/testdata/dirfs/dir/x
diff --git a/src/os/testdata/hello b/src/os/testdata/hello
new file mode 100644
index 0000000000..e47c092a51
--- /dev/null
+++ b/src/os/testdata/hello
@@ -0,0 +1 @@
+Hello, Gophers!
diff --git a/src/os/timeout_test.go b/src/os/timeout_test.go
index d848e41642..0a39f46333 100644
--- a/src/os/timeout_test.go
+++ b/src/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)
}
diff --git a/src/os/user/lookup_plan9.go b/src/os/user/lookup_plan9.go
index ea3ce0bc7c..33ae3a6adf 100644
--- a/src/os/user/lookup_plan9.go
+++ b/src/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)
}