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

View as plain text