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

View as plain text