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