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