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 func TestFileChdirFromTempDir(t *testing.T) {
1622
1623
1624
1625
1626 wd, err := Getwd()
1627 if err != nil {
1628 t.Fatalf("Getwd: %v", err)
1629 }
1630 defer func() {
1631 if err := Chdir(wd); err != nil {
1632 t.Errorf("restore wd: %v", err)
1633 }
1634 }()
1635
1636 fd, err := Open(".")
1637 if err != nil {
1638 t.Fatalf("Open .: %v", err)
1639 }
1640 defer fd.Close()
1641
1642 tmp := t.TempDir()
1643 if err := Chdir(tmp); err != nil {
1644 t.Fatalf("Chdir %s: %v", tmp, err)
1645 }
1646
1647 if err := fd.Chdir(); err != nil {
1648 t.Fatalf("fd.Chdir: %v", err)
1649 }
1650 }
1651
1652
1653
1654
1655
1656 func TestFileChdirTestlog(t *testing.T) {
1657 if Getenv("GO_TEST_FILE_CHDIR_TESTLOG") == "1" {
1658 fd, err := Open(".")
1659 if err != nil {
1660 t.Fatalf("Open .: %s", err)
1661 }
1662 defer fd.Close()
1663 if err := Chdir(TempDir()); err != nil {
1664 t.Fatalf("Chdir: %s", err)
1665 }
1666 if err := fd.Chdir(); err != nil {
1667 t.Fatalf("fd.Chdir: %s", err)
1668 }
1669 return
1670 }
1671
1672 testenv.MustHaveExec(t)
1673 exe := testenv.Executable(t)
1674 logfile := filepath.Join(t.TempDir(), "testlog.txt")
1675 cmd := testenv.Command(t, exe,
1676 "-test.run=^TestFileChdirTestlog$",
1677 "-test.testlogfile="+logfile)
1678 cmd = testenv.CleanCmdEnv(cmd)
1679 cmd.Env = append(cmd.Env, "GO_TEST_FILE_CHDIR_TESTLOG=1")
1680 if out, err := cmd.CombinedOutput(); err != nil {
1681 t.Fatalf("helper failed: %v\n%s", err, out)
1682 }
1683
1684 data, err := ReadFile(logfile)
1685 if err != nil {
1686 t.Fatal(err)
1687 }
1688 chdirs := 0
1689 for _, line := range strings.Split(string(data), "\n") {
1690 if strings.HasPrefix(line, "chdir ") {
1691 chdirs++
1692 }
1693 }
1694 if chdirs < 2 {
1695 t.Fatalf("got %d chdir testlog entries, want at least 2; testlog:\n%s", chdirs, data)
1696 }
1697 }
1698
1699 func TestChdirAndGetwd(t *testing.T) {
1700 t.Chdir(t.TempDir())
1701
1702
1703
1704 dirs := []string{"/", "/usr/bin", "/tmp"}
1705
1706 switch runtime.GOOS {
1707 case "android":
1708 dirs = []string{"/system/bin"}
1709 case "plan9":
1710 dirs = []string{"/", "/usr"}
1711 case "ios", "windows", "wasip1":
1712 dirs = nil
1713 for _, dir := range []string{t.TempDir(), t.TempDir()} {
1714
1715 dir, err := filepath.EvalSymlinks(dir)
1716 if err != nil {
1717 t.Fatalf("EvalSymlinks: %v", err)
1718 }
1719 dirs = append(dirs, dir)
1720 }
1721 }
1722 for mode := 0; mode < 2; mode++ {
1723 for _, d := range dirs {
1724 var err error
1725 if mode == 0 {
1726 err = Chdir(d)
1727 } else {
1728 fd1, err1 := Open(d)
1729 if err1 != nil {
1730 t.Errorf("Open %s: %s", d, err1)
1731 continue
1732 }
1733 err = fd1.Chdir()
1734 fd1.Close()
1735 }
1736 if d == "/tmp" {
1737 Setenv("PWD", "/tmp")
1738 }
1739 pwd, err1 := Getwd()
1740 if err != nil {
1741 t.Fatalf("Chdir %s: %s", d, err)
1742 }
1743 if err1 != nil {
1744 t.Fatalf("Getwd in %s: %s", d, err1)
1745 }
1746 if !equal(pwd, d) {
1747 t.Fatalf("Getwd returned %q want %q", pwd, d)
1748 }
1749 }
1750 }
1751 }
1752
1753
1754 func TestProgWideChdir(t *testing.T) {
1755 const N = 10
1756 var wg sync.WaitGroup
1757 hold := make(chan struct{})
1758 done := make(chan struct{})
1759
1760 d := t.TempDir()
1761 t.Chdir(d)
1762
1763
1764
1765
1766
1767
1768 defer wg.Wait()
1769 defer close(done)
1770
1771 for i := 0; i < N; i++ {
1772 wg.Add(1)
1773 go func(i int) {
1774 defer wg.Done()
1775
1776
1777 if i%2 == 1 {
1778
1779
1780
1781
1782
1783 runtime.LockOSThread()
1784 }
1785 select {
1786 case <-done:
1787 return
1788 case <-hold:
1789 }
1790
1791 f0, err := Stat(".")
1792 if err != nil {
1793 t.Error(err)
1794 return
1795 }
1796 pwd, err := Getwd()
1797 if err != nil {
1798 t.Errorf("Getwd: %v", err)
1799 return
1800 }
1801 if pwd != d {
1802 t.Errorf("Getwd() = %q, want %q", pwd, d)
1803 return
1804 }
1805 f1, err := Stat(pwd)
1806 if err != nil {
1807 t.Error(err)
1808 return
1809 }
1810 if !SameFile(f0, f1) {
1811 t.Errorf(`Samefile(Stat("."), Getwd()) reports false (%s != %s)`, f0.Name(), f1.Name())
1812 return
1813 }
1814 }(i)
1815 }
1816 var err error
1817 if err = Chdir(d); err != nil {
1818 t.Fatalf("Chdir: %v", err)
1819 }
1820
1821
1822 d, err = Getwd()
1823 if err != nil {
1824 t.Fatalf("Getwd: %v", err)
1825 }
1826 close(hold)
1827 wg.Wait()
1828 }
1829
1830 func TestSeek(t *testing.T) {
1831 t.Parallel()
1832
1833 f := newFile(t)
1834
1835 const data = "hello, world\n"
1836 io.WriteString(f, data)
1837
1838 type test struct {
1839 in int64
1840 whence int
1841 out int64
1842 }
1843 var tests = []test{
1844 {0, io.SeekCurrent, int64(len(data))},
1845 {0, io.SeekStart, 0},
1846 {5, io.SeekStart, 5},
1847 {0, io.SeekEnd, int64(len(data))},
1848 {0, io.SeekStart, 0},
1849 {-1, io.SeekEnd, int64(len(data)) - 1},
1850 {1 << 33, io.SeekStart, 1 << 33},
1851 {1 << 33, io.SeekEnd, 1<<33 + int64(len(data))},
1852
1853
1854 {1<<32 - 1, io.SeekStart, 1<<32 - 1},
1855 {0, io.SeekCurrent, 1<<32 - 1},
1856 {2<<32 - 1, io.SeekStart, 2<<32 - 1},
1857 {0, io.SeekCurrent, 2<<32 - 1},
1858 }
1859 for i, tt := range tests {
1860 off, err := f.Seek(tt.in, tt.whence)
1861 if off != tt.out || err != nil {
1862 t.Errorf("#%d: Seek(%v, %v) = %v, %v want %v, nil", i, tt.in, tt.whence, off, err, tt.out)
1863 }
1864 }
1865 }
1866
1867 func TestSeekError(t *testing.T) {
1868 switch runtime.GOOS {
1869 case "js", "plan9", "wasip1":
1870 t.Skipf("skipping test on %v", runtime.GOOS)
1871 }
1872 t.Parallel()
1873
1874 r, w, err := Pipe()
1875 if err != nil {
1876 t.Fatal(err)
1877 }
1878 _, err = r.Seek(0, 0)
1879 if err == nil {
1880 t.Fatal("Seek on pipe should fail")
1881 }
1882 if perr, ok := err.(*PathError); !ok || perr.Err != syscall.ESPIPE {
1883 t.Errorf("Seek returned error %v, want &PathError{Err: syscall.ESPIPE}", err)
1884 }
1885 _, err = w.Seek(0, 0)
1886 if err == nil {
1887 t.Fatal("Seek on pipe should fail")
1888 }
1889 if perr, ok := err.(*PathError); !ok || perr.Err != syscall.ESPIPE {
1890 t.Errorf("Seek returned error %v, want &PathError{Err: syscall.ESPIPE}", err)
1891 }
1892 }
1893
1894 func TestOpenError(t *testing.T) {
1895 t.Parallel()
1896 dir := makefs(t, []string{
1897 "is-a-file",
1898 "is-a-dir/",
1899 })
1900 t.Run("NoRoot", func(t *testing.T) { testOpenError(t, dir, false) })
1901 t.Run("InRoot", func(t *testing.T) { testOpenError(t, dir, true) })
1902 }
1903 func testOpenError(t *testing.T, dir string, rooted bool) {
1904 t.Parallel()
1905 var r *Root
1906 if rooted {
1907 var err error
1908 r, err = OpenRoot(dir)
1909 if err != nil {
1910 t.Fatal(err)
1911 }
1912 defer r.Close()
1913 }
1914 for _, tt := range []struct {
1915 path string
1916 mode int
1917 error error
1918 }{{
1919 "no-such-file",
1920 O_RDONLY,
1921 syscall.ENOENT,
1922 }, {
1923 "is-a-dir",
1924 O_WRONLY,
1925 syscall.EISDIR,
1926 }, {
1927 "is-a-file/no-such-file",
1928 O_WRONLY,
1929 syscall.ENOTDIR,
1930 }} {
1931 var f *File
1932 var err error
1933 var name string
1934 if rooted {
1935 name = fmt.Sprintf("Root(%q).OpenFile(%q, %d)", dir, tt.path, tt.mode)
1936 f, err = r.OpenFile(tt.path, tt.mode, 0)
1937 } else {
1938 path := filepath.Join(dir, tt.path)
1939 name = fmt.Sprintf("OpenFile(%q, %d)", path, tt.mode)
1940 f, err = OpenFile(path, tt.mode, 0)
1941 }
1942 if err == nil {
1943 t.Errorf("%v succeeded", name)
1944 f.Close()
1945 continue
1946 }
1947 perr, ok := err.(*PathError)
1948 if !ok {
1949 t.Errorf("%v returns error of %T type; want *PathError", name, err)
1950 }
1951 if perr.Err != tt.error {
1952 if runtime.GOOS == "plan9" {
1953 syscallErrStr := perr.Err.Error()
1954 expectedErrStr := strings.Replace(tt.error.Error(), "file ", "", 1)
1955 if !strings.HasSuffix(syscallErrStr, expectedErrStr) {
1956
1957
1958
1959 if tt.error == syscall.EISDIR &&
1960 (strings.HasSuffix(syscallErrStr, syscall.EPERM.Error()) ||
1961 strings.HasSuffix(syscallErrStr, syscall.EACCES.Error())) {
1962 continue
1963 }
1964 t.Errorf("%v = _, %q; want suffix %q", name, syscallErrStr, expectedErrStr)
1965 }
1966 continue
1967 }
1968 if runtime.GOOS == "dragonfly" {
1969
1970
1971 if tt.error == syscall.EISDIR && perr.Err == syscall.EACCES {
1972 continue
1973 }
1974 }
1975 t.Errorf("%v = _, %q; want %q", name, perr.Err.Error(), tt.error.Error())
1976 }
1977 }
1978 }
1979
1980 func TestOpenNoName(t *testing.T) {
1981 f, err := Open("")
1982 if err == nil {
1983 f.Close()
1984 t.Fatal(`Open("") succeeded`)
1985 }
1986 }
1987
1988 func runBinHostname(t *testing.T) string {
1989
1990 r, w, err := Pipe()
1991 if err != nil {
1992 t.Fatal(err)
1993 }
1994 defer r.Close()
1995
1996 path, err := exec.LookPath("hostname")
1997 if err != nil {
1998 if errors.Is(err, exec.ErrNotFound) {
1999 t.Skip("skipping test; test requires hostname but it does not exist")
2000 }
2001 t.Fatal(err)
2002 }
2003
2004 argv := []string{"hostname"}
2005 if runtime.GOOS == "aix" {
2006 argv = []string{"hostname", "-s"}
2007 }
2008 p, err := StartProcess(path, argv, &ProcAttr{Files: []*File{nil, w, Stderr}})
2009 if err != nil {
2010 t.Fatal(err)
2011 }
2012 w.Close()
2013
2014 var b strings.Builder
2015 io.Copy(&b, r)
2016 _, err = p.Wait()
2017 if err != nil {
2018 t.Fatalf("run hostname Wait: %v", err)
2019 }
2020 err = p.Kill()
2021 if err == nil {
2022 t.Errorf("expected an error from Kill running 'hostname'")
2023 }
2024 output := b.String()
2025 if n := len(output); n > 0 && output[n-1] == '\n' {
2026 output = output[0 : n-1]
2027 }
2028 if output == "" {
2029 t.Fatalf("/bin/hostname produced no output")
2030 }
2031
2032 return output
2033 }
2034
2035 func testWindowsHostname(t *testing.T, hostname string) {
2036 cmd := testenv.Command(t, "hostname")
2037 out, err := cmd.Output()
2038 if err != nil {
2039 t.Fatalf("Failed to execute hostname command: %v %s", err, out)
2040 }
2041 want := strings.Trim(string(out), "\r\n")
2042 if hostname != want {
2043 t.Fatalf("Hostname() = %q != system hostname of %q", hostname, want)
2044 }
2045 }
2046
2047 func TestHostname(t *testing.T) {
2048 t.Parallel()
2049
2050 hostname, err := Hostname()
2051 if err != nil {
2052 t.Fatal(err)
2053 }
2054 if hostname == "" {
2055 t.Fatal("Hostname returned empty string and no error")
2056 }
2057 if strings.Contains(hostname, "\x00") {
2058 t.Fatalf("unexpected zero byte in hostname: %q", hostname)
2059 }
2060
2061
2062
2063 switch runtime.GOOS {
2064 case "android", "plan9":
2065
2066 return
2067 case "windows":
2068 testWindowsHostname(t, hostname)
2069 return
2070 }
2071
2072 testenv.MustHaveExec(t)
2073
2074
2075
2076
2077 want := runBinHostname(t)
2078 if hostname != want {
2079 host, _, ok := strings.Cut(hostname, ".")
2080 if !ok || host != want {
2081 t.Errorf("Hostname() = %q, want %q", hostname, want)
2082 }
2083 }
2084 }
2085
2086 func TestReadAt(t *testing.T) {
2087 t.Parallel()
2088
2089 f := newFile(t)
2090
2091 const data = "hello, world\n"
2092 io.WriteString(f, data)
2093
2094 b := make([]byte, 5)
2095 n, err := f.ReadAt(b, 7)
2096 if err != nil || n != len(b) {
2097 t.Fatalf("ReadAt 7: %d, %v", n, err)
2098 }
2099 if string(b) != "world" {
2100 t.Fatalf("ReadAt 7: have %q want %q", string(b), "world")
2101 }
2102 }
2103
2104
2105
2106
2107
2108 func TestReadAtOffset(t *testing.T) {
2109 t.Parallel()
2110
2111 f := newFile(t)
2112
2113 const data = "hello, world\n"
2114 io.WriteString(f, data)
2115
2116 f.Seek(0, 0)
2117 b := make([]byte, 5)
2118
2119 n, err := f.ReadAt(b, 7)
2120 if err != nil || n != len(b) {
2121 t.Fatalf("ReadAt 7: %d, %v", n, err)
2122 }
2123 if string(b) != "world" {
2124 t.Fatalf("ReadAt 7: have %q want %q", string(b), "world")
2125 }
2126
2127 n, err = f.Read(b)
2128 if err != nil || n != len(b) {
2129 t.Fatalf("Read: %d, %v", n, err)
2130 }
2131 if string(b) != "hello" {
2132 t.Fatalf("Read: have %q want %q", string(b), "hello")
2133 }
2134 }
2135
2136
2137 func TestReadAtNegativeOffset(t *testing.T) {
2138 t.Parallel()
2139
2140 f := newFile(t)
2141
2142 const data = "hello, world\n"
2143 io.WriteString(f, data)
2144
2145 f.Seek(0, 0)
2146 b := make([]byte, 5)
2147
2148 n, err := f.ReadAt(b, -10)
2149
2150 const wantsub = "negative offset"
2151 if !strings.Contains(fmt.Sprint(err), wantsub) || n != 0 {
2152 t.Errorf("ReadAt(-10) = %v, %v; want 0, ...%q...", n, err, wantsub)
2153 }
2154 }
2155
2156 func TestWriteAt(t *testing.T) {
2157 t.Parallel()
2158
2159 f := newFile(t)
2160
2161 const data = "hello, world"
2162 io.WriteString(f, data)
2163
2164 n, err := f.WriteAt([]byte("WOR"), 7)
2165 if err != nil || n != 3 {
2166 t.Fatalf("WriteAt 7: %d, %v", n, err)
2167 }
2168 n, err = io.WriteString(f, "!")
2169 if err != nil || n != 1 {
2170 t.Fatal(err)
2171 }
2172
2173 got, err := ReadFile(f.Name())
2174 if err != nil {
2175 t.Fatalf("ReadFile %s: %v", f.Name(), err)
2176 }
2177 want := "hello, WORld!"
2178 if string(got) != want {
2179 t.Fatalf("after write: have %q want %q", string(got), want)
2180 }
2181 }
2182
2183 func TestWriteAtConcurrent(t *testing.T) {
2184 t.Parallel()
2185
2186 f := newFile(t)
2187 io.WriteString(f, "0000000000")
2188
2189 var wg sync.WaitGroup
2190 for i := range 10 {
2191 wg.Add(1)
2192 go func() {
2193 defer wg.Done()
2194 n, err := f.WriteAt([]byte(strconv.Itoa(i)), int64(i))
2195 if err != nil || n != 1 {
2196 t.Errorf("WriteAt %d: %d, %v", i, n, err)
2197 }
2198 n, err = io.WriteString(f, "!")
2199 if err != nil || n != 1 {
2200 t.Error(err)
2201 }
2202 }()
2203 }
2204 wg.Wait()
2205
2206 got, err := ReadFile(f.Name())
2207 if err != nil {
2208 t.Fatalf("ReadFile %s: %v", f.Name(), err)
2209 }
2210 want := "0123456789!!!!!!!!!!"
2211 if string(got) != want {
2212 t.Fatalf("after write: have %q want %q", string(got), want)
2213 }
2214 }
2215
2216
2217 func TestWriteAtNegativeOffset(t *testing.T) {
2218 t.Parallel()
2219
2220 f := newFile(t)
2221
2222 n, err := f.WriteAt([]byte("WORLD"), -10)
2223
2224 const wantsub = "negative offset"
2225 if !strings.Contains(fmt.Sprint(err), wantsub) || n != 0 {
2226 t.Errorf("WriteAt(-10) = %v, %v; want 0, ...%q...", n, err, wantsub)
2227 }
2228 }
2229
2230
2231 func TestWriteAtInAppendMode(t *testing.T) {
2232 t.Chdir(t.TempDir())
2233 f, err := OpenFile("write_at_in_append_mode.txt", O_APPEND|O_CREATE, 0666)
2234 if err != nil {
2235 t.Fatalf("OpenFile: %v", err)
2236 }
2237 defer f.Close()
2238
2239 _, err = f.WriteAt([]byte(""), 1)
2240 if err != ErrWriteAtInAppendMode {
2241 t.Fatalf("f.WriteAt returned %v, expected %v", err, ErrWriteAtInAppendMode)
2242 }
2243 }
2244
2245 func writeFile(t *testing.T, r *Root, fname string, flag int, text string) string {
2246 t.Helper()
2247 var f *File
2248 var err error
2249 if r == nil {
2250 f, err = OpenFile(fname, flag, 0666)
2251 } else {
2252 f, err = r.OpenFile(fname, flag, 0666)
2253 }
2254 if err != nil {
2255 t.Fatalf("Open: %v", err)
2256 }
2257 n, err := io.WriteString(f, text)
2258 if err != nil {
2259 t.Fatalf("WriteString: %d, %v", n, err)
2260 }
2261 f.Close()
2262 data, err := ReadFile(fname)
2263 if err != nil {
2264 t.Fatalf("ReadFile: %v", err)
2265 }
2266 return string(data)
2267 }
2268
2269 func TestAppend(t *testing.T) {
2270 testMaybeRooted(t, func(t *testing.T, r *Root) {
2271 const f = "append.txt"
2272 s := writeFile(t, r, f, O_CREATE|O_TRUNC|O_RDWR, "new")
2273 if s != "new" {
2274 t.Fatalf("writeFile: have %q want %q", s, "new")
2275 }
2276 s = writeFile(t, r, f, O_APPEND|O_RDWR, "|append")
2277 if s != "new|append" {
2278 t.Fatalf("writeFile: have %q want %q", s, "new|append")
2279 }
2280 s = writeFile(t, r, f, O_CREATE|O_APPEND|O_RDWR, "|append")
2281 if s != "new|append|append" {
2282 t.Fatalf("writeFile: have %q want %q", s, "new|append|append")
2283 }
2284 err := Remove(f)
2285 if err != nil {
2286 t.Fatalf("Remove: %v", err)
2287 }
2288 s = writeFile(t, r, f, O_CREATE|O_APPEND|O_RDWR, "new&append")
2289 if s != "new&append" {
2290 t.Fatalf("writeFile: after append have %q want %q", s, "new&append")
2291 }
2292 s = writeFile(t, r, f, O_CREATE|O_RDWR, "old")
2293 if s != "old&append" {
2294 t.Fatalf("writeFile: after create have %q want %q", s, "old&append")
2295 }
2296 s = writeFile(t, r, f, O_CREATE|O_TRUNC|O_RDWR, "new")
2297 if s != "new" {
2298 t.Fatalf("writeFile: after truncate have %q want %q", s, "new")
2299 }
2300 })
2301 }
2302
2303
2304 func TestFilePermissions(t *testing.T) {
2305 if Getuid() == 0 {
2306 t.Skip("skipping test when running as root")
2307 }
2308 for _, test := range []struct {
2309 name string
2310 mode FileMode
2311 }{
2312 {"r", 0o444},
2313 {"w", 0o222},
2314 {"rw", 0o666},
2315 } {
2316 t.Run(test.name, func(t *testing.T) {
2317 switch runtime.GOOS {
2318 case "windows":
2319 if test.mode&0444 == 0 {
2320 t.Skip("write-only files not supported on " + runtime.GOOS)
2321 }
2322 case "wasip1":
2323 t.Skip("file permissions not supported on " + runtime.GOOS)
2324 }
2325 testMaybeRooted(t, func(t *testing.T, r *Root) {
2326 const filename = "f"
2327 var f *File
2328 var err error
2329 if r == nil {
2330 f, err = OpenFile(filename, O_RDWR|O_CREATE|O_EXCL, test.mode)
2331 } else {
2332 f, err = r.OpenFile(filename, O_RDWR|O_CREATE|O_EXCL, test.mode)
2333 }
2334 if err != nil {
2335 t.Fatal(err)
2336 }
2337 f.Close()
2338 b, err := ReadFile(filename)
2339 if test.mode&0o444 != 0 {
2340 if err != nil {
2341 t.Errorf("ReadFile = %v; want success", err)
2342 }
2343 } else {
2344 if err == nil {
2345 t.Errorf("ReadFile = %q, <nil>; want failure", string(b))
2346 }
2347 }
2348 _, err = Stat(filename)
2349 if err != nil {
2350 t.Errorf("Stat = %v; want success", err)
2351 }
2352 err = WriteFile(filename, nil, 0666)
2353 if test.mode&0o222 != 0 {
2354 if err != nil {
2355 t.Errorf("WriteFile = %v; want success", err)
2356 b, err := ReadFile(filename)
2357 t.Errorf("ReadFile: %v", err)
2358 t.Errorf("file contents: %q", b)
2359 }
2360 } else {
2361 if err == nil {
2362 t.Errorf("WriteFile(%q) = <nil>; want failure", filename)
2363 st, err := Stat(filename)
2364 if err == nil {
2365 t.Errorf("mode: %s", st.Mode())
2366 }
2367 b, err := ReadFile(filename)
2368 t.Errorf("ReadFile: %v", err)
2369 t.Errorf("file contents: %q", b)
2370 }
2371 }
2372 })
2373 })
2374 }
2375
2376 }
2377
2378 func TestOpenFileCreateExclDanglingSymlink(t *testing.T) {
2379 testenv.MustHaveSymlink(t)
2380 testMaybeRooted(t, func(t *testing.T, r *Root) {
2381 const link = "link"
2382 if err := Symlink("does_not_exist", link); err != nil {
2383 t.Fatal(err)
2384 }
2385 var f *File
2386 var err error
2387 if r == nil {
2388 f, err = OpenFile(link, O_WRONLY|O_CREATE|O_EXCL, 0o444)
2389 } else {
2390 f, err = r.OpenFile(link, O_WRONLY|O_CREATE|O_EXCL, 0o444)
2391 }
2392 if err == nil {
2393 f.Close()
2394 }
2395 if !errors.Is(err, ErrExist) {
2396 t.Errorf("OpenFile of a dangling symlink with O_CREATE|O_EXCL = %v, want ErrExist", err)
2397 }
2398 if _, err := Stat(link); err == nil {
2399 t.Errorf("OpenFile of a dangling symlink with O_CREATE|O_EXCL created a file")
2400 }
2401 })
2402 }
2403
2404
2405 func TestFileRDWRFlags(t *testing.T) {
2406 for _, test := range []struct {
2407 name string
2408 flag int
2409 }{
2410 {"O_RDONLY", O_RDONLY},
2411 {"O_WRONLY", O_WRONLY},
2412 {"O_RDWR", O_RDWR},
2413 } {
2414 t.Run(test.name, func(t *testing.T) {
2415 testMaybeRooted(t, func(t *testing.T, r *Root) {
2416 const filename = "f"
2417 content := []byte("content")
2418 if err := WriteFile(filename, content, 0666); err != nil {
2419 t.Fatal(err)
2420 }
2421 var f *File
2422 var err error
2423 if r == nil {
2424 f, err = OpenFile(filename, test.flag, 0)
2425 } else {
2426 f, err = r.OpenFile(filename, test.flag, 0)
2427 }
2428 if err != nil {
2429 t.Fatal(err)
2430 }
2431 defer f.Close()
2432 got, err := io.ReadAll(f)
2433 if test.flag == O_WRONLY {
2434 if err == nil {
2435 t.Errorf("read file: %q, %v; want error", got, err)
2436 }
2437 } else {
2438 if err != nil || !bytes.Equal(got, content) {
2439 t.Errorf("read file: %q, %v; want %q, <nil>", got, err, content)
2440 }
2441 }
2442 if _, err := f.Seek(0, 0); err != nil {
2443 t.Fatalf("f.Seek: %v", err)
2444 }
2445 newcontent := []byte("CONTENT")
2446 _, err = f.Write(newcontent)
2447 if test.flag == O_RDONLY {
2448 if err == nil {
2449 t.Errorf("write file: succeeded, want error")
2450 }
2451 } else {
2452 if err != nil {
2453 t.Errorf("write file: %v, want success", err)
2454 }
2455 }
2456 f.Close()
2457 got, err = ReadFile(filename)
2458 if err != nil {
2459 t.Fatal(err)
2460 }
2461 want := content
2462 if test.flag != O_RDONLY {
2463 want = newcontent
2464 }
2465 if !bytes.Equal(got, want) {
2466 t.Fatalf("after write, file contains %q, want %q", got, want)
2467 }
2468 })
2469 })
2470 }
2471 }
2472
2473 func TestStatDirWithTrailingSlash(t *testing.T) {
2474 t.Parallel()
2475
2476
2477 path := t.TempDir()
2478
2479
2480 if _, err := Stat(path); err != nil {
2481 t.Fatalf("stat %s failed: %s", path, err)
2482 }
2483
2484
2485 path += "/"
2486 if _, err := Stat(path); err != nil {
2487 t.Fatalf("stat %s failed: %s", path, err)
2488 }
2489 }
2490
2491 func TestNilProcessStateString(t *testing.T) {
2492 var ps *ProcessState
2493 s := ps.String()
2494 if s != "<nil>" {
2495 t.Errorf("(*ProcessState)(nil).String() = %q, want %q", s, "<nil>")
2496 }
2497 }
2498
2499 func TestSameFile(t *testing.T) {
2500 t.Chdir(t.TempDir())
2501 fa, err := Create("a")
2502 if err != nil {
2503 t.Fatalf("Create(a): %v", err)
2504 }
2505 fa.Close()
2506 fb, err := Create("b")
2507 if err != nil {
2508 t.Fatalf("Create(b): %v", err)
2509 }
2510 fb.Close()
2511
2512 ia1, err := Stat("a")
2513 if err != nil {
2514 t.Fatalf("Stat(a): %v", err)
2515 }
2516 ia2, err := Stat("a")
2517 if err != nil {
2518 t.Fatalf("Stat(a): %v", err)
2519 }
2520 if !SameFile(ia1, ia2) {
2521 t.Errorf("files should be same")
2522 }
2523
2524 ib, err := Stat("b")
2525 if err != nil {
2526 t.Fatalf("Stat(b): %v", err)
2527 }
2528 if SameFile(ia1, ib) {
2529 t.Errorf("files should be different")
2530 }
2531 }
2532
2533 func testDevNullFileInfo(t *testing.T, statname, devNullName string, fi FileInfo) {
2534 pre := fmt.Sprintf("%s(%q): ", statname, devNullName)
2535 if fi.Size() != 0 {
2536 t.Errorf(pre+"wrong file size have %d want 0", fi.Size())
2537 }
2538 if fi.Mode()&ModeDevice == 0 {
2539 t.Errorf(pre+"wrong file mode %q: ModeDevice is not set", fi.Mode())
2540 }
2541 if fi.Mode()&ModeCharDevice == 0 {
2542 t.Errorf(pre+"wrong file mode %q: ModeCharDevice is not set", fi.Mode())
2543 }
2544 if fi.Mode().IsRegular() {
2545 t.Errorf(pre+"wrong file mode %q: IsRegular returns true", fi.Mode())
2546 }
2547 }
2548
2549 func testDevNullFile(t *testing.T, devNullName string) {
2550 f, err := Open(devNullName)
2551 if err != nil {
2552 t.Fatalf("Open(%s): %v", devNullName, err)
2553 }
2554 defer f.Close()
2555
2556 fi, err := f.Stat()
2557 if err != nil {
2558 t.Fatalf("Stat(%s): %v", devNullName, err)
2559 }
2560 testDevNullFileInfo(t, "f.Stat", devNullName, fi)
2561
2562 fi, err = Stat(devNullName)
2563 if err != nil {
2564 t.Fatalf("Stat(%s): %v", devNullName, err)
2565 }
2566 testDevNullFileInfo(t, "Stat", devNullName, fi)
2567 }
2568
2569 func TestDevNullFile(t *testing.T) {
2570 t.Parallel()
2571
2572 testDevNullFile(t, DevNull)
2573 if runtime.GOOS == "windows" {
2574 testDevNullFile(t, "./nul")
2575 testDevNullFile(t, "//./nul")
2576 }
2577 }
2578
2579 var testLargeWrite = flag.Bool("large_write", false, "run TestLargeWriteToConsole test that floods console with output")
2580
2581 func TestLargeWriteToConsole(t *testing.T) {
2582 if !*testLargeWrite {
2583 t.Skip("skipping console-flooding test; enable with -large_write")
2584 }
2585 b := make([]byte, 32000)
2586 for i := range b {
2587 b[i] = '.'
2588 }
2589 b[len(b)-1] = '\n'
2590 n, err := Stdout.Write(b)
2591 if err != nil {
2592 t.Fatalf("Write to os.Stdout failed: %v", err)
2593 }
2594 if n != len(b) {
2595 t.Errorf("Write to os.Stdout should return %d; got %d", len(b), n)
2596 }
2597 n, err = Stderr.Write(b)
2598 if err != nil {
2599 t.Fatalf("Write to os.Stderr failed: %v", err)
2600 }
2601 if n != len(b) {
2602 t.Errorf("Write to os.Stderr should return %d; got %d", len(b), n)
2603 }
2604 }
2605
2606 func TestStatDirModeExec(t *testing.T) {
2607 if runtime.GOOS == "wasip1" {
2608 t.Skip("Chmod is not supported on " + runtime.GOOS)
2609 }
2610 t.Parallel()
2611
2612 const mode = 0111
2613
2614 path := t.TempDir()
2615 if err := Chmod(path, 0777); err != nil {
2616 t.Fatalf("Chmod %q 0777: %v", path, err)
2617 }
2618
2619 dir, err := Stat(path)
2620 if err != nil {
2621 t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err)
2622 }
2623 if dir.Mode()&mode != mode {
2624 t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode()&mode, mode)
2625 }
2626 }
2627
2628 func TestStatStdin(t *testing.T) {
2629 switch runtime.GOOS {
2630 case "android", "plan9":
2631 t.Skipf("%s doesn't have /bin/sh", runtime.GOOS)
2632 }
2633
2634 if Getenv("GO_WANT_HELPER_PROCESS") == "1" {
2635 st, err := Stdin.Stat()
2636 if err != nil {
2637 t.Fatalf("Stat failed: %v", err)
2638 }
2639 fmt.Println(st.Mode() & ModeNamedPipe)
2640 Exit(0)
2641 }
2642
2643 t.Parallel()
2644 exe := testenv.Executable(t)
2645
2646 fi, err := Stdin.Stat()
2647 if err != nil {
2648 t.Fatal(err)
2649 }
2650 switch mode := fi.Mode(); {
2651 case mode&ModeCharDevice != 0 && mode&ModeDevice != 0:
2652 case mode&ModeNamedPipe != 0:
2653 default:
2654 t.Fatalf("unexpected Stdin mode (%v), want ModeCharDevice or ModeNamedPipe", mode)
2655 }
2656
2657 cmd := testenv.Command(t, exe, "-test.run=^TestStatStdin$")
2658 cmd = testenv.CleanCmdEnv(cmd)
2659 cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
2660
2661 cmd.Stdin = strings.NewReader("output")
2662
2663 output, err := cmd.CombinedOutput()
2664 if err != nil {
2665 t.Fatalf("Failed to spawn child process: %v %q", err, string(output))
2666 }
2667
2668
2669 if len(output) < 1 || output[0] != 'p' {
2670 t.Fatalf("Child process reports stdin is not pipe '%v'", string(output))
2671 }
2672 }
2673
2674 func TestStatRelativeSymlink(t *testing.T) {
2675 testenv.MustHaveSymlink(t)
2676 t.Parallel()
2677
2678 tmpdir := t.TempDir()
2679 target := filepath.Join(tmpdir, "target")
2680 f, err := Create(target)
2681 if err != nil {
2682 t.Fatal(err)
2683 }
2684 defer f.Close()
2685
2686 st, err := f.Stat()
2687 if err != nil {
2688 t.Fatal(err)
2689 }
2690
2691 link := filepath.Join(tmpdir, "link")
2692 err = Symlink(filepath.Base(target), link)
2693 if err != nil {
2694 t.Fatal(err)
2695 }
2696
2697 st1, err := Stat(link)
2698 if err != nil {
2699 t.Fatal(err)
2700 }
2701
2702 if !SameFile(st, st1) {
2703 t.Error("Stat doesn't follow relative symlink")
2704 }
2705
2706 if runtime.GOOS == "windows" {
2707 Remove(link)
2708 err = Symlink(target[len(filepath.VolumeName(target)):], link)
2709 if err != nil {
2710 t.Fatal(err)
2711 }
2712
2713 st1, err := Stat(link)
2714 if err != nil {
2715 t.Fatal(err)
2716 }
2717
2718 if !SameFile(st, st1) {
2719 t.Error("Stat doesn't follow relative symlink")
2720 }
2721 }
2722 }
2723
2724 func TestReadAtEOF(t *testing.T) {
2725 t.Parallel()
2726
2727 f := newFile(t)
2728
2729 _, err := f.ReadAt(make([]byte, 10), 0)
2730 switch err {
2731 case io.EOF:
2732
2733 case nil:
2734 t.Fatalf("ReadAt succeeded")
2735 default:
2736 t.Fatalf("ReadAt failed: %s", err)
2737 }
2738 }
2739
2740 func TestLongPath(t *testing.T) {
2741 t.Parallel()
2742
2743 tmpdir := t.TempDir()
2744
2745
2746 sizes := []int{247, 248, 249, 400}
2747 for len(tmpdir) < 400 {
2748 tmpdir += "/dir3456789"
2749 }
2750 for _, sz := range sizes {
2751 t.Run(fmt.Sprintf("length=%d", sz), func(t *testing.T) {
2752 sizedTempDir := tmpdir[:sz-1] + "x"
2753
2754
2755
2756 if err := MkdirAll(sizedTempDir, 0755); err != nil {
2757 t.Fatalf("MkdirAll failed: %v", err)
2758 }
2759 data := []byte("hello world\n")
2760 if err := WriteFile(sizedTempDir+"/foo.txt", data, 0644); err != nil {
2761 t.Fatalf("os.WriteFile() failed: %v", err)
2762 }
2763 if err := Rename(sizedTempDir+"/foo.txt", sizedTempDir+"/bar.txt"); err != nil {
2764 t.Fatalf("Rename failed: %v", err)
2765 }
2766 mtime := time.Now().Truncate(time.Minute)
2767 if err := Chtimes(sizedTempDir+"/bar.txt", mtime, mtime); err != nil {
2768 t.Fatalf("Chtimes failed: %v", err)
2769 }
2770 names := []string{"bar.txt"}
2771 if testenv.HasSymlink() {
2772 if err := Symlink(sizedTempDir+"/bar.txt", sizedTempDir+"/symlink.txt"); err != nil {
2773 t.Fatalf("Symlink failed: %v", err)
2774 }
2775 names = append(names, "symlink.txt")
2776 }
2777 if testenv.HasLink() {
2778 if err := Link(sizedTempDir+"/bar.txt", sizedTempDir+"/link.txt"); err != nil {
2779 t.Fatalf("Link failed: %v", err)
2780 }
2781 names = append(names, "link.txt")
2782 }
2783 for _, wantSize := range []int64{int64(len(data)), 0} {
2784 for _, name := range names {
2785 path := sizedTempDir + "/" + name
2786 dir, err := Stat(path)
2787 if err != nil {
2788 t.Fatalf("Stat(%q) failed: %v", path, err)
2789 }
2790 filesize := size(path, t)
2791 if dir.Size() != filesize || filesize != wantSize {
2792 t.Errorf("Size(%q) is %d, len(ReadFile()) is %d, want %d", path, dir.Size(), filesize, wantSize)
2793 }
2794 if runtime.GOOS != "wasip1" {
2795 err = Chmod(path, dir.Mode())
2796 if err != nil {
2797 t.Fatalf("Chmod(%q) failed: %v", path, err)
2798 }
2799 }
2800 }
2801 if err := Truncate(sizedTempDir+"/bar.txt", 0); err != nil {
2802 t.Fatalf("Truncate failed: %v", err)
2803 }
2804 }
2805 })
2806 }
2807 }
2808
2809 func testKillProcess(t *testing.T, processKiller func(p *Process)) {
2810 t.Parallel()
2811
2812
2813 cmd := testenv.Command(t, testenv.Executable(t))
2814 cmd.Env = append(cmd.Environ(), "GO_OS_TEST_DRAIN_STDIN=1")
2815 stdout, err := cmd.StdoutPipe()
2816 if err != nil {
2817 t.Fatal(err)
2818 }
2819 stdin, err := cmd.StdinPipe()
2820 if err != nil {
2821 t.Fatal(err)
2822 }
2823 err = cmd.Start()
2824 if err != nil {
2825 t.Fatalf("Failed to start test process: %v", err)
2826 }
2827
2828 defer func() {
2829 if err := cmd.Wait(); err == nil {
2830 t.Errorf("Test process succeeded, but expected to fail")
2831 }
2832 stdin.Close()
2833 }()
2834
2835
2836
2837 io.Copy(io.Discard, stdout)
2838
2839 processKiller(cmd.Process)
2840 }
2841
2842 func TestKillStartProcess(t *testing.T) {
2843 testKillProcess(t, func(p *Process) {
2844 err := p.Kill()
2845 if err != nil {
2846 t.Fatalf("Failed to kill test process: %v", err)
2847 }
2848 })
2849 }
2850
2851 func TestGetppid(t *testing.T) {
2852 if runtime.GOOS == "plan9" {
2853
2854 t.Skipf("skipping test on plan9; see issue 8206")
2855 }
2856
2857 if Getenv("GO_WANT_HELPER_PROCESS") == "1" {
2858 fmt.Print(Getppid())
2859 Exit(0)
2860 }
2861
2862 t.Parallel()
2863
2864 cmd := testenv.Command(t, testenv.Executable(t), "-test.run=^TestGetppid$")
2865 cmd.Env = append(Environ(), "GO_WANT_HELPER_PROCESS=1")
2866
2867
2868 output, err := cmd.CombinedOutput()
2869 if err != nil {
2870 t.Fatalf("Failed to spawn child process: %v %q", err, string(output))
2871 }
2872
2873 childPpid := string(output)
2874 ourPid := fmt.Sprintf("%d", Getpid())
2875 if childPpid != ourPid {
2876 t.Fatalf("Child process reports parent process id '%v', expected '%v'", childPpid, ourPid)
2877 }
2878 }
2879
2880 func TestKillFindProcess(t *testing.T) {
2881 testKillProcess(t, func(p *Process) {
2882 p2, err := FindProcess(p.Pid)
2883 if err != nil {
2884 t.Fatalf("Failed to find test process: %v", err)
2885 }
2886 err = p2.Kill()
2887 if err != nil {
2888 t.Fatalf("Failed to kill test process: %v", err)
2889 }
2890 })
2891 }
2892
2893 var nilFileMethodTests = []struct {
2894 name string
2895 f func(*File) error
2896 }{
2897 {"Chdir", func(f *File) error { return f.Chdir() }},
2898 {"Close", func(f *File) error { return f.Close() }},
2899 {"Chmod", func(f *File) error { return f.Chmod(0) }},
2900 {"Chown", func(f *File) error { return f.Chown(0, 0) }},
2901 {"Read", func(f *File) error { _, err := f.Read(make([]byte, 0)); return err }},
2902 {"ReadAt", func(f *File) error { _, err := f.ReadAt(make([]byte, 0), 0); return err }},
2903 {"Readdir", func(f *File) error { _, err := f.Readdir(1); return err }},
2904 {"Readdirnames", func(f *File) error { _, err := f.Readdirnames(1); return err }},
2905 {"Seek", func(f *File) error { _, err := f.Seek(0, io.SeekStart); return err }},
2906 {"Stat", func(f *File) error { _, err := f.Stat(); return err }},
2907 {"Sync", func(f *File) error { return f.Sync() }},
2908 {"Truncate", func(f *File) error { return f.Truncate(0) }},
2909 {"Write", func(f *File) error { _, err := f.Write(make([]byte, 0)); return err }},
2910 {"WriteAt", func(f *File) error { _, err := f.WriteAt(make([]byte, 0), 0); return err }},
2911 {"WriteString", func(f *File) error { _, err := f.WriteString(""); return err }},
2912 }
2913
2914
2915 func TestNilFileMethods(t *testing.T) {
2916 t.Parallel()
2917
2918 for _, tt := range nilFileMethodTests {
2919 var file *File
2920 got := tt.f(file)
2921 if got != ErrInvalid {
2922 t.Errorf("%v should fail when f is nil; got %v", tt.name, got)
2923 }
2924 }
2925 }
2926
2927 func mkdirTree(t *testing.T, root string, level, max int) {
2928 if level >= max {
2929 return
2930 }
2931 level++
2932 for i := 'a'; i < 'c'; i++ {
2933 dir := filepath.Join(root, string(i))
2934 if err := Mkdir(dir, 0700); err != nil {
2935 t.Fatal(err)
2936 }
2937 mkdirTree(t, dir, level, max)
2938 }
2939 }
2940
2941
2942
2943 func TestRemoveAllRace(t *testing.T) {
2944 if runtime.GOOS == "windows" {
2945
2946
2947
2948
2949 t.Skip("skipping on windows")
2950 }
2951 if runtime.GOOS == "dragonfly" {
2952 testenv.SkipFlaky(t, 52301)
2953 }
2954
2955 n := runtime.GOMAXPROCS(16)
2956 defer runtime.GOMAXPROCS(n)
2957 root := t.TempDir()
2958 mkdirTree(t, root, 1, 6)
2959 hold := make(chan struct{})
2960 var wg sync.WaitGroup
2961 for i := 0; i < 4; i++ {
2962 wg.Add(1)
2963 go func() {
2964 defer wg.Done()
2965 <-hold
2966 err := RemoveAll(root)
2967 if err != nil {
2968 t.Errorf("unexpected error: %T, %q", err, err)
2969 }
2970 }()
2971 }
2972 close(hold)
2973 wg.Wait()
2974 }
2975
2976
2977 func TestPipeThreads(t *testing.T) {
2978 switch runtime.GOOS {
2979 case "aix":
2980 t.Skip("skipping on aix; issue 70131")
2981 case "illumos", "solaris":
2982 t.Skip("skipping on Solaris and illumos; issue 19111")
2983 case "windows":
2984 t.Skip("skipping on Windows; issue 19098")
2985 case "plan9":
2986 t.Skip("skipping on Plan 9; does not support runtime poller")
2987 case "js":
2988 t.Skip("skipping on js; no support for os.Pipe")
2989 case "wasip1":
2990 t.Skip("skipping on wasip1; no support for os.Pipe")
2991 }
2992
2993 threads := 100
2994
2995 r := make([]*File, threads)
2996 w := make([]*File, threads)
2997 for i := 0; i < threads; i++ {
2998 rp, wp, err := Pipe()
2999 if err != nil {
3000 for j := 0; j < i; j++ {
3001 r[j].Close()
3002 w[j].Close()
3003 }
3004 t.Fatal(err)
3005 }
3006 r[i] = rp
3007 w[i] = wp
3008 }
3009
3010 defer debug.SetMaxThreads(debug.SetMaxThreads(threads / 2))
3011
3012 creading := make(chan bool, threads)
3013 cdone := make(chan bool, threads)
3014 for i := 0; i < threads; i++ {
3015 go func(i int) {
3016 var b [1]byte
3017 creading <- true
3018 if _, err := r[i].Read(b[:]); err != nil {
3019 t.Error(err)
3020 }
3021 if err := r[i].Close(); err != nil {
3022 t.Error(err)
3023 }
3024 cdone <- true
3025 }(i)
3026 }
3027
3028 for i := 0; i < threads; i++ {
3029 <-creading
3030 }
3031
3032
3033
3034
3035 for i := 0; i < threads; i++ {
3036 if _, err := w[i].Write([]byte{0}); err != nil {
3037 t.Error(err)
3038 }
3039 if err := w[i].Close(); err != nil {
3040 t.Error(err)
3041 }
3042 <-cdone
3043 }
3044 }
3045
3046 func testDoubleCloseError(path string) func(*testing.T) {
3047 return func(t *testing.T) {
3048 t.Parallel()
3049
3050 file, err := Open(path)
3051 if err != nil {
3052 t.Fatal(err)
3053 }
3054 if err := file.Close(); err != nil {
3055 t.Fatalf("unexpected error from Close: %v", err)
3056 }
3057 if err := file.Close(); err == nil {
3058 t.Error("second Close did not fail")
3059 } else if pe, ok := err.(*PathError); !ok {
3060 t.Errorf("second Close: got %T, want %T", err, pe)
3061 } else if pe.Err != ErrClosed {
3062 t.Errorf("second Close: got %q, want %q", pe.Err, ErrClosed)
3063 } else {
3064 t.Logf("second close returned expected error %q", err)
3065 }
3066 }
3067 }
3068
3069 func TestDoubleCloseError(t *testing.T) {
3070 t.Parallel()
3071 t.Run("file", testDoubleCloseError(filepath.Join(sfdir, sfname)))
3072 t.Run("dir", testDoubleCloseError(sfdir))
3073 }
3074
3075 func TestUserCacheDir(t *testing.T) {
3076 t.Parallel()
3077
3078 dir, err := UserCacheDir()
3079 if err != nil {
3080 t.Skipf("skipping: %v", err)
3081 }
3082 if dir == "" {
3083 t.Fatalf("UserCacheDir returned %q; want non-empty path or error", dir)
3084 }
3085
3086 fi, err := Stat(dir)
3087 if err != nil {
3088 if IsNotExist(err) {
3089 t.Log(err)
3090 return
3091 }
3092 t.Fatal(err)
3093 }
3094 if !fi.IsDir() {
3095 t.Fatalf("dir %s is not directory; type = %v", dir, fi.Mode())
3096 }
3097 }
3098
3099 func TestUserCacheDirXDGConfigDirEnvVar(t *testing.T) {
3100 switch runtime.GOOS {
3101 case "windows", "darwin", "plan9":
3102 t.Skip("$XDG_CACHE_HOME is effective only on Unix systems")
3103 }
3104
3105 wd, err := Getwd()
3106 if err != nil {
3107 t.Fatal(err)
3108 }
3109 t.Setenv("XDG_CACHE_HOME", wd)
3110
3111 dir, err := UserCacheDir()
3112 if err != nil {
3113 t.Fatal(err)
3114 }
3115 if dir != wd {
3116 t.Fatalf("UserCacheDir returned %q; want the value of $XDG_CACHE_HOME %q", dir, wd)
3117 }
3118
3119 t.Setenv("XDG_CACHE_HOME", "some-dir")
3120 _, err = UserCacheDir()
3121 if err == nil {
3122 t.Fatal("UserCacheDir succeeded though $XDG_CACHE_HOME contains a relative path")
3123 }
3124 }
3125
3126 func TestUserConfigDir(t *testing.T) {
3127 t.Parallel()
3128
3129 dir, err := UserConfigDir()
3130 if err != nil {
3131 t.Skipf("skipping: %v", err)
3132 }
3133 if dir == "" {
3134 t.Fatalf("UserConfigDir returned %q; want non-empty path or error", dir)
3135 }
3136
3137 fi, err := Stat(dir)
3138 if err != nil {
3139 if IsNotExist(err) {
3140 t.Log(err)
3141 return
3142 }
3143 t.Fatal(err)
3144 }
3145 if !fi.IsDir() {
3146 t.Fatalf("dir %s is not directory; type = %v", dir, fi.Mode())
3147 }
3148 }
3149
3150 func TestUserConfigDirXDGConfigDirEnvVar(t *testing.T) {
3151 switch runtime.GOOS {
3152 case "windows", "darwin", "plan9":
3153 t.Skip("$XDG_CONFIG_HOME is effective only on Unix systems")
3154 }
3155
3156 wd, err := Getwd()
3157 if err != nil {
3158 t.Fatal(err)
3159 }
3160 t.Setenv("XDG_CONFIG_HOME", wd)
3161
3162 dir, err := UserConfigDir()
3163 if err != nil {
3164 t.Fatal(err)
3165 }
3166 if dir != wd {
3167 t.Fatalf("UserConfigDir returned %q; want the value of $XDG_CONFIG_HOME %q", dir, wd)
3168 }
3169
3170 t.Setenv("XDG_CONFIG_HOME", "some-dir")
3171 _, err = UserConfigDir()
3172 if err == nil {
3173 t.Fatal("UserConfigDir succeeded though $XDG_CONFIG_HOME contains a relative path")
3174 }
3175 }
3176
3177 func TestUserHomeDir(t *testing.T) {
3178 t.Parallel()
3179
3180 dir, err := UserHomeDir()
3181 if dir == "" && err == nil {
3182 t.Fatal("UserHomeDir returned an empty string but no error")
3183 }
3184 if err != nil {
3185
3186
3187 t.Skipf("skipping: %v", err)
3188 }
3189
3190 fi, err := Stat(dir)
3191 if err != nil {
3192 if IsNotExist(err) {
3193
3194
3195
3196 t.Log(err)
3197 return
3198 }
3199 t.Fatal(err)
3200 }
3201 if !fi.IsDir() {
3202 t.Fatalf("dir %s is not directory; type = %v", dir, fi.Mode())
3203 }
3204 }
3205
3206 func TestDirSeek(t *testing.T) {
3207 t.Parallel()
3208
3209 wd, err := Getwd()
3210 if err != nil {
3211 t.Fatal(err)
3212 }
3213 f, err := Open(wd)
3214 if err != nil {
3215 t.Fatal(err)
3216 }
3217 dirnames1, err := f.Readdirnames(0)
3218 if err != nil {
3219 t.Fatal(err)
3220 }
3221
3222 ret, err := f.Seek(0, 0)
3223 if err != nil {
3224 t.Fatal(err)
3225 }
3226 if ret != 0 {
3227 t.Fatalf("seek result not zero: %d", ret)
3228 }
3229
3230 dirnames2, err := f.Readdirnames(0)
3231 if err != nil {
3232 t.Fatal(err)
3233 }
3234
3235 if len(dirnames1) != len(dirnames2) {
3236 t.Fatalf("listings have different lengths: %d and %d\n", len(dirnames1), len(dirnames2))
3237 }
3238 for i, n1 := range dirnames1 {
3239 n2 := dirnames2[i]
3240 if n1 != n2 {
3241 t.Fatalf("different name i=%d n1=%s n2=%s\n", i, n1, n2)
3242 }
3243 }
3244 }
3245
3246 func TestReaddirSmallSeek(t *testing.T) {
3247
3248
3249
3250 t.Parallel()
3251
3252 wd, err := Getwd()
3253 if err != nil {
3254 t.Fatal(err)
3255 }
3256 df, err := Open(filepath.Join(wd, "testdata", "issue37161"))
3257 if err != nil {
3258 t.Fatal(err)
3259 }
3260 names1, err := df.Readdirnames(1)
3261 if err != nil {
3262 t.Fatal(err)
3263 }
3264 if _, err = df.Seek(0, 0); err != nil {
3265 t.Fatal(err)
3266 }
3267 names2, err := df.Readdirnames(0)
3268 if err != nil {
3269 t.Fatal(err)
3270 }
3271 if len(names2) != 3 {
3272 t.Fatalf("first names: %v, second names: %v", names1, names2)
3273 }
3274 }
3275
3276
3277
3278 func isDeadlineExceeded(err error) bool {
3279 if !IsTimeout(err) {
3280 return false
3281 }
3282 if !errors.Is(err, ErrDeadlineExceeded) {
3283 return false
3284 }
3285 return true
3286 }
3287
3288
3289 func TestOpenFileKeepsPermissions(t *testing.T) {
3290 t.Run("OpenFile", func(t *testing.T) {
3291 testOpenFileKeepsPermissions(t, OpenFile)
3292 })
3293 t.Run("RootOpenFile", func(t *testing.T) {
3294 testOpenFileKeepsPermissions(t, func(name string, flag int, perm FileMode) (*File, error) {
3295 dir, file := filepath.Split(name)
3296 r, err := OpenRoot(dir)
3297 if err != nil {
3298 return nil, err
3299 }
3300 defer r.Close()
3301 return r.OpenFile(file, flag, perm)
3302 })
3303 })
3304 }
3305 func testOpenFileKeepsPermissions(t *testing.T, openf func(name string, flag int, perm FileMode) (*File, error)) {
3306 t.Parallel()
3307
3308 dir := t.TempDir()
3309 name := filepath.Join(dir, "x")
3310 f, err := Create(name)
3311 if err != nil {
3312 t.Fatal(err)
3313 }
3314 if err := f.Close(); err != nil {
3315 t.Error(err)
3316 }
3317 f, err = openf(name, O_WRONLY|O_CREATE|O_TRUNC, 0)
3318 if err != nil {
3319 t.Fatal(err)
3320 }
3321 if fi, err := f.Stat(); err != nil {
3322 t.Error(err)
3323 } else if fi.Mode()&0222 == 0 {
3324 t.Errorf("f.Stat.Mode after OpenFile is %v, should be writable", fi.Mode())
3325 }
3326 if err := f.Close(); err != nil {
3327 t.Error(err)
3328 }
3329 if fi, err := Stat(name); err != nil {
3330 t.Error(err)
3331 } else if fi.Mode()&0222 == 0 {
3332 t.Errorf("Stat after OpenFile is %v, should be writable", fi.Mode())
3333 }
3334 }
3335
3336 func forceMFTUpdateOnWindows(t *testing.T, path string) {
3337 t.Helper()
3338
3339 if runtime.GOOS != "windows" {
3340 return
3341 }
3342
3343
3344
3345 if err := filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error {
3346 if err != nil {
3347 t.Fatal(err)
3348 }
3349 info, err := d.Info()
3350 if err != nil {
3351 t.Fatal(err)
3352 }
3353 stat, err := Stat(path)
3354 if err != nil {
3355 t.Fatal(err)
3356 }
3357 if stat.ModTime() == info.ModTime() {
3358 return nil
3359 }
3360 if err := Chtimes(path, stat.ModTime(), stat.ModTime()); err != nil {
3361 t.Log(err)
3362 }
3363 return nil
3364 }); err != nil {
3365 t.Fatal(err)
3366 }
3367 }
3368
3369 func TestDirFS(t *testing.T) {
3370 t.Parallel()
3371 testDirFS(t, DirFS("./testdata/dirfs"))
3372 }
3373
3374 func TestRootDirFS(t *testing.T) {
3375 t.Parallel()
3376 r, err := OpenRoot("./testdata/dirfs")
3377 if err != nil {
3378 t.Fatal(err)
3379 }
3380 defer r.Close()
3381 testDirFS(t, r.FS())
3382 }
3383
3384 func testDirFS(t *testing.T, fsys fs.FS) {
3385 forceMFTUpdateOnWindows(t, "./testdata/dirfs")
3386
3387 if err := fstest.TestFS(fsys, "a", "b", "dir/x"); err != nil {
3388 t.Fatal(err)
3389 }
3390
3391 rdfs, ok := fsys.(fs.ReadDirFS)
3392 if !ok {
3393 t.Error("expected DirFS result to implement fs.ReadDirFS")
3394 }
3395 if _, err := rdfs.ReadDir("nonexistent"); err == nil {
3396 t.Error("fs.ReadDir of nonexistent directory succeeded")
3397 }
3398
3399
3400
3401 const nonesuch = "dir/nonesuch"
3402 _, err := fsys.Open(nonesuch)
3403 if err == nil {
3404 t.Error("fs.Open of nonexistent file succeeded")
3405 } else {
3406 if !strings.Contains(err.Error(), nonesuch) {
3407 t.Errorf("error %q does not contain %q", err, nonesuch)
3408 }
3409 if strings.Contains(err.(*PathError).Path, "testdata") {
3410 t.Errorf("error %q contains %q", err, "testdata")
3411 }
3412 }
3413
3414
3415 d := DirFS(".")
3416 _, err = d.Open(`testdata\dirfs`)
3417 if err == nil {
3418 t.Fatalf(`Open testdata\dirfs succeeded`)
3419 }
3420
3421
3422 _, err = d.Open(`NUL`)
3423 if err == nil {
3424 t.Errorf(`Open NUL succeeded`)
3425 }
3426 }
3427
3428 func TestDirFSRootDir(t *testing.T) {
3429 t.Parallel()
3430
3431 cwd, err := Getwd()
3432 if err != nil {
3433 t.Fatal(err)
3434 }
3435 cwd = cwd[len(filepath.VolumeName(cwd)):]
3436 cwd = filepath.ToSlash(cwd)
3437 cwd = strings.TrimPrefix(cwd, "/")
3438
3439
3440 d := DirFS("/")
3441 f, err := d.Open(cwd + "/testdata/dirfs/a")
3442 if err != nil {
3443 t.Fatal(err)
3444 }
3445 f.Close()
3446 }
3447
3448 func TestDirFSEmptyDir(t *testing.T) {
3449 t.Parallel()
3450
3451 d := DirFS("")
3452 cwd, _ := Getwd()
3453 for _, path := range []string{
3454 "testdata/dirfs/a",
3455 filepath.ToSlash(cwd) + "/testdata/dirfs/a",
3456 } {
3457 _, err := d.Open(path)
3458 if err == nil {
3459 t.Fatalf(`DirFS("").Open(%q) succeeded`, path)
3460 }
3461 }
3462 }
3463
3464 func TestDirFSPathsValid(t *testing.T) {
3465 if runtime.GOOS == "windows" {
3466 t.Skipf("skipping on Windows")
3467 }
3468 t.Parallel()
3469
3470 d := t.TempDir()
3471 if err := WriteFile(filepath.Join(d, "control.txt"), []byte(string("Hello, world!")), 0644); err != nil {
3472 t.Fatal(err)
3473 }
3474 if err := WriteFile(filepath.Join(d, `e:xperi\ment.txt`), []byte(string("Hello, colon and backslash!")), 0644); err != nil {
3475 t.Fatal(err)
3476 }
3477
3478 fsys := DirFS(d)
3479 err := fs.WalkDir(fsys, ".", func(path string, e fs.DirEntry, err error) error {
3480 if fs.ValidPath(e.Name()) {
3481 t.Logf("%q ok", e.Name())
3482 } else {
3483 t.Errorf("%q INVALID", e.Name())
3484 }
3485 return nil
3486 })
3487 if err != nil {
3488 t.Fatal(err)
3489 }
3490 }
3491
3492 func TestReadFileProc(t *testing.T) {
3493 t.Parallel()
3494
3495
3496
3497
3498
3499
3500 name := "/proc/sys/fs/pipe-max-size"
3501 if _, err := Stat(name); err != nil {
3502 t.Skip(err)
3503 }
3504 data, err := ReadFile(name)
3505 if err != nil {
3506 t.Fatal(err)
3507 }
3508 if len(data) == 0 || data[len(data)-1] != '\n' {
3509 t.Fatalf("read %s: not newline-terminated: %q", name, data)
3510 }
3511 }
3512
3513 func TestDirFSReadFileProc(t *testing.T) {
3514 t.Parallel()
3515
3516 fsys := DirFS("/")
3517 name := "proc/sys/fs/pipe-max-size"
3518 if _, err := fs.Stat(fsys, name); err != nil {
3519 t.Skip()
3520 }
3521 data, err := fs.ReadFile(fsys, name)
3522 if err != nil {
3523 t.Fatal(err)
3524 }
3525 if len(data) == 0 || data[len(data)-1] != '\n' {
3526 t.Fatalf("read %s: not newline-terminated: %q", name, data)
3527 }
3528 }
3529
3530 func TestWriteStringAlloc(t *testing.T) {
3531 if runtime.GOOS == "js" {
3532 t.Skip("js allocates a lot during File.WriteString")
3533 }
3534 d := t.TempDir()
3535 f, err := Create(filepath.Join(d, "whiteboard.txt"))
3536 if err != nil {
3537 t.Fatal(err)
3538 }
3539 defer f.Close()
3540 allocs := testing.AllocsPerRun(100, func() {
3541 f.WriteString("I will not allocate when passed a string longer than 32 bytes.\n")
3542 })
3543 if allocs != 0 {
3544 t.Errorf("expected 0 allocs for File.WriteString, got %v", allocs)
3545 }
3546 }
3547
3548
3549 func TestFileIOCloseRace(t *testing.T) {
3550 t.Parallel()
3551 file, err := Create(filepath.Join(t.TempDir(), "test.txt"))
3552 if err != nil {
3553 t.Fatal(err)
3554 }
3555 var wg sync.WaitGroup
3556 wg.Go(func() {
3557 var tmp [100]byte
3558 if _, err := file.Write(tmp[:]); err != nil && !errors.Is(err, ErrClosed) {
3559 t.Error(err)
3560 }
3561 })
3562 wg.Go(func() {
3563 var tmp [100]byte
3564 if _, err := file.Read(tmp[:]); err != nil && err != io.EOF && !errors.Is(err, ErrClosed) {
3565 t.Error(err)
3566 }
3567 })
3568 wg.Go(func() {
3569 if err := file.Close(); err != nil {
3570 t.Error(err)
3571 }
3572 })
3573 wg.Wait()
3574 }
3575
3576
3577 func TestPipeIOCloseRace(t *testing.T) {
3578
3579 if runtime.GOOS == "js" || runtime.GOOS == "wasip1" {
3580 t.Skipf("skipping on %s: no pipes", runtime.GOOS)
3581 }
3582 t.Parallel()
3583
3584 r, w, err := Pipe()
3585 if err != nil {
3586 t.Fatal(err)
3587 }
3588
3589 var wg sync.WaitGroup
3590 wg.Add(3)
3591
3592 go func() {
3593 defer wg.Done()
3594 for {
3595 n, err := w.Write([]byte("hi"))
3596 if err != nil {
3597
3598
3599 switch {
3600 case errors.Is(err, ErrClosed),
3601 strings.Contains(err.Error(), "broken pipe"),
3602 strings.Contains(err.Error(), "pipe is being closed"),
3603 strings.Contains(err.Error(), "hungup channel"):
3604
3605 default:
3606
3607 t.Error(err)
3608 }
3609 return
3610 }
3611 if n != 2 {
3612 t.Errorf("wrote %d bytes, expected 2", n)
3613 return
3614 }
3615 }
3616 }()
3617
3618 go func() {
3619 defer wg.Done()
3620 for {
3621 var buf [2]byte
3622 n, err := r.Read(buf[:])
3623 if err != nil {
3624 if err != io.EOF && !errors.Is(err, ErrClosed) {
3625 t.Error(err)
3626 }
3627 return
3628 }
3629 if n != 2 {
3630 t.Errorf("read %d bytes, want 2", n)
3631 }
3632 }
3633 }()
3634
3635 go func() {
3636 defer wg.Done()
3637
3638
3639
3640
3641 time.Sleep(time.Millisecond)
3642
3643 if err := r.Close(); err != nil {
3644 t.Error(err)
3645 }
3646 if err := w.Close(); err != nil {
3647 t.Error(err)
3648 }
3649 }()
3650
3651 wg.Wait()
3652 }
3653
3654
3655 func TestPipeCloseRace(t *testing.T) {
3656
3657 if runtime.GOOS == "js" || runtime.GOOS == "wasip1" {
3658 t.Skipf("skipping on %s: no pipes", runtime.GOOS)
3659 }
3660 t.Parallel()
3661
3662 r, w, err := Pipe()
3663 if err != nil {
3664 t.Fatal(err)
3665 }
3666 var wg sync.WaitGroup
3667 c := make(chan error, 4)
3668 f := func() {
3669 defer wg.Done()
3670 c <- r.Close()
3671 c <- w.Close()
3672 }
3673 wg.Add(2)
3674 go f()
3675 go f()
3676 nils, errs := 0, 0
3677 for i := 0; i < 4; i++ {
3678 err := <-c
3679 if err == nil {
3680 nils++
3681 } else {
3682 errs++
3683 }
3684 }
3685 if nils != 2 || errs != 2 {
3686 t.Errorf("got nils %d errs %d, want 2 2", nils, errs)
3687 }
3688 }
3689
3690 func TestRandomLen(t *testing.T) {
3691 for range 5 {
3692 dir, err := MkdirTemp(t.TempDir(), "*")
3693 if err != nil {
3694 t.Fatal(err)
3695 }
3696 base := filepath.Base(dir)
3697 if len(base) > 10 {
3698 t.Errorf("MkdirTemp returned len %d: %s", len(base), base)
3699 }
3700 }
3701 for range 5 {
3702 f, err := CreateTemp(t.TempDir(), "*")
3703 if err != nil {
3704 t.Fatal(err)
3705 }
3706 base := filepath.Base(f.Name())
3707 f.Close()
3708 if len(base) > 10 {
3709 t.Errorf("CreateTemp returned len %d: %s", len(base), base)
3710 }
3711 }
3712 }
3713
3714 func TestCopyFS(t *testing.T) {
3715 t.Parallel()
3716
3717
3718 forceMFTUpdateOnWindows(t, "./testdata/dirfs")
3719 fsys := DirFS("./testdata/dirfs")
3720 tmpDir := t.TempDir()
3721 if err := CopyFS(tmpDir, fsys); err != nil {
3722 t.Fatal("CopyFS:", err)
3723 }
3724 forceMFTUpdateOnWindows(t, tmpDir)
3725 tmpFsys := DirFS(tmpDir)
3726 if err := fstest.TestFS(tmpFsys, "a", "b", "dir/x"); err != nil {
3727 t.Fatal("TestFS:", err)
3728 }
3729 if err := verifyCopyFS(t, fsys, tmpFsys); err != nil {
3730 t.Fatal("comparing two directories:", err)
3731 }
3732
3733
3734
3735 if err := CopyFS(tmpDir, fsys); !errors.Is(err, fs.ErrExist) {
3736 t.Errorf("CopyFS should have failed and returned error when there is"+
3737 "any existing file in the destination directory (in disk filesystem), "+
3738 "got: %v, expected any error that indicates <file exists>", err)
3739 }
3740
3741
3742 fsys = fstest.MapFS{
3743 "william": {Data: []byte("Shakespeare\n")},
3744 "carl": {Data: []byte("Gauss\n")},
3745 "daVinci": {Data: []byte("Leonardo\n")},
3746 "einstein": {Data: []byte("Albert\n")},
3747 "dir/newton": {Data: []byte("Sir Isaac\n")},
3748 }
3749 tmpDir = t.TempDir()
3750 if err := CopyFS(tmpDir, fsys); err != nil {
3751 t.Fatal("CopyFS:", err)
3752 }
3753 forceMFTUpdateOnWindows(t, tmpDir)
3754 tmpFsys = DirFS(tmpDir)
3755 if err := fstest.TestFS(tmpFsys, "william", "carl", "daVinci", "einstein", "dir/newton"); err != nil {
3756 t.Fatal("TestFS:", err)
3757 }
3758 if err := verifyCopyFS(t, fsys, tmpFsys); err != nil {
3759 t.Fatal("comparing two directories:", err)
3760 }
3761
3762
3763
3764 if err := CopyFS(tmpDir, fsys); !errors.Is(err, fs.ErrExist) {
3765 t.Errorf("CopyFS should have failed and returned error when there is"+
3766 "any existing file in the destination directory (in memory filesystem), "+
3767 "got: %v, expected any error that indicates <file exists>", err)
3768 }
3769 }
3770
3771
3772
3773 func verifyCopyFS(t *testing.T, originFS, copiedFS fs.FS) error {
3774 testDir := filepath.Join(t.TempDir(), "test")
3775
3776
3777 if err := Mkdir(testDir, ModePerm); err != nil {
3778 return fmt.Errorf("mkdir %q failed: %v", testDir, err)
3779 }
3780 dirStat, err := Stat(testDir)
3781 if err != nil {
3782 return fmt.Errorf("stat dir %q failed: %v", testDir, err)
3783 }
3784 wantDirMode := dirStat.Mode()
3785
3786 f, err := Create(filepath.Join(testDir, "tmp"))
3787 if err != nil {
3788 return fmt.Errorf("open %q failed: %v", filepath.Join(testDir, "tmp"), err)
3789 }
3790 defer f.Close()
3791 wantFileRWStat, err := f.Stat()
3792 if err != nil {
3793 return fmt.Errorf("stat file %q failed: %v", f.Name(), err)
3794 }
3795 wantFileRWMode := wantFileRWStat.Mode()
3796
3797 return fs.WalkDir(originFS, ".", func(path string, d fs.DirEntry, err error) error {
3798 if d.IsDir() {
3799
3800 if d.Name() == "." {
3801 return nil
3802 }
3803
3804 dinfo, err := fs.Stat(copiedFS, path)
3805 if err != nil {
3806 return err
3807 }
3808
3809 if dinfo.Mode() != wantDirMode {
3810 return fmt.Errorf("dir %q mode is %v, want %v",
3811 d.Name(), dinfo.Mode(), wantDirMode)
3812 }
3813 return nil
3814 }
3815
3816 fInfo, err := originFS.Open(path)
3817 if err != nil {
3818 return err
3819 }
3820 defer fInfo.Close()
3821 copiedInfo, err := copiedFS.Open(path)
3822 if err != nil {
3823 return err
3824 }
3825 defer copiedInfo.Close()
3826
3827
3828 data, err := io.ReadAll(fInfo)
3829 if err != nil {
3830 return err
3831 }
3832 newData, err := io.ReadAll(copiedInfo)
3833 if err != nil {
3834 return err
3835 }
3836 if !bytes.Equal(data, newData) {
3837 return fmt.Errorf("file %q content is %s, want %s", path, newData, data)
3838 }
3839
3840 fStat, err := fInfo.Stat()
3841 if err != nil {
3842 return err
3843 }
3844 copiedStat, err := copiedInfo.Stat()
3845 if err != nil {
3846 return err
3847 }
3848
3849
3850
3851 if copiedStat.Mode()&0111&wantFileRWMode != fStat.Mode()&0111&wantFileRWMode {
3852 return fmt.Errorf("file %q execute mode is %v, want %v",
3853 path, copiedStat.Mode()&0111, fStat.Mode()&0111)
3854 }
3855
3856 rwMode := copiedStat.Mode() &^ 0111
3857 if rwMode != wantFileRWMode {
3858 return fmt.Errorf("file %q rw mode is %v, want %v",
3859 path, rwMode, wantFileRWStat.Mode())
3860 }
3861 return nil
3862 })
3863 }
3864
3865 func TestCopyFSWithSymlinks(t *testing.T) {
3866
3867 testenv.MustHaveSymlink(t)
3868
3869
3870 tmpDir := t.TempDir()
3871 outsideDir := filepath.Join(tmpDir, "copyfs_out")
3872 if err := Mkdir(outsideDir, 0755); err != nil {
3873 t.Fatalf("Mkdir: %v", err)
3874 }
3875 outsideFile := filepath.Join(outsideDir, "file.out.txt")
3876
3877 if err := WriteFile(outsideFile, []byte("Testing CopyFS outside"), 0644); err != nil {
3878 t.Fatalf("WriteFile: %v", err)
3879 }
3880
3881
3882 insideDir := filepath.Join(tmpDir, "copyfs_in")
3883 if err := Mkdir(insideDir, 0755); err != nil {
3884 t.Fatalf("Mkdir: %v", err)
3885 }
3886 insideFile := filepath.Join(insideDir, "file.in.txt")
3887 if err := WriteFile(insideFile, []byte("Testing CopyFS inside"), 0644); err != nil {
3888 t.Fatalf("WriteFile: %v", err)
3889 }
3890
3891
3892 linkInDir := filepath.Join(insideDir, "in_symlinks")
3893 if err := Mkdir(linkInDir, 0755); err != nil {
3894 t.Fatalf("Mkdir: %v", err)
3895 }
3896 linkOutDir := filepath.Join(insideDir, "out_symlinks")
3897 if err := Mkdir(linkOutDir, 0755); err != nil {
3898 t.Fatalf("Mkdir: %v", err)
3899 }
3900
3901
3902 outLinkFile := filepath.Join(linkOutDir, "file.abs.out.link")
3903 if err := Symlink(outsideFile, outLinkFile); err != nil {
3904 t.Fatalf("Symlink: %v", err)
3905 }
3906
3907
3908 relOutsideFile, err := filepath.Rel(filepath.Join(linkOutDir, "."), outsideFile)
3909 if err != nil {
3910 t.Fatalf("filepath.Rel: %v", err)
3911 }
3912 relOutLinkFile := filepath.Join(linkOutDir, "file.rel.out.link")
3913 if err := Symlink(relOutsideFile, relOutLinkFile); err != nil {
3914 t.Fatalf("Symlink: %v", err)
3915 }
3916
3917
3918 relInsideFile, err := filepath.Rel(filepath.Join(linkInDir, "."), insideFile)
3919 if err != nil {
3920 t.Fatalf("filepath.Rel: %v", err)
3921 }
3922 relInLinkFile := filepath.Join(linkInDir, "file.rel.in.link")
3923 if err := Symlink(relInsideFile, relInLinkFile); err != nil {
3924 t.Fatalf("Symlink: %v", err)
3925 }
3926
3927
3928 forceMFTUpdateOnWindows(t, insideDir)
3929 fsys := DirFS(insideDir)
3930 tmpDupDir := filepath.Join(tmpDir, "copyfs_dup")
3931 if err := Mkdir(tmpDupDir, 0755); err != nil {
3932 t.Fatalf("Mkdir: %v", err)
3933 }
3934
3935 if err := CopyFS(tmpDupDir, fsys); err != nil {
3936 t.Fatalf("CopyFS: %v", err)
3937 }
3938
3939 forceMFTUpdateOnWindows(t, tmpDupDir)
3940 tmpFsys := DirFS(tmpDupDir)
3941 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 {
3942 t.Fatal("TestFS:", err)
3943 }
3944 if err := fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
3945 if d.IsDir() {
3946 return nil
3947 }
3948
3949 fi, err := d.Info()
3950 if err != nil {
3951 return err
3952 }
3953 if filepath.Ext(path) == ".link" {
3954 if fi.Mode()&ModeSymlink == 0 {
3955 return errors.New("original file " + path + " should be a symlink")
3956 }
3957 tmpfi, err := fs.Stat(tmpFsys, path)
3958 if err != nil {
3959 return err
3960 }
3961 if tmpfi.Mode()&ModeSymlink != 0 {
3962 return errors.New("copied file " + path + " should not be a symlink")
3963 }
3964 }
3965
3966 data, err := fs.ReadFile(fsys, path)
3967 if err != nil {
3968 return err
3969 }
3970 newData, err := fs.ReadFile(tmpFsys, path)
3971 if err != nil {
3972 return err
3973 }
3974 if !bytes.Equal(data, newData) {
3975 return errors.New("file " + path + " contents differ")
3976 }
3977
3978 var target string
3979 switch fileName := filepath.Base(path); fileName {
3980 case "file.abs.out.link", "file.rel.out.link":
3981 target = outsideFile
3982 case "file.rel.in.link":
3983 target = insideFile
3984 }
3985 if len(target) > 0 {
3986 targetData, err := ReadFile(target)
3987 if err != nil {
3988 return err
3989 }
3990 if !bytes.Equal(targetData, newData) {
3991 return errors.New("file " + path + " contents differ from target")
3992 }
3993 }
3994
3995 return nil
3996 }); err != nil {
3997 t.Fatal("comparing two directories:", err)
3998 }
3999 }
4000
4001 func TestAppendDoesntOverwrite(t *testing.T) {
4002 testMaybeRooted(t, func(t *testing.T, r *Root) {
4003 name := "file"
4004 if err := WriteFile(name, []byte("hello"), 0666); err != nil {
4005 t.Fatal(err)
4006 }
4007 var f *File
4008 var err error
4009 if r == nil {
4010 f, err = OpenFile(name, O_APPEND|O_WRONLY, 0)
4011 } else {
4012 f, err = r.OpenFile(name, O_APPEND|O_WRONLY, 0)
4013 }
4014 if err != nil {
4015 t.Fatal(err)
4016 }
4017 if _, err := f.Write([]byte(" world")); err != nil {
4018 f.Close()
4019 t.Fatal(err)
4020 }
4021 if err := f.Close(); err != nil {
4022 t.Fatal(err)
4023 }
4024 got, err := ReadFile(name)
4025 if err != nil {
4026 t.Fatal(err)
4027 }
4028 want := "hello world"
4029 if string(got) != want {
4030 t.Fatalf("got %q, want %q", got, want)
4031 }
4032 })
4033 }
4034
4035 func TestRemoveReadOnlyFile(t *testing.T) {
4036 testMaybeRooted(t, func(t *testing.T, r *Root) {
4037 if err := WriteFile("file", []byte("1"), 0); err != nil {
4038 t.Fatal(err)
4039 }
4040 var err error
4041 if r == nil {
4042 err = Remove("file")
4043 } else {
4044 err = r.Remove("file")
4045 }
4046 if err != nil {
4047 t.Fatalf("Remove read-only file: %v", err)
4048 }
4049 if _, err := Stat("file"); !IsNotExist(err) {
4050 t.Fatalf("Stat read-only file after removal: %v (want IsNotExist)", err)
4051 }
4052 })
4053 }
4054
4055 func TestOpenFileDevNull(t *testing.T) {
4056
4057 t.Parallel()
4058
4059 f, err := OpenFile(DevNull, O_WRONLY|O_CREATE|O_TRUNC, 0o644)
4060 if err != nil {
4061 t.Fatalf("OpenFile(DevNull): %v", err)
4062 }
4063 f.Close()
4064 }
4065
4066 func TestReadFileContents(t *testing.T) {
4067 type readStep struct {
4068 bufSize int
4069 retN int
4070 retErr error
4071 }
4072 errFoo := errors.New("foo")
4073 tests := []struct {
4074 name string
4075 statSize int64
4076 wantSize int
4077 wantErr error
4078 reads []readStep
4079 }{
4080 {
4081 name: "big-file",
4082 statSize: 2000,
4083 wantSize: 2000,
4084 reads: []readStep{
4085 {bufSize: 2001, retN: 21, retErr: nil},
4086 {bufSize: 1980, retN: 1979, retErr: io.EOF},
4087 },
4088 },
4089 {
4090 name: "small-file",
4091 statSize: 100,
4092 wantSize: 100,
4093 reads: []readStep{
4094 {bufSize: 512, retN: 100, retErr: io.EOF},
4095 },
4096 },
4097 {
4098 name: "returning-error",
4099 statSize: 1000,
4100 wantSize: 50,
4101 wantErr: errFoo,
4102 reads: []readStep{
4103 {bufSize: 1001, retN: 25, retErr: nil},
4104 {retN: 25, retErr: errFoo},
4105 },
4106 },
4107 {
4108 name: "proc-file",
4109 statSize: 0,
4110 wantSize: 1023,
4111 reads: []readStep{
4112 {bufSize: 512, retN: 512, retErr: nil},
4113 {retN: 511, retErr: io.EOF},
4114 },
4115 },
4116 {
4117 name: "plan9-iproute-file",
4118 statSize: 0,
4119 wantSize: 1032,
4120 reads: []readStep{
4121 {bufSize: 512, retN: 511, retErr: nil},
4122 {retN: 511, retErr: nil},
4123 {retN: 10, retErr: io.EOF},
4124 },
4125 },
4126 }
4127 for _, tt := range tests {
4128 t.Run(tt.name, func(t *testing.T) {
4129 remain := tt.reads
4130 i := -1
4131 got, err := ExportReadFileContents(tt.statSize, func(buf []byte) (int, error) {
4132 i++
4133 t.Logf("read[%d] with buf size %d", i, len(buf))
4134 if len(remain) == 0 {
4135 t.Fatalf("unexpected read of length %d after %d expected reads", len(buf), len(tt.reads))
4136 }
4137 if tt.statSize == 0 && len(buf) < 512 {
4138
4139
4140 t.Fatalf("read[%d] with buf size %d; want at least 512 for 0-sized file", i, len(buf))
4141 }
4142 step := remain[0]
4143 remain = remain[1:]
4144 if step.bufSize != 0 && len(buf) != step.bufSize {
4145 t.Fatalf("read[%d] has buffer size %d; want %d", i, len(buf), step.bufSize)
4146 }
4147 return step.retN, step.retErr
4148 })
4149 if len(remain) > 0 {
4150 t.Fatalf("expected %d reads, got %d", len(tt.reads), i+1)
4151 }
4152 if fmt.Sprint(err) != fmt.Sprint(tt.wantErr) {
4153 t.Errorf("got error %v; want %v", err, tt.wantErr)
4154 }
4155 if len(got) != tt.wantSize {
4156 t.Errorf("got size %d; want %d", len(got), tt.wantSize)
4157 }
4158 })
4159 }
4160 }
4161
View as plain text