Source file
src/os/os_windows_test.go
1
2
3
4
5 package os_test
6
7 import (
8 "bytes"
9 "errors"
10 "fmt"
11 "internal/godebug"
12 "internal/poll"
13 "internal/syscall/windows"
14 "internal/syscall/windows/registry"
15 "internal/testenv"
16 "io"
17 "io/fs"
18 "os"
19 "os/exec"
20 "path/filepath"
21 "runtime"
22 "slices"
23 "strconv"
24 "strings"
25 "sync"
26 "sync/atomic"
27 "syscall"
28 "testing"
29 "time"
30 "unicode/utf16"
31 "unsafe"
32 )
33
34 var winsymlink = godebug.New("winsymlink")
35 var winreadlinkvolume = godebug.New("winreadlinkvolume")
36
37
38 type syscallDescriptor = syscall.Handle
39
40 func TestSameWindowsFile(t *testing.T) {
41 t.Chdir(t.TempDir())
42
43 f, err := os.Create("a")
44 if err != nil {
45 t.Fatal(err)
46 }
47 f.Close()
48
49 ia1, err := os.Stat("a")
50 if err != nil {
51 t.Fatal(err)
52 }
53
54 path, err := filepath.Abs("a")
55 if err != nil {
56 t.Fatal(err)
57 }
58 ia2, err := os.Stat(path)
59 if err != nil {
60 t.Fatal(err)
61 }
62 if !os.SameFile(ia1, ia2) {
63 t.Errorf("files should be same")
64 }
65
66 p := filepath.VolumeName(path) + filepath.Base(path)
67 ia3, err := os.Stat(p)
68 if err != nil {
69 t.Fatal(err)
70 }
71 if !os.SameFile(ia1, ia3) {
72 t.Errorf("files should be same")
73 }
74 }
75
76 type dirLinkTest struct {
77 name string
78 mklink func(link, target string) error
79 isMountPoint bool
80 }
81
82 func testDirLinks(t *testing.T, tests []dirLinkTest) {
83 tmpdir := t.TempDir()
84 t.Chdir(tmpdir)
85
86 dir := filepath.Join(tmpdir, "dir")
87 err := os.Mkdir(dir, 0777)
88 if err != nil {
89 t.Fatal(err)
90 }
91 fi, err := os.Stat(dir)
92 if err != nil {
93 t.Fatal(err)
94 }
95 err = os.WriteFile(filepath.Join(dir, "abc"), []byte("abc"), 0644)
96 if err != nil {
97 t.Fatal(err)
98 }
99 for _, test := range tests {
100 link := filepath.Join(tmpdir, test.name+"_link")
101 err := test.mklink(link, dir)
102 if err != nil {
103 t.Errorf("creating link for %q test failed: %v", test.name, err)
104 continue
105 }
106
107 data, err := os.ReadFile(filepath.Join(link, "abc"))
108 if err != nil {
109 t.Errorf("failed to read abc file: %v", err)
110 continue
111 }
112 if string(data) != "abc" {
113 t.Errorf(`abc file is expected to have "abc" in it, but has %v`, data)
114 continue
115 }
116
117 fi1, err := os.Stat(link)
118 if err != nil {
119 t.Errorf("failed to stat link %v: %v", link, err)
120 continue
121 }
122 if tp := fi1.Mode().Type(); tp != fs.ModeDir {
123 t.Errorf("Stat(%q) is type %v; want %v", link, tp, fs.ModeDir)
124 continue
125 }
126 if fi1.Name() != filepath.Base(link) {
127 t.Errorf("Stat(%q).Name() = %q, want %q", link, fi1.Name(), filepath.Base(link))
128 continue
129 }
130 if !os.SameFile(fi, fi1) {
131 t.Errorf("%q should point to %q", link, dir)
132 continue
133 }
134
135 fi2, err := os.Lstat(link)
136 if err != nil {
137 t.Errorf("failed to lstat link %v: %v", link, err)
138 continue
139 }
140 var wantType fs.FileMode
141 if test.isMountPoint && winsymlink.Value() != "0" {
142
143 wantType = fs.ModeIrregular
144 } else {
145
146 wantType = fs.ModeSymlink
147 }
148 if tp := fi2.Mode().Type(); tp != wantType {
149 t.Errorf("Lstat(%q) is type %v; want %v", link, tp, wantType)
150 }
151 }
152 }
153
154
155 type reparseData struct {
156 substituteName namePosition
157 printName namePosition
158 pathBuf []uint16
159 }
160
161 type namePosition struct {
162 offset uint16
163 length uint16
164 }
165
166 func (rd *reparseData) addUTF16s(s []uint16) (offset uint16) {
167 off := len(rd.pathBuf) * 2
168 rd.pathBuf = append(rd.pathBuf, s...)
169 return uint16(off)
170 }
171
172 func (rd *reparseData) addString(s string) (offset, length uint16) {
173 p := syscall.StringToUTF16(s)
174 return rd.addUTF16s(p), uint16(len(p)-1) * 2
175 }
176
177 func (rd *reparseData) addSubstituteName(name string) {
178 rd.substituteName.offset, rd.substituteName.length = rd.addString(name)
179 }
180
181 func (rd *reparseData) addPrintName(name string) {
182 rd.printName.offset, rd.printName.length = rd.addString(name)
183 }
184
185 func (rd *reparseData) addStringNoNUL(s string) (offset, length uint16) {
186 p := syscall.StringToUTF16(s)
187 p = p[:len(p)-1]
188 return rd.addUTF16s(p), uint16(len(p)) * 2
189 }
190
191 func (rd *reparseData) addSubstituteNameNoNUL(name string) {
192 rd.substituteName.offset, rd.substituteName.length = rd.addStringNoNUL(name)
193 }
194
195 func (rd *reparseData) addPrintNameNoNUL(name string) {
196 rd.printName.offset, rd.printName.length = rd.addStringNoNUL(name)
197 }
198
199
200 func (rd *reparseData) pathBuffeLen() uint16 {
201 return uint16(len(rd.pathBuf)) * 2
202 }
203
204
205
206
207
208 type _REPARSE_DATA_BUFFER struct {
209 header windows.REPARSE_DATA_BUFFER_HEADER
210 detail [syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]byte
211 }
212
213 func createDirLink(link string, rdb *_REPARSE_DATA_BUFFER) error {
214 err := os.Mkdir(link, 0777)
215 if err != nil {
216 return err
217 }
218
219 linkp := syscall.StringToUTF16(link)
220 fd, err := syscall.CreateFile(&linkp[0], syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING,
221 syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
222 if err != nil {
223 return err
224 }
225 defer syscall.CloseHandle(fd)
226
227 buflen := uint32(rdb.header.ReparseDataLength) + uint32(unsafe.Sizeof(rdb.header))
228 var bytesReturned uint32
229 return syscall.DeviceIoControl(fd, windows.FSCTL_SET_REPARSE_POINT,
230 (*byte)(unsafe.Pointer(&rdb.header)), buflen, nil, 0, &bytesReturned, nil)
231 }
232
233 func createMountPoint(link string, target *reparseData) error {
234 var buf *windows.MountPointReparseBuffer
235 buflen := uint16(unsafe.Offsetof(buf.PathBuffer)) + target.pathBuffeLen()
236 byteblob := make([]byte, buflen)
237 buf = (*windows.MountPointReparseBuffer)(unsafe.Pointer(&byteblob[0]))
238 buf.SubstituteNameOffset = target.substituteName.offset
239 buf.SubstituteNameLength = target.substituteName.length
240 buf.PrintNameOffset = target.printName.offset
241 buf.PrintNameLength = target.printName.length
242 pbuflen := len(target.pathBuf)
243 copy((*[2048]uint16)(unsafe.Pointer(&buf.PathBuffer[0]))[:pbuflen:pbuflen], target.pathBuf)
244
245 var rdb _REPARSE_DATA_BUFFER
246 rdb.header.ReparseTag = windows.IO_REPARSE_TAG_MOUNT_POINT
247 rdb.header.ReparseDataLength = buflen
248 copy(rdb.detail[:], byteblob)
249
250 return createDirLink(link, &rdb)
251 }
252
253 func TestDirectoryJunction(t *testing.T) {
254 var tests = []dirLinkTest{
255 {
256
257 name: "standard",
258 isMountPoint: true,
259 mklink: func(link, target string) error {
260 var t reparseData
261 t.addSubstituteName(`\??\` + target)
262 t.addPrintName(target)
263 return createMountPoint(link, &t)
264 },
265 },
266 {
267
268 name: "have_blank_print_name",
269 isMountPoint: true,
270 mklink: func(link, target string) error {
271 var t reparseData
272 t.addSubstituteName(`\??\` + target)
273 t.addPrintName("")
274 return createMountPoint(link, &t)
275 },
276 },
277 }
278 output, _ := testenv.Command(t, "cmd", "/c", "mklink", "/?").Output()
279 mklinkSupportsJunctionLinks := strings.Contains(string(output), " /J ")
280 if mklinkSupportsJunctionLinks {
281 tests = append(tests,
282 dirLinkTest{
283 name: "use_mklink_cmd",
284 isMountPoint: true,
285 mklink: func(link, target string) error {
286 output, err := testenv.Command(t, "cmd", "/c", "mklink", "/J", link, target).CombinedOutput()
287 if err != nil {
288 t.Errorf("failed to run mklink %v %v: %v %q", link, target, err, output)
289 }
290 return nil
291 },
292 },
293 )
294 } else {
295 t.Log(`skipping "use_mklink_cmd" test, mklink does not supports directory junctions`)
296 }
297 testDirLinks(t, tests)
298 }
299
300 func enableCurrentThreadPrivilege(privilegeName string) error {
301 ct, err := windows.GetCurrentThread()
302 if err != nil {
303 return err
304 }
305 var t syscall.Token
306 err = windows.OpenThreadToken(ct, syscall.TOKEN_QUERY|windows.TOKEN_ADJUST_PRIVILEGES, false, &t)
307 if err != nil {
308 return err
309 }
310 defer syscall.CloseHandle(syscall.Handle(t))
311
312 var tp windows.TOKEN_PRIVILEGES
313
314 privStr, err := syscall.UTF16PtrFromString(privilegeName)
315 if err != nil {
316 return err
317 }
318 err = windows.LookupPrivilegeValue(nil, privStr, &tp.Privileges[0].Luid)
319 if err != nil {
320 return err
321 }
322 tp.PrivilegeCount = 1
323 tp.Privileges[0].Attributes = windows.SE_PRIVILEGE_ENABLED
324 return windows.AdjustTokenPrivileges(t, false, &tp, 0, nil, nil)
325 }
326
327 func createSymbolicLink(link string, target *reparseData, isrelative bool) error {
328 var buf *windows.SymbolicLinkReparseBuffer
329 buflen := uint16(unsafe.Offsetof(buf.PathBuffer)) + target.pathBuffeLen()
330 byteblob := make([]byte, buflen)
331 buf = (*windows.SymbolicLinkReparseBuffer)(unsafe.Pointer(&byteblob[0]))
332 buf.SubstituteNameOffset = target.substituteName.offset
333 buf.SubstituteNameLength = target.substituteName.length
334 buf.PrintNameOffset = target.printName.offset
335 buf.PrintNameLength = target.printName.length
336 if isrelative {
337 buf.Flags = windows.SYMLINK_FLAG_RELATIVE
338 }
339 pbuflen := len(target.pathBuf)
340 copy((*[2048]uint16)(unsafe.Pointer(&buf.PathBuffer[0]))[:pbuflen:pbuflen], target.pathBuf)
341
342 var rdb _REPARSE_DATA_BUFFER
343 rdb.header.ReparseTag = syscall.IO_REPARSE_TAG_SYMLINK
344 rdb.header.ReparseDataLength = buflen
345 copy(rdb.detail[:], byteblob)
346
347 return createDirLink(link, &rdb)
348 }
349
350 func TestDirectorySymbolicLink(t *testing.T) {
351 var tests []dirLinkTest
352 output, _ := testenv.Command(t, "cmd", "/c", "mklink", "/?").Output()
353 mklinkSupportsDirectorySymbolicLinks := strings.Contains(string(output), " /D ")
354 if mklinkSupportsDirectorySymbolicLinks {
355 tests = append(tests,
356 dirLinkTest{
357 name: "use_mklink_cmd",
358 mklink: func(link, target string) error {
359 output, err := testenv.Command(t, "cmd", "/c", "mklink", "/D", link, target).CombinedOutput()
360 if err != nil {
361 t.Errorf("failed to run mklink %v %v: %v %q", link, target, err, output)
362 }
363 return nil
364 },
365 },
366 )
367 } else {
368 t.Log(`skipping "use_mklink_cmd" test, mklink does not supports directory symbolic links`)
369 }
370
371
372 runtime.LockOSThread()
373 defer runtime.UnlockOSThread()
374
375 err := windows.ImpersonateSelf(windows.SecurityImpersonation)
376 if err != nil {
377 t.Fatal(err)
378 }
379 defer windows.RevertToSelf()
380
381 err = enableCurrentThreadPrivilege("SeCreateSymbolicLinkPrivilege")
382 if err != nil {
383 t.Skipf(`skipping some tests, could not enable "SeCreateSymbolicLinkPrivilege": %v`, err)
384 }
385 tests = append(tests,
386 dirLinkTest{
387 name: "use_os_pkg",
388 mklink: func(link, target string) error {
389 return os.Symlink(target, link)
390 },
391 },
392 dirLinkTest{
393
394 name: "standard",
395 mklink: func(link, target string) error {
396 var t reparseData
397 t.addPrintName(target)
398 t.addSubstituteName(`\??\` + target)
399 return createSymbolicLink(link, &t, false)
400 },
401 },
402 dirLinkTest{
403 name: "relative",
404 mklink: func(link, target string) error {
405 var t reparseData
406 t.addSubstituteNameNoNUL(filepath.Base(target))
407 t.addPrintNameNoNUL(filepath.Base(target))
408 return createSymbolicLink(link, &t, true)
409 },
410 },
411 )
412 testDirLinks(t, tests)
413 }
414
415 func mustHaveWorkstation(t *testing.T) {
416 mar, err := windows.OpenSCManager(nil, nil, windows.SERVICE_QUERY_STATUS)
417 if err != nil {
418 return
419 }
420 defer syscall.CloseHandle(mar)
421
422 srv, err := windows.OpenService(mar, syscall.StringToUTF16Ptr("LanmanWorkstation"), windows.SERVICE_QUERY_STATUS)
423 if err != nil {
424 return
425 }
426 defer syscall.CloseHandle(srv)
427 var state windows.SERVICE_STATUS
428 err = windows.QueryServiceStatus(srv, &state)
429 if err != nil {
430 return
431 }
432 if state.CurrentState != windows.SERVICE_RUNNING {
433 t.Skip("Requires the Windows service Workstation, but it is detected that it is not enabled.")
434 }
435 }
436
437 func TestNetworkSymbolicLink(t *testing.T) {
438 testenv.MustHaveSymlink(t)
439
440 const _NERR_ServerNotStarted = syscall.Errno(2114)
441
442 dir := t.TempDir()
443 t.Chdir(dir)
444
445 pid := os.Getpid()
446 shareName := fmt.Sprintf("GoSymbolicLinkTestShare%d", pid)
447 sharePath := filepath.Join(dir, shareName)
448 testDir := "TestDir"
449
450 err := os.MkdirAll(filepath.Join(sharePath, testDir), 0777)
451 if err != nil {
452 t.Fatal(err)
453 }
454
455 wShareName, err := syscall.UTF16PtrFromString(shareName)
456 if err != nil {
457 t.Fatal(err)
458 }
459 wSharePath, err := syscall.UTF16PtrFromString(sharePath)
460 if err != nil {
461 t.Fatal(err)
462 }
463
464
465
466
467
468
469
470
471
472
473 const permissions = 0
474
475 p := windows.SHARE_INFO_2{
476 Netname: wShareName,
477 Type: windows.STYPE_DISKTREE | windows.STYPE_TEMPORARY,
478 Remark: nil,
479 Permissions: permissions,
480 MaxUses: 1,
481 CurrentUses: 0,
482 Path: wSharePath,
483 Passwd: nil,
484 }
485
486 err = windows.NetShareAdd(nil, 2, (*byte)(unsafe.Pointer(&p)), nil)
487 if err != nil {
488 if err == syscall.ERROR_ACCESS_DENIED || err == _NERR_ServerNotStarted {
489 t.Skipf("skipping: NetShareAdd: %v", err)
490 }
491 t.Fatal(err)
492 }
493 defer func() {
494 err := windows.NetShareDel(nil, wShareName, 0)
495 if err != nil {
496 t.Fatal(err)
497 }
498 }()
499
500 UNCPath := `\\localhost\` + shareName + `\`
501
502 fi1, err := os.Stat(sharePath)
503 if err != nil {
504 t.Fatal(err)
505 }
506 fi2, err := os.Stat(UNCPath)
507 if err != nil {
508 mustHaveWorkstation(t)
509 t.Fatal(err)
510 }
511 if !os.SameFile(fi1, fi2) {
512 t.Fatalf("%q and %q should be the same directory, but not", sharePath, UNCPath)
513 }
514
515 target := filepath.Join(UNCPath, testDir)
516 link := "link"
517
518 err = os.Symlink(target, link)
519 if err != nil {
520 t.Fatal(err)
521 }
522 defer os.Remove(link)
523
524 got, err := os.Readlink(link)
525 if err != nil {
526 t.Fatal(err)
527 }
528 if got != target {
529 t.Errorf(`os.Readlink(%#q): got %v, want %v`, link, got, target)
530 }
531
532 got, err = filepath.EvalSymlinks(link)
533 if err != nil {
534 t.Fatal(err)
535 }
536 if got != target {
537 t.Errorf(`filepath.EvalSymlinks(%#q): got %v, want %v`, link, got, target)
538 }
539 }
540
541 func TestStatLxSymLink(t *testing.T) {
542 if _, err := exec.LookPath("wsl"); err != nil {
543 t.Skip("skipping: WSL not detected")
544 }
545
546 t.Chdir(t.TempDir())
547
548 const target = "target"
549 const link = "link"
550
551 _, err := testenv.Command(t, "wsl", "/bin/mkdir", target).Output()
552 if err != nil {
553
554 t.Skipf("skipping: WSL is not correctly installed: %v", err)
555 }
556
557 _, err = testenv.Command(t, "wsl", "/bin/ln", "-s", target, link).Output()
558 if err != nil {
559 t.Fatal(err)
560 }
561
562 fi, err := os.Lstat(link)
563 if err != nil {
564 t.Fatal(err)
565 }
566 if m := fi.Mode(); m&fs.ModeSymlink != 0 {
567
568 t.Skip("skipping: WSL created reparse tag IO_REPARSE_TAG_SYMLINK instead of an IO_REPARSE_TAG_LX_SYMLINK")
569 }
570
571
572 _, err = os.Stat(link)
573 const ERROR_CANT_ACCESS_FILE = syscall.Errno(1920)
574 if err == nil || !errors.Is(err, ERROR_CANT_ACCESS_FILE) {
575 t.Fatalf("os.Stat(%q): got %v, want ERROR_CANT_ACCESS_FILE", link, err)
576 }
577 }
578
579 func TestStartProcessAttr(t *testing.T) {
580 t.Parallel()
581
582 p, err := os.StartProcess(os.Getenv("COMSPEC"), []string{"/c", "cd"}, new(os.ProcAttr))
583 if err != nil {
584 return
585 }
586 defer p.Wait()
587 t.Fatalf("StartProcess expected to fail, but succeeded.")
588 }
589
590 func TestShareNotExistError(t *testing.T) {
591 if testing.Short() {
592 t.Skip("slow test that uses network; skipping")
593 }
594 t.Parallel()
595
596 _, err := os.Stat(`\\no_such_server\no_such_share\no_such_file`)
597 if err == nil {
598 t.Fatal("stat succeeded, but expected to fail")
599 }
600 if !os.IsNotExist(err) {
601 t.Fatalf("os.Stat failed with %q, but os.IsNotExist(err) is false", err)
602 }
603 }
604
605 func TestBadNetPathError(t *testing.T) {
606 const ERROR_BAD_NETPATH = syscall.Errno(53)
607 if !os.IsNotExist(ERROR_BAD_NETPATH) {
608 t.Fatal("os.IsNotExist(syscall.Errno(53)) is false, but want true")
609 }
610 }
611
612 func TestStatDir(t *testing.T) {
613 t.Chdir(t.TempDir())
614
615 f, err := os.Open(".")
616 if err != nil {
617 t.Fatal(err)
618 }
619 defer f.Close()
620
621 fi, err := f.Stat()
622 if err != nil {
623 t.Fatal(err)
624 }
625
626 err = os.Chdir("..")
627 if err != nil {
628 t.Fatal(err)
629 }
630
631 fi2, err := f.Stat()
632 if err != nil {
633 t.Fatal(err)
634 }
635
636 if !os.SameFile(fi, fi2) {
637 t.Fatal("race condition occurred")
638 }
639 }
640
641 func TestOpenVolumeName(t *testing.T) {
642 tmpdir := t.TempDir()
643 t.Chdir(tmpdir)
644
645 want := []string{"file1", "file2", "file3", "gopher.txt"}
646 slices.Sort(want)
647 for _, name := range want {
648 err := os.WriteFile(filepath.Join(tmpdir, name), nil, 0777)
649 if err != nil {
650 t.Fatal(err)
651 }
652 }
653
654 f, err := os.Open(filepath.VolumeName(tmpdir))
655 if err != nil {
656 t.Fatal(err)
657 }
658 defer f.Close()
659
660 have, err := f.Readdirnames(-1)
661 if err != nil {
662 t.Fatal(err)
663 }
664 slices.Sort(have)
665
666 if strings.Join(want, "/") != strings.Join(have, "/") {
667 t.Fatalf("unexpected file list %q, want %q", have, want)
668 }
669 }
670
671 func TestDeleteReadOnly(t *testing.T) {
672 t.Parallel()
673
674 tmpdir := t.TempDir()
675 p := filepath.Join(tmpdir, "a")
676
677 f, err := os.OpenFile(p, os.O_CREATE, 0400)
678 if err != nil {
679 t.Fatal(err)
680 }
681 f.Close()
682
683 if err = os.Chmod(p, 0400); err != nil {
684 t.Fatal(err)
685 }
686 if err = os.Remove(p); err != nil {
687 t.Fatal(err)
688 }
689 }
690
691 func TestReadStdin(t *testing.T) {
692 old := poll.ReadConsole
693 defer func() {
694 poll.ReadConsole = old
695 }()
696
697 p, err := syscall.GetCurrentProcess()
698 if err != nil {
699 t.Fatalf("Unable to get handle to current process: %v", err)
700 }
701 var stdinDuplicate syscall.Handle
702 err = syscall.DuplicateHandle(p, syscall.Handle(syscall.Stdin), p, &stdinDuplicate, 0, false, syscall.DUPLICATE_SAME_ACCESS)
703 if err != nil {
704 t.Fatalf("Unable to duplicate stdin: %v", err)
705 }
706 testConsole := os.NewConsoleFile(stdinDuplicate, "test")
707
708 var tests = []string{
709 "abc",
710 "äöü",
711 "\u3042",
712 "“hi”™",
713 "hello\x1aworld",
714 "\U0001F648\U0001F649\U0001F64A",
715 }
716
717 for _, consoleSize := range []int{1, 2, 3, 10, 16, 100, 1000} {
718 for _, readSize := range []int{1, 2, 3, 4, 5, 8, 10, 16, 20, 50, 100} {
719 for _, s := range tests {
720 t.Run(fmt.Sprintf("c%d/r%d/%s", consoleSize, readSize, s), func(t *testing.T) {
721 s16 := utf16.Encode([]rune(s))
722 poll.ReadConsole = func(h syscall.Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) error {
723 if inputControl != nil {
724 t.Fatalf("inputControl not nil")
725 }
726 n := int(toread)
727 if n > consoleSize {
728 n = consoleSize
729 }
730 n = copy((*[10000]uint16)(unsafe.Pointer(buf))[:n:n], s16)
731 s16 = s16[n:]
732 *read = uint32(n)
733 t.Logf("read %d -> %d", toread, *read)
734 return nil
735 }
736
737 var all []string
738 var buf []byte
739 chunk := make([]byte, readSize)
740 for {
741 n, err := testConsole.Read(chunk)
742 buf = append(buf, chunk[:n]...)
743 if err == io.EOF {
744 all = append(all, string(buf))
745 if len(all) >= 5 {
746 break
747 }
748 buf = buf[:0]
749 } else if err != nil {
750 t.Fatalf("reading %q: error: %v", s, err)
751 }
752 if len(buf) >= 2000 {
753 t.Fatalf("reading %q: stuck in loop: %q", s, buf)
754 }
755 }
756
757 want := strings.Split(s, "\x1a")
758 for len(want) < 5 {
759 want = append(want, "")
760 }
761 if !slices.Equal(all, want) {
762 t.Errorf("reading %q:\nhave %x\nwant %x", s, all, want)
763 }
764 })
765 }
766 }
767 }
768 }
769
770 func TestStatPagefile(t *testing.T) {
771 t.Parallel()
772
773 const path = `c:\pagefile.sys`
774 fi, err := os.Stat(path)
775 if err == nil {
776 if fi.Name() == "" {
777 t.Fatalf("Stat(%q).Name() is empty", path)
778 }
779 t.Logf("Stat(%q).Size() = %v", path, fi.Size())
780 return
781 }
782 if os.IsNotExist(err) {
783 t.Skip(`skipping because c:\pagefile.sys is not found`)
784 }
785 t.Fatal(err)
786 }
787
788
789
790 func syscallCommandLineToArgv(cmd string) ([]string, error) {
791 var argc int32
792 argv, err := syscall.CommandLineToArgv(&syscall.StringToUTF16(cmd)[0], &argc)
793 if err != nil {
794 return nil, err
795 }
796 defer syscall.LocalFree(syscall.Handle(uintptr(unsafe.Pointer(argv))))
797
798 var args []string
799 for _, v := range (*argv)[:argc] {
800 args = append(args, syscall.UTF16ToString((*v)[:]))
801 }
802 return args, nil
803 }
804
805
806
807
808 func compareCommandLineToArgvWithSyscall(t *testing.T, cmd string) {
809 syscallArgs, err := syscallCommandLineToArgv(cmd)
810 if err != nil {
811 t.Fatal(err)
812 }
813 args := os.CommandLineToArgv(cmd)
814 if want, have := fmt.Sprintf("%q", syscallArgs), fmt.Sprintf("%q", args); want != have {
815 t.Errorf("testing os.commandLineToArgv(%q) failed: have %q want %q", cmd, args, syscallArgs)
816 return
817 }
818 }
819
820 func TestCmdArgs(t *testing.T) {
821 if testing.Short() {
822 t.Skipf("in short mode; skipping test that builds a binary")
823 }
824 t.Parallel()
825
826 tmpdir := t.TempDir()
827
828 const prog = `
829 package main
830
831 import (
832 "fmt"
833 "os"
834 )
835
836 func main() {
837 fmt.Printf("%q", os.Args)
838 }
839 `
840 src := filepath.Join(tmpdir, "main.go")
841 if err := os.WriteFile(src, []byte(prog), 0666); err != nil {
842 t.Fatal(err)
843 }
844
845 exe := filepath.Join(tmpdir, "main.exe")
846 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, src)
847 cmd.Dir = tmpdir
848 out, err := cmd.CombinedOutput()
849 if err != nil {
850 t.Fatalf("building main.exe failed: %v\n%s", err, out)
851 }
852
853 var cmds = []string{
854 ``,
855 ` a b c`,
856 ` "`,
857 ` ""`,
858 ` """`,
859 ` "" a`,
860 ` "123"`,
861 ` \"123\"`,
862 ` \"123 456\"`,
863 ` \\"`,
864 ` \\\"`,
865 ` \\\\\"`,
866 ` \\\"x`,
867 ` """"\""\\\"`,
868 ` abc`,
869 ` \\\\\""x"""y z`,
870 "\tb\t\"x\ty\"",
871 ` "Брад" d e`,
872
873 ` "abc" d e`,
874 ` a\\b d"e f"g h`,
875 ` a\\\"b c d`,
876 ` a\\\\"b c" d e`,
877
878
879 ` CallMeIshmael`,
880 ` "Call Me Ishmael"`,
881 ` Cal"l Me I"shmael`,
882 ` CallMe\"Ishmael`,
883 ` "CallMe\"Ishmael"`,
884 ` "Call Me Ishmael\\"`,
885 ` "CallMe\\\"Ishmael"`,
886 ` a\\\b`,
887 ` "a\\\b"`,
888
889 ` "\"Call Me Ishmael\""`,
890 ` "C:\TEST A\\"`,
891 ` "\"C:\TEST A\\\""`,
892
893 ` "a b c" d e`,
894 ` "ab\"c" "\\" d`,
895 ` a\\\b d"e f"g h`,
896 ` a\\\"b c d`,
897 ` a\\\\"b c" d e`,
898
899 ` "a b c""`,
900 ` """CallMeIshmael""" b c`,
901 ` """Call Me Ishmael"""`,
902 ` """"Call Me Ishmael"" b c`,
903 }
904 for _, cmd := range cmds {
905 compareCommandLineToArgvWithSyscall(t, "test"+cmd)
906 compareCommandLineToArgvWithSyscall(t, `"cmd line"`+cmd)
907 compareCommandLineToArgvWithSyscall(t, exe+cmd)
908
909
910 args := os.CommandLineToArgv(exe + cmd)
911 out, err := testenv.Command(t, args[0], args[1:]...).CombinedOutput()
912 if err != nil {
913 t.Fatalf("running %q failed: %v\n%v", args, err, string(out))
914 }
915 if want, have := fmt.Sprintf("%q", args), string(out); want != have {
916 t.Errorf("wrong output of executing %q: have %q want %q", args, have, want)
917 continue
918 }
919 }
920 }
921
922 func findOneDriveDir() (string, error) {
923
924 const onedrivekey = `SOFTWARE\Microsoft\OneDrive`
925 k, err := registry.OpenKey(registry.CURRENT_USER, onedrivekey, registry.READ)
926 if err != nil {
927 return "", fmt.Errorf("OpenKey(%q) failed: %v", onedrivekey, err)
928 }
929 defer k.Close()
930
931 path, valtype, err := k.GetStringValue("UserFolder")
932 if err != nil {
933 return "", fmt.Errorf("reading UserFolder failed: %v", err)
934 }
935
936
937
938 if valtype == registry.EXPAND_SZ || valtype == registry.SZ {
939 expanded, err := registry.ExpandString(path)
940 if err != nil {
941 return "", fmt.Errorf("expanding UserFolder failed: %v", err)
942 }
943 path = expanded
944 }
945
946 return path, nil
947 }
948
949
950 func TestOneDrive(t *testing.T) {
951 t.Parallel()
952
953 dir, err := findOneDriveDir()
954 if err != nil {
955 t.Skipf("Skipping, because we did not find OneDrive directory: %v", err)
956 }
957 testDirStats(t, dir)
958 }
959
960 func TestWindowsDevNullFile(t *testing.T) {
961 t.Parallel()
962
963 f1, err := os.Open("NUL")
964 if err != nil {
965 t.Fatal(err)
966 }
967 defer f1.Close()
968
969 fi1, err := f1.Stat()
970 if err != nil {
971 t.Fatal(err)
972 }
973
974 f2, err := os.Open("nul")
975 if err != nil {
976 t.Fatal(err)
977 }
978 defer f2.Close()
979
980 fi2, err := f2.Stat()
981 if err != nil {
982 t.Fatal(err)
983 }
984
985 if !os.SameFile(fi1, fi2) {
986 t.Errorf(`"NUL" and "nul" are not the same file`)
987 }
988 }
989
990 func TestFileStatNUL(t *testing.T) {
991 t.Parallel()
992
993 f, err := os.Open("NUL")
994 if err != nil {
995 t.Fatal(err)
996 }
997 defer f.Close()
998
999 fi, err := f.Stat()
1000 if err != nil {
1001 t.Fatal(err)
1002 }
1003 if got, want := fi.Mode(), os.ModeDevice|os.ModeCharDevice|0666; got != want {
1004 t.Errorf("Open(%q).Stat().Mode() = %v, want %v", "NUL", got, want)
1005 }
1006 }
1007
1008 func TestStatNUL(t *testing.T) {
1009 t.Parallel()
1010
1011 fi, err := os.Stat("NUL")
1012 if err != nil {
1013 t.Fatal(err)
1014 }
1015 if got, want := fi.Mode(), os.ModeDevice|os.ModeCharDevice|0666; got != want {
1016 t.Errorf("Stat(%q).Mode() = %v, want %v", "NUL", got, want)
1017 }
1018 }
1019
1020
1021
1022
1023 func TestSymlinkCreation(t *testing.T) {
1024 if !testenv.HasSymlink() {
1025 t.Skip("skipping test; no symlink support")
1026 }
1027 t.Parallel()
1028
1029 temp := t.TempDir()
1030 dummyFile := filepath.Join(temp, "file")
1031 if err := os.WriteFile(dummyFile, []byte(""), 0644); err != nil {
1032 t.Fatal(err)
1033 }
1034
1035 linkFile := filepath.Join(temp, "link")
1036 if err := os.Symlink(dummyFile, linkFile); err != nil {
1037 t.Fatal(err)
1038 }
1039 }
1040
1041
1042
1043
1044
1045 func TestRootRelativeDirSymlink(t *testing.T) {
1046 testenv.MustHaveSymlink(t)
1047 t.Parallel()
1048
1049 temp := t.TempDir()
1050 dir := filepath.Join(temp, "dir")
1051 if err := os.Mkdir(dir, 0755); err != nil {
1052 t.Fatal(err)
1053 }
1054
1055 volumeRelDir := strings.TrimPrefix(dir, filepath.VolumeName(dir))
1056
1057 link := filepath.Join(temp, "link")
1058 err := os.Symlink(volumeRelDir, link)
1059 if err != nil {
1060 t.Fatal(err)
1061 }
1062 t.Logf("Symlink(%#q, %#q)", volumeRelDir, link)
1063
1064 f, err := os.Open(link)
1065 if err != nil {
1066 t.Fatal(err)
1067 }
1068 defer f.Close()
1069 if fi, err := f.Stat(); err != nil {
1070 t.Fatal(err)
1071 } else if !fi.IsDir() {
1072 t.Errorf("Open(%#q).Stat().IsDir() = false; want true", f.Name())
1073 }
1074 }
1075
1076
1077
1078
1079
1080 func TestWorkingDirectoryRelativeSymlink(t *testing.T) {
1081 testenv.MustHaveSymlink(t)
1082
1083
1084 temp := t.TempDir()
1085 if v := filepath.VolumeName(temp); len(v) < 2 || v[1] != ':' {
1086 t.Skipf("Can't test relative symlinks: t.TempDir() (%#q) does not begin with a drive letter.", temp)
1087 }
1088
1089 absDir := filepath.Join(temp, `dir\sub`)
1090 if err := os.MkdirAll(absDir, 0755); err != nil {
1091 t.Fatal(err)
1092 }
1093
1094
1095
1096 oldwd, err := os.Getwd()
1097 if err != nil {
1098 t.Fatal(err)
1099 }
1100 t.Chdir(temp)
1101 t.Logf("Chdir(%#q)", temp)
1102
1103 wdRelDir := filepath.VolumeName(temp) + `dir\sub`
1104 absLink := filepath.Join(temp, "link")
1105 err = os.Symlink(wdRelDir, absLink)
1106 if err != nil {
1107 t.Fatal(err)
1108 }
1109 t.Logf("Symlink(%#q, %#q)", wdRelDir, absLink)
1110
1111
1112
1113
1114 if err := os.Chdir(oldwd); err != nil {
1115 t.Fatal(err)
1116 }
1117 t.Logf("Chdir(%#q)", oldwd)
1118
1119 resolved, err := os.Readlink(absLink)
1120 if err != nil {
1121 t.Errorf("Readlink(%#q): %v", absLink, err)
1122 } else if resolved != absDir {
1123 t.Errorf("Readlink(%#q) = %#q; want %#q", absLink, resolved, absDir)
1124 }
1125
1126 linkFile, err := os.Open(absLink)
1127 if err != nil {
1128 t.Fatal(err)
1129 }
1130 defer linkFile.Close()
1131
1132 linkInfo, err := linkFile.Stat()
1133 if err != nil {
1134 t.Fatal(err)
1135 }
1136 if !linkInfo.IsDir() {
1137 t.Errorf("Open(%#q).Stat().IsDir() = false; want true", absLink)
1138 }
1139
1140 absInfo, err := os.Stat(absDir)
1141 if err != nil {
1142 t.Fatal(err)
1143 }
1144
1145 if !os.SameFile(absInfo, linkInfo) {
1146 t.Errorf("SameFile(Stat(%#q), Open(%#q).Stat()) = false; want true", absDir, absLink)
1147 }
1148 }
1149
1150
1151 func TestStatOfInvalidName(t *testing.T) {
1152 t.Parallel()
1153
1154 _, err := os.Stat("*.go")
1155 if err == nil {
1156 t.Fatal(`os.Stat("*.go") unexpectedly succeeded`)
1157 }
1158 }
1159
1160
1161
1162
1163 func findUnusedDriveLetter() (string, error) {
1164
1165
1166 for l := 'Z'; l >= 'D'; l-- {
1167 p := string(l) + `:\`
1168 _, err := os.Stat(p)
1169 if os.IsNotExist(err) {
1170 return p, nil
1171 }
1172 }
1173 return "", errors.New("Could not find unused drive letter.")
1174 }
1175
1176 func TestRootDirAsTemp(t *testing.T) {
1177 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
1178 fmt.Print(os.TempDir())
1179 os.Exit(0)
1180 }
1181
1182 testenv.MustHaveExec(t)
1183 t.Parallel()
1184
1185 exe := testenv.Executable(t)
1186
1187 newtmp, err := findUnusedDriveLetter()
1188 if err != nil {
1189 t.Skip(err)
1190 }
1191
1192 cmd := testenv.Command(t, exe, "-test.run=^TestRootDirAsTemp$")
1193 cmd.Env = cmd.Environ()
1194 cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
1195 cmd.Env = append(cmd.Env, "TMP="+newtmp)
1196 cmd.Env = append(cmd.Env, "TEMP="+newtmp)
1197 output, err := cmd.CombinedOutput()
1198 if err != nil {
1199 t.Fatalf("Failed to spawn child process: %v %q", err, string(output))
1200 }
1201 if want, have := newtmp, string(output); have != want {
1202 t.Fatalf("unexpected child process output %q, want %q", have, want)
1203 }
1204 }
1205
1206
1207
1208 func replaceDriveWithVolumeID(t *testing.T, path string) string {
1209 t.Helper()
1210 cmd := testenv.Command(t, "cmd", "/c", "mountvol", filepath.VolumeName(path), "/L")
1211 out, err := cmd.CombinedOutput()
1212 if err != nil {
1213 t.Fatalf("%v: %v\n%s", cmd, err, out)
1214 }
1215 vol := strings.Trim(string(out), " \n\r")
1216 return filepath.Join(vol, path[len(filepath.VolumeName(path)):])
1217 }
1218
1219 func TestReadlink(t *testing.T) {
1220 tests := []struct {
1221 junction bool
1222 dir bool
1223 drive bool
1224 relative bool
1225 }{
1226 {junction: true, dir: true, drive: true, relative: false},
1227 {junction: true, dir: true, drive: false, relative: false},
1228 {junction: true, dir: true, drive: false, relative: true},
1229 {junction: false, dir: true, drive: true, relative: false},
1230 {junction: false, dir: true, drive: false, relative: false},
1231 {junction: false, dir: true, drive: false, relative: true},
1232 {junction: false, dir: false, drive: true, relative: false},
1233 {junction: false, dir: false, drive: false, relative: false},
1234 {junction: false, dir: false, drive: false, relative: true},
1235 }
1236 for _, tt := range tests {
1237 tt := tt
1238 var name string
1239 if tt.junction {
1240 name = "junction"
1241 } else {
1242 name = "symlink"
1243 }
1244 if tt.dir {
1245 name += "_dir"
1246 } else {
1247 name += "_file"
1248 }
1249 if tt.drive {
1250 name += "_drive"
1251 } else {
1252 name += "_volume"
1253 }
1254 if tt.relative {
1255 name += "_relative"
1256 } else {
1257 name += "_absolute"
1258 }
1259
1260 t.Run(name, func(t *testing.T) {
1261 if !tt.junction {
1262 testenv.MustHaveSymlink(t)
1263 }
1264 if !tt.relative {
1265 t.Parallel()
1266 }
1267
1268 tmpdir, err := filepath.EvalSymlinks(t.TempDir())
1269 if err != nil {
1270 t.Fatal(err)
1271 }
1272 link := filepath.Join(tmpdir, "link")
1273 target := filepath.Join(tmpdir, "target")
1274 if tt.dir {
1275 if err := os.MkdirAll(target, 0777); err != nil {
1276 t.Fatal(err)
1277 }
1278 } else {
1279 if err := os.WriteFile(target, nil, 0666); err != nil {
1280 t.Fatal(err)
1281 }
1282 }
1283 var want string
1284 if tt.relative {
1285 relTarget := filepath.Base(target)
1286 if tt.junction {
1287 want = target
1288 } else {
1289 want = relTarget
1290 }
1291 t.Chdir(tmpdir)
1292 link = filepath.Base(link)
1293 target = relTarget
1294 } else {
1295 if tt.drive {
1296 want = target
1297 } else {
1298 volTarget := replaceDriveWithVolumeID(t, target)
1299 if winreadlinkvolume.Value() == "0" {
1300 want = target
1301 } else {
1302 want = volTarget
1303 }
1304 target = volTarget
1305 }
1306 }
1307 if tt.junction {
1308 cmd := testenv.Command(t, "cmd", "/c", "mklink", "/J", link, target)
1309 if out, err := cmd.CombinedOutput(); err != nil {
1310 t.Fatalf("%v: %v\n%s", cmd, err, out)
1311 }
1312 } else {
1313 if err := os.Symlink(target, link); err != nil {
1314 t.Fatalf("Symlink(%#q, %#q): %v", target, link, err)
1315 }
1316 }
1317 got, err := os.Readlink(link)
1318 if err != nil {
1319 t.Fatal(err)
1320 }
1321 if got != want {
1322 t.Fatalf("Readlink(%#q) = %#q; want %#q", target, got, want)
1323 }
1324 })
1325 }
1326 }
1327
1328 func TestOpenDirTOCTOU(t *testing.T) {
1329 t.Parallel()
1330
1331
1332
1333 tmpdir := t.TempDir()
1334 dir := filepath.Join(tmpdir, "dir")
1335 if err := os.Mkdir(dir, 0777); err != nil {
1336 t.Fatal(err)
1337 }
1338 f, err := os.Open(dir)
1339 if err != nil {
1340 t.Fatal(err)
1341 }
1342 newpath := filepath.Join(tmpdir, "dir1")
1343 err = os.Rename(dir, newpath)
1344 if err == nil || !errors.Is(err, windows.ERROR_SHARING_VIOLATION) {
1345 f.Close()
1346 t.Fatalf("Rename(%q, %q) = %v; want windows.ERROR_SHARING_VIOLATION", dir, newpath, err)
1347 }
1348 f.Close()
1349 err = os.Rename(dir, newpath)
1350 if err != nil {
1351 t.Error(err)
1352 }
1353 }
1354
1355 func TestAppExecLinkStat(t *testing.T) {
1356
1357
1358
1359
1360 appdata := os.Getenv("LOCALAPPDATA")
1361 if appdata == "" {
1362 t.Skipf("skipping: LOCALAPPDATA not set")
1363 }
1364
1365 pythonExeName := "python3.exe"
1366 pythonPath := filepath.Join(appdata, `Microsoft\WindowsApps`, pythonExeName)
1367
1368 lfi, err := os.Lstat(pythonPath)
1369 if err != nil {
1370 t.Skip("skipping test, because Python 3 is not installed via the Windows App Store on this system; see https://golang.org/issue/42919")
1371 }
1372
1373
1374
1375 linkName, err := os.Readlink(pythonPath)
1376 if err == nil {
1377 t.Errorf("os.Readlink(%q) = %q, but expected an error\n(should be an APPEXECLINK reparse point, not a symlink)", pythonPath, linkName)
1378 }
1379
1380 sfi, err := os.Stat(pythonPath)
1381 if err != nil {
1382 t.Fatalf("Stat %s: %v", pythonPath, err)
1383 }
1384
1385 if lfi.Name() != sfi.Name() {
1386 t.Logf("os.Lstat(%q) = %+v", pythonPath, lfi)
1387 t.Logf("os.Stat(%q) = %+v", pythonPath, sfi)
1388 t.Errorf("files should be same")
1389 }
1390
1391 if lfi.Name() != pythonExeName {
1392 t.Errorf("Stat %s: got %q, but wanted %q", pythonPath, lfi.Name(), pythonExeName)
1393 }
1394 if tp := lfi.Mode().Type(); tp != fs.ModeIrregular {
1395
1396
1397 t.Errorf("%q should not be a an irregular file (mode=0x%x)", pythonPath, uint32(tp))
1398 }
1399
1400 if sfi.Name() != pythonExeName {
1401 t.Errorf("Stat %s: got %q, but wanted %q", pythonPath, sfi.Name(), pythonExeName)
1402 }
1403 if m := sfi.Mode(); m&fs.ModeSymlink != 0 {
1404 t.Errorf("%q should be a file, not a link (mode=0x%x)", pythonPath, uint32(m))
1405 }
1406 if m := sfi.Mode(); m&fs.ModeDir != 0 {
1407 t.Errorf("%q should be a file, not a directory (mode=0x%x)", pythonPath, uint32(m))
1408 }
1409 if m := sfi.Mode(); m&fs.ModeIrregular == 0 {
1410
1411
1412 t.Errorf("%q should not be a regular file (mode=0x%x)", pythonPath, uint32(m))
1413 }
1414
1415 p, err := exec.LookPath(pythonPath)
1416 if err != nil {
1417 t.Errorf("exec.LookPath(%q): %v", pythonPath, err)
1418 }
1419 if p != pythonPath {
1420 t.Errorf("exec.LookPath(%q) = %q; want %q", pythonPath, p, pythonPath)
1421 }
1422 }
1423
1424 func TestIllformedUTF16FileName(t *testing.T) {
1425 dir := t.TempDir()
1426 const sep = string(os.PathSeparator)
1427 if !strings.HasSuffix(dir, sep) {
1428 dir += sep
1429 }
1430
1431
1432 namew := []uint16{0x2e, 0xdc6d, 0xdc73, 0xdc79, 0xdc73, 0x30, 0x30, 0x30, 0x31, 0}
1433
1434
1435
1436
1437 dirw := utf16.Encode([]rune(dir))
1438 pathw := append(dirw, namew...)
1439 fd, err := syscall.CreateFile(&pathw[0], syscall.GENERIC_ALL, 0, nil, syscall.CREATE_NEW, 0, 0)
1440 if err != nil {
1441 t.Fatal(err)
1442 }
1443 syscall.CloseHandle(fd)
1444
1445 name := syscall.UTF16ToString(namew)
1446 path := filepath.Join(dir, name)
1447
1448 fi, err := os.Lstat(path)
1449 if err != nil {
1450 t.Fatal(err)
1451 }
1452 if got := fi.Name(); got != name {
1453 t.Errorf("got %q, want %q", got, name)
1454 }
1455
1456 f, err := os.Open(dir)
1457 if err != nil {
1458 t.Fatal(err)
1459 }
1460 files, err := f.Readdirnames(0)
1461 f.Close()
1462 if err != nil {
1463 t.Fatal(err)
1464 }
1465 if !slices.Contains(files, name) {
1466 t.Error("file not listed")
1467 }
1468
1469
1470 err = os.RemoveAll(dir)
1471 if err != nil {
1472 t.Error(err)
1473 }
1474 }
1475
1476 func TestUTF16Alloc(t *testing.T) {
1477 allowsPerRun := func(want int, f func()) {
1478 t.Helper()
1479 got := int(testing.AllocsPerRun(5, f))
1480 if got != want {
1481 t.Errorf("got %d allocs, want %d", got, want)
1482 }
1483 }
1484 allowsPerRun(1, func() {
1485 syscall.UTF16ToString([]uint16{'a', 'b', 'c'})
1486 })
1487 allowsPerRun(1, func() {
1488 syscall.UTF16FromString("abc")
1489 })
1490 }
1491
1492 func TestNewFileInvalid(t *testing.T) {
1493 t.Parallel()
1494 if f := os.NewFile(uintptr(syscall.InvalidHandle), "invalid"); f != nil {
1495 t.Errorf("NewFile(InvalidHandle) got %v want nil", f)
1496 }
1497 }
1498
1499 func TestReadDirPipe(t *testing.T) {
1500 dir := `\\.\pipe\`
1501 fi, err := os.Stat(dir)
1502 if err != nil || !fi.IsDir() {
1503 t.Skipf("%s is not a directory", dir)
1504 }
1505 _, err = os.ReadDir(dir)
1506 if err != nil {
1507 t.Errorf("ReadDir(%q) = %v", dir, err)
1508 }
1509 }
1510
1511 func TestReadDirNoFileID(t *testing.T) {
1512 *os.AllowReadDirFileID = false
1513 defer func() { *os.AllowReadDirFileID = true }()
1514
1515 dir := t.TempDir()
1516 pathA := filepath.Join(dir, "a")
1517 pathB := filepath.Join(dir, "b")
1518 if err := os.WriteFile(pathA, nil, 0666); err != nil {
1519 t.Fatal(err)
1520 }
1521 if err := os.WriteFile(pathB, nil, 0666); err != nil {
1522 t.Fatal(err)
1523 }
1524
1525 files, err := os.ReadDir(dir)
1526 if err != nil {
1527 t.Fatal(err)
1528 }
1529 if len(files) != 2 {
1530 t.Fatalf("ReadDir(%q) = %v; want 2 files", dir, files)
1531 }
1532
1533
1534 f1, err := files[0].Info()
1535 if err != nil {
1536 t.Fatal(err)
1537 }
1538 f2, err := files[1].Info()
1539 if err != nil {
1540 t.Fatal(err)
1541 }
1542 if !os.SameFile(f1, f1) {
1543 t.Errorf("SameFile(%v, %v) = false; want true", f1, f1)
1544 }
1545 if !os.SameFile(f2, f2) {
1546 t.Errorf("SameFile(%v, %v) = false; want true", f2, f2)
1547 }
1548 if os.SameFile(f1, f2) {
1549 t.Errorf("SameFile(%v, %v) = true; want false", f1, f2)
1550 }
1551
1552
1553 f1s, err := os.Stat(pathA)
1554 if err != nil {
1555 t.Fatal(err)
1556 }
1557 f2s, err := os.Stat(pathB)
1558 if err != nil {
1559 t.Fatal(err)
1560 }
1561 if !os.SameFile(f1, f1s) {
1562 t.Errorf("SameFile(%v, %v) = false; want true", f1, f1s)
1563 }
1564 if !os.SameFile(f2, f2s) {
1565 t.Errorf("SameFile(%v, %v) = false; want true", f2, f2s)
1566 }
1567 }
1568
1569 func TestReadWriteFileOverlapped(t *testing.T) {
1570
1571 t.Parallel()
1572
1573 name := filepath.Join(t.TempDir(), "test.txt")
1574 wname, err := syscall.UTF16PtrFromString(name)
1575 if err != nil {
1576 t.Fatal(err)
1577 }
1578 h, err := syscall.CreateFile(wname, syscall.GENERIC_ALL, 0, nil, syscall.CREATE_NEW, syscall.FILE_ATTRIBUTE_NORMAL|syscall.FILE_FLAG_OVERLAPPED, 0)
1579 if err != nil {
1580 t.Fatal(err)
1581 }
1582 f := os.NewFile(uintptr(h), name)
1583 defer f.Close()
1584
1585 data := []byte("test")
1586 n, err := f.Write(data)
1587 if err != nil {
1588 t.Fatal(err)
1589 }
1590 if n != len(data) {
1591 t.Fatalf("Write = %d; want %d", n, len(data))
1592 }
1593
1594 if _, err := f.Seek(0, io.SeekStart); err != nil {
1595 t.Fatal(err)
1596 }
1597
1598 got, err := io.ReadAll(f)
1599 if err != nil {
1600 t.Fatal(err)
1601 }
1602 if !bytes.Equal(got, data) {
1603 t.Fatalf("Read = %q; want %q", got, data)
1604 }
1605 }
1606
1607 func TestStdinOverlappedPipe(t *testing.T) {
1608
1609
1610 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
1611 var buf string
1612 _, err := fmt.Scanln(&buf)
1613 if err != nil {
1614 fmt.Print(err)
1615 os.Exit(1)
1616 }
1617 fmt.Println(buf)
1618 os.Exit(0)
1619 }
1620
1621 t.Parallel()
1622 name := pipeName()
1623
1624
1625 r := newPipe(t, name, false, true)
1626 defer r.Close()
1627
1628
1629 w, err := os.OpenFile(name, os.O_WRONLY, 0666)
1630 if err != nil {
1631 t.Fatal(err)
1632 }
1633 defer w.Close()
1634
1635
1636 want := []byte("test\n")
1637 if _, err := w.Write(want); err != nil {
1638 t.Fatal(err)
1639 }
1640
1641
1642
1643 cmd := testenv.Command(t, testenv.Executable(t), fmt.Sprintf("-test.run=^%s$", t.Name()), "-test.v")
1644 cmd = testenv.CleanCmdEnv(cmd)
1645 cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
1646 cmd.Stdin = r
1647 got, err := cmd.CombinedOutput()
1648 if err != nil {
1649 t.Fatalf("running %q failed: %v\n%s", cmd, err, got)
1650 }
1651
1652 if !bytes.Contains(got, want) {
1653 t.Fatalf("output %q does not contain %q", got, want)
1654 }
1655 }
1656
1657 func newFileOverlapped(t testing.TB, name string, overlapped bool) *os.File {
1658 namep, err := syscall.UTF16PtrFromString(name)
1659 if err != nil {
1660 t.Fatal(err)
1661 }
1662 flags := syscall.FILE_ATTRIBUTE_NORMAL
1663 if overlapped {
1664 flags |= syscall.FILE_FLAG_OVERLAPPED
1665 }
1666 h, err := syscall.CreateFile(namep,
1667 syscall.GENERIC_READ|syscall.GENERIC_WRITE,
1668 syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_READ,
1669 nil, syscall.OPEN_ALWAYS, uint32(flags), 0)
1670 if err != nil {
1671 t.Fatal(err)
1672 }
1673 f := os.NewFile(uintptr(h), name)
1674 t.Cleanup(func() {
1675 if err := f.Close(); err != nil && !errors.Is(err, os.ErrClosed) {
1676 t.Fatal(err)
1677 }
1678 })
1679 return f
1680 }
1681
1682 var currentProcess = sync.OnceValue(func() string {
1683
1684 return strconv.FormatUint(uint64(os.Getpid()), 10)
1685 })
1686
1687 var pipeCounter atomic.Uint64
1688
1689 func newBytePipe(t testing.TB, name string, overlapped bool) *os.File {
1690 return newPipe(t, name, false, overlapped)
1691 }
1692
1693 func newMessagePipe(t testing.TB, name string, overlapped bool) *os.File {
1694 return newPipe(t, name, true, overlapped)
1695 }
1696
1697 func pipeName() string {
1698 return `\\.\pipe\go-os-test-` + currentProcess() + `-` + strconv.FormatUint(pipeCounter.Add(1), 10)
1699 }
1700
1701 func newPipe(t testing.TB, name string, message, overlapped bool) *os.File {
1702 wname, err := syscall.UTF16PtrFromString(name)
1703 if err != nil {
1704 t.Fatal(err)
1705 }
1706
1707 flags := windows.PIPE_ACCESS_DUPLEX
1708 if overlapped {
1709 flags |= syscall.FILE_FLAG_OVERLAPPED
1710 }
1711 typ := windows.PIPE_TYPE_BYTE | windows.PIPE_READMODE_BYTE
1712 if message {
1713 typ = windows.PIPE_TYPE_MESSAGE | windows.PIPE_READMODE_MESSAGE
1714 }
1715 h, err := windows.CreateNamedPipe(wname, uint32(flags), uint32(typ), 1, 4096, 4096, 0, nil)
1716 if err != nil {
1717 t.Fatal(err)
1718 }
1719 f := os.NewFile(uintptr(h), name)
1720 t.Cleanup(func() {
1721 if err := f.Close(); err != nil && !errors.Is(err, os.ErrClosed) {
1722 t.Fatal(err)
1723 }
1724 })
1725 return f
1726 }
1727
1728 func testReadWrite(t *testing.T, fdr, fdw *os.File) {
1729 write := make(chan string, 1)
1730 read := make(chan struct{}, 1)
1731 go func() {
1732 for s := range write {
1733 n, err := fdw.Write([]byte(s))
1734 read <- struct{}{}
1735 if err != nil {
1736 t.Error(err)
1737 }
1738 if n != len(s) {
1739 t.Errorf("expected to write %d bytes, got %d", len(s), n)
1740 }
1741 }
1742 }()
1743 for i := range 10 {
1744 s := strconv.Itoa(i)
1745 write <- s
1746 <-read
1747 buf := make([]byte, len(s))
1748 _, err := io.ReadFull(fdr, buf)
1749 if err != nil {
1750 t.Fatalf("read failed: %v", err)
1751 }
1752 if !bytes.Equal(buf, []byte(s)) {
1753 t.Fatalf("expected %q, got %q", s, buf)
1754 }
1755 }
1756 close(read)
1757 close(write)
1758 }
1759
1760 func testPreadPwrite(t *testing.T, fdr, fdw *os.File) {
1761 type op struct {
1762 s string
1763 off int64
1764 }
1765 write := make(chan op, 1)
1766 read := make(chan struct{}, 1)
1767 go func() {
1768 for o := range write {
1769 n, err := fdw.WriteAt([]byte(o.s), o.off)
1770 read <- struct{}{}
1771 if err != nil {
1772 t.Error(err)
1773 }
1774 if n != len(o.s) {
1775 t.Errorf("expected to write %d bytes, got %d", len(o.s), n)
1776 }
1777 }
1778 }()
1779 for i := range 10 {
1780 off := int64(i % 3)
1781 s := strconv.Itoa(i)
1782 write <- op{s, off}
1783 <-read
1784 buf := make([]byte, len(s))
1785 n, err := fdr.ReadAt(buf, off)
1786 if err != nil {
1787 t.Fatal(err)
1788 }
1789 if n != len(s) {
1790 t.Fatalf("expected to read %d bytes, got %d", len(s), n)
1791 }
1792 if !bytes.Equal(buf, []byte(s)) {
1793 t.Fatalf("expected %q, got %q", s, buf)
1794 }
1795 }
1796 close(read)
1797 close(write)
1798 }
1799
1800 func testFileReadEOF(t *testing.T, f *os.File) {
1801 end, err := f.Seek(0, io.SeekEnd)
1802 if err != nil {
1803 t.Fatal(err)
1804 }
1805 var buf [1]byte
1806 n, err := f.Read(buf[:])
1807 if err != nil && err != io.EOF {
1808 t.Errorf("expected EOF, got %v", err)
1809 }
1810 if n != 0 {
1811 t.Errorf("expected 0 bytes, got %d", n)
1812 }
1813
1814 n, err = f.ReadAt(buf[:], end)
1815 if err != nil && err != io.EOF {
1816 t.Errorf("expected EOF, got %v", err)
1817 }
1818 if n != 0 {
1819 t.Errorf("expected 0 bytes, got %d", n)
1820 }
1821 }
1822
1823 func TestFile(t *testing.T) {
1824 t.Parallel()
1825 tests := []struct {
1826 name string
1827 overlappedRead bool
1828 overlappedWrite bool
1829 }{
1830 {"overlapped", true, true},
1831 {"overlapped-read", true, false},
1832 {"overlapped-write", false, true},
1833 {"sync", false, false},
1834 }
1835 for _, tt := range tests {
1836 t.Run(tt.name, func(t *testing.T) {
1837 t.Parallel()
1838 name := filepath.Join(t.TempDir(), "foo")
1839 rh := newFileOverlapped(t, name, tt.overlappedRead)
1840 wh := newFileOverlapped(t, name, tt.overlappedWrite)
1841 testReadWrite(t, rh, wh)
1842 testPreadPwrite(t, rh, wh)
1843 testFileReadEOF(t, rh)
1844 })
1845 }
1846 }
1847
1848 func TestPipe(t *testing.T) {
1849 t.Parallel()
1850 r, w, err := os.Pipe()
1851 if err != nil {
1852 t.Fatal(err)
1853 }
1854 defer func() {
1855 if err := r.Close(); err != nil {
1856 t.Fatal(err)
1857 }
1858 if err := w.Close(); err != nil {
1859 t.Fatal(err)
1860 }
1861 }()
1862 testReadWrite(t, r, w)
1863 }
1864
1865 func TestNamedPipe(t *testing.T) {
1866 t.Parallel()
1867 tests := []struct {
1868 name string
1869 overlappedRead bool
1870 overlappedWrite bool
1871 }{
1872 {"overlapped", true, true},
1873 {"overlapped-write", false, true},
1874 {"overlapped-read", true, false},
1875 {"sync", false, false},
1876 }
1877 for _, tt := range tests {
1878 t.Run(tt.name, func(t *testing.T) {
1879 t.Parallel()
1880 name := pipeName()
1881 pipe := newBytePipe(t, name, tt.overlappedWrite)
1882 file := newFileOverlapped(t, name, tt.overlappedRead)
1883 testReadWrite(t, pipe, file)
1884 })
1885 }
1886 }
1887
1888 func TestPipeMessageReadEOF(t *testing.T) {
1889 t.Parallel()
1890 name := pipeName()
1891 pipe := newMessagePipe(t, name, true)
1892 file := newFileOverlapped(t, name, true)
1893
1894 _, err := pipe.Write(nil)
1895 if err != nil {
1896 t.Error(err)
1897 }
1898
1899 var buf [10]byte
1900 n, err := file.Read(buf[:])
1901 if err != io.EOF {
1902 t.Errorf("expected EOF, got %v", err)
1903 }
1904 if n != 0 {
1905 t.Errorf("expected 0 bytes, got %d", n)
1906 }
1907 }
1908
1909 func TestPipeClosedEOF(t *testing.T) {
1910 t.Parallel()
1911 name := pipeName()
1912 pipe := newBytePipe(t, name, true)
1913 file := newFileOverlapped(t, name, true)
1914
1915 pipe.Close()
1916
1917 var buf [10]byte
1918 n, err := file.Read(buf[:])
1919 if err != io.EOF {
1920 t.Errorf("expected EOF, got %v", err)
1921 }
1922 if n != 0 {
1923 t.Errorf("expected 0 bytes, got %d", n)
1924 }
1925 }
1926
1927 func TestPipeReadTimeout(t *testing.T) {
1928 t.Parallel()
1929 name := pipeName()
1930 _ = newBytePipe(t, name, true)
1931 file := newFileOverlapped(t, name, true)
1932
1933 err := file.SetReadDeadline(time.Now().Add(time.Millisecond))
1934 if err != nil {
1935 t.Fatal(err)
1936 }
1937
1938 var buf [10]byte
1939 _, err = file.Read(buf[:])
1940 if !errors.Is(err, os.ErrDeadlineExceeded) {
1941 t.Errorf("expected deadline exceeded, got %v", err)
1942 }
1943 }
1944
1945 func TestPipeCanceled(t *testing.T) {
1946 t.Parallel()
1947 name := pipeName()
1948 _ = newBytePipe(t, name, true)
1949 file := newFileOverlapped(t, name, true)
1950 ch := make(chan struct{}, 1)
1951 go func() {
1952 for {
1953 select {
1954 case <-ch:
1955 return
1956 default:
1957 sc, err := file.SyscallConn()
1958 if err != nil {
1959 t.Error(err)
1960 return
1961 }
1962 if err := sc.Control(func(fd uintptr) {
1963 syscall.CancelIoEx(syscall.Handle(fd), nil)
1964 }); err != nil {
1965 t.Error(err)
1966 return
1967 }
1968 time.Sleep(100 * time.Millisecond)
1969 }
1970 }
1971 }()
1972 var tmp [1]byte
1973
1974 _, err := file.Read(tmp[:])
1975 ch <- struct{}{}
1976 if errors.Is(err, os.ErrDeadlineExceeded) {
1977 t.Skip("took too long to cancel")
1978 }
1979 if !errors.Is(err, syscall.ERROR_OPERATION_ABORTED) {
1980 t.Errorf("expected ERROR_OPERATION_ABORTED, got %v", err)
1981 }
1982 }
1983
1984 func iocpAssociateFile(f *os.File, iocp syscall.Handle) error {
1985 _, err := windows.CreateIoCompletionPort(syscall.Handle(f.Fd()), iocp, 0, 0)
1986 return err
1987 }
1988
1989 func TestFileAssociatedWithExternalIOCP(t *testing.T) {
1990
1991
1992
1993
1994 t.Parallel()
1995 name := pipeName()
1996 pipe := newMessagePipe(t, name, true)
1997 _ = newFileOverlapped(t, name, true)
1998
1999
2000 file := newFileOverlapped(t, filepath.Join(t.TempDir(), "a"), true)
2001
2002 iocp, err := windows.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)
2003 if err != nil {
2004 t.Fatal(err)
2005 }
2006 defer func() {
2007 if iocp == syscall.InvalidHandle {
2008
2009 return
2010 }
2011 if err := syscall.CloseHandle(iocp); err != nil {
2012 t.Fatal(err)
2013 }
2014 }()
2015
2016 ch := make(chan error, 1)
2017 go func() {
2018 var bytes, key uint32
2019 var overlapped *syscall.Overlapped
2020 err := syscall.GetQueuedCompletionStatus(syscall.Handle(iocp), &bytes, &key, &overlapped, syscall.INFINITE)
2021 ch <- err
2022 }()
2023
2024 if err := iocpAssociateFile(pipe, iocp); err != nil {
2025 t.Fatal(err)
2026 }
2027 if err := iocpAssociateFile(file, iocp); err != nil {
2028 t.Fatal(err)
2029 }
2030
2031 if _, err := pipe.Write([]byte("hello")); err != nil {
2032 t.Fatal(err)
2033 }
2034 if _, err := file.Write([]byte("hello")); err != nil {
2035 t.Fatal(err)
2036 }
2037 if _, err := file.WriteAt([]byte("hello"), 0); err != nil {
2038 t.Fatal(err)
2039 }
2040
2041
2042 time.Sleep(100 * time.Millisecond)
2043
2044
2045 if err := syscall.CloseHandle(iocp); err != nil {
2046 t.Fatal(err)
2047 }
2048
2049
2050 err = <-ch
2051 iocp = syscall.InvalidHandle
2052 const ERROR_ABANDONED_WAIT_0 = syscall.Errno(735)
2053 switch err {
2054 case ERROR_ABANDONED_WAIT_0:
2055
2056 case nil:
2057 t.Error("unexpected queued completion")
2058 default:
2059 t.Error(err)
2060 }
2061 }
2062
2063 func TestFileWriteFdRace(t *testing.T) {
2064 t.Parallel()
2065
2066 f := newFileOverlapped(t, filepath.Join(t.TempDir(), "a"), true)
2067
2068 var wg sync.WaitGroup
2069 wg.Add(2)
2070
2071 go func() {
2072 defer wg.Done()
2073 n, err := f.Write([]byte("hi"))
2074 if err != nil {
2075
2076
2077 switch {
2078 case errors.Is(err, windows.ERROR_INVALID_HANDLE):
2079
2080 default:
2081
2082 t.Error(err)
2083 }
2084 return
2085 }
2086 if n != 2 {
2087 t.Errorf("wrote %d bytes, expected 2", n)
2088 return
2089 }
2090 }()
2091 go func() {
2092 defer wg.Done()
2093 f.Fd()
2094 }()
2095 wg.Wait()
2096
2097 iocp, err := windows.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)
2098 if err != nil {
2099 t.Fatal(err)
2100 }
2101 defer syscall.CloseHandle(iocp)
2102 if err := iocpAssociateFile(f, iocp); err != nil {
2103 t.Fatal(err)
2104 }
2105
2106 if _, err := f.Write([]byte("hi")); err != nil {
2107 t.Error(err)
2108 }
2109 }
2110
2111 func TestSplitPath(t *testing.T) {
2112 t.Parallel()
2113 for _, tt := range []struct{ path, wantDir, wantBase string }{
2114 {`a`, `.`, `a`},
2115 {`a\`, `.`, `a`},
2116 {`a\\`, `.`, `a`},
2117 {`a\b`, `a`, `b`},
2118 {`a\\b`, `a`, `b`},
2119 {`a\b\`, `a`, `b`},
2120 {`a\b\c`, `a\b`, `c`},
2121 {`\a`, `\`, `a`},
2122 {`\a\`, `\`, `a`},
2123 {`\a\b`, `\a`, `b`},
2124 {`\a\b\`, `\a`, `b`},
2125 {`\a\b\c`, `\a\b`, `c`},
2126 {`\\a`, `\\a`, `.`},
2127 {`\\a\`, `\\a\`, `.`},
2128 {`\\\a`, `\\\a`, `.`},
2129 {`\\\a\`, `\\\a`, `.`},
2130 {`\\a\b\c`, `\\a\b`, `c`},
2131 {`c:`, `c:`, `.`},
2132 {`c:\`, `c:\`, `.`},
2133 {`c:\a`, `c:\`, `a`},
2134 {`c:a`, `c:`, `a`},
2135 {`c:a\b\`, `c:a`, `b`},
2136 {`c:base`, `c:`, `base`},
2137 {`a/b/c`, `a/b`, `c`},
2138 {`a/b/c/`, `a/b`, `c`},
2139 {`\\?\c:\a`, `\\?\c:\`, `a`},
2140 } {
2141 if dir, base := os.SplitPath(tt.path); dir != tt.wantDir || base != tt.wantBase {
2142 t.Errorf("splitPath(%q) = %q, %q, want %q, %q", tt.path, dir, base, tt.wantDir, tt.wantBase)
2143 }
2144 }
2145 }
2146
View as plain text