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

View as plain text