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