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.
11 "internal/syscall/windows"
12 "internal/syscall/windows/registry"
30 // For TestRawConnReadWrite.
31 type syscallDescriptor = syscall.Handle
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()
38 t.Fatalf("chdir: %v", err)
40 if err := os.Chdir(dir); err != nil {
41 t.Fatalf("chdir %s: %v", dir, err)
45 if err := os.Chdir(olddir); err != nil {
46 t.Errorf("chdir to original working directory %s: %v", olddir, err)
52 func TestSameWindowsFile(t *testing.T) {
56 f, err := os.Create("a")
62 ia1, err := os.Stat("a")
67 path, err := filepath.Abs("a")
71 ia2, err := os.Stat(path)
75 if !os.SameFile(ia1, ia2) {
76 t.Errorf("files should be same")
79 p := filepath.VolumeName(path) + filepath.Base(path)
83 ia3, err := os.Stat(p)
87 if !os.SameFile(ia1, ia3) {
88 t.Errorf("files should be same")
92 type dirLinkTest struct {
94 mklink func(link, target string) error
95 issueNo int // correspondent issue number (for broken tests)
98 func testDirLinks(t *testing.T, tests []dirLinkTest) {
102 dir := filepath.Join(tmpdir, "dir")
103 err := os.Mkdir(dir, 0777)
107 fi, err := os.Stat(dir)
111 err = os.WriteFile(filepath.Join(dir, "abc"), []byte("abc"), 0644)
115 for _, test := range tests {
116 link := filepath.Join(tmpdir, test.name+"_link")
117 err := test.mklink(link, dir)
119 t.Errorf("creating link for %q test failed: %v", test.name, err)
123 data, err := os.ReadFile(filepath.Join(link, "abc"))
125 t.Errorf("failed to read abc file: %v", err)
128 if string(data) != "abc" {
129 t.Errorf(`abc file is expected to have "abc" in it, but has %v`, data)
133 if test.issueNo > 0 {
134 t.Logf("skipping broken %q test: see issue %d", test.name, test.issueNo)
138 fi1, err := os.Stat(link)
140 t.Errorf("failed to stat link %v: %v", link, err)
144 t.Errorf("%q should be a directory", link)
147 if fi1.Name() != filepath.Base(link) {
148 t.Errorf("Stat(%q).Name() = %q, want %q", link, fi1.Name(), filepath.Base(link))
151 if !os.SameFile(fi, fi1) {
152 t.Errorf("%q should point to %q", link, dir)
156 fi2, err := os.Lstat(link)
158 t.Errorf("failed to lstat link %v: %v", link, err)
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))
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))
172 // reparseData is used to build reparse buffer data required for tests.
173 type reparseData struct {
174 substituteName namePosition
175 printName namePosition
179 type namePosition struct {
184 func (rd *reparseData) addUTF16s(s []uint16) (offset uint16) {
185 off := len(rd.pathBuf) * 2
186 rd.pathBuf = append(rd.pathBuf, s...)
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)
195 func (rd *reparseData) addSubstituteName(name string) {
196 rd.substituteName.offset, rd.substituteName.length = rd.addString(name)
199 func (rd *reparseData) addPrintName(name string) {
200 rd.printName.offset, rd.printName.length = rd.addString(name)
203 func (rd *reparseData) addStringNoNUL(s string) (offset, length uint16) {
204 p := syscall.StringToUTF16(s)
206 return rd.addUTF16s(p), uint16(len(p)) * 2
209 func (rd *reparseData) addSubstituteNameNoNUL(name string) {
210 rd.substituteName.offset, rd.substituteName.length = rd.addStringNoNUL(name)
213 func (rd *reparseData) addPrintNameNoNUL(name string) {
214 rd.printName.offset, rd.printName.length = rd.addStringNoNUL(name)
217 // pathBuffeLen returns length of rd pathBuf in bytes.
218 func (rd *reparseData) pathBuffeLen() uint16 {
219 return uint16(len(rd.pathBuf)) * 2
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
231 func createDirLink(link string, rdb *_REPARSE_DATA_BUFFER) error {
232 err := os.Mkdir(link, 0777)
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)
243 defer syscall.CloseHandle(fd)
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)
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)
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)
268 return createDirLink(link, &rdb)
271 func TestDirectoryJunction(t *testing.T) {
272 var tests = []dirLinkTest{
274 // Create link similar to what mklink does, by inserting \??\ at the front of absolute target.
276 mklink: func(link, target string) error {
278 t.addSubstituteName(`\??\` + target)
279 t.addPrintName(target)
280 return createMountPoint(link, &t)
284 // Do as junction utility https://learn.microsoft.com/en-us/sysinternals/downloads/junction does - set PrintNameLength to 0.
285 name: "have_blank_print_name",
286 mklink: func(link, target string) error {
288 t.addSubstituteName(`\??\` + target)
290 return createMountPoint(link, &t)
294 output, _ := testenv.Command(t, "cmd", "/c", "mklink", "/?").Output()
295 mklinkSupportsJunctionLinks := strings.Contains(string(output), " /J ")
296 if mklinkSupportsJunctionLinks {
297 tests = append(tests,
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()
303 t.Errorf("failed to run mklink %v %v: %v %q", link, target, err, output)
310 t.Log(`skipping "use_mklink_cmd" test, mklink does not supports directory junctions`)
312 testDirLinks(t, tests)
315 func enableCurrentThreadPrivilege(privilegeName string) error {
316 ct, err := windows.GetCurrentThread()
321 err = windows.OpenThreadToken(ct, syscall.TOKEN_QUERY|windows.TOKEN_ADJUST_PRIVILEGES, false, &t)
325 defer syscall.CloseHandle(syscall.Handle(t))
327 var tp windows.TOKEN_PRIVILEGES
329 privStr, err := syscall.UTF16PtrFromString(privilegeName)
333 err = windows.LookupPrivilegeValue(nil, privStr, &tp.Privileges[0].Luid)
337 tp.PrivilegeCount = 1
338 tp.Privileges[0].Attributes = windows.SE_PRIVILEGE_ENABLED
339 return windows.AdjustTokenPrivileges(t, false, &tp, 0, nil, nil)
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
352 buf.Flags = windows.SYMLINK_FLAG_RELATIVE
354 pbuflen := len(target.pathBuf)
355 copy((*[2048]uint16)(unsafe.Pointer(&buf.PathBuffer[0]))[:pbuflen:pbuflen], target.pathBuf)
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)
362 return createDirLink(link, &rdb)
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,
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()
376 t.Errorf("failed to run mklink %v %v: %v %q", link, target, err, output)
383 t.Log(`skipping "use_mklink_cmd" test, mklink does not supports directory symbolic links`)
386 // The rest of these test requires SeCreateSymbolicLinkPrivilege to be held.
387 runtime.LockOSThread()
388 defer runtime.UnlockOSThread()
390 err := windows.ImpersonateSelf(windows.SecurityImpersonation)
394 defer windows.RevertToSelf()
396 err = enableCurrentThreadPrivilege("SeCreateSymbolicLinkPrivilege")
398 t.Skipf(`skipping some tests, could not enable "SeCreateSymbolicLinkPrivilege": %v`, err)
400 tests = append(tests,
403 mklink: func(link, target string) error {
404 return os.Symlink(target, link)
408 // Create link similar to what mklink does, by inserting \??\ at the front of absolute target.
410 mklink: func(link, target string) error {
412 t.addPrintName(target)
413 t.addSubstituteName(`\??\` + target)
414 return createSymbolicLink(link, &t, false)
419 mklink: func(link, target string) error {
421 t.addSubstituteNameNoNUL(filepath.Base(target))
422 t.addPrintNameNoNUL(filepath.Base(target))
423 return createSymbolicLink(link, &t, true)
427 testDirLinks(t, tests)
430 func mustHaveWorkstation(t *testing.T) {
431 mar, err := windows.OpenSCManager(nil, nil, windows.SERVICE_QUERY_STATUS)
435 defer syscall.CloseHandle(mar)
436 //LanmanWorkstation is the service name, and Workstation is the display name.
437 srv, err := windows.OpenService(mar, syscall.StringToUTF16Ptr("LanmanWorkstation"), windows.SERVICE_QUERY_STATUS)
441 defer syscall.CloseHandle(srv)
442 var state windows.SERVICE_STATUS
443 err = windows.QueryServiceStatus(srv, &state)
447 if state.CurrentState != windows.SERVICE_RUNNING {
448 t.Skip("Requires the Windows service Workstation, but it is detected that it is not enabled.")
452 func TestNetworkSymbolicLink(t *testing.T) {
453 testenv.MustHaveSymlink(t)
455 const _NERR_ServerNotStarted = syscall.Errno(2114)
461 shareName := fmt.Sprintf("GoSymbolicLinkTestShare%d", pid)
462 sharePath := filepath.Join(dir, shareName)
465 err := os.MkdirAll(filepath.Join(sharePath, testDir), 0777)
470 wShareName, err := syscall.UTF16PtrFromString(shareName)
474 wSharePath, err := syscall.UTF16PtrFromString(sharePath)
479 // Per https://learn.microsoft.com/en-us/windows/win32/api/lmshare/ns-lmshare-share_info_2:
481 // “[The shi2_permissions field] indicates the shared resource's permissions
482 // for servers running with share-level security. A server running user-level
483 // security ignores this member.
485 // Note that Windows does not support share-level security.”
487 // So it shouldn't matter what permissions we set here.
488 const permissions = 0
490 p := windows.SHARE_INFO_2{
492 Type: windows.STYPE_DISKTREE | windows.STYPE_TEMPORARY,
494 Permissions: permissions,
501 err = windows.NetShareAdd(nil, 2, (*byte)(unsafe.Pointer(&p)), nil)
503 if err == syscall.ERROR_ACCESS_DENIED || err == _NERR_ServerNotStarted {
504 t.Skipf("skipping: NetShareAdd: %v", err)
509 err := windows.NetShareDel(nil, wShareName, 0)
515 UNCPath := `\\localhost\` + shareName + `\`
517 fi1, err := os.Stat(sharePath)
521 fi2, err := os.Stat(UNCPath)
523 mustHaveWorkstation(t)
526 if !os.SameFile(fi1, fi2) {
527 t.Fatalf("%q and %q should be the same directory, but not", sharePath, UNCPath)
530 target := filepath.Join(UNCPath, testDir)
533 err = os.Symlink(target, link)
537 defer os.Remove(link)
539 got, err := os.Readlink(link)
544 t.Errorf(`os.Readlink(%#q): got %v, want %v`, link, got, target)
547 got, err = filepath.EvalSymlinks(link)
552 t.Errorf(`filepath.EvalSymlinks(%#q): got %v, want %v`, link, got, target)
556 func TestStatLxSymLink(t *testing.T) {
557 if _, err := exec.LookPath("wsl"); err != nil {
558 t.Skip("skipping: WSL not detected")
564 const target = "target"
567 _, err := testenv.Command(t, "wsl", "/bin/mkdir", target).Output()
569 // This normally happens when WSL still doesn't have a distro installed to run on.
570 t.Skipf("skipping: WSL is not correctly installed: %v", err)
573 _, err = testenv.Command(t, "wsl", "/bin/ln", "-s", target, link).Output()
578 fi, err := os.Lstat(link)
582 if m := fi.Mode(); m&fs.ModeSymlink != 0 {
583 // This can happen depending on newer WSL versions when running as admin or in developer mode.
584 t.Skip("skipping: WSL created reparse tag IO_REPARSE_TAG_SYMLINK instead of a IO_REPARSE_TAG_LX_SYMLINK")
586 // Stat'ing a IO_REPARSE_TAG_LX_SYMLINK from outside WSL always return ERROR_CANT_ACCESS_FILE.
587 // We check this condition to validate that os.Stat has tried to follow the link.
588 _, err = os.Stat(link)
589 const ERROR_CANT_ACCESS_FILE = syscall.Errno(1920)
590 if err == nil || !errors.Is(err, ERROR_CANT_ACCESS_FILE) {
591 t.Fatalf("os.Stat(%q): got %v, want ERROR_CANT_ACCESS_FILE", link, err)
595 func TestStartProcessAttr(t *testing.T) {
598 p, err := os.StartProcess(os.Getenv("COMSPEC"), []string{"/c", "cd"}, new(os.ProcAttr))
603 t.Fatalf("StartProcess expected to fail, but succeeded.")
606 func TestShareNotExistError(t *testing.T) {
608 t.Skip("slow test that uses network; skipping")
612 _, err := os.Stat(`\\no_such_server\no_such_share\no_such_file`)
614 t.Fatal("stat succeeded, but expected to fail")
616 if !os.IsNotExist(err) {
617 t.Fatalf("os.Stat failed with %q, but os.IsNotExist(err) is false", err)
621 func TestBadNetPathError(t *testing.T) {
622 const ERROR_BAD_NETPATH = syscall.Errno(53)
623 if !os.IsNotExist(ERROR_BAD_NETPATH) {
624 t.Fatal("os.IsNotExist(syscall.Errno(53)) is false, but want true")
628 func TestStatDir(t *testing.T) {
631 f, err := os.Open(".")
652 if !os.SameFile(fi, fi2) {
653 t.Fatal("race condition occurred")
657 func TestOpenVolumeName(t *testing.T) {
658 tmpdir := t.TempDir()
661 want := []string{"file1", "file2", "file3", "gopher.txt"}
663 for _, name := range want {
664 err := os.WriteFile(filepath.Join(tmpdir, name), nil, 0777)
670 f, err := os.Open(filepath.VolumeName(tmpdir))
676 have, err := f.Readdirnames(-1)
682 if strings.Join(want, "/") != strings.Join(have, "/") {
683 t.Fatalf("unexpected file list %q, want %q", have, want)
687 func TestDeleteReadOnly(t *testing.T) {
690 tmpdir := t.TempDir()
691 p := filepath.Join(tmpdir, "a")
692 // This sets FILE_ATTRIBUTE_READONLY.
693 f, err := os.OpenFile(p, os.O_CREATE, 0400)
699 if err = os.Chmod(p, 0400); err != nil {
702 if err = os.Remove(p); err != nil {
707 func TestReadStdin(t *testing.T) {
708 old := poll.ReadConsole
710 poll.ReadConsole = old
713 p, err := syscall.GetCurrentProcess()
715 t.Fatalf("Unable to get handle to current process: %v", err)
717 var stdinDuplicate syscall.Handle
718 err = syscall.DuplicateHandle(p, syscall.Handle(syscall.Stdin), p, &stdinDuplicate, 0, false, syscall.DUPLICATE_SAME_ACCESS)
720 t.Fatalf("Unable to duplicate stdin: %v", err)
722 testConsole := os.NewConsoleFile(stdinDuplicate, "test")
724 var tests = []string{
730 "\U0001F648\U0001F649\U0001F64A",
733 for _, consoleSize := range []int{1, 2, 3, 10, 16, 100, 1000} {
734 for _, readSize := range []int{1, 2, 3, 4, 5, 8, 10, 16, 20, 50, 100} {
735 for _, s := range tests {
736 t.Run(fmt.Sprintf("c%d/r%d/%s", consoleSize, readSize, s), func(t *testing.T) {
737 s16 := utf16.Encode([]rune(s))
738 poll.ReadConsole = func(h syscall.Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) error {
739 if inputControl != nil {
740 t.Fatalf("inputControl not nil")
746 n = copy((*[10000]uint16)(unsafe.Pointer(buf))[:n:n], s16)
749 t.Logf("read %d -> %d", toread, *read)
755 chunk := make([]byte, readSize)
757 n, err := testConsole.Read(chunk)
758 buf = append(buf, chunk[:n]...)
760 all = append(all, string(buf))
765 } else if err != nil {
766 t.Fatalf("reading %q: error: %v", s, err)
768 if len(buf) >= 2000 {
769 t.Fatalf("reading %q: stuck in loop: %q", s, buf)
773 want := strings.Split(s, "\x1a")
775 want = append(want, "")
777 if !reflect.DeepEqual(all, want) {
778 t.Errorf("reading %q:\nhave %x\nwant %x", s, all, want)
786 func TestStatPagefile(t *testing.T) {
789 const path = `c:\pagefile.sys`
790 fi, err := os.Stat(path)
793 t.Fatalf("Stat(%q).Name() is empty", path)
795 t.Logf("Stat(%q).Size() = %v", path, fi.Size())
798 if os.IsNotExist(err) {
799 t.Skip(`skipping because c:\pagefile.sys is not found`)
804 // syscallCommandLineToArgv calls syscall.CommandLineToArgv
805 // and converts returned result into []string.
806 func syscallCommandLineToArgv(cmd string) ([]string, error) {
808 argv, err := syscall.CommandLineToArgv(&syscall.StringToUTF16(cmd)[0], &argc)
812 defer syscall.LocalFree(syscall.Handle(uintptr(unsafe.Pointer(argv))))
815 for _, v := range (*argv)[:argc] {
816 args = append(args, syscall.UTF16ToString((*v)[:]))
821 // compareCommandLineToArgvWithSyscall ensures that
822 // os.CommandLineToArgv(cmd) and syscall.CommandLineToArgv(cmd)
823 // return the same result.
824 func compareCommandLineToArgvWithSyscall(t *testing.T, cmd string) {
825 syscallArgs, err := syscallCommandLineToArgv(cmd)
829 args := os.CommandLineToArgv(cmd)
830 if want, have := fmt.Sprintf("%q", syscallArgs), fmt.Sprintf("%q", args); want != have {
831 t.Errorf("testing os.commandLineToArgv(%q) failed: have %q want %q", cmd, args, syscallArgs)
836 func TestCmdArgs(t *testing.T) {
838 t.Skipf("in short mode; skipping test that builds a binary")
842 tmpdir := t.TempDir()
853 fmt.Printf("%q", os.Args)
856 src := filepath.Join(tmpdir, "main.go")
857 if err := os.WriteFile(src, []byte(prog), 0666); err != nil {
861 exe := filepath.Join(tmpdir, "main.exe")
862 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, src)
864 out, err := cmd.CombinedOutput()
866 t.Fatalf("building main.exe failed: %v\n%s", err, out)
888 // examples from https://learn.microsoft.com/en-us/cpp/cpp/main-function-command-line-args
893 // http://daviddeley.com/autohotkey/parameters/parameters.htm#WINARGV
896 ` "Call Me Ishmael"`,
897 ` Cal"l Me I"shmael`,
899 ` "CallMe\"Ishmael"`,
900 ` "Call Me Ishmael\\"`,
901 ` "CallMe\\\"Ishmael"`,
904 // from 5.5 Some Common Tasks
905 ` "\"Call Me Ishmael\""`,
907 ` "\"C:\TEST A\\\""`,
908 // from 5.6 The Microsoft Examples Explained
914 // from 5.7 Double Double Quote Examples (pre 2008)
916 ` """CallMeIshmael""" b c`,
917 ` """Call Me Ishmael"""`,
918 ` """"Call Me Ishmael"" b c`,
920 for _, cmd := range cmds {
921 compareCommandLineToArgvWithSyscall(t, "test"+cmd)
922 compareCommandLineToArgvWithSyscall(t, `"cmd line"`+cmd)
923 compareCommandLineToArgvWithSyscall(t, exe+cmd)
925 // test both syscall.EscapeArg and os.commandLineToArgv
926 args := os.CommandLineToArgv(exe + cmd)
927 out, err := testenv.Command(t, args[0], args[1:]...).CombinedOutput()
929 t.Fatalf("running %q failed: %v\n%v", args, err, string(out))
931 if want, have := fmt.Sprintf("%q", args), string(out); want != have {
932 t.Errorf("wrong output of executing %q: have %q want %q", args, have, want)
938 func findOneDriveDir() (string, error) {
939 // as per https://stackoverflow.com/questions/42519624/how-to-determine-location-of-onedrive-on-windows-7-and-8-in-c
940 const onedrivekey = `SOFTWARE\Microsoft\OneDrive`
941 k, err := registry.OpenKey(registry.CURRENT_USER, onedrivekey, registry.READ)
943 return "", fmt.Errorf("OpenKey(%q) failed: %v", onedrivekey, err)
947 path, valtype, err := k.GetStringValue("UserFolder")
949 return "", fmt.Errorf("reading UserFolder failed: %v", err)
952 if valtype == registry.EXPAND_SZ {
953 expanded, err := registry.ExpandString(path)
955 return "", fmt.Errorf("expanding UserFolder failed: %v", err)
963 // TestOneDrive verifies that OneDrive folder is a directory and not a symlink.
964 func TestOneDrive(t *testing.T) {
967 dir, err := findOneDriveDir()
969 t.Skipf("Skipping, because we did not find OneDrive directory: %v", err)
974 func TestWindowsDevNullFile(t *testing.T) {
977 f1, err := os.Open("NUL")
983 fi1, err := f1.Stat()
988 f2, err := os.Open("nul")
994 fi2, err := f2.Stat()
999 if !os.SameFile(fi1, fi2) {
1000 t.Errorf(`"NUL" and "nul" are not the same file`)
1004 func TestFileStatNUL(t *testing.T) {
1007 f, err := os.Open("NUL")
1015 if got, want := fi.Mode(), os.ModeDevice|os.ModeCharDevice|0666; got != want {
1016 t.Errorf("Open(%q).Stat().Mode() = %v, want %v", "NUL", got, want)
1020 func TestStatNUL(t *testing.T) {
1023 fi, err := os.Stat("NUL")
1027 if got, want := fi.Mode(), os.ModeDevice|os.ModeCharDevice|0666; got != want {
1028 t.Errorf("Stat(%q).Mode() = %v, want %v", "NUL", got, want)
1032 // TestSymlinkCreation verifies that creating a symbolic link
1033 // works on Windows when developer mode is active.
1034 // This is supported starting Windows 10 (1703, v10.0.14972).
1035 func TestSymlinkCreation(t *testing.T) {
1036 if !testenv.HasSymlink() && !isWindowsDeveloperModeActive() {
1037 t.Skip("Windows developer mode is not active")
1042 dummyFile := filepath.Join(temp, "file")
1043 if err := os.WriteFile(dummyFile, []byte(""), 0644); err != nil {
1047 linkFile := filepath.Join(temp, "link")
1048 if err := os.Symlink(dummyFile, linkFile); err != nil {
1053 // isWindowsDeveloperModeActive checks whether or not the developer mode is active on Windows 10.
1054 // Returns false for prior Windows versions.
1055 // see https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development
1056 func isWindowsDeveloperModeActive() bool {
1057 key, err := registry.OpenKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", registry.READ)
1062 val, _, err := key.GetIntegerValue("AllowDevelopmentWithoutDevLicense")
1070 // TestRootRelativeDirSymlink verifies that symlinks to paths relative to the
1071 // drive root (beginning with "\" but no volume name) are created with the
1072 // correct symlink type.
1073 // (See https://golang.org/issue/39183#issuecomment-632175728.)
1074 func TestRootRelativeDirSymlink(t *testing.T) {
1075 testenv.MustHaveSymlink(t)
1079 dir := filepath.Join(temp, "dir")
1080 if err := os.Mkdir(dir, 0755); err != nil {
1084 volumeRelDir := strings.TrimPrefix(dir, filepath.VolumeName(dir)) // leaves leading backslash
1086 link := filepath.Join(temp, "link")
1087 err := os.Symlink(volumeRelDir, link)
1091 t.Logf("Symlink(%#q, %#q)", volumeRelDir, link)
1093 f, err := os.Open(link)
1098 if fi, err := f.Stat(); err != nil {
1100 } else if !fi.IsDir() {
1101 t.Errorf("Open(%#q).Stat().IsDir() = false; want true", f.Name())
1105 // TestWorkingDirectoryRelativeSymlink verifies that symlinks to paths relative
1106 // to the current working directory for the drive, such as "C:File.txt", are
1107 // correctly converted to absolute links of the correct symlink type (per
1108 // https://docs.microsoft.com/en-us/windows/win32/fileio/creating-symbolic-links).
1109 func TestWorkingDirectoryRelativeSymlink(t *testing.T) {
1110 testenv.MustHaveSymlink(t)
1112 // Construct a directory to be symlinked.
1114 if v := filepath.VolumeName(temp); len(v) < 2 || v[1] != ':' {
1115 t.Skipf("Can't test relative symlinks: t.TempDir() (%#q) does not begin with a drive letter.", temp)
1118 absDir := filepath.Join(temp, `dir\sub`)
1119 if err := os.MkdirAll(absDir, 0755); err != nil {
1123 // Change to the temporary directory and construct a
1124 // working-directory-relative symlink.
1125 oldwd, err := os.Getwd()
1130 if err := os.Chdir(oldwd); err != nil {
1134 if err := os.Chdir(temp); err != nil {
1137 t.Logf("Chdir(%#q)", temp)
1139 wdRelDir := filepath.VolumeName(temp) + `dir\sub` // no backslash after volume.
1140 absLink := filepath.Join(temp, "link")
1141 err = os.Symlink(wdRelDir, absLink)
1145 t.Logf("Symlink(%#q, %#q)", wdRelDir, absLink)
1147 // Now change back to the original working directory and verify that the
1148 // symlink still refers to its original path and is correctly marked as a
1150 if err := os.Chdir(oldwd); err != nil {
1153 t.Logf("Chdir(%#q)", oldwd)
1155 resolved, err := os.Readlink(absLink)
1157 t.Errorf("Readlink(%#q): %v", absLink, err)
1158 } else if resolved != absDir {
1159 t.Errorf("Readlink(%#q) = %#q; want %#q", absLink, resolved, absDir)
1162 linkFile, err := os.Open(absLink)
1166 defer linkFile.Close()
1168 linkInfo, err := linkFile.Stat()
1172 if !linkInfo.IsDir() {
1173 t.Errorf("Open(%#q).Stat().IsDir() = false; want true", absLink)
1176 absInfo, err := os.Stat(absDir)
1181 if !os.SameFile(absInfo, linkInfo) {
1182 t.Errorf("SameFile(Stat(%#q), Open(%#q).Stat()) = false; want true", absDir, absLink)
1186 // TestStatOfInvalidName is regression test for issue #24999.
1187 func TestStatOfInvalidName(t *testing.T) {
1190 _, err := os.Stat("*.go")
1192 t.Fatal(`os.Stat("*.go") unexpectedly succeeded`)
1196 // findUnusedDriveLetter searches mounted drive list on the system
1197 // (starting from Z: and ending at D:) for unused drive letter.
1198 // It returns path to the found drive root directory (like Z:\) or error.
1199 func findUnusedDriveLetter() (string, error) {
1200 // Do not use A: and B:, because they are reserved for floppy drive.
1201 // Do not use C:, because it is normally used for main drive.
1202 for l := 'Z'; l >= 'D'; l-- {
1203 p := string(l) + `:\`
1204 _, err := os.Stat(p)
1205 if os.IsNotExist(err) {
1209 return "", errors.New("Could not find unused drive letter.")
1212 func TestRootDirAsTemp(t *testing.T) {
1213 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
1214 fmt.Print(os.TempDir())
1218 testenv.MustHaveExec(t)
1221 exe, err := os.Executable()
1226 newtmp, err := findUnusedDriveLetter()
1231 cmd := testenv.Command(t, exe, "-test.run=^TestRootDirAsTemp$")
1232 cmd.Env = cmd.Environ()
1233 cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
1234 cmd.Env = append(cmd.Env, "TMP="+newtmp)
1235 cmd.Env = append(cmd.Env, "TEMP="+newtmp)
1236 output, err := cmd.CombinedOutput()
1238 t.Fatalf("Failed to spawn child process: %v %q", err, string(output))
1240 if want, have := newtmp, string(output); have != want {
1241 t.Fatalf("unexpected child process output %q, want %q", have, want)
1245 func testReadlink(t *testing.T, path, want string) {
1246 got, err := os.Readlink(path)
1252 t.Errorf(`Readlink(%q): got %q, want %q`, path, got, want)
1256 func mklink(t *testing.T, link, target string) {
1257 output, err := testenv.Command(t, "cmd", "/c", "mklink", link, target).CombinedOutput()
1259 t.Fatalf("failed to run mklink %v %v: %v %q", link, target, err, output)
1263 func mklinkj(t *testing.T, link, target string) {
1264 output, err := testenv.Command(t, "cmd", "/c", "mklink", "/J", link, target).CombinedOutput()
1266 t.Fatalf("failed to run mklink %v %v: %v %q", link, target, err, output)
1270 func mklinkd(t *testing.T, link, target string) {
1271 output, err := testenv.Command(t, "cmd", "/c", "mklink", "/D", link, target).CombinedOutput()
1273 t.Fatalf("failed to run mklink %v %v: %v %q", link, target, err, output)
1277 func TestWindowsReadlink(t *testing.T) {
1278 tmpdir, err := os.MkdirTemp("", "TestWindowsReadlink")
1282 defer os.RemoveAll(tmpdir)
1284 // Make sure tmpdir is not a symlink, otherwise tests will fail.
1285 tmpdir, err = filepath.EvalSymlinks(tmpdir)
1291 vol := filepath.VolumeName(tmpdir)
1292 output, err := testenv.Command(t, "cmd", "/c", "mountvol", vol, "/L").CombinedOutput()
1294 t.Fatalf("failed to run mountvol %v /L: %v %q", vol, err, output)
1296 ntvol := strings.Trim(string(output), " \n\r")
1298 dir := filepath.Join(tmpdir, "dir")
1299 err = os.MkdirAll(dir, 0777)
1304 absdirjlink := filepath.Join(tmpdir, "absdirjlink")
1305 mklinkj(t, absdirjlink, dir)
1306 testReadlink(t, absdirjlink, dir)
1308 ntdirjlink := filepath.Join(tmpdir, "ntdirjlink")
1309 mklinkj(t, ntdirjlink, ntvol+absdirjlink[len(filepath.VolumeName(absdirjlink)):])
1310 testReadlink(t, ntdirjlink, absdirjlink)
1312 ntdirjlinktolink := filepath.Join(tmpdir, "ntdirjlinktolink")
1313 mklinkj(t, ntdirjlinktolink, ntvol+absdirjlink[len(filepath.VolumeName(absdirjlink)):])
1314 testReadlink(t, ntdirjlinktolink, absdirjlink)
1316 mklinkj(t, "reldirjlink", "dir")
1317 testReadlink(t, "reldirjlink", dir) // relative directory junction resolves to absolute path
1319 // Make sure we have sufficient privilege to run mklink command.
1320 testenv.MustHaveSymlink(t)
1322 absdirlink := filepath.Join(tmpdir, "absdirlink")
1323 mklinkd(t, absdirlink, dir)
1324 testReadlink(t, absdirlink, dir)
1326 ntdirlink := filepath.Join(tmpdir, "ntdirlink")
1327 mklinkd(t, ntdirlink, ntvol+absdirlink[len(filepath.VolumeName(absdirlink)):])
1328 testReadlink(t, ntdirlink, absdirlink)
1330 mklinkd(t, "reldirlink", "dir")
1331 testReadlink(t, "reldirlink", "dir")
1333 file := filepath.Join(tmpdir, "file")
1334 err = os.WriteFile(file, []byte(""), 0666)
1339 filelink := filepath.Join(tmpdir, "filelink")
1340 mklink(t, filelink, file)
1341 testReadlink(t, filelink, file)
1343 linktofilelink := filepath.Join(tmpdir, "linktofilelink")
1344 mklink(t, linktofilelink, ntvol+filelink[len(filepath.VolumeName(filelink)):])
1345 testReadlink(t, linktofilelink, filelink)
1347 mklink(t, "relfilelink", "file")
1348 testReadlink(t, "relfilelink", "file")
1351 func TestOpenDirTOCTOU(t *testing.T) {
1354 // Check opened directories can't be renamed until the handle is closed.
1356 tmpdir := t.TempDir()
1357 dir := filepath.Join(tmpdir, "dir")
1358 if err := os.Mkdir(dir, 0777); err != nil {
1361 f, err := os.Open(dir)
1365 newpath := filepath.Join(tmpdir, "dir1")
1366 err = os.Rename(dir, newpath)
1367 if err == nil || !errors.Is(err, windows.ERROR_SHARING_VIOLATION) {
1369 t.Fatalf("Rename(%q, %q) = %v; want windows.ERROR_SHARING_VIOLATION", dir, newpath, err)
1372 err = os.Rename(dir, newpath)
1378 func TestAppExecLinkStat(t *testing.T) {
1379 // We expect executables installed to %LOCALAPPDATA%\Microsoft\WindowsApps to
1380 // be reparse points with tag IO_REPARSE_TAG_APPEXECLINK. Here we check that
1381 // such reparse points are treated as irregular (but executable) files, not
1383 appdata := os.Getenv("LOCALAPPDATA")
1385 t.Skipf("skipping: LOCALAPPDATA not set")
1388 pythonExeName := "python3.exe"
1389 pythonPath := filepath.Join(appdata, `Microsoft\WindowsApps`, pythonExeName)
1391 lfi, err := os.Lstat(pythonPath)
1393 t.Skip("skipping test, because Python 3 is not installed via the Windows App Store on this system; see https://golang.org/issue/42919")
1396 // An APPEXECLINK reparse point is not a symlink, so os.Readlink should return
1397 // a non-nil error for it, and Stat should return results identical to Lstat.
1398 linkName, err := os.Readlink(pythonPath)
1400 t.Errorf("os.Readlink(%q) = %q, but expected an error\n(should be an APPEXECLINK reparse point, not a symlink)", pythonPath, linkName)
1403 sfi, err := os.Stat(pythonPath)
1405 t.Fatalf("Stat %s: %v", pythonPath, err)
1408 if lfi.Name() != sfi.Name() {
1409 t.Logf("os.Lstat(%q) = %+v", pythonPath, lfi)
1410 t.Logf("os.Stat(%q) = %+v", pythonPath, sfi)
1411 t.Errorf("files should be same")
1414 if lfi.Name() != pythonExeName {
1415 t.Errorf("Stat %s: got %q, but wanted %q", pythonPath, lfi.Name(), pythonExeName)
1417 if m := lfi.Mode(); m&fs.ModeSymlink != 0 {
1418 t.Errorf("%q should be a file, not a link (mode=0x%x)", pythonPath, uint32(m))
1420 if m := lfi.Mode(); m&fs.ModeDir != 0 {
1421 t.Errorf("%q should be a file, not a directory (mode=0x%x)", pythonPath, uint32(m))
1423 if m := lfi.Mode(); m&fs.ModeIrregular == 0 {
1424 // A reparse point is not a regular file, but we don't have a more appropriate
1425 // ModeType bit for it, so it should be marked as irregular.
1426 t.Errorf("%q should not be a regular file (mode=0x%x)", pythonPath, uint32(m))
1429 if sfi.Name() != pythonExeName {
1430 t.Errorf("Stat %s: got %q, but wanted %q", pythonPath, sfi.Name(), pythonExeName)
1432 if m := sfi.Mode(); m&fs.ModeSymlink != 0 {
1433 t.Errorf("%q should be a file, not a link (mode=0x%x)", pythonPath, uint32(m))
1435 if m := sfi.Mode(); m&fs.ModeDir != 0 {
1436 t.Errorf("%q should be a file, not a directory (mode=0x%x)", pythonPath, uint32(m))
1438 if m := sfi.Mode(); m&fs.ModeIrregular == 0 {
1439 // A reparse point is not a regular file, but we don't have a more appropriate
1440 // ModeType bit for it, so it should be marked as irregular.
1441 t.Errorf("%q should not be a regular file (mode=0x%x)", pythonPath, uint32(m))
1444 p, err := exec.LookPath(pythonPath)
1446 t.Errorf("exec.LookPath(%q): %v", pythonPath, err)
1448 if p != pythonPath {
1449 t.Errorf("exec.LookPath(%q) = %q; want %q", pythonPath, p, pythonPath)
1453 func TestIllformedUTF16FileName(t *testing.T) {
1455 const sep = string(os.PathSeparator)
1456 if !strings.HasSuffix(dir, sep) {
1460 // This UTF-16 file name is ill-formed as it contains low surrogates that are not preceded by high surrogates ([1:5]).
1461 namew := []uint16{0x2e, 0xdc6d, 0xdc73, 0xdc79, 0xdc73, 0x30, 0x30, 0x30, 0x31, 0}
1463 // Create a file whose name contains unpaired surrogates.
1464 // Use syscall.CreateFile instead of os.Create to simulate a file that is created by
1465 // a non-Go program so the file name hasn't gone through syscall.UTF16FromString.
1466 dirw := utf16.Encode([]rune(dir))
1467 pathw := append(dirw, namew...)
1468 fd, err := syscall.CreateFile(&pathw[0], syscall.GENERIC_ALL, 0, nil, syscall.CREATE_NEW, 0, 0)
1472 syscall.CloseHandle(fd)
1474 name := syscall.UTF16ToString(namew)
1475 path := filepath.Join(dir, name)
1476 // Verify that os.Lstat can query the file.
1477 fi, err := os.Lstat(path)
1481 if got := fi.Name(); got != name {
1482 t.Errorf("got %q, want %q", got, name)
1484 // Verify that File.Readdirnames lists the file.
1485 f, err := os.Open(dir)
1489 files, err := f.Readdirnames(0)
1494 if !slices.Contains(files, name) {
1495 t.Error("file not listed")
1497 // Verify that os.RemoveAll can remove the directory
1498 // and that it doesn't hang.
1499 err = os.RemoveAll(dir)
1505 func TestUTF16Alloc(t *testing.T) {
1506 allowsPerRun := func(want int, f func()) {
1508 got := int(testing.AllocsPerRun(5, f))
1510 t.Errorf("got %d allocs, want %d", got, want)
1513 allowsPerRun(1, func() {
1514 syscall.UTF16ToString([]uint16{'a', 'b', 'c'})
1516 allowsPerRun(1, func() {
1517 syscall.UTF16FromString("abc")
1521 func TestNewFileInvalid(t *testing.T) {
1523 if f := os.NewFile(uintptr(syscall.InvalidHandle), "invalid"); f != nil {
1524 t.Errorf("NewFile(InvalidHandle) got %v want nil", f)
1528 func TestReadDirPipe(t *testing.T) {
1530 fi, err := os.Stat(dir)
1531 if err != nil || !fi.IsDir() {
1532 t.Skipf("%s is not a directory", dir)
1534 _, err = os.ReadDir(dir)
1536 t.Errorf("ReadDir(%q) = %v", dir, err)
1540 func TestReadDirNoFileID(t *testing.T) {
1541 *os.AllowReadDirFileID = false
1542 defer func() { *os.AllowReadDirFileID = true }()
1545 pathA := filepath.Join(dir, "a")
1546 pathB := filepath.Join(dir, "b")
1547 if err := os.WriteFile(pathA, nil, 0666); err != nil {
1550 if err := os.WriteFile(pathB, nil, 0666); err != nil {
1554 files, err := os.ReadDir(dir)
1558 if len(files) != 2 {
1559 t.Fatalf("ReadDir(%q) = %v; want 2 files", dir, files)
1562 // Check that os.SameFile works with files returned by os.ReadDir.
1563 f1, err := files[0].Info()
1567 f2, err := files[1].Info()
1571 if !os.SameFile(f1, f1) {
1572 t.Errorf("SameFile(%v, %v) = false; want true", f1, f1)
1574 if !os.SameFile(f2, f2) {
1575 t.Errorf("SameFile(%v, %v) = false; want true", f2, f2)
1577 if os.SameFile(f1, f2) {
1578 t.Errorf("SameFile(%v, %v) = true; want false", f1, f2)
1581 // Check that os.SameFile works with a mix of os.ReadDir and os.Stat files.
1582 f1s, err := os.Stat(pathA)
1586 f2s, err := os.Stat(pathB)
1590 if !os.SameFile(f1, f1s) {
1591 t.Errorf("SameFile(%v, %v) = false; want true", f1, f1s)
1593 if !os.SameFile(f2, f2s) {
1594 t.Errorf("SameFile(%v, %v) = false; want true", f2, f2s)