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