github.com/lovishpuri/go-40569/src@v0.0.0-20230519171745-f8623e7c56cf/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 const ErrPwd = "Error!" 1700 c := make(chan bool) 1701 cpwd := make(chan string, N) 1702 for i := 0; i < N; i++ { 1703 go func(i int) { 1704 // Lock half the goroutines in their own operating system 1705 // thread to exercise more scheduler possibilities. 1706 if i%2 == 1 { 1707 // On Plan 9, after calling LockOSThread, the goroutines 1708 // run on different processes which don't share the working 1709 // directory. This used to be an issue because Go expects 1710 // the working directory to be program-wide. 1711 // See issue 9428. 1712 runtime.LockOSThread() 1713 } 1714 hasErr, closed := <-c 1715 if !closed && hasErr { 1716 cpwd <- ErrPwd 1717 return 1718 } 1719 pwd, err := Getwd() 1720 if err != nil { 1721 t.Errorf("Getwd on goroutine %d: %v", i, err) 1722 cpwd <- ErrPwd 1723 return 1724 } 1725 cpwd <- pwd 1726 }(i) 1727 } 1728 oldwd, err := Getwd() 1729 if err != nil { 1730 c <- true 1731 t.Fatalf("Getwd: %v", err) 1732 } 1733 d, err := MkdirTemp("", "test") 1734 if err != nil { 1735 c <- true 1736 t.Fatalf("TempDir: %v", err) 1737 } 1738 defer func() { 1739 if err := Chdir(oldwd); err != nil { 1740 t.Fatalf("Chdir: %v", err) 1741 } 1742 RemoveAll(d) 1743 }() 1744 if err := Chdir(d); err != nil { 1745 c <- true 1746 t.Fatalf("Chdir: %v", err) 1747 } 1748 // OS X sets TMPDIR to a symbolic link. 1749 // So we resolve our working directory again before the test. 1750 d, err = Getwd() 1751 if err != nil { 1752 c <- true 1753 t.Fatalf("Getwd: %v", err) 1754 } 1755 close(c) 1756 for i := 0; i < N; i++ { 1757 pwd := <-cpwd 1758 if pwd == ErrPwd { 1759 t.FailNow() 1760 } 1761 if pwd != d { 1762 t.Errorf("Getwd returned %q; want %q", pwd, d) 1763 } 1764 } 1765 } 1766 1767 func TestSeek(t *testing.T) { 1768 t.Parallel() 1769 1770 f := newFile("TestSeek", t) 1771 defer Remove(f.Name()) 1772 defer f.Close() 1773 1774 const data = "hello, world\n" 1775 io.WriteString(f, data) 1776 1777 type test struct { 1778 in int64 1779 whence int 1780 out int64 1781 } 1782 var tests = []test{ 1783 {0, io.SeekCurrent, int64(len(data))}, 1784 {0, io.SeekStart, 0}, 1785 {5, io.SeekStart, 5}, 1786 {0, io.SeekEnd, int64(len(data))}, 1787 {0, io.SeekStart, 0}, 1788 {-1, io.SeekEnd, int64(len(data)) - 1}, 1789 {1 << 33, io.SeekStart, 1 << 33}, 1790 {1 << 33, io.SeekEnd, 1<<33 + int64(len(data))}, 1791 1792 // Issue 21681, Windows 4G-1, etc: 1793 {1<<32 - 1, io.SeekStart, 1<<32 - 1}, 1794 {0, io.SeekCurrent, 1<<32 - 1}, 1795 {2<<32 - 1, io.SeekStart, 2<<32 - 1}, 1796 {0, io.SeekCurrent, 2<<32 - 1}, 1797 } 1798 for i, tt := range tests { 1799 off, err := f.Seek(tt.in, tt.whence) 1800 if off != tt.out || err != nil { 1801 if e, ok := err.(*PathError); ok && e.Err == syscall.EINVAL && tt.out > 1<<32 && runtime.GOOS == "linux" { 1802 mounts, _ := ReadFile("/proc/mounts") 1803 if strings.Contains(string(mounts), "reiserfs") { 1804 // Reiserfs rejects the big seeks. 1805 t.Skipf("skipping test known to fail on reiserfs; https://golang.org/issue/91") 1806 } 1807 } 1808 t.Errorf("#%d: Seek(%v, %v) = %v, %v want %v, nil", i, tt.in, tt.whence, off, err, tt.out) 1809 } 1810 } 1811 } 1812 1813 func TestSeekError(t *testing.T) { 1814 switch runtime.GOOS { 1815 case "js", "plan9", "wasip1": 1816 t.Skipf("skipping test on %v", runtime.GOOS) 1817 } 1818 t.Parallel() 1819 1820 r, w, err := Pipe() 1821 if err != nil { 1822 t.Fatal(err) 1823 } 1824 _, err = r.Seek(0, 0) 1825 if err == nil { 1826 t.Fatal("Seek on pipe should fail") 1827 } 1828 if perr, ok := err.(*PathError); !ok || perr.Err != syscall.ESPIPE { 1829 t.Errorf("Seek returned error %v, want &PathError{Err: syscall.ESPIPE}", err) 1830 } 1831 _, err = w.Seek(0, 0) 1832 if err == nil { 1833 t.Fatal("Seek on pipe should fail") 1834 } 1835 if perr, ok := err.(*PathError); !ok || perr.Err != syscall.ESPIPE { 1836 t.Errorf("Seek returned error %v, want &PathError{Err: syscall.ESPIPE}", err) 1837 } 1838 } 1839 1840 type openErrorTest struct { 1841 path string 1842 mode int 1843 error error 1844 } 1845 1846 var openErrorTests = []openErrorTest{ 1847 { 1848 sfdir + "/no-such-file", 1849 O_RDONLY, 1850 syscall.ENOENT, 1851 }, 1852 { 1853 sfdir, 1854 O_WRONLY, 1855 syscall.EISDIR, 1856 }, 1857 { 1858 sfdir + "/" + sfname + "/no-such-file", 1859 O_WRONLY, 1860 syscall.ENOTDIR, 1861 }, 1862 } 1863 1864 func TestOpenError(t *testing.T) { 1865 t.Parallel() 1866 1867 for _, tt := range openErrorTests { 1868 f, err := OpenFile(tt.path, tt.mode, 0) 1869 if err == nil { 1870 t.Errorf("Open(%q, %d) succeeded", tt.path, tt.mode) 1871 f.Close() 1872 continue 1873 } 1874 perr, ok := err.(*PathError) 1875 if !ok { 1876 t.Errorf("Open(%q, %d) returns error of %T type; want *PathError", tt.path, tt.mode, err) 1877 } 1878 if perr.Err != tt.error { 1879 if runtime.GOOS == "plan9" { 1880 syscallErrStr := perr.Err.Error() 1881 expectedErrStr := strings.Replace(tt.error.Error(), "file ", "", 1) 1882 if !strings.HasSuffix(syscallErrStr, expectedErrStr) { 1883 // Some Plan 9 file servers incorrectly return 1884 // EACCES rather than EISDIR when a directory is 1885 // opened for write. 1886 if tt.error == syscall.EISDIR && strings.HasSuffix(syscallErrStr, syscall.EACCES.Error()) { 1887 continue 1888 } 1889 t.Errorf("Open(%q, %d) = _, %q; want suffix %q", tt.path, tt.mode, syscallErrStr, expectedErrStr) 1890 } 1891 continue 1892 } 1893 if runtime.GOOS == "dragonfly" { 1894 // DragonFly incorrectly returns EACCES rather 1895 // EISDIR when a directory is opened for write. 1896 if tt.error == syscall.EISDIR && perr.Err == syscall.EACCES { 1897 continue 1898 } 1899 } 1900 t.Errorf("Open(%q, %d) = _, %q; want %q", tt.path, tt.mode, perr.Err.Error(), tt.error.Error()) 1901 } 1902 } 1903 } 1904 1905 func TestOpenNoName(t *testing.T) { 1906 f, err := Open("") 1907 if err == nil { 1908 f.Close() 1909 t.Fatal(`Open("") succeeded`) 1910 } 1911 } 1912 1913 func runBinHostname(t *testing.T) string { 1914 // Run /bin/hostname and collect output. 1915 r, w, err := Pipe() 1916 if err != nil { 1917 t.Fatal(err) 1918 } 1919 defer r.Close() 1920 1921 path, err := exec.LookPath("hostname") 1922 if err != nil { 1923 if errors.Is(err, exec.ErrNotFound) { 1924 t.Skip("skipping test; test requires hostname but it does not exist") 1925 } 1926 t.Fatal(err) 1927 } 1928 1929 argv := []string{"hostname"} 1930 if runtime.GOOS == "aix" { 1931 argv = []string{"hostname", "-s"} 1932 } 1933 p, err := StartProcess(path, argv, &ProcAttr{Files: []*File{nil, w, Stderr}}) 1934 if err != nil { 1935 t.Fatal(err) 1936 } 1937 w.Close() 1938 1939 var b strings.Builder 1940 io.Copy(&b, r) 1941 _, err = p.Wait() 1942 if err != nil { 1943 t.Fatalf("run hostname Wait: %v", err) 1944 } 1945 err = p.Kill() 1946 if err == nil { 1947 t.Errorf("expected an error from Kill running 'hostname'") 1948 } 1949 output := b.String() 1950 if n := len(output); n > 0 && output[n-1] == '\n' { 1951 output = output[0 : n-1] 1952 } 1953 if output == "" { 1954 t.Fatalf("/bin/hostname produced no output") 1955 } 1956 1957 return output 1958 } 1959 1960 func testWindowsHostname(t *testing.T, hostname string) { 1961 cmd := testenv.Command(t, "hostname") 1962 out, err := cmd.Output() 1963 if err != nil { 1964 t.Fatalf("Failed to execute hostname command: %v %s", err, out) 1965 } 1966 want := strings.Trim(string(out), "\r\n") 1967 if hostname != want { 1968 t.Fatalf("Hostname() = %q != system hostname of %q", hostname, want) 1969 } 1970 } 1971 1972 func TestHostname(t *testing.T) { 1973 t.Parallel() 1974 1975 hostname, err := Hostname() 1976 if err != nil { 1977 t.Fatal(err) 1978 } 1979 if hostname == "" { 1980 t.Fatal("Hostname returned empty string and no error") 1981 } 1982 if strings.Contains(hostname, "\x00") { 1983 t.Fatalf("unexpected zero byte in hostname: %q", hostname) 1984 } 1985 1986 // There is no other way to fetch hostname on windows, but via winapi. 1987 // On Plan 9 it can be taken from #c/sysname as Hostname() does. 1988 switch runtime.GOOS { 1989 case "android", "plan9": 1990 // No /bin/hostname to verify against. 1991 return 1992 case "windows": 1993 testWindowsHostname(t, hostname) 1994 return 1995 } 1996 1997 testenv.MustHaveExec(t) 1998 1999 // Check internal Hostname() against the output of /bin/hostname. 2000 // Allow that the internal Hostname returns a Fully Qualified Domain Name 2001 // and the /bin/hostname only returns the first component 2002 want := runBinHostname(t) 2003 if hostname != want { 2004 host, _, ok := strings.Cut(hostname, ".") 2005 if !ok || host != want { 2006 t.Errorf("Hostname() = %q, want %q", hostname, want) 2007 } 2008 } 2009 } 2010 2011 func TestReadAt(t *testing.T) { 2012 t.Parallel() 2013 2014 f := newFile("TestReadAt", t) 2015 defer Remove(f.Name()) 2016 defer f.Close() 2017 2018 const data = "hello, world\n" 2019 io.WriteString(f, data) 2020 2021 b := make([]byte, 5) 2022 n, err := f.ReadAt(b, 7) 2023 if err != nil || n != len(b) { 2024 t.Fatalf("ReadAt 7: %d, %v", n, err) 2025 } 2026 if string(b) != "world" { 2027 t.Fatalf("ReadAt 7: have %q want %q", string(b), "world") 2028 } 2029 } 2030 2031 // Verify that ReadAt doesn't affect seek offset. 2032 // In the Plan 9 kernel, there used to be a bug in the implementation of 2033 // the pread syscall, where the channel offset was erroneously updated after 2034 // calling pread on a file. 2035 func TestReadAtOffset(t *testing.T) { 2036 t.Parallel() 2037 2038 f := newFile("TestReadAtOffset", t) 2039 defer Remove(f.Name()) 2040 defer f.Close() 2041 2042 const data = "hello, world\n" 2043 io.WriteString(f, data) 2044 2045 f.Seek(0, 0) 2046 b := make([]byte, 5) 2047 2048 n, err := f.ReadAt(b, 7) 2049 if err != nil || n != len(b) { 2050 t.Fatalf("ReadAt 7: %d, %v", n, err) 2051 } 2052 if string(b) != "world" { 2053 t.Fatalf("ReadAt 7: have %q want %q", string(b), "world") 2054 } 2055 2056 n, err = f.Read(b) 2057 if err != nil || n != len(b) { 2058 t.Fatalf("Read: %d, %v", n, err) 2059 } 2060 if string(b) != "hello" { 2061 t.Fatalf("Read: have %q want %q", string(b), "hello") 2062 } 2063 } 2064 2065 // Verify that ReadAt doesn't allow negative offset. 2066 func TestReadAtNegativeOffset(t *testing.T) { 2067 t.Parallel() 2068 2069 f := newFile("TestReadAtNegativeOffset", t) 2070 defer Remove(f.Name()) 2071 defer f.Close() 2072 2073 const data = "hello, world\n" 2074 io.WriteString(f, data) 2075 2076 f.Seek(0, 0) 2077 b := make([]byte, 5) 2078 2079 n, err := f.ReadAt(b, -10) 2080 2081 const wantsub = "negative offset" 2082 if !strings.Contains(fmt.Sprint(err), wantsub) || n != 0 { 2083 t.Errorf("ReadAt(-10) = %v, %v; want 0, ...%q...", n, err, wantsub) 2084 } 2085 } 2086 2087 func TestWriteAt(t *testing.T) { 2088 t.Parallel() 2089 2090 f := newFile("TestWriteAt", t) 2091 defer Remove(f.Name()) 2092 defer f.Close() 2093 2094 const data = "hello, world\n" 2095 io.WriteString(f, data) 2096 2097 n, err := f.WriteAt([]byte("WORLD"), 7) 2098 if err != nil || n != 5 { 2099 t.Fatalf("WriteAt 7: %d, %v", n, err) 2100 } 2101 2102 b, err := ReadFile(f.Name()) 2103 if err != nil { 2104 t.Fatalf("ReadFile %s: %v", f.Name(), err) 2105 } 2106 if string(b) != "hello, WORLD\n" { 2107 t.Fatalf("after write: have %q want %q", string(b), "hello, WORLD\n") 2108 } 2109 } 2110 2111 // Verify that WriteAt doesn't allow negative offset. 2112 func TestWriteAtNegativeOffset(t *testing.T) { 2113 t.Parallel() 2114 2115 f := newFile("TestWriteAtNegativeOffset", t) 2116 defer Remove(f.Name()) 2117 defer f.Close() 2118 2119 n, err := f.WriteAt([]byte("WORLD"), -10) 2120 2121 const wantsub = "negative offset" 2122 if !strings.Contains(fmt.Sprint(err), wantsub) || n != 0 { 2123 t.Errorf("WriteAt(-10) = %v, %v; want 0, ...%q...", n, err, wantsub) 2124 } 2125 } 2126 2127 // Verify that WriteAt doesn't work in append mode. 2128 func TestWriteAtInAppendMode(t *testing.T) { 2129 defer chtmpdir(t)() 2130 f, err := OpenFile("write_at_in_append_mode.txt", O_APPEND|O_CREATE, 0666) 2131 if err != nil { 2132 t.Fatalf("OpenFile: %v", err) 2133 } 2134 defer f.Close() 2135 2136 _, err = f.WriteAt([]byte(""), 1) 2137 if err != ErrWriteAtInAppendMode { 2138 t.Fatalf("f.WriteAt returned %v, expected %v", err, ErrWriteAtInAppendMode) 2139 } 2140 } 2141 2142 func writeFile(t *testing.T, fname string, flag int, text string) string { 2143 f, err := OpenFile(fname, flag, 0666) 2144 if err != nil { 2145 t.Fatalf("Open: %v", err) 2146 } 2147 n, err := io.WriteString(f, text) 2148 if err != nil { 2149 t.Fatalf("WriteString: %d, %v", n, err) 2150 } 2151 f.Close() 2152 data, err := ReadFile(fname) 2153 if err != nil { 2154 t.Fatalf("ReadFile: %v", err) 2155 } 2156 return string(data) 2157 } 2158 2159 func TestAppend(t *testing.T) { 2160 defer chtmpdir(t)() 2161 const f = "append.txt" 2162 s := writeFile(t, f, O_CREATE|O_TRUNC|O_RDWR, "new") 2163 if s != "new" { 2164 t.Fatalf("writeFile: have %q want %q", s, "new") 2165 } 2166 s = writeFile(t, f, O_APPEND|O_RDWR, "|append") 2167 if s != "new|append" { 2168 t.Fatalf("writeFile: have %q want %q", s, "new|append") 2169 } 2170 s = writeFile(t, f, O_CREATE|O_APPEND|O_RDWR, "|append") 2171 if s != "new|append|append" { 2172 t.Fatalf("writeFile: have %q want %q", s, "new|append|append") 2173 } 2174 err := Remove(f) 2175 if err != nil { 2176 t.Fatalf("Remove: %v", err) 2177 } 2178 s = writeFile(t, f, O_CREATE|O_APPEND|O_RDWR, "new&append") 2179 if s != "new&append" { 2180 t.Fatalf("writeFile: after append have %q want %q", s, "new&append") 2181 } 2182 s = writeFile(t, f, O_CREATE|O_RDWR, "old") 2183 if s != "old&append" { 2184 t.Fatalf("writeFile: after create have %q want %q", s, "old&append") 2185 } 2186 s = writeFile(t, f, O_CREATE|O_TRUNC|O_RDWR, "new") 2187 if s != "new" { 2188 t.Fatalf("writeFile: after truncate have %q want %q", s, "new") 2189 } 2190 } 2191 2192 func TestStatDirWithTrailingSlash(t *testing.T) { 2193 t.Parallel() 2194 2195 // Create new temporary directory and arrange to clean it up. 2196 path := t.TempDir() 2197 2198 // Stat of path should succeed. 2199 if _, err := Stat(path); err != nil { 2200 t.Fatalf("stat %s failed: %s", path, err) 2201 } 2202 2203 // Stat of path+"/" should succeed too. 2204 path += "/" 2205 if _, err := Stat(path); err != nil { 2206 t.Fatalf("stat %s failed: %s", path, err) 2207 } 2208 } 2209 2210 func TestNilProcessStateString(t *testing.T) { 2211 var ps *ProcessState 2212 s := ps.String() 2213 if s != "<nil>" { 2214 t.Errorf("(*ProcessState)(nil).String() = %q, want %q", s, "<nil>") 2215 } 2216 } 2217 2218 func TestSameFile(t *testing.T) { 2219 defer chtmpdir(t)() 2220 fa, err := Create("a") 2221 if err != nil { 2222 t.Fatalf("Create(a): %v", err) 2223 } 2224 fa.Close() 2225 fb, err := Create("b") 2226 if err != nil { 2227 t.Fatalf("Create(b): %v", err) 2228 } 2229 fb.Close() 2230 2231 ia1, err := Stat("a") 2232 if err != nil { 2233 t.Fatalf("Stat(a): %v", err) 2234 } 2235 ia2, err := Stat("a") 2236 if err != nil { 2237 t.Fatalf("Stat(a): %v", err) 2238 } 2239 if !SameFile(ia1, ia2) { 2240 t.Errorf("files should be same") 2241 } 2242 2243 ib, err := Stat("b") 2244 if err != nil { 2245 t.Fatalf("Stat(b): %v", err) 2246 } 2247 if SameFile(ia1, ib) { 2248 t.Errorf("files should be different") 2249 } 2250 } 2251 2252 func testDevNullFileInfo(t *testing.T, statname, devNullName string, fi FileInfo) { 2253 pre := fmt.Sprintf("%s(%q): ", statname, devNullName) 2254 if fi.Size() != 0 { 2255 t.Errorf(pre+"wrong file size have %d want 0", fi.Size()) 2256 } 2257 if fi.Mode()&ModeDevice == 0 { 2258 t.Errorf(pre+"wrong file mode %q: ModeDevice is not set", fi.Mode()) 2259 } 2260 if fi.Mode()&ModeCharDevice == 0 { 2261 t.Errorf(pre+"wrong file mode %q: ModeCharDevice is not set", fi.Mode()) 2262 } 2263 if fi.Mode().IsRegular() { 2264 t.Errorf(pre+"wrong file mode %q: IsRegular returns true", fi.Mode()) 2265 } 2266 } 2267 2268 func testDevNullFile(t *testing.T, devNullName string) { 2269 f, err := Open(devNullName) 2270 if err != nil { 2271 t.Fatalf("Open(%s): %v", devNullName, err) 2272 } 2273 defer f.Close() 2274 2275 fi, err := f.Stat() 2276 if err != nil { 2277 t.Fatalf("Stat(%s): %v", devNullName, err) 2278 } 2279 testDevNullFileInfo(t, "f.Stat", devNullName, fi) 2280 2281 fi, err = Stat(devNullName) 2282 if err != nil { 2283 t.Fatalf("Stat(%s): %v", devNullName, err) 2284 } 2285 testDevNullFileInfo(t, "Stat", devNullName, fi) 2286 } 2287 2288 func TestDevNullFile(t *testing.T) { 2289 t.Parallel() 2290 2291 testDevNullFile(t, DevNull) 2292 if runtime.GOOS == "windows" { 2293 testDevNullFile(t, "./nul") 2294 testDevNullFile(t, "//./nul") 2295 } 2296 } 2297 2298 var testLargeWrite = flag.Bool("large_write", false, "run TestLargeWriteToConsole test that floods console with output") 2299 2300 func TestLargeWriteToConsole(t *testing.T) { 2301 if !*testLargeWrite { 2302 t.Skip("skipping console-flooding test; enable with -large_write") 2303 } 2304 b := make([]byte, 32000) 2305 for i := range b { 2306 b[i] = '.' 2307 } 2308 b[len(b)-1] = '\n' 2309 n, err := Stdout.Write(b) 2310 if err != nil { 2311 t.Fatalf("Write to os.Stdout failed: %v", err) 2312 } 2313 if n != len(b) { 2314 t.Errorf("Write to os.Stdout should return %d; got %d", len(b), n) 2315 } 2316 n, err = Stderr.Write(b) 2317 if err != nil { 2318 t.Fatalf("Write to os.Stderr failed: %v", err) 2319 } 2320 if n != len(b) { 2321 t.Errorf("Write to os.Stderr should return %d; got %d", len(b), n) 2322 } 2323 } 2324 2325 func TestStatDirModeExec(t *testing.T) { 2326 if runtime.GOOS == "wasip1" { 2327 t.Skip("Chmod is not supported on " + runtime.GOOS) 2328 } 2329 t.Parallel() 2330 2331 const mode = 0111 2332 2333 path := t.TempDir() 2334 if err := Chmod(path, 0777); err != nil { 2335 t.Fatalf("Chmod %q 0777: %v", path, err) 2336 } 2337 2338 dir, err := Stat(path) 2339 if err != nil { 2340 t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err) 2341 } 2342 if dir.Mode()&mode != mode { 2343 t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode()&mode, mode) 2344 } 2345 } 2346 2347 func TestStatStdin(t *testing.T) { 2348 switch runtime.GOOS { 2349 case "android", "plan9": 2350 t.Skipf("%s doesn't have /bin/sh", runtime.GOOS) 2351 } 2352 2353 if Getenv("GO_WANT_HELPER_PROCESS") == "1" { 2354 st, err := Stdin.Stat() 2355 if err != nil { 2356 t.Fatalf("Stat failed: %v", err) 2357 } 2358 fmt.Println(st.Mode() & ModeNamedPipe) 2359 Exit(0) 2360 } 2361 2362 testenv.MustHaveExec(t) 2363 t.Parallel() 2364 2365 fi, err := Stdin.Stat() 2366 if err != nil { 2367 t.Fatal(err) 2368 } 2369 switch mode := fi.Mode(); { 2370 case mode&ModeCharDevice != 0 && mode&ModeDevice != 0: 2371 case mode&ModeNamedPipe != 0: 2372 default: 2373 t.Fatalf("unexpected Stdin mode (%v), want ModeCharDevice or ModeNamedPipe", mode) 2374 } 2375 2376 var cmd *exec.Cmd 2377 if runtime.GOOS == "windows" { 2378 cmd = testenv.Command(t, "cmd", "/c", "echo output | "+Args[0]+" -test.run=TestStatStdin") 2379 } else { 2380 cmd = testenv.Command(t, "/bin/sh", "-c", "echo output | "+Args[0]+" -test.run=TestStatStdin") 2381 } 2382 cmd.Env = append(Environ(), "GO_WANT_HELPER_PROCESS=1") 2383 2384 output, err := cmd.CombinedOutput() 2385 if err != nil { 2386 t.Fatalf("Failed to spawn child process: %v %q", err, string(output)) 2387 } 2388 2389 // result will be like "prw-rw-rw" 2390 if len(output) < 1 || output[0] != 'p' { 2391 t.Fatalf("Child process reports stdin is not pipe '%v'", string(output)) 2392 } 2393 } 2394 2395 func TestStatRelativeSymlink(t *testing.T) { 2396 testenv.MustHaveSymlink(t) 2397 t.Parallel() 2398 2399 tmpdir := t.TempDir() 2400 target := filepath.Join(tmpdir, "target") 2401 f, err := Create(target) 2402 if err != nil { 2403 t.Fatal(err) 2404 } 2405 defer f.Close() 2406 2407 st, err := f.Stat() 2408 if err != nil { 2409 t.Fatal(err) 2410 } 2411 2412 link := filepath.Join(tmpdir, "link") 2413 err = Symlink(filepath.Base(target), link) 2414 if err != nil { 2415 t.Fatal(err) 2416 } 2417 2418 st1, err := Stat(link) 2419 if err != nil { 2420 t.Fatal(err) 2421 } 2422 2423 if !SameFile(st, st1) { 2424 t.Error("Stat doesn't follow relative symlink") 2425 } 2426 2427 if runtime.GOOS == "windows" { 2428 Remove(link) 2429 err = Symlink(target[len(filepath.VolumeName(target)):], link) 2430 if err != nil { 2431 t.Fatal(err) 2432 } 2433 2434 st1, err := Stat(link) 2435 if err != nil { 2436 t.Fatal(err) 2437 } 2438 2439 if !SameFile(st, st1) { 2440 t.Error("Stat doesn't follow relative symlink") 2441 } 2442 } 2443 } 2444 2445 func TestReadAtEOF(t *testing.T) { 2446 t.Parallel() 2447 2448 f := newFile("TestReadAtEOF", t) 2449 defer Remove(f.Name()) 2450 defer f.Close() 2451 2452 _, err := f.ReadAt(make([]byte, 10), 0) 2453 switch err { 2454 case io.EOF: 2455 // all good 2456 case nil: 2457 t.Fatalf("ReadAt succeeded") 2458 default: 2459 t.Fatalf("ReadAt failed: %s", err) 2460 } 2461 } 2462 2463 func TestLongPath(t *testing.T) { 2464 t.Parallel() 2465 2466 tmpdir := newDir("TestLongPath", t) 2467 defer func(d string) { 2468 if err := RemoveAll(d); err != nil { 2469 t.Fatalf("RemoveAll failed: %v", err) 2470 } 2471 }(tmpdir) 2472 2473 // Test the boundary of 247 and fewer bytes (normal) and 248 and more bytes (adjusted). 2474 sizes := []int{247, 248, 249, 400} 2475 for len(tmpdir) < 400 { 2476 tmpdir += "/dir3456789" 2477 } 2478 for _, sz := range sizes { 2479 t.Run(fmt.Sprintf("length=%d", sz), func(t *testing.T) { 2480 sizedTempDir := tmpdir[:sz-1] + "x" // Ensure it does not end with a slash. 2481 2482 // The various sized runs are for this call to trigger the boundary 2483 // condition. 2484 if err := MkdirAll(sizedTempDir, 0755); err != nil { 2485 t.Fatalf("MkdirAll failed: %v", err) 2486 } 2487 data := []byte("hello world\n") 2488 if err := WriteFile(sizedTempDir+"/foo.txt", data, 0644); err != nil { 2489 t.Fatalf("os.WriteFile() failed: %v", err) 2490 } 2491 if err := Rename(sizedTempDir+"/foo.txt", sizedTempDir+"/bar.txt"); err != nil { 2492 t.Fatalf("Rename failed: %v", err) 2493 } 2494 mtime := time.Now().Truncate(time.Minute) 2495 if err := Chtimes(sizedTempDir+"/bar.txt", mtime, mtime); err != nil { 2496 t.Fatalf("Chtimes failed: %v", err) 2497 } 2498 names := []string{"bar.txt"} 2499 if testenv.HasSymlink() { 2500 if err := Symlink(sizedTempDir+"/bar.txt", sizedTempDir+"/symlink.txt"); err != nil { 2501 t.Fatalf("Symlink failed: %v", err) 2502 } 2503 names = append(names, "symlink.txt") 2504 } 2505 if testenv.HasLink() { 2506 if err := Link(sizedTempDir+"/bar.txt", sizedTempDir+"/link.txt"); err != nil { 2507 t.Fatalf("Link failed: %v", err) 2508 } 2509 names = append(names, "link.txt") 2510 } 2511 for _, wantSize := range []int64{int64(len(data)), 0} { 2512 for _, name := range names { 2513 path := sizedTempDir + "/" + name 2514 dir, err := Stat(path) 2515 if err != nil { 2516 t.Fatalf("Stat(%q) failed: %v", path, err) 2517 } 2518 filesize := size(path, t) 2519 if dir.Size() != filesize || filesize != wantSize { 2520 t.Errorf("Size(%q) is %d, len(ReadFile()) is %d, want %d", path, dir.Size(), filesize, wantSize) 2521 } 2522 if runtime.GOOS != "wasip1" { // Chmod is not supported on wasip1 2523 err = Chmod(path, dir.Mode()) 2524 if err != nil { 2525 t.Fatalf("Chmod(%q) failed: %v", path, err) 2526 } 2527 } 2528 } 2529 if err := Truncate(sizedTempDir+"/bar.txt", 0); err != nil { 2530 t.Fatalf("Truncate failed: %v", err) 2531 } 2532 } 2533 }) 2534 } 2535 } 2536 2537 func testKillProcess(t *testing.T, processKiller func(p *Process)) { 2538 testenv.MustHaveExec(t) 2539 t.Parallel() 2540 2541 // Re-exec the test binary to start a process that hangs until stdin is closed. 2542 cmd := testenv.Command(t, Args[0]) 2543 cmd.Env = append(cmd.Environ(), "GO_OS_TEST_DRAIN_STDIN=1") 2544 stdout, err := cmd.StdoutPipe() 2545 if err != nil { 2546 t.Fatal(err) 2547 } 2548 stdin, err := cmd.StdinPipe() 2549 if err != nil { 2550 t.Fatal(err) 2551 } 2552 err = cmd.Start() 2553 if err != nil { 2554 t.Fatalf("Failed to start test process: %v", err) 2555 } 2556 2557 defer func() { 2558 if err := cmd.Wait(); err == nil { 2559 t.Errorf("Test process succeeded, but expected to fail") 2560 } 2561 stdin.Close() // Keep stdin alive until the process has finished dying. 2562 }() 2563 2564 // Wait for the process to be started. 2565 // (It will close its stdout when it reaches TestMain.) 2566 io.Copy(io.Discard, stdout) 2567 2568 processKiller(cmd.Process) 2569 } 2570 2571 func TestKillStartProcess(t *testing.T) { 2572 testKillProcess(t, func(p *Process) { 2573 err := p.Kill() 2574 if err != nil { 2575 t.Fatalf("Failed to kill test process: %v", err) 2576 } 2577 }) 2578 } 2579 2580 func TestGetppid(t *testing.T) { 2581 if runtime.GOOS == "plan9" { 2582 // TODO: golang.org/issue/8206 2583 t.Skipf("skipping test on plan9; see issue 8206") 2584 } 2585 2586 if Getenv("GO_WANT_HELPER_PROCESS") == "1" { 2587 fmt.Print(Getppid()) 2588 Exit(0) 2589 } 2590 2591 testenv.MustHaveExec(t) 2592 t.Parallel() 2593 2594 cmd := testenv.Command(t, Args[0], "-test.run=TestGetppid") 2595 cmd.Env = append(Environ(), "GO_WANT_HELPER_PROCESS=1") 2596 2597 // verify that Getppid() from the forked process reports our process id 2598 output, err := cmd.CombinedOutput() 2599 if err != nil { 2600 t.Fatalf("Failed to spawn child process: %v %q", err, string(output)) 2601 } 2602 2603 childPpid := string(output) 2604 ourPid := fmt.Sprintf("%d", Getpid()) 2605 if childPpid != ourPid { 2606 t.Fatalf("Child process reports parent process id '%v', expected '%v'", childPpid, ourPid) 2607 } 2608 } 2609 2610 func TestKillFindProcess(t *testing.T) { 2611 testKillProcess(t, func(p *Process) { 2612 p2, err := FindProcess(p.Pid) 2613 if err != nil { 2614 t.Fatalf("Failed to find test process: %v", err) 2615 } 2616 err = p2.Kill() 2617 if err != nil { 2618 t.Fatalf("Failed to kill test process: %v", err) 2619 } 2620 }) 2621 } 2622 2623 var nilFileMethodTests = []struct { 2624 name string 2625 f func(*File) error 2626 }{ 2627 {"Chdir", func(f *File) error { return f.Chdir() }}, 2628 {"Close", func(f *File) error { return f.Close() }}, 2629 {"Chmod", func(f *File) error { return f.Chmod(0) }}, 2630 {"Chown", func(f *File) error { return f.Chown(0, 0) }}, 2631 {"Read", func(f *File) error { _, err := f.Read(make([]byte, 0)); return err }}, 2632 {"ReadAt", func(f *File) error { _, err := f.ReadAt(make([]byte, 0), 0); return err }}, 2633 {"Readdir", func(f *File) error { _, err := f.Readdir(1); return err }}, 2634 {"Readdirnames", func(f *File) error { _, err := f.Readdirnames(1); return err }}, 2635 {"Seek", func(f *File) error { _, err := f.Seek(0, io.SeekStart); return err }}, 2636 {"Stat", func(f *File) error { _, err := f.Stat(); return err }}, 2637 {"Sync", func(f *File) error { return f.Sync() }}, 2638 {"Truncate", func(f *File) error { return f.Truncate(0) }}, 2639 {"Write", func(f *File) error { _, err := f.Write(make([]byte, 0)); return err }}, 2640 {"WriteAt", func(f *File) error { _, err := f.WriteAt(make([]byte, 0), 0); return err }}, 2641 {"WriteString", func(f *File) error { _, err := f.WriteString(""); return err }}, 2642 } 2643 2644 // Test that all File methods give ErrInvalid if the receiver is nil. 2645 func TestNilFileMethods(t *testing.T) { 2646 t.Parallel() 2647 2648 for _, tt := range nilFileMethodTests { 2649 var file *File 2650 got := tt.f(file) 2651 if got != ErrInvalid { 2652 t.Errorf("%v should fail when f is nil; got %v", tt.name, got) 2653 } 2654 } 2655 } 2656 2657 func mkdirTree(t *testing.T, root string, level, max int) { 2658 if level >= max { 2659 return 2660 } 2661 level++ 2662 for i := 'a'; i < 'c'; i++ { 2663 dir := filepath.Join(root, string(i)) 2664 if err := Mkdir(dir, 0700); err != nil { 2665 t.Fatal(err) 2666 } 2667 mkdirTree(t, dir, level, max) 2668 } 2669 } 2670 2671 // Test that simultaneous RemoveAll do not report an error. 2672 // As long as it gets removed, we should be happy. 2673 func TestRemoveAllRace(t *testing.T) { 2674 if runtime.GOOS == "windows" { 2675 // Windows has very strict rules about things like 2676 // removing directories while someone else has 2677 // them open. The racing doesn't work out nicely 2678 // like it does on Unix. 2679 t.Skip("skipping on windows") 2680 } 2681 if runtime.GOOS == "dragonfly" { 2682 testenv.SkipFlaky(t, 52301) 2683 } 2684 2685 n := runtime.GOMAXPROCS(16) 2686 defer runtime.GOMAXPROCS(n) 2687 root, err := MkdirTemp("", "issue") 2688 if err != nil { 2689 t.Fatal(err) 2690 } 2691 mkdirTree(t, root, 1, 6) 2692 hold := make(chan struct{}) 2693 var wg sync.WaitGroup 2694 for i := 0; i < 4; i++ { 2695 wg.Add(1) 2696 go func() { 2697 defer wg.Done() 2698 <-hold 2699 err := RemoveAll(root) 2700 if err != nil { 2701 t.Errorf("unexpected error: %T, %q", err, err) 2702 } 2703 }() 2704 } 2705 close(hold) // let workers race to remove root 2706 wg.Wait() 2707 } 2708 2709 // Test that reading from a pipe doesn't use up a thread. 2710 func TestPipeThreads(t *testing.T) { 2711 switch runtime.GOOS { 2712 case "illumos", "solaris": 2713 t.Skip("skipping on Solaris and illumos; issue 19111") 2714 case "windows": 2715 t.Skip("skipping on Windows; issue 19098") 2716 case "plan9": 2717 t.Skip("skipping on Plan 9; does not support runtime poller") 2718 case "js": 2719 t.Skip("skipping on js; no support for os.Pipe") 2720 case "wasip1": 2721 t.Skip("skipping on wasip1; no support for os.Pipe") 2722 } 2723 2724 threads := 100 2725 2726 // OpenBSD has a low default for max number of files. 2727 if runtime.GOOS == "openbsd" { 2728 threads = 50 2729 } 2730 2731 r := make([]*File, threads) 2732 w := make([]*File, threads) 2733 for i := 0; i < threads; i++ { 2734 rp, wp, err := Pipe() 2735 if err != nil { 2736 for j := 0; j < i; j++ { 2737 r[j].Close() 2738 w[j].Close() 2739 } 2740 t.Fatal(err) 2741 } 2742 r[i] = rp 2743 w[i] = wp 2744 } 2745 2746 defer debug.SetMaxThreads(debug.SetMaxThreads(threads / 2)) 2747 2748 creading := make(chan bool, threads) 2749 cdone := make(chan bool, threads) 2750 for i := 0; i < threads; i++ { 2751 go func(i int) { 2752 var b [1]byte 2753 creading <- true 2754 if _, err := r[i].Read(b[:]); err != nil { 2755 t.Error(err) 2756 } 2757 if err := r[i].Close(); err != nil { 2758 t.Error(err) 2759 } 2760 cdone <- true 2761 }(i) 2762 } 2763 2764 for i := 0; i < threads; i++ { 2765 <-creading 2766 } 2767 2768 // If we are still alive, it means that the 100 goroutines did 2769 // not require 100 threads. 2770 2771 for i := 0; i < threads; i++ { 2772 if _, err := w[i].Write([]byte{0}); err != nil { 2773 t.Error(err) 2774 } 2775 if err := w[i].Close(); err != nil { 2776 t.Error(err) 2777 } 2778 <-cdone 2779 } 2780 } 2781 2782 func testDoubleCloseError(path string) func(*testing.T) { 2783 return func(t *testing.T) { 2784 t.Parallel() 2785 2786 file, err := Open(path) 2787 if err != nil { 2788 t.Fatal(err) 2789 } 2790 if err := file.Close(); err != nil { 2791 t.Fatalf("unexpected error from Close: %v", err) 2792 } 2793 if err := file.Close(); err == nil { 2794 t.Error("second Close did not fail") 2795 } else if pe, ok := err.(*PathError); !ok { 2796 t.Errorf("second Close: got %T, want %T", err, pe) 2797 } else if pe.Err != ErrClosed { 2798 t.Errorf("second Close: got %q, want %q", pe.Err, ErrClosed) 2799 } else { 2800 t.Logf("second close returned expected error %q", err) 2801 } 2802 } 2803 } 2804 2805 func TestDoubleCloseError(t *testing.T) { 2806 t.Parallel() 2807 t.Run("file", testDoubleCloseError(filepath.Join(sfdir, sfname))) 2808 t.Run("dir", testDoubleCloseError(sfdir)) 2809 } 2810 2811 func TestUserHomeDir(t *testing.T) { 2812 t.Parallel() 2813 2814 dir, err := UserHomeDir() 2815 if dir == "" && err == nil { 2816 t.Fatal("UserHomeDir returned an empty string but no error") 2817 } 2818 if err != nil { 2819 // UserHomeDir may return a non-nil error if the environment variable 2820 // for the home directory is empty or unset in the environment. 2821 t.Skipf("skipping: %v", err) 2822 } 2823 2824 fi, err := Stat(dir) 2825 if err != nil { 2826 if IsNotExist(err) { 2827 // The user's home directory has a well-defined location, but does not 2828 // exist. (Maybe nothing has written to it yet? That could happen, for 2829 // example, on minimal VM images used for CI testing.) 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 2840 func TestDirSeek(t *testing.T) { 2841 t.Parallel() 2842 2843 wd, err := Getwd() 2844 if err != nil { 2845 t.Fatal(err) 2846 } 2847 f, err := Open(wd) 2848 if err != nil { 2849 t.Fatal(err) 2850 } 2851 dirnames1, err := f.Readdirnames(0) 2852 if err != nil { 2853 t.Fatal(err) 2854 } 2855 2856 ret, err := f.Seek(0, 0) 2857 if err != nil { 2858 t.Fatal(err) 2859 } 2860 if ret != 0 { 2861 t.Fatalf("seek result not zero: %d", ret) 2862 } 2863 2864 dirnames2, err := f.Readdirnames(0) 2865 if err != nil { 2866 t.Fatal(err) 2867 } 2868 2869 if len(dirnames1) != len(dirnames2) { 2870 t.Fatalf("listings have different lengths: %d and %d\n", len(dirnames1), len(dirnames2)) 2871 } 2872 for i, n1 := range dirnames1 { 2873 n2 := dirnames2[i] 2874 if n1 != n2 { 2875 t.Fatalf("different name i=%d n1=%s n2=%s\n", i, n1, n2) 2876 } 2877 } 2878 } 2879 2880 func TestReaddirSmallSeek(t *testing.T) { 2881 // See issue 37161. Read only one entry from a directory, 2882 // seek to the beginning, and read again. We should not see 2883 // duplicate entries. 2884 t.Parallel() 2885 2886 wd, err := Getwd() 2887 if err != nil { 2888 t.Fatal(err) 2889 } 2890 df, err := Open(filepath.Join(wd, "testdata", "issue37161")) 2891 if err != nil { 2892 t.Fatal(err) 2893 } 2894 names1, err := df.Readdirnames(1) 2895 if err != nil { 2896 t.Fatal(err) 2897 } 2898 if _, err = df.Seek(0, 0); err != nil { 2899 t.Fatal(err) 2900 } 2901 names2, err := df.Readdirnames(0) 2902 if err != nil { 2903 t.Fatal(err) 2904 } 2905 if len(names2) != 3 { 2906 t.Fatalf("first names: %v, second names: %v", names1, names2) 2907 } 2908 } 2909 2910 // isDeadlineExceeded reports whether err is or wraps ErrDeadlineExceeded. 2911 // We also check that the error has a Timeout method that returns true. 2912 func isDeadlineExceeded(err error) bool { 2913 if !IsTimeout(err) { 2914 return false 2915 } 2916 if !errors.Is(err, ErrDeadlineExceeded) { 2917 return false 2918 } 2919 return true 2920 } 2921 2922 // Test that opening a file does not change its permissions. Issue 38225. 2923 func TestOpenFileKeepsPermissions(t *testing.T) { 2924 t.Parallel() 2925 2926 dir := t.TempDir() 2927 name := filepath.Join(dir, "x") 2928 f, err := Create(name) 2929 if err != nil { 2930 t.Fatal(err) 2931 } 2932 if err := f.Close(); err != nil { 2933 t.Error(err) 2934 } 2935 f, err = OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, 0) 2936 if err != nil { 2937 t.Fatal(err) 2938 } 2939 if fi, err := f.Stat(); err != nil { 2940 t.Error(err) 2941 } else if fi.Mode()&0222 == 0 { 2942 t.Errorf("f.Stat.Mode after OpenFile is %v, should be writable", fi.Mode()) 2943 } 2944 if err := f.Close(); err != nil { 2945 t.Error(err) 2946 } 2947 if fi, err := Stat(name); err != nil { 2948 t.Error(err) 2949 } else if fi.Mode()&0222 == 0 { 2950 t.Errorf("Stat after OpenFile is %v, should be writable", fi.Mode()) 2951 } 2952 } 2953 2954 func TestDirFS(t *testing.T) { 2955 t.Parallel() 2956 2957 // On Windows, we force the MFT to update by reading the actual metadata from GetFileInformationByHandle and then 2958 // explicitly setting that. Otherwise it might get out of sync with FindFirstFile. See golang.org/issues/42637. 2959 if runtime.GOOS == "windows" { 2960 if err := filepath.WalkDir("./testdata/dirfs", func(path string, d fs.DirEntry, err error) error { 2961 if err != nil { 2962 t.Fatal(err) 2963 } 2964 info, err := d.Info() 2965 if err != nil { 2966 t.Fatal(err) 2967 } 2968 stat, err := Stat(path) // This uses GetFileInformationByHandle internally. 2969 if err != nil { 2970 t.Fatal(err) 2971 } 2972 if stat.ModTime() == info.ModTime() { 2973 return nil 2974 } 2975 if err := Chtimes(path, stat.ModTime(), stat.ModTime()); err != nil { 2976 t.Log(err) // We only log, not die, in case the test directory is not writable. 2977 } 2978 return nil 2979 }); err != nil { 2980 t.Fatal(err) 2981 } 2982 } 2983 fs := DirFS("./testdata/dirfs") 2984 if err := fstest.TestFS(fs, "a", "b", "dir/x"); err != nil { 2985 t.Fatal(err) 2986 } 2987 2988 // Test that the error message does not contain a backslash, 2989 // and does not contain the DirFS argument. 2990 const nonesuch = "dir/nonesuch" 2991 _, err := fs.Open(nonesuch) 2992 if err == nil { 2993 t.Error("fs.Open of nonexistent file succeeded") 2994 } else { 2995 if !strings.Contains(err.Error(), nonesuch) { 2996 t.Errorf("error %q does not contain %q", err, nonesuch) 2997 } 2998 if strings.Contains(err.(*PathError).Path, "testdata") { 2999 t.Errorf("error %q contains %q", err, "testdata") 3000 } 3001 } 3002 3003 // Test that Open does not accept backslash as separator. 3004 d := DirFS(".") 3005 _, err = d.Open(`testdata\dirfs`) 3006 if err == nil { 3007 t.Fatalf(`Open testdata\dirfs succeeded`) 3008 } 3009 3010 // Test that Open does not open Windows device files. 3011 _, err = d.Open(`NUL`) 3012 if err == nil { 3013 t.Errorf(`Open NUL succeeded`) 3014 } 3015 } 3016 3017 func TestDirFSRootDir(t *testing.T) { 3018 t.Parallel() 3019 3020 cwd, err := Getwd() 3021 if err != nil { 3022 t.Fatal(err) 3023 } 3024 cwd = cwd[len(filepath.VolumeName(cwd)):] // trim volume prefix (C:) on Windows 3025 cwd = filepath.ToSlash(cwd) // convert \ to / 3026 cwd = strings.TrimPrefix(cwd, "/") // trim leading / 3027 3028 // Test that Open can open a path starting at /. 3029 d := DirFS("/") 3030 f, err := d.Open(cwd + "/testdata/dirfs/a") 3031 if err != nil { 3032 t.Fatal(err) 3033 } 3034 f.Close() 3035 } 3036 3037 func TestDirFSEmptyDir(t *testing.T) { 3038 t.Parallel() 3039 3040 d := DirFS("") 3041 cwd, _ := Getwd() 3042 for _, path := range []string{ 3043 "testdata/dirfs/a", // not DirFS(".") 3044 filepath.ToSlash(cwd) + "/testdata/dirfs/a", // not DirFS("/") 3045 } { 3046 _, err := d.Open(path) 3047 if err == nil { 3048 t.Fatalf(`DirFS("").Open(%q) succeeded`, path) 3049 } 3050 } 3051 } 3052 3053 func TestDirFSPathsValid(t *testing.T) { 3054 if runtime.GOOS == "windows" { 3055 t.Skipf("skipping on Windows") 3056 } 3057 t.Parallel() 3058 3059 d := t.TempDir() 3060 if err := WriteFile(filepath.Join(d, "control.txt"), []byte(string("Hello, world!")), 0644); err != nil { 3061 t.Fatal(err) 3062 } 3063 if err := WriteFile(filepath.Join(d, `e:xperi\ment.txt`), []byte(string("Hello, colon and backslash!")), 0644); err != nil { 3064 t.Fatal(err) 3065 } 3066 3067 fsys := DirFS(d) 3068 err := fs.WalkDir(fsys, ".", func(path string, e fs.DirEntry, err error) error { 3069 if fs.ValidPath(e.Name()) { 3070 t.Logf("%q ok", e.Name()) 3071 } else { 3072 t.Errorf("%q INVALID", e.Name()) 3073 } 3074 return nil 3075 }) 3076 if err != nil { 3077 t.Fatal(err) 3078 } 3079 } 3080 3081 func TestReadFileProc(t *testing.T) { 3082 t.Parallel() 3083 3084 // Linux files in /proc report 0 size, 3085 // but then if ReadFile reads just a single byte at offset 0, 3086 // the read at offset 1 returns EOF instead of more data. 3087 // ReadFile has a minimum read size of 512 to work around this, 3088 // but test explicitly that it's working. 3089 name := "/proc/sys/fs/pipe-max-size" 3090 if _, err := Stat(name); err != nil { 3091 t.Skip(err) 3092 } 3093 data, err := ReadFile(name) 3094 if err != nil { 3095 t.Fatal(err) 3096 } 3097 if len(data) == 0 || data[len(data)-1] != '\n' { 3098 t.Fatalf("read %s: not newline-terminated: %q", name, data) 3099 } 3100 } 3101 3102 func TestWriteStringAlloc(t *testing.T) { 3103 if runtime.GOOS == "js" { 3104 t.Skip("js allocates a lot during File.WriteString") 3105 } 3106 d := t.TempDir() 3107 f, err := Create(filepath.Join(d, "whiteboard.txt")) 3108 if err != nil { 3109 t.Fatal(err) 3110 } 3111 defer f.Close() 3112 allocs := testing.AllocsPerRun(100, func() { 3113 f.WriteString("I will not allocate when passed a string longer than 32 bytes.\n") 3114 }) 3115 if allocs != 0 { 3116 t.Errorf("expected 0 allocs for File.WriteString, got %v", allocs) 3117 } 3118 } 3119 3120 // Test that it's OK to have parallel I/O and Close on a pipe. 3121 func TestPipeIOCloseRace(t *testing.T) { 3122 // Skip on wasm, which doesn't have pipes. 3123 if runtime.GOOS == "js" || runtime.GOOS == "wasip1" { 3124 t.Skipf("skipping on %s: no pipes", runtime.GOOS) 3125 } 3126 t.Parallel() 3127 3128 r, w, err := Pipe() 3129 if err != nil { 3130 t.Fatal(err) 3131 } 3132 3133 var wg sync.WaitGroup 3134 wg.Add(3) 3135 3136 go func() { 3137 defer wg.Done() 3138 for { 3139 n, err := w.Write([]byte("hi")) 3140 if err != nil { 3141 // We look at error strings as the 3142 // expected errors are OS-specific. 3143 switch { 3144 case errors.Is(err, ErrClosed), 3145 strings.Contains(err.Error(), "broken pipe"), 3146 strings.Contains(err.Error(), "pipe is being closed"), 3147 strings.Contains(err.Error(), "hungup channel"): 3148 // Ignore an expected error. 3149 default: 3150 // Unexpected error. 3151 t.Error(err) 3152 } 3153 return 3154 } 3155 if n != 2 { 3156 t.Errorf("wrote %d bytes, expected 2", n) 3157 return 3158 } 3159 } 3160 }() 3161 3162 go func() { 3163 defer wg.Done() 3164 for { 3165 var buf [2]byte 3166 n, err := r.Read(buf[:]) 3167 if err != nil { 3168 if err != io.EOF && !errors.Is(err, ErrClosed) { 3169 t.Error(err) 3170 } 3171 return 3172 } 3173 if n != 2 { 3174 t.Errorf("read %d bytes, want 2", n) 3175 } 3176 } 3177 }() 3178 3179 go func() { 3180 defer wg.Done() 3181 3182 // Let the other goroutines start. This is just to get 3183 // a better test, the test will still pass if they 3184 // don't start. 3185 time.Sleep(time.Millisecond) 3186 3187 if err := r.Close(); err != nil { 3188 t.Error(err) 3189 } 3190 if err := w.Close(); err != nil { 3191 t.Error(err) 3192 } 3193 }() 3194 3195 wg.Wait() 3196 } 3197 3198 // Test that it's OK to call Close concurrently on a pipe. 3199 func TestPipeCloseRace(t *testing.T) { 3200 // Skip on wasm, which doesn't have pipes. 3201 if runtime.GOOS == "js" || runtime.GOOS == "wasip1" { 3202 t.Skipf("skipping on %s: no pipes", runtime.GOOS) 3203 } 3204 t.Parallel() 3205 3206 r, w, err := Pipe() 3207 if err != nil { 3208 t.Fatal(err) 3209 } 3210 var wg sync.WaitGroup 3211 c := make(chan error, 4) 3212 f := func() { 3213 defer wg.Done() 3214 c <- r.Close() 3215 c <- w.Close() 3216 } 3217 wg.Add(2) 3218 go f() 3219 go f() 3220 nils, errs := 0, 0 3221 for i := 0; i < 4; i++ { 3222 err := <-c 3223 if err == nil { 3224 nils++ 3225 } else { 3226 errs++ 3227 } 3228 } 3229 if nils != 2 || errs != 2 { 3230 t.Errorf("got nils %d errs %d, want 2 2", nils, errs) 3231 } 3232 }