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

View as plain text