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