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