1// Copyright 2009 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package os_test 6 7import ( 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 "reflect" 20 "runtime" 21 "runtime/debug" 22 "slices" 23 "strings" 24 "sync" 25 "syscall" 26 "testing" 27 "testing/fstest" 28 "time" 29) 30 31func 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 43var 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 54type sysDir struct { 55 name string 56 files []string 57} 58 59var 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 // In a self-hosted iOS build the above files might 93 // not exist. Look for system files instead below. 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 // wasmtime has issues resolving symbolic links that are often present 113 // in directories like /etc/group below (e.g. private/etc/group on OSX). 114 // For this reason we use files in the Go source tree instead. 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 134func 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 151func 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 161func 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 178var sfdir = sysdir.name 179var sfname = sysdir.files[0] 180 181func 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 198func TestStatError(t *testing.T) { 199 defer chtmpdir(t)() 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 234func TestStatSymlinkLoop(t *testing.T) { 235 testenv.MustHaveSymlink(t) 236 237 defer chtmpdir(t)() 238 239 err := Symlink("x", "y") 240 if err != nil { 241 t.Fatal(err) 242 } 243 defer Remove("y") 244 245 err = Symlink("y", "x") 246 if err != nil { 247 t.Fatal(err) 248 } 249 defer Remove("x") 250 251 _, err = Stat("x") 252 if _, ok := err.(*fs.PathError); !ok { 253 t.Errorf("expected *PathError, got %T: %v\n", err, err) 254 } 255} 256 257func TestFstat(t *testing.T) { 258 t.Parallel() 259 260 path := sfdir + "/" + sfname 261 file, err1 := Open(path) 262 if err1 != nil { 263 t.Fatal("open failed:", err1) 264 } 265 defer file.Close() 266 dir, err2 := file.Stat() 267 if err2 != nil { 268 t.Fatal("fstat failed:", err2) 269 } 270 if !equal(sfname, dir.Name()) { 271 t.Error("name should be ", sfname, "; is", dir.Name()) 272 } 273 filesize := size(path, t) 274 if dir.Size() != filesize { 275 t.Error("size should be", filesize, "; is", dir.Size()) 276 } 277} 278 279func TestLstat(t *testing.T) { 280 t.Parallel() 281 282 path := sfdir + "/" + sfname 283 dir, err := Lstat(path) 284 if err != nil { 285 t.Fatal("lstat failed:", err) 286 } 287 if !equal(sfname, dir.Name()) { 288 t.Error("name should be ", sfname, "; is", dir.Name()) 289 } 290 if dir.Mode()&ModeSymlink == 0 { 291 filesize := size(path, t) 292 if dir.Size() != filesize { 293 t.Error("size should be", filesize, "; is", dir.Size()) 294 } 295 } 296} 297 298// Read with length 0 should not return EOF. 299func TestRead0(t *testing.T) { 300 t.Parallel() 301 302 path := sfdir + "/" + sfname 303 f, err := Open(path) 304 if err != nil { 305 t.Fatal("open failed:", err) 306 } 307 defer f.Close() 308 309 b := make([]byte, 0) 310 n, err := f.Read(b) 311 if n != 0 || err != nil { 312 t.Errorf("Read(0) = %d, %v, want 0, nil", n, err) 313 } 314 b = make([]byte, 100) 315 n, err = f.Read(b) 316 if n <= 0 || err != nil { 317 t.Errorf("Read(100) = %d, %v, want >0, nil", n, err) 318 } 319} 320 321// Reading a closed file should return ErrClosed error 322func TestReadClosed(t *testing.T) { 323 t.Parallel() 324 325 path := sfdir + "/" + sfname 326 file, err := Open(path) 327 if err != nil { 328 t.Fatal("open failed:", err) 329 } 330 file.Close() // close immediately 331 332 b := make([]byte, 100) 333 _, err = file.Read(b) 334 335 e, ok := err.(*PathError) 336 if !ok || e.Err != ErrClosed { 337 t.Fatalf("Read: got %T(%v), want %T(%v)", err, err, e, ErrClosed) 338 } 339} 340 341func testReaddirnames(dir string, contents []string) func(*testing.T) { 342 return func(t *testing.T) { 343 t.Parallel() 344 345 file, err := Open(dir) 346 if err != nil { 347 t.Fatalf("open %q failed: %v", dir, err) 348 } 349 defer file.Close() 350 s, err2 := file.Readdirnames(-1) 351 if err2 != nil { 352 t.Fatalf("Readdirnames %q failed: %v", dir, err2) 353 } 354 for _, m := range contents { 355 found := false 356 for _, n := range s { 357 if n == "." || n == ".." { 358 t.Errorf("got %q in directory", n) 359 } 360 if !equal(m, n) { 361 continue 362 } 363 if found { 364 t.Error("present twice:", m) 365 } 366 found = true 367 } 368 if !found { 369 t.Error("could not find", m) 370 } 371 } 372 if s == nil { 373 t.Error("Readdirnames returned nil instead of empty slice") 374 } 375 } 376} 377 378func testReaddir(dir string, contents []string) func(*testing.T) { 379 return func(t *testing.T) { 380 t.Parallel() 381 382 file, err := Open(dir) 383 if err != nil { 384 t.Fatalf("open %q failed: %v", dir, err) 385 } 386 defer file.Close() 387 s, err2 := file.Readdir(-1) 388 if err2 != nil { 389 t.Fatalf("Readdir %q failed: %v", dir, err2) 390 } 391 for _, m := range contents { 392 found := false 393 for _, n := range s { 394 if n.Name() == "." || n.Name() == ".." { 395 t.Errorf("got %q in directory", n.Name()) 396 } 397 if !equal(m, n.Name()) { 398 continue 399 } 400 if found { 401 t.Error("present twice:", m) 402 } 403 found = true 404 } 405 if !found { 406 t.Error("could not find", m) 407 } 408 } 409 if s == nil { 410 t.Error("Readdir returned nil instead of empty slice") 411 } 412 } 413} 414 415func testReadDir(dir string, contents []string) func(*testing.T) { 416 return func(t *testing.T) { 417 t.Parallel() 418 419 file, err := Open(dir) 420 if err != nil { 421 t.Fatalf("open %q failed: %v", dir, err) 422 } 423 defer file.Close() 424 s, err2 := file.ReadDir(-1) 425 if err2 != nil { 426 t.Fatalf("ReadDir %q failed: %v", dir, err2) 427 } 428 for _, m := range contents { 429 found := false 430 for _, n := range s { 431 if n.Name() == "." || n.Name() == ".." { 432 t.Errorf("got %q in directory", n) 433 } 434 if !equal(m, n.Name()) { 435 continue 436 } 437 if found { 438 t.Error("present twice:", m) 439 } 440 found = true 441 lstat, err := Lstat(dir + "/" + m) 442 if err != nil { 443 t.Fatal(err) 444 } 445 if n.IsDir() != lstat.IsDir() { 446 t.Errorf("%s: IsDir=%v, want %v", m, n.IsDir(), lstat.IsDir()) 447 } 448 if n.Type() != lstat.Mode().Type() { 449 t.Errorf("%s: IsDir=%v, want %v", m, n.Type(), lstat.Mode().Type()) 450 } 451 info, err := n.Info() 452 if err != nil { 453 t.Errorf("%s: Info: %v", m, err) 454 continue 455 } 456 if !SameFile(info, lstat) { 457 t.Errorf("%s: Info: SameFile(info, lstat) = false", m) 458 } 459 } 460 if !found { 461 t.Error("could not find", m) 462 } 463 } 464 if s == nil { 465 t.Error("ReadDir returned nil instead of empty slice") 466 } 467 } 468} 469 470func TestFileReaddirnames(t *testing.T) { 471 t.Parallel() 472 473 t.Run(".", testReaddirnames(".", dot)) 474 t.Run("sysdir", testReaddirnames(sysdir.name, sysdir.files)) 475 t.Run("TempDir", testReaddirnames(t.TempDir(), nil)) 476} 477 478func TestFileReaddir(t *testing.T) { 479 t.Parallel() 480 481 t.Run(".", testReaddir(".", dot)) 482 t.Run("sysdir", testReaddir(sysdir.name, sysdir.files)) 483 t.Run("TempDir", testReaddir(t.TempDir(), nil)) 484} 485 486func TestFileReadDir(t *testing.T) { 487 t.Parallel() 488 489 t.Run(".", testReadDir(".", dot)) 490 t.Run("sysdir", testReadDir(sysdir.name, sysdir.files)) 491 t.Run("TempDir", testReadDir(t.TempDir(), nil)) 492} 493 494func benchmarkReaddirname(path string, b *testing.B) { 495 var nentries int 496 for i := 0; i < b.N; i++ { 497 f, err := Open(path) 498 if err != nil { 499 b.Fatalf("open %q failed: %v", path, err) 500 } 501 ns, err := f.Readdirnames(-1) 502 f.Close() 503 if err != nil { 504 b.Fatalf("readdirnames %q failed: %v", path, err) 505 } 506 nentries = len(ns) 507 } 508 b.Logf("benchmarkReaddirname %q: %d entries", path, nentries) 509} 510 511func benchmarkReaddir(path string, b *testing.B) { 512 var nentries int 513 for i := 0; i < b.N; i++ { 514 f, err := Open(path) 515 if err != nil { 516 b.Fatalf("open %q failed: %v", path, err) 517 } 518 fs, err := f.Readdir(-1) 519 f.Close() 520 if err != nil { 521 b.Fatalf("readdir %q failed: %v", path, err) 522 } 523 nentries = len(fs) 524 } 525 b.Logf("benchmarkReaddir %q: %d entries", path, nentries) 526} 527 528func benchmarkReadDir(path string, b *testing.B) { 529 var nentries int 530 for i := 0; i < b.N; i++ { 531 f, err := Open(path) 532 if err != nil { 533 b.Fatalf("open %q failed: %v", path, err) 534 } 535 fs, err := f.ReadDir(-1) 536 f.Close() 537 if err != nil { 538 b.Fatalf("readdir %q failed: %v", path, err) 539 } 540 nentries = len(fs) 541 } 542 b.Logf("benchmarkReadDir %q: %d entries", path, nentries) 543} 544 545func BenchmarkReaddirname(b *testing.B) { 546 benchmarkReaddirname(".", b) 547} 548 549func BenchmarkReaddir(b *testing.B) { 550 benchmarkReaddir(".", b) 551} 552 553func BenchmarkReadDir(b *testing.B) { 554 benchmarkReadDir(".", b) 555} 556 557func benchmarkStat(b *testing.B, path string) { 558 b.ResetTimer() 559 for i := 0; i < b.N; i++ { 560 _, err := Stat(path) 561 if err != nil { 562 b.Fatalf("Stat(%q) failed: %v", path, err) 563 } 564 } 565} 566 567func benchmarkLstat(b *testing.B, path string) { 568 b.ResetTimer() 569 for i := 0; i < b.N; i++ { 570 _, err := Lstat(path) 571 if err != nil { 572 b.Fatalf("Lstat(%q) failed: %v", path, err) 573 } 574 } 575} 576 577func BenchmarkStatDot(b *testing.B) { 578 benchmarkStat(b, ".") 579} 580 581func BenchmarkStatFile(b *testing.B) { 582 benchmarkStat(b, filepath.Join(runtime.GOROOT(), "src/os/os_test.go")) 583} 584 585func BenchmarkStatDir(b *testing.B) { 586 benchmarkStat(b, filepath.Join(runtime.GOROOT(), "src/os")) 587} 588 589func BenchmarkLstatDot(b *testing.B) { 590 benchmarkLstat(b, ".") 591} 592 593func BenchmarkLstatFile(b *testing.B) { 594 benchmarkLstat(b, filepath.Join(runtime.GOROOT(), "src/os/os_test.go")) 595} 596 597func BenchmarkLstatDir(b *testing.B) { 598 benchmarkLstat(b, filepath.Join(runtime.GOROOT(), "src/os")) 599} 600 601// Read the directory one entry at a time. 602func smallReaddirnames(file *File, length int, t *testing.T) []string { 603 names := make([]string, length) 604 count := 0 605 for { 606 d, err := file.Readdirnames(1) 607 if err == io.EOF { 608 break 609 } 610 if err != nil { 611 t.Fatalf("readdirnames %q failed: %v", file.Name(), err) 612 } 613 if len(d) == 0 { 614 t.Fatalf("readdirnames %q returned empty slice and no error", file.Name()) 615 } 616 names[count] = d[0] 617 count++ 618 } 619 return names[0:count] 620} 621 622// Check that reading a directory one entry at a time gives the same result 623// as reading it all at once. 624func TestReaddirnamesOneAtATime(t *testing.T) { 625 t.Parallel() 626 627 // big directory that doesn't change often. 628 dir := "/usr/bin" 629 switch runtime.GOOS { 630 case "android": 631 dir = "/system/bin" 632 case "ios", "wasip1": 633 wd, err := Getwd() 634 if err != nil { 635 t.Fatal(err) 636 } 637 dir = wd 638 case "plan9": 639 dir = "/bin" 640 case "windows": 641 dir = Getenv("SystemRoot") + "\\system32" 642 } 643 file, err := Open(dir) 644 if err != nil { 645 t.Fatalf("open %q failed: %v", dir, err) 646 } 647 defer file.Close() 648 all, err1 := file.Readdirnames(-1) 649 if err1 != nil { 650 t.Fatalf("readdirnames %q failed: %v", dir, err1) 651 } 652 file1, err2 := Open(dir) 653 if err2 != nil { 654 t.Fatalf("open %q failed: %v", dir, err2) 655 } 656 defer file1.Close() 657 small := smallReaddirnames(file1, len(all)+100, t) // +100 in case we screw up 658 if len(small) < len(all) { 659 t.Fatalf("len(small) is %d, less than %d", len(small), len(all)) 660 } 661 for i, n := range all { 662 if small[i] != n { 663 t.Errorf("small read %q mismatch: %v", small[i], n) 664 } 665 } 666} 667 668func TestReaddirNValues(t *testing.T) { 669 if testing.Short() { 670 t.Skip("test.short; skipping") 671 } 672 t.Parallel() 673 674 dir := t.TempDir() 675 for i := 1; i <= 105; i++ { 676 f, err := Create(filepath.Join(dir, fmt.Sprintf("%d", i))) 677 if err != nil { 678 t.Fatalf("Create: %v", err) 679 } 680 f.Write([]byte(strings.Repeat("X", i))) 681 f.Close() 682 } 683 684 var d *File 685 openDir := func() { 686 var err error 687 d, err = Open(dir) 688 if err != nil { 689 t.Fatalf("Open directory: %v", err) 690 } 691 } 692 693 readdirExpect := func(n, want int, wantErr error) { 694 t.Helper() 695 fi, err := d.Readdir(n) 696 if err != wantErr { 697 t.Fatalf("Readdir of %d got error %v, want %v", n, err, wantErr) 698 } 699 if g, e := len(fi), want; g != e { 700 t.Errorf("Readdir of %d got %d files, want %d", n, g, e) 701 } 702 } 703 704 readDirExpect := func(n, want int, wantErr error) { 705 t.Helper() 706 de, err := d.ReadDir(n) 707 if err != wantErr { 708 t.Fatalf("ReadDir of %d got error %v, want %v", n, err, wantErr) 709 } 710 if g, e := len(de), want; g != e { 711 t.Errorf("ReadDir of %d got %d files, want %d", n, g, e) 712 } 713 } 714 715 readdirnamesExpect := func(n, want int, wantErr error) { 716 t.Helper() 717 fi, err := d.Readdirnames(n) 718 if err != wantErr { 719 t.Fatalf("Readdirnames of %d got error %v, want %v", n, err, wantErr) 720 } 721 if g, e := len(fi), want; g != e { 722 t.Errorf("Readdirnames of %d got %d files, want %d", n, g, e) 723 } 724 } 725 726 for _, fn := range []func(int, int, error){readdirExpect, readdirnamesExpect, readDirExpect} { 727 // Test the slurp case 728 openDir() 729 fn(0, 105, nil) 730 fn(0, 0, nil) 731 d.Close() 732 733 // Slurp with -1 instead 734 openDir() 735 fn(-1, 105, nil) 736 fn(-2, 0, nil) 737 fn(0, 0, nil) 738 d.Close() 739 740 // Test the bounded case 741 openDir() 742 fn(1, 1, nil) 743 fn(2, 2, nil) 744 fn(105, 102, nil) // and tests buffer >100 case 745 fn(3, 0, io.EOF) 746 d.Close() 747 } 748} 749 750func touch(t *testing.T, name string) { 751 f, err := Create(name) 752 if err != nil { 753 t.Fatal(err) 754 } 755 if err := f.Close(); err != nil { 756 t.Fatal(err) 757 } 758} 759 760func TestReaddirStatFailures(t *testing.T) { 761 switch runtime.GOOS { 762 case "windows", "plan9": 763 // Windows and Plan 9 already do this correctly, 764 // but are structured with different syscalls such 765 // that they don't use Lstat, so the hook below for 766 // testing it wouldn't work. 767 t.Skipf("skipping test on %v", runtime.GOOS) 768 } 769 770 var xerr error // error to return for x 771 *LstatP = func(path string) (FileInfo, error) { 772 if xerr != nil && strings.HasSuffix(path, "x") { 773 return nil, xerr 774 } 775 return Lstat(path) 776 } 777 defer func() { *LstatP = Lstat }() 778 779 dir := t.TempDir() 780 touch(t, filepath.Join(dir, "good1")) 781 touch(t, filepath.Join(dir, "x")) // will disappear or have an error 782 touch(t, filepath.Join(dir, "good2")) 783 readDir := func() ([]FileInfo, error) { 784 d, err := Open(dir) 785 if err != nil { 786 t.Fatal(err) 787 } 788 defer d.Close() 789 return d.Readdir(-1) 790 } 791 mustReadDir := func(testName string) []FileInfo { 792 fis, err := readDir() 793 if err != nil { 794 t.Fatalf("%s: Readdir: %v", testName, err) 795 } 796 return fis 797 } 798 names := func(fis []FileInfo) []string { 799 s := make([]string, len(fis)) 800 for i, fi := range fis { 801 s[i] = fi.Name() 802 } 803 slices.Sort(s) 804 return s 805 } 806 807 if got, want := names(mustReadDir("initial readdir")), 808 []string{"good1", "good2", "x"}; !reflect.DeepEqual(got, want) { 809 t.Errorf("initial readdir got %q; want %q", got, want) 810 } 811 812 xerr = ErrNotExist 813 if got, want := names(mustReadDir("with x disappearing")), 814 []string{"good1", "good2"}; !reflect.DeepEqual(got, want) { 815 t.Errorf("with x disappearing, got %q; want %q", got, want) 816 } 817 818 xerr = errors.New("some real error") 819 if _, err := readDir(); err != xerr { 820 t.Errorf("with a non-ErrNotExist error, got error %v; want %v", err, xerr) 821 } 822} 823 824// Readdir on a regular file should fail. 825func TestReaddirOfFile(t *testing.T) { 826 t.Parallel() 827 828 f, err := CreateTemp(t.TempDir(), "_Go_ReaddirOfFile") 829 if err != nil { 830 t.Fatal(err) 831 } 832 f.Write([]byte("foo")) 833 f.Close() 834 reg, err := Open(f.Name()) 835 if err != nil { 836 t.Fatal(err) 837 } 838 defer reg.Close() 839 840 names, err := reg.Readdirnames(-1) 841 if err == nil { 842 t.Error("Readdirnames succeeded; want non-nil error") 843 } 844 var pe *PathError 845 if !errors.As(err, &pe) || pe.Path != f.Name() { 846 t.Errorf("Readdirnames returned %q; want a PathError with path %q", err, f.Name()) 847 } 848 if len(names) > 0 { 849 t.Errorf("unexpected dir names in regular file: %q", names) 850 } 851} 852 853func TestHardLink(t *testing.T) { 854 testenv.MustHaveLink(t) 855 856 defer chtmpdir(t)() 857 from, to := "hardlinktestfrom", "hardlinktestto" 858 file, err := Create(to) 859 if err != nil { 860 t.Fatalf("open %q failed: %v", to, err) 861 } 862 if err = file.Close(); err != nil { 863 t.Errorf("close %q failed: %v", to, err) 864 } 865 err = Link(to, from) 866 if err != nil { 867 t.Fatalf("link %q, %q failed: %v", to, from, err) 868 } 869 870 none := "hardlinktestnone" 871 err = Link(none, none) 872 // Check the returned error is well-formed. 873 if lerr, ok := err.(*LinkError); !ok || lerr.Error() == "" { 874 t.Errorf("link %q, %q failed to return a valid error", none, none) 875 } 876 877 tostat, err := Stat(to) 878 if err != nil { 879 t.Fatalf("stat %q failed: %v", to, err) 880 } 881 fromstat, err := Stat(from) 882 if err != nil { 883 t.Fatalf("stat %q failed: %v", from, err) 884 } 885 if !SameFile(tostat, fromstat) { 886 t.Errorf("link %q, %q did not create hard link", to, from) 887 } 888 // We should not be able to perform the same Link() a second time 889 err = Link(to, from) 890 switch err := err.(type) { 891 case *LinkError: 892 if err.Op != "link" { 893 t.Errorf("Link(%q, %q) err.Op = %q; want %q", to, from, err.Op, "link") 894 } 895 if err.Old != to { 896 t.Errorf("Link(%q, %q) err.Old = %q; want %q", to, from, err.Old, to) 897 } 898 if err.New != from { 899 t.Errorf("Link(%q, %q) err.New = %q; want %q", to, from, err.New, from) 900 } 901 if !IsExist(err.Err) { 902 t.Errorf("Link(%q, %q) err.Err = %q; want %q", to, from, err.Err, "file exists error") 903 } 904 case nil: 905 t.Errorf("link %q, %q: expected error, got nil", from, to) 906 default: 907 t.Errorf("link %q, %q: expected %T, got %T %v", from, to, new(LinkError), err, err) 908 } 909} 910 911// chtmpdir changes the working directory to a new temporary directory and 912// provides a cleanup function. 913func chtmpdir(t *testing.T) func() { 914 oldwd, err := Getwd() 915 if err != nil { 916 t.Fatalf("chtmpdir: %v", err) 917 } 918 d, err := MkdirTemp("", "test") 919 if err != nil { 920 t.Fatalf("chtmpdir: %v", err) 921 } 922 if err := Chdir(d); err != nil { 923 t.Fatalf("chtmpdir: %v", err) 924 } 925 return func() { 926 if err := Chdir(oldwd); err != nil { 927 t.Fatalf("chtmpdir: %v", err) 928 } 929 RemoveAll(d) 930 } 931} 932 933func TestSymlink(t *testing.T) { 934 testenv.MustHaveSymlink(t) 935 936 defer chtmpdir(t)() 937 from, to := "symlinktestfrom", "symlinktestto" 938 file, err := Create(to) 939 if err != nil { 940 t.Fatalf("Create(%q) failed: %v", to, err) 941 } 942 if err = file.Close(); err != nil { 943 t.Errorf("Close(%q) failed: %v", to, err) 944 } 945 err = Symlink(to, from) 946 if err != nil { 947 t.Fatalf("Symlink(%q, %q) failed: %v", to, from, err) 948 } 949 tostat, err := Lstat(to) 950 if err != nil { 951 t.Fatalf("Lstat(%q) failed: %v", to, err) 952 } 953 if tostat.Mode()&ModeSymlink != 0 { 954 t.Fatalf("Lstat(%q).Mode()&ModeSymlink = %v, want 0", to, tostat.Mode()&ModeSymlink) 955 } 956 fromstat, err := Stat(from) 957 if err != nil { 958 t.Fatalf("Stat(%q) failed: %v", from, err) 959 } 960 if !SameFile(tostat, fromstat) { 961 t.Errorf("Symlink(%q, %q) did not create symlink", to, from) 962 } 963 fromstat, err = Lstat(from) 964 if err != nil { 965 t.Fatalf("Lstat(%q) failed: %v", from, err) 966 } 967 if fromstat.Mode()&ModeSymlink == 0 { 968 t.Fatalf("Lstat(%q).Mode()&ModeSymlink = 0, want %v", from, ModeSymlink) 969 } 970 fromstat, err = Stat(from) 971 if err != nil { 972 t.Fatalf("Stat(%q) failed: %v", from, err) 973 } 974 if fromstat.Name() != from { 975 t.Errorf("Stat(%q).Name() = %q, want %q", from, fromstat.Name(), from) 976 } 977 if fromstat.Mode()&ModeSymlink != 0 { 978 t.Fatalf("Stat(%q).Mode()&ModeSymlink = %v, want 0", from, fromstat.Mode()&ModeSymlink) 979 } 980 s, err := Readlink(from) 981 if err != nil { 982 t.Fatalf("Readlink(%q) failed: %v", from, err) 983 } 984 if s != to { 985 t.Fatalf("Readlink(%q) = %q, want %q", from, s, to) 986 } 987 file, err = Open(from) 988 if err != nil { 989 t.Fatalf("Open(%q) failed: %v", from, err) 990 } 991 file.Close() 992} 993 994func TestLongSymlink(t *testing.T) { 995 testenv.MustHaveSymlink(t) 996 997 defer chtmpdir(t)() 998 s := "0123456789abcdef" 999 // Long, but not too long: a common limit is 255. 1000 s = s + s + s + s + s + s + s + s + s + s + s + s + s + s + s 1001 from := "longsymlinktestfrom" 1002 err := Symlink(s, from) 1003 if err != nil { 1004 t.Fatalf("symlink %q, %q failed: %v", s, from, err) 1005 } 1006 r, err := Readlink(from) 1007 if err != nil { 1008 t.Fatalf("readlink %q failed: %v", from, err) 1009 } 1010 if r != s { 1011 t.Fatalf("after symlink %q != %q", r, s) 1012 } 1013} 1014 1015func TestRename(t *testing.T) { 1016 defer chtmpdir(t)() 1017 from, to := "renamefrom", "renameto" 1018 1019 file, err := Create(from) 1020 if err != nil { 1021 t.Fatalf("open %q failed: %v", from, err) 1022 } 1023 if err = file.Close(); err != nil { 1024 t.Errorf("close %q failed: %v", from, err) 1025 } 1026 err = Rename(from, to) 1027 if err != nil { 1028 t.Fatalf("rename %q, %q failed: %v", to, from, err) 1029 } 1030 _, err = Stat(to) 1031 if err != nil { 1032 t.Errorf("stat %q failed: %v", to, err) 1033 } 1034} 1035 1036func TestRenameOverwriteDest(t *testing.T) { 1037 defer chtmpdir(t)() 1038 from, to := "renamefrom", "renameto" 1039 1040 toData := []byte("to") 1041 fromData := []byte("from") 1042 1043 err := WriteFile(to, toData, 0777) 1044 if err != nil { 1045 t.Fatalf("write file %q failed: %v", to, err) 1046 } 1047 1048 err = WriteFile(from, fromData, 0777) 1049 if err != nil { 1050 t.Fatalf("write file %q failed: %v", from, err) 1051 } 1052 err = Rename(from, to) 1053 if err != nil { 1054 t.Fatalf("rename %q, %q failed: %v", to, from, err) 1055 } 1056 1057 _, err = Stat(from) 1058 if err == nil { 1059 t.Errorf("from file %q still exists", from) 1060 } 1061 if err != nil && !IsNotExist(err) { 1062 t.Fatalf("stat from: %v", err) 1063 } 1064 toFi, err := Stat(to) 1065 if err != nil { 1066 t.Fatalf("stat %q failed: %v", to, err) 1067 } 1068 if toFi.Size() != int64(len(fromData)) { 1069 t.Errorf(`"to" size = %d; want %d (old "from" size)`, toFi.Size(), len(fromData)) 1070 } 1071} 1072 1073func TestRenameFailed(t *testing.T) { 1074 defer chtmpdir(t)() 1075 from, to := "renamefrom", "renameto" 1076 1077 err := Rename(from, to) 1078 switch err := err.(type) { 1079 case *LinkError: 1080 if err.Op != "rename" { 1081 t.Errorf("rename %q, %q: err.Op: want %q, got %q", from, to, "rename", err.Op) 1082 } 1083 if err.Old != from { 1084 t.Errorf("rename %q, %q: err.Old: want %q, got %q", from, to, from, err.Old) 1085 } 1086 if err.New != to { 1087 t.Errorf("rename %q, %q: err.New: want %q, got %q", from, to, to, err.New) 1088 } 1089 case nil: 1090 t.Errorf("rename %q, %q: expected error, got nil", from, to) 1091 default: 1092 t.Errorf("rename %q, %q: expected %T, got %T %v", from, to, new(LinkError), err, err) 1093 } 1094} 1095 1096func TestRenameNotExisting(t *testing.T) { 1097 defer chtmpdir(t)() 1098 from, to := "doesnt-exist", "dest" 1099 1100 Mkdir(to, 0777) 1101 1102 if err := Rename(from, to); !IsNotExist(err) { 1103 t.Errorf("Rename(%q, %q) = %v; want an IsNotExist error", from, to, err) 1104 } 1105} 1106 1107func TestRenameToDirFailed(t *testing.T) { 1108 defer chtmpdir(t)() 1109 from, to := "renamefrom", "renameto" 1110 1111 Mkdir(from, 0777) 1112 Mkdir(to, 0777) 1113 1114 err := Rename(from, to) 1115 switch err := err.(type) { 1116 case *LinkError: 1117 if err.Op != "rename" { 1118 t.Errorf("rename %q, %q: err.Op: want %q, got %q", from, to, "rename", err.Op) 1119 } 1120 if err.Old != from { 1121 t.Errorf("rename %q, %q: err.Old: want %q, got %q", from, to, from, err.Old) 1122 } 1123 if err.New != to { 1124 t.Errorf("rename %q, %q: err.New: want %q, got %q", from, to, to, err.New) 1125 } 1126 case nil: 1127 t.Errorf("rename %q, %q: expected error, got nil", from, to) 1128 default: 1129 t.Errorf("rename %q, %q: expected %T, got %T %v", from, to, new(LinkError), err, err) 1130 } 1131} 1132 1133func TestRenameCaseDifference(pt *testing.T) { 1134 from, to := "renameFROM", "RENAMEfrom" 1135 tests := []struct { 1136 name string 1137 create func() error 1138 }{ 1139 {"dir", func() error { 1140 return Mkdir(from, 0777) 1141 }}, 1142 {"file", func() error { 1143 fd, err := Create(from) 1144 if err != nil { 1145 return err 1146 } 1147 return fd.Close() 1148 }}, 1149 } 1150 1151 for _, test := range tests { 1152 pt.Run(test.name, func(t *testing.T) { 1153 defer chtmpdir(t)() 1154 1155 if err := test.create(); err != nil { 1156 t.Fatalf("failed to create test file: %s", err) 1157 } 1158 1159 if _, err := Stat(to); err != nil { 1160 // Sanity check that the underlying filesystem is not case sensitive. 1161 if IsNotExist(err) { 1162 t.Skipf("case sensitive filesystem") 1163 } 1164 t.Fatalf("stat %q, got: %q", to, err) 1165 } 1166 1167 if err := Rename(from, to); err != nil { 1168 t.Fatalf("unexpected error when renaming from %q to %q: %s", from, to, err) 1169 } 1170 1171 fd, err := Open(".") 1172 if err != nil { 1173 t.Fatalf("Open .: %s", err) 1174 } 1175 1176 // Stat does not return the real case of the file (it returns what the called asked for) 1177 // So we have to use readdir to get the real name of the file. 1178 dirNames, err := fd.Readdirnames(-1) 1179 fd.Close() 1180 if err != nil { 1181 t.Fatalf("readdirnames: %s", err) 1182 } 1183 1184 if dirNamesLen := len(dirNames); dirNamesLen != 1 { 1185 t.Fatalf("unexpected dirNames len, got %q, want %q", dirNamesLen, 1) 1186 } 1187 1188 if dirNames[0] != to { 1189 t.Errorf("unexpected name, got %q, want %q", dirNames[0], to) 1190 } 1191 }) 1192 } 1193} 1194 1195func testStartProcess(dir, cmd string, args []string, expect string) func(t *testing.T) { 1196 return func(t *testing.T) { 1197 t.Parallel() 1198 1199 r, w, err := Pipe() 1200 if err != nil { 1201 t.Fatalf("Pipe: %v", err) 1202 } 1203 defer r.Close() 1204 attr := &ProcAttr{Dir: dir, Files: []*File{nil, w, Stderr}} 1205 p, err := StartProcess(cmd, args, attr) 1206 if err != nil { 1207 t.Fatalf("StartProcess: %v", err) 1208 } 1209 w.Close() 1210 1211 var b strings.Builder 1212 io.Copy(&b, r) 1213 output := b.String() 1214 1215 fi1, _ := Stat(strings.TrimSpace(output)) 1216 fi2, _ := Stat(expect) 1217 if !SameFile(fi1, fi2) { 1218 t.Errorf("exec %q returned %q wanted %q", 1219 strings.Join(append([]string{cmd}, args...), " "), output, expect) 1220 } 1221 p.Wait() 1222 } 1223} 1224 1225func TestStartProcess(t *testing.T) { 1226 testenv.MustHaveExec(t) 1227 t.Parallel() 1228 1229 var dir, cmd string 1230 var args []string 1231 switch runtime.GOOS { 1232 case "android": 1233 t.Skip("android doesn't have /bin/pwd") 1234 case "windows": 1235 cmd = Getenv("COMSPEC") 1236 dir = Getenv("SystemRoot") 1237 args = []string{"/c", "cd"} 1238 default: 1239 var err error 1240 cmd, err = exec.LookPath("pwd") 1241 if err != nil { 1242 t.Fatalf("Can't find pwd: %v", err) 1243 } 1244 dir = "/" 1245 args = []string{} 1246 t.Logf("Testing with %v", cmd) 1247 } 1248 cmddir, cmdbase := filepath.Split(cmd) 1249 args = append([]string{cmdbase}, args...) 1250 t.Run("absolute", testStartProcess(dir, cmd, args, dir)) 1251 t.Run("relative", testStartProcess(cmddir, cmdbase, args, cmddir)) 1252} 1253 1254func checkMode(t *testing.T, path string, mode FileMode) { 1255 dir, err := Stat(path) 1256 if err != nil { 1257 t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err) 1258 } 1259 if dir.Mode()&ModePerm != mode { 1260 t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode(), mode) 1261 } 1262} 1263 1264func TestChmod(t *testing.T) { 1265 // Chmod is not supported on wasip1. 1266 if runtime.GOOS == "wasip1" { 1267 t.Skip("Chmod is not supported on " + runtime.GOOS) 1268 } 1269 t.Parallel() 1270 1271 f := newFile(t) 1272 // Creation mode is read write 1273 1274 fm := FileMode(0456) 1275 if runtime.GOOS == "windows" { 1276 fm = FileMode(0444) // read-only file 1277 } 1278 if err := Chmod(f.Name(), fm); err != nil { 1279 t.Fatalf("chmod %s %#o: %s", f.Name(), fm, err) 1280 } 1281 checkMode(t, f.Name(), fm) 1282 1283 fm = FileMode(0123) 1284 if runtime.GOOS == "windows" { 1285 fm = FileMode(0666) // read-write file 1286 } 1287 if err := f.Chmod(fm); err != nil { 1288 t.Fatalf("chmod %s %#o: %s", f.Name(), fm, err) 1289 } 1290 checkMode(t, f.Name(), fm) 1291} 1292 1293func checkSize(t *testing.T, f *File, size int64) { 1294 t.Helper() 1295 dir, err := f.Stat() 1296 if err != nil { 1297 t.Fatalf("Stat %q (looking for size %d): %s", f.Name(), size, err) 1298 } 1299 if dir.Size() != size { 1300 t.Errorf("Stat %q: size %d want %d", f.Name(), dir.Size(), size) 1301 } 1302} 1303 1304func TestFTruncate(t *testing.T) { 1305 t.Parallel() 1306 1307 f := newFile(t) 1308 1309 checkSize(t, f, 0) 1310 f.Write([]byte("hello, world\n")) 1311 checkSize(t, f, 13) 1312 f.Truncate(10) 1313 checkSize(t, f, 10) 1314 f.Truncate(1024) 1315 checkSize(t, f, 1024) 1316 f.Truncate(0) 1317 checkSize(t, f, 0) 1318 _, err := f.Write([]byte("surprise!")) 1319 if err == nil { 1320 checkSize(t, f, 13+9) // wrote at offset past where hello, world was. 1321 } 1322} 1323 1324func TestTruncate(t *testing.T) { 1325 t.Parallel() 1326 1327 f := newFile(t) 1328 1329 checkSize(t, f, 0) 1330 f.Write([]byte("hello, world\n")) 1331 checkSize(t, f, 13) 1332 Truncate(f.Name(), 10) 1333 checkSize(t, f, 10) 1334 Truncate(f.Name(), 1024) 1335 checkSize(t, f, 1024) 1336 Truncate(f.Name(), 0) 1337 checkSize(t, f, 0) 1338 _, err := f.Write([]byte("surprise!")) 1339 if err == nil { 1340 checkSize(t, f, 13+9) // wrote at offset past where hello, world was. 1341 } 1342} 1343 1344func TestTruncateNonexistentFile(t *testing.T) { 1345 t.Parallel() 1346 1347 assertPathError := func(t testing.TB, path string, err error) { 1348 t.Helper() 1349 if pe, ok := err.(*PathError); !ok || !IsNotExist(err) || pe.Path != path { 1350 t.Errorf("got error: %v\nwant an ErrNotExist PathError with path %q", err, path) 1351 } 1352 } 1353 1354 path := filepath.Join(t.TempDir(), "nonexistent") 1355 1356 err := Truncate(path, 1) 1357 assertPathError(t, path, err) 1358 1359 // Truncate shouldn't create any new file. 1360 _, err = Stat(path) 1361 assertPathError(t, path, err) 1362} 1363 1364var hasNoatime = sync.OnceValue(func() bool { 1365 // A sloppy way to check if noatime flag is set (as all filesystems are 1366 // checked, not just the one we're interested in). A correct way 1367 // would be to use statvfs syscall and check if flags has ST_NOATIME, 1368 // but the syscall is OS-specific and is not even wired into Go stdlib. 1369 // 1370 // Only used on NetBSD (which ignores explicit atime updates with noatime). 1371 mounts, _ := ReadFile("/proc/mounts") 1372 return bytes.Contains(mounts, []byte("noatime")) 1373}) 1374 1375func TestChtimes(t *testing.T) { 1376 t.Parallel() 1377 1378 f := newFile(t) 1379 // This should be an empty file (see #68687, #68663). 1380 f.Close() 1381 1382 testChtimes(t, f.Name()) 1383} 1384 1385func TestChtimesOmit(t *testing.T) { 1386 t.Parallel() 1387 1388 testChtimesOmit(t, true, false) 1389 testChtimesOmit(t, false, true) 1390 testChtimesOmit(t, true, true) 1391 testChtimesOmit(t, false, false) // Same as TestChtimes. 1392} 1393 1394func testChtimesOmit(t *testing.T, omitAt, omitMt bool) { 1395 t.Logf("omit atime: %v, mtime: %v", omitAt, omitMt) 1396 file := newFile(t) 1397 // This should be an empty file (see #68687, #68663). 1398 name := file.Name() 1399 err := file.Close() 1400 if err != nil { 1401 t.Error(err) 1402 } 1403 fs, err := Stat(name) 1404 if err != nil { 1405 t.Fatal(err) 1406 } 1407 1408 wantAtime := Atime(fs) 1409 wantMtime := fs.ModTime() 1410 switch runtime.GOOS { 1411 case "js": 1412 wantAtime = wantAtime.Truncate(time.Second) 1413 wantMtime = wantMtime.Truncate(time.Second) 1414 } 1415 1416 var setAtime, setMtime time.Time // Zero value means omit. 1417 if !omitAt { 1418 wantAtime = wantAtime.Add(-1 * time.Second) 1419 setAtime = wantAtime 1420 } 1421 if !omitMt { 1422 wantMtime = wantMtime.Add(-1 * time.Second) 1423 setMtime = wantMtime 1424 } 1425 1426 // Change the times accordingly. 1427 if err := Chtimes(name, setAtime, setMtime); err != nil { 1428 t.Error(err) 1429 } 1430 1431 // Verify the expectations. 1432 fs, err = Stat(name) 1433 if err != nil { 1434 t.Error(err) 1435 } 1436 gotAtime := Atime(fs) 1437 gotMtime := fs.ModTime() 1438 1439 // TODO: remove the dragonfly omitAt && omitMt exceptions below once the 1440 // fix (https://github.com/DragonFlyBSD/DragonFlyBSD/commit/c7c71870ed0) 1441 // is available generally and on CI runners. 1442 if !gotAtime.Equal(wantAtime) { 1443 errormsg := fmt.Sprintf("atime mismatch, got: %q, want: %q", gotAtime, wantAtime) 1444 switch runtime.GOOS { 1445 case "plan9": 1446 // Mtime is the time of the last change of content. 1447 // Similarly, atime is set whenever the contents are 1448 // accessed; also, it is set whenever mtime is set. 1449 case "dragonfly": 1450 if omitAt && omitMt { 1451 t.Log(errormsg) 1452 t.Log("Known DragonFly BSD issue (won't work when both times are omitted); ignoring.") 1453 } else { 1454 // Assume hammer2 fs; https://www.dragonflybsd.org/hammer/ says: 1455 // > Because HAMMER2 is a block copy-on-write filesystem, 1456 // > the "atime" field is not supported and will typically 1457 // > just reflect local system in-memory caches or mtime. 1458 // 1459 // TODO: if only can CI define TMPDIR to point to a tmpfs 1460 // (e.g. /var/run/shm), this exception can be removed. 1461 t.Log(errormsg) 1462 t.Log("Known DragonFly BSD issue (atime not supported on hammer2); ignoring.") 1463 } 1464 case "netbsd": 1465 if !omitAt && hasNoatime() { 1466 t.Log(errormsg) 1467 t.Log("Known NetBSD issue (atime not changed on fs mounted with noatime); ignoring.") 1468 } else { 1469 t.Error(errormsg) 1470 } 1471 default: 1472 t.Error(errormsg) 1473 } 1474 } 1475 if !gotMtime.Equal(wantMtime) { 1476 errormsg := fmt.Sprintf("mtime mismatch, got: %q, want: %q", gotMtime, wantMtime) 1477 switch runtime.GOOS { 1478 case "dragonfly": 1479 if omitAt && omitMt { 1480 t.Log(errormsg) 1481 t.Log("Known DragonFly BSD issue (won't work when both times are omitted); ignoring.") 1482 } else { 1483 t.Error(errormsg) 1484 } 1485 default: 1486 t.Error(errormsg) 1487 } 1488 } 1489} 1490 1491func TestChtimesDir(t *testing.T) { 1492 t.Parallel() 1493 1494 testChtimes(t, t.TempDir()) 1495} 1496 1497func testChtimes(t *testing.T, name string) { 1498 st, err := Stat(name) 1499 if err != nil { 1500 t.Fatalf("Stat %s: %s", name, err) 1501 } 1502 preStat := st 1503 1504 // Move access and modification time back a second 1505 at := Atime(preStat) 1506 mt := preStat.ModTime() 1507 err = Chtimes(name, at.Add(-time.Second), mt.Add(-time.Second)) 1508 if err != nil { 1509 t.Fatalf("Chtimes %s: %s", name, err) 1510 } 1511 1512 st, err = Stat(name) 1513 if err != nil { 1514 t.Fatalf("second Stat %s: %s", name, err) 1515 } 1516 postStat := st 1517 1518 pat := Atime(postStat) 1519 pmt := postStat.ModTime() 1520 if !pat.Before(at) { 1521 errormsg := fmt.Sprintf("AccessTime didn't go backwards; was=%v, after=%v", at, pat) 1522 switch runtime.GOOS { 1523 case "plan9": 1524 // Mtime is the time of the last change of 1525 // content. Similarly, atime is set whenever 1526 // the contents are accessed; also, it is set 1527 // whenever mtime is set. 1528 case "netbsd": 1529 if hasNoatime() { 1530 t.Log(errormsg) 1531 t.Log("Known NetBSD issue (atime not changed on fs mounted with noatime); ignoring.") 1532 } else { 1533 t.Errorf(errormsg) 1534 } 1535 default: 1536 t.Errorf(errormsg) 1537 } 1538 } 1539 1540 if !pmt.Before(mt) { 1541 t.Errorf("ModTime didn't go backwards; was=%v, after=%v", mt, pmt) 1542 } 1543} 1544 1545func TestChtimesToUnixZero(t *testing.T) { 1546 file := newFile(t) 1547 fn := file.Name() 1548 if _, err := file.Write([]byte("hi")); err != nil { 1549 t.Fatal(err) 1550 } 1551 if err := file.Close(); err != nil { 1552 t.Fatal(err) 1553 } 1554 1555 unixZero := time.Unix(0, 0) 1556 if err := Chtimes(fn, unixZero, unixZero); err != nil { 1557 t.Fatalf("Chtimes failed: %v", err) 1558 } 1559 1560 st, err := Stat(fn) 1561 if err != nil { 1562 t.Fatal(err) 1563 } 1564 1565 if mt := st.ModTime(); mt != unixZero { 1566 t.Errorf("mtime is %v, want %v", mt, unixZero) 1567 } 1568} 1569 1570func TestFileChdir(t *testing.T) { 1571 wd, err := Getwd() 1572 if err != nil { 1573 t.Fatalf("Getwd: %s", err) 1574 } 1575 defer Chdir(wd) 1576 1577 fd, err := Open(".") 1578 if err != nil { 1579 t.Fatalf("Open .: %s", err) 1580 } 1581 defer fd.Close() 1582 1583 if err := Chdir("/"); err != nil { 1584 t.Fatalf("Chdir /: %s", err) 1585 } 1586 1587 if err := fd.Chdir(); err != nil { 1588 t.Fatalf("fd.Chdir: %s", err) 1589 } 1590 1591 wdNew, err := Getwd() 1592 if err != nil { 1593 t.Fatalf("Getwd: %s", err) 1594 } 1595 1596 wdInfo, err := fd.Stat() 1597 if err != nil { 1598 t.Fatal(err) 1599 } 1600 newInfo, err := Stat(wdNew) 1601 if err != nil { 1602 t.Fatal(err) 1603 } 1604 if !SameFile(wdInfo, newInfo) { 1605 t.Fatalf("fd.Chdir failed: got %s, want %s", wdNew, wd) 1606 } 1607} 1608 1609func TestChdirAndGetwd(t *testing.T) { 1610 fd, err := Open(".") 1611 if err != nil { 1612 t.Fatalf("Open .: %s", err) 1613 } 1614 // These are chosen carefully not to be symlinks on a Mac 1615 // (unlike, say, /var, /etc), except /tmp, which we handle below. 1616 dirs := []string{"/", "/usr/bin", "/tmp"} 1617 // /usr/bin does not usually exist on Plan 9 or Android. 1618 switch runtime.GOOS { 1619 case "android": 1620 dirs = []string{"/system/bin"} 1621 case "plan9": 1622 dirs = []string{"/", "/usr"} 1623 case "ios", "windows", "wasip1": 1624 dirs = nil 1625 for _, dir := range []string{t.TempDir(), t.TempDir()} { 1626 // Expand symlinks so path equality tests work. 1627 dir, err = filepath.EvalSymlinks(dir) 1628 if err != nil { 1629 t.Fatalf("EvalSymlinks: %v", err) 1630 } 1631 dirs = append(dirs, dir) 1632 } 1633 } 1634 oldwd := Getenv("PWD") 1635 for mode := 0; mode < 2; mode++ { 1636 for _, d := range dirs { 1637 if mode == 0 { 1638 err = Chdir(d) 1639 } else { 1640 fd1, err1 := Open(d) 1641 if err1 != nil { 1642 t.Errorf("Open %s: %s", d, err1) 1643 continue 1644 } 1645 err = fd1.Chdir() 1646 fd1.Close() 1647 } 1648 if d == "/tmp" { 1649 Setenv("PWD", "/tmp") 1650 } 1651 pwd, err1 := Getwd() 1652 Setenv("PWD", oldwd) 1653 err2 := fd.Chdir() 1654 if err2 != nil { 1655 // We changed the current directory and cannot go back. 1656 // Don't let the tests continue; they'll scribble 1657 // all over some other directory. 1658 fmt.Fprintf(Stderr, "fchdir back to dot failed: %s\n", err2) 1659 Exit(1) 1660 } 1661 if err != nil { 1662 fd.Close() 1663 t.Fatalf("Chdir %s: %s", d, err) 1664 } 1665 if err1 != nil { 1666 fd.Close() 1667 t.Fatalf("Getwd in %s: %s", d, err1) 1668 } 1669 if !equal(pwd, d) { 1670 fd.Close() 1671 t.Fatalf("Getwd returned %q want %q", pwd, d) 1672 } 1673 } 1674 } 1675 fd.Close() 1676} 1677 1678// Test that Chdir+Getwd is program-wide. 1679func TestProgWideChdir(t *testing.T) { 1680 const N = 10 1681 var wg sync.WaitGroup 1682 hold := make(chan struct{}) 1683 done := make(chan struct{}) 1684 1685 d := t.TempDir() 1686 oldwd, err := Getwd() 1687 if err != nil { 1688 t.Fatalf("Getwd: %v", err) 1689 } 1690 defer func() { 1691 if err := Chdir(oldwd); err != nil { 1692 // It's not safe to continue with tests if we can't get back to 1693 // the original working directory. 1694 panic(err) 1695 } 1696 }() 1697 1698 // Note the deferred Wait must be called after the deferred close(done), 1699 // to ensure the N goroutines have been released even if the main goroutine 1700 // calls Fatalf. It must be called before the Chdir back to the original 1701 // directory, and before the deferred deletion implied by TempDir, 1702 // so as not to interfere while the N goroutines are still running. 1703 defer wg.Wait() 1704 defer close(done) 1705 1706 for i := 0; i < N; i++ { 1707 wg.Add(1) 1708 go func(i int) { 1709 defer wg.Done() 1710 // Lock half the goroutines in their own operating system 1711 // thread to exercise more scheduler possibilities. 1712 if i%2 == 1 { 1713 // On Plan 9, after calling LockOSThread, the goroutines 1714 // run on different processes which don't share the working 1715 // directory. This used to be an issue because Go expects 1716 // the working directory to be program-wide. 1717 // See issue 9428. 1718 runtime.LockOSThread() 1719 } 1720 select { 1721 case <-done: 1722 return 1723 case <-hold: 1724 } 1725 // Getwd might be wrong 1726 f0, err := Stat(".") 1727 if err != nil { 1728 t.Error(err) 1729 return 1730 } 1731 pwd, err := Getwd() 1732 if err != nil { 1733 t.Errorf("Getwd: %v", err) 1734 return 1735 } 1736 if pwd != d { 1737 t.Errorf("Getwd() = %q, want %q", pwd, d) 1738 return 1739 } 1740 f1, err := Stat(pwd) 1741 if err != nil { 1742 t.Error(err) 1743 return 1744 } 1745 if !SameFile(f0, f1) { 1746 t.Errorf(`Samefile(Stat("."), Getwd()) reports false (%s != %s)`, f0.Name(), f1.Name()) 1747 return 1748 } 1749 }(i) 1750 } 1751 if err = Chdir(d); err != nil { 1752 t.Fatalf("Chdir: %v", err) 1753 } 1754 // OS X sets TMPDIR to a symbolic link. 1755 // So we resolve our working directory again before the test. 1756 d, err = Getwd() 1757 if err != nil { 1758 t.Fatalf("Getwd: %v", err) 1759 } 1760 close(hold) 1761 wg.Wait() 1762} 1763 1764func TestSeek(t *testing.T) { 1765 t.Parallel() 1766 1767 f := newFile(t) 1768 1769 const data = "hello, world\n" 1770 io.WriteString(f, data) 1771 1772 type test struct { 1773 in int64 1774 whence int 1775 out int64 1776 } 1777 var tests = []test{ 1778 {0, io.SeekCurrent, int64(len(data))}, 1779 {0, io.SeekStart, 0}, 1780 {5, io.SeekStart, 5}, 1781 {0, io.SeekEnd, int64(len(data))}, 1782 {0, io.SeekStart, 0}, 1783 {-1, io.SeekEnd, int64(len(data)) - 1}, 1784 {1 << 33, io.SeekStart, 1 << 33}, 1785 {1 << 33, io.SeekEnd, 1<<33 + int64(len(data))}, 1786 1787 // Issue 21681, Windows 4G-1, etc: 1788 {1<<32 - 1, io.SeekStart, 1<<32 - 1}, 1789 {0, io.SeekCurrent, 1<<32 - 1}, 1790 {2<<32 - 1, io.SeekStart, 2<<32 - 1}, 1791 {0, io.SeekCurrent, 2<<32 - 1}, 1792 } 1793 for i, tt := range tests { 1794 off, err := f.Seek(tt.in, tt.whence) 1795 if off != tt.out || err != nil { 1796 if e, ok := err.(*PathError); ok && e.Err == syscall.EINVAL && tt.out > 1<<32 && runtime.GOOS == "linux" { 1797 mounts, _ := ReadFile("/proc/mounts") 1798 if strings.Contains(string(mounts), "reiserfs") { 1799 // Reiserfs rejects the big seeks. 1800 t.Skipf("skipping test known to fail on reiserfs; https://golang.org/issue/91") 1801 } 1802 } 1803 t.Errorf("#%d: Seek(%v, %v) = %v, %v want %v, nil", i, tt.in, tt.whence, off, err, tt.out) 1804 } 1805 } 1806} 1807 1808func TestSeekError(t *testing.T) { 1809 switch runtime.GOOS { 1810 case "js", "plan9", "wasip1": 1811 t.Skipf("skipping test on %v", runtime.GOOS) 1812 } 1813 t.Parallel() 1814 1815 r, w, err := Pipe() 1816 if err != nil { 1817 t.Fatal(err) 1818 } 1819 _, err = r.Seek(0, 0) 1820 if err == nil { 1821 t.Fatal("Seek on pipe should fail") 1822 } 1823 if perr, ok := err.(*PathError); !ok || perr.Err != syscall.ESPIPE { 1824 t.Errorf("Seek returned error %v, want &PathError{Err: syscall.ESPIPE}", err) 1825 } 1826 _, err = w.Seek(0, 0) 1827 if err == nil { 1828 t.Fatal("Seek on pipe should fail") 1829 } 1830 if perr, ok := err.(*PathError); !ok || perr.Err != syscall.ESPIPE { 1831 t.Errorf("Seek returned error %v, want &PathError{Err: syscall.ESPIPE}", err) 1832 } 1833} 1834 1835type openErrorTest struct { 1836 path string 1837 mode int 1838 error error 1839} 1840 1841var openErrorTests = []openErrorTest{ 1842 { 1843 sfdir + "/no-such-file", 1844 O_RDONLY, 1845 syscall.ENOENT, 1846 }, 1847 { 1848 sfdir, 1849 O_WRONLY, 1850 syscall.EISDIR, 1851 }, 1852 { 1853 sfdir + "/" + sfname + "/no-such-file", 1854 O_WRONLY, 1855 syscall.ENOTDIR, 1856 }, 1857} 1858 1859func TestOpenError(t *testing.T) { 1860 t.Parallel() 1861 1862 for _, tt := range openErrorTests { 1863 f, err := OpenFile(tt.path, tt.mode, 0) 1864 if err == nil { 1865 t.Errorf("Open(%q, %d) succeeded", tt.path, tt.mode) 1866 f.Close() 1867 continue 1868 } 1869 perr, ok := err.(*PathError) 1870 if !ok { 1871 t.Errorf("Open(%q, %d) returns error of %T type; want *PathError", tt.path, tt.mode, err) 1872 } 1873 if perr.Err != tt.error { 1874 if runtime.GOOS == "plan9" { 1875 syscallErrStr := perr.Err.Error() 1876 expectedErrStr := strings.Replace(tt.error.Error(), "file ", "", 1) 1877 if !strings.HasSuffix(syscallErrStr, expectedErrStr) { 1878 // Some Plan 9 file servers incorrectly return 1879 // EACCES rather than EISDIR when a directory is 1880 // opened for write. 1881 if tt.error == syscall.EISDIR && strings.HasSuffix(syscallErrStr, syscall.EACCES.Error()) { 1882 continue 1883 } 1884 t.Errorf("Open(%q, %d) = _, %q; want suffix %q", tt.path, tt.mode, syscallErrStr, expectedErrStr) 1885 } 1886 continue 1887 } 1888 if runtime.GOOS == "dragonfly" { 1889 // DragonFly incorrectly returns EACCES rather 1890 // EISDIR when a directory is opened for write. 1891 if tt.error == syscall.EISDIR && perr.Err == syscall.EACCES { 1892 continue 1893 } 1894 } 1895 t.Errorf("Open(%q, %d) = _, %q; want %q", tt.path, tt.mode, perr.Err.Error(), tt.error.Error()) 1896 } 1897 } 1898} 1899 1900func TestOpenNoName(t *testing.T) { 1901 f, err := Open("") 1902 if err == nil { 1903 f.Close() 1904 t.Fatal(`Open("") succeeded`) 1905 } 1906} 1907 1908func runBinHostname(t *testing.T) string { 1909 // Run /bin/hostname and collect output. 1910 r, w, err := Pipe() 1911 if err != nil { 1912 t.Fatal(err) 1913 } 1914 defer r.Close() 1915 1916 path, err := exec.LookPath("hostname") 1917 if err != nil { 1918 if errors.Is(err, exec.ErrNotFound) { 1919 t.Skip("skipping test; test requires hostname but it does not exist") 1920 } 1921 t.Fatal(err) 1922 } 1923 1924 argv := []string{"hostname"} 1925 if runtime.GOOS == "aix" { 1926 argv = []string{"hostname", "-s"} 1927 } 1928 p, err := StartProcess(path, argv, &ProcAttr{Files: []*File{nil, w, Stderr}}) 1929 if err != nil { 1930 t.Fatal(err) 1931 } 1932 w.Close() 1933 1934 var b strings.Builder 1935 io.Copy(&b, r) 1936 _, err = p.Wait() 1937 if err != nil { 1938 t.Fatalf("run hostname Wait: %v", err) 1939 } 1940 err = p.Kill() 1941 if err == nil { 1942 t.Errorf("expected an error from Kill running 'hostname'") 1943 } 1944 output := b.String() 1945 if n := len(output); n > 0 && output[n-1] == '\n' { 1946 output = output[0 : n-1] 1947 } 1948 if output == "" { 1949 t.Fatalf("/bin/hostname produced no output") 1950 } 1951 1952 return output 1953} 1954 1955func testWindowsHostname(t *testing.T, hostname string) { 1956 cmd := testenv.Command(t, "hostname") 1957 out, err := cmd.Output() 1958 if err != nil { 1959 t.Fatalf("Failed to execute hostname command: %v %s", err, out) 1960 } 1961 want := strings.Trim(string(out), "\r\n") 1962 if hostname != want { 1963 t.Fatalf("Hostname() = %q != system hostname of %q", hostname, want) 1964 } 1965} 1966 1967func TestHostname(t *testing.T) { 1968 t.Parallel() 1969 1970 hostname, err := Hostname() 1971 if err != nil { 1972 t.Fatal(err) 1973 } 1974 if hostname == "" { 1975 t.Fatal("Hostname returned empty string and no error") 1976 } 1977 if strings.Contains(hostname, "\x00") { 1978 t.Fatalf("unexpected zero byte in hostname: %q", hostname) 1979 } 1980 1981 // There is no other way to fetch hostname on windows, but via winapi. 1982 // On Plan 9 it can be taken from #c/sysname as Hostname() does. 1983 switch runtime.GOOS { 1984 case "android", "plan9": 1985 // No /bin/hostname to verify against. 1986 return 1987 case "windows": 1988 testWindowsHostname(t, hostname) 1989 return 1990 } 1991 1992 testenv.MustHaveExec(t) 1993 1994 // Check internal Hostname() against the output of /bin/hostname. 1995 // Allow that the internal Hostname returns a Fully Qualified Domain Name 1996 // and the /bin/hostname only returns the first component 1997 want := runBinHostname(t) 1998 if hostname != want { 1999 host, _, ok := strings.Cut(hostname, ".") 2000 if !ok || host != want { 2001 t.Errorf("Hostname() = %q, want %q", hostname, want) 2002 } 2003 } 2004} 2005 2006func TestReadAt(t *testing.T) { 2007 t.Parallel() 2008 2009 f := newFile(t) 2010 2011 const data = "hello, world\n" 2012 io.WriteString(f, data) 2013 2014 b := make([]byte, 5) 2015 n, err := f.ReadAt(b, 7) 2016 if err != nil || n != len(b) { 2017 t.Fatalf("ReadAt 7: %d, %v", n, err) 2018 } 2019 if string(b) != "world" { 2020 t.Fatalf("ReadAt 7: have %q want %q", string(b), "world") 2021 } 2022} 2023 2024// Verify that ReadAt doesn't affect seek offset. 2025// In the Plan 9 kernel, there used to be a bug in the implementation of 2026// the pread syscall, where the channel offset was erroneously updated after 2027// calling pread on a file. 2028func TestReadAtOffset(t *testing.T) { 2029 t.Parallel() 2030 2031 f := newFile(t) 2032 2033 const data = "hello, world\n" 2034 io.WriteString(f, data) 2035 2036 f.Seek(0, 0) 2037 b := make([]byte, 5) 2038 2039 n, err := f.ReadAt(b, 7) 2040 if err != nil || n != len(b) { 2041 t.Fatalf("ReadAt 7: %d, %v", n, err) 2042 } 2043 if string(b) != "world" { 2044 t.Fatalf("ReadAt 7: have %q want %q", string(b), "world") 2045 } 2046 2047 n, err = f.Read(b) 2048 if err != nil || n != len(b) { 2049 t.Fatalf("Read: %d, %v", n, err) 2050 } 2051 if string(b) != "hello" { 2052 t.Fatalf("Read: have %q want %q", string(b), "hello") 2053 } 2054} 2055 2056// Verify that ReadAt doesn't allow negative offset. 2057func TestReadAtNegativeOffset(t *testing.T) { 2058 t.Parallel() 2059 2060 f := newFile(t) 2061 2062 const data = "hello, world\n" 2063 io.WriteString(f, data) 2064 2065 f.Seek(0, 0) 2066 b := make([]byte, 5) 2067 2068 n, err := f.ReadAt(b, -10) 2069 2070 const wantsub = "negative offset" 2071 if !strings.Contains(fmt.Sprint(err), wantsub) || n != 0 { 2072 t.Errorf("ReadAt(-10) = %v, %v; want 0, ...%q...", n, err, wantsub) 2073 } 2074} 2075 2076func TestWriteAt(t *testing.T) { 2077 t.Parallel() 2078 2079 f := newFile(t) 2080 2081 const data = "hello, world\n" 2082 io.WriteString(f, data) 2083 2084 n, err := f.WriteAt([]byte("WORLD"), 7) 2085 if err != nil || n != 5 { 2086 t.Fatalf("WriteAt 7: %d, %v", n, err) 2087 } 2088 2089 b, err := ReadFile(f.Name()) 2090 if err != nil { 2091 t.Fatalf("ReadFile %s: %v", f.Name(), err) 2092 } 2093 if string(b) != "hello, WORLD\n" { 2094 t.Fatalf("after write: have %q want %q", string(b), "hello, WORLD\n") 2095 } 2096} 2097 2098// Verify that WriteAt doesn't allow negative offset. 2099func TestWriteAtNegativeOffset(t *testing.T) { 2100 t.Parallel() 2101 2102 f := newFile(t) 2103 2104 n, err := f.WriteAt([]byte("WORLD"), -10) 2105 2106 const wantsub = "negative offset" 2107 if !strings.Contains(fmt.Sprint(err), wantsub) || n != 0 { 2108 t.Errorf("WriteAt(-10) = %v, %v; want 0, ...%q...", n, err, wantsub) 2109 } 2110} 2111 2112// Verify that WriteAt doesn't work in append mode. 2113func TestWriteAtInAppendMode(t *testing.T) { 2114 defer chtmpdir(t)() 2115 f, err := OpenFile("write_at_in_append_mode.txt", O_APPEND|O_CREATE, 0666) 2116 if err != nil { 2117 t.Fatalf("OpenFile: %v", err) 2118 } 2119 defer f.Close() 2120 2121 _, err = f.WriteAt([]byte(""), 1) 2122 if err != ErrWriteAtInAppendMode { 2123 t.Fatalf("f.WriteAt returned %v, expected %v", err, ErrWriteAtInAppendMode) 2124 } 2125} 2126 2127func writeFile(t *testing.T, fname string, flag int, text string) string { 2128 f, err := OpenFile(fname, flag, 0666) 2129 if err != nil { 2130 t.Fatalf("Open: %v", err) 2131 } 2132 n, err := io.WriteString(f, text) 2133 if err != nil { 2134 t.Fatalf("WriteString: %d, %v", n, err) 2135 } 2136 f.Close() 2137 data, err := ReadFile(fname) 2138 if err != nil { 2139 t.Fatalf("ReadFile: %v", err) 2140 } 2141 return string(data) 2142} 2143 2144func TestAppend(t *testing.T) { 2145 defer chtmpdir(t)() 2146 const f = "append.txt" 2147 s := writeFile(t, f, O_CREATE|O_TRUNC|O_RDWR, "new") 2148 if s != "new" { 2149 t.Fatalf("writeFile: have %q want %q", s, "new") 2150 } 2151 s = writeFile(t, f, O_APPEND|O_RDWR, "|append") 2152 if s != "new|append" { 2153 t.Fatalf("writeFile: have %q want %q", s, "new|append") 2154 } 2155 s = writeFile(t, f, O_CREATE|O_APPEND|O_RDWR, "|append") 2156 if s != "new|append|append" { 2157 t.Fatalf("writeFile: have %q want %q", s, "new|append|append") 2158 } 2159 err := Remove(f) 2160 if err != nil { 2161 t.Fatalf("Remove: %v", err) 2162 } 2163 s = writeFile(t, f, O_CREATE|O_APPEND|O_RDWR, "new&append") 2164 if s != "new&append" { 2165 t.Fatalf("writeFile: after append have %q want %q", s, "new&append") 2166 } 2167 s = writeFile(t, f, O_CREATE|O_RDWR, "old") 2168 if s != "old&append" { 2169 t.Fatalf("writeFile: after create have %q want %q", s, "old&append") 2170 } 2171 s = writeFile(t, f, O_CREATE|O_TRUNC|O_RDWR, "new") 2172 if s != "new" { 2173 t.Fatalf("writeFile: after truncate have %q want %q", s, "new") 2174 } 2175} 2176 2177func TestStatDirWithTrailingSlash(t *testing.T) { 2178 t.Parallel() 2179 2180 // Create new temporary directory and arrange to clean it up. 2181 path := t.TempDir() 2182 2183 // Stat of path should succeed. 2184 if _, err := Stat(path); err != nil { 2185 t.Fatalf("stat %s failed: %s", path, err) 2186 } 2187 2188 // Stat of path+"/" should succeed too. 2189 path += "/" 2190 if _, err := Stat(path); err != nil { 2191 t.Fatalf("stat %s failed: %s", path, err) 2192 } 2193} 2194 2195func TestNilProcessStateString(t *testing.T) { 2196 var ps *ProcessState 2197 s := ps.String() 2198 if s != "<nil>" { 2199 t.Errorf("(*ProcessState)(nil).String() = %q, want %q", s, "<nil>") 2200 } 2201} 2202 2203func TestSameFile(t *testing.T) { 2204 defer chtmpdir(t)() 2205 fa, err := Create("a") 2206 if err != nil { 2207 t.Fatalf("Create(a): %v", err) 2208 } 2209 fa.Close() 2210 fb, err := Create("b") 2211 if err != nil { 2212 t.Fatalf("Create(b): %v", err) 2213 } 2214 fb.Close() 2215 2216 ia1, err := Stat("a") 2217 if err != nil { 2218 t.Fatalf("Stat(a): %v", err) 2219 } 2220 ia2, err := Stat("a") 2221 if err != nil { 2222 t.Fatalf("Stat(a): %v", err) 2223 } 2224 if !SameFile(ia1, ia2) { 2225 t.Errorf("files should be same") 2226 } 2227 2228 ib, err := Stat("b") 2229 if err != nil { 2230 t.Fatalf("Stat(b): %v", err) 2231 } 2232 if SameFile(ia1, ib) { 2233 t.Errorf("files should be different") 2234 } 2235} 2236 2237func testDevNullFileInfo(t *testing.T, statname, devNullName string, fi FileInfo) { 2238 pre := fmt.Sprintf("%s(%q): ", statname, devNullName) 2239 if fi.Size() != 0 { 2240 t.Errorf(pre+"wrong file size have %d want 0", fi.Size()) 2241 } 2242 if fi.Mode()&ModeDevice == 0 { 2243 t.Errorf(pre+"wrong file mode %q: ModeDevice is not set", fi.Mode()) 2244 } 2245 if fi.Mode()&ModeCharDevice == 0 { 2246 t.Errorf(pre+"wrong file mode %q: ModeCharDevice is not set", fi.Mode()) 2247 } 2248 if fi.Mode().IsRegular() { 2249 t.Errorf(pre+"wrong file mode %q: IsRegular returns true", fi.Mode()) 2250 } 2251} 2252 2253func testDevNullFile(t *testing.T, devNullName string) { 2254 f, err := Open(devNullName) 2255 if err != nil { 2256 t.Fatalf("Open(%s): %v", devNullName, err) 2257 } 2258 defer f.Close() 2259 2260 fi, err := f.Stat() 2261 if err != nil { 2262 t.Fatalf("Stat(%s): %v", devNullName, err) 2263 } 2264 testDevNullFileInfo(t, "f.Stat", devNullName, fi) 2265 2266 fi, err = Stat(devNullName) 2267 if err != nil { 2268 t.Fatalf("Stat(%s): %v", devNullName, err) 2269 } 2270 testDevNullFileInfo(t, "Stat", devNullName, fi) 2271} 2272 2273func TestDevNullFile(t *testing.T) { 2274 t.Parallel() 2275 2276 testDevNullFile(t, DevNull) 2277 if runtime.GOOS == "windows" { 2278 testDevNullFile(t, "./nul") 2279 testDevNullFile(t, "//./nul") 2280 } 2281} 2282 2283var testLargeWrite = flag.Bool("large_write", false, "run TestLargeWriteToConsole test that floods console with output") 2284 2285func TestLargeWriteToConsole(t *testing.T) { 2286 if !*testLargeWrite { 2287 t.Skip("skipping console-flooding test; enable with -large_write") 2288 } 2289 b := make([]byte, 32000) 2290 for i := range b { 2291 b[i] = '.' 2292 } 2293 b[len(b)-1] = '\n' 2294 n, err := Stdout.Write(b) 2295 if err != nil { 2296 t.Fatalf("Write to os.Stdout failed: %v", err) 2297 } 2298 if n != len(b) { 2299 t.Errorf("Write to os.Stdout should return %d; got %d", len(b), n) 2300 } 2301 n, err = Stderr.Write(b) 2302 if err != nil { 2303 t.Fatalf("Write to os.Stderr failed: %v", err) 2304 } 2305 if n != len(b) { 2306 t.Errorf("Write to os.Stderr should return %d; got %d", len(b), n) 2307 } 2308} 2309 2310func TestStatDirModeExec(t *testing.T) { 2311 if runtime.GOOS == "wasip1" { 2312 t.Skip("Chmod is not supported on " + runtime.GOOS) 2313 } 2314 t.Parallel() 2315 2316 const mode = 0111 2317 2318 path := t.TempDir() 2319 if err := Chmod(path, 0777); err != nil { 2320 t.Fatalf("Chmod %q 0777: %v", path, err) 2321 } 2322 2323 dir, err := Stat(path) 2324 if err != nil { 2325 t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err) 2326 } 2327 if dir.Mode()&mode != mode { 2328 t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode()&mode, mode) 2329 } 2330} 2331 2332func TestStatStdin(t *testing.T) { 2333 switch runtime.GOOS { 2334 case "android", "plan9": 2335 t.Skipf("%s doesn't have /bin/sh", runtime.GOOS) 2336 } 2337 2338 if Getenv("GO_WANT_HELPER_PROCESS") == "1" { 2339 st, err := Stdin.Stat() 2340 if err != nil { 2341 t.Fatalf("Stat failed: %v", err) 2342 } 2343 fmt.Println(st.Mode() & ModeNamedPipe) 2344 Exit(0) 2345 } 2346 2347 exe, err := Executable() 2348 if err != nil { 2349 t.Skipf("can't find executable: %v", err) 2350 } 2351 2352 testenv.MustHaveExec(t) 2353 t.Parallel() 2354 2355 fi, err := Stdin.Stat() 2356 if err != nil { 2357 t.Fatal(err) 2358 } 2359 switch mode := fi.Mode(); { 2360 case mode&ModeCharDevice != 0 && mode&ModeDevice != 0: 2361 case mode&ModeNamedPipe != 0: 2362 default: 2363 t.Fatalf("unexpected Stdin mode (%v), want ModeCharDevice or ModeNamedPipe", mode) 2364 } 2365 2366 cmd := testenv.Command(t, exe, "-test.run=^TestStatStdin$") 2367 cmd = testenv.CleanCmdEnv(cmd) 2368 cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1") 2369 // This will make standard input a pipe. 2370 cmd.Stdin = strings.NewReader("output") 2371 2372 output, err := cmd.CombinedOutput() 2373 if err != nil { 2374 t.Fatalf("Failed to spawn child process: %v %q", err, string(output)) 2375 } 2376 2377 // result will be like "prw-rw-rw" 2378 if len(output) < 1 || output[0] != 'p' { 2379 t.Fatalf("Child process reports stdin is not pipe '%v'", string(output)) 2380 } 2381} 2382 2383func TestStatRelativeSymlink(t *testing.T) { 2384 testenv.MustHaveSymlink(t) 2385 t.Parallel() 2386 2387 tmpdir := t.TempDir() 2388 target := filepath.Join(tmpdir, "target") 2389 f, err := Create(target) 2390 if err != nil { 2391 t.Fatal(err) 2392 } 2393 defer f.Close() 2394 2395 st, err := f.Stat() 2396 if err != nil { 2397 t.Fatal(err) 2398 } 2399 2400 link := filepath.Join(tmpdir, "link") 2401 err = Symlink(filepath.Base(target), link) 2402 if err != nil { 2403 t.Fatal(err) 2404 } 2405 2406 st1, err := Stat(link) 2407 if err != nil { 2408 t.Fatal(err) 2409 } 2410 2411 if !SameFile(st, st1) { 2412 t.Error("Stat doesn't follow relative symlink") 2413 } 2414 2415 if runtime.GOOS == "windows" { 2416 Remove(link) 2417 err = Symlink(target[len(filepath.VolumeName(target)):], link) 2418 if err != nil { 2419 t.Fatal(err) 2420 } 2421 2422 st1, err := Stat(link) 2423 if err != nil { 2424 t.Fatal(err) 2425 } 2426 2427 if !SameFile(st, st1) { 2428 t.Error("Stat doesn't follow relative symlink") 2429 } 2430 } 2431} 2432 2433func TestReadAtEOF(t *testing.T) { 2434 t.Parallel() 2435 2436 f := newFile(t) 2437 2438 _, err := f.ReadAt(make([]byte, 10), 0) 2439 switch err { 2440 case io.EOF: 2441 // all good 2442 case nil: 2443 t.Fatalf("ReadAt succeeded") 2444 default: 2445 t.Fatalf("ReadAt failed: %s", err) 2446 } 2447} 2448 2449func TestLongPath(t *testing.T) { 2450 t.Parallel() 2451 2452 tmpdir := t.TempDir() 2453 2454 // Test the boundary of 247 and fewer bytes (normal) and 248 and more bytes (adjusted). 2455 sizes := []int{247, 248, 249, 400} 2456 for len(tmpdir) < 400 { 2457 tmpdir += "/dir3456789" 2458 } 2459 for _, sz := range sizes { 2460 t.Run(fmt.Sprintf("length=%d", sz), func(t *testing.T) { 2461 sizedTempDir := tmpdir[:sz-1] + "x" // Ensure it does not end with a slash. 2462 2463 // The various sized runs are for this call to trigger the boundary 2464 // condition. 2465 if err := MkdirAll(sizedTempDir, 0755); err != nil { 2466 t.Fatalf("MkdirAll failed: %v", err) 2467 } 2468 data := []byte("hello world\n") 2469 if err := WriteFile(sizedTempDir+"/foo.txt", data, 0644); err != nil { 2470 t.Fatalf("os.WriteFile() failed: %v", err) 2471 } 2472 if err := Rename(sizedTempDir+"/foo.txt", sizedTempDir+"/bar.txt"); err != nil { 2473 t.Fatalf("Rename failed: %v", err) 2474 } 2475 mtime := time.Now().Truncate(time.Minute) 2476 if err := Chtimes(sizedTempDir+"/bar.txt", mtime, mtime); err != nil { 2477 t.Fatalf("Chtimes failed: %v", err) 2478 } 2479 names := []string{"bar.txt"} 2480 if testenv.HasSymlink() { 2481 if err := Symlink(sizedTempDir+"/bar.txt", sizedTempDir+"/symlink.txt"); err != nil { 2482 t.Fatalf("Symlink failed: %v", err) 2483 } 2484 names = append(names, "symlink.txt") 2485 } 2486 if testenv.HasLink() { 2487 if err := Link(sizedTempDir+"/bar.txt", sizedTempDir+"/link.txt"); err != nil { 2488 t.Fatalf("Link failed: %v", err) 2489 } 2490 names = append(names, "link.txt") 2491 } 2492 for _, wantSize := range []int64{int64(len(data)), 0} { 2493 for _, name := range names { 2494 path := sizedTempDir + "/" + name 2495 dir, err := Stat(path) 2496 if err != nil { 2497 t.Fatalf("Stat(%q) failed: %v", path, err) 2498 } 2499 filesize := size(path, t) 2500 if dir.Size() != filesize || filesize != wantSize { 2501 t.Errorf("Size(%q) is %d, len(ReadFile()) is %d, want %d", path, dir.Size(), filesize, wantSize) 2502 } 2503 if runtime.GOOS != "wasip1" { // Chmod is not supported on wasip1 2504 err = Chmod(path, dir.Mode()) 2505 if err != nil { 2506 t.Fatalf("Chmod(%q) failed: %v", path, err) 2507 } 2508 } 2509 } 2510 if err := Truncate(sizedTempDir+"/bar.txt", 0); err != nil { 2511 t.Fatalf("Truncate failed: %v", err) 2512 } 2513 } 2514 }) 2515 } 2516} 2517 2518func testKillProcess(t *testing.T, processKiller func(p *Process)) { 2519 testenv.MustHaveExec(t) 2520 t.Parallel() 2521 2522 // Re-exec the test binary to start a process that hangs until stdin is closed. 2523 cmd := testenv.Command(t, Args[0]) 2524 cmd.Env = append(cmd.Environ(), "GO_OS_TEST_DRAIN_STDIN=1") 2525 stdout, err := cmd.StdoutPipe() 2526 if err != nil { 2527 t.Fatal(err) 2528 } 2529 stdin, err := cmd.StdinPipe() 2530 if err != nil { 2531 t.Fatal(err) 2532 } 2533 err = cmd.Start() 2534 if err != nil { 2535 t.Fatalf("Failed to start test process: %v", err) 2536 } 2537 2538 defer func() { 2539 if err := cmd.Wait(); err == nil { 2540 t.Errorf("Test process succeeded, but expected to fail") 2541 } 2542 stdin.Close() // Keep stdin alive until the process has finished dying. 2543 }() 2544 2545 // Wait for the process to be started. 2546 // (It will close its stdout when it reaches TestMain.) 2547 io.Copy(io.Discard, stdout) 2548 2549 processKiller(cmd.Process) 2550} 2551 2552func TestKillStartProcess(t *testing.T) { 2553 testKillProcess(t, func(p *Process) { 2554 err := p.Kill() 2555 if err != nil { 2556 t.Fatalf("Failed to kill test process: %v", err) 2557 } 2558 }) 2559} 2560 2561func TestGetppid(t *testing.T) { 2562 if runtime.GOOS == "plan9" { 2563 // TODO: golang.org/issue/8206 2564 t.Skipf("skipping test on plan9; see issue 8206") 2565 } 2566 2567 if Getenv("GO_WANT_HELPER_PROCESS") == "1" { 2568 fmt.Print(Getppid()) 2569 Exit(0) 2570 } 2571 2572 testenv.MustHaveExec(t) 2573 t.Parallel() 2574 2575 cmd := testenv.Command(t, Args[0], "-test.run=^TestGetppid$") 2576 cmd.Env = append(Environ(), "GO_WANT_HELPER_PROCESS=1") 2577 2578 // verify that Getppid() from the forked process reports our process id 2579 output, err := cmd.CombinedOutput() 2580 if err != nil { 2581 t.Fatalf("Failed to spawn child process: %v %q", err, string(output)) 2582 } 2583 2584 childPpid := string(output) 2585 ourPid := fmt.Sprintf("%d", Getpid()) 2586 if childPpid != ourPid { 2587 t.Fatalf("Child process reports parent process id '%v', expected '%v'", childPpid, ourPid) 2588 } 2589} 2590 2591func TestKillFindProcess(t *testing.T) { 2592 testKillProcess(t, func(p *Process) { 2593 p2, err := FindProcess(p.Pid) 2594 if err != nil { 2595 t.Fatalf("Failed to find test process: %v", err) 2596 } 2597 err = p2.Kill() 2598 if err != nil { 2599 t.Fatalf("Failed to kill test process: %v", err) 2600 } 2601 }) 2602} 2603 2604var nilFileMethodTests = []struct { 2605 name string 2606 f func(*File) error 2607}{ 2608 {"Chdir", func(f *File) error { return f.Chdir() }}, 2609 {"Close", func(f *File) error { return f.Close() }}, 2610 {"Chmod", func(f *File) error { return f.Chmod(0) }}, 2611 {"Chown", func(f *File) error { return f.Chown(0, 0) }}, 2612 {"Read", func(f *File) error { _, err := f.Read(make([]byte, 0)); return err }}, 2613 {"ReadAt", func(f *File) error { _, err := f.ReadAt(make([]byte, 0), 0); return err }}, 2614 {"Readdir", func(f *File) error { _, err := f.Readdir(1); return err }}, 2615 {"Readdirnames", func(f *File) error { _, err := f.Readdirnames(1); return err }}, 2616 {"Seek", func(f *File) error { _, err := f.Seek(0, io.SeekStart); return err }}, 2617 {"Stat", func(f *File) error { _, err := f.Stat(); return err }}, 2618 {"Sync", func(f *File) error { return f.Sync() }}, 2619 {"Truncate", func(f *File) error { return f.Truncate(0) }}, 2620 {"Write", func(f *File) error { _, err := f.Write(make([]byte, 0)); return err }}, 2621 {"WriteAt", func(f *File) error { _, err := f.WriteAt(make([]byte, 0), 0); return err }}, 2622 {"WriteString", func(f *File) error { _, err := f.WriteString(""); return err }}, 2623} 2624 2625// Test that all File methods give ErrInvalid if the receiver is nil. 2626func TestNilFileMethods(t *testing.T) { 2627 t.Parallel() 2628 2629 for _, tt := range nilFileMethodTests { 2630 var file *File 2631 got := tt.f(file) 2632 if got != ErrInvalid { 2633 t.Errorf("%v should fail when f is nil; got %v", tt.name, got) 2634 } 2635 } 2636} 2637 2638func mkdirTree(t *testing.T, root string, level, max int) { 2639 if level >= max { 2640 return 2641 } 2642 level++ 2643 for i := 'a'; i < 'c'; i++ { 2644 dir := filepath.Join(root, string(i)) 2645 if err := Mkdir(dir, 0700); err != nil { 2646 t.Fatal(err) 2647 } 2648 mkdirTree(t, dir, level, max) 2649 } 2650} 2651 2652// Test that simultaneous RemoveAll do not report an error. 2653// As long as it gets removed, we should be happy. 2654func TestRemoveAllRace(t *testing.T) { 2655 if runtime.GOOS == "windows" { 2656 // Windows has very strict rules about things like 2657 // removing directories while someone else has 2658 // them open. The racing doesn't work out nicely 2659 // like it does on Unix. 2660 t.Skip("skipping on windows") 2661 } 2662 if runtime.GOOS == "dragonfly" { 2663 testenv.SkipFlaky(t, 52301) 2664 } 2665 2666 n := runtime.GOMAXPROCS(16) 2667 defer runtime.GOMAXPROCS(n) 2668 root, err := MkdirTemp("", "issue") 2669 if err != nil { 2670 t.Fatal(err) 2671 } 2672 mkdirTree(t, root, 1, 6) 2673 hold := make(chan struct{}) 2674 var wg sync.WaitGroup 2675 for i := 0; i < 4; i++ { 2676 wg.Add(1) 2677 go func() { 2678 defer wg.Done() 2679 <-hold 2680 err := RemoveAll(root) 2681 if err != nil { 2682 t.Errorf("unexpected error: %T, %q", err, err) 2683 } 2684 }() 2685 } 2686 close(hold) // let workers race to remove root 2687 wg.Wait() 2688} 2689 2690// Test that reading from a pipe doesn't use up a thread. 2691func TestPipeThreads(t *testing.T) { 2692 switch runtime.GOOS { 2693 case "illumos", "solaris": 2694 t.Skip("skipping on Solaris and illumos; issue 19111") 2695 case "windows": 2696 t.Skip("skipping on Windows; issue 19098") 2697 case "plan9": 2698 t.Skip("skipping on Plan 9; does not support runtime poller") 2699 case "js": 2700 t.Skip("skipping on js; no support for os.Pipe") 2701 case "wasip1": 2702 t.Skip("skipping on wasip1; no support for os.Pipe") 2703 } 2704 2705 threads := 100 2706 2707 // OpenBSD has a low default for max number of files. 2708 if runtime.GOOS == "openbsd" { 2709 threads = 50 2710 } 2711 2712 r := make([]*File, threads) 2713 w := make([]*File, threads) 2714 for i := 0; i < threads; i++ { 2715 rp, wp, err := Pipe() 2716 if err != nil { 2717 for j := 0; j < i; j++ { 2718 r[j].Close() 2719 w[j].Close() 2720 } 2721 t.Fatal(err) 2722 } 2723 r[i] = rp 2724 w[i] = wp 2725 } 2726 2727 defer debug.SetMaxThreads(debug.SetMaxThreads(threads / 2)) 2728 2729 creading := make(chan bool, threads) 2730 cdone := make(chan bool, threads) 2731 for i := 0; i < threads; i++ { 2732 go func(i int) { 2733 var b [1]byte 2734 creading <- true 2735 if _, err := r[i].Read(b[:]); err != nil { 2736 t.Error(err) 2737 } 2738 if err := r[i].Close(); err != nil { 2739 t.Error(err) 2740 } 2741 cdone <- true 2742 }(i) 2743 } 2744 2745 for i := 0; i < threads; i++ { 2746 <-creading 2747 } 2748 2749 // If we are still alive, it means that the 100 goroutines did 2750 // not require 100 threads. 2751 2752 for i := 0; i < threads; i++ { 2753 if _, err := w[i].Write([]byte{0}); err != nil { 2754 t.Error(err) 2755 } 2756 if err := w[i].Close(); err != nil { 2757 t.Error(err) 2758 } 2759 <-cdone 2760 } 2761} 2762 2763func testDoubleCloseError(path string) func(*testing.T) { 2764 return func(t *testing.T) { 2765 t.Parallel() 2766 2767 file, err := Open(path) 2768 if err != nil { 2769 t.Fatal(err) 2770 } 2771 if err := file.Close(); err != nil { 2772 t.Fatalf("unexpected error from Close: %v", err) 2773 } 2774 if err := file.Close(); err == nil { 2775 t.Error("second Close did not fail") 2776 } else if pe, ok := err.(*PathError); !ok { 2777 t.Errorf("second Close: got %T, want %T", err, pe) 2778 } else if pe.Err != ErrClosed { 2779 t.Errorf("second Close: got %q, want %q", pe.Err, ErrClosed) 2780 } else { 2781 t.Logf("second close returned expected error %q", err) 2782 } 2783 } 2784} 2785 2786func TestDoubleCloseError(t *testing.T) { 2787 t.Parallel() 2788 t.Run("file", testDoubleCloseError(filepath.Join(sfdir, sfname))) 2789 t.Run("dir", testDoubleCloseError(sfdir)) 2790} 2791 2792func TestUserCacheDir(t *testing.T) { 2793 t.Parallel() 2794 2795 dir, err := UserCacheDir() 2796 if err != nil { 2797 t.Skipf("skipping: %v", err) 2798 } 2799 if dir == "" { 2800 t.Fatalf("UserCacheDir returned %q; want non-empty path or error", dir) 2801 } 2802 2803 fi, err := Stat(dir) 2804 if err != nil { 2805 if IsNotExist(err) { 2806 t.Log(err) 2807 return 2808 } 2809 t.Fatal(err) 2810 } 2811 if !fi.IsDir() { 2812 t.Fatalf("dir %s is not directory; type = %v", dir, fi.Mode()) 2813 } 2814} 2815 2816func TestUserConfigDir(t *testing.T) { 2817 t.Parallel() 2818 2819 dir, err := UserConfigDir() 2820 if err != nil { 2821 t.Skipf("skipping: %v", err) 2822 } 2823 if dir == "" { 2824 t.Fatalf("UserConfigDir returned %q; want non-empty path or error", dir) 2825 } 2826 2827 fi, err := Stat(dir) 2828 if err != nil { 2829 if IsNotExist(err) { 2830 t.Log(err) 2831 return 2832 } 2833 t.Fatal(err) 2834 } 2835 if !fi.IsDir() { 2836 t.Fatalf("dir %s is not directory; type = %v", dir, fi.Mode()) 2837 } 2838} 2839 2840func TestUserHomeDir(t *testing.T) { 2841 t.Parallel() 2842 2843 dir, err := UserHomeDir() 2844 if dir == "" && err == nil { 2845 t.Fatal("UserHomeDir returned an empty string but no error") 2846 } 2847 if err != nil { 2848 // UserHomeDir may return a non-nil error if the environment variable 2849 // for the home directory is empty or unset in the environment. 2850 t.Skipf("skipping: %v", err) 2851 } 2852 2853 fi, err := Stat(dir) 2854 if err != nil { 2855 if IsNotExist(err) { 2856 // The user's home directory has a well-defined location, but does not 2857 // exist. (Maybe nothing has written to it yet? That could happen, for 2858 // example, on minimal VM images used for CI testing.) 2859 t.Log(err) 2860 return 2861 } 2862 t.Fatal(err) 2863 } 2864 if !fi.IsDir() { 2865 t.Fatalf("dir %s is not directory; type = %v", dir, fi.Mode()) 2866 } 2867} 2868 2869func TestDirSeek(t *testing.T) { 2870 t.Parallel() 2871 2872 wd, err := Getwd() 2873 if err != nil { 2874 t.Fatal(err) 2875 } 2876 f, err := Open(wd) 2877 if err != nil { 2878 t.Fatal(err) 2879 } 2880 dirnames1, err := f.Readdirnames(0) 2881 if err != nil { 2882 t.Fatal(err) 2883 } 2884 2885 ret, err := f.Seek(0, 0) 2886 if err != nil { 2887 t.Fatal(err) 2888 } 2889 if ret != 0 { 2890 t.Fatalf("seek result not zero: %d", ret) 2891 } 2892 2893 dirnames2, err := f.Readdirnames(0) 2894 if err != nil { 2895 t.Fatal(err) 2896 } 2897 2898 if len(dirnames1) != len(dirnames2) { 2899 t.Fatalf("listings have different lengths: %d and %d\n", len(dirnames1), len(dirnames2)) 2900 } 2901 for i, n1 := range dirnames1 { 2902 n2 := dirnames2[i] 2903 if n1 != n2 { 2904 t.Fatalf("different name i=%d n1=%s n2=%s\n", i, n1, n2) 2905 } 2906 } 2907} 2908 2909func TestReaddirSmallSeek(t *testing.T) { 2910 // See issue 37161. Read only one entry from a directory, 2911 // seek to the beginning, and read again. We should not see 2912 // duplicate entries. 2913 t.Parallel() 2914 2915 wd, err := Getwd() 2916 if err != nil { 2917 t.Fatal(err) 2918 } 2919 df, err := Open(filepath.Join(wd, "testdata", "issue37161")) 2920 if err != nil { 2921 t.Fatal(err) 2922 } 2923 names1, err := df.Readdirnames(1) 2924 if err != nil { 2925 t.Fatal(err) 2926 } 2927 if _, err = df.Seek(0, 0); err != nil { 2928 t.Fatal(err) 2929 } 2930 names2, err := df.Readdirnames(0) 2931 if err != nil { 2932 t.Fatal(err) 2933 } 2934 if len(names2) != 3 { 2935 t.Fatalf("first names: %v, second names: %v", names1, names2) 2936 } 2937} 2938 2939// isDeadlineExceeded reports whether err is or wraps ErrDeadlineExceeded. 2940// We also check that the error has a Timeout method that returns true. 2941func isDeadlineExceeded(err error) bool { 2942 if !IsTimeout(err) { 2943 return false 2944 } 2945 if !errors.Is(err, ErrDeadlineExceeded) { 2946 return false 2947 } 2948 return true 2949} 2950 2951// Test that opening a file does not change its permissions. Issue 38225. 2952func TestOpenFileKeepsPermissions(t *testing.T) { 2953 t.Parallel() 2954 2955 dir := t.TempDir() 2956 name := filepath.Join(dir, "x") 2957 f, err := Create(name) 2958 if err != nil { 2959 t.Fatal(err) 2960 } 2961 if err := f.Close(); err != nil { 2962 t.Error(err) 2963 } 2964 f, err = OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, 0) 2965 if err != nil { 2966 t.Fatal(err) 2967 } 2968 if fi, err := f.Stat(); err != nil { 2969 t.Error(err) 2970 } else if fi.Mode()&0222 == 0 { 2971 t.Errorf("f.Stat.Mode after OpenFile is %v, should be writable", fi.Mode()) 2972 } 2973 if err := f.Close(); err != nil { 2974 t.Error(err) 2975 } 2976 if fi, err := Stat(name); err != nil { 2977 t.Error(err) 2978 } else if fi.Mode()&0222 == 0 { 2979 t.Errorf("Stat after OpenFile is %v, should be writable", fi.Mode()) 2980 } 2981} 2982 2983func forceMFTUpdateOnWindows(t *testing.T, path string) { 2984 t.Helper() 2985 2986 if runtime.GOOS != "windows" { 2987 return 2988 } 2989 2990 // On Windows, we force the MFT to update by reading the actual metadata from GetFileInformationByHandle and then 2991 // explicitly setting that. Otherwise it might get out of sync with FindFirstFile. See golang.org/issues/42637. 2992 if err := filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error { 2993 if err != nil { 2994 t.Fatal(err) 2995 } 2996 info, err := d.Info() 2997 if err != nil { 2998 t.Fatal(err) 2999 } 3000 stat, err := Stat(path) // This uses GetFileInformationByHandle internally. 3001 if err != nil { 3002 t.Fatal(err) 3003 } 3004 if stat.ModTime() == info.ModTime() { 3005 return nil 3006 } 3007 if err := Chtimes(path, stat.ModTime(), stat.ModTime()); err != nil { 3008 t.Log(err) // We only log, not die, in case the test directory is not writable. 3009 } 3010 return nil 3011 }); err != nil { 3012 t.Fatal(err) 3013 } 3014} 3015 3016func TestDirFS(t *testing.T) { 3017 t.Parallel() 3018 3019 forceMFTUpdateOnWindows(t, "./testdata/dirfs") 3020 3021 fsys := DirFS("./testdata/dirfs") 3022 if err := fstest.TestFS(fsys, "a", "b", "dir/x"); err != nil { 3023 t.Fatal(err) 3024 } 3025 3026 rdfs, ok := fsys.(fs.ReadDirFS) 3027 if !ok { 3028 t.Error("expected DirFS result to implement fs.ReadDirFS") 3029 } 3030 if _, err := rdfs.ReadDir("nonexistent"); err == nil { 3031 t.Error("fs.ReadDir of nonexistent directory succeeded") 3032 } 3033 3034 // Test that the error message does not contain a backslash, 3035 // and does not contain the DirFS argument. 3036 const nonesuch = "dir/nonesuch" 3037 _, err := fsys.Open(nonesuch) 3038 if err == nil { 3039 t.Error("fs.Open of nonexistent file succeeded") 3040 } else { 3041 if !strings.Contains(err.Error(), nonesuch) { 3042 t.Errorf("error %q does not contain %q", err, nonesuch) 3043 } 3044 if strings.Contains(err.(*PathError).Path, "testdata") { 3045 t.Errorf("error %q contains %q", err, "testdata") 3046 } 3047 } 3048 3049 // Test that Open does not accept backslash as separator. 3050 d := DirFS(".") 3051 _, err = d.Open(`testdata\dirfs`) 3052 if err == nil { 3053 t.Fatalf(`Open testdata\dirfs succeeded`) 3054 } 3055 3056 // Test that Open does not open Windows device files. 3057 _, err = d.Open(`NUL`) 3058 if err == nil { 3059 t.Errorf(`Open NUL succeeded`) 3060 } 3061} 3062 3063func TestDirFSRootDir(t *testing.T) { 3064 t.Parallel() 3065 3066 cwd, err := Getwd() 3067 if err != nil { 3068 t.Fatal(err) 3069 } 3070 cwd = cwd[len(filepath.VolumeName(cwd)):] // trim volume prefix (C:) on Windows 3071 cwd = filepath.ToSlash(cwd) // convert \ to / 3072 cwd = strings.TrimPrefix(cwd, "/") // trim leading / 3073 3074 // Test that Open can open a path starting at /. 3075 d := DirFS("/") 3076 f, err := d.Open(cwd + "/testdata/dirfs/a") 3077 if err != nil { 3078 t.Fatal(err) 3079 } 3080 f.Close() 3081} 3082 3083func TestDirFSEmptyDir(t *testing.T) { 3084 t.Parallel() 3085 3086 d := DirFS("") 3087 cwd, _ := Getwd() 3088 for _, path := range []string{ 3089 "testdata/dirfs/a", // not DirFS(".") 3090 filepath.ToSlash(cwd) + "/testdata/dirfs/a", // not DirFS("/") 3091 } { 3092 _, err := d.Open(path) 3093 if err == nil { 3094 t.Fatalf(`DirFS("").Open(%q) succeeded`, path) 3095 } 3096 } 3097} 3098 3099func TestDirFSPathsValid(t *testing.T) { 3100 if runtime.GOOS == "windows" { 3101 t.Skipf("skipping on Windows") 3102 } 3103 t.Parallel() 3104 3105 d := t.TempDir() 3106 if err := WriteFile(filepath.Join(d, "control.txt"), []byte(string("Hello, world!")), 0644); err != nil { 3107 t.Fatal(err) 3108 } 3109 if err := WriteFile(filepath.Join(d, `e:xperi\ment.txt`), []byte(string("Hello, colon and backslash!")), 0644); err != nil { 3110 t.Fatal(err) 3111 } 3112 3113 fsys := DirFS(d) 3114 err := fs.WalkDir(fsys, ".", func(path string, e fs.DirEntry, err error) error { 3115 if fs.ValidPath(e.Name()) { 3116 t.Logf("%q ok", e.Name()) 3117 } else { 3118 t.Errorf("%q INVALID", e.Name()) 3119 } 3120 return nil 3121 }) 3122 if err != nil { 3123 t.Fatal(err) 3124 } 3125} 3126 3127func TestReadFileProc(t *testing.T) { 3128 t.Parallel() 3129 3130 // Linux files in /proc report 0 size, 3131 // but then if ReadFile reads just a single byte at offset 0, 3132 // the read at offset 1 returns EOF instead of more data. 3133 // ReadFile has a minimum read size of 512 to work around this, 3134 // but test explicitly that it's working. 3135 name := "/proc/sys/fs/pipe-max-size" 3136 if _, err := Stat(name); err != nil { 3137 t.Skip(err) 3138 } 3139 data, err := ReadFile(name) 3140 if err != nil { 3141 t.Fatal(err) 3142 } 3143 if len(data) == 0 || data[len(data)-1] != '\n' { 3144 t.Fatalf("read %s: not newline-terminated: %q", name, data) 3145 } 3146} 3147 3148func TestDirFSReadFileProc(t *testing.T) { 3149 t.Parallel() 3150 3151 fsys := DirFS("/") 3152 name := "proc/sys/fs/pipe-max-size" 3153 if _, err := fs.Stat(fsys, name); err != nil { 3154 t.Skip() 3155 } 3156 data, err := fs.ReadFile(fsys, name) 3157 if err != nil { 3158 t.Fatal(err) 3159 } 3160 if len(data) == 0 || data[len(data)-1] != '\n' { 3161 t.Fatalf("read %s: not newline-terminated: %q", name, data) 3162 } 3163} 3164 3165func TestWriteStringAlloc(t *testing.T) { 3166 if runtime.GOOS == "js" { 3167 t.Skip("js allocates a lot during File.WriteString") 3168 } 3169 d := t.TempDir() 3170 f, err := Create(filepath.Join(d, "whiteboard.txt")) 3171 if err != nil { 3172 t.Fatal(err) 3173 } 3174 defer f.Close() 3175 allocs := testing.AllocsPerRun(100, func() { 3176 f.WriteString("I will not allocate when passed a string longer than 32 bytes.\n") 3177 }) 3178 if allocs != 0 { 3179 t.Errorf("expected 0 allocs for File.WriteString, got %v", allocs) 3180 } 3181} 3182 3183// Test that it's OK to have parallel I/O and Close on a pipe. 3184func TestPipeIOCloseRace(t *testing.T) { 3185 // Skip on wasm, which doesn't have pipes. 3186 if runtime.GOOS == "js" || runtime.GOOS == "wasip1" { 3187 t.Skipf("skipping on %s: no pipes", runtime.GOOS) 3188 } 3189 t.Parallel() 3190 3191 r, w, err := Pipe() 3192 if err != nil { 3193 t.Fatal(err) 3194 } 3195 3196 var wg sync.WaitGroup 3197 wg.Add(3) 3198 3199 go func() { 3200 defer wg.Done() 3201 for { 3202 n, err := w.Write([]byte("hi")) 3203 if err != nil { 3204 // We look at error strings as the 3205 // expected errors are OS-specific. 3206 switch { 3207 case errors.Is(err, ErrClosed), 3208 strings.Contains(err.Error(), "broken pipe"), 3209 strings.Contains(err.Error(), "pipe is being closed"), 3210 strings.Contains(err.Error(), "hungup channel"): 3211 // Ignore an expected error. 3212 default: 3213 // Unexpected error. 3214 t.Error(err) 3215 } 3216 return 3217 } 3218 if n != 2 { 3219 t.Errorf("wrote %d bytes, expected 2", n) 3220 return 3221 } 3222 } 3223 }() 3224 3225 go func() { 3226 defer wg.Done() 3227 for { 3228 var buf [2]byte 3229 n, err := r.Read(buf[:]) 3230 if err != nil { 3231 if err != io.EOF && !errors.Is(err, ErrClosed) { 3232 t.Error(err) 3233 } 3234 return 3235 } 3236 if n != 2 { 3237 t.Errorf("read %d bytes, want 2", n) 3238 } 3239 } 3240 }() 3241 3242 go func() { 3243 defer wg.Done() 3244 3245 // Let the other goroutines start. This is just to get 3246 // a better test, the test will still pass if they 3247 // don't start. 3248 time.Sleep(time.Millisecond) 3249 3250 if err := r.Close(); err != nil { 3251 t.Error(err) 3252 } 3253 if err := w.Close(); err != nil { 3254 t.Error(err) 3255 } 3256 }() 3257 3258 wg.Wait() 3259} 3260 3261// Test that it's OK to call Close concurrently on a pipe. 3262func TestPipeCloseRace(t *testing.T) { 3263 // Skip on wasm, which doesn't have pipes. 3264 if runtime.GOOS == "js" || runtime.GOOS == "wasip1" { 3265 t.Skipf("skipping on %s: no pipes", runtime.GOOS) 3266 } 3267 t.Parallel() 3268 3269 r, w, err := Pipe() 3270 if err != nil { 3271 t.Fatal(err) 3272 } 3273 var wg sync.WaitGroup 3274 c := make(chan error, 4) 3275 f := func() { 3276 defer wg.Done() 3277 c <- r.Close() 3278 c <- w.Close() 3279 } 3280 wg.Add(2) 3281 go f() 3282 go f() 3283 nils, errs := 0, 0 3284 for i := 0; i < 4; i++ { 3285 err := <-c 3286 if err == nil { 3287 nils++ 3288 } else { 3289 errs++ 3290 } 3291 } 3292 if nils != 2 || errs != 2 { 3293 t.Errorf("got nils %d errs %d, want 2 2", nils, errs) 3294 } 3295} 3296 3297func TestRandomLen(t *testing.T) { 3298 for range 5 { 3299 dir, err := MkdirTemp(t.TempDir(), "*") 3300 if err != nil { 3301 t.Fatal(err) 3302 } 3303 base := filepath.Base(dir) 3304 if len(base) > 10 { 3305 t.Errorf("MkdirTemp returned len %d: %s", len(base), base) 3306 } 3307 } 3308 for range 5 { 3309 f, err := CreateTemp(t.TempDir(), "*") 3310 if err != nil { 3311 t.Fatal(err) 3312 } 3313 base := filepath.Base(f.Name()) 3314 f.Close() 3315 if len(base) > 10 { 3316 t.Errorf("CreateTemp returned len %d: %s", len(base), base) 3317 } 3318 } 3319} 3320 3321func TestCopyFS(t *testing.T) { 3322 t.Parallel() 3323 3324 // Test with disk filesystem. 3325 forceMFTUpdateOnWindows(t, "./testdata/dirfs") 3326 fsys := DirFS("./testdata/dirfs") 3327 tmpDir := t.TempDir() 3328 if err := CopyFS(tmpDir, fsys); err != nil { 3329 t.Fatal("CopyFS:", err) 3330 } 3331 forceMFTUpdateOnWindows(t, tmpDir) 3332 tmpFsys := DirFS(tmpDir) 3333 if err := fstest.TestFS(tmpFsys, "a", "b", "dir/x"); err != nil { 3334 t.Fatal("TestFS:", err) 3335 } 3336 if err := fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error { 3337 if d.IsDir() { 3338 return nil 3339 } 3340 3341 data, err := fs.ReadFile(fsys, path) 3342 if err != nil { 3343 return err 3344 } 3345 newData, err := fs.ReadFile(tmpFsys, path) 3346 if err != nil { 3347 return err 3348 } 3349 if !bytes.Equal(data, newData) { 3350 return errors.New("file " + path + " contents differ") 3351 } 3352 return nil 3353 }); err != nil { 3354 t.Fatal("comparing two directories:", err) 3355 } 3356 3357 // Test whether CopyFS disallows copying for disk filesystem when there is any 3358 // existing file in the destination directory. 3359 if err := CopyFS(tmpDir, fsys); !errors.Is(err, fs.ErrExist) { 3360 t.Errorf("CopyFS should have failed and returned error when there is"+ 3361 "any existing file in the destination directory (in disk filesystem), "+ 3362 "got: %v, expected any error that indicates <file exists>", err) 3363 } 3364 3365 // Test with memory filesystem. 3366 fsys = fstest.MapFS{ 3367 "william": {Data: []byte("Shakespeare\n")}, 3368 "carl": {Data: []byte("Gauss\n")}, 3369 "daVinci": {Data: []byte("Leonardo\n")}, 3370 "einstein": {Data: []byte("Albert\n")}, 3371 "dir/newton": {Data: []byte("Sir Isaac\n")}, 3372 } 3373 tmpDir = t.TempDir() 3374 if err := CopyFS(tmpDir, fsys); err != nil { 3375 t.Fatal("CopyFS:", err) 3376 } 3377 forceMFTUpdateOnWindows(t, tmpDir) 3378 tmpFsys = DirFS(tmpDir) 3379 if err := fstest.TestFS(tmpFsys, "william", "carl", "daVinci", "einstein", "dir/newton"); err != nil { 3380 t.Fatal("TestFS:", err) 3381 } 3382 if err := fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error { 3383 if d.IsDir() { 3384 return nil 3385 } 3386 3387 data, err := fs.ReadFile(fsys, path) 3388 if err != nil { 3389 return err 3390 } 3391 newData, err := fs.ReadFile(tmpFsys, path) 3392 if err != nil { 3393 return err 3394 } 3395 if !bytes.Equal(data, newData) { 3396 return errors.New("file " + path + " contents differ") 3397 } 3398 return nil 3399 }); err != nil { 3400 t.Fatal("comparing two directories:", err) 3401 } 3402 3403 // Test whether CopyFS disallows copying for memory filesystem when there is any 3404 // existing file in the destination directory. 3405 if err := CopyFS(tmpDir, fsys); !errors.Is(err, fs.ErrExist) { 3406 t.Errorf("CopyFS should have failed and returned error when there is"+ 3407 "any existing file in the destination directory (in memory filesystem), "+ 3408 "got: %v, expected any error that indicates <file exists>", err) 3409 } 3410} 3411 3412func TestCopyFSWithSymlinks(t *testing.T) { 3413 // Test it with absolute and relative symlinks that point inside and outside the tree. 3414 testenv.MustHaveSymlink(t) 3415 3416 // Create a directory and file outside. 3417 tmpDir := t.TempDir() 3418 outsideDir, err := MkdirTemp(tmpDir, "copyfs_out_") 3419 if err != nil { 3420 t.Fatalf("MkdirTemp: %v", err) 3421 } 3422 outsideFile := filepath.Join(outsideDir, "file.out.txt") 3423 3424 if err := WriteFile(outsideFile, []byte("Testing CopyFS outside"), 0644); err != nil { 3425 t.Fatalf("WriteFile: %v", err) 3426 } 3427 3428 // Create a directory and file inside. 3429 insideDir, err := MkdirTemp(tmpDir, "copyfs_in_") 3430 if err != nil { 3431 t.Fatalf("MkdirTemp: %v", err) 3432 } 3433 insideFile := filepath.Join(insideDir, "file.in.txt") 3434 if err := WriteFile(insideFile, []byte("Testing CopyFS inside"), 0644); err != nil { 3435 t.Fatalf("WriteFile: %v", err) 3436 } 3437 3438 // Create directories for symlinks. 3439 linkInDir := filepath.Join(insideDir, "in_symlinks") 3440 if err := Mkdir(linkInDir, 0755); err != nil { 3441 t.Fatalf("Mkdir: %v", err) 3442 } 3443 linkOutDir := filepath.Join(insideDir, "out_symlinks") 3444 if err := Mkdir(linkOutDir, 0755); err != nil { 3445 t.Fatalf("Mkdir: %v", err) 3446 } 3447 3448 // First, we create the absolute symlink pointing outside. 3449 outLinkFile := filepath.Join(linkOutDir, "file.abs.out.link") 3450 if err := Symlink(outsideFile, outLinkFile); err != nil { 3451 t.Fatalf("Symlink: %v", err) 3452 } 3453 3454 // Then, we create the relative symlink pointing outside. 3455 relOutsideFile, err := filepath.Rel(filepath.Join(linkOutDir, "."), outsideFile) 3456 if err != nil { 3457 t.Fatalf("filepath.Rel: %v", err) 3458 } 3459 relOutLinkFile := filepath.Join(linkOutDir, "file.rel.out.link") 3460 if err := Symlink(relOutsideFile, relOutLinkFile); err != nil { 3461 t.Fatalf("Symlink: %v", err) 3462 } 3463 3464 // Last, we create the relative symlink pointing inside. 3465 relInsideFile, err := filepath.Rel(filepath.Join(linkInDir, "."), insideFile) 3466 if err != nil { 3467 t.Fatalf("filepath.Rel: %v", err) 3468 } 3469 relInLinkFile := filepath.Join(linkInDir, "file.rel.in.link") 3470 if err := Symlink(relInsideFile, relInLinkFile); err != nil { 3471 t.Fatalf("Symlink: %v", err) 3472 } 3473 3474 // Copy the directory tree and verify. 3475 forceMFTUpdateOnWindows(t, insideDir) 3476 fsys := DirFS(insideDir) 3477 tmpDupDir, err := MkdirTemp(tmpDir, "copyfs_dup_") 3478 if err != nil { 3479 t.Fatalf("MkdirTemp: %v", err) 3480 } 3481 3482 // TODO(panjf2000): symlinks are currently not supported, and a specific error 3483 // will be returned. Verify that error and skip the subsequent test, 3484 // revisit this once #49580 is closed. 3485 if err := CopyFS(tmpDupDir, fsys); !errors.Is(err, ErrInvalid) { 3486 t.Fatalf("got %v, want ErrInvalid", err) 3487 } 3488 t.Skip("skip the subsequent test and wait for #49580") 3489 3490 forceMFTUpdateOnWindows(t, tmpDupDir) 3491 tmpFsys := DirFS(tmpDupDir) 3492 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 { 3493 t.Fatal("TestFS:", err) 3494 } 3495 if err := fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error { 3496 if d.IsDir() { 3497 return nil 3498 } 3499 3500 fi, err := d.Info() 3501 if err != nil { 3502 return err 3503 } 3504 if filepath.Ext(path) == ".link" { 3505 if fi.Mode()&ModeSymlink == 0 { 3506 return errors.New("original file " + path + " should be a symlink") 3507 } 3508 tmpfi, err := fs.Stat(tmpFsys, path) 3509 if err != nil { 3510 return err 3511 } 3512 if tmpfi.Mode()&ModeSymlink != 0 { 3513 return errors.New("copied file " + path + " should not be a symlink") 3514 } 3515 } 3516 3517 data, err := fs.ReadFile(fsys, path) 3518 if err != nil { 3519 return err 3520 } 3521 newData, err := fs.ReadFile(tmpFsys, path) 3522 if err != nil { 3523 return err 3524 } 3525 if !bytes.Equal(data, newData) { 3526 return errors.New("file " + path + " contents differ") 3527 } 3528 3529 var target string 3530 switch fileName := filepath.Base(path); fileName { 3531 case "file.abs.out.link", "file.rel.out.link": 3532 target = outsideFile 3533 case "file.rel.in.link": 3534 target = insideFile 3535 } 3536 if len(target) > 0 { 3537 targetData, err := ReadFile(target) 3538 if err != nil { 3539 return err 3540 } 3541 if !bytes.Equal(targetData, newData) { 3542 return errors.New("file " + path + " contents differ from target") 3543 } 3544 } 3545 3546 return nil 3547 }); err != nil { 3548 t.Fatal("comparing two directories:", err) 3549 } 3550} 3551