...

Source file src/os/os_test.go

Documentation: os

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

View as plain text