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