]> Cypherpunks.ru repositories - gostls13.git/blob - src/os/os_windows_test.go
cmd/compile/internal/inline: score call sites exposed by inlines
[gostls13.git] / src / os / os_windows_test.go
1 // Copyright 2014 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package os_test
6
7 import (
8         "errors"
9         "fmt"
10         "internal/poll"
11         "internal/syscall/windows"
12         "internal/syscall/windows/registry"
13         "internal/testenv"
14         "io"
15         "io/fs"
16         "os"
17         "os/exec"
18         "path/filepath"
19         "reflect"
20         "runtime"
21         "slices"
22         "sort"
23         "strings"
24         "syscall"
25         "testing"
26         "unicode/utf16"
27         "unsafe"
28 )
29
30 // For TestRawConnReadWrite.
31 type syscallDescriptor = syscall.Handle
32
33 // chdir changes the current working directory to the named directory,
34 // and then restore the original working directory at the end of the test.
35 func chdir(t *testing.T, dir string) {
36         olddir, err := os.Getwd()
37         if err != nil {
38                 t.Fatalf("chdir: %v", err)
39         }
40         if err := os.Chdir(dir); err != nil {
41                 t.Fatalf("chdir %s: %v", dir, err)
42         }
43
44         t.Cleanup(func() {
45                 if err := os.Chdir(olddir); err != nil {
46                         t.Errorf("chdir to original working directory %s: %v", olddir, err)
47                         os.Exit(1)
48                 }
49         })
50 }
51
52 func TestSameWindowsFile(t *testing.T) {
53         temp := t.TempDir()
54         chdir(t, temp)
55
56         f, err := os.Create("a")
57         if err != nil {
58                 t.Fatal(err)
59         }
60         f.Close()
61
62         ia1, err := os.Stat("a")
63         if err != nil {
64                 t.Fatal(err)
65         }
66
67         path, err := filepath.Abs("a")
68         if err != nil {
69                 t.Fatal(err)
70         }
71         ia2, err := os.Stat(path)
72         if err != nil {
73                 t.Fatal(err)
74         }
75         if !os.SameFile(ia1, ia2) {
76                 t.Errorf("files should be same")
77         }
78
79         p := filepath.VolumeName(path) + filepath.Base(path)
80         if err != nil {
81                 t.Fatal(err)
82         }
83         ia3, err := os.Stat(p)
84         if err != nil {
85                 t.Fatal(err)
86         }
87         if !os.SameFile(ia1, ia3) {
88                 t.Errorf("files should be same")
89         }
90 }
91
92 type dirLinkTest struct {
93         name    string
94         mklink  func(link, target string) error
95         issueNo int // correspondent issue number (for broken tests)
96 }
97
98 func testDirLinks(t *testing.T, tests []dirLinkTest) {
99         tmpdir := t.TempDir()
100         chdir(t, tmpdir)
101
102         dir := filepath.Join(tmpdir, "dir")
103         err := os.Mkdir(dir, 0777)
104         if err != nil {
105                 t.Fatal(err)
106         }
107         fi, err := os.Stat(dir)
108         if err != nil {
109                 t.Fatal(err)
110         }
111         err = os.WriteFile(filepath.Join(dir, "abc"), []byte("abc"), 0644)
112         if err != nil {
113                 t.Fatal(err)
114         }
115         for _, test := range tests {
116                 link := filepath.Join(tmpdir, test.name+"_link")
117                 err := test.mklink(link, dir)
118                 if err != nil {
119                         t.Errorf("creating link for %q test failed: %v", test.name, err)
120                         continue
121                 }
122
123                 data, err := os.ReadFile(filepath.Join(link, "abc"))
124                 if err != nil {
125                         t.Errorf("failed to read abc file: %v", err)
126                         continue
127                 }
128                 if string(data) != "abc" {
129                         t.Errorf(`abc file is expected to have "abc" in it, but has %v`, data)
130                         continue
131                 }
132
133                 if test.issueNo > 0 {
134                         t.Logf("skipping broken %q test: see issue %d", test.name, test.issueNo)
135                         continue
136                 }
137
138                 fi1, err := os.Stat(link)
139                 if err != nil {
140                         t.Errorf("failed to stat link %v: %v", link, err)
141                         continue
142                 }
143                 if !fi1.IsDir() {
144                         t.Errorf("%q should be a directory", link)
145                         continue
146                 }
147                 if fi1.Name() != filepath.Base(link) {
148                         t.Errorf("Stat(%q).Name() = %q, want %q", link, fi1.Name(), filepath.Base(link))
149                         continue
150                 }
151                 if !os.SameFile(fi, fi1) {
152                         t.Errorf("%q should point to %q", link, dir)
153                         continue
154                 }
155
156                 fi2, err := os.Lstat(link)
157                 if err != nil {
158                         t.Errorf("failed to lstat link %v: %v", link, err)
159                         continue
160                 }
161                 if m := fi2.Mode(); m&fs.ModeSymlink == 0 {
162                         t.Errorf("%q should be a link, but is not (mode=0x%x)", link, uint32(m))
163                         continue
164                 }
165                 if m := fi2.Mode(); m&fs.ModeDir != 0 {
166                         t.Errorf("%q should be a link, not a directory (mode=0x%x)", link, uint32(m))
167                         continue
168                 }
169         }
170 }
171
172 // reparseData is used to build reparse buffer data required for tests.
173 type reparseData struct {
174         substituteName namePosition
175         printName      namePosition
176         pathBuf        []uint16
177 }
178
179 type namePosition struct {
180         offset uint16
181         length uint16
182 }
183
184 func (rd *reparseData) addUTF16s(s []uint16) (offset uint16) {
185         off := len(rd.pathBuf) * 2
186         rd.pathBuf = append(rd.pathBuf, s...)
187         return uint16(off)
188 }
189
190 func (rd *reparseData) addString(s string) (offset, length uint16) {
191         p := syscall.StringToUTF16(s)
192         return rd.addUTF16s(p), uint16(len(p)-1) * 2 // do not include terminating NUL in the length (as per PrintNameLength and SubstituteNameLength documentation)
193 }
194
195 func (rd *reparseData) addSubstituteName(name string) {
196         rd.substituteName.offset, rd.substituteName.length = rd.addString(name)
197 }
198
199 func (rd *reparseData) addPrintName(name string) {
200         rd.printName.offset, rd.printName.length = rd.addString(name)
201 }
202
203 func (rd *reparseData) addStringNoNUL(s string) (offset, length uint16) {
204         p := syscall.StringToUTF16(s)
205         p = p[:len(p)-1]
206         return rd.addUTF16s(p), uint16(len(p)) * 2
207 }
208
209 func (rd *reparseData) addSubstituteNameNoNUL(name string) {
210         rd.substituteName.offset, rd.substituteName.length = rd.addStringNoNUL(name)
211 }
212
213 func (rd *reparseData) addPrintNameNoNUL(name string) {
214         rd.printName.offset, rd.printName.length = rd.addStringNoNUL(name)
215 }
216
217 // pathBuffeLen returns length of rd pathBuf in bytes.
218 func (rd *reparseData) pathBuffeLen() uint16 {
219         return uint16(len(rd.pathBuf)) * 2
220 }
221
222 // Windows REPARSE_DATA_BUFFER contains union member, and cannot be
223 // translated into Go directly. _REPARSE_DATA_BUFFER type is to help
224 // construct alternative versions of Windows REPARSE_DATA_BUFFER with
225 // union part of SymbolicLinkReparseBuffer or MountPointReparseBuffer type.
226 type _REPARSE_DATA_BUFFER struct {
227         header windows.REPARSE_DATA_BUFFER_HEADER
228         detail [syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]byte
229 }
230
231 func createDirLink(link string, rdb *_REPARSE_DATA_BUFFER) error {
232         err := os.Mkdir(link, 0777)
233         if err != nil {
234                 return err
235         }
236
237         linkp := syscall.StringToUTF16(link)
238         fd, err := syscall.CreateFile(&linkp[0], syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING,
239                 syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
240         if err != nil {
241                 return err
242         }
243         defer syscall.CloseHandle(fd)
244
245         buflen := uint32(rdb.header.ReparseDataLength) + uint32(unsafe.Sizeof(rdb.header))
246         var bytesReturned uint32
247         return syscall.DeviceIoControl(fd, windows.FSCTL_SET_REPARSE_POINT,
248                 (*byte)(unsafe.Pointer(&rdb.header)), buflen, nil, 0, &bytesReturned, nil)
249 }
250
251 func createMountPoint(link string, target *reparseData) error {
252         var buf *windows.MountPointReparseBuffer
253         buflen := uint16(unsafe.Offsetof(buf.PathBuffer)) + target.pathBuffeLen() // see ReparseDataLength documentation
254         byteblob := make([]byte, buflen)
255         buf = (*windows.MountPointReparseBuffer)(unsafe.Pointer(&byteblob[0]))
256         buf.SubstituteNameOffset = target.substituteName.offset
257         buf.SubstituteNameLength = target.substituteName.length
258         buf.PrintNameOffset = target.printName.offset
259         buf.PrintNameLength = target.printName.length
260         pbuflen := len(target.pathBuf)
261         copy((*[2048]uint16)(unsafe.Pointer(&buf.PathBuffer[0]))[:pbuflen:pbuflen], target.pathBuf)
262
263         var rdb _REPARSE_DATA_BUFFER
264         rdb.header.ReparseTag = windows.IO_REPARSE_TAG_MOUNT_POINT
265         rdb.header.ReparseDataLength = buflen
266         copy(rdb.detail[:], byteblob)
267
268         return createDirLink(link, &rdb)
269 }
270
271 func TestDirectoryJunction(t *testing.T) {
272         var tests = []dirLinkTest{
273                 {
274                         // Create link similar to what mklink does, by inserting \??\ at the front of absolute target.
275                         name: "standard",
276                         mklink: func(link, target string) error {
277                                 var t reparseData
278                                 t.addSubstituteName(`\??\` + target)
279                                 t.addPrintName(target)
280                                 return createMountPoint(link, &t)
281                         },
282                 },
283                 {
284                         // Do as junction utility https://learn.microsoft.com/en-us/sysinternals/downloads/junction does - set PrintNameLength to 0.
285                         name: "have_blank_print_name",
286                         mklink: func(link, target string) error {
287                                 var t reparseData
288                                 t.addSubstituteName(`\??\` + target)
289                                 t.addPrintName("")
290                                 return createMountPoint(link, &t)
291                         },
292                 },
293         }
294         output, _ := testenv.Command(t, "cmd", "/c", "mklink", "/?").Output()
295         mklinkSupportsJunctionLinks := strings.Contains(string(output), " /J ")
296         if mklinkSupportsJunctionLinks {
297                 tests = append(tests,
298                         dirLinkTest{
299                                 name: "use_mklink_cmd",
300                                 mklink: func(link, target string) error {
301                                         output, err := testenv.Command(t, "cmd", "/c", "mklink", "/J", link, target).CombinedOutput()
302                                         if err != nil {
303                                                 t.Errorf("failed to run mklink %v %v: %v %q", link, target, err, output)
304                                         }
305                                         return nil
306                                 },
307                         },
308                 )
309         } else {
310                 t.Log(`skipping "use_mklink_cmd" test, mklink does not supports directory junctions`)
311         }
312         testDirLinks(t, tests)
313 }
314
315 func enableCurrentThreadPrivilege(privilegeName string) error {
316         ct, err := windows.GetCurrentThread()
317         if err != nil {
318                 return err
319         }
320         var t syscall.Token
321         err = windows.OpenThreadToken(ct, syscall.TOKEN_QUERY|windows.TOKEN_ADJUST_PRIVILEGES, false, &t)
322         if err != nil {
323                 return err
324         }
325         defer syscall.CloseHandle(syscall.Handle(t))
326
327         var tp windows.TOKEN_PRIVILEGES
328
329         privStr, err := syscall.UTF16PtrFromString(privilegeName)
330         if err != nil {
331                 return err
332         }
333         err = windows.LookupPrivilegeValue(nil, privStr, &tp.Privileges[0].Luid)
334         if err != nil {
335                 return err
336         }
337         tp.PrivilegeCount = 1
338         tp.Privileges[0].Attributes = windows.SE_PRIVILEGE_ENABLED
339         return windows.AdjustTokenPrivileges(t, false, &tp, 0, nil, nil)
340 }
341
342 func createSymbolicLink(link string, target *reparseData, isrelative bool) error {
343         var buf *windows.SymbolicLinkReparseBuffer
344         buflen := uint16(unsafe.Offsetof(buf.PathBuffer)) + target.pathBuffeLen() // see ReparseDataLength documentation
345         byteblob := make([]byte, buflen)
346         buf = (*windows.SymbolicLinkReparseBuffer)(unsafe.Pointer(&byteblob[0]))
347         buf.SubstituteNameOffset = target.substituteName.offset
348         buf.SubstituteNameLength = target.substituteName.length
349         buf.PrintNameOffset = target.printName.offset
350         buf.PrintNameLength = target.printName.length
351         if isrelative {
352                 buf.Flags = windows.SYMLINK_FLAG_RELATIVE
353         }
354         pbuflen := len(target.pathBuf)
355         copy((*[2048]uint16)(unsafe.Pointer(&buf.PathBuffer[0]))[:pbuflen:pbuflen], target.pathBuf)
356
357         var rdb _REPARSE_DATA_BUFFER
358         rdb.header.ReparseTag = syscall.IO_REPARSE_TAG_SYMLINK
359         rdb.header.ReparseDataLength = buflen
360         copy(rdb.detail[:], byteblob)
361
362         return createDirLink(link, &rdb)
363 }
364
365 func TestDirectorySymbolicLink(t *testing.T) {
366         var tests []dirLinkTest
367         output, _ := testenv.Command(t, "cmd", "/c", "mklink", "/?").Output()
368         mklinkSupportsDirectorySymbolicLinks := strings.Contains(string(output), " /D ")
369         if mklinkSupportsDirectorySymbolicLinks {
370                 tests = append(tests,
371                         dirLinkTest{
372                                 name: "use_mklink_cmd",
373                                 mklink: func(link, target string) error {
374                                         output, err := testenv.Command(t, "cmd", "/c", "mklink", "/D", link, target).CombinedOutput()
375                                         if err != nil {
376                                                 t.Errorf("failed to run mklink %v %v: %v %q", link, target, err, output)
377                                         }
378                                         return nil
379                                 },
380                         },
381                 )
382         } else {
383                 t.Log(`skipping "use_mklink_cmd" test, mklink does not supports directory symbolic links`)
384         }
385
386         // The rest of these test requires SeCreateSymbolicLinkPrivilege to be held.
387         runtime.LockOSThread()
388         defer runtime.UnlockOSThread()
389
390         err := windows.ImpersonateSelf(windows.SecurityImpersonation)
391         if err != nil {
392                 t.Fatal(err)
393         }
394         defer windows.RevertToSelf()
395
396         err = enableCurrentThreadPrivilege("SeCreateSymbolicLinkPrivilege")
397         if err != nil {
398                 t.Skipf(`skipping some tests, could not enable "SeCreateSymbolicLinkPrivilege": %v`, err)
399         }
400         tests = append(tests,
401                 dirLinkTest{
402                         name: "use_os_pkg",
403                         mklink: func(link, target string) error {
404                                 return os.Symlink(target, link)
405                         },
406                 },
407                 dirLinkTest{
408                         // Create link similar to what mklink does, by inserting \??\ at the front of absolute target.
409                         name: "standard",
410                         mklink: func(link, target string) error {
411                                 var t reparseData
412                                 t.addPrintName(target)
413                                 t.addSubstituteName(`\??\` + target)
414                                 return createSymbolicLink(link, &t, false)
415                         },
416                 },
417                 dirLinkTest{
418                         name: "relative",
419                         mklink: func(link, target string) error {
420                                 var t reparseData
421                                 t.addSubstituteNameNoNUL(filepath.Base(target))
422                                 t.addPrintNameNoNUL(filepath.Base(target))
423                                 return createSymbolicLink(link, &t, true)
424                         },
425                 },
426         )
427         testDirLinks(t, tests)
428 }
429
430 func mustHaveWorkstation(t *testing.T) {
431         mar, err := windows.OpenSCManager(nil, nil, windows.SERVICE_QUERY_STATUS)
432         if err != nil {
433                 return
434         }
435         defer syscall.CloseHandle(mar)
436         //LanmanWorkstation is the service name, and Workstation is the display name.
437         srv, err := windows.OpenService(mar, syscall.StringToUTF16Ptr("LanmanWorkstation"), windows.SERVICE_QUERY_STATUS)
438         if err != nil {
439                 return
440         }
441         defer syscall.CloseHandle(srv)
442         var state windows.SERVICE_STATUS
443         err = windows.QueryServiceStatus(srv, &state)
444         if err != nil {
445                 return
446         }
447         if state.CurrentState != windows.SERVICE_RUNNING {
448                 t.Skip("Requires the Windows service Workstation, but it is detected that it is not enabled.")
449         }
450 }
451
452 func TestNetworkSymbolicLink(t *testing.T) {
453         testenv.MustHaveSymlink(t)
454
455         const _NERR_ServerNotStarted = syscall.Errno(2114)
456
457         dir := t.TempDir()
458         chdir(t, dir)
459
460         pid := os.Getpid()
461         shareName := fmt.Sprintf("GoSymbolicLinkTestShare%d", pid)
462         sharePath := filepath.Join(dir, shareName)
463         testDir := "TestDir"
464
465         err := os.MkdirAll(filepath.Join(sharePath, testDir), 0777)
466         if err != nil {
467                 t.Fatal(err)
468         }
469
470         wShareName, err := syscall.UTF16PtrFromString(shareName)
471         if err != nil {
472                 t.Fatal(err)
473         }
474         wSharePath, err := syscall.UTF16PtrFromString(sharePath)
475         if err != nil {
476                 t.Fatal(err)
477         }
478
479         // Per https://learn.microsoft.com/en-us/windows/win32/api/lmshare/ns-lmshare-share_info_2:
480         //
481         // “[The shi2_permissions field] indicates the shared resource's permissions
482         // for servers running with share-level security. A server running user-level
483         // security ignores this member.
484         // …
485         // Note that Windows does not support share-level security.”
486         //
487         // So it shouldn't matter what permissions we set here.
488         const permissions = 0
489
490         p := windows.SHARE_INFO_2{
491                 Netname:     wShareName,
492                 Type:        windows.STYPE_DISKTREE | windows.STYPE_TEMPORARY,
493                 Remark:      nil,
494                 Permissions: permissions,
495                 MaxUses:     1,
496                 CurrentUses: 0,
497                 Path:        wSharePath,
498                 Passwd:      nil,
499         }
500
501         err = windows.NetShareAdd(nil, 2, (*byte)(unsafe.Pointer(&p)), nil)
502         if err != nil {
503                 if err == syscall.ERROR_ACCESS_DENIED || err == _NERR_ServerNotStarted {
504                         t.Skipf("skipping: NetShareAdd: %v", err)
505                 }
506                 t.Fatal(err)
507         }
508         defer func() {
509                 err := windows.NetShareDel(nil, wShareName, 0)
510                 if err != nil {
511                         t.Fatal(err)
512                 }
513         }()
514
515         UNCPath := `\\localhost\` + shareName + `\`
516
517         fi1, err := os.Stat(sharePath)
518         if err != nil {
519                 t.Fatal(err)
520         }
521         fi2, err := os.Stat(UNCPath)
522         if err != nil {
523                 mustHaveWorkstation(t)
524                 t.Fatal(err)
525         }
526         if !os.SameFile(fi1, fi2) {
527                 t.Fatalf("%q and %q should be the same directory, but not", sharePath, UNCPath)
528         }
529
530         target := filepath.Join(UNCPath, testDir)
531         link := "link"
532
533         err = os.Symlink(target, link)
534         if err != nil {
535                 t.Fatal(err)
536         }
537         defer os.Remove(link)
538
539         got, err := os.Readlink(link)
540         if err != nil {
541                 t.Fatal(err)
542         }
543         if got != target {
544                 t.Errorf(`os.Readlink(%#q): got %v, want %v`, link, got, target)
545         }
546
547         got, err = filepath.EvalSymlinks(link)
548         if err != nil {
549                 t.Fatal(err)
550         }
551         if got != target {
552                 t.Errorf(`filepath.EvalSymlinks(%#q): got %v, want %v`, link, got, target)
553         }
554 }
555
556 func TestStatLxSymLink(t *testing.T) {
557         if _, err := exec.LookPath("wsl"); err != nil {
558                 t.Skip("skipping: WSL not detected")
559         }
560
561         temp := t.TempDir()
562         chdir(t, temp)
563
564         const target = "target"
565         const link = "link"
566
567         _, err := testenv.Command(t, "wsl", "/bin/mkdir", target).Output()
568         if err != nil {
569                 // This normally happens when WSL still doesn't have a distro installed to run on.
570                 t.Skipf("skipping: WSL is not correctly installed: %v", err)
571         }
572
573         _, err = testenv.Command(t, "wsl", "/bin/ln", "-s", target, link).Output()
574         if err != nil {
575                 t.Fatal(err)
576         }
577
578         fi, err := os.Lstat(link)
579         if err != nil {
580                 t.Fatal(err)
581         }
582         if m := fi.Mode(); m&fs.ModeSymlink != 0 {
583                 // This can happen depending on newer WSL versions when running as admin or in developer mode.
584                 t.Skip("skipping: WSL created reparse tag IO_REPARSE_TAG_SYMLINK instead of a IO_REPARSE_TAG_LX_SYMLINK")
585         }
586         // Stat'ing a IO_REPARSE_TAG_LX_SYMLINK from outside WSL always return ERROR_CANT_ACCESS_FILE.
587         // We check this condition to validate that os.Stat has tried to follow the link.
588         _, err = os.Stat(link)
589         const ERROR_CANT_ACCESS_FILE = syscall.Errno(1920)
590         if err == nil || !errors.Is(err, ERROR_CANT_ACCESS_FILE) {
591                 t.Fatalf("os.Stat(%q): got %v, want ERROR_CANT_ACCESS_FILE", link, err)
592         }
593 }
594
595 func TestStartProcessAttr(t *testing.T) {
596         t.Parallel()
597
598         p, err := os.StartProcess(os.Getenv("COMSPEC"), []string{"/c", "cd"}, new(os.ProcAttr))
599         if err != nil {
600                 return
601         }
602         defer p.Wait()
603         t.Fatalf("StartProcess expected to fail, but succeeded.")
604 }
605
606 func TestShareNotExistError(t *testing.T) {
607         if testing.Short() {
608                 t.Skip("slow test that uses network; skipping")
609         }
610         t.Parallel()
611
612         _, err := os.Stat(`\\no_such_server\no_such_share\no_such_file`)
613         if err == nil {
614                 t.Fatal("stat succeeded, but expected to fail")
615         }
616         if !os.IsNotExist(err) {
617                 t.Fatalf("os.Stat failed with %q, but os.IsNotExist(err) is false", err)
618         }
619 }
620
621 func TestBadNetPathError(t *testing.T) {
622         const ERROR_BAD_NETPATH = syscall.Errno(53)
623         if !os.IsNotExist(ERROR_BAD_NETPATH) {
624                 t.Fatal("os.IsNotExist(syscall.Errno(53)) is false, but want true")
625         }
626 }
627
628 func TestStatDir(t *testing.T) {
629         defer chtmpdir(t)()
630
631         f, err := os.Open(".")
632         if err != nil {
633                 t.Fatal(err)
634         }
635         defer f.Close()
636
637         fi, err := f.Stat()
638         if err != nil {
639                 t.Fatal(err)
640         }
641
642         err = os.Chdir("..")
643         if err != nil {
644                 t.Fatal(err)
645         }
646
647         fi2, err := f.Stat()
648         if err != nil {
649                 t.Fatal(err)
650         }
651
652         if !os.SameFile(fi, fi2) {
653                 t.Fatal("race condition occurred")
654         }
655 }
656
657 func TestOpenVolumeName(t *testing.T) {
658         tmpdir := t.TempDir()
659         chdir(t, tmpdir)
660
661         want := []string{"file1", "file2", "file3", "gopher.txt"}
662         sort.Strings(want)
663         for _, name := range want {
664                 err := os.WriteFile(filepath.Join(tmpdir, name), nil, 0777)
665                 if err != nil {
666                         t.Fatal(err)
667                 }
668         }
669
670         f, err := os.Open(filepath.VolumeName(tmpdir))
671         if err != nil {
672                 t.Fatal(err)
673         }
674         defer f.Close()
675
676         have, err := f.Readdirnames(-1)
677         if err != nil {
678                 t.Fatal(err)
679         }
680         sort.Strings(have)
681
682         if strings.Join(want, "/") != strings.Join(have, "/") {
683                 t.Fatalf("unexpected file list %q, want %q", have, want)
684         }
685 }
686
687 func TestDeleteReadOnly(t *testing.T) {
688         t.Parallel()
689
690         tmpdir := t.TempDir()
691         p := filepath.Join(tmpdir, "a")
692         // This sets FILE_ATTRIBUTE_READONLY.
693         f, err := os.OpenFile(p, os.O_CREATE, 0400)
694         if err != nil {
695                 t.Fatal(err)
696         }
697         f.Close()
698
699         if err = os.Chmod(p, 0400); err != nil {
700                 t.Fatal(err)
701         }
702         if err = os.Remove(p); err != nil {
703                 t.Fatal(err)
704         }
705 }
706
707 func TestReadStdin(t *testing.T) {
708         old := poll.ReadConsole
709         defer func() {
710                 poll.ReadConsole = old
711         }()
712
713         p, err := syscall.GetCurrentProcess()
714         if err != nil {
715                 t.Fatalf("Unable to get handle to current process: %v", err)
716         }
717         var stdinDuplicate syscall.Handle
718         err = syscall.DuplicateHandle(p, syscall.Handle(syscall.Stdin), p, &stdinDuplicate, 0, false, syscall.DUPLICATE_SAME_ACCESS)
719         if err != nil {
720                 t.Fatalf("Unable to duplicate stdin: %v", err)
721         }
722         testConsole := os.NewConsoleFile(stdinDuplicate, "test")
723
724         var tests = []string{
725                 "abc",
726                 "äöü",
727                 "\u3042",
728                 "“hi”™",
729                 "hello\x1aworld",
730                 "\U0001F648\U0001F649\U0001F64A",
731         }
732
733         for _, consoleSize := range []int{1, 2, 3, 10, 16, 100, 1000} {
734                 for _, readSize := range []int{1, 2, 3, 4, 5, 8, 10, 16, 20, 50, 100} {
735                         for _, s := range tests {
736                                 t.Run(fmt.Sprintf("c%d/r%d/%s", consoleSize, readSize, s), func(t *testing.T) {
737                                         s16 := utf16.Encode([]rune(s))
738                                         poll.ReadConsole = func(h syscall.Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) error {
739                                                 if inputControl != nil {
740                                                         t.Fatalf("inputControl not nil")
741                                                 }
742                                                 n := int(toread)
743                                                 if n > consoleSize {
744                                                         n = consoleSize
745                                                 }
746                                                 n = copy((*[10000]uint16)(unsafe.Pointer(buf))[:n:n], s16)
747                                                 s16 = s16[n:]
748                                                 *read = uint32(n)
749                                                 t.Logf("read %d -> %d", toread, *read)
750                                                 return nil
751                                         }
752
753                                         var all []string
754                                         var buf []byte
755                                         chunk := make([]byte, readSize)
756                                         for {
757                                                 n, err := testConsole.Read(chunk)
758                                                 buf = append(buf, chunk[:n]...)
759                                                 if err == io.EOF {
760                                                         all = append(all, string(buf))
761                                                         if len(all) >= 5 {
762                                                                 break
763                                                         }
764                                                         buf = buf[:0]
765                                                 } else if err != nil {
766                                                         t.Fatalf("reading %q: error: %v", s, err)
767                                                 }
768                                                 if len(buf) >= 2000 {
769                                                         t.Fatalf("reading %q: stuck in loop: %q", s, buf)
770                                                 }
771                                         }
772
773                                         want := strings.Split(s, "\x1a")
774                                         for len(want) < 5 {
775                                                 want = append(want, "")
776                                         }
777                                         if !reflect.DeepEqual(all, want) {
778                                                 t.Errorf("reading %q:\nhave %x\nwant %x", s, all, want)
779                                         }
780                                 })
781                         }
782                 }
783         }
784 }
785
786 func TestStatPagefile(t *testing.T) {
787         t.Parallel()
788
789         const path = `c:\pagefile.sys`
790         fi, err := os.Stat(path)
791         if err == nil {
792                 if fi.Name() == "" {
793                         t.Fatalf("Stat(%q).Name() is empty", path)
794                 }
795                 t.Logf("Stat(%q).Size() = %v", path, fi.Size())
796                 return
797         }
798         if os.IsNotExist(err) {
799                 t.Skip(`skipping because c:\pagefile.sys is not found`)
800         }
801         t.Fatal(err)
802 }
803
804 // syscallCommandLineToArgv calls syscall.CommandLineToArgv
805 // and converts returned result into []string.
806 func syscallCommandLineToArgv(cmd string) ([]string, error) {
807         var argc int32
808         argv, err := syscall.CommandLineToArgv(&syscall.StringToUTF16(cmd)[0], &argc)
809         if err != nil {
810                 return nil, err
811         }
812         defer syscall.LocalFree(syscall.Handle(uintptr(unsafe.Pointer(argv))))
813
814         var args []string
815         for _, v := range (*argv)[:argc] {
816                 args = append(args, syscall.UTF16ToString((*v)[:]))
817         }
818         return args, nil
819 }
820
821 // compareCommandLineToArgvWithSyscall ensures that
822 // os.CommandLineToArgv(cmd) and syscall.CommandLineToArgv(cmd)
823 // return the same result.
824 func compareCommandLineToArgvWithSyscall(t *testing.T, cmd string) {
825         syscallArgs, err := syscallCommandLineToArgv(cmd)
826         if err != nil {
827                 t.Fatal(err)
828         }
829         args := os.CommandLineToArgv(cmd)
830         if want, have := fmt.Sprintf("%q", syscallArgs), fmt.Sprintf("%q", args); want != have {
831                 t.Errorf("testing os.commandLineToArgv(%q) failed: have %q want %q", cmd, args, syscallArgs)
832                 return
833         }
834 }
835
836 func TestCmdArgs(t *testing.T) {
837         if testing.Short() {
838                 t.Skipf("in short mode; skipping test that builds a binary")
839         }
840         t.Parallel()
841
842         tmpdir := t.TempDir()
843
844         const prog = `
845 package main
846
847 import (
848         "fmt"
849         "os"
850 )
851
852 func main() {
853         fmt.Printf("%q", os.Args)
854 }
855 `
856         src := filepath.Join(tmpdir, "main.go")
857         if err := os.WriteFile(src, []byte(prog), 0666); err != nil {
858                 t.Fatal(err)
859         }
860
861         exe := filepath.Join(tmpdir, "main.exe")
862         cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, src)
863         cmd.Dir = tmpdir
864         out, err := cmd.CombinedOutput()
865         if err != nil {
866                 t.Fatalf("building main.exe failed: %v\n%s", err, out)
867         }
868
869         var cmds = []string{
870                 ``,
871                 ` a b c`,
872                 ` "`,
873                 ` ""`,
874                 ` """`,
875                 ` "" a`,
876                 ` "123"`,
877                 ` \"123\"`,
878                 ` \"123 456\"`,
879                 ` \\"`,
880                 ` \\\"`,
881                 ` \\\\\"`,
882                 ` \\\"x`,
883                 ` """"\""\\\"`,
884                 ` abc`,
885                 ` \\\\\""x"""y z`,
886                 "\tb\t\"x\ty\"",
887                 ` "Брад" d e`,
888                 // examples from https://learn.microsoft.com/en-us/cpp/cpp/main-function-command-line-args
889                 ` "abc" d e`,
890                 ` a\\b d"e f"g h`,
891                 ` a\\\"b c d`,
892                 ` a\\\\"b c" d e`,
893                 // http://daviddeley.com/autohotkey/parameters/parameters.htm#WINARGV
894                 // from 5.4  Examples
895                 ` CallMeIshmael`,
896                 ` "Call Me Ishmael"`,
897                 ` Cal"l Me I"shmael`,
898                 ` CallMe\"Ishmael`,
899                 ` "CallMe\"Ishmael"`,
900                 ` "Call Me Ishmael\\"`,
901                 ` "CallMe\\\"Ishmael"`,
902                 ` a\\\b`,
903                 ` "a\\\b"`,
904                 // from 5.5  Some Common Tasks
905                 ` "\"Call Me Ishmael\""`,
906                 ` "C:\TEST A\\"`,
907                 ` "\"C:\TEST A\\\""`,
908                 // from 5.6  The Microsoft Examples Explained
909                 ` "a b c"  d  e`,
910                 ` "ab\"c"  "\\"  d`,
911                 ` a\\\b d"e f"g h`,
912                 ` a\\\"b c d`,
913                 ` a\\\\"b c" d e`,
914                 // from 5.7  Double Double Quote Examples (pre 2008)
915                 ` "a b c""`,
916                 ` """CallMeIshmael"""  b  c`,
917                 ` """Call Me Ishmael"""`,
918                 ` """"Call Me Ishmael"" b c`,
919         }
920         for _, cmd := range cmds {
921                 compareCommandLineToArgvWithSyscall(t, "test"+cmd)
922                 compareCommandLineToArgvWithSyscall(t, `"cmd line"`+cmd)
923                 compareCommandLineToArgvWithSyscall(t, exe+cmd)
924
925                 // test both syscall.EscapeArg and os.commandLineToArgv
926                 args := os.CommandLineToArgv(exe + cmd)
927                 out, err := testenv.Command(t, args[0], args[1:]...).CombinedOutput()
928                 if err != nil {
929                         t.Fatalf("running %q failed: %v\n%v", args, err, string(out))
930                 }
931                 if want, have := fmt.Sprintf("%q", args), string(out); want != have {
932                         t.Errorf("wrong output of executing %q: have %q want %q", args, have, want)
933                         continue
934                 }
935         }
936 }
937
938 func findOneDriveDir() (string, error) {
939         // as per https://stackoverflow.com/questions/42519624/how-to-determine-location-of-onedrive-on-windows-7-and-8-in-c
940         const onedrivekey = `SOFTWARE\Microsoft\OneDrive`
941         k, err := registry.OpenKey(registry.CURRENT_USER, onedrivekey, registry.READ)
942         if err != nil {
943                 return "", fmt.Errorf("OpenKey(%q) failed: %v", onedrivekey, err)
944         }
945         defer k.Close()
946
947         path, valtype, err := k.GetStringValue("UserFolder")
948         if err != nil {
949                 return "", fmt.Errorf("reading UserFolder failed: %v", err)
950         }
951
952         if valtype == registry.EXPAND_SZ {
953                 expanded, err := registry.ExpandString(path)
954                 if err != nil {
955                         return "", fmt.Errorf("expanding UserFolder failed: %v", err)
956                 }
957                 path = expanded
958         }
959
960         return path, nil
961 }
962
963 // TestOneDrive verifies that OneDrive folder is a directory and not a symlink.
964 func TestOneDrive(t *testing.T) {
965         t.Parallel()
966
967         dir, err := findOneDriveDir()
968         if err != nil {
969                 t.Skipf("Skipping, because we did not find OneDrive directory: %v", err)
970         }
971         testDirStats(t, dir)
972 }
973
974 func TestWindowsDevNullFile(t *testing.T) {
975         t.Parallel()
976
977         f1, err := os.Open("NUL")
978         if err != nil {
979                 t.Fatal(err)
980         }
981         defer f1.Close()
982
983         fi1, err := f1.Stat()
984         if err != nil {
985                 t.Fatal(err)
986         }
987
988         f2, err := os.Open("nul")
989         if err != nil {
990                 t.Fatal(err)
991         }
992         defer f2.Close()
993
994         fi2, err := f2.Stat()
995         if err != nil {
996                 t.Fatal(err)
997         }
998
999         if !os.SameFile(fi1, fi2) {
1000                 t.Errorf(`"NUL" and "nul" are not the same file`)
1001         }
1002 }
1003
1004 func TestFileStatNUL(t *testing.T) {
1005         t.Parallel()
1006
1007         f, err := os.Open("NUL")
1008         if err != nil {
1009                 t.Fatal(err)
1010         }
1011         fi, err := f.Stat()
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("Open(%q).Stat().Mode() = %v, want %v", "NUL", got, want)
1017         }
1018 }
1019
1020 func TestStatNUL(t *testing.T) {
1021         t.Parallel()
1022
1023         fi, err := os.Stat("NUL")
1024         if err != nil {
1025                 t.Fatal(err)
1026         }
1027         if got, want := fi.Mode(), os.ModeDevice|os.ModeCharDevice|0666; got != want {
1028                 t.Errorf("Stat(%q).Mode() = %v, want %v", "NUL", got, want)
1029         }
1030 }
1031
1032 // TestSymlinkCreation verifies that creating a symbolic link
1033 // works on Windows when developer mode is active.
1034 // This is supported starting Windows 10 (1703, v10.0.14972).
1035 func TestSymlinkCreation(t *testing.T) {
1036         if !testenv.HasSymlink() && !isWindowsDeveloperModeActive() {
1037                 t.Skip("Windows developer mode is not active")
1038         }
1039         t.Parallel()
1040
1041         temp := t.TempDir()
1042         dummyFile := filepath.Join(temp, "file")
1043         if err := os.WriteFile(dummyFile, []byte(""), 0644); err != nil {
1044                 t.Fatal(err)
1045         }
1046
1047         linkFile := filepath.Join(temp, "link")
1048         if err := os.Symlink(dummyFile, linkFile); err != nil {
1049                 t.Fatal(err)
1050         }
1051 }
1052
1053 // isWindowsDeveloperModeActive checks whether or not the developer mode is active on Windows 10.
1054 // Returns false for prior Windows versions.
1055 // see https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development
1056 func isWindowsDeveloperModeActive() bool {
1057         key, err := registry.OpenKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", registry.READ)
1058         if err != nil {
1059                 return false
1060         }
1061
1062         val, _, err := key.GetIntegerValue("AllowDevelopmentWithoutDevLicense")
1063         if err != nil {
1064                 return false
1065         }
1066
1067         return val != 0
1068 }
1069
1070 // TestRootRelativeDirSymlink verifies that symlinks to paths relative to the
1071 // drive root (beginning with "\" but no volume name) are created with the
1072 // correct symlink type.
1073 // (See https://golang.org/issue/39183#issuecomment-632175728.)
1074 func TestRootRelativeDirSymlink(t *testing.T) {
1075         testenv.MustHaveSymlink(t)
1076         t.Parallel()
1077
1078         temp := t.TempDir()
1079         dir := filepath.Join(temp, "dir")
1080         if err := os.Mkdir(dir, 0755); err != nil {
1081                 t.Fatal(err)
1082         }
1083
1084         volumeRelDir := strings.TrimPrefix(dir, filepath.VolumeName(dir)) // leaves leading backslash
1085
1086         link := filepath.Join(temp, "link")
1087         err := os.Symlink(volumeRelDir, link)
1088         if err != nil {
1089                 t.Fatal(err)
1090         }
1091         t.Logf("Symlink(%#q, %#q)", volumeRelDir, link)
1092
1093         f, err := os.Open(link)
1094         if err != nil {
1095                 t.Fatal(err)
1096         }
1097         defer f.Close()
1098         if fi, err := f.Stat(); err != nil {
1099                 t.Fatal(err)
1100         } else if !fi.IsDir() {
1101                 t.Errorf("Open(%#q).Stat().IsDir() = false; want true", f.Name())
1102         }
1103 }
1104
1105 // TestWorkingDirectoryRelativeSymlink verifies that symlinks to paths relative
1106 // to the current working directory for the drive, such as "C:File.txt", are
1107 // correctly converted to absolute links of the correct symlink type (per
1108 // https://docs.microsoft.com/en-us/windows/win32/fileio/creating-symbolic-links).
1109 func TestWorkingDirectoryRelativeSymlink(t *testing.T) {
1110         testenv.MustHaveSymlink(t)
1111
1112         // Construct a directory to be symlinked.
1113         temp := t.TempDir()
1114         if v := filepath.VolumeName(temp); len(v) < 2 || v[1] != ':' {
1115                 t.Skipf("Can't test relative symlinks: t.TempDir() (%#q) does not begin with a drive letter.", temp)
1116         }
1117
1118         absDir := filepath.Join(temp, `dir\sub`)
1119         if err := os.MkdirAll(absDir, 0755); err != nil {
1120                 t.Fatal(err)
1121         }
1122
1123         // Change to the temporary directory and construct a
1124         // working-directory-relative symlink.
1125         oldwd, err := os.Getwd()
1126         if err != nil {
1127                 t.Fatal(err)
1128         }
1129         defer func() {
1130                 if err := os.Chdir(oldwd); err != nil {
1131                         t.Fatal(err)
1132                 }
1133         }()
1134         if err := os.Chdir(temp); err != nil {
1135                 t.Fatal(err)
1136         }
1137         t.Logf("Chdir(%#q)", temp)
1138
1139         wdRelDir := filepath.VolumeName(temp) + `dir\sub` // no backslash after volume.
1140         absLink := filepath.Join(temp, "link")
1141         err = os.Symlink(wdRelDir, absLink)
1142         if err != nil {
1143                 t.Fatal(err)
1144         }
1145         t.Logf("Symlink(%#q, %#q)", wdRelDir, absLink)
1146
1147         // Now change back to the original working directory and verify that the
1148         // symlink still refers to its original path and is correctly marked as a
1149         // directory.
1150         if err := os.Chdir(oldwd); err != nil {
1151                 t.Fatal(err)
1152         }
1153         t.Logf("Chdir(%#q)", oldwd)
1154
1155         resolved, err := os.Readlink(absLink)
1156         if err != nil {
1157                 t.Errorf("Readlink(%#q): %v", absLink, err)
1158         } else if resolved != absDir {
1159                 t.Errorf("Readlink(%#q) = %#q; want %#q", absLink, resolved, absDir)
1160         }
1161
1162         linkFile, err := os.Open(absLink)
1163         if err != nil {
1164                 t.Fatal(err)
1165         }
1166         defer linkFile.Close()
1167
1168         linkInfo, err := linkFile.Stat()
1169         if err != nil {
1170                 t.Fatal(err)
1171         }
1172         if !linkInfo.IsDir() {
1173                 t.Errorf("Open(%#q).Stat().IsDir() = false; want true", absLink)
1174         }
1175
1176         absInfo, err := os.Stat(absDir)
1177         if err != nil {
1178                 t.Fatal(err)
1179         }
1180
1181         if !os.SameFile(absInfo, linkInfo) {
1182                 t.Errorf("SameFile(Stat(%#q), Open(%#q).Stat()) = false; want true", absDir, absLink)
1183         }
1184 }
1185
1186 // TestStatOfInvalidName is regression test for issue #24999.
1187 func TestStatOfInvalidName(t *testing.T) {
1188         t.Parallel()
1189
1190         _, err := os.Stat("*.go")
1191         if err == nil {
1192                 t.Fatal(`os.Stat("*.go") unexpectedly succeeded`)
1193         }
1194 }
1195
1196 // findUnusedDriveLetter searches mounted drive list on the system
1197 // (starting from Z: and ending at D:) for unused drive letter.
1198 // It returns path to the found drive root directory (like Z:\) or error.
1199 func findUnusedDriveLetter() (string, error) {
1200         // Do not use A: and B:, because they are reserved for floppy drive.
1201         // Do not use C:, because it is normally used for main drive.
1202         for l := 'Z'; l >= 'D'; l-- {
1203                 p := string(l) + `:\`
1204                 _, err := os.Stat(p)
1205                 if os.IsNotExist(err) {
1206                         return p, nil
1207                 }
1208         }
1209         return "", errors.New("Could not find unused drive letter.")
1210 }
1211
1212 func TestRootDirAsTemp(t *testing.T) {
1213         if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
1214                 fmt.Print(os.TempDir())
1215                 os.Exit(0)
1216         }
1217
1218         testenv.MustHaveExec(t)
1219         t.Parallel()
1220
1221         exe, err := os.Executable()
1222         if err != nil {
1223                 t.Fatal(err)
1224         }
1225
1226         newtmp, err := findUnusedDriveLetter()
1227         if err != nil {
1228                 t.Skip(err)
1229         }
1230
1231         cmd := testenv.Command(t, exe, "-test.run=^TestRootDirAsTemp$")
1232         cmd.Env = cmd.Environ()
1233         cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
1234         cmd.Env = append(cmd.Env, "TMP="+newtmp)
1235         cmd.Env = append(cmd.Env, "TEMP="+newtmp)
1236         output, err := cmd.CombinedOutput()
1237         if err != nil {
1238                 t.Fatalf("Failed to spawn child process: %v %q", err, string(output))
1239         }
1240         if want, have := newtmp, string(output); have != want {
1241                 t.Fatalf("unexpected child process output %q, want %q", have, want)
1242         }
1243 }
1244
1245 func testReadlink(t *testing.T, path, want string) {
1246         got, err := os.Readlink(path)
1247         if err != nil {
1248                 t.Error(err)
1249                 return
1250         }
1251         if got != want {
1252                 t.Errorf(`Readlink(%q): got %q, want %q`, path, got, want)
1253         }
1254 }
1255
1256 func mklink(t *testing.T, link, target string) {
1257         output, err := testenv.Command(t, "cmd", "/c", "mklink", link, target).CombinedOutput()
1258         if err != nil {
1259                 t.Fatalf("failed to run mklink %v %v: %v %q", link, target, err, output)
1260         }
1261 }
1262
1263 func mklinkj(t *testing.T, link, target string) {
1264         output, err := testenv.Command(t, "cmd", "/c", "mklink", "/J", link, target).CombinedOutput()
1265         if err != nil {
1266                 t.Fatalf("failed to run mklink %v %v: %v %q", link, target, err, output)
1267         }
1268 }
1269
1270 func mklinkd(t *testing.T, link, target string) {
1271         output, err := testenv.Command(t, "cmd", "/c", "mklink", "/D", link, target).CombinedOutput()
1272         if err != nil {
1273                 t.Fatalf("failed to run mklink %v %v: %v %q", link, target, err, output)
1274         }
1275 }
1276
1277 func TestWindowsReadlink(t *testing.T) {
1278         tmpdir, err := os.MkdirTemp("", "TestWindowsReadlink")
1279         if err != nil {
1280                 t.Fatal(err)
1281         }
1282         defer os.RemoveAll(tmpdir)
1283
1284         // Make sure tmpdir is not a symlink, otherwise tests will fail.
1285         tmpdir, err = filepath.EvalSymlinks(tmpdir)
1286         if err != nil {
1287                 t.Fatal(err)
1288         }
1289         chdir(t, tmpdir)
1290
1291         vol := filepath.VolumeName(tmpdir)
1292         output, err := testenv.Command(t, "cmd", "/c", "mountvol", vol, "/L").CombinedOutput()
1293         if err != nil {
1294                 t.Fatalf("failed to run mountvol %v /L: %v %q", vol, err, output)
1295         }
1296         ntvol := strings.Trim(string(output), " \n\r")
1297
1298         dir := filepath.Join(tmpdir, "dir")
1299         err = os.MkdirAll(dir, 0777)
1300         if err != nil {
1301                 t.Fatal(err)
1302         }
1303
1304         absdirjlink := filepath.Join(tmpdir, "absdirjlink")
1305         mklinkj(t, absdirjlink, dir)
1306         testReadlink(t, absdirjlink, dir)
1307
1308         ntdirjlink := filepath.Join(tmpdir, "ntdirjlink")
1309         mklinkj(t, ntdirjlink, ntvol+absdirjlink[len(filepath.VolumeName(absdirjlink)):])
1310         testReadlink(t, ntdirjlink, absdirjlink)
1311
1312         ntdirjlinktolink := filepath.Join(tmpdir, "ntdirjlinktolink")
1313         mklinkj(t, ntdirjlinktolink, ntvol+absdirjlink[len(filepath.VolumeName(absdirjlink)):])
1314         testReadlink(t, ntdirjlinktolink, absdirjlink)
1315
1316         mklinkj(t, "reldirjlink", "dir")
1317         testReadlink(t, "reldirjlink", dir) // relative directory junction resolves to absolute path
1318
1319         // Make sure we have sufficient privilege to run mklink command.
1320         testenv.MustHaveSymlink(t)
1321
1322         absdirlink := filepath.Join(tmpdir, "absdirlink")
1323         mklinkd(t, absdirlink, dir)
1324         testReadlink(t, absdirlink, dir)
1325
1326         ntdirlink := filepath.Join(tmpdir, "ntdirlink")
1327         mklinkd(t, ntdirlink, ntvol+absdirlink[len(filepath.VolumeName(absdirlink)):])
1328         testReadlink(t, ntdirlink, absdirlink)
1329
1330         mklinkd(t, "reldirlink", "dir")
1331         testReadlink(t, "reldirlink", "dir")
1332
1333         file := filepath.Join(tmpdir, "file")
1334         err = os.WriteFile(file, []byte(""), 0666)
1335         if err != nil {
1336                 t.Fatal(err)
1337         }
1338
1339         filelink := filepath.Join(tmpdir, "filelink")
1340         mklink(t, filelink, file)
1341         testReadlink(t, filelink, file)
1342
1343         linktofilelink := filepath.Join(tmpdir, "linktofilelink")
1344         mklink(t, linktofilelink, ntvol+filelink[len(filepath.VolumeName(filelink)):])
1345         testReadlink(t, linktofilelink, filelink)
1346
1347         mklink(t, "relfilelink", "file")
1348         testReadlink(t, "relfilelink", "file")
1349 }
1350
1351 func TestOpenDirTOCTOU(t *testing.T) {
1352         t.Parallel()
1353
1354         // Check opened directories can't be renamed until the handle is closed.
1355         // See issue 52747.
1356         tmpdir := t.TempDir()
1357         dir := filepath.Join(tmpdir, "dir")
1358         if err := os.Mkdir(dir, 0777); err != nil {
1359                 t.Fatal(err)
1360         }
1361         f, err := os.Open(dir)
1362         if err != nil {
1363                 t.Fatal(err)
1364         }
1365         newpath := filepath.Join(tmpdir, "dir1")
1366         err = os.Rename(dir, newpath)
1367         if err == nil || !errors.Is(err, windows.ERROR_SHARING_VIOLATION) {
1368                 f.Close()
1369                 t.Fatalf("Rename(%q, %q) = %v; want windows.ERROR_SHARING_VIOLATION", dir, newpath, err)
1370         }
1371         f.Close()
1372         err = os.Rename(dir, newpath)
1373         if err != nil {
1374                 t.Error(err)
1375         }
1376 }
1377
1378 func TestAppExecLinkStat(t *testing.T) {
1379         // We expect executables installed to %LOCALAPPDATA%\Microsoft\WindowsApps to
1380         // be reparse points with tag IO_REPARSE_TAG_APPEXECLINK. Here we check that
1381         // such reparse points are treated as irregular (but executable) files, not
1382         // broken symlinks.
1383         appdata := os.Getenv("LOCALAPPDATA")
1384         if appdata == "" {
1385                 t.Skipf("skipping: LOCALAPPDATA not set")
1386         }
1387
1388         pythonExeName := "python3.exe"
1389         pythonPath := filepath.Join(appdata, `Microsoft\WindowsApps`, pythonExeName)
1390
1391         lfi, err := os.Lstat(pythonPath)
1392         if err != nil {
1393                 t.Skip("skipping test, because Python 3 is not installed via the Windows App Store on this system; see https://golang.org/issue/42919")
1394         }
1395
1396         // An APPEXECLINK reparse point is not a symlink, so os.Readlink should return
1397         // a non-nil error for it, and Stat should return results identical to Lstat.
1398         linkName, err := os.Readlink(pythonPath)
1399         if err == nil {
1400                 t.Errorf("os.Readlink(%q) = %q, but expected an error\n(should be an APPEXECLINK reparse point, not a symlink)", pythonPath, linkName)
1401         }
1402
1403         sfi, err := os.Stat(pythonPath)
1404         if err != nil {
1405                 t.Fatalf("Stat %s: %v", pythonPath, err)
1406         }
1407
1408         if lfi.Name() != sfi.Name() {
1409                 t.Logf("os.Lstat(%q) = %+v", pythonPath, lfi)
1410                 t.Logf("os.Stat(%q)  = %+v", pythonPath, sfi)
1411                 t.Errorf("files should be same")
1412         }
1413
1414         if lfi.Name() != pythonExeName {
1415                 t.Errorf("Stat %s: got %q, but wanted %q", pythonPath, lfi.Name(), pythonExeName)
1416         }
1417         if m := lfi.Mode(); m&fs.ModeSymlink != 0 {
1418                 t.Errorf("%q should be a file, not a link (mode=0x%x)", pythonPath, uint32(m))
1419         }
1420         if m := lfi.Mode(); m&fs.ModeDir != 0 {
1421                 t.Errorf("%q should be a file, not a directory (mode=0x%x)", pythonPath, uint32(m))
1422         }
1423         if m := lfi.Mode(); m&fs.ModeIrregular == 0 {
1424                 // A reparse point is not a regular file, but we don't have a more appropriate
1425                 // ModeType bit for it, so it should be marked as irregular.
1426                 t.Errorf("%q should not be a regular file (mode=0x%x)", pythonPath, uint32(m))
1427         }
1428
1429         if sfi.Name() != pythonExeName {
1430                 t.Errorf("Stat %s: got %q, but wanted %q", pythonPath, sfi.Name(), pythonExeName)
1431         }
1432         if m := sfi.Mode(); m&fs.ModeSymlink != 0 {
1433                 t.Errorf("%q should be a file, not a link (mode=0x%x)", pythonPath, uint32(m))
1434         }
1435         if m := sfi.Mode(); m&fs.ModeDir != 0 {
1436                 t.Errorf("%q should be a file, not a directory (mode=0x%x)", pythonPath, uint32(m))
1437         }
1438         if m := sfi.Mode(); m&fs.ModeIrregular == 0 {
1439                 // A reparse point is not a regular file, but we don't have a more appropriate
1440                 // ModeType bit for it, so it should be marked as irregular.
1441                 t.Errorf("%q should not be a regular file (mode=0x%x)", pythonPath, uint32(m))
1442         }
1443
1444         p, err := exec.LookPath(pythonPath)
1445         if err != nil {
1446                 t.Errorf("exec.LookPath(%q): %v", pythonPath, err)
1447         }
1448         if p != pythonPath {
1449                 t.Errorf("exec.LookPath(%q) = %q; want %q", pythonPath, p, pythonPath)
1450         }
1451 }
1452
1453 func TestIllformedUTF16FileName(t *testing.T) {
1454         dir := t.TempDir()
1455         const sep = string(os.PathSeparator)
1456         if !strings.HasSuffix(dir, sep) {
1457                 dir += sep
1458         }
1459
1460         // This UTF-16 file name is ill-formed as it contains low surrogates that are not preceded by high surrogates ([1:5]).
1461         namew := []uint16{0x2e, 0xdc6d, 0xdc73, 0xdc79, 0xdc73, 0x30, 0x30, 0x30, 0x31, 0}
1462
1463         // Create a file whose name contains unpaired surrogates.
1464         // Use syscall.CreateFile instead of os.Create to simulate a file that is created by
1465         // a non-Go program so the file name hasn't gone through syscall.UTF16FromString.
1466         dirw := utf16.Encode([]rune(dir))
1467         pathw := append(dirw, namew...)
1468         fd, err := syscall.CreateFile(&pathw[0], syscall.GENERIC_ALL, 0, nil, syscall.CREATE_NEW, 0, 0)
1469         if err != nil {
1470                 t.Fatal(err)
1471         }
1472         syscall.CloseHandle(fd)
1473
1474         name := syscall.UTF16ToString(namew)
1475         path := filepath.Join(dir, name)
1476         // Verify that os.Lstat can query the file.
1477         fi, err := os.Lstat(path)
1478         if err != nil {
1479                 t.Fatal(err)
1480         }
1481         if got := fi.Name(); got != name {
1482                 t.Errorf("got %q, want %q", got, name)
1483         }
1484         // Verify that File.Readdirnames lists the file.
1485         f, err := os.Open(dir)
1486         if err != nil {
1487                 t.Fatal(err)
1488         }
1489         files, err := f.Readdirnames(0)
1490         f.Close()
1491         if err != nil {
1492                 t.Fatal(err)
1493         }
1494         if !slices.Contains(files, name) {
1495                 t.Error("file not listed")
1496         }
1497         // Verify that os.RemoveAll can remove the directory
1498         // and that it doesn't hang.
1499         err = os.RemoveAll(dir)
1500         if err != nil {
1501                 t.Error(err)
1502         }
1503 }
1504
1505 func TestUTF16Alloc(t *testing.T) {
1506         allowsPerRun := func(want int, f func()) {
1507                 t.Helper()
1508                 got := int(testing.AllocsPerRun(5, f))
1509                 if got != want {
1510                         t.Errorf("got %d allocs, want %d", got, want)
1511                 }
1512         }
1513         allowsPerRun(1, func() {
1514                 syscall.UTF16ToString([]uint16{'a', 'b', 'c'})
1515         })
1516         allowsPerRun(1, func() {
1517                 syscall.UTF16FromString("abc")
1518         })
1519 }
1520
1521 func TestNewFileInvalid(t *testing.T) {
1522         t.Parallel()
1523         if f := os.NewFile(uintptr(syscall.InvalidHandle), "invalid"); f != nil {
1524                 t.Errorf("NewFile(InvalidHandle) got %v want nil", f)
1525         }
1526 }
1527
1528 func TestReadDirPipe(t *testing.T) {
1529         dir := `\\.\pipe\`
1530         fi, err := os.Stat(dir)
1531         if err != nil || !fi.IsDir() {
1532                 t.Skipf("%s is not a directory", dir)
1533         }
1534         _, err = os.ReadDir(dir)
1535         if err != nil {
1536                 t.Errorf("ReadDir(%q) = %v", dir, err)
1537         }
1538 }
1539
1540 func TestReadDirNoFileID(t *testing.T) {
1541         *os.AllowReadDirFileID = false
1542         defer func() { *os.AllowReadDirFileID = true }()
1543
1544         dir := t.TempDir()
1545         pathA := filepath.Join(dir, "a")
1546         pathB := filepath.Join(dir, "b")
1547         if err := os.WriteFile(pathA, nil, 0666); err != nil {
1548                 t.Fatal(err)
1549         }
1550         if err := os.WriteFile(pathB, nil, 0666); err != nil {
1551                 t.Fatal(err)
1552         }
1553
1554         files, err := os.ReadDir(dir)
1555         if err != nil {
1556                 t.Fatal(err)
1557         }
1558         if len(files) != 2 {
1559                 t.Fatalf("ReadDir(%q) = %v; want 2 files", dir, files)
1560         }
1561
1562         // Check that os.SameFile works with files returned by os.ReadDir.
1563         f1, err := files[0].Info()
1564         if err != nil {
1565                 t.Fatal(err)
1566         }
1567         f2, err := files[1].Info()
1568         if err != nil {
1569                 t.Fatal(err)
1570         }
1571         if !os.SameFile(f1, f1) {
1572                 t.Errorf("SameFile(%v, %v) = false; want true", f1, f1)
1573         }
1574         if !os.SameFile(f2, f2) {
1575                 t.Errorf("SameFile(%v, %v) = false; want true", f2, f2)
1576         }
1577         if os.SameFile(f1, f2) {
1578                 t.Errorf("SameFile(%v, %v) = true; want false", f1, f2)
1579         }
1580
1581         // Check that os.SameFile works with a mix of os.ReadDir and os.Stat files.
1582         f1s, err := os.Stat(pathA)
1583         if err != nil {
1584                 t.Fatal(err)
1585         }
1586         f2s, err := os.Stat(pathB)
1587         if err != nil {
1588                 t.Fatal(err)
1589         }
1590         if !os.SameFile(f1, f1s) {
1591                 t.Errorf("SameFile(%v, %v) = false; want true", f1, f1s)
1592         }
1593         if !os.SameFile(f2, f2s) {
1594                 t.Errorf("SameFile(%v, %v) = false; want true", f2, f2s)
1595         }
1596 }