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