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