Source file 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 an 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  }
  1597  

View as plain text