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