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"
29 // For TestRawConnReadWrite.
30 type syscallDescriptor
= syscall
.Handle
32 // chdir changes the current working directory to the named directory,
33 // and then restore the original working directory at the end of the test.
34 func chdir(t
*testing
.T
, dir
string) {
35 olddir
, err
:= os
.Getwd()
37 t
.Fatalf("chdir: %v", err
)
39 if err
:= os
.Chdir(dir
); err
!= nil {
40 t
.Fatalf("chdir %s: %v", dir
, err
)
44 if err
:= os
.Chdir(olddir
); err
!= nil {
45 t
.Errorf("chdir to original working directory %s: %v", olddir
, err
)
51 func TestSameWindowsFile(t
*testing
.T
) {
52 temp
, err
:= os
.MkdirTemp("", "TestSameWindowsFile")
56 defer os
.RemoveAll(temp
)
59 f
, err
:= os
.Create("a")
65 ia1
, err
:= os
.Stat("a")
70 path
, err
:= filepath
.Abs("a")
74 ia2
, err
:= os
.Stat(path
)
78 if !os
.SameFile(ia1
, ia2
) {
79 t
.Errorf("files should be same")
82 p
:= filepath
.VolumeName(path
) + filepath
.Base(path
)
86 ia3
, err
:= os
.Stat(p
)
90 if !os
.SameFile(ia1
, ia3
) {
91 t
.Errorf("files should be same")
95 type dirLinkTest
struct {
97 mklink
func(link
, target
string) error
98 issueNo
int // correspondent issue number (for broken tests)
101 func testDirLinks(t
*testing
.T
, tests
[]dirLinkTest
) {
102 tmpdir
, err
:= os
.MkdirTemp("", "testDirLinks")
106 defer os
.RemoveAll(tmpdir
)
109 dir
:= filepath
.Join(tmpdir
, "dir")
110 err
= os
.Mkdir(dir
, 0777)
114 fi
, err
:= os
.Stat(dir
)
118 err
= os
.WriteFile(filepath
.Join(dir
, "abc"), []byte("abc"), 0644)
122 for _
, test
:= range tests
{
123 link
:= filepath
.Join(tmpdir
, test
.name
+"_link")
124 err
:= test
.mklink(link
, dir
)
126 t
.Errorf("creating link for %q test failed: %v", test
.name
, err
)
130 data
, err
:= os
.ReadFile(filepath
.Join(link
, "abc"))
132 t
.Errorf("failed to read abc file: %v", err
)
135 if string(data
) != "abc" {
136 t
.Errorf(`abc file is expected to have "abc" in it, but has %v`, data
)
140 if test
.issueNo
> 0 {
141 t
.Logf("skipping broken %q test: see issue %d", test
.name
, test
.issueNo
)
145 fi1
, err
:= os
.Stat(link
)
147 t
.Errorf("failed to stat link %v: %v", link
, err
)
151 t
.Errorf("%q should be a directory", link
)
154 if fi1
.Name() != filepath
.Base(link
) {
155 t
.Errorf("Stat(%q).Name() = %q, want %q", link
, fi1
.Name(), filepath
.Base(link
))
158 if !os
.SameFile(fi
, fi1
) {
159 t
.Errorf("%q should point to %q", link
, dir
)
163 fi2
, err
:= os
.Lstat(link
)
165 t
.Errorf("failed to lstat link %v: %v", link
, err
)
168 if m
:= fi2
.Mode(); m
&fs
.ModeSymlink
== 0 {
169 t
.Errorf("%q should be a link, but is not (mode=0x%x)", link
, uint32(m
))
172 if m
:= fi2
.Mode(); m
&fs
.ModeDir
!= 0 {
173 t
.Errorf("%q should be a link, not a directory (mode=0x%x)", link
, uint32(m
))
179 // reparseData is used to build reparse buffer data required for tests.
180 type reparseData
struct {
181 substituteName namePosition
182 printName namePosition
186 type namePosition
struct {
191 func (rd
*reparseData
) addUTF16s(s
[]uint16) (offset
uint16) {
192 off
:= len(rd
.pathBuf
) * 2
193 rd
.pathBuf
= append(rd
.pathBuf
, s
...)
197 func (rd
*reparseData
) addString(s
string) (offset
, length
uint16) {
198 p
:= syscall
.StringToUTF16(s
)
199 return rd
.addUTF16s(p
), uint16(len(p
)-1) * 2 // do not include terminating NUL in the length (as per PrintNameLength and SubstituteNameLength documentation)
202 func (rd
*reparseData
) addSubstituteName(name
string) {
203 rd
.substituteName
.offset
, rd
.substituteName
.length
= rd
.addString(name
)
206 func (rd
*reparseData
) addPrintName(name
string) {
207 rd
.printName
.offset
, rd
.printName
.length
= rd
.addString(name
)
210 func (rd
*reparseData
) addStringNoNUL(s
string) (offset
, length
uint16) {
211 p
:= syscall
.StringToUTF16(s
)
213 return rd
.addUTF16s(p
), uint16(len(p
)) * 2
216 func (rd
*reparseData
) addSubstituteNameNoNUL(name
string) {
217 rd
.substituteName
.offset
, rd
.substituteName
.length
= rd
.addStringNoNUL(name
)
220 func (rd
*reparseData
) addPrintNameNoNUL(name
string) {
221 rd
.printName
.offset
, rd
.printName
.length
= rd
.addStringNoNUL(name
)
224 // pathBuffeLen returns length of rd pathBuf in bytes.
225 func (rd
*reparseData
) pathBuffeLen() uint16 {
226 return uint16(len(rd
.pathBuf
)) * 2
229 // Windows REPARSE_DATA_BUFFER contains union member, and cannot be
230 // translated into Go directly. _REPARSE_DATA_BUFFER type is to help
231 // construct alternative versions of Windows REPARSE_DATA_BUFFER with
232 // union part of SymbolicLinkReparseBuffer or MountPointReparseBuffer type.
233 type _REPARSE_DATA_BUFFER
struct {
234 header windows
.REPARSE_DATA_BUFFER_HEADER
235 detail
[syscall
.MAXIMUM_REPARSE_DATA_BUFFER_SIZE
]byte
238 func createDirLink(link
string, rdb
*_REPARSE_DATA_BUFFER
) error
{
239 err
:= os
.Mkdir(link
, 0777)
244 linkp
:= syscall
.StringToUTF16(link
)
245 fd
, err
:= syscall
.CreateFile(&linkp
[0], syscall
.GENERIC_WRITE
, 0, nil, syscall
.OPEN_EXISTING
,
246 syscall
.FILE_FLAG_OPEN_REPARSE_POINT|syscall
.FILE_FLAG_BACKUP_SEMANTICS
, 0)
250 defer syscall
.CloseHandle(fd
)
252 buflen
:= uint32(rdb
.header
.ReparseDataLength
) + uint32(unsafe
.Sizeof(rdb
.header
))
253 var bytesReturned
uint32
254 return syscall
.DeviceIoControl(fd
, windows
.FSCTL_SET_REPARSE_POINT
,
255 (*byte)(unsafe
.Pointer(&rdb
.header
)), buflen
, nil, 0, &bytesReturned
, nil)
258 func createMountPoint(link
string, target
*reparseData
) error
{
259 var buf
*windows
.MountPointReparseBuffer
260 buflen
:= uint16(unsafe
.Offsetof(buf
.PathBuffer
)) + target
.pathBuffeLen() // see ReparseDataLength documentation
261 byteblob
:= make([]byte, buflen
)
262 buf
= (*windows
.MountPointReparseBuffer
)(unsafe
.Pointer(&byteblob
[0]))
263 buf
.SubstituteNameOffset
= target
.substituteName
.offset
264 buf
.SubstituteNameLength
= target
.substituteName
.length
265 buf
.PrintNameOffset
= target
.printName
.offset
266 buf
.PrintNameLength
= target
.printName
.length
267 pbuflen
:= len(target
.pathBuf
)
268 copy((*[2048]uint16)(unsafe
.Pointer(&buf
.PathBuffer
[0]))[:pbuflen
:pbuflen
], target
.pathBuf
)
270 var rdb _REPARSE_DATA_BUFFER
271 rdb
.header
.ReparseTag
= windows
.IO_REPARSE_TAG_MOUNT_POINT
272 rdb
.header
.ReparseDataLength
= buflen
273 copy(rdb
.detail
[:], byteblob
)
275 return createDirLink(link
, &rdb
)
278 func TestDirectoryJunction(t
*testing
.T
) {
279 var tests
= []dirLinkTest
{
281 // Create link similar to what mklink does, by inserting \??\ at the front of absolute target.
283 mklink
: func(link
, target
string) error
{
285 t
.addSubstituteName(`\??\` + target
)
286 t
.addPrintName(target
)
287 return createMountPoint(link
, &t
)
291 // Do as junction utility https://technet.microsoft.com/en-au/sysinternals/bb896768.aspx does - set PrintNameLength to 0.
292 name
: "have_blank_print_name",
293 mklink
: func(link
, target
string) error
{
295 t
.addSubstituteName(`\??\` + target
)
297 return createMountPoint(link
, &t
)
301 output
, _
:= osexec
.Command("cmd", "/c", "mklink", "/?").Output()
302 mklinkSupportsJunctionLinks
:= strings
.Contains(string(output
), " /J ")
303 if mklinkSupportsJunctionLinks
{
304 tests
= append(tests
,
306 name
: "use_mklink_cmd",
307 mklink
: func(link
, target
string) error
{
308 output
, err
:= osexec
.Command("cmd", "/c", "mklink", "/J", link
, target
).CombinedOutput()
310 t
.Errorf("failed to run mklink %v %v: %v %q", link
, target
, err
, output
)
317 t
.Log(`skipping "use_mklink_cmd" test, mklink does not supports directory junctions`)
319 testDirLinks(t
, tests
)
322 func enableCurrentThreadPrivilege(privilegeName
string) error
{
323 ct
, err
:= windows
.GetCurrentThread()
328 err
= windows
.OpenThreadToken(ct
, syscall
.TOKEN_QUERY|windows
.TOKEN_ADJUST_PRIVILEGES
, false, &t
)
332 defer syscall
.CloseHandle(syscall
.Handle(t
))
334 var tp windows
.TOKEN_PRIVILEGES
336 privStr
, err
:= syscall
.UTF16PtrFromString(privilegeName
)
340 err
= windows
.LookupPrivilegeValue(nil, privStr
, &tp
.Privileges
[0].Luid
)
344 tp
.PrivilegeCount
= 1
345 tp
.Privileges
[0].Attributes
= windows
.SE_PRIVILEGE_ENABLED
346 return windows
.AdjustTokenPrivileges(t
, false, &tp
, 0, nil, nil)
349 func createSymbolicLink(link
string, target
*reparseData
, isrelative
bool) error
{
350 var buf
*windows
.SymbolicLinkReparseBuffer
351 buflen
:= uint16(unsafe
.Offsetof(buf
.PathBuffer
)) + target
.pathBuffeLen() // see ReparseDataLength documentation
352 byteblob
:= make([]byte, buflen
)
353 buf
= (*windows
.SymbolicLinkReparseBuffer
)(unsafe
.Pointer(&byteblob
[0]))
354 buf
.SubstituteNameOffset
= target
.substituteName
.offset
355 buf
.SubstituteNameLength
= target
.substituteName
.length
356 buf
.PrintNameOffset
= target
.printName
.offset
357 buf
.PrintNameLength
= target
.printName
.length
359 buf
.Flags
= windows
.SYMLINK_FLAG_RELATIVE
361 pbuflen
:= len(target
.pathBuf
)
362 copy((*[2048]uint16)(unsafe
.Pointer(&buf
.PathBuffer
[0]))[:pbuflen
:pbuflen
], target
.pathBuf
)
364 var rdb _REPARSE_DATA_BUFFER
365 rdb
.header
.ReparseTag
= syscall
.IO_REPARSE_TAG_SYMLINK
366 rdb
.header
.ReparseDataLength
= buflen
367 copy(rdb
.detail
[:], byteblob
)
369 return createDirLink(link
, &rdb
)
372 func TestDirectorySymbolicLink(t
*testing
.T
) {
373 var tests
[]dirLinkTest
374 output
, _
:= osexec
.Command("cmd", "/c", "mklink", "/?").Output()
375 mklinkSupportsDirectorySymbolicLinks
:= strings
.Contains(string(output
), " /D ")
376 if mklinkSupportsDirectorySymbolicLinks
{
377 tests
= append(tests
,
379 name
: "use_mklink_cmd",
380 mklink
: func(link
, target
string) error
{
381 output
, err
:= osexec
.Command("cmd", "/c", "mklink", "/D", link
, target
).CombinedOutput()
383 t
.Errorf("failed to run mklink %v %v: %v %q", link
, target
, err
, output
)
390 t
.Log(`skipping "use_mklink_cmd" test, mklink does not supports directory symbolic links`)
393 // The rest of these test requires SeCreateSymbolicLinkPrivilege to be held.
394 runtime
.LockOSThread()
395 defer runtime
.UnlockOSThread()
397 err
:= windows
.ImpersonateSelf(windows
.SecurityImpersonation
)
401 defer windows
.RevertToSelf()
403 err
= enableCurrentThreadPrivilege("SeCreateSymbolicLinkPrivilege")
405 t
.Skipf(`skipping some tests, could not enable "SeCreateSymbolicLinkPrivilege": %v`, err
)
407 tests
= append(tests
,
410 mklink
: func(link
, target
string) error
{
411 return os
.Symlink(target
, link
)
415 // Create link similar to what mklink does, by inserting \??\ at the front of absolute target.
417 mklink
: func(link
, target
string) error
{
419 t
.addPrintName(target
)
420 t
.addSubstituteName(`\??\` + target
)
421 return createSymbolicLink(link
, &t
, false)
426 mklink
: func(link
, target
string) error
{
428 t
.addSubstituteNameNoNUL(filepath
.Base(target
))
429 t
.addPrintNameNoNUL(filepath
.Base(target
))
430 return createSymbolicLink(link
, &t
, true)
434 testDirLinks(t
, tests
)
437 func TestNetworkSymbolicLink(t
*testing
.T
) {
438 testenv
.MustHaveSymlink(t
)
440 const _NERR_ServerNotStarted
= syscall
.Errno(2114)
442 dir
, err
:= os
.MkdirTemp("", "TestNetworkSymbolicLink")
446 defer os
.RemoveAll(dir
)
450 shareName
:= "GoSymbolicLinkTestShare" // hope no conflictions
451 sharePath
:= filepath
.Join(dir
, shareName
)
454 err
= os
.MkdirAll(filepath
.Join(sharePath
, testDir
), 0777)
459 wShareName
, err
:= syscall
.UTF16PtrFromString(shareName
)
463 wSharePath
, err
:= syscall
.UTF16PtrFromString(sharePath
)
468 p
:= windows
.SHARE_INFO_2
{
470 Type
: windows
.STYPE_DISKTREE
,
479 err
= windows
.NetShareAdd(nil, 2, (*byte)(unsafe
.Pointer(&p
)), nil)
481 if err
== syscall
.ERROR_ACCESS_DENIED
{
482 t
.Skip("you don't have enough privileges to add network share")
484 if err
== _NERR_ServerNotStarted
{
485 t
.Skip(_NERR_ServerNotStarted
.Error())
490 err
:= windows
.NetShareDel(nil, wShareName
, 0)
496 UNCPath
:= `\\localhost\` + shareName
+ `\`
498 fi1
, err
:= os
.Stat(sharePath
)
502 fi2
, err
:= os
.Stat(UNCPath
)
506 if !os
.SameFile(fi1
, fi2
) {
507 t
.Fatalf("%q and %q should be the same directory, but not", sharePath
, UNCPath
)
510 target
:= filepath
.Join(UNCPath
, testDir
)
513 err
= os
.Symlink(target
, link
)
517 defer os
.Remove(link
)
519 got
, err
:= os
.Readlink(link
)
524 t
.Errorf(`os.Readlink("%s"): got %v, want %v`, link
, got
, target
)
527 got
, err
= filepath
.EvalSymlinks(link
)
532 t
.Errorf(`filepath.EvalSymlinks("%s"): got %v, want %v`, link
, got
, target
)
536 func TestStartProcessAttr(t
*testing
.T
) {
537 p
, err
:= os
.StartProcess(os
.Getenv("COMSPEC"), []string{"/c", "cd"}, new(os
.ProcAttr
))
542 t
.Fatalf("StartProcess expected to fail, but succeeded.")
545 func TestShareNotExistError(t
*testing
.T
) {
547 t
.Skip("slow test that uses network; skipping")
549 _
, err
:= os
.Stat(`\\no_such_server\no_such_share\no_such_file`)
551 t
.Fatal("stat succeeded, but expected to fail")
553 if !os
.IsNotExist(err
) {
554 t
.Fatalf("os.Stat failed with %q, but os.IsNotExist(err) is false", err
)
558 func TestBadNetPathError(t
*testing
.T
) {
559 const ERROR_BAD_NETPATH
= syscall
.Errno(53)
560 if !os
.IsNotExist(ERROR_BAD_NETPATH
) {
561 t
.Fatal("os.IsNotExist(syscall.Errno(53)) is false, but want true")
565 func TestStatDir(t
*testing
.T
) {
568 f
, err
:= os
.Open(".")
589 if !os
.SameFile(fi
, fi2
) {
590 t
.Fatal("race condition occurred")
594 func TestOpenVolumeName(t
*testing
.T
) {
595 tmpdir
, err
:= os
.MkdirTemp("", "TestOpenVolumeName")
599 defer os
.RemoveAll(tmpdir
)
602 want
:= []string{"file1", "file2", "file3", "gopher.txt"}
604 for _
, name
:= range want
{
605 err
:= os
.WriteFile(filepath
.Join(tmpdir
, name
), nil, 0777)
611 f
, err
:= os
.Open(filepath
.VolumeName(tmpdir
))
617 have
, err
:= f
.Readdirnames(-1)
623 if strings
.Join(want
, "/") != strings
.Join(have
, "/") {
624 t
.Fatalf("unexpected file list %q, want %q", have
, want
)
628 func TestDeleteReadOnly(t
*testing
.T
) {
629 tmpdir
:= t
.TempDir()
630 p
:= filepath
.Join(tmpdir
, "a")
631 // This sets FILE_ATTRIBUTE_READONLY.
632 f
, err
:= os
.OpenFile(p
, os
.O_CREATE
, 0400)
638 if err
= os
.Chmod(p
, 0400); err
!= nil {
641 if err
= os
.Remove(p
); err
!= nil {
646 func TestStatSymlinkLoop(t
*testing
.T
) {
647 testenv
.MustHaveSymlink(t
)
651 err
:= os
.Symlink("x", "y")
657 err
= os
.Symlink("y", "x")
663 _
, err
= os
.Stat("x")
664 if _
, ok
:= err
.(*fs
.PathError
); !ok
{
665 t
.Errorf("expected *PathError, got %T: %v\n", err
, err
)
669 func TestReadStdin(t
*testing
.T
) {
670 old
:= poll
.ReadConsole
672 poll
.ReadConsole
= old
675 p
, err
:= syscall
.GetCurrentProcess()
677 t
.Fatalf("Unable to get handle to current process: %v", err
)
679 var stdinDuplicate syscall
.Handle
680 err
= syscall
.DuplicateHandle(p
, syscall
.Handle(syscall
.Stdin
), p
, &stdinDuplicate
, 0, false, syscall
.DUPLICATE_SAME_ACCESS
)
682 t
.Fatalf("Unable to duplicate stdin: %v", err
)
684 testConsole
:= os
.NewConsoleFile(stdinDuplicate
, "test")
686 var tests
= []string{
692 "\U0001F648\U0001F649\U0001F64A",
695 for _
, consoleSize
:= range []int{1, 2, 3, 10, 16, 100, 1000} {
696 for _
, readSize
:= range []int{1, 2, 3, 4, 5, 8, 10, 16, 20, 50, 100} {
697 for _
, s
:= range tests
{
698 t
.Run(fmt
.Sprintf("c%d/r%d/%s", consoleSize
, readSize
, s
), func(t
*testing
.T
) {
699 s16
:= utf16
.Encode([]rune(s
))
700 poll
.ReadConsole
= func(h syscall
.Handle
, buf
*uint16, toread
uint32, read
*uint32, inputControl
*byte) error
{
701 if inputControl
!= nil {
702 t
.Fatalf("inputControl not nil")
708 n
= copy((*[10000]uint16)(unsafe
.Pointer(buf
))[:n
:n
], s16
)
711 t
.Logf("read %d -> %d", toread
, *read
)
717 chunk
:= make([]byte, readSize
)
719 n
, err
:= testConsole
.Read(chunk
)
720 buf
= append(buf
, chunk
[:n
]...)
722 all
= append(all
, string(buf
))
727 } else if err
!= nil {
728 t
.Fatalf("reading %q: error: %v", s
, err
)
730 if len(buf
) >= 2000 {
731 t
.Fatalf("reading %q: stuck in loop: %q", s
, buf
)
735 want
:= strings
.Split(s
, "\x1a")
737 want
= append(want
, "")
739 if !reflect
.DeepEqual(all
, want
) {
740 t
.Errorf("reading %q:\nhave %x\nwant %x", s
, all
, want
)
748 func TestStatPagefile(t
*testing
.T
) {
749 fi
, err
:= os
.Stat(`c:\pagefile.sys`)
752 t
.Fatal(`FileInfo of c:\pagefile.sys has empty name`)
756 if os
.IsNotExist(err
) {
757 t
.Skip(`skipping because c:\pagefile.sys is not found`)
762 // syscallCommandLineToArgv calls syscall.CommandLineToArgv
763 // and converts returned result into []string.
764 func syscallCommandLineToArgv(cmd
string) ([]string, error
) {
766 argv
, err
:= syscall
.CommandLineToArgv(&syscall
.StringToUTF16(cmd
)[0], &argc
)
770 defer syscall
.LocalFree(syscall
.Handle(uintptr(unsafe
.Pointer(argv
))))
773 for _
, v
:= range (*argv
)[:argc
] {
774 args
= append(args
, syscall
.UTF16ToString((*v
)[:]))
779 // compareCommandLineToArgvWithSyscall ensures that
780 // os.CommandLineToArgv(cmd) and syscall.CommandLineToArgv(cmd)
781 // return the same result.
782 func compareCommandLineToArgvWithSyscall(t
*testing
.T
, cmd
string) {
783 syscallArgs
, err
:= syscallCommandLineToArgv(cmd
)
787 args
:= os
.CommandLineToArgv(cmd
)
788 if want
, have
:= fmt
.Sprintf("%q", syscallArgs
), fmt
.Sprintf("%q", args
); want
!= have
{
789 t
.Errorf("testing os.commandLineToArgv(%q) failed: have %q want %q", cmd
, args
, syscallArgs
)
794 func TestCmdArgs(t
*testing
.T
) {
795 tmpdir
:= t
.TempDir()
806 fmt.Printf("%q", os.Args)
809 src
:= filepath
.Join(tmpdir
, "main.go")
810 if err
:= os
.WriteFile(src
, []byte(prog
), 0666); err
!= nil {
814 exe
:= filepath
.Join(tmpdir
, "main.exe")
815 cmd
:= osexec
.Command(testenv
.GoToolPath(t
), "build", "-o", exe
, src
)
817 out
, err
:= cmd
.CombinedOutput()
819 t
.Fatalf("building main.exe failed: %v\n%s", err
, out
)
841 // examples from https://msdn.microsoft.com/en-us/library/17w5ykft.aspx
846 // http://daviddeley.com/autohotkey/parameters/parameters.htm#WINARGV
849 ` "Call Me Ishmael"`,
850 ` Cal"l Me I"shmael`,
852 ` "CallMe\"Ishmael"`,
853 ` "Call Me Ishmael\\"`,
854 ` "CallMe\\\"Ishmael"`,
857 // from 5.5 Some Common Tasks
858 ` "\"Call Me Ishmael\""`,
860 ` "\"C:\TEST A\\\""`,
861 // from 5.6 The Microsoft Examples Explained
867 // from 5.7 Double Double Quote Examples (pre 2008)
869 ` """CallMeIshmael""" b c`,
870 ` """Call Me Ishmael"""`,
871 ` """"Call Me Ishmael"" b c`,
873 for _
, cmd
:= range cmds
{
874 compareCommandLineToArgvWithSyscall(t
, "test"+cmd
)
875 compareCommandLineToArgvWithSyscall(t
, `"cmd line"`+cmd
)
876 compareCommandLineToArgvWithSyscall(t
, exe
+cmd
)
878 // test both syscall.EscapeArg and os.commandLineToArgv
879 args
:= os
.CommandLineToArgv(exe
+ cmd
)
880 out
, err
:= osexec
.Command(args
[0], args
[1:]...).CombinedOutput()
882 t
.Fatalf("running %q failed: %v\n%v", args
, err
, string(out
))
884 if want
, have
:= fmt
.Sprintf("%q", args
), string(out
); want
!= have
{
885 t
.Errorf("wrong output of executing %q: have %q want %q", args
, have
, want
)
891 func findOneDriveDir() (string, error
) {
892 // as per https://stackoverflow.com/questions/42519624/how-to-determine-location-of-onedrive-on-windows-7-and-8-in-c
893 const onedrivekey
= `SOFTWARE\Microsoft\OneDrive`
894 k
, err
:= registry
.OpenKey(registry
.CURRENT_USER
, onedrivekey
, registry
.READ
)
896 return "", fmt
.Errorf("OpenKey(%q) failed: %v", onedrivekey
, err
)
900 path
, _
, err
:= k
.GetStringValue("UserFolder")
902 return "", fmt
.Errorf("reading UserFolder failed: %v", err
)
907 // TestOneDrive verifies that OneDrive folder is a directory and not a symlink.
908 func TestOneDrive(t
*testing
.T
) {
909 dir
, err
:= findOneDriveDir()
911 t
.Skipf("Skipping, because we did not find OneDrive directory: %v", err
)
916 func TestWindowsDevNullFile(t
*testing
.T
) {
917 testDevNullFile(t
, "NUL", true)
918 testDevNullFile(t
, "nul", true)
919 testDevNullFile(t
, "Nul", true)
921 f1
, err
:= os
.Open("NUL")
927 fi1
, err
:= f1
.Stat()
932 f2
, err
:= os
.Open("nul")
938 fi2
, err
:= f2
.Stat()
943 if !os
.SameFile(fi1
, fi2
) {
944 t
.Errorf(`"NUL" and "nul" are not the same file`)
948 // TestSymlinkCreation verifies that creating a symbolic link
949 // works on Windows when developer mode is active.
950 // This is supported starting Windows 10 (1703, v10.0.14972).
951 func TestSymlinkCreation(t
*testing
.T
) {
952 if !testenv
.HasSymlink() && !isWindowsDeveloperModeActive() {
953 t
.Skip("Windows developer mode is not active")
958 dummyFile
:= filepath
.Join(temp
, "file")
959 if err
:= os
.WriteFile(dummyFile
, []byte(""), 0644); err
!= nil {
963 linkFile
:= filepath
.Join(temp
, "link")
964 if err
:= os
.Symlink(dummyFile
, linkFile
); err
!= nil {
969 // isWindowsDeveloperModeActive checks whether or not the developer mode is active on Windows 10.
970 // Returns false for prior Windows versions.
971 // see https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development
972 func isWindowsDeveloperModeActive() bool {
973 key
, err
:= registry
.OpenKey(registry
.LOCAL_MACHINE
, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", registry
.READ
)
978 val
, _
, err
:= key
.GetIntegerValue("AllowDevelopmentWithoutDevLicense")
986 // TestRootRelativeDirSymlink verifies that symlinks to paths relative to the
987 // drive root (beginning with "\" but no volume name) are created with the
988 // correct symlink type.
989 // (See https://golang.org/issue/39183#issuecomment-632175728.)
990 func TestRootRelativeDirSymlink(t
*testing
.T
) {
991 testenv
.MustHaveSymlink(t
)
995 dir
:= filepath
.Join(temp
, "dir")
996 if err
:= os
.Mkdir(dir
, 0755); err
!= nil {
1000 volumeRelDir
:= strings
.TrimPrefix(dir
, filepath
.VolumeName(dir
)) // leaves leading backslash
1002 link
:= filepath
.Join(temp
, "link")
1003 err
:= os
.Symlink(volumeRelDir
, link
)
1007 t
.Logf("Symlink(%#q, %#q)", volumeRelDir
, link
)
1009 f
, err
:= os
.Open(link
)
1014 if fi
, err
:= f
.Stat(); err
!= nil {
1016 } else if !fi
.IsDir() {
1017 t
.Errorf("Open(%#q).Stat().IsDir() = false; want true", f
.Name())
1021 // TestWorkingDirectoryRelativeSymlink verifies that symlinks to paths relative
1022 // to the current working directory for the drive, such as "C:File.txt", are
1023 // correctly converted to absolute links of the correct symlink type (per
1024 // https://docs.microsoft.com/en-us/windows/win32/fileio/creating-symbolic-links).
1025 func TestWorkingDirectoryRelativeSymlink(t
*testing
.T
) {
1026 testenv
.MustHaveSymlink(t
)
1028 // Construct a directory to be symlinked.
1030 if v
:= filepath
.VolumeName(temp
); len(v
) < 2 || v
[1] != ':' {
1031 t
.Skipf("Can't test relative symlinks: t.TempDir() (%#q) does not begin with a drive letter.", temp
)
1034 absDir
:= filepath
.Join(temp
, `dir\sub`)
1035 if err
:= os
.MkdirAll(absDir
, 0755); err
!= nil {
1039 // Change to the temporary directory and construct a
1040 // working-directory-relative symlink.
1041 oldwd
, err
:= os
.Getwd()
1046 if err
:= os
.Chdir(oldwd
); err
!= nil {
1050 if err
:= os
.Chdir(temp
); err
!= nil {
1053 t
.Logf("Chdir(%#q)", temp
)
1055 wdRelDir
:= filepath
.VolumeName(temp
) + `dir\sub` // no backslash after volume.
1056 absLink
:= filepath
.Join(temp
, "link")
1057 err
= os
.Symlink(wdRelDir
, absLink
)
1061 t
.Logf("Symlink(%#q, %#q)", wdRelDir
, absLink
)
1063 // Now change back to the original working directory and verify that the
1064 // symlink still refers to its original path and is correctly marked as a
1066 if err
:= os
.Chdir(oldwd
); err
!= nil {
1069 t
.Logf("Chdir(%#q)", oldwd
)
1071 resolved
, err
:= os
.Readlink(absLink
)
1073 t
.Errorf("Readlink(%#q): %v", absLink
, err
)
1074 } else if resolved
!= absDir
{
1075 t
.Errorf("Readlink(%#q) = %#q; want %#q", absLink
, resolved
, absDir
)
1078 linkFile
, err
:= os
.Open(absLink
)
1082 defer linkFile
.Close()
1084 linkInfo
, err
:= linkFile
.Stat()
1088 if !linkInfo
.IsDir() {
1089 t
.Errorf("Open(%#q).Stat().IsDir() = false; want true", absLink
)
1092 absInfo
, err
:= os
.Stat(absDir
)
1097 if !os
.SameFile(absInfo
, linkInfo
) {
1098 t
.Errorf("SameFile(Stat(%#q), Open(%#q).Stat()) = false; want true", absDir
, absLink
)
1102 // TestStatOfInvalidName is regression test for issue #24999.
1103 func TestStatOfInvalidName(t
*testing
.T
) {
1104 _
, err
:= os
.Stat("*.go")
1106 t
.Fatal(`os.Stat("*.go") unexpectedly succeeded`)
1110 // findUnusedDriveLetter searches mounted drive list on the system
1111 // (starting from Z: and ending at D:) for unused drive letter.
1112 // It returns path to the found drive root directory (like Z:\) or error.
1113 func findUnusedDriveLetter() (string, error
) {
1114 // Do not use A: and B:, because they are reserved for floppy drive.
1115 // Do not use C:, because it is normally used for main drive.
1116 for l
:= 'Z'; l
>= 'D'; l
-- {
1117 p
:= string(l
) + `:\`
1118 _
, err
:= os
.Stat(p
)
1119 if os
.IsNotExist(err
) {
1123 return "", errors
.New("Could not find unused drive letter.")
1126 func TestRootDirAsTemp(t
*testing
.T
) {
1127 testenv
.MustHaveExec(t
)
1129 if os
.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
1130 fmt
.Print(os
.TempDir())
1134 newtmp
, err
:= findUnusedDriveLetter()
1139 cmd
:= osexec
.Command(os
.Args
[0], "-test.run=TestRootDirAsTemp")
1140 cmd
.Env
= os
.Environ()
1141 cmd
.Env
= append(cmd
.Env
, "GO_WANT_HELPER_PROCESS=1")
1142 cmd
.Env
= append(cmd
.Env
, "TMP="+newtmp
)
1143 cmd
.Env
= append(cmd
.Env
, "TEMP="+newtmp
)
1144 output
, err
:= cmd
.CombinedOutput()
1146 t
.Fatalf("Failed to spawn child process: %v %q", err
, string(output
))
1148 if want
, have
:= newtmp
, string(output
); have
!= want
{
1149 t
.Fatalf("unexpected child process output %q, want %q", have
, want
)
1153 func testReadlink(t
*testing
.T
, path
, want
string) {
1154 got
, err
:= os
.Readlink(path
)
1160 t
.Errorf(`Readlink(%q): got %q, want %q`, path
, got
, want
)
1164 func mklink(t
*testing
.T
, link
, target
string) {
1165 output
, err
:= osexec
.Command("cmd", "/c", "mklink", link
, target
).CombinedOutput()
1167 t
.Fatalf("failed to run mklink %v %v: %v %q", link
, target
, err
, output
)
1171 func mklinkj(t
*testing
.T
, link
, target
string) {
1172 output
, err
:= osexec
.Command("cmd", "/c", "mklink", "/J", link
, target
).CombinedOutput()
1174 t
.Fatalf("failed to run mklink %v %v: %v %q", link
, target
, err
, output
)
1178 func mklinkd(t
*testing
.T
, link
, target
string) {
1179 output
, err
:= osexec
.Command("cmd", "/c", "mklink", "/D", link
, target
).CombinedOutput()
1181 t
.Fatalf("failed to run mklink %v %v: %v %q", link
, target
, err
, output
)
1185 func TestWindowsReadlink(t
*testing
.T
) {
1186 tmpdir
, err
:= os
.MkdirTemp("", "TestWindowsReadlink")
1190 defer os
.RemoveAll(tmpdir
)
1192 // Make sure tmpdir is not a symlink, otherwise tests will fail.
1193 tmpdir
, err
= filepath
.EvalSymlinks(tmpdir
)
1199 vol
:= filepath
.VolumeName(tmpdir
)
1200 output
, err
:= osexec
.Command("cmd", "/c", "mountvol", vol
, "/L").CombinedOutput()
1202 t
.Fatalf("failed to run mountvol %v /L: %v %q", vol
, err
, output
)
1204 ntvol
:= strings
.Trim(string(output
), " \n\r")
1206 dir
:= filepath
.Join(tmpdir
, "dir")
1207 err
= os
.MkdirAll(dir
, 0777)
1212 absdirjlink
:= filepath
.Join(tmpdir
, "absdirjlink")
1213 mklinkj(t
, absdirjlink
, dir
)
1214 testReadlink(t
, absdirjlink
, dir
)
1216 ntdirjlink
:= filepath
.Join(tmpdir
, "ntdirjlink")
1217 mklinkj(t
, ntdirjlink
, ntvol
+absdirjlink
[len(filepath
.VolumeName(absdirjlink
)):])
1218 testReadlink(t
, ntdirjlink
, absdirjlink
)
1220 ntdirjlinktolink
:= filepath
.Join(tmpdir
, "ntdirjlinktolink")
1221 mklinkj(t
, ntdirjlinktolink
, ntvol
+absdirjlink
[len(filepath
.VolumeName(absdirjlink
)):])
1222 testReadlink(t
, ntdirjlinktolink
, absdirjlink
)
1224 mklinkj(t
, "reldirjlink", "dir")
1225 testReadlink(t
, "reldirjlink", dir
) // relative directory junction resolves to absolute path
1227 // Make sure we have sufficient privilege to run mklink command.
1228 testenv
.MustHaveSymlink(t
)
1230 absdirlink
:= filepath
.Join(tmpdir
, "absdirlink")
1231 mklinkd(t
, absdirlink
, dir
)
1232 testReadlink(t
, absdirlink
, dir
)
1234 ntdirlink
:= filepath
.Join(tmpdir
, "ntdirlink")
1235 mklinkd(t
, ntdirlink
, ntvol
+absdirlink
[len(filepath
.VolumeName(absdirlink
)):])
1236 testReadlink(t
, ntdirlink
, absdirlink
)
1238 mklinkd(t
, "reldirlink", "dir")
1239 testReadlink(t
, "reldirlink", "dir")
1241 file
:= filepath
.Join(tmpdir
, "file")
1242 err
= os
.WriteFile(file
, []byte(""), 0666)
1247 filelink
:= filepath
.Join(tmpdir
, "filelink")
1248 mklink(t
, filelink
, file
)
1249 testReadlink(t
, filelink
, file
)
1251 linktofilelink
:= filepath
.Join(tmpdir
, "linktofilelink")
1252 mklink(t
, linktofilelink
, ntvol
+filelink
[len(filepath
.VolumeName(filelink
)):])
1253 testReadlink(t
, linktofilelink
, filelink
)
1255 mklink(t
, "relfilelink", "file")
1256 testReadlink(t
, "relfilelink", "file")
1259 // os.Mkdir(os.DevNull) fails.
1260 func TestMkdirDevNull(t
*testing
.T
) {
1261 err
:= os
.Mkdir(os
.DevNull
, 777)
1262 oserr
, ok
:= err
.(*fs
.PathError
)
1264 t
.Fatalf("error (%T) is not *fs.PathError", err
)
1266 errno
, ok
:= oserr
.Err
.(syscall
.Errno
)
1268 t
.Fatalf("error (%T) is not syscall.Errno", oserr
)
1270 if errno
!= syscall
.ENOTDIR
{
1271 t
.Fatalf("error %d is not syscall.ENOTDIR", errno
)