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