Home | History | Annotate | Download | only in 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