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

View as plain text