...
Run Format

Source file src/os/os_windows_test.go

Documentation: os

     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  	"fmt"
     9  	"internal/poll"
    10  	"internal/syscall/windows"
    11  	"internal/syscall/windows/registry"
    12  	"internal/testenv"
    13  	"io"
    14  	"io/ioutil"
    15  	"os"
    16  	osexec "os/exec"
    17  	"path/filepath"
    18  	"reflect"
    19  	"runtime"
    20  	"sort"
    21  	"strings"
    22  	"syscall"
    23  	"testing"
    24  	"unicode/utf16"
    25  	"unsafe"
    26  )
    27  
    28  func TestSameWindowsFile(t *testing.T) {
    29  	temp, err := ioutil.TempDir("", "TestSameWindowsFile")
    30  	if err != nil {
    31  		t.Fatal(err)
    32  	}
    33  	defer os.RemoveAll(temp)
    34  
    35  	wd, err := os.Getwd()
    36  	if err != nil {
    37  		t.Fatal(err)
    38  	}
    39  	err = os.Chdir(temp)
    40  	if err != nil {
    41  		t.Fatal(err)
    42  	}
    43  	defer os.Chdir(wd)
    44  
    45  	f, err := os.Create("a")
    46  	if err != nil {
    47  		t.Fatal(err)
    48  	}
    49  	f.Close()
    50  
    51  	ia1, err := os.Stat("a")
    52  	if err != nil {
    53  		t.Fatal(err)
    54  	}
    55  
    56  	path, err := filepath.Abs("a")
    57  	if err != nil {
    58  		t.Fatal(err)
    59  	}
    60  	ia2, err := os.Stat(path)
    61  	if err != nil {
    62  		t.Fatal(err)
    63  	}
    64  	if !os.SameFile(ia1, ia2) {
    65  		t.Errorf("files should be same")
    66  	}
    67  
    68  	p := filepath.VolumeName(path) + filepath.Base(path)
    69  	if err != nil {
    70  		t.Fatal(err)
    71  	}
    72  	ia3, err := os.Stat(p)
    73  	if err != nil {
    74  		t.Fatal(err)
    75  	}
    76  	if !os.SameFile(ia1, ia3) {
    77  		t.Errorf("files should be same")
    78  	}
    79  }
    80  
    81  type dirLinkTest struct {
    82  	name    string
    83  	mklink  func(link, target string) error
    84  	issueNo int // correspondent issue number (for broken tests)
    85  }
    86  
    87  func testDirLinks(t *testing.T, tests []dirLinkTest) {
    88  	tmpdir, err := ioutil.TempDir("", "testDirLinks")
    89  	if err != nil {
    90  		t.Fatal(err)
    91  	}
    92  	defer os.RemoveAll(tmpdir)
    93  
    94  	oldwd, err := os.Getwd()
    95  	if err != nil {
    96  		t.Fatal(err)
    97  	}
    98  	err = os.Chdir(tmpdir)
    99  	if err != nil {
   100  		t.Fatal(err)
   101  	}
   102  	defer os.Chdir(oldwd)
   103  
   104  	dir := filepath.Join(tmpdir, "dir")
   105  	err = os.Mkdir(dir, 0777)
   106  	if err != nil {
   107  		t.Fatal(err)
   108  	}
   109  	fi, err := os.Stat(dir)
   110  	if err != nil {
   111  		t.Fatal(err)
   112  	}
   113  	err = ioutil.WriteFile(filepath.Join(dir, "abc"), []byte("abc"), 0644)
   114  	if err != nil {
   115  		t.Fatal(err)
   116  	}
   117  	for _, test := range tests {
   118  		link := filepath.Join(tmpdir, test.name+"_link")
   119  		err := test.mklink(link, dir)
   120  		if err != nil {
   121  			t.Errorf("creating link for %q test failed: %v", test.name, err)
   122  			continue
   123  		}
   124  
   125  		data, err := ioutil.ReadFile(filepath.Join(link, "abc"))
   126  		if err != nil {
   127  			t.Errorf("failed to read abc file: %v", err)
   128  			continue
   129  		}
   130  		if string(data) != "abc" {
   131  			t.Errorf(`abc file is expected to have "abc" in it, but has %v`, data)
   132  			continue
   133  		}
   134  
   135  		if test.issueNo > 0 {
   136  			t.Logf("skipping broken %q test: see issue %d", test.name, test.issueNo)
   137  			continue
   138  		}
   139  
   140  		fi1, err := os.Stat(link)
   141  		if err != nil {
   142  			t.Errorf("failed to stat link %v: %v", link, err)
   143  			continue
   144  		}
   145  		if !fi1.IsDir() {
   146  			t.Errorf("%q should be a directory", link)
   147  			continue
   148  		}
   149  		if fi1.Name() != filepath.Base(link) {
   150  			t.Errorf("Stat(%q).Name() = %q, want %q", link, fi1.Name(), filepath.Base(link))
   151  			continue
   152  		}
   153  		if !os.SameFile(fi, fi1) {
   154  			t.Errorf("%q should point to %q", link, dir)
   155  			continue
   156  		}
   157  
   158  		fi2, err := os.Lstat(link)
   159  		if err != nil {
   160  			t.Errorf("failed to lstat link %v: %v", link, err)
   161  			continue
   162  		}
   163  		if m := fi2.Mode(); m&os.ModeSymlink == 0 {
   164  			t.Errorf("%q should be a link, but is not (mode=0x%x)", link, uint32(m))
   165  			continue
   166  		}
   167  		if m := fi2.Mode(); m&os.ModeDir != 0 {
   168  			t.Errorf("%q should be a link, not a directory (mode=0x%x)", link, uint32(m))
   169  			continue
   170  		}
   171  	}
   172  }
   173  
   174  // reparseData is used to build reparse buffer data required for tests.
   175  type reparseData struct {
   176  	substituteName namePosition
   177  	printName      namePosition
   178  	pathBuf        []uint16
   179  }
   180  
   181  type namePosition struct {
   182  	offset uint16
   183  	length uint16
   184  }
   185  
   186  func (rd *reparseData) addUTF16s(s []uint16) (offset uint16) {
   187  	off := len(rd.pathBuf) * 2
   188  	rd.pathBuf = append(rd.pathBuf, s...)
   189  	return uint16(off)
   190  }
   191  
   192  func (rd *reparseData) addString(s string) (offset, length uint16) {
   193  	p := syscall.StringToUTF16(s)
   194  	return rd.addUTF16s(p), uint16(len(p)-1) * 2 // do not include terminating NUL in the length (as per PrintNameLength and SubstituteNameLength documentation)
   195  }
   196  
   197  func (rd *reparseData) addSubstituteName(name string) {
   198  	rd.substituteName.offset, rd.substituteName.length = rd.addString(name)
   199  }
   200  
   201  func (rd *reparseData) addPrintName(name string) {
   202  	rd.printName.offset, rd.printName.length = rd.addString(name)
   203  }
   204  
   205  func (rd *reparseData) addStringNoNUL(s string) (offset, length uint16) {
   206  	p := syscall.StringToUTF16(s)
   207  	p = p[:len(p)-1]
   208  	return rd.addUTF16s(p), uint16(len(p)) * 2
   209  }
   210  
   211  func (rd *reparseData) addSubstituteNameNoNUL(name string) {
   212  	rd.substituteName.offset, rd.substituteName.length = rd.addStringNoNUL(name)
   213  }
   214  
   215  func (rd *reparseData) addPrintNameNoNUL(name string) {
   216  	rd.printName.offset, rd.printName.length = rd.addStringNoNUL(name)
   217  }
   218  
   219  // pathBuffeLen returns length of rd pathBuf in bytes.
   220  func (rd *reparseData) pathBuffeLen() uint16 {
   221  	return uint16(len(rd.pathBuf)) * 2
   222  }
   223  
   224  // Windows REPARSE_DATA_BUFFER contains union member, and cannot be
   225  // translated into Go directly. _REPARSE_DATA_BUFFER type is to help
   226  // construct alternative versions of Windows REPARSE_DATA_BUFFER with
   227  // union part of SymbolicLinkReparseBuffer or MountPointReparseBuffer type.
   228  type _REPARSE_DATA_BUFFER struct {
   229  	header windows.REPARSE_DATA_BUFFER_HEADER
   230  	detail [syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]byte
   231  }
   232  
   233  func createDirLink(link string, rdb *_REPARSE_DATA_BUFFER) error {
   234  	err := os.Mkdir(link, 0777)
   235  	if err != nil {
   236  		return err
   237  	}
   238  
   239  	linkp := syscall.StringToUTF16(link)
   240  	fd, err := syscall.CreateFile(&linkp[0], syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING,
   241  		syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
   242  	if err != nil {
   243  		return err
   244  	}
   245  	defer syscall.CloseHandle(fd)
   246  
   247  	buflen := uint32(rdb.header.ReparseDataLength) + uint32(unsafe.Sizeof(rdb.header))
   248  	var bytesReturned uint32
   249  	return syscall.DeviceIoControl(fd, windows.FSCTL_SET_REPARSE_POINT,
   250  		(*byte)(unsafe.Pointer(&rdb.header)), buflen, nil, 0, &bytesReturned, nil)
   251  }
   252  
   253  func createMountPoint(link string, target *reparseData) error {
   254  	var buf *windows.MountPointReparseBuffer
   255  	buflen := uint16(unsafe.Offsetof(buf.PathBuffer)) + target.pathBuffeLen() // see ReparseDataLength documentation
   256  	byteblob := make([]byte, buflen)
   257  	buf = (*windows.MountPointReparseBuffer)(unsafe.Pointer(&byteblob[0]))
   258  	buf.SubstituteNameOffset = target.substituteName.offset
   259  	buf.SubstituteNameLength = target.substituteName.length
   260  	buf.PrintNameOffset = target.printName.offset
   261  	buf.PrintNameLength = target.printName.length
   262  	copy((*[2048]uint16)(unsafe.Pointer(&buf.PathBuffer[0]))[:], target.pathBuf)
   263  
   264  	var rdb _REPARSE_DATA_BUFFER
   265  	rdb.header.ReparseTag = windows.IO_REPARSE_TAG_MOUNT_POINT
   266  	rdb.header.ReparseDataLength = buflen
   267  	copy(rdb.detail[:], byteblob)
   268  
   269  	return createDirLink(link, &rdb)
   270  }
   271  
   272  func TestDirectoryJunction(t *testing.T) {
   273  	var tests = []dirLinkTest{
   274  		{
   275  			// Create link similar to what mklink does, by inserting \??\ at the front of absolute target.
   276  			name: "standard",
   277  			mklink: func(link, target string) error {
   278  				var t reparseData
   279  				t.addSubstituteName(`\??\` + target)
   280  				t.addPrintName(target)
   281  				return createMountPoint(link, &t)
   282  			},
   283  		},
   284  		{
   285  			// Do as junction utility https://technet.microsoft.com/en-au/sysinternals/bb896768.aspx does - set PrintNameLength to 0.
   286  			name: "have_blank_print_name",
   287  			mklink: func(link, target string) error {
   288  				var t reparseData
   289  				t.addSubstituteName(`\??\` + target)
   290  				t.addPrintName("")
   291  				return createMountPoint(link, &t)
   292  			},
   293  		},
   294  	}
   295  	output, _ := osexec.Command("cmd", "/c", "mklink", "/?").Output()
   296  	mklinkSupportsJunctionLinks := strings.Contains(string(output), " /J ")
   297  	if mklinkSupportsJunctionLinks {
   298  		tests = append(tests,
   299  			dirLinkTest{
   300  				name: "use_mklink_cmd",
   301  				mklink: func(link, target string) error {
   302  					output, err := osexec.Command("cmd", "/c", "mklink", "/J", link, target).CombinedOutput()
   303  					if err != nil {
   304  						t.Errorf("failed to run mklink %v %v: %v %q", link, target, err, output)
   305  					}
   306  					return nil
   307  				},
   308  			},
   309  		)
   310  	} else {
   311  		t.Log(`skipping "use_mklink_cmd" test, mklink does not supports directory junctions`)
   312  	}
   313  	testDirLinks(t, tests)
   314  }
   315  
   316  func enableCurrentThreadPrivilege(privilegeName string) error {
   317  	ct, err := windows.GetCurrentThread()
   318  	if err != nil {
   319  		return err
   320  	}
   321  	var t syscall.Token
   322  	err = windows.OpenThreadToken(ct, syscall.TOKEN_QUERY|windows.TOKEN_ADJUST_PRIVILEGES, false, &t)
   323  	if err != nil {
   324  		return err
   325  	}
   326  	defer syscall.CloseHandle(syscall.Handle(t))
   327  
   328  	var tp windows.TOKEN_PRIVILEGES
   329  
   330  	privStr, err := syscall.UTF16PtrFromString(privilegeName)
   331  	if err != nil {
   332  		return err
   333  	}
   334  	err = windows.LookupPrivilegeValue(nil, privStr, &tp.Privileges[0].Luid)
   335  	if err != nil {
   336  		return err
   337  	}
   338  	tp.PrivilegeCount = 1
   339  	tp.Privileges[0].Attributes = windows.SE_PRIVILEGE_ENABLED
   340  	return windows.AdjustTokenPrivileges(t, false, &tp, 0, nil, nil)
   341  }
   342  
   343  func createSymbolicLink(link string, target *reparseData, isrelative bool) error {
   344  	var buf *windows.SymbolicLinkReparseBuffer
   345  	buflen := uint16(unsafe.Offsetof(buf.PathBuffer)) + target.pathBuffeLen() // see ReparseDataLength documentation
   346  	byteblob := make([]byte, buflen)
   347  	buf = (*windows.SymbolicLinkReparseBuffer)(unsafe.Pointer(&byteblob[0]))
   348  	buf.SubstituteNameOffset = target.substituteName.offset
   349  	buf.SubstituteNameLength = target.substituteName.length
   350  	buf.PrintNameOffset = target.printName.offset
   351  	buf.PrintNameLength = target.printName.length
   352  	if isrelative {
   353  		buf.Flags = windows.SYMLINK_FLAG_RELATIVE
   354  	}
   355  	copy((*[2048]uint16)(unsafe.Pointer(&buf.PathBuffer[0]))[:], target.pathBuf)
   356  
   357  	var rdb _REPARSE_DATA_BUFFER
   358  	rdb.header.ReparseTag = syscall.IO_REPARSE_TAG_SYMLINK
   359  	rdb.header.ReparseDataLength = buflen
   360  	copy(rdb.detail[:], byteblob)
   361  
   362  	return createDirLink(link, &rdb)
   363  }
   364  
   365  func TestDirectorySymbolicLink(t *testing.T) {
   366  	var tests []dirLinkTest
   367  	output, _ := osexec.Command("cmd", "/c", "mklink", "/?").Output()
   368  	mklinkSupportsDirectorySymbolicLinks := strings.Contains(string(output), " /D ")
   369  	if mklinkSupportsDirectorySymbolicLinks {
   370  		tests = append(tests,
   371  			dirLinkTest{
   372  				name: "use_mklink_cmd",
   373  				mklink: func(link, target string) error {
   374  					output, err := osexec.Command("cmd", "/c", "mklink", "/D", link, target).CombinedOutput()
   375  					if err != nil {
   376  						t.Errorf("failed to run mklink %v %v: %v %q", link, target, err, output)
   377  					}
   378  					return nil
   379  				},
   380  			},
   381  		)
   382  	} else {
   383  		t.Log(`skipping "use_mklink_cmd" test, mklink does not supports directory symbolic links`)
   384  	}
   385  
   386  	// The rest of these test requires SeCreateSymbolicLinkPrivilege to be held.
   387  	runtime.LockOSThread()
   388  	defer runtime.UnlockOSThread()
   389  
   390  	err := windows.ImpersonateSelf(windows.SecurityImpersonation)
   391  	if err != nil {
   392  		t.Fatal(err)
   393  	}
   394  	defer windows.RevertToSelf()
   395  
   396  	err = enableCurrentThreadPrivilege("SeCreateSymbolicLinkPrivilege")
   397  	if err != nil {
   398  		t.Skipf(`skipping some tests, could not enable "SeCreateSymbolicLinkPrivilege": %v`, err)
   399  	}
   400  	tests = append(tests,
   401  		dirLinkTest{
   402  			name: "use_os_pkg",
   403  			mklink: func(link, target string) error {
   404  				return os.Symlink(target, link)
   405  			},
   406  		},
   407  		dirLinkTest{
   408  			// Create link similar to what mklink does, by inserting \??\ at the front of absolute target.
   409  			name: "standard",
   410  			mklink: func(link, target string) error {
   411  				var t reparseData
   412  				t.addPrintName(target)
   413  				t.addSubstituteName(`\??\` + target)
   414  				return createSymbolicLink(link, &t, false)
   415  			},
   416  		},
   417  		dirLinkTest{
   418  			name: "relative",
   419  			mklink: func(link, target string) error {
   420  				var t reparseData
   421  				t.addSubstituteNameNoNUL(filepath.Base(target))
   422  				t.addPrintNameNoNUL(filepath.Base(target))
   423  				return createSymbolicLink(link, &t, true)
   424  			},
   425  		},
   426  	)
   427  	testDirLinks(t, tests)
   428  }
   429  
   430  func TestNetworkSymbolicLink(t *testing.T) {
   431  	testenv.MustHaveSymlink(t)
   432  
   433  	const _NERR_ServerNotStarted = syscall.Errno(2114)
   434  
   435  	dir, err := ioutil.TempDir("", "TestNetworkSymbolicLink")
   436  	if err != nil {
   437  		t.Fatal(err)
   438  	}
   439  	defer os.RemoveAll(dir)
   440  
   441  	oldwd, err := os.Getwd()
   442  	if err != nil {
   443  		t.Fatal(err)
   444  	}
   445  	err = os.Chdir(dir)
   446  	if err != nil {
   447  		t.Fatal(err)
   448  	}
   449  	defer os.Chdir(oldwd)
   450  
   451  	shareName := "GoSymbolicLinkTestShare" // hope no conflictions
   452  	sharePath := filepath.Join(dir, shareName)
   453  	testDir := "TestDir"
   454  
   455  	err = os.MkdirAll(filepath.Join(sharePath, testDir), 0777)
   456  	if err != nil {
   457  		t.Fatal(err)
   458  	}
   459  
   460  	wShareName, err := syscall.UTF16PtrFromString(shareName)
   461  	if err != nil {
   462  		t.Fatal(err)
   463  	}
   464  	wSharePath, err := syscall.UTF16PtrFromString(sharePath)
   465  	if err != nil {
   466  		t.Fatal(err)
   467  	}
   468  
   469  	p := windows.SHARE_INFO_2{
   470  		Netname:     wShareName,
   471  		Type:        windows.STYPE_DISKTREE,
   472  		Remark:      nil,
   473  		Permissions: 0,
   474  		MaxUses:     1,
   475  		CurrentUses: 0,
   476  		Path:        wSharePath,
   477  		Passwd:      nil,
   478  	}
   479  
   480  	err = windows.NetShareAdd(nil, 2, (*byte)(unsafe.Pointer(&p)), nil)
   481  	if err != nil {
   482  		if err == syscall.ERROR_ACCESS_DENIED {
   483  			t.Skip("you don't have enough privileges to add network share")
   484  		}
   485  		if err == _NERR_ServerNotStarted {
   486  			t.Skip(_NERR_ServerNotStarted.Error())
   487  		}
   488  		t.Fatal(err)
   489  	}
   490  	defer func() {
   491  		err := windows.NetShareDel(nil, wShareName, 0)
   492  		if err != nil {
   493  			t.Fatal(err)
   494  		}
   495  	}()
   496  
   497  	UNCPath := `\\localhost\` + shareName + `\`
   498  
   499  	fi1, err := os.Stat(sharePath)
   500  	if err != nil {
   501  		t.Fatal(err)
   502  	}
   503  	fi2, err := os.Stat(UNCPath)
   504  	if err != nil {
   505  		t.Fatal(err)
   506  	}
   507  	if !os.SameFile(fi1, fi2) {
   508  		t.Fatalf("%q and %q should be the same directory, but not", sharePath, UNCPath)
   509  	}
   510  
   511  	target := filepath.Join(UNCPath, testDir)
   512  	link := "link"
   513  
   514  	err = os.Symlink(target, link)
   515  	if err != nil {
   516  		t.Fatal(err)
   517  	}
   518  	defer os.Remove(link)
   519  
   520  	got, err := os.Readlink(link)
   521  	if err != nil {
   522  		t.Fatal(err)
   523  	}
   524  	if got != target {
   525  		t.Errorf(`os.Readlink("%s"): got %v, want %v`, link, got, target)
   526  	}
   527  
   528  	got, err = filepath.EvalSymlinks(link)
   529  	if err != nil {
   530  		t.Fatal(err)
   531  	}
   532  	if got != target {
   533  		t.Errorf(`filepath.EvalSymlinks("%s"): got %v, want %v`, link, got, target)
   534  	}
   535  }
   536  
   537  func TestStartProcessAttr(t *testing.T) {
   538  	p, err := os.StartProcess(os.Getenv("COMSPEC"), []string{"/c", "cd"}, new(os.ProcAttr))
   539  	if err != nil {
   540  		return
   541  	}
   542  	defer p.Wait()
   543  	t.Fatalf("StartProcess expected to fail, but succeeded.")
   544  }
   545  
   546  func TestShareNotExistError(t *testing.T) {
   547  	if testing.Short() {
   548  		t.Skip("slow test that uses network; skipping")
   549  	}
   550  	_, err := os.Stat(`\\no_such_server\no_such_share\no_such_file`)
   551  	if err == nil {
   552  		t.Fatal("stat succeeded, but expected to fail")
   553  	}
   554  	if !os.IsNotExist(err) {
   555  		t.Fatalf("os.Stat failed with %q, but os.IsNotExist(err) is false", err)
   556  	}
   557  }
   558  
   559  func TestBadNetPathError(t *testing.T) {
   560  	const ERROR_BAD_NETPATH = syscall.Errno(53)
   561  	if !os.IsNotExist(ERROR_BAD_NETPATH) {
   562  		t.Fatal("os.IsNotExist(syscall.Errno(53)) is false, but want true")
   563  	}
   564  }
   565  
   566  func TestStatDir(t *testing.T) {
   567  	defer chtmpdir(t)()
   568  
   569  	f, err := os.Open(".")
   570  	if err != nil {
   571  		t.Fatal(err)
   572  	}
   573  	defer f.Close()
   574  
   575  	fi, err := f.Stat()
   576  	if err != nil {
   577  		t.Fatal(err)
   578  	}
   579  
   580  	err = os.Chdir("..")
   581  	if err != nil {
   582  		t.Fatal(err)
   583  	}
   584  
   585  	fi2, err := f.Stat()
   586  	if err != nil {
   587  		t.Fatal(err)
   588  	}
   589  
   590  	if !os.SameFile(fi, fi2) {
   591  		t.Fatal("race condition occurred")
   592  	}
   593  }
   594  
   595  func TestOpenVolumeName(t *testing.T) {
   596  	tmpdir, err := ioutil.TempDir("", "TestOpenVolumeName")
   597  	if err != nil {
   598  		t.Fatal(err)
   599  	}
   600  	defer os.RemoveAll(tmpdir)
   601  
   602  	wd, err := os.Getwd()
   603  	if err != nil {
   604  		t.Fatal(err)
   605  	}
   606  	err = os.Chdir(tmpdir)
   607  	if err != nil {
   608  		t.Fatal(err)
   609  	}
   610  	defer os.Chdir(wd)
   611  
   612  	want := []string{"file1", "file2", "file3", "gopher.txt"}
   613  	sort.Strings(want)
   614  	for _, name := range want {
   615  		err := ioutil.WriteFile(filepath.Join(tmpdir, name), nil, 0777)
   616  		if err != nil {
   617  			t.Fatal(err)
   618  		}
   619  	}
   620  
   621  	f, err := os.Open(filepath.VolumeName(tmpdir))
   622  	if err != nil {
   623  		t.Fatal(err)
   624  	}
   625  	defer f.Close()
   626  
   627  	have, err := f.Readdirnames(-1)
   628  	if err != nil {
   629  		t.Fatal(err)
   630  	}
   631  	sort.Strings(have)
   632  
   633  	if strings.Join(want, "/") != strings.Join(have, "/") {
   634  		t.Fatalf("unexpected file list %q, want %q", have, want)
   635  	}
   636  }
   637  
   638  func TestDeleteReadOnly(t *testing.T) {
   639  	tmpdir, err := ioutil.TempDir("", "TestDeleteReadOnly")
   640  	if err != nil {
   641  		t.Fatal(err)
   642  	}
   643  	defer os.RemoveAll(tmpdir)
   644  	p := filepath.Join(tmpdir, "a")
   645  	// This sets FILE_ATTRIBUTE_READONLY.
   646  	f, err := os.OpenFile(p, os.O_CREATE, 0400)
   647  	if err != nil {
   648  		t.Fatal(err)
   649  	}
   650  	f.Close()
   651  
   652  	if err = os.Chmod(p, 0400); err != nil {
   653  		t.Fatal(err)
   654  	}
   655  	if err = os.Remove(p); err != nil {
   656  		t.Fatal(err)
   657  	}
   658  }
   659  
   660  func TestStatSymlinkLoop(t *testing.T) {
   661  	testenv.MustHaveSymlink(t)
   662  
   663  	defer chtmpdir(t)()
   664  
   665  	err := os.Symlink("x", "y")
   666  	if err != nil {
   667  		t.Fatal(err)
   668  	}
   669  	defer os.Remove("y")
   670  
   671  	err = os.Symlink("y", "x")
   672  	if err != nil {
   673  		t.Fatal(err)
   674  	}
   675  	defer os.Remove("x")
   676  
   677  	_, err = os.Stat("x")
   678  	if _, ok := err.(*os.PathError); !ok {
   679  		t.Errorf("expected *PathError, got %T: %v\n", err, err)
   680  	}
   681  }
   682  
   683  func TestReadStdin(t *testing.T) {
   684  	old := poll.ReadConsole
   685  	defer func() {
   686  		poll.ReadConsole = old
   687  	}()
   688  
   689  	testConsole := os.NewConsoleFile(syscall.Stdin, "test")
   690  
   691  	var tests = []string{
   692  		"abc",
   693  		"äöü",
   694  		"\u3042",
   695  		"“hi”™",
   696  		"hello\x1aworld",
   697  		"\U0001F648\U0001F649\U0001F64A",
   698  	}
   699  
   700  	for _, consoleSize := range []int{1, 2, 3, 10, 16, 100, 1000} {
   701  		for _, readSize := range []int{1, 2, 3, 4, 5, 8, 10, 16, 20, 50, 100} {
   702  			for _, s := range tests {
   703  				t.Run(fmt.Sprintf("c%d/r%d/%s", consoleSize, readSize, s), func(t *testing.T) {
   704  					s16 := utf16.Encode([]rune(s))
   705  					poll.ReadConsole = func(h syscall.Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) error {
   706  						if inputControl != nil {
   707  							t.Fatalf("inputControl not nil")
   708  						}
   709  						n := int(toread)
   710  						if n > consoleSize {
   711  							n = consoleSize
   712  						}
   713  						n = copy((*[10000]uint16)(unsafe.Pointer(buf))[:n], s16)
   714  						s16 = s16[n:]
   715  						*read = uint32(n)
   716  						t.Logf("read %d -> %d", toread, *read)
   717  						return nil
   718  					}
   719  
   720  					var all []string
   721  					var buf []byte
   722  					chunk := make([]byte, readSize)
   723  					for {
   724  						n, err := testConsole.Read(chunk)
   725  						buf = append(buf, chunk[:n]...)
   726  						if err == io.EOF {
   727  							all = append(all, string(buf))
   728  							if len(all) >= 5 {
   729  								break
   730  							}
   731  							buf = buf[:0]
   732  						} else if err != nil {
   733  							t.Fatalf("reading %q: error: %v", s, err)
   734  						}
   735  						if len(buf) >= 2000 {
   736  							t.Fatalf("reading %q: stuck in loop: %q", s, buf)
   737  						}
   738  					}
   739  
   740  					want := strings.Split(s, "\x1a")
   741  					for len(want) < 5 {
   742  						want = append(want, "")
   743  					}
   744  					if !reflect.DeepEqual(all, want) {
   745  						t.Errorf("reading %q:\nhave %x\nwant %x", s, all, want)
   746  					}
   747  				})
   748  			}
   749  		}
   750  	}
   751  }
   752  
   753  func TestStatPagefile(t *testing.T) {
   754  	_, err := os.Stat(`c:\pagefile.sys`)
   755  	if err == nil {
   756  		return
   757  	}
   758  	if os.IsNotExist(err) {
   759  		t.Skip(`skipping because c:\pagefile.sys is not found`)
   760  	}
   761  	t.Fatal(err)
   762  }
   763  
   764  // syscallCommandLineToArgv calls syscall.CommandLineToArgv
   765  // and converts returned result into []string.
   766  func syscallCommandLineToArgv(cmd string) ([]string, error) {
   767  	var argc int32
   768  	argv, err := syscall.CommandLineToArgv(&syscall.StringToUTF16(cmd)[0], &argc)
   769  	if err != nil {
   770  		return nil, err
   771  	}
   772  	defer syscall.LocalFree(syscall.Handle(uintptr(unsafe.Pointer(argv))))
   773  
   774  	var args []string
   775  	for _, v := range (*argv)[:argc] {
   776  		args = append(args, syscall.UTF16ToString((*v)[:]))
   777  	}
   778  	return args, nil
   779  }
   780  
   781  // compareCommandLineToArgvWithSyscall ensures that
   782  // os.CommandLineToArgv(cmd) and syscall.CommandLineToArgv(cmd)
   783  // return the same result.
   784  func compareCommandLineToArgvWithSyscall(t *testing.T, cmd string) {
   785  	syscallArgs, err := syscallCommandLineToArgv(cmd)
   786  	if err != nil {
   787  		t.Fatal(err)
   788  	}
   789  	args := os.CommandLineToArgv(cmd)
   790  	if want, have := fmt.Sprintf("%q", syscallArgs), fmt.Sprintf("%q", args); want != have {
   791  		t.Errorf("testing os.commandLineToArgv(%q) failed: have %q want %q", cmd, args, syscallArgs)
   792  		return
   793  	}
   794  }
   795  
   796  func TestCmdArgs(t *testing.T) {
   797  	tmpdir, err := ioutil.TempDir("", "TestCmdArgs")
   798  	if err != nil {
   799  		t.Fatal(err)
   800  	}
   801  	defer os.RemoveAll(tmpdir)
   802  
   803  	const prog = `
   804  package main
   805  
   806  import (
   807  	"fmt"
   808  	"os"
   809  )
   810  
   811  func main() {
   812  	fmt.Printf("%q", os.Args)
   813  }
   814  `
   815  	src := filepath.Join(tmpdir, "main.go")
   816  	err = ioutil.WriteFile(src, []byte(prog), 0666)
   817  	if err != nil {
   818  		t.Fatal(err)
   819  	}
   820  
   821  	exe := filepath.Join(tmpdir, "main.exe")
   822  	cmd := osexec.Command(testenv.GoToolPath(t), "build", "-o", exe, src)
   823  	cmd.Dir = tmpdir
   824  	out, err := cmd.CombinedOutput()
   825  	if err != nil {
   826  		t.Fatalf("building main.exe failed: %v\n%s", err, out)
   827  	}
   828  
   829  	var cmds = []string{
   830  		``,
   831  		` a b c`,
   832  		` "`,
   833  		` ""`,
   834  		` """`,
   835  		` "" a`,
   836  		` "123"`,
   837  		` \"123\"`,
   838  		` \"123 456\"`,
   839  		` \\"`,
   840  		` \\\"`,
   841  		` \\\\\"`,
   842  		` \\\"x`,
   843  		` """"\""\\\"`,
   844  		` abc`,
   845  		` \\\\\""x"""y z`,
   846  		"\tb\t\"x\ty\"",
   847  		` "Брад" d e`,
   848  		// examples from https://msdn.microsoft.com/en-us/library/17w5ykft.aspx
   849  		` "abc" d e`,
   850  		` a\\b d"e f"g h`,
   851  		` a\\\"b c d`,
   852  		` a\\\\"b c" d e`,
   853  		// http://daviddeley.com/autohotkey/parameters/parameters.htm#WINARGV
   854  		// from 5.4  Examples
   855  		` CallMeIshmael`,
   856  		` "Call Me Ishmael"`,
   857  		` Cal"l Me I"shmael`,
   858  		` CallMe\"Ishmael`,
   859  		` "CallMe\"Ishmael"`,
   860  		` "Call Me Ishmael\\"`,
   861  		` "CallMe\\\"Ishmael"`,
   862  		` a\\\b`,
   863  		` "a\\\b"`,
   864  		// from 5.5  Some Common Tasks
   865  		` "\"Call Me Ishmael\""`,
   866  		` "C:\TEST A\\"`,
   867  		` "\"C:\TEST A\\\""`,
   868  		// from 5.6  The Microsoft Examples Explained
   869  		` "a b c"  d  e`,
   870  		` "ab\"c"  "\\"  d`,
   871  		` a\\\b d"e f"g h`,
   872  		` a\\\"b c d`,
   873  		` a\\\\"b c" d e`,
   874  		// from 5.7  Double Double Quote Examples (pre 2008)
   875  		` "a b c""`,
   876  		` """CallMeIshmael"""  b  c`,
   877  		` """Call Me Ishmael"""`,
   878  		` """"Call Me Ishmael"" b c`,
   879  	}
   880  	for _, cmd := range cmds {
   881  		compareCommandLineToArgvWithSyscall(t, "test"+cmd)
   882  		compareCommandLineToArgvWithSyscall(t, `"cmd line"`+cmd)
   883  		compareCommandLineToArgvWithSyscall(t, exe+cmd)
   884  
   885  		// test both syscall.EscapeArg and os.commandLineToArgv
   886  		args := os.CommandLineToArgv(exe + cmd)
   887  		out, err := osexec.Command(args[0], args[1:]...).CombinedOutput()
   888  		if err != nil {
   889  			t.Fatalf("running %q failed: %v\n%v", args, err, string(out))
   890  		}
   891  		if want, have := fmt.Sprintf("%q", args), string(out); want != have {
   892  			t.Errorf("wrong output of executing %q: have %q want %q", args, have, want)
   893  			continue
   894  		}
   895  	}
   896  }
   897  
   898  func testIsDir(t *testing.T, path string, fi os.FileInfo) {
   899  	t.Helper()
   900  	if !fi.IsDir() {
   901  		t.Errorf("%q should be a directory", path)
   902  	}
   903  	if fi.Mode()&os.ModeSymlink != 0 {
   904  		t.Errorf("%q should not be a symlink", path)
   905  	}
   906  }
   907  
   908  func findOneDriveDir() (string, error) {
   909  	// as per https://stackoverflow.com/questions/42519624/how-to-determine-location-of-onedrive-on-windows-7-and-8-in-c
   910  	const onedrivekey = `SOFTWARE\Microsoft\OneDrive`
   911  	k, err := registry.OpenKey(registry.CURRENT_USER, onedrivekey, registry.READ)
   912  	if err != nil {
   913  		return "", fmt.Errorf("OpenKey(%q) failed: %v", onedrivekey, err)
   914  	}
   915  	defer k.Close()
   916  
   917  	path, _, err := k.GetStringValue("UserFolder")
   918  	if err != nil {
   919  		return "", fmt.Errorf("reading UserFolder failed: %v", err)
   920  	}
   921  	return path, nil
   922  }
   923  
   924  // TestOneDrive verifies that OneDrive folder is a directory and not a symlink.
   925  func TestOneDrive(t *testing.T) {
   926  	dir, err := findOneDriveDir()
   927  	if err != nil {
   928  		t.Skipf("Skipping, because we did not find OneDrive directory: %v", err)
   929  	}
   930  
   931  	// test os.Stat
   932  	fi, err := os.Stat(dir)
   933  	if err != nil {
   934  		t.Fatal(err)
   935  	}
   936  	testIsDir(t, dir, fi)
   937  
   938  	// test os.Lstat
   939  	fi, err = os.Lstat(dir)
   940  	if err != nil {
   941  		t.Fatal(err)
   942  	}
   943  	testIsDir(t, dir, fi)
   944  
   945  	// test os.File.Stat
   946  	f, err := os.Open(dir)
   947  	if err != nil {
   948  		t.Fatal(err)
   949  	}
   950  	defer f.Close()
   951  
   952  	fi, err = f.Stat()
   953  	if err != nil {
   954  		t.Fatal(err)
   955  	}
   956  	testIsDir(t, dir, fi)
   957  
   958  	// test os.FileInfo returned by os.Readdir
   959  	parent, err := os.Open(filepath.Dir(dir))
   960  	if err != nil {
   961  		t.Fatal(err)
   962  	}
   963  	defer parent.Close()
   964  
   965  	fis, err := parent.Readdir(-1)
   966  	if err != nil {
   967  		t.Fatal(err)
   968  	}
   969  	fi = nil
   970  	base := filepath.Base(dir)
   971  	for _, fi2 := range fis {
   972  		if fi2.Name() == base {
   973  			fi = fi2
   974  			break
   975  		}
   976  	}
   977  	if fi == nil {
   978  		t.Errorf("failed to find %q in its parent", dir)
   979  	}
   980  	testIsDir(t, dir, fi)
   981  }
   982  
   983  func TestWindowsDevNullFile(t *testing.T) {
   984  	testDevNullFile(t, "NUL", true)
   985  	testDevNullFile(t, "nul", true)
   986  	testDevNullFile(t, "Nul", true)
   987  
   988  	f1, err := os.Open("NUL")
   989  	if err != nil {
   990  		t.Fatal(err)
   991  	}
   992  	defer f1.Close()
   993  
   994  	fi1, err := f1.Stat()
   995  	if err != nil {
   996  		t.Fatal(err)
   997  	}
   998  
   999  	f2, err := os.Open("nul")
  1000  	if err != nil {
  1001  		t.Fatal(err)
  1002  	}
  1003  	defer f2.Close()
  1004  
  1005  	fi2, err := f2.Stat()
  1006  	if err != nil {
  1007  		t.Fatal(err)
  1008  	}
  1009  
  1010  	if !os.SameFile(fi1, fi2) {
  1011  		t.Errorf(`"NUL" and "nul" are not the same file`)
  1012  	}
  1013  }
  1014  
  1015  // TestSymlinkCreation verifies that creating a symbolic link
  1016  // works on Windows when developer mode is active.
  1017  // This is supported starting Windows 10 (1703, v10.0.14972).
  1018  func TestSymlinkCreation(t *testing.T) {
  1019  	if !isWindowsDeveloperModeActive() {
  1020  		t.Skip("Windows developer mode is not active")
  1021  	}
  1022  
  1023  	temp, err := ioutil.TempDir("", "TestSymlinkCreation")
  1024  	if err != nil {
  1025  		t.Fatal(err)
  1026  	}
  1027  	defer os.RemoveAll(temp)
  1028  
  1029  	dummyFile := filepath.Join(temp, "file")
  1030  	err = ioutil.WriteFile(dummyFile, []byte(""), 0644)
  1031  	if err != nil {
  1032  		t.Fatal(err)
  1033  	}
  1034  
  1035  	linkFile := filepath.Join(temp, "link")
  1036  	err = os.Symlink(dummyFile, linkFile)
  1037  	if err != nil {
  1038  		t.Fatal(err)
  1039  	}
  1040  }
  1041  
  1042  // isWindowsDeveloperModeActive checks whether or not the developer mode is active on Windows 10.
  1043  // Returns false for prior Windows versions.
  1044  // see https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development
  1045  func isWindowsDeveloperModeActive() bool {
  1046  	key, err := registry.OpenKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", registry.READ)
  1047  	if err != nil {
  1048  		return false
  1049  	}
  1050  
  1051  	val, _, err := key.GetIntegerValue("AllowDevelopmentWithoutDevLicense")
  1052  	if err != nil {
  1053  		return false
  1054  	}
  1055  
  1056  	return val != 0
  1057  }
  1058  
  1059  // TestStatOfInvalidName is regression test for issue #24999.
  1060  func TestStatOfInvalidName(t *testing.T) {
  1061  	_, err := os.Stat("*.go")
  1062  	if err == nil {
  1063  		t.Fatal(`os.Stat("*.go") unexpectedly succeeded`)
  1064  	}
  1065  }
  1066  

View as plain text