Source file src/os/os_test.go

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package os_test
     6  
     7  import (
     8  	"bytes"
     9  	"errors"
    10  	"flag"
    11  	"fmt"
    12  	"internal/testenv"
    13  	"io"
    14  	"io/fs"
    15  	"log"
    16  	. "os"
    17  	"os/exec"
    18  	"path/filepath"
    19  	"runtime"
    20  	"runtime/debug"
    21  	"slices"
    22  	"strconv"
    23  	"strings"
    24  	"sync"
    25  	"syscall"
    26  	"testing"
    27  	"testing/fstest"
    28  	"time"
    29  )
    30  
    31  func TestMain(m *testing.M) {
    32  	if Getenv("GO_OS_TEST_DRAIN_STDIN") == "1" {
    33  		Stdout.Close()
    34  		io.Copy(io.Discard, Stdin)
    35  		Exit(0)
    36  	}
    37  
    38  	log.SetFlags(log.LstdFlags | log.Lshortfile)
    39  
    40  	Exit(m.Run())
    41  }
    42  
    43  var dot = []string{
    44  	"dir_unix.go",
    45  	"env.go",
    46  	"error.go",
    47  	"file.go",
    48  	"os_test.go",
    49  	"types.go",
    50  	"stat_darwin.go",
    51  	"stat_linux.go",
    52  }
    53  
    54  type sysDir struct {
    55  	name  string
    56  	files []string
    57  }
    58  
    59  var sysdir = func() *sysDir {
    60  	switch runtime.GOOS {
    61  	case "android":
    62  		return &sysDir{
    63  			"/system/lib",
    64  			[]string{
    65  				"libmedia.so",
    66  				"libpowermanager.so",
    67  			},
    68  		}
    69  	case "ios":
    70  		wd, err := syscall.Getwd()
    71  		if err != nil {
    72  			wd = err.Error()
    73  		}
    74  		sd := &sysDir{
    75  			filepath.Join(wd, "..", ".."),
    76  			[]string{
    77  				"ResourceRules.plist",
    78  				"Info.plist",
    79  			},
    80  		}
    81  		found := true
    82  		for _, f := range sd.files {
    83  			path := filepath.Join(sd.name, f)
    84  			if _, err := Stat(path); err != nil {
    85  				found = false
    86  				break
    87  			}
    88  		}
    89  		if found {
    90  			return sd
    91  		}
    92  		// In a self-hosted iOS build the above files might
    93  		// not exist. Look for system files instead below.
    94  	case "windows":
    95  		return &sysDir{
    96  			Getenv("SystemRoot") + "\\system32\\drivers\\etc",
    97  			[]string{
    98  				"networks",
    99  				"protocol",
   100  				"services",
   101  			},
   102  		}
   103  	case "plan9":
   104  		return &sysDir{
   105  			"/lib/ndb",
   106  			[]string{
   107  				"common",
   108  				"local",
   109  			},
   110  		}
   111  	case "wasip1":
   112  		// wasmtime has issues resolving symbolic links that are often present
   113  		// in directories like /etc/group below (e.g. private/etc/group on OSX).
   114  		// For this reason we use files in the Go source tree instead.
   115  		return &sysDir{
   116  			runtime.GOROOT(),
   117  			[]string{
   118  				"go.env",
   119  				"LICENSE",
   120  				"CONTRIBUTING.md",
   121  			},
   122  		}
   123  	}
   124  	return &sysDir{
   125  		"/etc",
   126  		[]string{
   127  			"group",
   128  			"hosts",
   129  			"passwd",
   130  		},
   131  	}
   132  }()
   133  
   134  func size(name string, t *testing.T) int64 {
   135  	file, err := Open(name)
   136  	if err != nil {
   137  		t.Fatal("open failed:", err)
   138  	}
   139  	defer func() {
   140  		if err := file.Close(); err != nil {
   141  			t.Error(err)
   142  		}
   143  	}()
   144  	n, err := io.Copy(io.Discard, file)
   145  	if err != nil {
   146  		t.Fatal(err)
   147  	}
   148  	return n
   149  }
   150  
   151  func equal(name1, name2 string) (r bool) {
   152  	switch runtime.GOOS {
   153  	case "windows":
   154  		r = strings.EqualFold(name1, name2)
   155  	default:
   156  		r = name1 == name2
   157  	}
   158  	return
   159  }
   160  
   161  func newFile(t *testing.T) (f *File) {
   162  	t.Helper()
   163  	f, err := CreateTemp("", "_Go_"+t.Name())
   164  	if err != nil {
   165  		t.Fatal(err)
   166  	}
   167  	t.Cleanup(func() {
   168  		if err := f.Close(); err != nil && !errors.Is(err, ErrClosed) {
   169  			t.Fatal(err)
   170  		}
   171  		if err := Remove(f.Name()); err != nil {
   172  			t.Fatal(err)
   173  		}
   174  	})
   175  	return
   176  }
   177  
   178  var sfdir = sysdir.name
   179  var sfname = sysdir.files[0]
   180  
   181  func TestStat(t *testing.T) {
   182  	t.Parallel()
   183  
   184  	path := sfdir + "/" + sfname
   185  	dir, err := Stat(path)
   186  	if err != nil {
   187  		t.Fatal("stat failed:", err)
   188  	}
   189  	if !equal(sfname, dir.Name()) {
   190  		t.Error("name should be ", sfname, "; is", dir.Name())
   191  	}
   192  	filesize := size(path, t)
   193  	if dir.Size() != filesize {
   194  		t.Error("size should be", filesize, "; is", dir.Size())
   195  	}
   196  }
   197  
   198  func TestStatError(t *testing.T) {
   199  	t.Chdir(t.TempDir())
   200  
   201  	path := "no-such-file"
   202  
   203  	fi, err := Stat(path)
   204  	if err == nil {
   205  		t.Fatal("got nil, want error")
   206  	}
   207  	if fi != nil {
   208  		t.Errorf("got %v, want nil", fi)
   209  	}
   210  	if perr, ok := err.(*PathError); !ok {
   211  		t.Errorf("got %T, want %T", err, perr)
   212  	}
   213  
   214  	testenv.MustHaveSymlink(t)
   215  
   216  	link := "symlink"
   217  	err = Symlink(path, link)
   218  	if err != nil {
   219  		t.Fatal(err)
   220  	}
   221  
   222  	fi, err = Stat(link)
   223  	if err == nil {
   224  		t.Fatal("got nil, want error")
   225  	}
   226  	if fi != nil {
   227  		t.Errorf("got %v, want nil", fi)
   228  	}
   229  	if perr, ok := err.(*PathError); !ok {
   230  		t.Errorf("got %T, want %T", err, perr)
   231  	}
   232  }
   233  
   234  func TestStatSymlinkLoop(t *testing.T) {
   235  	testenv.MustHaveSymlink(t)
   236  	t.Chdir(t.TempDir())
   237  
   238  	err := Symlink("x", "y")
   239  	if err != nil {
   240  		t.Fatal(err)
   241  	}
   242  	defer Remove("y")
   243  
   244  	err = Symlink("y", "x")
   245  	if err != nil {
   246  		t.Fatal(err)
   247  	}
   248  	defer Remove("x")
   249  
   250  	_, err = Stat("x")
   251  	if _, ok := err.(*fs.PathError); !ok {
   252  		t.Errorf("expected *PathError, got %T: %v\n", err, err)
   253  	}
   254  }
   255  
   256  func TestFstat(t *testing.T) {
   257  	t.Parallel()
   258  
   259  	path := sfdir + "/" + sfname
   260  	file, err1 := Open(path)
   261  	if err1 != nil {
   262  		t.Fatal("open failed:", err1)
   263  	}
   264  	defer file.Close()
   265  	dir, err2 := file.Stat()
   266  	if err2 != nil {
   267  		t.Fatal("fstat failed:", err2)
   268  	}
   269  	if !equal(sfname, dir.Name()) {
   270  		t.Error("name should be ", sfname, "; is", dir.Name())
   271  	}
   272  	filesize := size(path, t)
   273  	if dir.Size() != filesize {
   274  		t.Error("size should be", filesize, "; is", dir.Size())
   275  	}
   276  }
   277  
   278  func TestLstat(t *testing.T) {
   279  	t.Parallel()
   280  
   281  	path := sfdir + "/" + sfname
   282  	dir, err := Lstat(path)
   283  	if err != nil {
   284  		t.Fatal("lstat failed:", err)
   285  	}
   286  	if !equal(sfname, dir.Name()) {
   287  		t.Error("name should be ", sfname, "; is", dir.Name())
   288  	}
   289  	if dir.Mode()&ModeSymlink == 0 {
   290  		filesize := size(path, t)
   291  		if dir.Size() != filesize {
   292  			t.Error("size should be", filesize, "; is", dir.Size())
   293  		}
   294  	}
   295  }
   296  
   297  // Read with length 0 should not return EOF.
   298  func TestRead0(t *testing.T) {
   299  	t.Parallel()
   300  
   301  	path := sfdir + "/" + sfname
   302  	f, err := Open(path)
   303  	if err != nil {
   304  		t.Fatal("open failed:", err)
   305  	}
   306  	defer f.Close()
   307  
   308  	b := make([]byte, 0)
   309  	n, err := f.Read(b)
   310  	if n != 0 || err != nil {
   311  		t.Errorf("Read(0) = %d, %v, want 0, nil", n, err)
   312  	}
   313  	b = make([]byte, 100)
   314  	n, err = f.Read(b)
   315  	if n <= 0 || err != nil {
   316  		t.Errorf("Read(100) = %d, %v, want >0, nil", n, err)
   317  	}
   318  }
   319  
   320  // Reading a closed file should return ErrClosed error
   321  func TestReadClosed(t *testing.T) {
   322  	t.Parallel()
   323  
   324  	path := sfdir + "/" + sfname
   325  	file, err := Open(path)
   326  	if err != nil {
   327  		t.Fatal("open failed:", err)
   328  	}
   329  	file.Close() // close immediately
   330  
   331  	b := make([]byte, 100)
   332  	_, err = file.Read(b)
   333  
   334  	e, ok := err.(*PathError)
   335  	if !ok || e.Err != ErrClosed {
   336  		t.Fatalf("Read: got %T(%v), want %T(%v)", err, err, e, ErrClosed)
   337  	}
   338  }
   339  
   340  func testReaddirnames(dir string, contents []string) func(*testing.T) {
   341  	return func(t *testing.T) {
   342  		t.Parallel()
   343  
   344  		file, err := Open(dir)
   345  		if err != nil {
   346  			t.Fatalf("open %q failed: %v", dir, err)
   347  		}
   348  		defer file.Close()
   349  		s, err2 := file.Readdirnames(-1)
   350  		if err2 != nil {
   351  			t.Fatalf("Readdirnames %q failed: %v", dir, err2)
   352  		}
   353  		for _, m := range contents {
   354  			found := false
   355  			for _, n := range s {
   356  				if n == "." || n == ".." {
   357  					t.Errorf("got %q in directory", n)
   358  				}
   359  				if !equal(m, n) {
   360  					continue
   361  				}
   362  				if found {
   363  					t.Error("present twice:", m)
   364  				}
   365  				found = true
   366  			}
   367  			if !found {
   368  				t.Error("could not find", m)
   369  			}
   370  		}
   371  		if s == nil {
   372  			t.Error("Readdirnames returned nil instead of empty slice")
   373  		}
   374  	}
   375  }
   376  
   377  func testReaddir(dir string, contents []string) func(*testing.T) {
   378  	return func(t *testing.T) {
   379  		t.Parallel()
   380  
   381  		file, err := Open(dir)
   382  		if err != nil {
   383  			t.Fatalf("open %q failed: %v", dir, err)
   384  		}
   385  		defer file.Close()
   386  		s, err2 := file.Readdir(-1)
   387  		if err2 != nil {
   388  			t.Fatalf("Readdir %q failed: %v", dir, err2)
   389  		}
   390  		for _, m := range contents {
   391  			found := false
   392  			for _, n := range s {
   393  				if n.Name() == "." || n.Name() == ".." {
   394  					t.Errorf("got %q in directory", n.Name())
   395  				}
   396  				if !equal(m, n.Name()) {
   397  					continue
   398  				}
   399  				if found {
   400  					t.Error("present twice:", m)
   401  				}
   402  				found = true
   403  			}
   404  			if !found {
   405  				t.Error("could not find", m)
   406  			}
   407  		}
   408  		if s == nil {
   409  			t.Error("Readdir returned nil instead of empty slice")
   410  		}
   411  	}
   412  }
   413  
   414  func testReadDir(dir string, contents []string) func(*testing.T) {
   415  	return func(t *testing.T) {
   416  		t.Parallel()
   417  
   418  		file, err := Open(dir)
   419  		if err != nil {
   420  			t.Fatalf("open %q failed: %v", dir, err)
   421  		}
   422  		defer file.Close()
   423  		s, err2 := file.ReadDir(-1)
   424  		if err2 != nil {
   425  			t.Fatalf("ReadDir %q failed: %v", dir, err2)
   426  		}
   427  		for _, m := range contents {
   428  			found := false
   429  			for _, n := range s {
   430  				if n.Name() == "." || n.Name() == ".." {
   431  					t.Errorf("got %q in directory", n)
   432  				}
   433  				if !equal(m, n.Name()) {
   434  					continue
   435  				}
   436  				if found {
   437  					t.Error("present twice:", m)
   438  				}
   439  				found = true
   440  				lstat, err := Lstat(dir + "/" + m)
   441  				if err != nil {
   442  					t.Fatal(err)
   443  				}
   444  				if n.IsDir() != lstat.IsDir() {
   445  					t.Errorf("%s: IsDir=%v, want %v", m, n.IsDir(), lstat.IsDir())
   446  				}
   447  				if n.Type() != lstat.Mode().Type() {
   448  					t.Errorf("%s: IsDir=%v, want %v", m, n.Type(), lstat.Mode().Type())
   449  				}
   450  				info, err := n.Info()
   451  				if err != nil {
   452  					t.Errorf("%s: Info: %v", m, err)
   453  					continue
   454  				}
   455  				if !SameFile(info, lstat) {
   456  					t.Errorf("%s: Info: SameFile(info, lstat) = false", m)
   457  				}
   458  			}
   459  			if !found {
   460  				t.Error("could not find", m)
   461  			}
   462  		}
   463  		if s == nil {
   464  			t.Error("ReadDir returned nil instead of empty slice")
   465  		}
   466  	}
   467  }
   468  
   469  func TestFileReaddirnames(t *testing.T) {
   470  	t.Parallel()
   471  
   472  	t.Run(".", testReaddirnames(".", dot))
   473  	t.Run("sysdir", testReaddirnames(sysdir.name, sysdir.files))
   474  	t.Run("TempDir", testReaddirnames(t.TempDir(), nil))
   475  }
   476  
   477  func TestFileReaddir(t *testing.T) {
   478  	t.Parallel()
   479  
   480  	t.Run(".", testReaddir(".", dot))
   481  	t.Run("sysdir", testReaddir(sysdir.name, sysdir.files))
   482  	t.Run("TempDir", testReaddir(t.TempDir(), nil))
   483  }
   484  
   485  func TestFileReadDir(t *testing.T) {
   486  	t.Parallel()
   487  
   488  	t.Run(".", testReadDir(".", dot))
   489  	t.Run("sysdir", testReadDir(sysdir.name, sysdir.files))
   490  	t.Run("TempDir", testReadDir(t.TempDir(), nil))
   491  }
   492  
   493  func benchmarkReaddirname(path string, b *testing.B) {
   494  	var nentries int
   495  	for i := 0; i < b.N; i++ {
   496  		f, err := Open(path)
   497  		if err != nil {
   498  			b.Fatalf("open %q failed: %v", path, err)
   499  		}
   500  		ns, err := f.Readdirnames(-1)
   501  		f.Close()
   502  		if err != nil {
   503  			b.Fatalf("readdirnames %q failed: %v", path, err)
   504  		}
   505  		nentries = len(ns)
   506  	}
   507  	b.Logf("benchmarkReaddirname %q: %d entries", path, nentries)
   508  }
   509  
   510  func benchmarkReaddir(path string, b *testing.B) {
   511  	var nentries int
   512  	for i := 0; i < b.N; i++ {
   513  		f, err := Open(path)
   514  		if err != nil {
   515  			b.Fatalf("open %q failed: %v", path, err)
   516  		}
   517  		fs, err := f.Readdir(-1)
   518  		f.Close()
   519  		if err != nil {
   520  			b.Fatalf("readdir %q failed: %v", path, err)
   521  		}
   522  		nentries = len(fs)
   523  	}
   524  	b.Logf("benchmarkReaddir %q: %d entries", path, nentries)
   525  }
   526  
   527  func benchmarkReadDir(path string, b *testing.B) {
   528  	var nentries int
   529  	for i := 0; i < b.N; i++ {
   530  		f, err := Open(path)
   531  		if err != nil {
   532  			b.Fatalf("open %q failed: %v", path, err)
   533  		}
   534  		fs, err := f.ReadDir(-1)
   535  		f.Close()
   536  		if err != nil {
   537  			b.Fatalf("readdir %q failed: %v", path, err)
   538  		}
   539  		nentries = len(fs)
   540  	}
   541  	b.Logf("benchmarkReadDir %q: %d entries", path, nentries)
   542  }
   543  
   544  func BenchmarkReaddirname(b *testing.B) {
   545  	benchmarkReaddirname(".", b)
   546  }
   547  
   548  func BenchmarkReaddir(b *testing.B) {
   549  	benchmarkReaddir(".", b)
   550  }
   551  
   552  func BenchmarkReadDir(b *testing.B) {
   553  	benchmarkReadDir(".", b)
   554  }
   555  
   556  func benchmarkStat(b *testing.B, path string) {
   557  	b.ResetTimer()
   558  	for i := 0; i < b.N; i++ {
   559  		_, err := Stat(path)
   560  		if err != nil {
   561  			b.Fatalf("Stat(%q) failed: %v", path, err)
   562  		}
   563  	}
   564  }
   565  
   566  func benchmarkLstat(b *testing.B, path string) {
   567  	b.ResetTimer()
   568  	for i := 0; i < b.N; i++ {
   569  		_, err := Lstat(path)
   570  		if err != nil {
   571  			b.Fatalf("Lstat(%q) failed: %v", path, err)
   572  		}
   573  	}
   574  }
   575  
   576  func BenchmarkStatDot(b *testing.B) {
   577  	benchmarkStat(b, ".")
   578  }
   579  
   580  func BenchmarkStatFile(b *testing.B) {
   581  	benchmarkStat(b, filepath.Join(runtime.GOROOT(), "src/os/os_test.go"))
   582  }
   583  
   584  func BenchmarkStatDir(b *testing.B) {
   585  	benchmarkStat(b, filepath.Join(runtime.GOROOT(), "src/os"))
   586  }
   587  
   588  func BenchmarkLstatDot(b *testing.B) {
   589  	benchmarkLstat(b, ".")
   590  }
   591  
   592  func BenchmarkLstatFile(b *testing.B) {
   593  	benchmarkLstat(b, filepath.Join(runtime.GOROOT(), "src/os/os_test.go"))
   594  }
   595  
   596  func BenchmarkLstatDir(b *testing.B) {
   597  	benchmarkLstat(b, filepath.Join(runtime.GOROOT(), "src/os"))
   598  }
   599  
   600  // Read the directory one entry at a time.
   601  func smallReaddirnames(file *File, length int, t *testing.T) []string {
   602  	names := make([]string, length)
   603  	count := 0
   604  	for {
   605  		d, err := file.Readdirnames(1)
   606  		if err == io.EOF {
   607  			break
   608  		}
   609  		if err != nil {
   610  			t.Fatalf("readdirnames %q failed: %v", file.Name(), err)
   611  		}
   612  		if len(d) == 0 {
   613  			t.Fatalf("readdirnames %q returned empty slice and no error", file.Name())
   614  		}
   615  		names[count] = d[0]
   616  		count++
   617  	}
   618  	return names[0:count]
   619  }
   620  
   621  // Check that reading a directory one entry at a time gives the same result
   622  // as reading it all at once.
   623  func TestReaddirnamesOneAtATime(t *testing.T) {
   624  	t.Parallel()
   625  
   626  	// big directory that doesn't change often.
   627  	dir := "/usr/bin"
   628  	switch runtime.GOOS {
   629  	case "android":
   630  		dir = "/system/bin"
   631  	case "ios", "wasip1":
   632  		wd, err := Getwd()
   633  		if err != nil {
   634  			t.Fatal(err)
   635  		}
   636  		dir = wd
   637  	case "plan9":
   638  		dir = "/bin"
   639  	case "windows":
   640  		dir = Getenv("SystemRoot") + "\\system32"
   641  	}
   642  	file, err := Open(dir)
   643  	if err != nil {
   644  		t.Fatalf("open %q failed: %v", dir, err)
   645  	}
   646  	defer file.Close()
   647  	all, err1 := file.Readdirnames(-1)
   648  	if err1 != nil {
   649  		t.Fatalf("readdirnames %q failed: %v", dir, err1)
   650  	}
   651  	file1, err2 := Open(dir)
   652  	if err2 != nil {
   653  		t.Fatalf("open %q failed: %v", dir, err2)
   654  	}
   655  	defer file1.Close()
   656  	small := smallReaddirnames(file1, len(all)+100, t) // +100 in case we screw up
   657  	if len(small) < len(all) {
   658  		t.Fatalf("len(small) is %d, less than %d", len(small), len(all))
   659  	}
   660  	for i, n := range all {
   661  		if small[i] != n {
   662  			t.Errorf("small read %q mismatch: %v", small[i], n)
   663  		}
   664  	}
   665  }
   666  
   667  func TestReaddirNValues(t *testing.T) {
   668  	if testing.Short() {
   669  		t.Skip("test.short; skipping")
   670  	}
   671  	t.Parallel()
   672  
   673  	dir := t.TempDir()
   674  	for i := 1; i <= 105; i++ {
   675  		f, err := Create(filepath.Join(dir, fmt.Sprintf("%d", i)))
   676  		if err != nil {
   677  			t.Fatalf("Create: %v", err)
   678  		}
   679  		f.Write([]byte(strings.Repeat("X", i)))
   680  		f.Close()
   681  	}
   682  
   683  	var d *File
   684  	openDir := func() {
   685  		var err error
   686  		d, err = Open(dir)
   687  		if err != nil {
   688  			t.Fatalf("Open directory: %v", err)
   689  		}
   690  	}
   691  
   692  	readdirExpect := func(n, want int, wantErr error) {
   693  		t.Helper()
   694  		fi, err := d.Readdir(n)
   695  		if err != wantErr {
   696  			t.Fatalf("Readdir of %d got error %v, want %v", n, err, wantErr)
   697  		}
   698  		if g, e := len(fi), want; g != e {
   699  			t.Errorf("Readdir of %d got %d files, want %d", n, g, e)
   700  		}
   701  	}
   702  
   703  	readDirExpect := func(n, want int, wantErr error) {
   704  		t.Helper()
   705  		de, err := d.ReadDir(n)
   706  		if err != wantErr {
   707  			t.Fatalf("ReadDir of %d got error %v, want %v", n, err, wantErr)
   708  		}
   709  		if g, e := len(de), want; g != e {
   710  			t.Errorf("ReadDir of %d got %d files, want %d", n, g, e)
   711  		}
   712  	}
   713  
   714  	readdirnamesExpect := func(n, want int, wantErr error) {
   715  		t.Helper()
   716  		fi, err := d.Readdirnames(n)
   717  		if err != wantErr {
   718  			t.Fatalf("Readdirnames of %d got error %v, want %v", n, err, wantErr)
   719  		}
   720  		if g, e := len(fi), want; g != e {
   721  			t.Errorf("Readdirnames of %d got %d files, want %d", n, g, e)
   722  		}
   723  	}
   724  
   725  	for _, fn := range []func(int, int, error){readdirExpect, readdirnamesExpect, readDirExpect} {
   726  		// Test the slurp case
   727  		openDir()
   728  		fn(0, 105, nil)
   729  		fn(0, 0, nil)
   730  		d.Close()
   731  
   732  		// Slurp with -1 instead
   733  		openDir()
   734  		fn(-1, 105, nil)
   735  		fn(-2, 0, nil)
   736  		fn(0, 0, nil)
   737  		d.Close()
   738  
   739  		// Test the bounded case
   740  		openDir()
   741  		fn(1, 1, nil)
   742  		fn(2, 2, nil)
   743  		fn(105, 102, nil) // and tests buffer >100 case
   744  		fn(3, 0, io.EOF)
   745  		d.Close()
   746  	}
   747  }
   748  
   749  func touch(t *testing.T, name string) {
   750  	f, err := Create(name)
   751  	if err != nil {
   752  		t.Fatal(err)
   753  	}
   754  	if err := f.Close(); err != nil {
   755  		t.Fatal(err)
   756  	}
   757  }
   758  
   759  func TestReaddirStatFailures(t *testing.T) {
   760  	switch runtime.GOOS {
   761  	case "windows", "plan9":
   762  		// Windows and Plan 9 already do this correctly,
   763  		// but are structured with different syscalls such
   764  		// that they don't use Lstat, so the hook below for
   765  		// testing it wouldn't work.
   766  		t.Skipf("skipping test on %v", runtime.GOOS)
   767  	}
   768  
   769  	var xerr error // error to return for x
   770  	SetStatHook(t, func(f *File, path string) (FileInfo, error) {
   771  		if xerr != nil && strings.HasSuffix(path, "x") {
   772  			return nil, xerr
   773  		}
   774  		return nil, nil
   775  	})
   776  
   777  	dir := t.TempDir()
   778  	touch(t, filepath.Join(dir, "good1"))
   779  	touch(t, filepath.Join(dir, "x")) // will disappear or have an error
   780  	touch(t, filepath.Join(dir, "good2"))
   781  	readDir := func() ([]FileInfo, error) {
   782  		d, err := Open(dir)
   783  		if err != nil {
   784  			t.Fatal(err)
   785  		}
   786  		defer d.Close()
   787  		return d.Readdir(-1)
   788  	}
   789  	mustReadDir := func(testName string) []FileInfo {
   790  		fis, err := readDir()
   791  		if err != nil {
   792  			t.Fatalf("%s: Readdir: %v", testName, err)
   793  		}
   794  		return fis
   795  	}
   796  	names := func(fis []FileInfo) []string {
   797  		s := make([]string, len(fis))
   798  		for i, fi := range fis {
   799  			s[i] = fi.Name()
   800  		}
   801  		slices.Sort(s)
   802  		return s
   803  	}
   804  
   805  	if got, want := names(mustReadDir("initial readdir")),
   806  		[]string{"good1", "good2", "x"}; !slices.Equal(got, want) {
   807  		t.Errorf("initial readdir got %q; want %q", got, want)
   808  	}
   809  
   810  	xerr = ErrNotExist
   811  	if got, want := names(mustReadDir("with x disappearing")),
   812  		[]string{"good1", "good2"}; !slices.Equal(got, want) {
   813  		t.Errorf("with x disappearing, got %q; want %q", got, want)
   814  	}
   815  
   816  	xerr = errors.New("some real error")
   817  	if _, err := readDir(); err != xerr {
   818  		t.Errorf("with a non-ErrNotExist error, got error %v; want %v", err, xerr)
   819  	}
   820  }
   821  
   822  // Readdir on a regular file should fail.
   823  func TestReaddirOfFile(t *testing.T) {
   824  	t.Parallel()
   825  
   826  	f, err := CreateTemp(t.TempDir(), "_Go_ReaddirOfFile")
   827  	if err != nil {
   828  		t.Fatal(err)
   829  	}
   830  	f.Write([]byte("foo"))
   831  	f.Close()
   832  	reg, err := Open(f.Name())
   833  	if err != nil {
   834  		t.Fatal(err)
   835  	}
   836  	defer reg.Close()
   837  
   838  	names, err := reg.Readdirnames(-1)
   839  	if err == nil {
   840  		t.Error("Readdirnames succeeded; want non-nil error")
   841  	}
   842  	if pe, ok := errors.AsType[*PathError](err); !ok || pe.Path != f.Name() {
   843  		t.Errorf("Readdirnames returned %q; want a PathError with path %q", err, f.Name())
   844  	}
   845  	if len(names) > 0 {
   846  		t.Errorf("unexpected dir names in regular file: %q", names)
   847  	}
   848  }
   849  
   850  func TestHardLink(t *testing.T) {
   851  	testMaybeRooted(t, testHardLink)
   852  }
   853  func testHardLink(t *testing.T, root *Root) {
   854  	testenv.MustHaveLink(t)
   855  
   856  	var (
   857  		create = Create
   858  		link   = Link
   859  		stat   = Stat
   860  		op     = "link"
   861  	)
   862  	if root != nil {
   863  		create = root.Create
   864  		link = root.Link
   865  		stat = root.Stat
   866  		op = "linkat"
   867  	}
   868  
   869  	from, to := "hardlinktestfrom", "hardlinktestto"
   870  	file, err := create(to)
   871  	if err != nil {
   872  		t.Fatalf("open %q failed: %v", to, err)
   873  	}
   874  	if err = file.Close(); err != nil {
   875  		t.Errorf("close %q failed: %v", to, err)
   876  	}
   877  	err = link(to, from)
   878  	if err != nil {
   879  		t.Fatalf("link %q, %q failed: %v", to, from, err)
   880  	}
   881  
   882  	none := "hardlinktestnone"
   883  	err = link(none, none)
   884  	// Check the returned error is well-formed.
   885  	if lerr, ok := err.(*LinkError); !ok || lerr.Error() == "" {
   886  		t.Errorf("link %q, %q failed to return a valid error", none, none)
   887  	}
   888  
   889  	tostat, err := stat(to)
   890  	if err != nil {
   891  		t.Fatalf("stat %q failed: %v", to, err)
   892  	}
   893  	fromstat, err := stat(from)
   894  	if err != nil {
   895  		t.Fatalf("stat %q failed: %v", from, err)
   896  	}
   897  	if !SameFile(tostat, fromstat) {
   898  		t.Errorf("link %q, %q did not create hard link", to, from)
   899  	}
   900  	// We should not be able to perform the same Link() a second time
   901  	err = link(to, from)
   902  	switch err := err.(type) {
   903  	case *LinkError:
   904  		if err.Op != op {
   905  			t.Errorf("Link(%q, %q) err.Op = %q; want %q", to, from, err.Op, op)
   906  		}
   907  		if err.Old != to {
   908  			t.Errorf("Link(%q, %q) err.Old = %q; want %q", to, from, err.Old, to)
   909  		}
   910  		if err.New != from {
   911  			t.Errorf("Link(%q, %q) err.New = %q; want %q", to, from, err.New, from)
   912  		}
   913  		if !IsExist(err.Err) {
   914  			t.Errorf("Link(%q, %q) err.Err = %q; want %q", to, from, err.Err, "file exists error")
   915  		}
   916  	case nil:
   917  		t.Errorf("link %q, %q: expected error, got nil", from, to)
   918  	default:
   919  		t.Errorf("link %q, %q: expected %T, got %T %v", from, to, new(LinkError), err, err)
   920  	}
   921  }
   922  
   923  func TestSymlink(t *testing.T) {
   924  	testMaybeRooted(t, testSymlink)
   925  }
   926  func testSymlink(t *testing.T, root *Root) {
   927  	testenv.MustHaveSymlink(t)
   928  
   929  	var (
   930  		create   = Create
   931  		open     = Open
   932  		symlink  = Symlink
   933  		stat     = Stat
   934  		lstat    = Lstat
   935  		readlink = Readlink
   936  	)
   937  	if root != nil {
   938  		create = root.Create
   939  		open = root.Open
   940  		symlink = root.Symlink
   941  		stat = root.Stat
   942  		lstat = root.Lstat
   943  		readlink = root.Readlink
   944  	}
   945  
   946  	from, to := "symlinktestfrom", "symlinktestto"
   947  	file, err := create(to)
   948  	if err != nil {
   949  		t.Fatalf("Create(%q) failed: %v", to, err)
   950  	}
   951  	if err = file.Close(); err != nil {
   952  		t.Errorf("Close(%q) failed: %v", to, err)
   953  	}
   954  	err = symlink(to, from)
   955  	if err != nil {
   956  		t.Fatalf("Symlink(%q, %q) failed: %v", to, from, err)
   957  	}
   958  	tostat, err := lstat(to)
   959  	if err != nil {
   960  		t.Fatalf("Lstat(%q) failed: %v", to, err)
   961  	}
   962  	if tostat.Mode()&ModeSymlink != 0 {
   963  		t.Fatalf("Lstat(%q).Mode()&ModeSymlink = %v, want 0", to, tostat.Mode()&ModeSymlink)
   964  	}
   965  	fromstat, err := stat(from)
   966  	if err != nil {
   967  		t.Fatalf("Stat(%q) failed: %v", from, err)
   968  	}
   969  	if !SameFile(tostat, fromstat) {
   970  		t.Errorf("Symlink(%q, %q) did not create symlink", to, from)
   971  	}
   972  	fromstat, err = lstat(from)
   973  	if err != nil {
   974  		t.Fatalf("Lstat(%q) failed: %v", from, err)
   975  	}
   976  	if fromstat.Mode()&ModeSymlink == 0 {
   977  		t.Fatalf("Lstat(%q).Mode()&ModeSymlink = 0, want %v", from, ModeSymlink)
   978  	}
   979  	fromstat, err = stat(from)
   980  	if err != nil {
   981  		t.Fatalf("Stat(%q) failed: %v", from, err)
   982  	}
   983  	if fromstat.Name() != from {
   984  		t.Errorf("Stat(%q).Name() = %q, want %q", from, fromstat.Name(), from)
   985  	}
   986  	if fromstat.Mode()&ModeSymlink != 0 {
   987  		t.Fatalf("Stat(%q).Mode()&ModeSymlink = %v, want 0", from, fromstat.Mode()&ModeSymlink)
   988  	}
   989  	s, err := readlink(from)
   990  	if err != nil {
   991  		t.Fatalf("Readlink(%q) failed: %v", from, err)
   992  	}
   993  	if s != to {
   994  		t.Fatalf("Readlink(%q) = %q, want %q", from, s, to)
   995  	}
   996  	file, err = open(from)
   997  	if err != nil {
   998  		t.Fatalf("Open(%q) failed: %v", from, err)
   999  	}
  1000  	file.Close()
  1001  }
  1002  
  1003  func TestLongSymlink(t *testing.T) {
  1004  	testenv.MustHaveSymlink(t)
  1005  	t.Chdir(t.TempDir())
  1006  
  1007  	s := "0123456789abcdef"
  1008  	// Long, but not too long: a common limit is 255.
  1009  	s = s + s + s + s + s + s + s + s + s + s + s + s + s + s + s
  1010  	from := "longsymlinktestfrom"
  1011  	err := Symlink(s, from)
  1012  	if err != nil {
  1013  		t.Fatalf("symlink %q, %q failed: %v", s, from, err)
  1014  	}
  1015  	r, err := Readlink(from)
  1016  	if err != nil {
  1017  		t.Fatalf("readlink %q failed: %v", from, err)
  1018  	}
  1019  	if r != s {
  1020  		t.Fatalf("after symlink %q != %q", r, s)
  1021  	}
  1022  }
  1023  
  1024  func TestRename(t *testing.T) {
  1025  	t.Chdir(t.TempDir())
  1026  	from, to := "renamefrom", "renameto"
  1027  
  1028  	file, err := Create(from)
  1029  	if err != nil {
  1030  		t.Fatalf("open %q failed: %v", from, err)
  1031  	}
  1032  	if err = file.Close(); err != nil {
  1033  		t.Errorf("close %q failed: %v", from, err)
  1034  	}
  1035  	err = Rename(from, to)
  1036  	if err != nil {
  1037  		t.Fatalf("rename %q, %q failed: %v", to, from, err)
  1038  	}
  1039  	_, err = Stat(to)
  1040  	if err != nil {
  1041  		t.Errorf("stat %q failed: %v", to, err)
  1042  	}
  1043  }
  1044  
  1045  func TestRenameOverwriteDest(t *testing.T) {
  1046  	t.Chdir(t.TempDir())
  1047  	from, to := "renamefrom", "renameto"
  1048  
  1049  	toData := []byte("to")
  1050  	fromData := []byte("from")
  1051  
  1052  	err := WriteFile(to, toData, 0777)
  1053  	if err != nil {
  1054  		t.Fatalf("write file %q failed: %v", to, err)
  1055  	}
  1056  
  1057  	err = WriteFile(from, fromData, 0777)
  1058  	if err != nil {
  1059  		t.Fatalf("write file %q failed: %v", from, err)
  1060  	}
  1061  	err = Rename(from, to)
  1062  	if err != nil {
  1063  		t.Fatalf("rename %q, %q failed: %v", to, from, err)
  1064  	}
  1065  
  1066  	_, err = Stat(from)
  1067  	if err == nil {
  1068  		t.Errorf("from file %q still exists", from)
  1069  	}
  1070  	if err != nil && !IsNotExist(err) {
  1071  		t.Fatalf("stat from: %v", err)
  1072  	}
  1073  	toFi, err := Stat(to)
  1074  	if err != nil {
  1075  		t.Fatalf("stat %q failed: %v", to, err)
  1076  	}
  1077  	if toFi.Size() != int64(len(fromData)) {
  1078  		t.Errorf(`"to" size = %d; want %d (old "from" size)`, toFi.Size(), len(fromData))
  1079  	}
  1080  }
  1081  
  1082  func TestRenameFailed(t *testing.T) {
  1083  	t.Chdir(t.TempDir())
  1084  	from, to := "renamefrom", "renameto"
  1085  
  1086  	err := Rename(from, to)
  1087  	switch err := err.(type) {
  1088  	case *LinkError:
  1089  		if err.Op != "rename" {
  1090  			t.Errorf("rename %q, %q: err.Op: want %q, got %q", from, to, "rename", err.Op)
  1091  		}
  1092  		if err.Old != from {
  1093  			t.Errorf("rename %q, %q: err.Old: want %q, got %q", from, to, from, err.Old)
  1094  		}
  1095  		if err.New != to {
  1096  			t.Errorf("rename %q, %q: err.New: want %q, got %q", from, to, to, err.New)
  1097  		}
  1098  	case nil:
  1099  		t.Errorf("rename %q, %q: expected error, got nil", from, to)
  1100  	default:
  1101  		t.Errorf("rename %q, %q: expected %T, got %T %v", from, to, new(LinkError), err, err)
  1102  	}
  1103  }
  1104  
  1105  func TestRenameNotExisting(t *testing.T) {
  1106  	t.Chdir(t.TempDir())
  1107  	from, to := "doesnt-exist", "dest"
  1108  
  1109  	Mkdir(to, 0777)
  1110  
  1111  	if err := Rename(from, to); !IsNotExist(err) {
  1112  		t.Errorf("Rename(%q, %q) = %v; want an IsNotExist error", from, to, err)
  1113  	}
  1114  }
  1115  
  1116  func TestRenameToDirFailed(t *testing.T) {
  1117  	t.Chdir(t.TempDir())
  1118  	from, to := "renamefrom", "renameto"
  1119  
  1120  	Mkdir(from, 0777)
  1121  	Mkdir(to, 0777)
  1122  
  1123  	err := Rename(from, to)
  1124  	switch err := err.(type) {
  1125  	case *LinkError:
  1126  		if err.Op != "rename" {
  1127  			t.Errorf("rename %q, %q: err.Op: want %q, got %q", from, to, "rename", err.Op)
  1128  		}
  1129  		if err.Old != from {
  1130  			t.Errorf("rename %q, %q: err.Old: want %q, got %q", from, to, from, err.Old)
  1131  		}
  1132  		if err.New != to {
  1133  			t.Errorf("rename %q, %q: err.New: want %q, got %q", from, to, to, err.New)
  1134  		}
  1135  	case nil:
  1136  		t.Errorf("rename %q, %q: expected error, got nil", from, to)
  1137  	default:
  1138  		t.Errorf("rename %q, %q: expected %T, got %T %v", from, to, new(LinkError), err, err)
  1139  	}
  1140  }
  1141  
  1142  func TestRenameCaseDifference(pt *testing.T) {
  1143  	from, to := "renameFROM", "RENAMEfrom"
  1144  	tests := []struct {
  1145  		name   string
  1146  		create func() error
  1147  	}{
  1148  		{"dir", func() error {
  1149  			return Mkdir(from, 0777)
  1150  		}},
  1151  		{"file", func() error {
  1152  			fd, err := Create(from)
  1153  			if err != nil {
  1154  				return err
  1155  			}
  1156  			return fd.Close()
  1157  		}},
  1158  	}
  1159  
  1160  	for _, test := range tests {
  1161  		pt.Run(test.name, func(t *testing.T) {
  1162  			t.Chdir(t.TempDir())
  1163  
  1164  			if err := test.create(); err != nil {
  1165  				t.Fatalf("failed to create test file: %s", err)
  1166  			}
  1167  
  1168  			if _, err := Stat(to); err != nil {
  1169  				// Sanity check that the underlying filesystem is not case sensitive.
  1170  				if IsNotExist(err) {
  1171  					t.Skipf("case sensitive filesystem")
  1172  				}
  1173  				t.Fatalf("stat %q, got: %q", to, err)
  1174  			}
  1175  
  1176  			if err := Rename(from, to); err != nil {
  1177  				t.Fatalf("unexpected error when renaming from %q to %q: %s", from, to, err)
  1178  			}
  1179  
  1180  			fd, err := Open(".")
  1181  			if err != nil {
  1182  				t.Fatalf("Open .: %s", err)
  1183  			}
  1184  
  1185  			// Stat does not return the real case of the file (it returns what the called asked for)
  1186  			// So we have to use readdir to get the real name of the file.
  1187  			dirNames, err := fd.Readdirnames(-1)
  1188  			fd.Close()
  1189  			if err != nil {
  1190  				t.Fatalf("readdirnames: %s", err)
  1191  			}
  1192  
  1193  			if dirNamesLen := len(dirNames); dirNamesLen != 1 {
  1194  				t.Fatalf("unexpected dirNames len, got %d, want %d", dirNamesLen, 1)
  1195  			}
  1196  
  1197  			if dirNames[0] != to {
  1198  				t.Errorf("unexpected name, got %q, want %q", dirNames[0], to)
  1199  			}
  1200  		})
  1201  	}
  1202  }
  1203  
  1204  func testStartProcess(dir, cmd string, args []string, expect string) func(t *testing.T) {
  1205  	return func(t *testing.T) {
  1206  		t.Parallel()
  1207  
  1208  		r, w, err := Pipe()
  1209  		if err != nil {
  1210  			t.Fatalf("Pipe: %v", err)
  1211  		}
  1212  		defer r.Close()
  1213  		attr := &ProcAttr{Dir: dir, Files: []*File{nil, w, Stderr}}
  1214  		p, err := StartProcess(cmd, args, attr)
  1215  		if err != nil {
  1216  			t.Fatalf("StartProcess: %v", err)
  1217  		}
  1218  		w.Close()
  1219  
  1220  		var b strings.Builder
  1221  		io.Copy(&b, r)
  1222  		output := b.String()
  1223  
  1224  		fi1, _ := Stat(strings.TrimSpace(output))
  1225  		fi2, _ := Stat(expect)
  1226  		if !SameFile(fi1, fi2) {
  1227  			t.Errorf("exec %q returned %q wanted %q",
  1228  				strings.Join(append([]string{cmd}, args...), " "), output, expect)
  1229  		}
  1230  		p.Wait()
  1231  	}
  1232  }
  1233  
  1234  func TestStartProcess(t *testing.T) {
  1235  	testenv.MustHaveExec(t)
  1236  	t.Parallel()
  1237  
  1238  	var dir, cmd string
  1239  	var args []string
  1240  	switch runtime.GOOS {
  1241  	case "android":
  1242  		t.Skip("android doesn't have /bin/pwd")
  1243  	case "windows":
  1244  		cmd = Getenv("COMSPEC")
  1245  		dir = Getenv("SystemRoot")
  1246  		args = []string{"/c", "cd"}
  1247  	default:
  1248  		var err error
  1249  		cmd, err = exec.LookPath("pwd")
  1250  		if err != nil {
  1251  			t.Fatalf("Can't find pwd: %v", err)
  1252  		}
  1253  		dir = "/"
  1254  		args = []string{}
  1255  		t.Logf("Testing with %v", cmd)
  1256  	}
  1257  	cmddir, cmdbase := filepath.Split(cmd)
  1258  	args = append([]string{cmdbase}, args...)
  1259  	t.Run("absolute", testStartProcess(dir, cmd, args, dir))
  1260  	t.Run("relative", testStartProcess(cmddir, cmdbase, args, cmddir))
  1261  }
  1262  
  1263  func checkMode(t *testing.T, path string, mode FileMode) {
  1264  	dir, err := Stat(path)
  1265  	if err != nil {
  1266  		t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err)
  1267  	}
  1268  	if dir.Mode()&ModePerm != mode {
  1269  		t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode(), mode)
  1270  	}
  1271  }
  1272  
  1273  func TestChmod(t *testing.T) {
  1274  	// Chmod is not supported on wasip1.
  1275  	if runtime.GOOS == "wasip1" {
  1276  		t.Skip("Chmod is not supported on " + runtime.GOOS)
  1277  	}
  1278  	t.Parallel()
  1279  
  1280  	f := newFile(t)
  1281  	// Creation mode is read write
  1282  
  1283  	fm := FileMode(0456)
  1284  	if runtime.GOOS == "windows" {
  1285  		fm = FileMode(0444) // read-only file
  1286  	}
  1287  	if err := Chmod(f.Name(), fm); err != nil {
  1288  		t.Fatalf("chmod %s %#o: %s", f.Name(), fm, err)
  1289  	}
  1290  	checkMode(t, f.Name(), fm)
  1291  
  1292  	fm = FileMode(0123)
  1293  	if runtime.GOOS == "windows" {
  1294  		fm = FileMode(0666) // read-write file
  1295  	}
  1296  	if err := f.Chmod(fm); err != nil {
  1297  		t.Fatalf("chmod %s %#o: %s", f.Name(), fm, err)
  1298  	}
  1299  	checkMode(t, f.Name(), fm)
  1300  }
  1301  
  1302  func checkSize(t *testing.T, f *File, size int64) {
  1303  	t.Helper()
  1304  	dir, err := f.Stat()
  1305  	if err != nil {
  1306  		t.Fatalf("Stat %q (looking for size %d): %s", f.Name(), size, err)
  1307  	}
  1308  	if dir.Size() != size {
  1309  		t.Errorf("Stat %q: size %d want %d", f.Name(), dir.Size(), size)
  1310  	}
  1311  }
  1312  
  1313  func TestFTruncate(t *testing.T) {
  1314  	t.Parallel()
  1315  
  1316  	f := newFile(t)
  1317  
  1318  	checkSize(t, f, 0)
  1319  	f.Write([]byte("hello, world\n"))
  1320  	checkSize(t, f, 13)
  1321  	f.Truncate(10)
  1322  	checkSize(t, f, 10)
  1323  	f.Truncate(1024)
  1324  	checkSize(t, f, 1024)
  1325  	f.Truncate(0)
  1326  	checkSize(t, f, 0)
  1327  	_, err := f.Write([]byte("surprise!"))
  1328  	if err == nil {
  1329  		checkSize(t, f, 13+9) // wrote at offset past where hello, world was.
  1330  	}
  1331  }
  1332  
  1333  func TestTruncate(t *testing.T) {
  1334  	t.Parallel()
  1335  
  1336  	f := newFile(t)
  1337  
  1338  	checkSize(t, f, 0)
  1339  	f.Write([]byte("hello, world\n"))
  1340  	checkSize(t, f, 13)
  1341  	Truncate(f.Name(), 10)
  1342  	checkSize(t, f, 10)
  1343  	Truncate(f.Name(), 1024)
  1344  	checkSize(t, f, 1024)
  1345  	Truncate(f.Name(), 0)
  1346  	checkSize(t, f, 0)
  1347  	_, err := f.Write([]byte("surprise!"))
  1348  	if err == nil {
  1349  		checkSize(t, f, 13+9) // wrote at offset past where hello, world was.
  1350  	}
  1351  }
  1352  
  1353  func TestTruncateNonexistentFile(t *testing.T) {
  1354  	t.Parallel()
  1355  
  1356  	assertPathError := func(t testing.TB, path string, err error) {
  1357  		t.Helper()
  1358  		if pe, ok := err.(*PathError); !ok || !IsNotExist(err) || pe.Path != path {
  1359  			t.Errorf("got error: %v\nwant an ErrNotExist PathError with path %q", err, path)
  1360  		}
  1361  	}
  1362  
  1363  	path := filepath.Join(t.TempDir(), "nonexistent")
  1364  
  1365  	err := Truncate(path, 1)
  1366  	assertPathError(t, path, err)
  1367  
  1368  	// Truncate shouldn't create any new file.
  1369  	_, err = Stat(path)
  1370  	assertPathError(t, path, err)
  1371  }
  1372  
  1373  var hasNoatime = sync.OnceValue(func() bool {
  1374  	// A sloppy way to check if noatime flag is set (as all filesystems are
  1375  	// checked, not just the one we're interested in). A correct way
  1376  	// would be to use statvfs syscall and check if flags has ST_NOATIME,
  1377  	// but the syscall is OS-specific and is not even wired into Go stdlib.
  1378  	//
  1379  	// Only used on NetBSD (which ignores explicit atime updates with noatime).
  1380  	if runtime.GOOS != "netbsd" {
  1381  		return false
  1382  	}
  1383  	mounts, _ := ReadFile("/proc/mounts")
  1384  	return bytes.Contains(mounts, []byte("noatime"))
  1385  })
  1386  
  1387  func TestChtimes(t *testing.T) {
  1388  	t.Parallel()
  1389  
  1390  	f := newFile(t)
  1391  	// This should be an empty file (see #68687, #68663).
  1392  	f.Close()
  1393  
  1394  	testChtimes(t, f.Name())
  1395  }
  1396  
  1397  func TestChtimesOmit(t *testing.T) {
  1398  	t.Parallel()
  1399  
  1400  	testChtimesOmit(t, true, false)
  1401  	testChtimesOmit(t, false, true)
  1402  	testChtimesOmit(t, true, true)
  1403  	testChtimesOmit(t, false, false) // Same as TestChtimes.
  1404  }
  1405  
  1406  func testChtimesOmit(t *testing.T, omitAt, omitMt bool) {
  1407  	t.Logf("omit atime: %v, mtime: %v", omitAt, omitMt)
  1408  	file := newFile(t)
  1409  	// This should be an empty file (see #68687, #68663).
  1410  	name := file.Name()
  1411  	err := file.Close()
  1412  	if err != nil {
  1413  		t.Error(err)
  1414  	}
  1415  	fs, err := Stat(name)
  1416  	if err != nil {
  1417  		t.Fatal(err)
  1418  	}
  1419  
  1420  	wantAtime := Atime(fs)
  1421  	wantMtime := fs.ModTime()
  1422  	switch runtime.GOOS {
  1423  	case "js":
  1424  		wantAtime = wantAtime.Truncate(time.Second)
  1425  		wantMtime = wantMtime.Truncate(time.Second)
  1426  	}
  1427  
  1428  	var setAtime, setMtime time.Time // Zero value means omit.
  1429  	if !omitAt {
  1430  		wantAtime = wantAtime.Add(-1 * time.Second)
  1431  		setAtime = wantAtime
  1432  	}
  1433  	if !omitMt {
  1434  		wantMtime = wantMtime.Add(-1 * time.Second)
  1435  		setMtime = wantMtime
  1436  	}
  1437  
  1438  	// Change the times accordingly.
  1439  	if err := Chtimes(name, setAtime, setMtime); err != nil {
  1440  		t.Error(err)
  1441  	}
  1442  
  1443  	// Verify the expectations.
  1444  	fs, err = Stat(name)
  1445  	if err != nil {
  1446  		t.Error(err)
  1447  	}
  1448  	gotAtime := Atime(fs)
  1449  	gotMtime := fs.ModTime()
  1450  
  1451  	// TODO: remove the dragonfly omitAt && omitMt exceptions below once the
  1452  	// fix (https://github.com/DragonFlyBSD/DragonFlyBSD/commit/c7c71870ed0)
  1453  	// is available generally and on CI runners.
  1454  	if !gotAtime.Equal(wantAtime) {
  1455  		errormsg := fmt.Sprintf("atime mismatch, got: %q, want: %q", gotAtime, wantAtime)
  1456  		switch runtime.GOOS {
  1457  		case "plan9":
  1458  			// Mtime is the time of the last change of content.
  1459  			// Similarly, atime is set whenever the contents are
  1460  			// accessed; also, it is set whenever mtime is set.
  1461  		case "dragonfly":
  1462  			if omitAt && omitMt {
  1463  				t.Log(errormsg)
  1464  				t.Log("Known DragonFly BSD issue (won't work when both times are omitted); ignoring.")
  1465  			} else {
  1466  				// Assume hammer2 fs; https://www.dragonflybsd.org/hammer/ says:
  1467  				// > Because HAMMER2 is a block copy-on-write filesystem,
  1468  				// > the "atime" field is not supported and will typically
  1469  				// > just reflect local system in-memory caches or mtime.
  1470  				//
  1471  				// TODO: if only can CI define TMPDIR to point to a tmpfs
  1472  				// (e.g. /var/run/shm), this exception can be removed.
  1473  				t.Log(errormsg)
  1474  				t.Log("Known DragonFly BSD issue (atime not supported on hammer2); ignoring.")
  1475  			}
  1476  		case "netbsd":
  1477  			if !omitAt && hasNoatime() {
  1478  				t.Log(errormsg)
  1479  				t.Log("Known NetBSD issue (atime not changed on fs mounted with noatime); ignoring.")
  1480  			} else {
  1481  				t.Error(errormsg)
  1482  			}
  1483  		default:
  1484  			t.Error(errormsg)
  1485  		}
  1486  	}
  1487  	if !gotMtime.Equal(wantMtime) {
  1488  		errormsg := fmt.Sprintf("mtime mismatch, got: %q, want: %q", gotMtime, wantMtime)
  1489  		switch runtime.GOOS {
  1490  		case "dragonfly":
  1491  			if omitAt && omitMt {
  1492  				t.Log(errormsg)
  1493  				t.Log("Known DragonFly BSD issue (won't work when both times are omitted); ignoring.")
  1494  			} else {
  1495  				t.Error(errormsg)
  1496  			}
  1497  		default:
  1498  			t.Error(errormsg)
  1499  		}
  1500  	}
  1501  }
  1502  
  1503  func TestChtimesDir(t *testing.T) {
  1504  	t.Parallel()
  1505  
  1506  	testChtimes(t, t.TempDir())
  1507  }
  1508  
  1509  func testChtimes(t *testing.T, name string) {
  1510  	st, err := Stat(name)
  1511  	if err != nil {
  1512  		t.Fatalf("Stat %s: %s", name, err)
  1513  	}
  1514  	preStat := st
  1515  
  1516  	// Move access and modification time back a second
  1517  	at := Atime(preStat)
  1518  	mt := preStat.ModTime()
  1519  	err = Chtimes(name, at.Add(-time.Second), mt.Add(-time.Second))
  1520  	if err != nil {
  1521  		t.Fatalf("Chtimes %s: %s", name, err)
  1522  	}
  1523  
  1524  	st, err = Stat(name)
  1525  	if err != nil {
  1526  		t.Fatalf("second Stat %s: %s", name, err)
  1527  	}
  1528  	postStat := st
  1529  
  1530  	pat := Atime(postStat)
  1531  	pmt := postStat.ModTime()
  1532  	if !pat.Before(at) {
  1533  		errormsg := fmt.Sprintf("AccessTime didn't go backwards; was=%v, after=%v", at, pat)
  1534  		switch runtime.GOOS {
  1535  		case "plan9":
  1536  			// Mtime is the time of the last change of
  1537  			// content.  Similarly, atime is set whenever
  1538  			// the contents are accessed; also, it is set
  1539  			// whenever mtime is set.
  1540  		case "netbsd":
  1541  			if hasNoatime() {
  1542  				t.Log(errormsg)
  1543  				t.Log("Known NetBSD issue (atime not changed on fs mounted with noatime); ignoring.")
  1544  			} else {
  1545  				t.Error(errormsg)
  1546  			}
  1547  		default:
  1548  			t.Error(errormsg)
  1549  		}
  1550  	}
  1551  
  1552  	if !pmt.Before(mt) {
  1553  		t.Errorf("ModTime didn't go backwards; was=%v, after=%v", mt, pmt)
  1554  	}
  1555  }
  1556  
  1557  func TestChtimesToUnixZero(t *testing.T) {
  1558  	file := newFile(t)
  1559  	fn := file.Name()
  1560  	if _, err := file.Write([]byte("hi")); err != nil {
  1561  		t.Fatal(err)
  1562  	}
  1563  	if err := file.Close(); err != nil {
  1564  		t.Fatal(err)
  1565  	}
  1566  
  1567  	unixZero := time.Unix(0, 0)
  1568  	if err := Chtimes(fn, unixZero, unixZero); err != nil {
  1569  		t.Fatalf("Chtimes failed: %v", err)
  1570  	}
  1571  
  1572  	st, err := Stat(fn)
  1573  	if err != nil {
  1574  		t.Fatal(err)
  1575  	}
  1576  
  1577  	if mt := st.ModTime(); mt != unixZero {
  1578  		t.Errorf("mtime is %v, want %v", mt, unixZero)
  1579  	}
  1580  }
  1581  
  1582  func TestFileChdir(t *testing.T) {
  1583  	wd, err := Getwd()
  1584  	if err != nil {
  1585  		t.Fatalf("Getwd: %s", err)
  1586  	}
  1587  	t.Chdir(".") // Ensure wd is restored after the test.
  1588  
  1589  	fd, err := Open(".")
  1590  	if err != nil {
  1591  		t.Fatalf("Open .: %s", err)
  1592  	}
  1593  	defer fd.Close()
  1594  
  1595  	if err := Chdir("/"); err != nil {
  1596  		t.Fatalf("Chdir /: %s", err)
  1597  	}
  1598  
  1599  	if err := fd.Chdir(); err != nil {
  1600  		t.Fatalf("fd.Chdir: %s", err)
  1601  	}
  1602  
  1603  	wdNew, err := Getwd()
  1604  	if err != nil {
  1605  		t.Fatalf("Getwd: %s", err)
  1606  	}
  1607  
  1608  	wdInfo, err := fd.Stat()
  1609  	if err != nil {
  1610  		t.Fatal(err)
  1611  	}
  1612  	newInfo, err := Stat(wdNew)
  1613  	if err != nil {
  1614  		t.Fatal(err)
  1615  	}
  1616  	if !SameFile(wdInfo, newInfo) {
  1617  		t.Fatalf("fd.Chdir failed: got %s, want %s", wdNew, wd)
  1618  	}
  1619  }
  1620  
  1621  func TestFileChdirFromTempDir(t *testing.T) {
  1622  	// This is most useful when the original working directory and TempDir are on
  1623  	// different volumes, such as a network-mounted working directory and a local
  1624  	// system temporary directory.
  1625  	// See https://go.dev/issue/80130.
  1626  	wd, err := Getwd()
  1627  	if err != nil {
  1628  		t.Fatalf("Getwd: %v", err)
  1629  	}
  1630  	defer func() {
  1631  		if err := Chdir(wd); err != nil {
  1632  			t.Errorf("restore wd: %v", err)
  1633  		}
  1634  	}()
  1635  
  1636  	fd, err := Open(".")
  1637  	if err != nil {
  1638  		t.Fatalf("Open .: %v", err)
  1639  	}
  1640  	defer fd.Close()
  1641  
  1642  	tmp := t.TempDir()
  1643  	if err := Chdir(tmp); err != nil {
  1644  		t.Fatalf("Chdir %s: %v", tmp, err)
  1645  	}
  1646  
  1647  	if err := fd.Chdir(); err != nil {
  1648  		t.Fatalf("fd.Chdir: %v", err)
  1649  	}
  1650  }
  1651  
  1652  // TestFileChdirTestlog verifies that (*File).Chdir notifies the testlog,
  1653  // just like os.Chdir does. cmd/go's test cache relies on every working
  1654  // directory change being recorded so that relative paths logged by later
  1655  // Open/Stat calls can be resolved against the correct directory.
  1656  func TestFileChdirTestlog(t *testing.T) {
  1657  	if Getenv("GO_TEST_FILE_CHDIR_TESTLOG") == "1" {
  1658  		fd, err := Open(".")
  1659  		if err != nil {
  1660  			t.Fatalf("Open .: %s", err)
  1661  		}
  1662  		defer fd.Close()
  1663  		if err := Chdir(TempDir()); err != nil {
  1664  			t.Fatalf("Chdir: %s", err)
  1665  		}
  1666  		if err := fd.Chdir(); err != nil {
  1667  			t.Fatalf("fd.Chdir: %s", err)
  1668  		}
  1669  		return
  1670  	}
  1671  
  1672  	testenv.MustHaveExec(t)
  1673  	exe := testenv.Executable(t)
  1674  	logfile := filepath.Join(t.TempDir(), "testlog.txt")
  1675  	cmd := testenv.Command(t, exe,
  1676  		"-test.run=^TestFileChdirTestlog$",
  1677  		"-test.testlogfile="+logfile)
  1678  	cmd = testenv.CleanCmdEnv(cmd)
  1679  	cmd.Env = append(cmd.Env, "GO_TEST_FILE_CHDIR_TESTLOG=1")
  1680  	if out, err := cmd.CombinedOutput(); err != nil {
  1681  		t.Fatalf("helper failed: %v\n%s", err, out)
  1682  	}
  1683  
  1684  	data, err := ReadFile(logfile)
  1685  	if err != nil {
  1686  		t.Fatal(err)
  1687  	}
  1688  	chdirs := 0
  1689  	for _, line := range strings.Split(string(data), "\n") {
  1690  		if strings.HasPrefix(line, "chdir ") {
  1691  			chdirs++
  1692  		}
  1693  	}
  1694  	if chdirs < 2 {
  1695  		t.Fatalf("got %d chdir testlog entries, want at least 2; testlog:\n%s", chdirs, data)
  1696  	}
  1697  }
  1698  
  1699  func TestChdirAndGetwd(t *testing.T) {
  1700  	t.Chdir(t.TempDir()) // Ensure wd is restored after the test.
  1701  
  1702  	// These are chosen carefully not to be symlinks on a Mac
  1703  	// (unlike, say, /var, /etc), except /tmp, which we handle below.
  1704  	dirs := []string{"/", "/usr/bin", "/tmp"}
  1705  	// /usr/bin does not usually exist on Plan 9 or Android.
  1706  	switch runtime.GOOS {
  1707  	case "android":
  1708  		dirs = []string{"/system/bin"}
  1709  	case "plan9":
  1710  		dirs = []string{"/", "/usr"}
  1711  	case "ios", "windows", "wasip1":
  1712  		dirs = nil
  1713  		for _, dir := range []string{t.TempDir(), t.TempDir()} {
  1714  			// Expand symlinks so path equality tests work.
  1715  			dir, err := filepath.EvalSymlinks(dir)
  1716  			if err != nil {
  1717  				t.Fatalf("EvalSymlinks: %v", err)
  1718  			}
  1719  			dirs = append(dirs, dir)
  1720  		}
  1721  	}
  1722  	for mode := 0; mode < 2; mode++ {
  1723  		for _, d := range dirs {
  1724  			var err error
  1725  			if mode == 0 {
  1726  				err = Chdir(d)
  1727  			} else {
  1728  				fd1, err1 := Open(d)
  1729  				if err1 != nil {
  1730  					t.Errorf("Open %s: %s", d, err1)
  1731  					continue
  1732  				}
  1733  				err = fd1.Chdir()
  1734  				fd1.Close()
  1735  			}
  1736  			if d == "/tmp" {
  1737  				Setenv("PWD", "/tmp")
  1738  			}
  1739  			pwd, err1 := Getwd()
  1740  			if err != nil {
  1741  				t.Fatalf("Chdir %s: %s", d, err)
  1742  			}
  1743  			if err1 != nil {
  1744  				t.Fatalf("Getwd in %s: %s", d, err1)
  1745  			}
  1746  			if !equal(pwd, d) {
  1747  				t.Fatalf("Getwd returned %q want %q", pwd, d)
  1748  			}
  1749  		}
  1750  	}
  1751  }
  1752  
  1753  // Test that Chdir+Getwd is program-wide.
  1754  func TestProgWideChdir(t *testing.T) {
  1755  	const N = 10
  1756  	var wg sync.WaitGroup
  1757  	hold := make(chan struct{})
  1758  	done := make(chan struct{})
  1759  
  1760  	d := t.TempDir()
  1761  	t.Chdir(d)
  1762  
  1763  	// Note the deferred Wait must be called after the deferred close(done),
  1764  	// to ensure the N goroutines have been released even if the main goroutine
  1765  	// calls Fatalf. It must be called before the Chdir back to the original
  1766  	// directory, and before the deferred deletion implied by TempDir,
  1767  	// so as not to interfere while the N goroutines are still running.
  1768  	defer wg.Wait()
  1769  	defer close(done)
  1770  
  1771  	for i := 0; i < N; i++ {
  1772  		wg.Add(1)
  1773  		go func(i int) {
  1774  			defer wg.Done()
  1775  			// Lock half the goroutines in their own operating system
  1776  			// thread to exercise more scheduler possibilities.
  1777  			if i%2 == 1 {
  1778  				// On Plan 9, after calling LockOSThread, the goroutines
  1779  				// run on different processes which don't share the working
  1780  				// directory. This used to be an issue because Go expects
  1781  				// the working directory to be program-wide.
  1782  				// See issue 9428.
  1783  				runtime.LockOSThread()
  1784  			}
  1785  			select {
  1786  			case <-done:
  1787  				return
  1788  			case <-hold:
  1789  			}
  1790  			// Getwd might be wrong
  1791  			f0, err := Stat(".")
  1792  			if err != nil {
  1793  				t.Error(err)
  1794  				return
  1795  			}
  1796  			pwd, err := Getwd()
  1797  			if err != nil {
  1798  				t.Errorf("Getwd: %v", err)
  1799  				return
  1800  			}
  1801  			if pwd != d {
  1802  				t.Errorf("Getwd() = %q, want %q", pwd, d)
  1803  				return
  1804  			}
  1805  			f1, err := Stat(pwd)
  1806  			if err != nil {
  1807  				t.Error(err)
  1808  				return
  1809  			}
  1810  			if !SameFile(f0, f1) {
  1811  				t.Errorf(`Samefile(Stat("."), Getwd()) reports false (%s != %s)`, f0.Name(), f1.Name())
  1812  				return
  1813  			}
  1814  		}(i)
  1815  	}
  1816  	var err error
  1817  	if err = Chdir(d); err != nil {
  1818  		t.Fatalf("Chdir: %v", err)
  1819  	}
  1820  	// OS X sets TMPDIR to a symbolic link.
  1821  	// So we resolve our working directory again before the test.
  1822  	d, err = Getwd()
  1823  	if err != nil {
  1824  		t.Fatalf("Getwd: %v", err)
  1825  	}
  1826  	close(hold)
  1827  	wg.Wait()
  1828  }
  1829  
  1830  func TestSeek(t *testing.T) {
  1831  	t.Parallel()
  1832  
  1833  	f := newFile(t)
  1834  
  1835  	const data = "hello, world\n"
  1836  	io.WriteString(f, data)
  1837  
  1838  	type test struct {
  1839  		in     int64
  1840  		whence int
  1841  		out    int64
  1842  	}
  1843  	var tests = []test{
  1844  		{0, io.SeekCurrent, int64(len(data))},
  1845  		{0, io.SeekStart, 0},
  1846  		{5, io.SeekStart, 5},
  1847  		{0, io.SeekEnd, int64(len(data))},
  1848  		{0, io.SeekStart, 0},
  1849  		{-1, io.SeekEnd, int64(len(data)) - 1},
  1850  		{1 << 33, io.SeekStart, 1 << 33},
  1851  		{1 << 33, io.SeekEnd, 1<<33 + int64(len(data))},
  1852  
  1853  		// Issue 21681, Windows 4G-1, etc:
  1854  		{1<<32 - 1, io.SeekStart, 1<<32 - 1},
  1855  		{0, io.SeekCurrent, 1<<32 - 1},
  1856  		{2<<32 - 1, io.SeekStart, 2<<32 - 1},
  1857  		{0, io.SeekCurrent, 2<<32 - 1},
  1858  	}
  1859  	for i, tt := range tests {
  1860  		off, err := f.Seek(tt.in, tt.whence)
  1861  		if off != tt.out || err != nil {
  1862  			t.Errorf("#%d: Seek(%v, %v) = %v, %v want %v, nil", i, tt.in, tt.whence, off, err, tt.out)
  1863  		}
  1864  	}
  1865  }
  1866  
  1867  func TestSeekError(t *testing.T) {
  1868  	switch runtime.GOOS {
  1869  	case "js", "plan9", "wasip1":
  1870  		t.Skipf("skipping test on %v", runtime.GOOS)
  1871  	}
  1872  	t.Parallel()
  1873  
  1874  	r, w, err := Pipe()
  1875  	if err != nil {
  1876  		t.Fatal(err)
  1877  	}
  1878  	_, err = r.Seek(0, 0)
  1879  	if err == nil {
  1880  		t.Fatal("Seek on pipe should fail")
  1881  	}
  1882  	if perr, ok := err.(*PathError); !ok || perr.Err != syscall.ESPIPE {
  1883  		t.Errorf("Seek returned error %v, want &PathError{Err: syscall.ESPIPE}", err)
  1884  	}
  1885  	_, err = w.Seek(0, 0)
  1886  	if err == nil {
  1887  		t.Fatal("Seek on pipe should fail")
  1888  	}
  1889  	if perr, ok := err.(*PathError); !ok || perr.Err != syscall.ESPIPE {
  1890  		t.Errorf("Seek returned error %v, want &PathError{Err: syscall.ESPIPE}", err)
  1891  	}
  1892  }
  1893  
  1894  func TestOpenError(t *testing.T) {
  1895  	t.Parallel()
  1896  	dir := makefs(t, []string{
  1897  		"is-a-file",
  1898  		"is-a-dir/",
  1899  	})
  1900  	t.Run("NoRoot", func(t *testing.T) { testOpenError(t, dir, false) })
  1901  	t.Run("InRoot", func(t *testing.T) { testOpenError(t, dir, true) })
  1902  }
  1903  func testOpenError(t *testing.T, dir string, rooted bool) {
  1904  	t.Parallel()
  1905  	var r *Root
  1906  	if rooted {
  1907  		var err error
  1908  		r, err = OpenRoot(dir)
  1909  		if err != nil {
  1910  			t.Fatal(err)
  1911  		}
  1912  		defer r.Close()
  1913  	}
  1914  	for _, tt := range []struct {
  1915  		path  string
  1916  		mode  int
  1917  		error error
  1918  	}{{
  1919  		"no-such-file",
  1920  		O_RDONLY,
  1921  		syscall.ENOENT,
  1922  	}, {
  1923  		"is-a-dir",
  1924  		O_WRONLY,
  1925  		syscall.EISDIR,
  1926  	}, {
  1927  		"is-a-file/no-such-file",
  1928  		O_WRONLY,
  1929  		syscall.ENOTDIR,
  1930  	}} {
  1931  		var f *File
  1932  		var err error
  1933  		var name string
  1934  		if rooted {
  1935  			name = fmt.Sprintf("Root(%q).OpenFile(%q, %d)", dir, tt.path, tt.mode)
  1936  			f, err = r.OpenFile(tt.path, tt.mode, 0)
  1937  		} else {
  1938  			path := filepath.Join(dir, tt.path)
  1939  			name = fmt.Sprintf("OpenFile(%q, %d)", path, tt.mode)
  1940  			f, err = OpenFile(path, tt.mode, 0)
  1941  		}
  1942  		if err == nil {
  1943  			t.Errorf("%v succeeded", name)
  1944  			f.Close()
  1945  			continue
  1946  		}
  1947  		perr, ok := err.(*PathError)
  1948  		if !ok {
  1949  			t.Errorf("%v returns error of %T type; want *PathError", name, err)
  1950  		}
  1951  		if perr.Err != tt.error {
  1952  			if runtime.GOOS == "plan9" {
  1953  				syscallErrStr := perr.Err.Error()
  1954  				expectedErrStr := strings.Replace(tt.error.Error(), "file ", "", 1)
  1955  				if !strings.HasSuffix(syscallErrStr, expectedErrStr) {
  1956  					// Some Plan 9 file servers incorrectly return
  1957  					// EPERM or EACCES rather than EISDIR when a directory is
  1958  					// opened for write.
  1959  					if tt.error == syscall.EISDIR &&
  1960  						(strings.HasSuffix(syscallErrStr, syscall.EPERM.Error()) ||
  1961  							strings.HasSuffix(syscallErrStr, syscall.EACCES.Error())) {
  1962  						continue
  1963  					}
  1964  					t.Errorf("%v = _, %q; want suffix %q", name, syscallErrStr, expectedErrStr)
  1965  				}
  1966  				continue
  1967  			}
  1968  			if runtime.GOOS == "dragonfly" {
  1969  				// DragonFly incorrectly returns EACCES rather
  1970  				// EISDIR when a directory is opened for write.
  1971  				if tt.error == syscall.EISDIR && perr.Err == syscall.EACCES {
  1972  					continue
  1973  				}
  1974  			}
  1975  			t.Errorf("%v = _, %q; want %q", name, perr.Err.Error(), tt.error.Error())
  1976  		}
  1977  	}
  1978  }
  1979  
  1980  func TestOpenNoName(t *testing.T) {
  1981  	f, err := Open("")
  1982  	if err == nil {
  1983  		f.Close()
  1984  		t.Fatal(`Open("") succeeded`)
  1985  	}
  1986  }
  1987  
  1988  func runBinHostname(t *testing.T) string {
  1989  	// Run /bin/hostname and collect output.
  1990  	r, w, err := Pipe()
  1991  	if err != nil {
  1992  		t.Fatal(err)
  1993  	}
  1994  	defer r.Close()
  1995  
  1996  	path, err := exec.LookPath("hostname")
  1997  	if err != nil {
  1998  		if errors.Is(err, exec.ErrNotFound) {
  1999  			t.Skip("skipping test; test requires hostname but it does not exist")
  2000  		}
  2001  		t.Fatal(err)
  2002  	}
  2003  
  2004  	argv := []string{"hostname"}
  2005  	if runtime.GOOS == "aix" {
  2006  		argv = []string{"hostname", "-s"}
  2007  	}
  2008  	p, err := StartProcess(path, argv, &ProcAttr{Files: []*File{nil, w, Stderr}})
  2009  	if err != nil {
  2010  		t.Fatal(err)
  2011  	}
  2012  	w.Close()
  2013  
  2014  	var b strings.Builder
  2015  	io.Copy(&b, r)
  2016  	_, err = p.Wait()
  2017  	if err != nil {
  2018  		t.Fatalf("run hostname Wait: %v", err)
  2019  	}
  2020  	err = p.Kill()
  2021  	if err == nil {
  2022  		t.Errorf("expected an error from Kill running 'hostname'")
  2023  	}
  2024  	output := b.String()
  2025  	if n := len(output); n > 0 && output[n-1] == '\n' {
  2026  		output = output[0 : n-1]
  2027  	}
  2028  	if output == "" {
  2029  		t.Fatalf("/bin/hostname produced no output")
  2030  	}
  2031  
  2032  	return output
  2033  }
  2034  
  2035  func testWindowsHostname(t *testing.T, hostname string) {
  2036  	cmd := testenv.Command(t, "hostname")
  2037  	out, err := cmd.Output()
  2038  	if err != nil {
  2039  		t.Fatalf("Failed to execute hostname command: %v %s", err, out)
  2040  	}
  2041  	want := strings.Trim(string(out), "\r\n")
  2042  	if hostname != want {
  2043  		t.Fatalf("Hostname() = %q != system hostname of %q", hostname, want)
  2044  	}
  2045  }
  2046  
  2047  func TestHostname(t *testing.T) {
  2048  	t.Parallel()
  2049  
  2050  	hostname, err := Hostname()
  2051  	if err != nil {
  2052  		t.Fatal(err)
  2053  	}
  2054  	if hostname == "" {
  2055  		t.Fatal("Hostname returned empty string and no error")
  2056  	}
  2057  	if strings.Contains(hostname, "\x00") {
  2058  		t.Fatalf("unexpected zero byte in hostname: %q", hostname)
  2059  	}
  2060  
  2061  	// There is no other way to fetch hostname on windows, but via winapi.
  2062  	// On Plan 9 it can be taken from #c/sysname as Hostname() does.
  2063  	switch runtime.GOOS {
  2064  	case "android", "plan9":
  2065  		// No /bin/hostname to verify against.
  2066  		return
  2067  	case "windows":
  2068  		testWindowsHostname(t, hostname)
  2069  		return
  2070  	}
  2071  
  2072  	testenv.MustHaveExec(t)
  2073  
  2074  	// Check internal Hostname() against the output of /bin/hostname.
  2075  	// Allow that the internal Hostname returns a Fully Qualified Domain Name
  2076  	// and the /bin/hostname only returns the first component
  2077  	want := runBinHostname(t)
  2078  	if hostname != want {
  2079  		host, _, ok := strings.Cut(hostname, ".")
  2080  		if !ok || host != want {
  2081  			t.Errorf("Hostname() = %q, want %q", hostname, want)
  2082  		}
  2083  	}
  2084  }
  2085  
  2086  func TestReadAt(t *testing.T) {
  2087  	t.Parallel()
  2088  
  2089  	f := newFile(t)
  2090  
  2091  	const data = "hello, world\n"
  2092  	io.WriteString(f, data)
  2093  
  2094  	b := make([]byte, 5)
  2095  	n, err := f.ReadAt(b, 7)
  2096  	if err != nil || n != len(b) {
  2097  		t.Fatalf("ReadAt 7: %d, %v", n, err)
  2098  	}
  2099  	if string(b) != "world" {
  2100  		t.Fatalf("ReadAt 7: have %q want %q", string(b), "world")
  2101  	}
  2102  }
  2103  
  2104  // Verify that ReadAt doesn't affect seek offset.
  2105  // In the Plan 9 kernel, there used to be a bug in the implementation of
  2106  // the pread syscall, where the channel offset was erroneously updated after
  2107  // calling pread on a file.
  2108  func TestReadAtOffset(t *testing.T) {
  2109  	t.Parallel()
  2110  
  2111  	f := newFile(t)
  2112  
  2113  	const data = "hello, world\n"
  2114  	io.WriteString(f, data)
  2115  
  2116  	f.Seek(0, 0)
  2117  	b := make([]byte, 5)
  2118  
  2119  	n, err := f.ReadAt(b, 7)
  2120  	if err != nil || n != len(b) {
  2121  		t.Fatalf("ReadAt 7: %d, %v", n, err)
  2122  	}
  2123  	if string(b) != "world" {
  2124  		t.Fatalf("ReadAt 7: have %q want %q", string(b), "world")
  2125  	}
  2126  
  2127  	n, err = f.Read(b)
  2128  	if err != nil || n != len(b) {
  2129  		t.Fatalf("Read: %d, %v", n, err)
  2130  	}
  2131  	if string(b) != "hello" {
  2132  		t.Fatalf("Read: have %q want %q", string(b), "hello")
  2133  	}
  2134  }
  2135  
  2136  // Verify that ReadAt doesn't allow negative offset.
  2137  func TestReadAtNegativeOffset(t *testing.T) {
  2138  	t.Parallel()
  2139  
  2140  	f := newFile(t)
  2141  
  2142  	const data = "hello, world\n"
  2143  	io.WriteString(f, data)
  2144  
  2145  	f.Seek(0, 0)
  2146  	b := make([]byte, 5)
  2147  
  2148  	n, err := f.ReadAt(b, -10)
  2149  
  2150  	const wantsub = "negative offset"
  2151  	if !strings.Contains(fmt.Sprint(err), wantsub) || n != 0 {
  2152  		t.Errorf("ReadAt(-10) = %v, %v; want 0, ...%q...", n, err, wantsub)
  2153  	}
  2154  }
  2155  
  2156  func TestWriteAt(t *testing.T) {
  2157  	t.Parallel()
  2158  
  2159  	f := newFile(t)
  2160  
  2161  	const data = "hello, world"
  2162  	io.WriteString(f, data)
  2163  
  2164  	n, err := f.WriteAt([]byte("WOR"), 7)
  2165  	if err != nil || n != 3 {
  2166  		t.Fatalf("WriteAt 7: %d, %v", n, err)
  2167  	}
  2168  	n, err = io.WriteString(f, "!") // test that WriteAt doesn't change the file offset
  2169  	if err != nil || n != 1 {
  2170  		t.Fatal(err)
  2171  	}
  2172  
  2173  	got, err := ReadFile(f.Name())
  2174  	if err != nil {
  2175  		t.Fatalf("ReadFile %s: %v", f.Name(), err)
  2176  	}
  2177  	want := "hello, WORld!"
  2178  	if string(got) != want {
  2179  		t.Fatalf("after write: have %q want %q", string(got), want)
  2180  	}
  2181  }
  2182  
  2183  func TestWriteAtConcurrent(t *testing.T) {
  2184  	t.Parallel()
  2185  
  2186  	f := newFile(t)
  2187  	io.WriteString(f, "0000000000")
  2188  
  2189  	var wg sync.WaitGroup
  2190  	for i := range 10 {
  2191  		wg.Add(1)
  2192  		go func() {
  2193  			defer wg.Done()
  2194  			n, err := f.WriteAt([]byte(strconv.Itoa(i)), int64(i))
  2195  			if err != nil || n != 1 {
  2196  				t.Errorf("WriteAt %d: %d, %v", i, n, err)
  2197  			}
  2198  			n, err = io.WriteString(f, "!") // test that WriteAt doesn't change the file offset
  2199  			if err != nil || n != 1 {
  2200  				t.Error(err)
  2201  			}
  2202  		}()
  2203  	}
  2204  	wg.Wait()
  2205  
  2206  	got, err := ReadFile(f.Name())
  2207  	if err != nil {
  2208  		t.Fatalf("ReadFile %s: %v", f.Name(), err)
  2209  	}
  2210  	want := "0123456789!!!!!!!!!!"
  2211  	if string(got) != want {
  2212  		t.Fatalf("after write: have %q want %q", string(got), want)
  2213  	}
  2214  }
  2215  
  2216  // Verify that WriteAt doesn't allow negative offset.
  2217  func TestWriteAtNegativeOffset(t *testing.T) {
  2218  	t.Parallel()
  2219  
  2220  	f := newFile(t)
  2221  
  2222  	n, err := f.WriteAt([]byte("WORLD"), -10)
  2223  
  2224  	const wantsub = "negative offset"
  2225  	if !strings.Contains(fmt.Sprint(err), wantsub) || n != 0 {
  2226  		t.Errorf("WriteAt(-10) = %v, %v; want 0, ...%q...", n, err, wantsub)
  2227  	}
  2228  }
  2229  
  2230  // Verify that WriteAt doesn't work in append mode.
  2231  func TestWriteAtInAppendMode(t *testing.T) {
  2232  	t.Chdir(t.TempDir())
  2233  	f, err := OpenFile("write_at_in_append_mode.txt", O_APPEND|O_CREATE, 0666)
  2234  	if err != nil {
  2235  		t.Fatalf("OpenFile: %v", err)
  2236  	}
  2237  	defer f.Close()
  2238  
  2239  	_, err = f.WriteAt([]byte(""), 1)
  2240  	if err != ErrWriteAtInAppendMode {
  2241  		t.Fatalf("f.WriteAt returned %v, expected %v", err, ErrWriteAtInAppendMode)
  2242  	}
  2243  }
  2244  
  2245  func writeFile(t *testing.T, r *Root, fname string, flag int, text string) string {
  2246  	t.Helper()
  2247  	var f *File
  2248  	var err error
  2249  	if r == nil {
  2250  		f, err = OpenFile(fname, flag, 0666)
  2251  	} else {
  2252  		f, err = r.OpenFile(fname, flag, 0666)
  2253  	}
  2254  	if err != nil {
  2255  		t.Fatalf("Open: %v", err)
  2256  	}
  2257  	n, err := io.WriteString(f, text)
  2258  	if err != nil {
  2259  		t.Fatalf("WriteString: %d, %v", n, err)
  2260  	}
  2261  	f.Close()
  2262  	data, err := ReadFile(fname)
  2263  	if err != nil {
  2264  		t.Fatalf("ReadFile: %v", err)
  2265  	}
  2266  	return string(data)
  2267  }
  2268  
  2269  func TestAppend(t *testing.T) {
  2270  	testMaybeRooted(t, func(t *testing.T, r *Root) {
  2271  		const f = "append.txt"
  2272  		s := writeFile(t, r, f, O_CREATE|O_TRUNC|O_RDWR, "new")
  2273  		if s != "new" {
  2274  			t.Fatalf("writeFile: have %q want %q", s, "new")
  2275  		}
  2276  		s = writeFile(t, r, f, O_APPEND|O_RDWR, "|append")
  2277  		if s != "new|append" {
  2278  			t.Fatalf("writeFile: have %q want %q", s, "new|append")
  2279  		}
  2280  		s = writeFile(t, r, f, O_CREATE|O_APPEND|O_RDWR, "|append")
  2281  		if s != "new|append|append" {
  2282  			t.Fatalf("writeFile: have %q want %q", s, "new|append|append")
  2283  		}
  2284  		err := Remove(f)
  2285  		if err != nil {
  2286  			t.Fatalf("Remove: %v", err)
  2287  		}
  2288  		s = writeFile(t, r, f, O_CREATE|O_APPEND|O_RDWR, "new&append")
  2289  		if s != "new&append" {
  2290  			t.Fatalf("writeFile: after append have %q want %q", s, "new&append")
  2291  		}
  2292  		s = writeFile(t, r, f, O_CREATE|O_RDWR, "old")
  2293  		if s != "old&append" {
  2294  			t.Fatalf("writeFile: after create have %q want %q", s, "old&append")
  2295  		}
  2296  		s = writeFile(t, r, f, O_CREATE|O_TRUNC|O_RDWR, "new")
  2297  		if s != "new" {
  2298  			t.Fatalf("writeFile: after truncate have %q want %q", s, "new")
  2299  		}
  2300  	})
  2301  }
  2302  
  2303  // TestFilePermissions tests setting Unix permission bits on file creation.
  2304  func TestFilePermissions(t *testing.T) {
  2305  	if Getuid() == 0 {
  2306  		t.Skip("skipping test when running as root")
  2307  	}
  2308  	for _, test := range []struct {
  2309  		name string
  2310  		mode FileMode
  2311  	}{
  2312  		{"r", 0o444},
  2313  		{"w", 0o222},
  2314  		{"rw", 0o666},
  2315  	} {
  2316  		t.Run(test.name, func(t *testing.T) {
  2317  			switch runtime.GOOS {
  2318  			case "windows":
  2319  				if test.mode&0444 == 0 {
  2320  					t.Skip("write-only files not supported on " + runtime.GOOS)
  2321  				}
  2322  			case "wasip1":
  2323  				t.Skip("file permissions not supported on " + runtime.GOOS)
  2324  			}
  2325  			testMaybeRooted(t, func(t *testing.T, r *Root) {
  2326  				const filename = "f"
  2327  				var f *File
  2328  				var err error
  2329  				if r == nil {
  2330  					f, err = OpenFile(filename, O_RDWR|O_CREATE|O_EXCL, test.mode)
  2331  				} else {
  2332  					f, err = r.OpenFile(filename, O_RDWR|O_CREATE|O_EXCL, test.mode)
  2333  				}
  2334  				if err != nil {
  2335  					t.Fatal(err)
  2336  				}
  2337  				f.Close()
  2338  				b, err := ReadFile(filename)
  2339  				if test.mode&0o444 != 0 {
  2340  					if err != nil {
  2341  						t.Errorf("ReadFile = %v; want success", err)
  2342  					}
  2343  				} else {
  2344  					if err == nil {
  2345  						t.Errorf("ReadFile = %q, <nil>; want failure", string(b))
  2346  					}
  2347  				}
  2348  				_, err = Stat(filename)
  2349  				if err != nil {
  2350  					t.Errorf("Stat = %v; want success", err)
  2351  				}
  2352  				err = WriteFile(filename, nil, 0666)
  2353  				if test.mode&0o222 != 0 {
  2354  					if err != nil {
  2355  						t.Errorf("WriteFile = %v; want success", err)
  2356  						b, err := ReadFile(filename)
  2357  						t.Errorf("ReadFile: %v", err)
  2358  						t.Errorf("file contents: %q", b)
  2359  					}
  2360  				} else {
  2361  					if err == nil {
  2362  						t.Errorf("WriteFile(%q) = <nil>; want failure", filename)
  2363  						st, err := Stat(filename)
  2364  						if err == nil {
  2365  							t.Errorf("mode: %s", st.Mode())
  2366  						}
  2367  						b, err := ReadFile(filename)
  2368  						t.Errorf("ReadFile: %v", err)
  2369  						t.Errorf("file contents: %q", b)
  2370  					}
  2371  				}
  2372  			})
  2373  		})
  2374  	}
  2375  
  2376  }
  2377  
  2378  func TestOpenFileCreateExclDanglingSymlink(t *testing.T) {
  2379  	testenv.MustHaveSymlink(t)
  2380  	testMaybeRooted(t, func(t *testing.T, r *Root) {
  2381  		const link = "link"
  2382  		if err := Symlink("does_not_exist", link); err != nil {
  2383  			t.Fatal(err)
  2384  		}
  2385  		var f *File
  2386  		var err error
  2387  		if r == nil {
  2388  			f, err = OpenFile(link, O_WRONLY|O_CREATE|O_EXCL, 0o444)
  2389  		} else {
  2390  			f, err = r.OpenFile(link, O_WRONLY|O_CREATE|O_EXCL, 0o444)
  2391  		}
  2392  		if err == nil {
  2393  			f.Close()
  2394  		}
  2395  		if !errors.Is(err, ErrExist) {
  2396  			t.Errorf("OpenFile of a dangling symlink with O_CREATE|O_EXCL = %v, want ErrExist", err)
  2397  		}
  2398  		if _, err := Stat(link); err == nil {
  2399  			t.Errorf("OpenFile of a dangling symlink with O_CREATE|O_EXCL created a file")
  2400  		}
  2401  	})
  2402  }
  2403  
  2404  // TestFileRDWRFlags tests the O_RDONLY, O_WRONLY, and O_RDWR flags.
  2405  func TestFileRDWRFlags(t *testing.T) {
  2406  	for _, test := range []struct {
  2407  		name string
  2408  		flag int
  2409  	}{
  2410  		{"O_RDONLY", O_RDONLY},
  2411  		{"O_WRONLY", O_WRONLY},
  2412  		{"O_RDWR", O_RDWR},
  2413  	} {
  2414  		t.Run(test.name, func(t *testing.T) {
  2415  			testMaybeRooted(t, func(t *testing.T, r *Root) {
  2416  				const filename = "f"
  2417  				content := []byte("content")
  2418  				if err := WriteFile(filename, content, 0666); err != nil {
  2419  					t.Fatal(err)
  2420  				}
  2421  				var f *File
  2422  				var err error
  2423  				if r == nil {
  2424  					f, err = OpenFile(filename, test.flag, 0)
  2425  				} else {
  2426  					f, err = r.OpenFile(filename, test.flag, 0)
  2427  				}
  2428  				if err != nil {
  2429  					t.Fatal(err)
  2430  				}
  2431  				defer f.Close()
  2432  				got, err := io.ReadAll(f)
  2433  				if test.flag == O_WRONLY {
  2434  					if err == nil {
  2435  						t.Errorf("read file: %q, %v; want error", got, err)
  2436  					}
  2437  				} else {
  2438  					if err != nil || !bytes.Equal(got, content) {
  2439  						t.Errorf("read file: %q, %v; want %q, <nil>", got, err, content)
  2440  					}
  2441  				}
  2442  				if _, err := f.Seek(0, 0); err != nil {
  2443  					t.Fatalf("f.Seek: %v", err)
  2444  				}
  2445  				newcontent := []byte("CONTENT")
  2446  				_, err = f.Write(newcontent)
  2447  				if test.flag == O_RDONLY {
  2448  					if err == nil {
  2449  						t.Errorf("write file: succeeded, want error")
  2450  					}
  2451  				} else {
  2452  					if err != nil {
  2453  						t.Errorf("write file: %v, want success", err)
  2454  					}
  2455  				}
  2456  				f.Close()
  2457  				got, err = ReadFile(filename)
  2458  				if err != nil {
  2459  					t.Fatal(err)
  2460  				}
  2461  				want := content
  2462  				if test.flag != O_RDONLY {
  2463  					want = newcontent
  2464  				}
  2465  				if !bytes.Equal(got, want) {
  2466  					t.Fatalf("after write, file contains %q, want %q", got, want)
  2467  				}
  2468  			})
  2469  		})
  2470  	}
  2471  }
  2472  
  2473  func TestStatDirWithTrailingSlash(t *testing.T) {
  2474  	t.Parallel()
  2475  
  2476  	// Create new temporary directory and arrange to clean it up.
  2477  	path := t.TempDir()
  2478  
  2479  	// Stat of path should succeed.
  2480  	if _, err := Stat(path); err != nil {
  2481  		t.Fatalf("stat %s failed: %s", path, err)
  2482  	}
  2483  
  2484  	// Stat of path+"/" should succeed too.
  2485  	path += "/"
  2486  	if _, err := Stat(path); err != nil {
  2487  		t.Fatalf("stat %s failed: %s", path, err)
  2488  	}
  2489  }
  2490  
  2491  func TestNilProcessStateString(t *testing.T) {
  2492  	var ps *ProcessState
  2493  	s := ps.String()
  2494  	if s != "<nil>" {
  2495  		t.Errorf("(*ProcessState)(nil).String() = %q, want %q", s, "<nil>")
  2496  	}
  2497  }
  2498  
  2499  func TestSameFile(t *testing.T) {
  2500  	t.Chdir(t.TempDir())
  2501  	fa, err := Create("a")
  2502  	if err != nil {
  2503  		t.Fatalf("Create(a): %v", err)
  2504  	}
  2505  	fa.Close()
  2506  	fb, err := Create("b")
  2507  	if err != nil {
  2508  		t.Fatalf("Create(b): %v", err)
  2509  	}
  2510  	fb.Close()
  2511  
  2512  	ia1, err := Stat("a")
  2513  	if err != nil {
  2514  		t.Fatalf("Stat(a): %v", err)
  2515  	}
  2516  	ia2, err := Stat("a")
  2517  	if err != nil {
  2518  		t.Fatalf("Stat(a): %v", err)
  2519  	}
  2520  	if !SameFile(ia1, ia2) {
  2521  		t.Errorf("files should be same")
  2522  	}
  2523  
  2524  	ib, err := Stat("b")
  2525  	if err != nil {
  2526  		t.Fatalf("Stat(b): %v", err)
  2527  	}
  2528  	if SameFile(ia1, ib) {
  2529  		t.Errorf("files should be different")
  2530  	}
  2531  }
  2532  
  2533  func testDevNullFileInfo(t *testing.T, statname, devNullName string, fi FileInfo) {
  2534  	pre := fmt.Sprintf("%s(%q): ", statname, devNullName)
  2535  	if fi.Size() != 0 {
  2536  		t.Errorf(pre+"wrong file size have %d want 0", fi.Size())
  2537  	}
  2538  	if fi.Mode()&ModeDevice == 0 {
  2539  		t.Errorf(pre+"wrong file mode %q: ModeDevice is not set", fi.Mode())
  2540  	}
  2541  	if fi.Mode()&ModeCharDevice == 0 {
  2542  		t.Errorf(pre+"wrong file mode %q: ModeCharDevice is not set", fi.Mode())
  2543  	}
  2544  	if fi.Mode().IsRegular() {
  2545  		t.Errorf(pre+"wrong file mode %q: IsRegular returns true", fi.Mode())
  2546  	}
  2547  }
  2548  
  2549  func testDevNullFile(t *testing.T, devNullName string) {
  2550  	f, err := Open(devNullName)
  2551  	if err != nil {
  2552  		t.Fatalf("Open(%s): %v", devNullName, err)
  2553  	}
  2554  	defer f.Close()
  2555  
  2556  	fi, err := f.Stat()
  2557  	if err != nil {
  2558  		t.Fatalf("Stat(%s): %v", devNullName, err)
  2559  	}
  2560  	testDevNullFileInfo(t, "f.Stat", devNullName, fi)
  2561  
  2562  	fi, err = Stat(devNullName)
  2563  	if err != nil {
  2564  		t.Fatalf("Stat(%s): %v", devNullName, err)
  2565  	}
  2566  	testDevNullFileInfo(t, "Stat", devNullName, fi)
  2567  }
  2568  
  2569  func TestDevNullFile(t *testing.T) {
  2570  	t.Parallel()
  2571  
  2572  	testDevNullFile(t, DevNull)
  2573  	if runtime.GOOS == "windows" {
  2574  		testDevNullFile(t, "./nul")
  2575  		testDevNullFile(t, "//./nul")
  2576  	}
  2577  }
  2578  
  2579  var testLargeWrite = flag.Bool("large_write", false, "run TestLargeWriteToConsole test that floods console with output")
  2580  
  2581  func TestLargeWriteToConsole(t *testing.T) {
  2582  	if !*testLargeWrite {
  2583  		t.Skip("skipping console-flooding test; enable with -large_write")
  2584  	}
  2585  	b := make([]byte, 32000)
  2586  	for i := range b {
  2587  		b[i] = '.'
  2588  	}
  2589  	b[len(b)-1] = '\n'
  2590  	n, err := Stdout.Write(b)
  2591  	if err != nil {
  2592  		t.Fatalf("Write to os.Stdout failed: %v", err)
  2593  	}
  2594  	if n != len(b) {
  2595  		t.Errorf("Write to os.Stdout should return %d; got %d", len(b), n)
  2596  	}
  2597  	n, err = Stderr.Write(b)
  2598  	if err != nil {
  2599  		t.Fatalf("Write to os.Stderr failed: %v", err)
  2600  	}
  2601  	if n != len(b) {
  2602  		t.Errorf("Write to os.Stderr should return %d; got %d", len(b), n)
  2603  	}
  2604  }
  2605  
  2606  func TestStatDirModeExec(t *testing.T) {
  2607  	if runtime.GOOS == "wasip1" {
  2608  		t.Skip("Chmod is not supported on " + runtime.GOOS)
  2609  	}
  2610  	t.Parallel()
  2611  
  2612  	const mode = 0111
  2613  
  2614  	path := t.TempDir()
  2615  	if err := Chmod(path, 0777); err != nil {
  2616  		t.Fatalf("Chmod %q 0777: %v", path, err)
  2617  	}
  2618  
  2619  	dir, err := Stat(path)
  2620  	if err != nil {
  2621  		t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err)
  2622  	}
  2623  	if dir.Mode()&mode != mode {
  2624  		t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode()&mode, mode)
  2625  	}
  2626  }
  2627  
  2628  func TestStatStdin(t *testing.T) {
  2629  	switch runtime.GOOS {
  2630  	case "android", "plan9":
  2631  		t.Skipf("%s doesn't have /bin/sh", runtime.GOOS)
  2632  	}
  2633  
  2634  	if Getenv("GO_WANT_HELPER_PROCESS") == "1" {
  2635  		st, err := Stdin.Stat()
  2636  		if err != nil {
  2637  			t.Fatalf("Stat failed: %v", err)
  2638  		}
  2639  		fmt.Println(st.Mode() & ModeNamedPipe)
  2640  		Exit(0)
  2641  	}
  2642  
  2643  	t.Parallel()
  2644  	exe := testenv.Executable(t)
  2645  
  2646  	fi, err := Stdin.Stat()
  2647  	if err != nil {
  2648  		t.Fatal(err)
  2649  	}
  2650  	switch mode := fi.Mode(); {
  2651  	case mode&ModeCharDevice != 0 && mode&ModeDevice != 0:
  2652  	case mode&ModeNamedPipe != 0:
  2653  	default:
  2654  		t.Fatalf("unexpected Stdin mode (%v), want ModeCharDevice or ModeNamedPipe", mode)
  2655  	}
  2656  
  2657  	cmd := testenv.Command(t, exe, "-test.run=^TestStatStdin$")
  2658  	cmd = testenv.CleanCmdEnv(cmd)
  2659  	cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
  2660  	// This will make standard input a pipe.
  2661  	cmd.Stdin = strings.NewReader("output")
  2662  
  2663  	output, err := cmd.CombinedOutput()
  2664  	if err != nil {
  2665  		t.Fatalf("Failed to spawn child process: %v %q", err, string(output))
  2666  	}
  2667  
  2668  	// result will be like "prw-rw-rw"
  2669  	if len(output) < 1 || output[0] != 'p' {
  2670  		t.Fatalf("Child process reports stdin is not pipe '%v'", string(output))
  2671  	}
  2672  }
  2673  
  2674  func TestStatRelativeSymlink(t *testing.T) {
  2675  	testenv.MustHaveSymlink(t)
  2676  	t.Parallel()
  2677  
  2678  	tmpdir := t.TempDir()
  2679  	target := filepath.Join(tmpdir, "target")
  2680  	f, err := Create(target)
  2681  	if err != nil {
  2682  		t.Fatal(err)
  2683  	}
  2684  	defer f.Close()
  2685  
  2686  	st, err := f.Stat()
  2687  	if err != nil {
  2688  		t.Fatal(err)
  2689  	}
  2690  
  2691  	link := filepath.Join(tmpdir, "link")
  2692  	err = Symlink(filepath.Base(target), link)
  2693  	if err != nil {
  2694  		t.Fatal(err)
  2695  	}
  2696  
  2697  	st1, err := Stat(link)
  2698  	if err != nil {
  2699  		t.Fatal(err)
  2700  	}
  2701  
  2702  	if !SameFile(st, st1) {
  2703  		t.Error("Stat doesn't follow relative symlink")
  2704  	}
  2705  
  2706  	if runtime.GOOS == "windows" {
  2707  		Remove(link)
  2708  		err = Symlink(target[len(filepath.VolumeName(target)):], link)
  2709  		if err != nil {
  2710  			t.Fatal(err)
  2711  		}
  2712  
  2713  		st1, err := Stat(link)
  2714  		if err != nil {
  2715  			t.Fatal(err)
  2716  		}
  2717  
  2718  		if !SameFile(st, st1) {
  2719  			t.Error("Stat doesn't follow relative symlink")
  2720  		}
  2721  	}
  2722  }
  2723  
  2724  func TestReadAtEOF(t *testing.T) {
  2725  	t.Parallel()
  2726  
  2727  	f := newFile(t)
  2728  
  2729  	_, err := f.ReadAt(make([]byte, 10), 0)
  2730  	switch err {
  2731  	case io.EOF:
  2732  		// all good
  2733  	case nil:
  2734  		t.Fatalf("ReadAt succeeded")
  2735  	default:
  2736  		t.Fatalf("ReadAt failed: %s", err)
  2737  	}
  2738  }
  2739  
  2740  func TestLongPath(t *testing.T) {
  2741  	t.Parallel()
  2742  
  2743  	tmpdir := t.TempDir()
  2744  
  2745  	// Test the boundary of 247 and fewer bytes (normal) and 248 and more bytes (adjusted).
  2746  	sizes := []int{247, 248, 249, 400}
  2747  	for len(tmpdir) < 400 {
  2748  		tmpdir += "/dir3456789"
  2749  	}
  2750  	for _, sz := range sizes {
  2751  		t.Run(fmt.Sprintf("length=%d", sz), func(t *testing.T) {
  2752  			sizedTempDir := tmpdir[:sz-1] + "x" // Ensure it does not end with a slash.
  2753  
  2754  			// The various sized runs are for this call to trigger the boundary
  2755  			// condition.
  2756  			if err := MkdirAll(sizedTempDir, 0755); err != nil {
  2757  				t.Fatalf("MkdirAll failed: %v", err)
  2758  			}
  2759  			data := []byte("hello world\n")
  2760  			if err := WriteFile(sizedTempDir+"/foo.txt", data, 0644); err != nil {
  2761  				t.Fatalf("os.WriteFile() failed: %v", err)
  2762  			}
  2763  			if err := Rename(sizedTempDir+"/foo.txt", sizedTempDir+"/bar.txt"); err != nil {
  2764  				t.Fatalf("Rename failed: %v", err)
  2765  			}
  2766  			mtime := time.Now().Truncate(time.Minute)
  2767  			if err := Chtimes(sizedTempDir+"/bar.txt", mtime, mtime); err != nil {
  2768  				t.Fatalf("Chtimes failed: %v", err)
  2769  			}
  2770  			names := []string{"bar.txt"}
  2771  			if testenv.HasSymlink() {
  2772  				if err := Symlink(sizedTempDir+"/bar.txt", sizedTempDir+"/symlink.txt"); err != nil {
  2773  					t.Fatalf("Symlink failed: %v", err)
  2774  				}
  2775  				names = append(names, "symlink.txt")
  2776  			}
  2777  			if testenv.HasLink() {
  2778  				if err := Link(sizedTempDir+"/bar.txt", sizedTempDir+"/link.txt"); err != nil {
  2779  					t.Fatalf("Link failed: %v", err)
  2780  				}
  2781  				names = append(names, "link.txt")
  2782  			}
  2783  			for _, wantSize := range []int64{int64(len(data)), 0} {
  2784  				for _, name := range names {
  2785  					path := sizedTempDir + "/" + name
  2786  					dir, err := Stat(path)
  2787  					if err != nil {
  2788  						t.Fatalf("Stat(%q) failed: %v", path, err)
  2789  					}
  2790  					filesize := size(path, t)
  2791  					if dir.Size() != filesize || filesize != wantSize {
  2792  						t.Errorf("Size(%q) is %d, len(ReadFile()) is %d, want %d", path, dir.Size(), filesize, wantSize)
  2793  					}
  2794  					if runtime.GOOS != "wasip1" { // Chmod is not supported on wasip1
  2795  						err = Chmod(path, dir.Mode())
  2796  						if err != nil {
  2797  							t.Fatalf("Chmod(%q) failed: %v", path, err)
  2798  						}
  2799  					}
  2800  				}
  2801  				if err := Truncate(sizedTempDir+"/bar.txt", 0); err != nil {
  2802  					t.Fatalf("Truncate failed: %v", err)
  2803  				}
  2804  			}
  2805  		})
  2806  	}
  2807  }
  2808  
  2809  func testKillProcess(t *testing.T, processKiller func(p *Process)) {
  2810  	t.Parallel()
  2811  
  2812  	// Re-exec the test binary to start a process that hangs until stdin is closed.
  2813  	cmd := testenv.Command(t, testenv.Executable(t))
  2814  	cmd.Env = append(cmd.Environ(), "GO_OS_TEST_DRAIN_STDIN=1")
  2815  	stdout, err := cmd.StdoutPipe()
  2816  	if err != nil {
  2817  		t.Fatal(err)
  2818  	}
  2819  	stdin, err := cmd.StdinPipe()
  2820  	if err != nil {
  2821  		t.Fatal(err)
  2822  	}
  2823  	err = cmd.Start()
  2824  	if err != nil {
  2825  		t.Fatalf("Failed to start test process: %v", err)
  2826  	}
  2827  
  2828  	defer func() {
  2829  		if err := cmd.Wait(); err == nil {
  2830  			t.Errorf("Test process succeeded, but expected to fail")
  2831  		}
  2832  		stdin.Close() // Keep stdin alive until the process has finished dying.
  2833  	}()
  2834  
  2835  	// Wait for the process to be started.
  2836  	// (It will close its stdout when it reaches TestMain.)
  2837  	io.Copy(io.Discard, stdout)
  2838  
  2839  	processKiller(cmd.Process)
  2840  }
  2841  
  2842  func TestKillStartProcess(t *testing.T) {
  2843  	testKillProcess(t, func(p *Process) {
  2844  		err := p.Kill()
  2845  		if err != nil {
  2846  			t.Fatalf("Failed to kill test process: %v", err)
  2847  		}
  2848  	})
  2849  }
  2850  
  2851  func TestGetppid(t *testing.T) {
  2852  	if runtime.GOOS == "plan9" {
  2853  		// TODO: golang.org/issue/8206
  2854  		t.Skipf("skipping test on plan9; see issue 8206")
  2855  	}
  2856  
  2857  	if Getenv("GO_WANT_HELPER_PROCESS") == "1" {
  2858  		fmt.Print(Getppid())
  2859  		Exit(0)
  2860  	}
  2861  
  2862  	t.Parallel()
  2863  
  2864  	cmd := testenv.Command(t, testenv.Executable(t), "-test.run=^TestGetppid$")
  2865  	cmd.Env = append(Environ(), "GO_WANT_HELPER_PROCESS=1")
  2866  
  2867  	// verify that Getppid() from the forked process reports our process id
  2868  	output, err := cmd.CombinedOutput()
  2869  	if err != nil {
  2870  		t.Fatalf("Failed to spawn child process: %v %q", err, string(output))
  2871  	}
  2872  
  2873  	childPpid := string(output)
  2874  	ourPid := fmt.Sprintf("%d", Getpid())
  2875  	if childPpid != ourPid {
  2876  		t.Fatalf("Child process reports parent process id '%v', expected '%v'", childPpid, ourPid)
  2877  	}
  2878  }
  2879  
  2880  func TestKillFindProcess(t *testing.T) {
  2881  	testKillProcess(t, func(p *Process) {
  2882  		p2, err := FindProcess(p.Pid)
  2883  		if err != nil {
  2884  			t.Fatalf("Failed to find test process: %v", err)
  2885  		}
  2886  		err = p2.Kill()
  2887  		if err != nil {
  2888  			t.Fatalf("Failed to kill test process: %v", err)
  2889  		}
  2890  	})
  2891  }
  2892  
  2893  var nilFileMethodTests = []struct {
  2894  	name string
  2895  	f    func(*File) error
  2896  }{
  2897  	{"Chdir", func(f *File) error { return f.Chdir() }},
  2898  	{"Close", func(f *File) error { return f.Close() }},
  2899  	{"Chmod", func(f *File) error { return f.Chmod(0) }},
  2900  	{"Chown", func(f *File) error { return f.Chown(0, 0) }},
  2901  	{"Read", func(f *File) error { _, err := f.Read(make([]byte, 0)); return err }},
  2902  	{"ReadAt", func(f *File) error { _, err := f.ReadAt(make([]byte, 0), 0); return err }},
  2903  	{"Readdir", func(f *File) error { _, err := f.Readdir(1); return err }},
  2904  	{"Readdirnames", func(f *File) error { _, err := f.Readdirnames(1); return err }},
  2905  	{"Seek", func(f *File) error { _, err := f.Seek(0, io.SeekStart); return err }},
  2906  	{"Stat", func(f *File) error { _, err := f.Stat(); return err }},
  2907  	{"Sync", func(f *File) error { return f.Sync() }},
  2908  	{"Truncate", func(f *File) error { return f.Truncate(0) }},
  2909  	{"Write", func(f *File) error { _, err := f.Write(make([]byte, 0)); return err }},
  2910  	{"WriteAt", func(f *File) error { _, err := f.WriteAt(make([]byte, 0), 0); return err }},
  2911  	{"WriteString", func(f *File) error { _, err := f.WriteString(""); return err }},
  2912  }
  2913  
  2914  // Test that all File methods give ErrInvalid if the receiver is nil.
  2915  func TestNilFileMethods(t *testing.T) {
  2916  	t.Parallel()
  2917  
  2918  	for _, tt := range nilFileMethodTests {
  2919  		var file *File
  2920  		got := tt.f(file)
  2921  		if got != ErrInvalid {
  2922  			t.Errorf("%v should fail when f is nil; got %v", tt.name, got)
  2923  		}
  2924  	}
  2925  }
  2926  
  2927  func mkdirTree(t *testing.T, root string, level, max int) {
  2928  	if level >= max {
  2929  		return
  2930  	}
  2931  	level++
  2932  	for i := 'a'; i < 'c'; i++ {
  2933  		dir := filepath.Join(root, string(i))
  2934  		if err := Mkdir(dir, 0700); err != nil {
  2935  			t.Fatal(err)
  2936  		}
  2937  		mkdirTree(t, dir, level, max)
  2938  	}
  2939  }
  2940  
  2941  // Test that simultaneous RemoveAll do not report an error.
  2942  // As long as it gets removed, we should be happy.
  2943  func TestRemoveAllRace(t *testing.T) {
  2944  	if runtime.GOOS == "windows" {
  2945  		// Windows has very strict rules about things like
  2946  		// removing directories while someone else has
  2947  		// them open. The racing doesn't work out nicely
  2948  		// like it does on Unix.
  2949  		t.Skip("skipping on windows")
  2950  	}
  2951  	if runtime.GOOS == "dragonfly" {
  2952  		testenv.SkipFlaky(t, 52301)
  2953  	}
  2954  
  2955  	n := runtime.GOMAXPROCS(16)
  2956  	defer runtime.GOMAXPROCS(n)
  2957  	root := t.TempDir()
  2958  	mkdirTree(t, root, 1, 6)
  2959  	hold := make(chan struct{})
  2960  	var wg sync.WaitGroup
  2961  	for i := 0; i < 4; i++ {
  2962  		wg.Add(1)
  2963  		go func() {
  2964  			defer wg.Done()
  2965  			<-hold
  2966  			err := RemoveAll(root)
  2967  			if err != nil {
  2968  				t.Errorf("unexpected error: %T, %q", err, err)
  2969  			}
  2970  		}()
  2971  	}
  2972  	close(hold) // let workers race to remove root
  2973  	wg.Wait()
  2974  }
  2975  
  2976  // Test that reading from a pipe doesn't use up a thread.
  2977  func TestPipeThreads(t *testing.T) {
  2978  	switch runtime.GOOS {
  2979  	case "aix":
  2980  		t.Skip("skipping on aix; issue 70131")
  2981  	case "illumos", "solaris":
  2982  		t.Skip("skipping on Solaris and illumos; issue 19111")
  2983  	case "windows":
  2984  		t.Skip("skipping on Windows; issue 19098")
  2985  	case "plan9":
  2986  		t.Skip("skipping on Plan 9; does not support runtime poller")
  2987  	case "js":
  2988  		t.Skip("skipping on js; no support for os.Pipe")
  2989  	case "wasip1":
  2990  		t.Skip("skipping on wasip1; no support for os.Pipe")
  2991  	}
  2992  
  2993  	threads := 100
  2994  
  2995  	r := make([]*File, threads)
  2996  	w := make([]*File, threads)
  2997  	for i := 0; i < threads; i++ {
  2998  		rp, wp, err := Pipe()
  2999  		if err != nil {
  3000  			for j := 0; j < i; j++ {
  3001  				r[j].Close()
  3002  				w[j].Close()
  3003  			}
  3004  			t.Fatal(err)
  3005  		}
  3006  		r[i] = rp
  3007  		w[i] = wp
  3008  	}
  3009  
  3010  	defer debug.SetMaxThreads(debug.SetMaxThreads(threads / 2))
  3011  
  3012  	creading := make(chan bool, threads)
  3013  	cdone := make(chan bool, threads)
  3014  	for i := 0; i < threads; i++ {
  3015  		go func(i int) {
  3016  			var b [1]byte
  3017  			creading <- true
  3018  			if _, err := r[i].Read(b[:]); err != nil {
  3019  				t.Error(err)
  3020  			}
  3021  			if err := r[i].Close(); err != nil {
  3022  				t.Error(err)
  3023  			}
  3024  			cdone <- true
  3025  		}(i)
  3026  	}
  3027  
  3028  	for i := 0; i < threads; i++ {
  3029  		<-creading
  3030  	}
  3031  
  3032  	// If we are still alive, it means that the 100 goroutines did
  3033  	// not require 100 threads.
  3034  
  3035  	for i := 0; i < threads; i++ {
  3036  		if _, err := w[i].Write([]byte{0}); err != nil {
  3037  			t.Error(err)
  3038  		}
  3039  		if err := w[i].Close(); err != nil {
  3040  			t.Error(err)
  3041  		}
  3042  		<-cdone
  3043  	}
  3044  }
  3045  
  3046  func testDoubleCloseError(path string) func(*testing.T) {
  3047  	return func(t *testing.T) {
  3048  		t.Parallel()
  3049  
  3050  		file, err := Open(path)
  3051  		if err != nil {
  3052  			t.Fatal(err)
  3053  		}
  3054  		if err := file.Close(); err != nil {
  3055  			t.Fatalf("unexpected error from Close: %v", err)
  3056  		}
  3057  		if err := file.Close(); err == nil {
  3058  			t.Error("second Close did not fail")
  3059  		} else if pe, ok := err.(*PathError); !ok {
  3060  			t.Errorf("second Close: got %T, want %T", err, pe)
  3061  		} else if pe.Err != ErrClosed {
  3062  			t.Errorf("second Close: got %q, want %q", pe.Err, ErrClosed)
  3063  		} else {
  3064  			t.Logf("second close returned expected error %q", err)
  3065  		}
  3066  	}
  3067  }
  3068  
  3069  func TestDoubleCloseError(t *testing.T) {
  3070  	t.Parallel()
  3071  	t.Run("file", testDoubleCloseError(filepath.Join(sfdir, sfname)))
  3072  	t.Run("dir", testDoubleCloseError(sfdir))
  3073  }
  3074  
  3075  func TestUserCacheDir(t *testing.T) {
  3076  	t.Parallel()
  3077  
  3078  	dir, err := UserCacheDir()
  3079  	if err != nil {
  3080  		t.Skipf("skipping: %v", err)
  3081  	}
  3082  	if dir == "" {
  3083  		t.Fatalf("UserCacheDir returned %q; want non-empty path or error", dir)
  3084  	}
  3085  
  3086  	fi, err := Stat(dir)
  3087  	if err != nil {
  3088  		if IsNotExist(err) {
  3089  			t.Log(err)
  3090  			return
  3091  		}
  3092  		t.Fatal(err)
  3093  	}
  3094  	if !fi.IsDir() {
  3095  		t.Fatalf("dir %s is not directory; type = %v", dir, fi.Mode())
  3096  	}
  3097  }
  3098  
  3099  func TestUserCacheDirXDGConfigDirEnvVar(t *testing.T) {
  3100  	switch runtime.GOOS {
  3101  	case "windows", "darwin", "plan9":
  3102  		t.Skip("$XDG_CACHE_HOME is effective only on Unix systems")
  3103  	}
  3104  
  3105  	wd, err := Getwd()
  3106  	if err != nil {
  3107  		t.Fatal(err)
  3108  	}
  3109  	t.Setenv("XDG_CACHE_HOME", wd)
  3110  
  3111  	dir, err := UserCacheDir()
  3112  	if err != nil {
  3113  		t.Fatal(err)
  3114  	}
  3115  	if dir != wd {
  3116  		t.Fatalf("UserCacheDir returned %q; want the value of $XDG_CACHE_HOME %q", dir, wd)
  3117  	}
  3118  
  3119  	t.Setenv("XDG_CACHE_HOME", "some-dir")
  3120  	_, err = UserCacheDir()
  3121  	if err == nil {
  3122  		t.Fatal("UserCacheDir succeeded though $XDG_CACHE_HOME contains a relative path")
  3123  	}
  3124  }
  3125  
  3126  func TestUserConfigDir(t *testing.T) {
  3127  	t.Parallel()
  3128  
  3129  	dir, err := UserConfigDir()
  3130  	if err != nil {
  3131  		t.Skipf("skipping: %v", err)
  3132  	}
  3133  	if dir == "" {
  3134  		t.Fatalf("UserConfigDir returned %q; want non-empty path or error", dir)
  3135  	}
  3136  
  3137  	fi, err := Stat(dir)
  3138  	if err != nil {
  3139  		if IsNotExist(err) {
  3140  			t.Log(err)
  3141  			return
  3142  		}
  3143  		t.Fatal(err)
  3144  	}
  3145  	if !fi.IsDir() {
  3146  		t.Fatalf("dir %s is not directory; type = %v", dir, fi.Mode())
  3147  	}
  3148  }
  3149  
  3150  func TestUserConfigDirXDGConfigDirEnvVar(t *testing.T) {
  3151  	switch runtime.GOOS {
  3152  	case "windows", "darwin", "plan9":
  3153  		t.Skip("$XDG_CONFIG_HOME is effective only on Unix systems")
  3154  	}
  3155  
  3156  	wd, err := Getwd()
  3157  	if err != nil {
  3158  		t.Fatal(err)
  3159  	}
  3160  	t.Setenv("XDG_CONFIG_HOME", wd)
  3161  
  3162  	dir, err := UserConfigDir()
  3163  	if err != nil {
  3164  		t.Fatal(err)
  3165  	}
  3166  	if dir != wd {
  3167  		t.Fatalf("UserConfigDir returned %q; want the value of $XDG_CONFIG_HOME %q", dir, wd)
  3168  	}
  3169  
  3170  	t.Setenv("XDG_CONFIG_HOME", "some-dir")
  3171  	_, err = UserConfigDir()
  3172  	if err == nil {
  3173  		t.Fatal("UserConfigDir succeeded though $XDG_CONFIG_HOME contains a relative path")
  3174  	}
  3175  }
  3176  
  3177  func TestUserHomeDir(t *testing.T) {
  3178  	t.Parallel()
  3179  
  3180  	dir, err := UserHomeDir()
  3181  	if dir == "" && err == nil {
  3182  		t.Fatal("UserHomeDir returned an empty string but no error")
  3183  	}
  3184  	if err != nil {
  3185  		// UserHomeDir may return a non-nil error if the environment variable
  3186  		// for the home directory is empty or unset in the environment.
  3187  		t.Skipf("skipping: %v", err)
  3188  	}
  3189  
  3190  	fi, err := Stat(dir)
  3191  	if err != nil {
  3192  		if IsNotExist(err) {
  3193  			// The user's home directory has a well-defined location, but does not
  3194  			// exist. (Maybe nothing has written to it yet? That could happen, for
  3195  			// example, on minimal VM images used for CI testing.)
  3196  			t.Log(err)
  3197  			return
  3198  		}
  3199  		t.Fatal(err)
  3200  	}
  3201  	if !fi.IsDir() {
  3202  		t.Fatalf("dir %s is not directory; type = %v", dir, fi.Mode())
  3203  	}
  3204  }
  3205  
  3206  func TestDirSeek(t *testing.T) {
  3207  	t.Parallel()
  3208  
  3209  	wd, err := Getwd()
  3210  	if err != nil {
  3211  		t.Fatal(err)
  3212  	}
  3213  	f, err := Open(wd)
  3214  	if err != nil {
  3215  		t.Fatal(err)
  3216  	}
  3217  	dirnames1, err := f.Readdirnames(0)
  3218  	if err != nil {
  3219  		t.Fatal(err)
  3220  	}
  3221  
  3222  	ret, err := f.Seek(0, 0)
  3223  	if err != nil {
  3224  		t.Fatal(err)
  3225  	}
  3226  	if ret != 0 {
  3227  		t.Fatalf("seek result not zero: %d", ret)
  3228  	}
  3229  
  3230  	dirnames2, err := f.Readdirnames(0)
  3231  	if err != nil {
  3232  		t.Fatal(err)
  3233  	}
  3234  
  3235  	if len(dirnames1) != len(dirnames2) {
  3236  		t.Fatalf("listings have different lengths: %d and %d\n", len(dirnames1), len(dirnames2))
  3237  	}
  3238  	for i, n1 := range dirnames1 {
  3239  		n2 := dirnames2[i]
  3240  		if n1 != n2 {
  3241  			t.Fatalf("different name i=%d n1=%s n2=%s\n", i, n1, n2)
  3242  		}
  3243  	}
  3244  }
  3245  
  3246  func TestReaddirSmallSeek(t *testing.T) {
  3247  	// See issue 37161. Read only one entry from a directory,
  3248  	// seek to the beginning, and read again. We should not see
  3249  	// duplicate entries.
  3250  	t.Parallel()
  3251  
  3252  	wd, err := Getwd()
  3253  	if err != nil {
  3254  		t.Fatal(err)
  3255  	}
  3256  	df, err := Open(filepath.Join(wd, "testdata", "issue37161"))
  3257  	if err != nil {
  3258  		t.Fatal(err)
  3259  	}
  3260  	names1, err := df.Readdirnames(1)
  3261  	if err != nil {
  3262  		t.Fatal(err)
  3263  	}
  3264  	if _, err = df.Seek(0, 0); err != nil {
  3265  		t.Fatal(err)
  3266  	}
  3267  	names2, err := df.Readdirnames(0)
  3268  	if err != nil {
  3269  		t.Fatal(err)
  3270  	}
  3271  	if len(names2) != 3 {
  3272  		t.Fatalf("first names: %v, second names: %v", names1, names2)
  3273  	}
  3274  }
  3275  
  3276  // isDeadlineExceeded reports whether err is or wraps ErrDeadlineExceeded.
  3277  // We also check that the error has a Timeout method that returns true.
  3278  func isDeadlineExceeded(err error) bool {
  3279  	if !IsTimeout(err) {
  3280  		return false
  3281  	}
  3282  	if !errors.Is(err, ErrDeadlineExceeded) {
  3283  		return false
  3284  	}
  3285  	return true
  3286  }
  3287  
  3288  // Test that opening a file does not change its permissions.  Issue 38225.
  3289  func TestOpenFileKeepsPermissions(t *testing.T) {
  3290  	t.Run("OpenFile", func(t *testing.T) {
  3291  		testOpenFileKeepsPermissions(t, OpenFile)
  3292  	})
  3293  	t.Run("RootOpenFile", func(t *testing.T) {
  3294  		testOpenFileKeepsPermissions(t, func(name string, flag int, perm FileMode) (*File, error) {
  3295  			dir, file := filepath.Split(name)
  3296  			r, err := OpenRoot(dir)
  3297  			if err != nil {
  3298  				return nil, err
  3299  			}
  3300  			defer r.Close()
  3301  			return r.OpenFile(file, flag, perm)
  3302  		})
  3303  	})
  3304  }
  3305  func testOpenFileKeepsPermissions(t *testing.T, openf func(name string, flag int, perm FileMode) (*File, error)) {
  3306  	t.Parallel()
  3307  
  3308  	dir := t.TempDir()
  3309  	name := filepath.Join(dir, "x")
  3310  	f, err := Create(name)
  3311  	if err != nil {
  3312  		t.Fatal(err)
  3313  	}
  3314  	if err := f.Close(); err != nil {
  3315  		t.Error(err)
  3316  	}
  3317  	f, err = openf(name, O_WRONLY|O_CREATE|O_TRUNC, 0)
  3318  	if err != nil {
  3319  		t.Fatal(err)
  3320  	}
  3321  	if fi, err := f.Stat(); err != nil {
  3322  		t.Error(err)
  3323  	} else if fi.Mode()&0222 == 0 {
  3324  		t.Errorf("f.Stat.Mode after OpenFile is %v, should be writable", fi.Mode())
  3325  	}
  3326  	if err := f.Close(); err != nil {
  3327  		t.Error(err)
  3328  	}
  3329  	if fi, err := Stat(name); err != nil {
  3330  		t.Error(err)
  3331  	} else if fi.Mode()&0222 == 0 {
  3332  		t.Errorf("Stat after OpenFile is %v, should be writable", fi.Mode())
  3333  	}
  3334  }
  3335  
  3336  func forceMFTUpdateOnWindows(t *testing.T, path string) {
  3337  	t.Helper()
  3338  
  3339  	if runtime.GOOS != "windows" {
  3340  		return
  3341  	}
  3342  
  3343  	// On Windows, we force the MFT to update by reading the actual metadata from GetFileInformationByHandle and then
  3344  	// explicitly setting that. Otherwise it might get out of sync with FindFirstFile. See golang.org/issues/42637.
  3345  	if err := filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error {
  3346  		if err != nil {
  3347  			t.Fatal(err)
  3348  		}
  3349  		info, err := d.Info()
  3350  		if err != nil {
  3351  			t.Fatal(err)
  3352  		}
  3353  		stat, err := Stat(path) // This uses GetFileInformationByHandle internally.
  3354  		if err != nil {
  3355  			t.Fatal(err)
  3356  		}
  3357  		if stat.ModTime() == info.ModTime() {
  3358  			return nil
  3359  		}
  3360  		if err := Chtimes(path, stat.ModTime(), stat.ModTime()); err != nil {
  3361  			t.Log(err) // We only log, not die, in case the test directory is not writable.
  3362  		}
  3363  		return nil
  3364  	}); err != nil {
  3365  		t.Fatal(err)
  3366  	}
  3367  }
  3368  
  3369  func TestDirFS(t *testing.T) {
  3370  	t.Parallel()
  3371  	testDirFS(t, DirFS("./testdata/dirfs"))
  3372  }
  3373  
  3374  func TestRootDirFS(t *testing.T) {
  3375  	t.Parallel()
  3376  	r, err := OpenRoot("./testdata/dirfs")
  3377  	if err != nil {
  3378  		t.Fatal(err)
  3379  	}
  3380  	defer r.Close()
  3381  	testDirFS(t, r.FS())
  3382  }
  3383  
  3384  func testDirFS(t *testing.T, fsys fs.FS) {
  3385  	forceMFTUpdateOnWindows(t, "./testdata/dirfs")
  3386  
  3387  	if err := fstest.TestFS(fsys, "a", "b", "dir/x"); err != nil {
  3388  		t.Fatal(err)
  3389  	}
  3390  
  3391  	rdfs, ok := fsys.(fs.ReadDirFS)
  3392  	if !ok {
  3393  		t.Error("expected DirFS result to implement fs.ReadDirFS")
  3394  	}
  3395  	if _, err := rdfs.ReadDir("nonexistent"); err == nil {
  3396  		t.Error("fs.ReadDir of nonexistent directory succeeded")
  3397  	}
  3398  
  3399  	// Test that the error message does not contain a backslash,
  3400  	// and does not contain the DirFS argument.
  3401  	const nonesuch = "dir/nonesuch"
  3402  	_, err := fsys.Open(nonesuch)
  3403  	if err == nil {
  3404  		t.Error("fs.Open of nonexistent file succeeded")
  3405  	} else {
  3406  		if !strings.Contains(err.Error(), nonesuch) {
  3407  			t.Errorf("error %q does not contain %q", err, nonesuch)
  3408  		}
  3409  		if strings.Contains(err.(*PathError).Path, "testdata") {
  3410  			t.Errorf("error %q contains %q", err, "testdata")
  3411  		}
  3412  	}
  3413  
  3414  	// Test that Open does not accept backslash as separator.
  3415  	d := DirFS(".")
  3416  	_, err = d.Open(`testdata\dirfs`)
  3417  	if err == nil {
  3418  		t.Fatalf(`Open testdata\dirfs succeeded`)
  3419  	}
  3420  
  3421  	// Test that Open does not open Windows device files.
  3422  	_, err = d.Open(`NUL`)
  3423  	if err == nil {
  3424  		t.Errorf(`Open NUL succeeded`)
  3425  	}
  3426  }
  3427  
  3428  func TestDirFSRootDir(t *testing.T) {
  3429  	t.Parallel()
  3430  
  3431  	cwd, err := Getwd()
  3432  	if err != nil {
  3433  		t.Fatal(err)
  3434  	}
  3435  	cwd = cwd[len(filepath.VolumeName(cwd)):] // trim volume prefix (C:) on Windows
  3436  	cwd = filepath.ToSlash(cwd)               // convert \ to /
  3437  	cwd = strings.TrimPrefix(cwd, "/")        // trim leading /
  3438  
  3439  	// Test that Open can open a path starting at /.
  3440  	d := DirFS("/")
  3441  	f, err := d.Open(cwd + "/testdata/dirfs/a")
  3442  	if err != nil {
  3443  		t.Fatal(err)
  3444  	}
  3445  	f.Close()
  3446  }
  3447  
  3448  func TestDirFSEmptyDir(t *testing.T) {
  3449  	t.Parallel()
  3450  
  3451  	d := DirFS("")
  3452  	cwd, _ := Getwd()
  3453  	for _, path := range []string{
  3454  		"testdata/dirfs/a",                          // not DirFS(".")
  3455  		filepath.ToSlash(cwd) + "/testdata/dirfs/a", // not DirFS("/")
  3456  	} {
  3457  		_, err := d.Open(path)
  3458  		if err == nil {
  3459  			t.Fatalf(`DirFS("").Open(%q) succeeded`, path)
  3460  		}
  3461  	}
  3462  }
  3463  
  3464  func TestDirFSPathsValid(t *testing.T) {
  3465  	if runtime.GOOS == "windows" {
  3466  		t.Skipf("skipping on Windows")
  3467  	}
  3468  	t.Parallel()
  3469  
  3470  	d := t.TempDir()
  3471  	if err := WriteFile(filepath.Join(d, "control.txt"), []byte(string("Hello, world!")), 0644); err != nil {
  3472  		t.Fatal(err)
  3473  	}
  3474  	if err := WriteFile(filepath.Join(d, `e:xperi\ment.txt`), []byte(string("Hello, colon and backslash!")), 0644); err != nil {
  3475  		t.Fatal(err)
  3476  	}
  3477  
  3478  	fsys := DirFS(d)
  3479  	err := fs.WalkDir(fsys, ".", func(path string, e fs.DirEntry, err error) error {
  3480  		if fs.ValidPath(e.Name()) {
  3481  			t.Logf("%q ok", e.Name())
  3482  		} else {
  3483  			t.Errorf("%q INVALID", e.Name())
  3484  		}
  3485  		return nil
  3486  	})
  3487  	if err != nil {
  3488  		t.Fatal(err)
  3489  	}
  3490  }
  3491  
  3492  func TestReadFileProc(t *testing.T) {
  3493  	t.Parallel()
  3494  
  3495  	// Linux files in /proc report 0 size,
  3496  	// but then if ReadFile reads just a single byte at offset 0,
  3497  	// the read at offset 1 returns EOF instead of more data.
  3498  	// ReadFile has a minimum read size of 512 to work around this,
  3499  	// but test explicitly that it's working.
  3500  	name := "/proc/sys/fs/pipe-max-size"
  3501  	if _, err := Stat(name); err != nil {
  3502  		t.Skip(err)
  3503  	}
  3504  	data, err := ReadFile(name)
  3505  	if err != nil {
  3506  		t.Fatal(err)
  3507  	}
  3508  	if len(data) == 0 || data[len(data)-1] != '\n' {
  3509  		t.Fatalf("read %s: not newline-terminated: %q", name, data)
  3510  	}
  3511  }
  3512  
  3513  func TestDirFSReadFileProc(t *testing.T) {
  3514  	t.Parallel()
  3515  
  3516  	fsys := DirFS("/")
  3517  	name := "proc/sys/fs/pipe-max-size"
  3518  	if _, err := fs.Stat(fsys, name); err != nil {
  3519  		t.Skip()
  3520  	}
  3521  	data, err := fs.ReadFile(fsys, name)
  3522  	if err != nil {
  3523  		t.Fatal(err)
  3524  	}
  3525  	if len(data) == 0 || data[len(data)-1] != '\n' {
  3526  		t.Fatalf("read %s: not newline-terminated: %q", name, data)
  3527  	}
  3528  }
  3529  
  3530  func TestWriteStringAlloc(t *testing.T) {
  3531  	if runtime.GOOS == "js" {
  3532  		t.Skip("js allocates a lot during File.WriteString")
  3533  	}
  3534  	d := t.TempDir()
  3535  	f, err := Create(filepath.Join(d, "whiteboard.txt"))
  3536  	if err != nil {
  3537  		t.Fatal(err)
  3538  	}
  3539  	defer f.Close()
  3540  	allocs := testing.AllocsPerRun(100, func() {
  3541  		f.WriteString("I will not allocate when passed a string longer than 32 bytes.\n")
  3542  	})
  3543  	if allocs != 0 {
  3544  		t.Errorf("expected 0 allocs for File.WriteString, got %v", allocs)
  3545  	}
  3546  }
  3547  
  3548  // Test that it's OK to have parallel I/O and Close on a file.
  3549  func TestFileIOCloseRace(t *testing.T) {
  3550  	t.Parallel()
  3551  	file, err := Create(filepath.Join(t.TempDir(), "test.txt"))
  3552  	if err != nil {
  3553  		t.Fatal(err)
  3554  	}
  3555  	var wg sync.WaitGroup
  3556  	wg.Go(func() {
  3557  		var tmp [100]byte
  3558  		if _, err := file.Write(tmp[:]); err != nil && !errors.Is(err, ErrClosed) {
  3559  			t.Error(err)
  3560  		}
  3561  	})
  3562  	wg.Go(func() {
  3563  		var tmp [100]byte
  3564  		if _, err := file.Read(tmp[:]); err != nil && err != io.EOF && !errors.Is(err, ErrClosed) {
  3565  			t.Error(err)
  3566  		}
  3567  	})
  3568  	wg.Go(func() {
  3569  		if err := file.Close(); err != nil {
  3570  			t.Error(err)
  3571  		}
  3572  	})
  3573  	wg.Wait()
  3574  }
  3575  
  3576  // Test that it's OK to have parallel I/O and Close on a pipe.
  3577  func TestPipeIOCloseRace(t *testing.T) {
  3578  	// Skip on wasm, which doesn't have pipes.
  3579  	if runtime.GOOS == "js" || runtime.GOOS == "wasip1" {
  3580  		t.Skipf("skipping on %s: no pipes", runtime.GOOS)
  3581  	}
  3582  	t.Parallel()
  3583  
  3584  	r, w, err := Pipe()
  3585  	if err != nil {
  3586  		t.Fatal(err)
  3587  	}
  3588  
  3589  	var wg sync.WaitGroup
  3590  	wg.Add(3)
  3591  
  3592  	go func() {
  3593  		defer wg.Done()
  3594  		for {
  3595  			n, err := w.Write([]byte("hi"))
  3596  			if err != nil {
  3597  				// We look at error strings as the
  3598  				// expected errors are OS-specific.
  3599  				switch {
  3600  				case errors.Is(err, ErrClosed),
  3601  					strings.Contains(err.Error(), "broken pipe"),
  3602  					strings.Contains(err.Error(), "pipe is being closed"),
  3603  					strings.Contains(err.Error(), "hungup channel"):
  3604  					// Ignore an expected error.
  3605  				default:
  3606  					// Unexpected error.
  3607  					t.Error(err)
  3608  				}
  3609  				return
  3610  			}
  3611  			if n != 2 {
  3612  				t.Errorf("wrote %d bytes, expected 2", n)
  3613  				return
  3614  			}
  3615  		}
  3616  	}()
  3617  
  3618  	go func() {
  3619  		defer wg.Done()
  3620  		for {
  3621  			var buf [2]byte
  3622  			n, err := r.Read(buf[:])
  3623  			if err != nil {
  3624  				if err != io.EOF && !errors.Is(err, ErrClosed) {
  3625  					t.Error(err)
  3626  				}
  3627  				return
  3628  			}
  3629  			if n != 2 {
  3630  				t.Errorf("read %d bytes, want 2", n)
  3631  			}
  3632  		}
  3633  	}()
  3634  
  3635  	go func() {
  3636  		defer wg.Done()
  3637  
  3638  		// Let the other goroutines start. This is just to get
  3639  		// a better test, the test will still pass if they
  3640  		// don't start.
  3641  		time.Sleep(time.Millisecond)
  3642  
  3643  		if err := r.Close(); err != nil {
  3644  			t.Error(err)
  3645  		}
  3646  		if err := w.Close(); err != nil {
  3647  			t.Error(err)
  3648  		}
  3649  	}()
  3650  
  3651  	wg.Wait()
  3652  }
  3653  
  3654  // Test that it's OK to call Close concurrently on a pipe.
  3655  func TestPipeCloseRace(t *testing.T) {
  3656  	// Skip on wasm, which doesn't have pipes.
  3657  	if runtime.GOOS == "js" || runtime.GOOS == "wasip1" {
  3658  		t.Skipf("skipping on %s: no pipes", runtime.GOOS)
  3659  	}
  3660  	t.Parallel()
  3661  
  3662  	r, w, err := Pipe()
  3663  	if err != nil {
  3664  		t.Fatal(err)
  3665  	}
  3666  	var wg sync.WaitGroup
  3667  	c := make(chan error, 4)
  3668  	f := func() {
  3669  		defer wg.Done()
  3670  		c <- r.Close()
  3671  		c <- w.Close()
  3672  	}
  3673  	wg.Add(2)
  3674  	go f()
  3675  	go f()
  3676  	nils, errs := 0, 0
  3677  	for i := 0; i < 4; i++ {
  3678  		err := <-c
  3679  		if err == nil {
  3680  			nils++
  3681  		} else {
  3682  			errs++
  3683  		}
  3684  	}
  3685  	if nils != 2 || errs != 2 {
  3686  		t.Errorf("got nils %d errs %d, want 2 2", nils, errs)
  3687  	}
  3688  }
  3689  
  3690  func TestRandomLen(t *testing.T) {
  3691  	for range 5 {
  3692  		dir, err := MkdirTemp(t.TempDir(), "*")
  3693  		if err != nil {
  3694  			t.Fatal(err)
  3695  		}
  3696  		base := filepath.Base(dir)
  3697  		if len(base) > 10 {
  3698  			t.Errorf("MkdirTemp returned len %d: %s", len(base), base)
  3699  		}
  3700  	}
  3701  	for range 5 {
  3702  		f, err := CreateTemp(t.TempDir(), "*")
  3703  		if err != nil {
  3704  			t.Fatal(err)
  3705  		}
  3706  		base := filepath.Base(f.Name())
  3707  		f.Close()
  3708  		if len(base) > 10 {
  3709  			t.Errorf("CreateTemp returned len %d: %s", len(base), base)
  3710  		}
  3711  	}
  3712  }
  3713  
  3714  func TestCopyFS(t *testing.T) {
  3715  	t.Parallel()
  3716  
  3717  	// Test with disk filesystem.
  3718  	forceMFTUpdateOnWindows(t, "./testdata/dirfs")
  3719  	fsys := DirFS("./testdata/dirfs")
  3720  	tmpDir := t.TempDir()
  3721  	if err := CopyFS(tmpDir, fsys); err != nil {
  3722  		t.Fatal("CopyFS:", err)
  3723  	}
  3724  	forceMFTUpdateOnWindows(t, tmpDir)
  3725  	tmpFsys := DirFS(tmpDir)
  3726  	if err := fstest.TestFS(tmpFsys, "a", "b", "dir/x"); err != nil {
  3727  		t.Fatal("TestFS:", err)
  3728  	}
  3729  	if err := verifyCopyFS(t, fsys, tmpFsys); err != nil {
  3730  		t.Fatal("comparing two directories:", err)
  3731  	}
  3732  
  3733  	// Test whether CopyFS disallows copying for disk filesystem when there is any
  3734  	// existing file in the destination directory.
  3735  	if err := CopyFS(tmpDir, fsys); !errors.Is(err, fs.ErrExist) {
  3736  		t.Errorf("CopyFS should have failed and returned error when there is"+
  3737  			"any existing file in the destination directory (in disk filesystem), "+
  3738  			"got: %v, expected any error that indicates <file exists>", err)
  3739  	}
  3740  
  3741  	// Test with memory filesystem.
  3742  	fsys = fstest.MapFS{
  3743  		"william":    {Data: []byte("Shakespeare\n")},
  3744  		"carl":       {Data: []byte("Gauss\n")},
  3745  		"daVinci":    {Data: []byte("Leonardo\n")},
  3746  		"einstein":   {Data: []byte("Albert\n")},
  3747  		"dir/newton": {Data: []byte("Sir Isaac\n")},
  3748  	}
  3749  	tmpDir = t.TempDir()
  3750  	if err := CopyFS(tmpDir, fsys); err != nil {
  3751  		t.Fatal("CopyFS:", err)
  3752  	}
  3753  	forceMFTUpdateOnWindows(t, tmpDir)
  3754  	tmpFsys = DirFS(tmpDir)
  3755  	if err := fstest.TestFS(tmpFsys, "william", "carl", "daVinci", "einstein", "dir/newton"); err != nil {
  3756  		t.Fatal("TestFS:", err)
  3757  	}
  3758  	if err := verifyCopyFS(t, fsys, tmpFsys); err != nil {
  3759  		t.Fatal("comparing two directories:", err)
  3760  	}
  3761  
  3762  	// Test whether CopyFS disallows copying for memory filesystem when there is any
  3763  	// existing file in the destination directory.
  3764  	if err := CopyFS(tmpDir, fsys); !errors.Is(err, fs.ErrExist) {
  3765  		t.Errorf("CopyFS should have failed and returned error when there is"+
  3766  			"any existing file in the destination directory (in memory filesystem), "+
  3767  			"got: %v, expected any error that indicates <file exists>", err)
  3768  	}
  3769  }
  3770  
  3771  // verifyCopyFS checks the content and permission of each file inside copied FS to ensure
  3772  // the copied files satisfy the convention stipulated in CopyFS.
  3773  func verifyCopyFS(t *testing.T, originFS, copiedFS fs.FS) error {
  3774  	testDir := filepath.Join(t.TempDir(), "test")
  3775  	// umask doesn't apply to the wasip and windows and there is no general way to get masked perm,
  3776  	// so create a dir and a file to compare the permission after umask if any
  3777  	if err := Mkdir(testDir, ModePerm); err != nil {
  3778  		return fmt.Errorf("mkdir %q failed: %v", testDir, err)
  3779  	}
  3780  	dirStat, err := Stat(testDir)
  3781  	if err != nil {
  3782  		return fmt.Errorf("stat dir %q failed: %v", testDir, err)
  3783  	}
  3784  	wantDirMode := dirStat.Mode()
  3785  
  3786  	f, err := Create(filepath.Join(testDir, "tmp"))
  3787  	if err != nil {
  3788  		return fmt.Errorf("open %q failed: %v", filepath.Join(testDir, "tmp"), err)
  3789  	}
  3790  	defer f.Close()
  3791  	wantFileRWStat, err := f.Stat()
  3792  	if err != nil {
  3793  		return fmt.Errorf("stat file %q failed: %v", f.Name(), err)
  3794  	}
  3795  	wantFileRWMode := wantFileRWStat.Mode()
  3796  
  3797  	return fs.WalkDir(originFS, ".", func(path string, d fs.DirEntry, err error) error {
  3798  		if d.IsDir() {
  3799  			// the dir . is not the dir created by CopyFS so skip checking its permission
  3800  			if d.Name() == "." {
  3801  				return nil
  3802  			}
  3803  
  3804  			dinfo, err := fs.Stat(copiedFS, path)
  3805  			if err != nil {
  3806  				return err
  3807  			}
  3808  
  3809  			if dinfo.Mode() != wantDirMode {
  3810  				return fmt.Errorf("dir %q mode is %v, want %v",
  3811  					d.Name(), dinfo.Mode(), wantDirMode)
  3812  			}
  3813  			return nil
  3814  		}
  3815  
  3816  		fInfo, err := originFS.Open(path)
  3817  		if err != nil {
  3818  			return err
  3819  		}
  3820  		defer fInfo.Close()
  3821  		copiedInfo, err := copiedFS.Open(path)
  3822  		if err != nil {
  3823  			return err
  3824  		}
  3825  		defer copiedInfo.Close()
  3826  
  3827  		// verify the file contents are the same
  3828  		data, err := io.ReadAll(fInfo)
  3829  		if err != nil {
  3830  			return err
  3831  		}
  3832  		newData, err := io.ReadAll(copiedInfo)
  3833  		if err != nil {
  3834  			return err
  3835  		}
  3836  		if !bytes.Equal(data, newData) {
  3837  			return fmt.Errorf("file %q content is %s, want %s", path, newData, data)
  3838  		}
  3839  
  3840  		fStat, err := fInfo.Stat()
  3841  		if err != nil {
  3842  			return err
  3843  		}
  3844  		copiedStat, err := copiedInfo.Stat()
  3845  		if err != nil {
  3846  			return err
  3847  		}
  3848  
  3849  		// check whether the execute permission is inherited from original FS
  3850  
  3851  		if copiedStat.Mode()&0111&wantFileRWMode != fStat.Mode()&0111&wantFileRWMode {
  3852  			return fmt.Errorf("file %q execute mode is %v, want %v",
  3853  				path, copiedStat.Mode()&0111, fStat.Mode()&0111)
  3854  		}
  3855  
  3856  		rwMode := copiedStat.Mode() &^ 0111 // unset the executable permission from file mode
  3857  		if rwMode != wantFileRWMode {
  3858  			return fmt.Errorf("file %q rw mode is %v, want %v",
  3859  				path, rwMode, wantFileRWStat.Mode())
  3860  		}
  3861  		return nil
  3862  	})
  3863  }
  3864  
  3865  func TestCopyFSWithSymlinks(t *testing.T) {
  3866  	// Test it with absolute and relative symlinks that point inside and outside the tree.
  3867  	testenv.MustHaveSymlink(t)
  3868  
  3869  	// Create a directory and file outside.
  3870  	tmpDir := t.TempDir()
  3871  	outsideDir := filepath.Join(tmpDir, "copyfs_out")
  3872  	if err := Mkdir(outsideDir, 0755); err != nil {
  3873  		t.Fatalf("Mkdir: %v", err)
  3874  	}
  3875  	outsideFile := filepath.Join(outsideDir, "file.out.txt")
  3876  
  3877  	if err := WriteFile(outsideFile, []byte("Testing CopyFS outside"), 0644); err != nil {
  3878  		t.Fatalf("WriteFile: %v", err)
  3879  	}
  3880  
  3881  	// Create a directory and file inside.
  3882  	insideDir := filepath.Join(tmpDir, "copyfs_in")
  3883  	if err := Mkdir(insideDir, 0755); err != nil {
  3884  		t.Fatalf("Mkdir: %v", err)
  3885  	}
  3886  	insideFile := filepath.Join(insideDir, "file.in.txt")
  3887  	if err := WriteFile(insideFile, []byte("Testing CopyFS inside"), 0644); err != nil {
  3888  		t.Fatalf("WriteFile: %v", err)
  3889  	}
  3890  
  3891  	// Create directories for symlinks.
  3892  	linkInDir := filepath.Join(insideDir, "in_symlinks")
  3893  	if err := Mkdir(linkInDir, 0755); err != nil {
  3894  		t.Fatalf("Mkdir: %v", err)
  3895  	}
  3896  	linkOutDir := filepath.Join(insideDir, "out_symlinks")
  3897  	if err := Mkdir(linkOutDir, 0755); err != nil {
  3898  		t.Fatalf("Mkdir: %v", err)
  3899  	}
  3900  
  3901  	// First, we create the absolute symlink pointing outside.
  3902  	outLinkFile := filepath.Join(linkOutDir, "file.abs.out.link")
  3903  	if err := Symlink(outsideFile, outLinkFile); err != nil {
  3904  		t.Fatalf("Symlink: %v", err)
  3905  	}
  3906  
  3907  	// Then, we create the relative symlink pointing outside.
  3908  	relOutsideFile, err := filepath.Rel(filepath.Join(linkOutDir, "."), outsideFile)
  3909  	if err != nil {
  3910  		t.Fatalf("filepath.Rel: %v", err)
  3911  	}
  3912  	relOutLinkFile := filepath.Join(linkOutDir, "file.rel.out.link")
  3913  	if err := Symlink(relOutsideFile, relOutLinkFile); err != nil {
  3914  		t.Fatalf("Symlink: %v", err)
  3915  	}
  3916  
  3917  	// Last, we create the relative symlink pointing inside.
  3918  	relInsideFile, err := filepath.Rel(filepath.Join(linkInDir, "."), insideFile)
  3919  	if err != nil {
  3920  		t.Fatalf("filepath.Rel: %v", err)
  3921  	}
  3922  	relInLinkFile := filepath.Join(linkInDir, "file.rel.in.link")
  3923  	if err := Symlink(relInsideFile, relInLinkFile); err != nil {
  3924  		t.Fatalf("Symlink: %v", err)
  3925  	}
  3926  
  3927  	// Copy the directory tree and verify.
  3928  	forceMFTUpdateOnWindows(t, insideDir)
  3929  	fsys := DirFS(insideDir)
  3930  	tmpDupDir := filepath.Join(tmpDir, "copyfs_dup")
  3931  	if err := Mkdir(tmpDupDir, 0755); err != nil {
  3932  		t.Fatalf("Mkdir: %v", err)
  3933  	}
  3934  
  3935  	if err := CopyFS(tmpDupDir, fsys); err != nil {
  3936  		t.Fatalf("CopyFS: %v", err)
  3937  	}
  3938  
  3939  	forceMFTUpdateOnWindows(t, tmpDupDir)
  3940  	tmpFsys := DirFS(tmpDupDir)
  3941  	if err := fstest.TestFS(tmpFsys, "file.in.txt", "out_symlinks/file.abs.out.link", "out_symlinks/file.rel.out.link", "in_symlinks/file.rel.in.link"); err != nil {
  3942  		t.Fatal("TestFS:", err)
  3943  	}
  3944  	if err := fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
  3945  		if d.IsDir() {
  3946  			return nil
  3947  		}
  3948  
  3949  		fi, err := d.Info()
  3950  		if err != nil {
  3951  			return err
  3952  		}
  3953  		if filepath.Ext(path) == ".link" {
  3954  			if fi.Mode()&ModeSymlink == 0 {
  3955  				return errors.New("original file " + path + " should be a symlink")
  3956  			}
  3957  			tmpfi, err := fs.Stat(tmpFsys, path)
  3958  			if err != nil {
  3959  				return err
  3960  			}
  3961  			if tmpfi.Mode()&ModeSymlink != 0 {
  3962  				return errors.New("copied file " + path + " should not be a symlink")
  3963  			}
  3964  		}
  3965  
  3966  		data, err := fs.ReadFile(fsys, path)
  3967  		if err != nil {
  3968  			return err
  3969  		}
  3970  		newData, err := fs.ReadFile(tmpFsys, path)
  3971  		if err != nil {
  3972  			return err
  3973  		}
  3974  		if !bytes.Equal(data, newData) {
  3975  			return errors.New("file " + path + " contents differ")
  3976  		}
  3977  
  3978  		var target string
  3979  		switch fileName := filepath.Base(path); fileName {
  3980  		case "file.abs.out.link", "file.rel.out.link":
  3981  			target = outsideFile
  3982  		case "file.rel.in.link":
  3983  			target = insideFile
  3984  		}
  3985  		if len(target) > 0 {
  3986  			targetData, err := ReadFile(target)
  3987  			if err != nil {
  3988  				return err
  3989  			}
  3990  			if !bytes.Equal(targetData, newData) {
  3991  				return errors.New("file " + path + " contents differ from target")
  3992  			}
  3993  		}
  3994  
  3995  		return nil
  3996  	}); err != nil {
  3997  		t.Fatal("comparing two directories:", err)
  3998  	}
  3999  }
  4000  
  4001  func TestAppendDoesntOverwrite(t *testing.T) {
  4002  	testMaybeRooted(t, func(t *testing.T, r *Root) {
  4003  		name := "file"
  4004  		if err := WriteFile(name, []byte("hello"), 0666); err != nil {
  4005  			t.Fatal(err)
  4006  		}
  4007  		var f *File
  4008  		var err error
  4009  		if r == nil {
  4010  			f, err = OpenFile(name, O_APPEND|O_WRONLY, 0)
  4011  		} else {
  4012  			f, err = r.OpenFile(name, O_APPEND|O_WRONLY, 0)
  4013  		}
  4014  		if err != nil {
  4015  			t.Fatal(err)
  4016  		}
  4017  		if _, err := f.Write([]byte(" world")); err != nil {
  4018  			f.Close()
  4019  			t.Fatal(err)
  4020  		}
  4021  		if err := f.Close(); err != nil {
  4022  			t.Fatal(err)
  4023  		}
  4024  		got, err := ReadFile(name)
  4025  		if err != nil {
  4026  			t.Fatal(err)
  4027  		}
  4028  		want := "hello world"
  4029  		if string(got) != want {
  4030  			t.Fatalf("got %q, want %q", got, want)
  4031  		}
  4032  	})
  4033  }
  4034  
  4035  func TestRemoveReadOnlyFile(t *testing.T) {
  4036  	testMaybeRooted(t, func(t *testing.T, r *Root) {
  4037  		if err := WriteFile("file", []byte("1"), 0); err != nil {
  4038  			t.Fatal(err)
  4039  		}
  4040  		var err error
  4041  		if r == nil {
  4042  			err = Remove("file")
  4043  		} else {
  4044  			err = r.Remove("file")
  4045  		}
  4046  		if err != nil {
  4047  			t.Fatalf("Remove read-only file: %v", err)
  4048  		}
  4049  		if _, err := Stat("file"); !IsNotExist(err) {
  4050  			t.Fatalf("Stat read-only file after removal: %v (want IsNotExist)", err)
  4051  		}
  4052  	})
  4053  }
  4054  
  4055  func TestOpenFileDevNull(t *testing.T) {
  4056  	// See https://go.dev/issue/71752.
  4057  	t.Parallel()
  4058  
  4059  	f, err := OpenFile(DevNull, O_WRONLY|O_CREATE|O_TRUNC, 0o644)
  4060  	if err != nil {
  4061  		t.Fatalf("OpenFile(DevNull): %v", err)
  4062  	}
  4063  	f.Close()
  4064  }
  4065  
  4066  func TestReadFileContents(t *testing.T) {
  4067  	type readStep struct {
  4068  		bufSize int   // non-zero to check length of buf to Read
  4069  		retN    int   // result of Read call
  4070  		retErr  error // error result of Read call
  4071  	}
  4072  	errFoo := errors.New("foo")
  4073  	tests := []struct {
  4074  		name     string
  4075  		statSize int64 // size of file to read, per stat (may be 0 for /proc files)
  4076  		wantSize int   // wanted length of []byte from readFileContents
  4077  		wantErr  error // wanted error from readFileContents
  4078  		reads    []readStep
  4079  	}{
  4080  		{
  4081  			name:     "big-file",
  4082  			statSize: 2000,
  4083  			wantSize: 2000,
  4084  			reads: []readStep{
  4085  				{bufSize: 2001, retN: 21, retErr: nil},
  4086  				{bufSize: 1980, retN: 1979, retErr: io.EOF},
  4087  			},
  4088  		},
  4089  		{
  4090  			name:     "small-file",
  4091  			statSize: 100,
  4092  			wantSize: 100,
  4093  			reads: []readStep{
  4094  				{bufSize: 512, retN: 100, retErr: io.EOF},
  4095  			},
  4096  		},
  4097  		{
  4098  			name:     "returning-error",
  4099  			statSize: 1000,
  4100  			wantSize: 50,
  4101  			wantErr:  errFoo,
  4102  			reads: []readStep{
  4103  				{bufSize: 1001, retN: 25, retErr: nil},
  4104  				{retN: 25, retErr: errFoo},
  4105  			},
  4106  		},
  4107  		{
  4108  			name:     "proc-file",
  4109  			statSize: 0,
  4110  			wantSize: 1023,
  4111  			reads: []readStep{
  4112  				{bufSize: 512, retN: 512, retErr: nil},
  4113  				{retN: 511, retErr: io.EOF},
  4114  			},
  4115  		},
  4116  		{
  4117  			name:     "plan9-iproute-file", // Issue 72080
  4118  			statSize: 0,
  4119  			wantSize: 1032,
  4120  			reads: []readStep{
  4121  				{bufSize: 512, retN: 511, retErr: nil},
  4122  				{retN: 511, retErr: nil},
  4123  				{retN: 10, retErr: io.EOF},
  4124  			},
  4125  		},
  4126  	}
  4127  	for _, tt := range tests {
  4128  		t.Run(tt.name, func(t *testing.T) {
  4129  			remain := tt.reads
  4130  			i := -1
  4131  			got, err := ExportReadFileContents(tt.statSize, func(buf []byte) (int, error) {
  4132  				i++
  4133  				t.Logf("read[%d] with buf size %d", i, len(buf))
  4134  				if len(remain) == 0 {
  4135  					t.Fatalf("unexpected read of length %d after %d expected reads", len(buf), len(tt.reads))
  4136  				}
  4137  				if tt.statSize == 0 && len(buf) < 512 {
  4138  					// Issue 72080: readFileContents should not do /proc reads with buffers
  4139  					// smaller than 512.
  4140  					t.Fatalf("read[%d] with buf size %d; want at least 512 for 0-sized file", i, len(buf))
  4141  				}
  4142  				step := remain[0]
  4143  				remain = remain[1:]
  4144  				if step.bufSize != 0 && len(buf) != step.bufSize {
  4145  					t.Fatalf("read[%d] has buffer size %d; want %d", i, len(buf), step.bufSize)
  4146  				}
  4147  				return step.retN, step.retErr
  4148  			})
  4149  			if len(remain) > 0 {
  4150  				t.Fatalf("expected %d reads, got %d", len(tt.reads), i+1)
  4151  			}
  4152  			if fmt.Sprint(err) != fmt.Sprint(tt.wantErr) {
  4153  				t.Errorf("got error %v; want %v", err, tt.wantErr)
  4154  			}
  4155  			if len(got) != tt.wantSize {
  4156  				t.Errorf("got size %d; want %d", len(got), tt.wantSize)
  4157  			}
  4158  		})
  4159  	}
  4160  }
  4161  

View as plain text