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