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