Source file
src/os/os_windows_test.go
1
2
3
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
31 type syscallDescriptor = syscall.Handle
32
33
34
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
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
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
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
218 func (rd *reparseData) pathBuffeLen() uint16 {
219 return uint16(len(rd.pathBuf)) * 2
220 }
221
222
223
224
225
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()
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
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
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()
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
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
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"
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
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
734
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
751
752
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
818 ` "abc" d e`,
819 ` a\\b d"e f"g h`,
820 ` a\\\"b c d`,
821 ` a\\\\"b c" d e`,
822
823
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
834 ` "\"Call Me Ishmael\""`,
835 ` "C:\TEST A\\"`,
836 ` "\"C:\TEST A\\\""`,
837
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
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
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
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
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
962
963
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
983
984
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
1000
1001
1002
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))
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
1035
1036
1037
1038 func TestWorkingDirectoryRelativeSymlink(t *testing.T) {
1039 testenv.MustHaveSymlink(t)
1040
1041
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
1053
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`
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
1077
1078
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
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
1126
1127
1128 func findUnusedDriveLetter() (string, error) {
1129
1130
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
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)
1247
1248
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
1284
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
1309
1310
1311
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
1326
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
1354
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
1369
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
1390 namew := []uint16{0x2e, 0xdc6d, 0xdc73, 0xdc79, 0xdc73, 0x30, 0x30, 0x30, 0x31, 0}
1391
1392
1393
1394
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
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
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
1427
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 }
1449
1450 func TestNewFileInvalid(t *testing.T) {
1451 t.Parallel()
1452 if f := os.NewFile(uintptr(syscall.InvalidHandle), "invalid"); f != nil {
1453 t.Errorf("NewFile(InvalidHandle) got %v want nil", f)
1454 }
1455 }
1456
1457 func TestReadDirPipe(t *testing.T) {
1458 dir := `\\.\pipe\`
1459 fi, err := os.Stat(dir)
1460 if err != nil || !fi.IsDir() {
1461 t.Skipf("%s is not a directory", dir)
1462 }
1463 _, err = os.ReadDir(dir)
1464 if err != nil {
1465 t.Errorf("ReadDir(%q) = %v", dir, err)
1466 }
1467 }
1468
View as plain text