github.com/lovishpuri/go-40569/src@v0.0.0-20230519171745-f8623e7c56cf/os/os_windows_test.go (about) 1 // Copyright 2014 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 "fmt" 10 "internal/poll" 11 "internal/syscall/windows" 12 "internal/syscall/windows/registry" 13 "internal/testenv" 14 "io" 15 "io/fs" 16 "os" 17 "os/exec" 18 "path/filepath" 19 "reflect" 20 "runtime" 21 "slices" 22 "sort" 23 "strings" 24 "syscall" 25 "testing" 26 "unicode/utf16" 27 "unsafe" 28 ) 29 30 // For TestRawConnReadWrite. 31 type syscallDescriptor = syscall.Handle 32 33 // chdir changes the current working directory to the named directory, 34 // and then restore the original working directory at the end of the test. 35 func chdir(t *testing.T, dir string) { 36 olddir, err := os.Getwd() 37 if err != nil { 38 t.Fatalf("chdir: %v", err) 39 } 40 if err := os.Chdir(dir); err != nil { 41 t.Fatalf("chdir %s: %v", dir, err) 42 } 43 44 t.Cleanup(func() { 45 if err := os.Chdir(olddir); err != nil { 46 t.Errorf("chdir to original working directory %s: %v", olddir, err) 47 os.Exit(1) 48 } 49 }) 50 } 51 52 func TestSameWindowsFile(t *testing.T) { 53 temp := t.TempDir() 54 chdir(t, temp) 55 56 f, err := os.Create("a") 57 if err != nil { 58 t.Fatal(err) 59 } 60 f.Close() 61 62 ia1, err := os.Stat("a") 63 if err != nil { 64 t.Fatal(err) 65 } 66 67 path, err := filepath.Abs("a") 68 if err != nil { 69 t.Fatal(err) 70 } 71 ia2, err := os.Stat(path) 72 if err != nil { 73 t.Fatal(err) 74 } 75 if !os.SameFile(ia1, ia2) { 76 t.Errorf("files should be same") 77 } 78 79 p := filepath.VolumeName(path) + filepath.Base(path) 80 if err != nil { 81 t.Fatal(err) 82 } 83 ia3, err := os.Stat(p) 84 if err != nil { 85 t.Fatal(err) 86 } 87 if !os.SameFile(ia1, ia3) { 88 t.Errorf("files should be same") 89 } 90 } 91 92 type dirLinkTest struct { 93 name string 94 mklink func(link, target string) error 95 issueNo int // correspondent issue number (for broken tests) 96 } 97 98 func testDirLinks(t *testing.T, tests []dirLinkTest) { 99 tmpdir := t.TempDir() 100 chdir(t, tmpdir) 101 102 dir := filepath.Join(tmpdir, "dir") 103 err := os.Mkdir(dir, 0777) 104 if err != nil { 105 t.Fatal(err) 106 } 107 fi, err := os.Stat(dir) 108 if err != nil { 109 t.Fatal(err) 110 } 111 err = os.WriteFile(filepath.Join(dir, "abc"), []byte("abc"), 0644) 112 if err != nil { 113 t.Fatal(err) 114 } 115 for _, test := range tests { 116 link := filepath.Join(tmpdir, test.name+"_link") 117 err := test.mklink(link, dir) 118 if err != nil { 119 t.Errorf("creating link for %q test failed: %v", test.name, err) 120 continue 121 } 122 123 data, err := os.ReadFile(filepath.Join(link, "abc")) 124 if err != nil { 125 t.Errorf("failed to read abc file: %v", err) 126 continue 127 } 128 if string(data) != "abc" { 129 t.Errorf(`abc file is expected to have "abc" in it, but has %v`, data) 130 continue 131 } 132 133 if test.issueNo > 0 { 134 t.Logf("skipping broken %q test: see issue %d", test.name, test.issueNo) 135 continue 136 } 137 138 fi1, err := os.Stat(link) 139 if err != nil { 140 t.Errorf("failed to stat link %v: %v", link, err) 141 continue 142 } 143 if !fi1.IsDir() { 144 t.Errorf("%q should be a directory", link) 145 continue 146 } 147 if fi1.Name() != filepath.Base(link) { 148 t.Errorf("Stat(%q).Name() = %q, want %q", link, fi1.Name(), filepath.Base(link)) 149 continue 150 } 151 if !os.SameFile(fi, fi1) { 152 t.Errorf("%q should point to %q", link, dir) 153 continue 154 } 155 156 fi2, err := os.Lstat(link) 157 if err != nil { 158 t.Errorf("failed to lstat link %v: %v", link, err) 159 continue 160 } 161 if m := fi2.Mode(); m&fs.ModeSymlink == 0 { 162 t.Errorf("%q should be a link, but is not (mode=0x%x)", link, uint32(m)) 163 continue 164 } 165 if m := fi2.Mode(); m&fs.ModeDir != 0 { 166 t.Errorf("%q should be a link, not a directory (mode=0x%x)", link, uint32(m)) 167 continue 168 } 169 } 170 } 171 172 // reparseData is used to build reparse buffer data required for tests. 173 type reparseData struct { 174 substituteName namePosition 175 printName namePosition 176 pathBuf []uint16 177 } 178 179 type namePosition struct { 180 offset uint16 181 length uint16 182 } 183 184 func (rd *reparseData) addUTF16s(s []uint16) (offset uint16) { 185 off := len(rd.pathBuf) * 2 186 rd.pathBuf = append(rd.pathBuf, s...) 187 return uint16(off) 188 } 189 190 func (rd *reparseData) addString(s string) (offset, length uint16) { 191 p := syscall.StringToUTF16(s) 192 return rd.addUTF16s(p), uint16(len(p)-1) * 2 // do not include terminating NUL in the length (as per PrintNameLength and SubstituteNameLength documentation) 193 } 194 195 func (rd *reparseData) addSubstituteName(name string) { 196 rd.substituteName.offset, rd.substituteName.length = rd.addString(name) 197 } 198 199 func (rd *reparseData) addPrintName(name string) { 200 rd.printName.offset, rd.printName.length = rd.addString(name) 201 } 202 203 func (rd *reparseData) addStringNoNUL(s string) (offset, length uint16) { 204 p := syscall.StringToUTF16(s) 205 p = p[:len(p)-1] 206 return rd.addUTF16s(p), uint16(len(p)) * 2 207 } 208 209 func (rd *reparseData) addSubstituteNameNoNUL(name string) { 210 rd.substituteName.offset, rd.substituteName.length = rd.addStringNoNUL(name) 211 } 212 213 func (rd *reparseData) addPrintNameNoNUL(name string) { 214 rd.printName.offset, rd.printName.length = rd.addStringNoNUL(name) 215 } 216 217 // pathBuffeLen returns length of rd pathBuf in bytes. 218 func (rd *reparseData) pathBuffeLen() uint16 { 219 return uint16(len(rd.pathBuf)) * 2 220 } 221 222 // Windows REPARSE_DATA_BUFFER contains union member, and cannot be 223 // translated into Go directly. _REPARSE_DATA_BUFFER type is to help 224 // construct alternative versions of Windows REPARSE_DATA_BUFFER with 225 // union part of SymbolicLinkReparseBuffer or MountPointReparseBuffer type. 226 type _REPARSE_DATA_BUFFER struct { 227 header windows.REPARSE_DATA_BUFFER_HEADER 228 detail [syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]byte 229 } 230 231 func createDirLink(link string, rdb *_REPARSE_DATA_BUFFER) error { 232 err := os.Mkdir(link, 0777) 233 if err != nil { 234 return err 235 } 236 237 linkp := syscall.StringToUTF16(link) 238 fd, err := syscall.CreateFile(&linkp[0], syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, 239 syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) 240 if err != nil { 241 return err 242 } 243 defer syscall.CloseHandle(fd) 244 245 buflen := uint32(rdb.header.ReparseDataLength) + uint32(unsafe.Sizeof(rdb.header)) 246 var bytesReturned uint32 247 return syscall.DeviceIoControl(fd, windows.FSCTL_SET_REPARSE_POINT, 248 (*byte)(unsafe.Pointer(&rdb.header)), buflen, nil, 0, &bytesReturned, nil) 249 } 250 251 func createMountPoint(link string, target *reparseData) error { 252 var buf *windows.MountPointReparseBuffer 253 buflen := uint16(unsafe.Offsetof(buf.PathBuffer)) + target.pathBuffeLen() // see ReparseDataLength documentation 254 byteblob := make([]byte, buflen) 255 buf = (*windows.MountPointReparseBuffer)(unsafe.Pointer(&byteblob[0])) 256 buf.SubstituteNameOffset = target.substituteName.offset 257 buf.SubstituteNameLength = target.substituteName.length 258 buf.PrintNameOffset = target.printName.offset 259 buf.PrintNameLength = target.printName.length 260 pbuflen := len(target.pathBuf) 261 copy((*[2048]uint16)(unsafe.Pointer(&buf.PathBuffer[0]))[:pbuflen:pbuflen], target.pathBuf) 262 263 var rdb _REPARSE_DATA_BUFFER 264 rdb.header.ReparseTag = windows.IO_REPARSE_TAG_MOUNT_POINT 265 rdb.header.ReparseDataLength = buflen 266 copy(rdb.detail[:], byteblob) 267 268 return createDirLink(link, &rdb) 269 } 270 271 func TestDirectoryJunction(t *testing.T) { 272 var tests = []dirLinkTest{ 273 { 274 // Create link similar to what mklink does, by inserting \??\ at the front of absolute target. 275 name: "standard", 276 mklink: func(link, target string) error { 277 var t reparseData 278 t.addSubstituteName(`\??\` + target) 279 t.addPrintName(target) 280 return createMountPoint(link, &t) 281 }, 282 }, 283 { 284 // Do as junction utility https://technet.microsoft.com/en-au/sysinternals/bb896768.aspx does - set PrintNameLength to 0. 285 name: "have_blank_print_name", 286 mklink: func(link, target string) error { 287 var t reparseData 288 t.addSubstituteName(`\??\` + target) 289 t.addPrintName("") 290 return createMountPoint(link, &t) 291 }, 292 }, 293 } 294 output, _ := testenv.Command(t, "cmd", "/c", "mklink", "/?").Output() 295 mklinkSupportsJunctionLinks := strings.Contains(string(output), " /J ") 296 if mklinkSupportsJunctionLinks { 297 tests = append(tests, 298 dirLinkTest{ 299 name: "use_mklink_cmd", 300 mklink: func(link, target string) error { 301 output, err := testenv.Command(t, "cmd", "/c", "mklink", "/J", link, target).CombinedOutput() 302 if err != nil { 303 t.Errorf("failed to run mklink %v %v: %v %q", link, target, err, output) 304 } 305 return nil 306 }, 307 }, 308 ) 309 } else { 310 t.Log(`skipping "use_mklink_cmd" test, mklink does not supports directory junctions`) 311 } 312 testDirLinks(t, tests) 313 } 314 315 func enableCurrentThreadPrivilege(privilegeName string) error { 316 ct, err := windows.GetCurrentThread() 317 if err != nil { 318 return err 319 } 320 var t syscall.Token 321 err = windows.OpenThreadToken(ct, syscall.TOKEN_QUERY|windows.TOKEN_ADJUST_PRIVILEGES, false, &t) 322 if err != nil { 323 return err 324 } 325 defer syscall.CloseHandle(syscall.Handle(t)) 326 327 var tp windows.TOKEN_PRIVILEGES 328 329 privStr, err := syscall.UTF16PtrFromString(privilegeName) 330 if err != nil { 331 return err 332 } 333 err = windows.LookupPrivilegeValue(nil, privStr, &tp.Privileges[0].Luid) 334 if err != nil { 335 return err 336 } 337 tp.PrivilegeCount = 1 338 tp.Privileges[0].Attributes = windows.SE_PRIVILEGE_ENABLED 339 return windows.AdjustTokenPrivileges(t, false, &tp, 0, nil, nil) 340 } 341 342 func createSymbolicLink(link string, target *reparseData, isrelative bool) error { 343 var buf *windows.SymbolicLinkReparseBuffer 344 buflen := uint16(unsafe.Offsetof(buf.PathBuffer)) + target.pathBuffeLen() // see ReparseDataLength documentation 345 byteblob := make([]byte, buflen) 346 buf = (*windows.SymbolicLinkReparseBuffer)(unsafe.Pointer(&byteblob[0])) 347 buf.SubstituteNameOffset = target.substituteName.offset 348 buf.SubstituteNameLength = target.substituteName.length 349 buf.PrintNameOffset = target.printName.offset 350 buf.PrintNameLength = target.printName.length 351 if isrelative { 352 buf.Flags = windows.SYMLINK_FLAG_RELATIVE 353 } 354 pbuflen := len(target.pathBuf) 355 copy((*[2048]uint16)(unsafe.Pointer(&buf.PathBuffer[0]))[:pbuflen:pbuflen], target.pathBuf) 356 357 var rdb _REPARSE_DATA_BUFFER 358 rdb.header.ReparseTag = syscall.IO_REPARSE_TAG_SYMLINK 359 rdb.header.ReparseDataLength = buflen 360 copy(rdb.detail[:], byteblob) 361 362 return createDirLink(link, &rdb) 363 } 364 365 func TestDirectorySymbolicLink(t *testing.T) { 366 var tests []dirLinkTest 367 output, _ := testenv.Command(t, "cmd", "/c", "mklink", "/?").Output() 368 mklinkSupportsDirectorySymbolicLinks := strings.Contains(string(output), " /D ") 369 if mklinkSupportsDirectorySymbolicLinks { 370 tests = append(tests, 371 dirLinkTest{ 372 name: "use_mklink_cmd", 373 mklink: func(link, target string) error { 374 output, err := testenv.Command(t, "cmd", "/c", "mklink", "/D", link, target).CombinedOutput() 375 if err != nil { 376 t.Errorf("failed to run mklink %v %v: %v %q", link, target, err, output) 377 } 378 return nil 379 }, 380 }, 381 ) 382 } else { 383 t.Log(`skipping "use_mklink_cmd" test, mklink does not supports directory symbolic links`) 384 } 385 386 // The rest of these test requires SeCreateSymbolicLinkPrivilege to be held. 387 runtime.LockOSThread() 388 defer runtime.UnlockOSThread() 389 390 err := windows.ImpersonateSelf(windows.SecurityImpersonation) 391 if err != nil { 392 t.Fatal(err) 393 } 394 defer windows.RevertToSelf() 395 396 err = enableCurrentThreadPrivilege("SeCreateSymbolicLinkPrivilege") 397 if err != nil { 398 t.Skipf(`skipping some tests, could not enable "SeCreateSymbolicLinkPrivilege": %v`, err) 399 } 400 tests = append(tests, 401 dirLinkTest{ 402 name: "use_os_pkg", 403 mklink: func(link, target string) error { 404 return os.Symlink(target, link) 405 }, 406 }, 407 dirLinkTest{ 408 // Create link similar to what mklink does, by inserting \??\ at the front of absolute target. 409 name: "standard", 410 mklink: func(link, target string) error { 411 var t reparseData 412 t.addPrintName(target) 413 t.addSubstituteName(`\??\` + target) 414 return createSymbolicLink(link, &t, false) 415 }, 416 }, 417 dirLinkTest{ 418 name: "relative", 419 mklink: func(link, target string) error { 420 var t reparseData 421 t.addSubstituteNameNoNUL(filepath.Base(target)) 422 t.addPrintNameNoNUL(filepath.Base(target)) 423 return createSymbolicLink(link, &t, true) 424 }, 425 }, 426 ) 427 testDirLinks(t, tests) 428 } 429 430 func TestNetworkSymbolicLink(t *testing.T) { 431 testenv.MustHaveSymlink(t) 432 433 const _NERR_ServerNotStarted = syscall.Errno(2114) 434 435 dir := t.TempDir() 436 chdir(t, dir) 437 438 shareName := "GoSymbolicLinkTestShare" // hope no conflictions 439 sharePath := filepath.Join(dir, shareName) 440 testDir := "TestDir" 441 442 err := os.MkdirAll(filepath.Join(sharePath, testDir), 0777) 443 if err != nil { 444 t.Fatal(err) 445 } 446 447 wShareName, err := syscall.UTF16PtrFromString(shareName) 448 if err != nil { 449 t.Fatal(err) 450 } 451 wSharePath, err := syscall.UTF16PtrFromString(sharePath) 452 if err != nil { 453 t.Fatal(err) 454 } 455 456 p := windows.SHARE_INFO_2{ 457 Netname: wShareName, 458 Type: windows.STYPE_DISKTREE, 459 Remark: nil, 460 Permissions: 0, 461 MaxUses: 1, 462 CurrentUses: 0, 463 Path: wSharePath, 464 Passwd: nil, 465 } 466 467 err = windows.NetShareAdd(nil, 2, (*byte)(unsafe.Pointer(&p)), nil) 468 if err != nil { 469 if err == syscall.ERROR_ACCESS_DENIED { 470 t.Skip("you don't have enough privileges to add network share") 471 } 472 if err == _NERR_ServerNotStarted { 473 t.Skip(_NERR_ServerNotStarted.Error()) 474 } 475 t.Fatal(err) 476 } 477 defer func() { 478 err := windows.NetShareDel(nil, wShareName, 0) 479 if err != nil { 480 t.Fatal(err) 481 } 482 }() 483 484 UNCPath := `\\localhost\` + shareName + `\` 485 486 fi1, err := os.Stat(sharePath) 487 if err != nil { 488 t.Fatal(err) 489 } 490 fi2, err := os.Stat(UNCPath) 491 if err != nil { 492 t.Fatal(err) 493 } 494 if !os.SameFile(fi1, fi2) { 495 t.Fatalf("%q and %q should be the same directory, but not", sharePath, UNCPath) 496 } 497 498 target := filepath.Join(UNCPath, testDir) 499 link := "link" 500 501 err = os.Symlink(target, link) 502 if err != nil { 503 t.Fatal(err) 504 } 505 defer os.Remove(link) 506 507 got, err := os.Readlink(link) 508 if err != nil { 509 t.Fatal(err) 510 } 511 if got != target { 512 t.Errorf(`os.Readlink("%s"): got %v, want %v`, link, got, target) 513 } 514 515 got, err = filepath.EvalSymlinks(link) 516 if err != nil { 517 t.Fatal(err) 518 } 519 if got != target { 520 t.Errorf(`filepath.EvalSymlinks("%s"): got %v, want %v`, link, got, target) 521 } 522 } 523 524 func TestStartProcessAttr(t *testing.T) { 525 t.Parallel() 526 527 p, err := os.StartProcess(os.Getenv("COMSPEC"), []string{"/c", "cd"}, new(os.ProcAttr)) 528 if err != nil { 529 return 530 } 531 defer p.Wait() 532 t.Fatalf("StartProcess expected to fail, but succeeded.") 533 } 534 535 func TestShareNotExistError(t *testing.T) { 536 if testing.Short() { 537 t.Skip("slow test that uses network; skipping") 538 } 539 t.Parallel() 540 541 _, err := os.Stat(`\\no_such_server\no_such_share\no_such_file`) 542 if err == nil { 543 t.Fatal("stat succeeded, but expected to fail") 544 } 545 if !os.IsNotExist(err) { 546 t.Fatalf("os.Stat failed with %q, but os.IsNotExist(err) is false", err) 547 } 548 } 549 550 func TestBadNetPathError(t *testing.T) { 551 const ERROR_BAD_NETPATH = syscall.Errno(53) 552 if !os.IsNotExist(ERROR_BAD_NETPATH) { 553 t.Fatal("os.IsNotExist(syscall.Errno(53)) is false, but want true") 554 } 555 } 556 557 func TestStatDir(t *testing.T) { 558 defer chtmpdir(t)() 559 560 f, err := os.Open(".") 561 if err != nil { 562 t.Fatal(err) 563 } 564 defer f.Close() 565 566 fi, err := f.Stat() 567 if err != nil { 568 t.Fatal(err) 569 } 570 571 err = os.Chdir("..") 572 if err != nil { 573 t.Fatal(err) 574 } 575 576 fi2, err := f.Stat() 577 if err != nil { 578 t.Fatal(err) 579 } 580 581 if !os.SameFile(fi, fi2) { 582 t.Fatal("race condition occurred") 583 } 584 } 585 586 func TestOpenVolumeName(t *testing.T) { 587 tmpdir := t.TempDir() 588 chdir(t, tmpdir) 589 590 want := []string{"file1", "file2", "file3", "gopher.txt"} 591 sort.Strings(want) 592 for _, name := range want { 593 err := os.WriteFile(filepath.Join(tmpdir, name), nil, 0777) 594 if err != nil { 595 t.Fatal(err) 596 } 597 } 598 599 f, err := os.Open(filepath.VolumeName(tmpdir)) 600 if err != nil { 601 t.Fatal(err) 602 } 603 defer f.Close() 604 605 have, err := f.Readdirnames(-1) 606 if err != nil { 607 t.Fatal(err) 608 } 609 sort.Strings(have) 610 611 if strings.Join(want, "/") != strings.Join(have, "/") { 612 t.Fatalf("unexpected file list %q, want %q", have, want) 613 } 614 } 615 616 func TestDeleteReadOnly(t *testing.T) { 617 t.Parallel() 618 619 tmpdir := t.TempDir() 620 p := filepath.Join(tmpdir, "a") 621 // This sets FILE_ATTRIBUTE_READONLY. 622 f, err := os.OpenFile(p, os.O_CREATE, 0400) 623 if err != nil { 624 t.Fatal(err) 625 } 626 f.Close() 627 628 if err = os.Chmod(p, 0400); err != nil { 629 t.Fatal(err) 630 } 631 if err = os.Remove(p); err != nil { 632 t.Fatal(err) 633 } 634 } 635 636 func TestReadStdin(t *testing.T) { 637 old := poll.ReadConsole 638 defer func() { 639 poll.ReadConsole = old 640 }() 641 642 p, err := syscall.GetCurrentProcess() 643 if err != nil { 644 t.Fatalf("Unable to get handle to current process: %v", err) 645 } 646 var stdinDuplicate syscall.Handle 647 err = syscall.DuplicateHandle(p, syscall.Handle(syscall.Stdin), p, &stdinDuplicate, 0, false, syscall.DUPLICATE_SAME_ACCESS) 648 if err != nil { 649 t.Fatalf("Unable to duplicate stdin: %v", err) 650 } 651 testConsole := os.NewConsoleFile(stdinDuplicate, "test") 652 653 var tests = []string{ 654 "abc", 655 "äöü", 656 "\u3042", 657 "“hi”™", 658 "hello\x1aworld", 659 "\U0001F648\U0001F649\U0001F64A", 660 } 661 662 for _, consoleSize := range []int{1, 2, 3, 10, 16, 100, 1000} { 663 for _, readSize := range []int{1, 2, 3, 4, 5, 8, 10, 16, 20, 50, 100} { 664 for _, s := range tests { 665 t.Run(fmt.Sprintf("c%d/r%d/%s", consoleSize, readSize, s), func(t *testing.T) { 666 s16 := utf16.Encode([]rune(s)) 667 poll.ReadConsole = func(h syscall.Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) error { 668 if inputControl != nil { 669 t.Fatalf("inputControl not nil") 670 } 671 n := int(toread) 672 if n > consoleSize { 673 n = consoleSize 674 } 675 n = copy((*[10000]uint16)(unsafe.Pointer(buf))[:n:n], s16) 676 s16 = s16[n:] 677 *read = uint32(n) 678 t.Logf("read %d -> %d", toread, *read) 679 return nil 680 } 681 682 var all []string 683 var buf []byte 684 chunk := make([]byte, readSize) 685 for { 686 n, err := testConsole.Read(chunk) 687 buf = append(buf, chunk[:n]...) 688 if err == io.EOF { 689 all = append(all, string(buf)) 690 if len(all) >= 5 { 691 break 692 } 693 buf = buf[:0] 694 } else if err != nil { 695 t.Fatalf("reading %q: error: %v", s, err) 696 } 697 if len(buf) >= 2000 { 698 t.Fatalf("reading %q: stuck in loop: %q", s, buf) 699 } 700 } 701 702 want := strings.Split(s, "\x1a") 703 for len(want) < 5 { 704 want = append(want, "") 705 } 706 if !reflect.DeepEqual(all, want) { 707 t.Errorf("reading %q:\nhave %x\nwant %x", s, all, want) 708 } 709 }) 710 } 711 } 712 } 713 } 714 715 func TestStatPagefile(t *testing.T) { 716 t.Parallel() 717 718 const path = `c:\pagefile.sys` 719 fi, err := os.Stat(path) 720 if err == nil { 721 if fi.Name() == "" { 722 t.Fatalf("Stat(%q).Name() is empty", path) 723 } 724 t.Logf("Stat(%q).Size() = %v", path, fi.Size()) 725 return 726 } 727 if os.IsNotExist(err) { 728 t.Skip(`skipping because c:\pagefile.sys is not found`) 729 } 730 t.Fatal(err) 731 } 732 733 // syscallCommandLineToArgv calls syscall.CommandLineToArgv 734 // and converts returned result into []string. 735 func syscallCommandLineToArgv(cmd string) ([]string, error) { 736 var argc int32 737 argv, err := syscall.CommandLineToArgv(&syscall.StringToUTF16(cmd)[0], &argc) 738 if err != nil { 739 return nil, err 740 } 741 defer syscall.LocalFree(syscall.Handle(uintptr(unsafe.Pointer(argv)))) 742 743 var args []string 744 for _, v := range (*argv)[:argc] { 745 args = append(args, syscall.UTF16ToString((*v)[:])) 746 } 747 return args, nil 748 } 749 750 // compareCommandLineToArgvWithSyscall ensures that 751 // os.CommandLineToArgv(cmd) and syscall.CommandLineToArgv(cmd) 752 // return the same result. 753 func compareCommandLineToArgvWithSyscall(t *testing.T, cmd string) { 754 syscallArgs, err := syscallCommandLineToArgv(cmd) 755 if err != nil { 756 t.Fatal(err) 757 } 758 args := os.CommandLineToArgv(cmd) 759 if want, have := fmt.Sprintf("%q", syscallArgs), fmt.Sprintf("%q", args); want != have { 760 t.Errorf("testing os.commandLineToArgv(%q) failed: have %q want %q", cmd, args, syscallArgs) 761 return 762 } 763 } 764 765 func TestCmdArgs(t *testing.T) { 766 if testing.Short() { 767 t.Skipf("in short mode; skipping test that builds a binary") 768 } 769 t.Parallel() 770 771 tmpdir := t.TempDir() 772 773 const prog = ` 774 package main 775 776 import ( 777 "fmt" 778 "os" 779 ) 780 781 func main() { 782 fmt.Printf("%q", os.Args) 783 } 784 ` 785 src := filepath.Join(tmpdir, "main.go") 786 if err := os.WriteFile(src, []byte(prog), 0666); err != nil { 787 t.Fatal(err) 788 } 789 790 exe := filepath.Join(tmpdir, "main.exe") 791 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, src) 792 cmd.Dir = tmpdir 793 out, err := cmd.CombinedOutput() 794 if err != nil { 795 t.Fatalf("building main.exe failed: %v\n%s", err, out) 796 } 797 798 var cmds = []string{ 799 ``, 800 ` a b c`, 801 ` "`, 802 ` ""`, 803 ` """`, 804 ` "" a`, 805 ` "123"`, 806 ` \"123\"`, 807 ` \"123 456\"`, 808 ` \\"`, 809 ` \\\"`, 810 ` \\\\\"`, 811 ` \\\"x`, 812 ` """"\""\\\"`, 813 ` abc`, 814 ` \\\\\""x"""y z`, 815 "\tb\t\"x\ty\"", 816 ` "Брад" d e`, 817 // examples from https://msdn.microsoft.com/en-us/library/17w5ykft.aspx 818 ` "abc" d e`, 819 ` a\\b d"e f"g h`, 820 ` a\\\"b c d`, 821 ` a\\\\"b c" d e`, 822 // http://daviddeley.com/autohotkey/parameters/parameters.htm#WINARGV 823 // from 5.4 Examples 824 ` CallMeIshmael`, 825 ` "Call Me Ishmael"`, 826 ` Cal"l Me I"shmael`, 827 ` CallMe\"Ishmael`, 828 ` "CallMe\"Ishmael"`, 829 ` "Call Me Ishmael\\"`, 830 ` "CallMe\\\"Ishmael"`, 831 ` a\\\b`, 832 ` "a\\\b"`, 833 // from 5.5 Some Common Tasks 834 ` "\"Call Me Ishmael\""`, 835 ` "C:\TEST A\\"`, 836 ` "\"C:\TEST A\\\""`, 837 // from 5.6 The Microsoft Examples Explained 838 ` "a b c" d e`, 839 ` "ab\"c" "\\" d`, 840 ` a\\\b d"e f"g h`, 841 ` a\\\"b c d`, 842 ` a\\\\"b c" d e`, 843 // from 5.7 Double Double Quote Examples (pre 2008) 844 ` "a b c""`, 845 ` """CallMeIshmael""" b c`, 846 ` """Call Me Ishmael"""`, 847 ` """"Call Me Ishmael"" b c`, 848 } 849 for _, cmd := range cmds { 850 compareCommandLineToArgvWithSyscall(t, "test"+cmd) 851 compareCommandLineToArgvWithSyscall(t, `"cmd line"`+cmd) 852 compareCommandLineToArgvWithSyscall(t, exe+cmd) 853 854 // test both syscall.EscapeArg and os.commandLineToArgv 855 args := os.CommandLineToArgv(exe + cmd) 856 out, err := testenv.Command(t, args[0], args[1:]...).CombinedOutput() 857 if err != nil { 858 t.Fatalf("running %q failed: %v\n%v", args, err, string(out)) 859 } 860 if want, have := fmt.Sprintf("%q", args), string(out); want != have { 861 t.Errorf("wrong output of executing %q: have %q want %q", args, have, want) 862 continue 863 } 864 } 865 } 866 867 func findOneDriveDir() (string, error) { 868 // as per https://stackoverflow.com/questions/42519624/how-to-determine-location-of-onedrive-on-windows-7-and-8-in-c 869 const onedrivekey = `SOFTWARE\Microsoft\OneDrive` 870 k, err := registry.OpenKey(registry.CURRENT_USER, onedrivekey, registry.READ) 871 if err != nil { 872 return "", fmt.Errorf("OpenKey(%q) failed: %v", onedrivekey, err) 873 } 874 defer k.Close() 875 876 path, valtype, err := k.GetStringValue("UserFolder") 877 if err != nil { 878 return "", fmt.Errorf("reading UserFolder failed: %v", err) 879 } 880 881 if valtype == registry.EXPAND_SZ { 882 expanded, err := registry.ExpandString(path) 883 if err != nil { 884 return "", fmt.Errorf("expanding UserFolder failed: %v", err) 885 } 886 path = expanded 887 } 888 889 return path, nil 890 } 891 892 // TestOneDrive verifies that OneDrive folder is a directory and not a symlink. 893 func TestOneDrive(t *testing.T) { 894 t.Parallel() 895 896 dir, err := findOneDriveDir() 897 if err != nil { 898 t.Skipf("Skipping, because we did not find OneDrive directory: %v", err) 899 } 900 testDirStats(t, dir) 901 } 902 903 func TestWindowsDevNullFile(t *testing.T) { 904 t.Parallel() 905 906 f1, err := os.Open("NUL") 907 if err != nil { 908 t.Fatal(err) 909 } 910 defer f1.Close() 911 912 fi1, err := f1.Stat() 913 if err != nil { 914 t.Fatal(err) 915 } 916 917 f2, err := os.Open("nul") 918 if err != nil { 919 t.Fatal(err) 920 } 921 defer f2.Close() 922 923 fi2, err := f2.Stat() 924 if err != nil { 925 t.Fatal(err) 926 } 927 928 if !os.SameFile(fi1, fi2) { 929 t.Errorf(`"NUL" and "nul" are not the same file`) 930 } 931 } 932 933 func TestFileStatNUL(t *testing.T) { 934 t.Parallel() 935 936 f, err := os.Open("NUL") 937 if err != nil { 938 t.Fatal(err) 939 } 940 fi, err := f.Stat() 941 if err != nil { 942 t.Fatal(err) 943 } 944 if got, want := fi.Mode(), os.ModeDevice|os.ModeCharDevice|0666; got != want { 945 t.Errorf("Open(%q).Stat().Mode() = %v, want %v", "NUL", got, want) 946 } 947 } 948 949 func TestStatNUL(t *testing.T) { 950 t.Parallel() 951 952 fi, err := os.Stat("NUL") 953 if err != nil { 954 t.Fatal(err) 955 } 956 if got, want := fi.Mode(), os.ModeDevice|os.ModeCharDevice|0666; got != want { 957 t.Errorf("Stat(%q).Mode() = %v, want %v", "NUL", got, want) 958 } 959 } 960 961 // TestSymlinkCreation verifies that creating a symbolic link 962 // works on Windows when developer mode is active. 963 // This is supported starting Windows 10 (1703, v10.0.14972). 964 func TestSymlinkCreation(t *testing.T) { 965 if !testenv.HasSymlink() && !isWindowsDeveloperModeActive() { 966 t.Skip("Windows developer mode is not active") 967 } 968 t.Parallel() 969 970 temp := t.TempDir() 971 dummyFile := filepath.Join(temp, "file") 972 if err := os.WriteFile(dummyFile, []byte(""), 0644); err != nil { 973 t.Fatal(err) 974 } 975 976 linkFile := filepath.Join(temp, "link") 977 if err := os.Symlink(dummyFile, linkFile); err != nil { 978 t.Fatal(err) 979 } 980 } 981 982 // isWindowsDeveloperModeActive checks whether or not the developer mode is active on Windows 10. 983 // Returns false for prior Windows versions. 984 // see https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development 985 func isWindowsDeveloperModeActive() bool { 986 key, err := registry.OpenKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", registry.READ) 987 if err != nil { 988 return false 989 } 990 991 val, _, err := key.GetIntegerValue("AllowDevelopmentWithoutDevLicense") 992 if err != nil { 993 return false 994 } 995 996 return val != 0 997 } 998 999 // TestRootRelativeDirSymlink verifies that symlinks to paths relative to the 1000 // drive root (beginning with "\" but no volume name) are created with the 1001 // correct symlink type. 1002 // (See https://golang.org/issue/39183#issuecomment-632175728.) 1003 func TestRootRelativeDirSymlink(t *testing.T) { 1004 testenv.MustHaveSymlink(t) 1005 t.Parallel() 1006 1007 temp := t.TempDir() 1008 dir := filepath.Join(temp, "dir") 1009 if err := os.Mkdir(dir, 0755); err != nil { 1010 t.Fatal(err) 1011 } 1012 1013 volumeRelDir := strings.TrimPrefix(dir, filepath.VolumeName(dir)) // leaves leading backslash 1014 1015 link := filepath.Join(temp, "link") 1016 err := os.Symlink(volumeRelDir, link) 1017 if err != nil { 1018 t.Fatal(err) 1019 } 1020 t.Logf("Symlink(%#q, %#q)", volumeRelDir, link) 1021 1022 f, err := os.Open(link) 1023 if err != nil { 1024 t.Fatal(err) 1025 } 1026 defer f.Close() 1027 if fi, err := f.Stat(); err != nil { 1028 t.Fatal(err) 1029 } else if !fi.IsDir() { 1030 t.Errorf("Open(%#q).Stat().IsDir() = false; want true", f.Name()) 1031 } 1032 } 1033 1034 // TestWorkingDirectoryRelativeSymlink verifies that symlinks to paths relative 1035 // to the current working directory for the drive, such as "C:File.txt", are 1036 // correctly converted to absolute links of the correct symlink type (per 1037 // https://docs.microsoft.com/en-us/windows/win32/fileio/creating-symbolic-links). 1038 func TestWorkingDirectoryRelativeSymlink(t *testing.T) { 1039 testenv.MustHaveSymlink(t) 1040 1041 // Construct a directory to be symlinked. 1042 temp := t.TempDir() 1043 if v := filepath.VolumeName(temp); len(v) < 2 || v[1] != ':' { 1044 t.Skipf("Can't test relative symlinks: t.TempDir() (%#q) does not begin with a drive letter.", temp) 1045 } 1046 1047 absDir := filepath.Join(temp, `dir\sub`) 1048 if err := os.MkdirAll(absDir, 0755); err != nil { 1049 t.Fatal(err) 1050 } 1051 1052 // Change to the temporary directory and construct a 1053 // working-directory-relative symlink. 1054 oldwd, err := os.Getwd() 1055 if err != nil { 1056 t.Fatal(err) 1057 } 1058 defer func() { 1059 if err := os.Chdir(oldwd); err != nil { 1060 t.Fatal(err) 1061 } 1062 }() 1063 if err := os.Chdir(temp); err != nil { 1064 t.Fatal(err) 1065 } 1066 t.Logf("Chdir(%#q)", temp) 1067 1068 wdRelDir := filepath.VolumeName(temp) + `dir\sub` // no backslash after volume. 1069 absLink := filepath.Join(temp, "link") 1070 err = os.Symlink(wdRelDir, absLink) 1071 if err != nil { 1072 t.Fatal(err) 1073 } 1074 t.Logf("Symlink(%#q, %#q)", wdRelDir, absLink) 1075 1076 // Now change back to the original working directory and verify that the 1077 // symlink still refers to its original path and is correctly marked as a 1078 // directory. 1079 if err := os.Chdir(oldwd); err != nil { 1080 t.Fatal(err) 1081 } 1082 t.Logf("Chdir(%#q)", oldwd) 1083 1084 resolved, err := os.Readlink(absLink) 1085 if err != nil { 1086 t.Errorf("Readlink(%#q): %v", absLink, err) 1087 } else if resolved != absDir { 1088 t.Errorf("Readlink(%#q) = %#q; want %#q", absLink, resolved, absDir) 1089 } 1090 1091 linkFile, err := os.Open(absLink) 1092 if err != nil { 1093 t.Fatal(err) 1094 } 1095 defer linkFile.Close() 1096 1097 linkInfo, err := linkFile.Stat() 1098 if err != nil { 1099 t.Fatal(err) 1100 } 1101 if !linkInfo.IsDir() { 1102 t.Errorf("Open(%#q).Stat().IsDir() = false; want true", absLink) 1103 } 1104 1105 absInfo, err := os.Stat(absDir) 1106 if err != nil { 1107 t.Fatal(err) 1108 } 1109 1110 if !os.SameFile(absInfo, linkInfo) { 1111 t.Errorf("SameFile(Stat(%#q), Open(%#q).Stat()) = false; want true", absDir, absLink) 1112 } 1113 } 1114 1115 // TestStatOfInvalidName is regression test for issue #24999. 1116 func TestStatOfInvalidName(t *testing.T) { 1117 t.Parallel() 1118 1119 _, err := os.Stat("*.go") 1120 if err == nil { 1121 t.Fatal(`os.Stat("*.go") unexpectedly succeeded`) 1122 } 1123 } 1124 1125 // findUnusedDriveLetter searches mounted drive list on the system 1126 // (starting from Z: and ending at D:) for unused drive letter. 1127 // It returns path to the found drive root directory (like Z:\) or error. 1128 func findUnusedDriveLetter() (string, error) { 1129 // Do not use A: and B:, because they are reserved for floppy drive. 1130 // Do not use C:, because it is normally used for main drive. 1131 for l := 'Z'; l >= 'D'; l-- { 1132 p := string(l) + `:\` 1133 _, err := os.Stat(p) 1134 if os.IsNotExist(err) { 1135 return p, nil 1136 } 1137 } 1138 return "", errors.New("Could not find unused drive letter.") 1139 } 1140 1141 func TestRootDirAsTemp(t *testing.T) { 1142 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { 1143 fmt.Print(os.TempDir()) 1144 os.Exit(0) 1145 } 1146 1147 testenv.MustHaveExec(t) 1148 t.Parallel() 1149 1150 exe, err := os.Executable() 1151 if err != nil { 1152 t.Fatal(err) 1153 } 1154 1155 newtmp, err := findUnusedDriveLetter() 1156 if err != nil { 1157 t.Skip(err) 1158 } 1159 1160 cmd := testenv.Command(t, exe, "-test.run=TestRootDirAsTemp") 1161 cmd.Env = cmd.Environ() 1162 cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1") 1163 cmd.Env = append(cmd.Env, "TMP="+newtmp) 1164 cmd.Env = append(cmd.Env, "TEMP="+newtmp) 1165 output, err := cmd.CombinedOutput() 1166 if err != nil { 1167 t.Fatalf("Failed to spawn child process: %v %q", err, string(output)) 1168 } 1169 if want, have := newtmp, string(output); have != want { 1170 t.Fatalf("unexpected child process output %q, want %q", have, want) 1171 } 1172 } 1173 1174 func testReadlink(t *testing.T, path, want string) { 1175 got, err := os.Readlink(path) 1176 if err != nil { 1177 t.Error(err) 1178 return 1179 } 1180 if got != want { 1181 t.Errorf(`Readlink(%q): got %q, want %q`, path, got, want) 1182 } 1183 } 1184 1185 func mklink(t *testing.T, link, target string) { 1186 output, err := testenv.Command(t, "cmd", "/c", "mklink", link, target).CombinedOutput() 1187 if err != nil { 1188 t.Fatalf("failed to run mklink %v %v: %v %q", link, target, err, output) 1189 } 1190 } 1191 1192 func mklinkj(t *testing.T, link, target string) { 1193 output, err := testenv.Command(t, "cmd", "/c", "mklink", "/J", link, target).CombinedOutput() 1194 if err != nil { 1195 t.Fatalf("failed to run mklink %v %v: %v %q", link, target, err, output) 1196 } 1197 } 1198 1199 func mklinkd(t *testing.T, link, target string) { 1200 output, err := testenv.Command(t, "cmd", "/c", "mklink", "/D", link, target).CombinedOutput() 1201 if err != nil { 1202 t.Fatalf("failed to run mklink %v %v: %v %q", link, target, err, output) 1203 } 1204 } 1205 1206 func TestWindowsReadlink(t *testing.T) { 1207 tmpdir, err := os.MkdirTemp("", "TestWindowsReadlink") 1208 if err != nil { 1209 t.Fatal(err) 1210 } 1211 defer os.RemoveAll(tmpdir) 1212 1213 // Make sure tmpdir is not a symlink, otherwise tests will fail. 1214 tmpdir, err = filepath.EvalSymlinks(tmpdir) 1215 if err != nil { 1216 t.Fatal(err) 1217 } 1218 chdir(t, tmpdir) 1219 1220 vol := filepath.VolumeName(tmpdir) 1221 output, err := testenv.Command(t, "cmd", "/c", "mountvol", vol, "/L").CombinedOutput() 1222 if err != nil { 1223 t.Fatalf("failed to run mountvol %v /L: %v %q", vol, err, output) 1224 } 1225 ntvol := strings.Trim(string(output), " \n\r") 1226 1227 dir := filepath.Join(tmpdir, "dir") 1228 err = os.MkdirAll(dir, 0777) 1229 if err != nil { 1230 t.Fatal(err) 1231 } 1232 1233 absdirjlink := filepath.Join(tmpdir, "absdirjlink") 1234 mklinkj(t, absdirjlink, dir) 1235 testReadlink(t, absdirjlink, dir) 1236 1237 ntdirjlink := filepath.Join(tmpdir, "ntdirjlink") 1238 mklinkj(t, ntdirjlink, ntvol+absdirjlink[len(filepath.VolumeName(absdirjlink)):]) 1239 testReadlink(t, ntdirjlink, absdirjlink) 1240 1241 ntdirjlinktolink := filepath.Join(tmpdir, "ntdirjlinktolink") 1242 mklinkj(t, ntdirjlinktolink, ntvol+absdirjlink[len(filepath.VolumeName(absdirjlink)):]) 1243 testReadlink(t, ntdirjlinktolink, absdirjlink) 1244 1245 mklinkj(t, "reldirjlink", "dir") 1246 testReadlink(t, "reldirjlink", dir) // relative directory junction resolves to absolute path 1247 1248 // Make sure we have sufficient privilege to run mklink command. 1249 testenv.MustHaveSymlink(t) 1250 1251 absdirlink := filepath.Join(tmpdir, "absdirlink") 1252 mklinkd(t, absdirlink, dir) 1253 testReadlink(t, absdirlink, dir) 1254 1255 ntdirlink := filepath.Join(tmpdir, "ntdirlink") 1256 mklinkd(t, ntdirlink, ntvol+absdirlink[len(filepath.VolumeName(absdirlink)):]) 1257 testReadlink(t, ntdirlink, absdirlink) 1258 1259 mklinkd(t, "reldirlink", "dir") 1260 testReadlink(t, "reldirlink", "dir") 1261 1262 file := filepath.Join(tmpdir, "file") 1263 err = os.WriteFile(file, []byte(""), 0666) 1264 if err != nil { 1265 t.Fatal(err) 1266 } 1267 1268 filelink := filepath.Join(tmpdir, "filelink") 1269 mklink(t, filelink, file) 1270 testReadlink(t, filelink, file) 1271 1272 linktofilelink := filepath.Join(tmpdir, "linktofilelink") 1273 mklink(t, linktofilelink, ntvol+filelink[len(filepath.VolumeName(filelink)):]) 1274 testReadlink(t, linktofilelink, filelink) 1275 1276 mklink(t, "relfilelink", "file") 1277 testReadlink(t, "relfilelink", "file") 1278 } 1279 1280 func TestOpenDirTOCTOU(t *testing.T) { 1281 t.Parallel() 1282 1283 // Check opened directories can't be renamed until the handle is closed. 1284 // See issue 52747. 1285 tmpdir := t.TempDir() 1286 dir := filepath.Join(tmpdir, "dir") 1287 if err := os.Mkdir(dir, 0777); err != nil { 1288 t.Fatal(err) 1289 } 1290 f, err := os.Open(dir) 1291 if err != nil { 1292 t.Fatal(err) 1293 } 1294 newpath := filepath.Join(tmpdir, "dir1") 1295 err = os.Rename(dir, newpath) 1296 if err == nil || !errors.Is(err, windows.ERROR_SHARING_VIOLATION) { 1297 f.Close() 1298 t.Fatalf("Rename(%q, %q) = %v; want windows.ERROR_SHARING_VIOLATION", dir, newpath, err) 1299 } 1300 f.Close() 1301 err = os.Rename(dir, newpath) 1302 if err != nil { 1303 t.Error(err) 1304 } 1305 } 1306 1307 func TestAppExecLinkStat(t *testing.T) { 1308 // We expect executables installed to %LOCALAPPDATA%\Microsoft\WindowsApps to 1309 // be reparse points with tag IO_REPARSE_TAG_APPEXECLINK. Here we check that 1310 // such reparse points are treated as irregular (but executable) files, not 1311 // broken symlinks. 1312 appdata := os.Getenv("LOCALAPPDATA") 1313 if appdata == "" { 1314 t.Skipf("skipping: LOCALAPPDATA not set") 1315 } 1316 1317 pythonExeName := "python3.exe" 1318 pythonPath := filepath.Join(appdata, `Microsoft\WindowsApps`, pythonExeName) 1319 1320 lfi, err := os.Lstat(pythonPath) 1321 if err != nil { 1322 t.Skip("skipping test, because Python 3 is not installed via the Windows App Store on this system; see https://golang.org/issue/42919") 1323 } 1324 1325 // An APPEXECLINK reparse point is not a symlink, so os.Readlink should return 1326 // a non-nil error for it, and Stat should return results identical to Lstat. 1327 linkName, err := os.Readlink(pythonPath) 1328 if err == nil { 1329 t.Errorf("os.Readlink(%q) = %q, but expected an error\n(should be an APPEXECLINK reparse point, not a symlink)", pythonPath, linkName) 1330 } 1331 1332 sfi, err := os.Stat(pythonPath) 1333 if err != nil { 1334 t.Fatalf("Stat %s: %v", pythonPath, err) 1335 } 1336 1337 if lfi.Name() != sfi.Name() { 1338 t.Logf("os.Lstat(%q) = %+v", pythonPath, lfi) 1339 t.Logf("os.Stat(%q) = %+v", pythonPath, sfi) 1340 t.Errorf("files should be same") 1341 } 1342 1343 if lfi.Name() != pythonExeName { 1344 t.Errorf("Stat %s: got %q, but wanted %q", pythonPath, lfi.Name(), pythonExeName) 1345 } 1346 if m := lfi.Mode(); m&fs.ModeSymlink != 0 { 1347 t.Errorf("%q should be a file, not a link (mode=0x%x)", pythonPath, uint32(m)) 1348 } 1349 if m := lfi.Mode(); m&fs.ModeDir != 0 { 1350 t.Errorf("%q should be a file, not a directory (mode=0x%x)", pythonPath, uint32(m)) 1351 } 1352 if m := lfi.Mode(); m&fs.ModeIrregular == 0 { 1353 // A reparse point is not a regular file, but we don't have a more appropriate 1354 // ModeType bit for it, so it should be marked as irregular. 1355 t.Errorf("%q should not be a regular file (mode=0x%x)", pythonPath, uint32(m)) 1356 } 1357 1358 if sfi.Name() != pythonExeName { 1359 t.Errorf("Stat %s: got %q, but wanted %q", pythonPath, sfi.Name(), pythonExeName) 1360 } 1361 if m := sfi.Mode(); m&fs.ModeSymlink != 0 { 1362 t.Errorf("%q should be a file, not a link (mode=0x%x)", pythonPath, uint32(m)) 1363 } 1364 if m := sfi.Mode(); m&fs.ModeDir != 0 { 1365 t.Errorf("%q should be a file, not a directory (mode=0x%x)", pythonPath, uint32(m)) 1366 } 1367 if m := sfi.Mode(); m&fs.ModeIrregular == 0 { 1368 // A reparse point is not a regular file, but we don't have a more appropriate 1369 // ModeType bit for it, so it should be marked as irregular. 1370 t.Errorf("%q should not be a regular file (mode=0x%x)", pythonPath, uint32(m)) 1371 } 1372 1373 p, err := exec.LookPath(pythonPath) 1374 if err != nil { 1375 t.Errorf("exec.LookPath(%q): %v", pythonPath, err) 1376 } 1377 if p != pythonPath { 1378 t.Errorf("exec.LookPath(%q) = %q; want %q", pythonPath, p, pythonPath) 1379 } 1380 } 1381 1382 func TestIllformedUTF16FileName(t *testing.T) { 1383 dir := t.TempDir() 1384 const sep = string(os.PathSeparator) 1385 if !strings.HasSuffix(dir, sep) { 1386 dir += sep 1387 } 1388 1389 // This UTF-16 file name is ill-formed as it contains low surrogates that are not preceded by high surrogates ([1:5]). 1390 namew := []uint16{0x2e, 0xdc6d, 0xdc73, 0xdc79, 0xdc73, 0x30, 0x30, 0x30, 0x31, 0} 1391 1392 // Create a file whose name contains unpaired surrogates. 1393 // Use syscall.CreateFile instead of os.Create to simulate a file that is created by 1394 // a non-Go program so the file name hasn't gone through syscall.UTF16FromString. 1395 dirw := utf16.Encode([]rune(dir)) 1396 pathw := append(dirw, namew...) 1397 fd, err := syscall.CreateFile(&pathw[0], syscall.GENERIC_ALL, 0, nil, syscall.CREATE_NEW, 0, 0) 1398 if err != nil { 1399 t.Fatal(err) 1400 } 1401 syscall.CloseHandle(fd) 1402 1403 name := syscall.UTF16ToString(namew) 1404 path := filepath.Join(dir, name) 1405 // Verify that os.Lstat can query the file. 1406 fi, err := os.Lstat(path) 1407 if err != nil { 1408 t.Fatal(err) 1409 } 1410 if got := fi.Name(); got != name { 1411 t.Errorf("got %q, want %q", got, name) 1412 } 1413 // Verify that File.Readdirnames lists the file. 1414 f, err := os.Open(dir) 1415 if err != nil { 1416 t.Fatal(err) 1417 } 1418 files, err := f.Readdirnames(0) 1419 f.Close() 1420 if err != nil { 1421 t.Fatal(err) 1422 } 1423 if !slices.Contains(files, name) { 1424 t.Error("file not listed") 1425 } 1426 // Verify that os.RemoveAll can remove the directory 1427 // and that it doesn't hang. 1428 err = os.RemoveAll(dir) 1429 if err != nil { 1430 t.Error(err) 1431 } 1432 } 1433 1434 func TestUTF16Alloc(t *testing.T) { 1435 allowsPerRun := func(want int, f func()) { 1436 t.Helper() 1437 got := int(testing.AllocsPerRun(5, f)) 1438 if got != want { 1439 t.Errorf("got %d allocs, want %d", got, want) 1440 } 1441 } 1442 allowsPerRun(1, func() { 1443 syscall.UTF16ToString([]uint16{'a', 'b', 'c'}) 1444 }) 1445 allowsPerRun(1, func() { 1446 syscall.UTF16FromString("abc") 1447 }) 1448 }