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