aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/os
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2020-01-02 15:05:27 -0800
committerIan Lance Taylor <iant@golang.org>2020-01-21 23:53:22 -0800
commit5a8ea165926cb0737ab03bc48c18dc5198ab5305 (patch)
tree962dc3357c57f019f85658f99e2e753e30201c27 /libgo/go/os
parent6ac6529e155c9baa0aaaed7aca06bd38ebda5b43 (diff)
downloadgcc-5a8ea165926cb0737ab03bc48c18dc5198ab5305.zip
gcc-5a8ea165926cb0737ab03bc48c18dc5198ab5305.tar.gz
gcc-5a8ea165926cb0737ab03bc48c18dc5198ab5305.tar.bz2
libgo: update to Go1.14beta1
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/214297
Diffstat (limited to 'libgo/go/os')
-rw-r--r--libgo/go/os/dir_gccgo.go24
-rw-r--r--libgo/go/os/env_windows.go18
-rw-r--r--libgo/go/os/error.go2
-rw-r--r--libgo/go/os/error_posix.go2
-rw-r--r--libgo/go/os/error_unix_test.go2
-rw-r--r--libgo/go/os/exec/exec.go11
-rw-r--r--libgo/go/os/exec/exec_plan9.go19
-rw-r--r--libgo/go/os/exec/exec_test.go177
-rw-r--r--libgo/go/os/exec/lp_unix.go2
-rw-r--r--libgo/go/os/exec_posix.go8
-rw-r--r--libgo/go/os/exec_unix.go2
-rw-r--r--libgo/go/os/exec_windows.go5
-rw-r--r--libgo/go/os/executable.go2
-rw-r--r--libgo/go/os/executable_procfs.go2
-rw-r--r--libgo/go/os/executable_test.go2
-rw-r--r--libgo/go/os/export_test.go1
-rw-r--r--libgo/go/os/export_unix_test.go2
-rw-r--r--libgo/go/os/file.go27
-rw-r--r--libgo/go/os/file_plan9.go2
-rw-r--r--libgo/go/os/file_posix.go2
-rw-r--r--libgo/go/os/file_unix.go22
-rw-r--r--libgo/go/os/os_test.go115
-rw-r--r--libgo/go/os/os_windows_test.go1188
-rw-r--r--libgo/go/os/path.go3
-rw-r--r--libgo/go/os/path_unix.go2
-rw-r--r--libgo/go/os/path_windows_test.go15
-rw-r--r--libgo/go/os/pipe_bsd.go2
-rw-r--r--libgo/go/os/pipe_test.go4
-rw-r--r--libgo/go/os/rawconn_test.go2
-rw-r--r--libgo/go/os/removeall_at.go1
-rw-r--r--libgo/go/os/removeall_noat.go9
-rw-r--r--libgo/go/os/removeall_test.go52
-rw-r--r--libgo/go/os/signal/doc.go8
-rw-r--r--libgo/go/os/signal/signal.go15
-rw-r--r--libgo/go/os/signal/signal_plan9.go3
-rw-r--r--libgo/go/os/signal/signal_test.go160
-rw-r--r--libgo/go/os/signal/signal_unix.go5
-rw-r--r--libgo/go/os/stat_js.go (renamed from libgo/go/os/stat_nacljs.go)2
-rw-r--r--libgo/go/os/stat_unix.go2
-rw-r--r--libgo/go/os/sys_bsd.go2
-rw-r--r--libgo/go/os/sys_nacl.go9
-rw-r--r--libgo/go/os/timeout_test.go1
-rw-r--r--libgo/go/os/user/cgo_lookup_unix.go4
-rw-r--r--libgo/go/os/user/lookup_stubs.go11
-rw-r--r--libgo/go/os/user/lookup_unix.go2
-rw-r--r--libgo/go/os/user/lookup_unix_test.go2
-rw-r--r--libgo/go/os/user/lookup_windows.go11
-rw-r--r--libgo/go/os/wait_unimp.go2
48 files changed, 1741 insertions, 225 deletions
diff --git a/libgo/go/os/dir_gccgo.go b/libgo/go/os/dir_gccgo.go
index ad77a40..171dde5 100644
--- a/libgo/go/os/dir_gccgo.go
+++ b/libgo/go/os/dir_gccgo.go
@@ -14,7 +14,10 @@ import (
// FIXME: pathconf returns long, not int.
//extern pathconf
-func libc_pathconf(*byte, int) int
+func libc_pathconf(*byte, int32) int
+
+//extern dup
+func libc_dup(int32) int32
func clen(n []byte) int {
for i := 0; i < len(n); i++ {
@@ -48,9 +51,17 @@ func (file *File) readdirnames(n int) (names []string, err error) {
}
syscall.Entersyscall()
- r := libc_fdopendir(int32(file.pfd.Sysfd))
+ fd := libc_dup(int32(file.pfd.Sysfd))
errno := syscall.GetErrno()
syscall.Exitsyscall()
+ if fd < 0 {
+ return nil, &PathError{"dup", file.name, errno}
+ }
+
+ syscall.Entersyscall()
+ r := libc_fdopendir(fd)
+ errno = syscall.GetErrno()
+ syscall.Exitsyscall()
if r == nil {
return nil, &PathError{"fdopendir", file.name, errno}
}
@@ -100,3 +111,12 @@ func (file *File) readdirnames(n int) (names []string, err error) {
}
return names, nil
}
+
+func (f *File) seekInvalidate() {
+ if f.file.dirinfo != nil {
+ syscall.Entersyscall()
+ libc_closedir(f.file.dirinfo.dir)
+ syscall.Exitsyscall()
+ f.file.dirinfo = nil
+ }
+}
diff --git a/libgo/go/os/env_windows.go b/libgo/go/os/env_windows.go
index e8f647e..b1b1ee4 100644
--- a/libgo/go/os/env_windows.go
+++ b/libgo/go/os/env_windows.go
@@ -23,16 +23,20 @@ func environForSysProcAttr(sys *syscall.SysProcAttr) (env []string, err error) {
defer windows.DestroyEnvironmentBlock(block)
blockp := uintptr(unsafe.Pointer(block))
for {
- entry := (*[(1 << 30) - 1]uint16)(unsafe.Pointer(blockp))[:]
- for i, v := range entry {
- if v == 0 {
- entry = entry[:i]
- break
- }
+
+ // find NUL terminator
+ end := unsafe.Pointer(blockp)
+ for *(*uint16)(end) != 0 {
+ end = unsafe.Pointer(uintptr(end) + 2)
}
- if len(entry) == 0 {
+
+ n := (uintptr(end) - uintptr(unsafe.Pointer(blockp))) / 2
+ if n == 0 {
+ // environment block ends with empty string
break
}
+
+ entry := (*[(1 << 30) - 1]uint16)(unsafe.Pointer(blockp))[:n:n]
env = append(env, string(utf16.Decode(entry)))
blockp += 2 * (uintptr(len(entry)) + 1)
}
diff --git a/libgo/go/os/error.go b/libgo/go/os/error.go
index 0e8e2d4..26bfe4c 100644
--- a/libgo/go/os/error.go
+++ b/libgo/go/os/error.go
@@ -110,7 +110,7 @@ func IsTimeout(err error) bool {
func underlyingErrorIs(err, target error) bool {
// Note that this function is not errors.Is:
// underlyingError only unwraps the specific error-wrapping types
- // that it historically did, not all errors.Wrapper implementations.
+ // that it historically did, not all errors implementing Unwrap().
err = underlyingError(err)
if err == target {
return true
diff --git a/libgo/go/os/error_posix.go b/libgo/go/os/error_posix.go
index 401e6f7..7d5a19c 100644
--- a/libgo/go/os/error_posix.go
+++ b/libgo/go/os/error_posix.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix darwin dragonfly freebsd hurd js,wasm linux nacl netbsd openbsd solaris windows
+// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris windows
package os
diff --git a/libgo/go/os/error_unix_test.go b/libgo/go/os/error_unix_test.go
index faf18ee..a01916e 100644
--- a/libgo/go/os/error_unix_test.go
+++ b/libgo/go/os/error_unix_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix darwin dragonfly freebsd hurd js,wasm linux nacl netbsd openbsd solaris
+// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris
package os_test
diff --git a/libgo/go/os/exec/exec.go b/libgo/go/os/exec/exec.go
index 17ef003..3474ae0 100644
--- a/libgo/go/os/exec/exec.go
+++ b/libgo/go/os/exec/exec.go
@@ -238,7 +238,6 @@ func (c *Cmd) argv() []string {
// skipStdinCopyError optionally specifies a function which reports
// whether the provided stdin copy error should be ignored.
-// It is non-nil everywhere but Plan 9, which lacks EPIPE. See exec_posix.go.
var skipStdinCopyError func(error) bool
func (c *Cmd) stdin() (f *os.File, err error) {
@@ -369,6 +368,8 @@ func lookExtensions(path, dir string) (string, error) {
// Start starts the specified command but does not wait for it to complete.
//
+// If Start returns successfully, the c.Process field will be set.
+//
// The Wait method will return the exit code and release associated resources
// once the command exits.
func (c *Cmd) Start() error {
@@ -606,8 +607,8 @@ func (c *closeOnce) close() {
// standard output when the command starts.
//
// Wait will close the pipe after seeing the command exit, so most callers
-// need not close the pipe themselves; however, an implication is that
-// it is incorrect to call Wait before all reads from the pipe have completed.
+// need not close the pipe themselves. It is thus incorrect to call Wait
+// before all reads from the pipe have completed.
// For the same reason, it is incorrect to call Run when using StdoutPipe.
// See the example for idiomatic usage.
func (c *Cmd) StdoutPipe() (io.ReadCloser, error) {
@@ -631,8 +632,8 @@ func (c *Cmd) StdoutPipe() (io.ReadCloser, error) {
// standard error when the command starts.
//
// Wait will close the pipe after seeing the command exit, so most callers
-// need not close the pipe themselves; however, an implication is that
-// it is incorrect to call Wait before all reads from the pipe have completed.
+// need not close the pipe themselves. It is thus incorrect to call Wait
+// before all reads from the pipe have completed.
// For the same reason, it is incorrect to use Run when using StderrPipe.
// See the StdoutPipe example for idiomatic usage.
func (c *Cmd) StderrPipe() (io.ReadCloser, error) {
diff --git a/libgo/go/os/exec/exec_plan9.go b/libgo/go/os/exec/exec_plan9.go
new file mode 100644
index 0000000..d90bd04
--- /dev/null
+++ b/libgo/go/os/exec/exec_plan9.go
@@ -0,0 +1,19 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package exec
+
+import "os"
+
+func init() {
+ skipStdinCopyError = func(err error) bool {
+ // Ignore hungup errors copying to stdin if the program
+ // completed successfully otherwise.
+ // See Issue 35753.
+ pe, ok := err.(*os.PathError)
+ return ok &&
+ pe.Op == "write" && pe.Path == "|1" &&
+ pe.Err.Error() == "i/o on hungup channel"
+ }
+}
diff --git a/libgo/go/os/exec/exec_test.go b/libgo/go/os/exec/exec_test.go
index cfbb87a..dce66c5 100644
--- a/libgo/go/os/exec/exec_test.go
+++ b/libgo/go/os/exec/exec_test.go
@@ -30,6 +30,45 @@ import (
"time"
)
+// haveUnexpectedFDs is set at init time to report whether any
+// file descriptors were open at program start.
+var haveUnexpectedFDs bool
+
+// unfinalizedFiles holds files that should not be finalized,
+// because that would close the associated file descriptor,
+// which we don't want to do.
+var unfinalizedFiles []*os.File
+
+func init() {
+ if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
+ return
+ }
+ if runtime.GOOS == "windows" {
+ return
+ }
+ for fd := uintptr(3); fd <= 100; fd++ {
+ if poll.IsPollDescriptor(fd) {
+ continue
+ }
+ // We have no good portable way to check whether an FD is open.
+ // We use NewFile to create a *os.File, which lets us
+ // know whether it is open, but then we have to cope with
+ // the finalizer on the *os.File.
+ f := os.NewFile(fd, "")
+ if _, err := f.Stat(); err != nil {
+ // Close the file to clear the finalizer.
+ // We expect the Close to fail.
+ f.Close()
+ } else {
+ fmt.Printf("fd %d open at test start\n", fd)
+ haveUnexpectedFDs = true
+ // Use a global variable to avoid running
+ // the finalizer, which would close the FD.
+ unfinalizedFiles = append(unfinalizedFiles, f)
+ }
+ }
+}
+
func helperCommandContext(t *testing.T, ctx context.Context, s ...string) (cmd *exec.Cmd) {
testenv.MustHaveExec(t)
@@ -40,11 +79,7 @@ func helperCommandContext(t *testing.T, ctx context.Context, s ...string) (cmd *
} else {
cmd = exec.Command(os.Args[0], cs...)
}
- cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
- path := os.Getenv("LD_LIBRARY_PATH")
- if path != "" {
- cmd.Env = append(cmd.Env, "LD_LIBRARY_PATH="+path)
- }
+ cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
return cmd
}
@@ -453,17 +488,15 @@ func numOpenFDsAndroid(t *testing.T) (n int, lsof []byte) {
return bytes.Count(lsof, []byte("\n")), lsof
}
-var testedAlreadyLeaked = false
-
// basefds returns the number of expected file descriptors
// to be present in a process at start.
-// stdin, stdout, stderr, epoll/kqueue, maybe testlog
+// stdin, stdout, stderr, epoll/kqueue, epoll/kqueue pipe, maybe testlog
func basefds() uintptr {
n := os.Stderr.Fd() + 1
// The poll (epoll/kqueue) descriptor can be numerically
// either between stderr and the testlog-fd, or after
// testlog-fd.
- if poll.IsPollDescriptor(n) {
+ for poll.IsPollDescriptor(n) {
n++
}
for _, arg := range os.Args {
@@ -474,29 +507,9 @@ func basefds() uintptr {
return n
}
-func closeUnexpectedFds(t *testing.T, m string) {
- for fd := basefds(); fd <= 101; fd++ {
- if poll.IsPollDescriptor(fd) {
- continue
- }
- err := os.NewFile(fd, "").Close()
- if err == nil {
- t.Logf("%s: Something already leaked - closed fd %d", m, fd)
- }
- }
-}
-
func TestExtraFilesFDShuffle(t *testing.T) {
t.Skip("flaky test; see https://golang.org/issue/5780")
switch runtime.GOOS {
- case "darwin":
- // TODO(cnicolaou): https://golang.org/issue/2603
- // leads to leaked file descriptors in this test when it's
- // run from a builder.
- closeUnexpectedFds(t, "TestExtraFilesFDShuffle")
- case "netbsd":
- // https://golang.org/issue/3955
- closeUnexpectedFds(t, "TestExtraFilesFDShuffle")
case "windows":
t.Skip("no operating system support; skipping")
}
@@ -591,19 +604,29 @@ func TestExtraFilesFDShuffle(t *testing.T) {
}
func TestExtraFiles(t *testing.T) {
+ if haveUnexpectedFDs {
+ // The point of this test is to make sure that any
+ // descriptors we open are marked close-on-exec.
+ // If haveUnexpectedFDs is true then there were other
+ // descriptors open when we started the test,
+ // so those descriptors are clearly not close-on-exec,
+ // and they will confuse the test. We could modify
+ // the test to expect those descriptors to remain open,
+ // but since we don't know where they came from or what
+ // they are doing, that seems fragile. For example,
+ // perhaps they are from the startup code on this
+ // system for some reason. Also, this test is not
+ // system-specific; as long as most systems do not skip
+ // the test, we will still be testing what we care about.
+ t.Skip("skipping test because test was run with FDs open")
+ }
+
testenv.MustHaveExec(t)
if runtime.GOOS == "windows" {
t.Skipf("skipping test on %q", runtime.GOOS)
}
- // Ensure that file descriptors have not already been leaked into
- // our environment.
- if !testedAlreadyLeaked {
- testedAlreadyLeaked = true
- closeUnexpectedFds(t, "TestExtraFiles")
- }
-
// Force network usage, to verify the epoll (or whatever) fd
// doesn't leak to the child,
ln, err := net.Listen("tcp", "127.0.0.1:0")
@@ -660,7 +683,7 @@ func TestExtraFiles(t *testing.T) {
c.ExtraFiles = []*os.File{tf}
err = c.Run()
if err != nil {
- t.Fatalf("Run: %v; stdout %q, stderr %q", err, stdout.Bytes(), stderr.Bytes())
+ t.Fatalf("Run: %v\n--- stdout:\n%s--- stderr:\n%s", err, stdout.Bytes(), stderr.Bytes())
}
if stdout.String() != text {
t.Errorf("got stdout %q, stderr %q; want %q on stdout", stdout.String(), stderr.String(), text)
@@ -825,55 +848,40 @@ func TestHelperProcess(*testing.T) {
fmt.Printf("ReadAll from fd 3: %v", err)
os.Exit(1)
}
- switch runtime.GOOS {
- case "dragonfly":
- // TODO(jsing): Determine why DragonFly is leaking
- // file descriptors...
- case "darwin":
- // TODO(bradfitz): broken? Sometimes.
- // https://golang.org/issue/2603
- // Skip this additional part of the test for now.
- case "netbsd":
- // TODO(jsing): This currently fails on NetBSD due to
- // the cloned file descriptors that result from opening
- // /dev/urandom.
- // https://golang.org/issue/3955
- case "illumos", "solaris":
- // TODO(aram): This fails on Solaris because libc opens
- // its own files, as it sees fit. Darwin does the same,
- // see: https://golang.org/issue/2603
- default:
- // Now verify that there are no other open fds.
- var files []*os.File
- for wantfd := basefds() + 1; wantfd <= 100; wantfd++ {
- if poll.IsPollDescriptor(wantfd) {
- continue
+ // Now verify that there are no other open fds.
+ var files []*os.File
+ for wantfd := basefds() + 1; wantfd <= 100; wantfd++ {
+ if poll.IsPollDescriptor(wantfd) {
+ continue
+ }
+ f, err := os.Open(os.Args[0])
+ if err != nil {
+ fmt.Printf("error opening file with expected fd %d: %v", wantfd, err)
+ os.Exit(1)
+ }
+ if got := f.Fd(); got != wantfd {
+ fmt.Printf("leaked parent file. fd = %d; want %d\n", got, wantfd)
+ var args []string
+ switch runtime.GOOS {
+ case "plan9":
+ args = []string{fmt.Sprintf("/proc/%d/fd", os.Getpid())}
+ case "aix":
+ args = []string{fmt.Sprint(os.Getpid())}
+ default:
+ args = []string{"-p", fmt.Sprint(os.Getpid())}
}
- f, err := os.Open(os.Args[0])
+ cmd := exec.Command(ofcmd, args...)
+ out, err := cmd.CombinedOutput()
if err != nil {
- fmt.Printf("error opening file with expected fd %d: %v", wantfd, err)
- os.Exit(1)
- }
- if got := f.Fd(); got != wantfd {
- fmt.Printf("leaked parent file. fd = %d; want %d\n", got, wantfd)
- var args []string
- switch runtime.GOOS {
- case "plan9":
- args = []string{fmt.Sprintf("/proc/%d/fd", os.Getpid())}
- case "aix":
- args = []string{fmt.Sprint(os.Getpid())}
- default:
- args = []string{"-p", fmt.Sprint(os.Getpid())}
- }
- out, _ := exec.Command(ofcmd, args...).CombinedOutput()
- fmt.Print(string(out))
- os.Exit(1)
+ fmt.Fprintf(os.Stderr, "%s failed: %v\n", strings.Join(cmd.Args, " "), err)
}
- files = append(files, f)
- }
- for _, f := range files {
- f.Close()
+ fmt.Printf("%s", out)
+ os.Exit(1)
}
+ files = append(files, f)
+ }
+ for _, f := range files {
+ f.Close()
}
// Referring to fd3 here ensures that it is not
// garbage collected, and therefore closed, while
@@ -966,11 +974,6 @@ func (delayedInfiniteReader) Read(b []byte) (int, error) {
func TestIgnorePipeErrorOnSuccess(t *testing.T) {
testenv.MustHaveExec(t)
- // We really only care about testing this on Unixy and Windowsy things.
- if runtime.GOOS == "plan9" {
- t.Skipf("skipping test on %q", runtime.GOOS)
- }
-
testWith := func(r io.Reader) func(*testing.T) {
return func(t *testing.T) {
cmd := helperCommand(t, "echo", "foo")
diff --git a/libgo/go/os/exec/lp_unix.go b/libgo/go/os/exec/lp_unix.go
index d3f174f..6a5e877 100644
--- a/libgo/go/os/exec/lp_unix.go
+++ b/libgo/go/os/exec/lp_unix.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix darwin dragonfly freebsd hurd linux nacl netbsd openbsd solaris
+// +build aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris
package exec
diff --git a/libgo/go/os/exec_posix.go b/libgo/go/os/exec_posix.go
index bb47e83..a944e4e 100644
--- a/libgo/go/os/exec_posix.go
+++ b/libgo/go/os/exec_posix.go
@@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix darwin dragonfly freebsd hurd js,wasm linux nacl netbsd openbsd solaris windows
+// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris windows
package os
import (
+ "runtime"
"syscall"
)
@@ -49,9 +50,14 @@ func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
}
pid, h, e := syscall.StartProcess(name, argv, sysattr)
+
+ // Make sure we don't run the finalizers of attr.Files.
+ runtime.KeepAlive(attr)
+
if e != nil {
return nil, &PathError{"fork/exec", name, e}
}
+
return newProcess(pid, h), nil
}
diff --git a/libgo/go/os/exec_unix.go b/libgo/go/os/exec_unix.go
index 1cb8ebc..238d755 100644
--- a/libgo/go/os/exec_unix.go
+++ b/libgo/go/os/exec_unix.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix darwin dragonfly freebsd hurd js,wasm linux nacl netbsd openbsd solaris
+// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris
package os
diff --git a/libgo/go/os/exec_windows.go b/libgo/go/os/exec_windows.go
index 38293a0..10503c5 100644
--- a/libgo/go/os/exec_windows.go
+++ b/libgo/go/os/exec_windows.go
@@ -6,11 +6,11 @@ package os
import (
"errors"
+ "internal/syscall/windows"
"runtime"
"sync/atomic"
"syscall"
"time"
- "unsafe"
)
func (p *Process) wait() (ps *ProcessState, err error) {
@@ -98,8 +98,7 @@ func findProcess(pid int) (p *Process, err error) {
}
func init() {
- p := syscall.GetCommandLine()
- cmd := syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(p))[:])
+ cmd := windows.UTF16PtrToString(syscall.GetCommandLine(), 0xffff)
if len(cmd) == 0 {
arg0, _ := Executable()
Args = []string{arg0}
diff --git a/libgo/go/os/executable.go b/libgo/go/os/executable.go
index 17eed10bc..cc3134a 100644
--- a/libgo/go/os/executable.go
+++ b/libgo/go/os/executable.go
@@ -15,8 +15,6 @@ package os
//
// The main use case is finding resources located relative to an
// executable.
-//
-// Executable is not supported on nacl.
func Executable() (string, error) {
return executable()
}
diff --git a/libgo/go/os/executable_procfs.go b/libgo/go/os/executable_procfs.go
index e690103..d2d2e5a 100644
--- a/libgo/go/os/executable_procfs.go
+++ b/libgo/go/os/executable_procfs.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build hurd linux netbsd dragonfly nacl js,wasm
+// +build hurd linux netbsd dragonfly js,wasm
package os
diff --git a/libgo/go/os/executable_test.go b/libgo/go/os/executable_test.go
index d513c87..f25ee0c 100644
--- a/libgo/go/os/executable_test.go
+++ b/libgo/go/os/executable_test.go
@@ -17,7 +17,7 @@ import (
const executable_EnvVar = "OSTEST_OUTPUT_EXECPATH"
func TestExecutable(t *testing.T) {
- testenv.MustHaveExec(t) // will also exclude nacl, which doesn't support Executable anyway
+ testenv.MustHaveExec(t)
ep, err := os.Executable()
if err != nil {
t.Fatalf("Executable failed: %v", err)
diff --git a/libgo/go/os/export_test.go b/libgo/go/os/export_test.go
index d17d5e62..812432c 100644
--- a/libgo/go/os/export_test.go
+++ b/libgo/go/os/export_test.go
@@ -9,4 +9,3 @@ package os
var Atime = atime
var LstatP = &lstat
var ErrWriteAtInAppendMode = errWriteAtInAppendMode
-var RemoveAllTestHook = &removeAllTestHook
diff --git a/libgo/go/os/export_unix_test.go b/libgo/go/os/export_unix_test.go
index 3a15aad..f4d399d 100644
--- a/libgo/go/os/export_unix_test.go
+++ b/libgo/go/os/export_unix_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix darwin dragonfly freebsd hurd js,wasm linux nacl netbsd openbsd solaris
+// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris
package os
diff --git a/libgo/go/os/file.go b/libgo/go/os/file.go
index c13babe..7995de7 100644
--- a/libgo/go/os/file.go
+++ b/libgo/go/os/file.go
@@ -228,6 +228,9 @@ func (f *File) WriteString(s string) (n int, err error) {
// bits (before umask).
// If there is an error, it will be of type *PathError.
func Mkdir(name string, perm FileMode) error {
+ if runtime.GOOS == "windows" && isWindowsNulName(name) {
+ return &PathError{"mkdir", name, syscall.ENOTDIR}
+ }
e := syscall.Mkdir(fixLongPath(name), syscallMode(perm))
if e != nil {
@@ -247,7 +250,7 @@ func Mkdir(name string, perm FileMode) error {
return nil
}
-// setStickyBit adds ModeSticky to the permision bits of path, non atomic.
+// setStickyBit adds ModeSticky to the permission bits of path, non atomic.
func setStickyBit(name string) error {
fi, err := Stat(name)
if err != nil {
@@ -408,7 +411,7 @@ func UserCacheDir() (string, error) {
// subdirectory within this one and use that.
//
// On Unix systems, it returns $XDG_CONFIG_HOME as specified by
-// https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html if
+// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html if
// non-empty, else $HOME/.config.
// On Darwin, it returns $HOME/Library/Application Support.
// On Windows, it returns %AppData%.
@@ -472,8 +475,6 @@ func UserHomeDir() (string, error) {
}
// On some geese the home directory is not always defined.
switch runtime.GOOS {
- case "nacl":
- return "/", nil
case "android":
return "/sdcard", nil
case "darwin":
@@ -560,3 +561,21 @@ func (f *File) SyscallConn() (syscall.RawConn, error) {
}
return newRawConn(f)
}
+
+// isWindowsNulName reports whether name is os.DevNull ('NUL') on Windows.
+// True is returned if name is 'NUL' whatever the case.
+func isWindowsNulName(name string) bool {
+ if len(name) != 3 {
+ return false
+ }
+ if name[0] != 'n' && name[0] != 'N' {
+ return false
+ }
+ if name[1] != 'u' && name[1] != 'U' {
+ return false
+ }
+ if name[2] != 'l' && name[2] != 'L' {
+ return false
+ }
+ return true
+}
diff --git a/libgo/go/os/file_plan9.go b/libgo/go/os/file_plan9.go
index e0a3826..48bf5f5 100644
--- a/libgo/go/os/file_plan9.go
+++ b/libgo/go/os/file_plan9.go
@@ -111,7 +111,7 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
fd, e = syscall.Create(name, flag, syscallMode(perm))
} else {
fd, e = syscall.Open(name, flag)
- if e != nil && create {
+ if IsNotExist(e) && create {
var e1 error
fd, e1 = syscall.Create(name, flag, syscallMode(perm))
if e1 == nil {
diff --git a/libgo/go/os/file_posix.go b/libgo/go/os/file_posix.go
index 2220a44..f59d137 100644
--- a/libgo/go/os/file_posix.go
+++ b/libgo/go/os/file_posix.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix darwin dragonfly freebsd hurd js,wasm linux nacl netbsd openbsd solaris windows
+// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris windows
package os
diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go
index 750771f..2fc6318 100644
--- a/libgo/go/os/file_unix.go
+++ b/libgo/go/os/file_unix.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix darwin dragonfly freebsd hurd js,wasm linux nacl netbsd openbsd solaris
+// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris
package os
@@ -27,13 +27,17 @@ func rename(oldname, newname string) error {
// At this point we've determined the newname is bad.
// But just in case oldname is also bad, prioritize returning
// the oldname error because that's what we did historically.
- if _, err := Lstat(oldname); err != nil {
+ // However, if the old name and new name are not the same, yet
+ // they refer to the same file, it implies a case-only
+ // rename on a case-insensitive filesystem, which is ok.
+ if ofi, err := Lstat(oldname); err != nil {
if pe, ok := err.(*PathError); ok {
err = pe.Err
}
return &LinkError{"rename", oldname, newname, err}
+ } else if newname == oldname || !SameFile(fi, ofi) {
+ return &LinkError{"rename", oldname, newname, syscall.EEXIST}
}
- return &LinkError{"rename", oldname, newname, syscall.EEXIST}
}
err = syscall.Rename(oldname, newname)
if err != nil {
@@ -253,13 +257,12 @@ func (file *file) close() error {
if i < 0 && errno != 0 {
err = &PathError{"closedir", file.name, errno}
}
- } else {
- if e := file.pfd.Close(); e != nil {
- if e == poll.ErrFileClosing {
- e = ErrClosed
- }
- err = &PathError{"close", file.name, e}
+ }
+ if e := file.pfd.Close(); e != nil {
+ if e == poll.ErrFileClosing {
+ e = ErrClosed
}
+ err = &PathError{"close", file.name, e}
}
// no need for a finalizer anymore
@@ -305,6 +308,7 @@ func (f *File) pwrite(b []byte, off int64) (n int, err error) {
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an error, if any.
func (f *File) seek(offset int64, whence int) (ret int64, err error) {
+ f.seekInvalidate()
ret, err = f.pfd.Seek(offset, whence)
runtime.KeepAlive(f)
return ret, err
diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go
index c27432f..a19b46d 100644
--- a/libgo/go/os/os_test.go
+++ b/libgo/go/os/os_test.go
@@ -971,6 +971,67 @@ func TestRenameToDirFailed(t *testing.T) {
}
}
+func TestRenameCaseDifference(pt *testing.T) {
+ from, to := "renameFROM", "RENAMEfrom"
+ tests := []struct {
+ name string
+ create func() error
+ }{
+ {"dir", func() error {
+ return Mkdir(from, 0777)
+ }},
+ {"file", func() error {
+ fd, err := Create(from)
+ if err != nil {
+ return err
+ }
+ return fd.Close()
+ }},
+ }
+
+ for _, test := range tests {
+ pt.Run(test.name, func(t *testing.T) {
+ defer chtmpdir(t)()
+
+ if err := test.create(); err != nil {
+ t.Fatalf("failed to create test file: %s", err)
+ }
+
+ if _, err := Stat(to); err != nil {
+ // Sanity check that the underlying filesystem is not case sensitive.
+ if IsNotExist(err) {
+ t.Skipf("case sensitive filesystem")
+ }
+ t.Fatalf("stat %q, got: %q", to, err)
+ }
+
+ if err := Rename(from, to); err != nil {
+ t.Fatalf("unexpected error when renaming from %q to %q: %s", from, to, err)
+ }
+
+ fd, err := Open(".")
+ if err != nil {
+ t.Fatalf("Open .: %s", err)
+ }
+
+ // Stat does not return the real case of the file (it returns what the called asked for)
+ // So we have to use readdir to get the real name of the file.
+ dirNames, err := fd.Readdirnames(-1)
+ if err != nil {
+ t.Fatalf("readdirnames: %s", err)
+ }
+
+ if dirNamesLen := len(dirNames); dirNamesLen != 1 {
+ t.Fatalf("unexpected dirNames len, got %q, want %q", dirNamesLen, 1)
+ }
+
+ if dirNames[0] != to {
+ t.Errorf("unexpected name, got %q, want %q", dirNames[0], to)
+ }
+ })
+ }
+}
+
func exec(t *testing.T, dir, cmd string, args []string, expect string) {
r, w, err := Pipe()
if err != nil {
@@ -1157,9 +1218,7 @@ func testChtimes(t *testing.T, name string) {
pmt := postStat.ModTime()
if !pat.Before(at) {
switch runtime.GOOS {
- case "plan9", "nacl":
- // Ignore.
- // Plan 9, NaCl:
+ case "plan9":
// Mtime is the time of the last change of
// content. Similarly, atime is set whenever
// the contents are accessed; also, it is set
@@ -1349,10 +1408,6 @@ func TestSeek(t *testing.T) {
{0, io.SeekCurrent, 2<<32 - 1},
}
for i, tt := range tests {
- if runtime.GOOS == "nacl" && tt.out > 1<<30 {
- t.Logf("skipping test case #%d on nacl; https://golang.org/issue/21728", i)
- continue
- }
if runtime.GOOS == "hurd" && tt.out > 1<<32 {
t.Logf("skipping test case #%d on Hurd: file too large", i)
continue
@@ -1373,7 +1428,7 @@ func TestSeek(t *testing.T) {
func TestSeekError(t *testing.T) {
switch runtime.GOOS {
- case "js", "nacl", "plan9":
+ case "js", "plan9":
t.Skipf("skipping test on %v", runtime.GOOS)
}
@@ -2253,8 +2308,6 @@ func TestPipeThreads(t *testing.T) {
t.Skip("skipping on Plan 9; does not support runtime poller")
case "js":
t.Skip("skipping on js; no support for os.Pipe")
- case "darwin":
- t.Skip("skipping on Darwin; issue 33953")
}
threads := 100
@@ -2355,3 +2408,45 @@ func TestUserHomeDir(t *testing.T) {
t.Fatalf("dir %s is not directory; type = %v", dir, fi.Mode())
}
}
+
+func TestDirSeek(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ testenv.SkipFlaky(t, 36019)
+ }
+ wd, err := Getwd()
+ if err != nil {
+ t.Fatal(err)
+ }
+ f, err := Open(wd)
+ if err != nil {
+ t.Fatal(err)
+ }
+ dirnames1, err := f.Readdirnames(0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ ret, err := f.Seek(0, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if ret != 0 {
+ t.Fatalf("seek result not zero: %d", ret)
+ }
+
+ dirnames2, err := f.Readdirnames(0)
+ if err != nil {
+ t.Fatal(err)
+ return
+ }
+
+ if len(dirnames1) != len(dirnames2) {
+ t.Fatalf("listings have different lengths: %d and %d\n", len(dirnames1), len(dirnames2))
+ }
+ for i, n1 := range dirnames1 {
+ n2 := dirnames2[i]
+ if n1 != n2 {
+ t.Fatalf("different name i=%d n1=%s n2=%s\n", i, n1, n2)
+ }
+ }
+}
diff --git a/libgo/go/os/os_windows_test.go b/libgo/go/os/os_windows_test.go
new file mode 100644
index 0000000..8c14103
--- /dev/null
+++ b/libgo/go/os/os_windows_test.go
@@ -0,0 +1,1188 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os_test
+
+import (
+ "errors"
+ "fmt"
+ "internal/poll"
+ "internal/syscall/windows"
+ "internal/syscall/windows/registry"
+ "internal/testenv"
+ "io"
+ "io/ioutil"
+ "os"
+ osexec "os/exec"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "sort"
+ "strings"
+ "syscall"
+ "testing"
+ "unicode/utf16"
+ "unsafe"
+)
+
+// For TestRawConnReadWrite.
+type syscallDescriptor = syscall.Handle
+
+func TestSameWindowsFile(t *testing.T) {
+ temp, err := ioutil.TempDir("", "TestSameWindowsFile")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(temp)
+
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = os.Chdir(temp)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.Chdir(wd)
+
+ f, err := os.Create("a")
+ if err != nil {
+ t.Fatal(err)
+ }
+ f.Close()
+
+ ia1, err := os.Stat("a")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ path, err := filepath.Abs("a")
+ if err != nil {
+ t.Fatal(err)
+ }
+ ia2, err := os.Stat(path)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !os.SameFile(ia1, ia2) {
+ t.Errorf("files should be same")
+ }
+
+ p := filepath.VolumeName(path) + filepath.Base(path)
+ if err != nil {
+ t.Fatal(err)
+ }
+ ia3, err := os.Stat(p)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !os.SameFile(ia1, ia3) {
+ t.Errorf("files should be same")
+ }
+}
+
+type dirLinkTest struct {
+ name string
+ mklink func(link, target string) error
+ issueNo int // correspondent issue number (for broken tests)
+}
+
+func testDirLinks(t *testing.T, tests []dirLinkTest) {
+ tmpdir, err := ioutil.TempDir("", "testDirLinks")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ oldwd, err := os.Getwd()
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = os.Chdir(tmpdir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.Chdir(oldwd)
+
+ dir := filepath.Join(tmpdir, "dir")
+ err = os.Mkdir(dir, 0777)
+ if err != nil {
+ t.Fatal(err)
+ }
+ fi, err := os.Stat(dir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = ioutil.WriteFile(filepath.Join(dir, "abc"), []byte("abc"), 0644)
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, test := range tests {
+ link := filepath.Join(tmpdir, test.name+"_link")
+ err := test.mklink(link, dir)
+ if err != nil {
+ t.Errorf("creating link for %q test failed: %v", test.name, err)
+ continue
+ }
+
+ data, err := ioutil.ReadFile(filepath.Join(link, "abc"))
+ if err != nil {
+ t.Errorf("failed to read abc file: %v", err)
+ continue
+ }
+ if string(data) != "abc" {
+ t.Errorf(`abc file is expected to have "abc" in it, but has %v`, data)
+ continue
+ }
+
+ if test.issueNo > 0 {
+ t.Logf("skipping broken %q test: see issue %d", test.name, test.issueNo)
+ continue
+ }
+
+ fi1, err := os.Stat(link)
+ if err != nil {
+ t.Errorf("failed to stat link %v: %v", link, err)
+ continue
+ }
+ if !fi1.IsDir() {
+ t.Errorf("%q should be a directory", link)
+ continue
+ }
+ if fi1.Name() != filepath.Base(link) {
+ t.Errorf("Stat(%q).Name() = %q, want %q", link, fi1.Name(), filepath.Base(link))
+ continue
+ }
+ if !os.SameFile(fi, fi1) {
+ t.Errorf("%q should point to %q", link, dir)
+ continue
+ }
+
+ fi2, err := os.Lstat(link)
+ if err != nil {
+ t.Errorf("failed to lstat link %v: %v", link, err)
+ continue
+ }
+ if m := fi2.Mode(); m&os.ModeSymlink == 0 {
+ t.Errorf("%q should be a link, but is not (mode=0x%x)", link, uint32(m))
+ continue
+ }
+ if m := fi2.Mode(); m&os.ModeDir != 0 {
+ t.Errorf("%q should be a link, not a directory (mode=0x%x)", link, uint32(m))
+ continue
+ }
+ }
+}
+
+// reparseData is used to build reparse buffer data required for tests.
+type reparseData struct {
+ substituteName namePosition
+ printName namePosition
+ pathBuf []uint16
+}
+
+type namePosition struct {
+ offset uint16
+ length uint16
+}
+
+func (rd *reparseData) addUTF16s(s []uint16) (offset uint16) {
+ off := len(rd.pathBuf) * 2
+ rd.pathBuf = append(rd.pathBuf, s...)
+ return uint16(off)
+}
+
+func (rd *reparseData) addString(s string) (offset, length uint16) {
+ p := syscall.StringToUTF16(s)
+ return rd.addUTF16s(p), uint16(len(p)-1) * 2 // do not include terminating NUL in the length (as per PrintNameLength and SubstituteNameLength documentation)
+}
+
+func (rd *reparseData) addSubstituteName(name string) {
+ rd.substituteName.offset, rd.substituteName.length = rd.addString(name)
+}
+
+func (rd *reparseData) addPrintName(name string) {
+ rd.printName.offset, rd.printName.length = rd.addString(name)
+}
+
+func (rd *reparseData) addStringNoNUL(s string) (offset, length uint16) {
+ p := syscall.StringToUTF16(s)
+ p = p[:len(p)-1]
+ return rd.addUTF16s(p), uint16(len(p)) * 2
+}
+
+func (rd *reparseData) addSubstituteNameNoNUL(name string) {
+ rd.substituteName.offset, rd.substituteName.length = rd.addStringNoNUL(name)
+}
+
+func (rd *reparseData) addPrintNameNoNUL(name string) {
+ rd.printName.offset, rd.printName.length = rd.addStringNoNUL(name)
+}
+
+// pathBuffeLen returns length of rd pathBuf in bytes.
+func (rd *reparseData) pathBuffeLen() uint16 {
+ return uint16(len(rd.pathBuf)) * 2
+}
+
+// Windows REPARSE_DATA_BUFFER contains union member, and cannot be
+// translated into Go directly. _REPARSE_DATA_BUFFER type is to help
+// construct alternative versions of Windows REPARSE_DATA_BUFFER with
+// union part of SymbolicLinkReparseBuffer or MountPointReparseBuffer type.
+type _REPARSE_DATA_BUFFER struct {
+ header windows.REPARSE_DATA_BUFFER_HEADER
+ detail [syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]byte
+}
+
+func createDirLink(link string, rdb *_REPARSE_DATA_BUFFER) error {
+ err := os.Mkdir(link, 0777)
+ if err != nil {
+ return err
+ }
+
+ linkp := syscall.StringToUTF16(link)
+ fd, err := syscall.CreateFile(&linkp[0], syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING,
+ syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
+ if err != nil {
+ return err
+ }
+ defer syscall.CloseHandle(fd)
+
+ buflen := uint32(rdb.header.ReparseDataLength) + uint32(unsafe.Sizeof(rdb.header))
+ var bytesReturned uint32
+ return syscall.DeviceIoControl(fd, windows.FSCTL_SET_REPARSE_POINT,
+ (*byte)(unsafe.Pointer(&rdb.header)), buflen, nil, 0, &bytesReturned, nil)
+}
+
+func createMountPoint(link string, target *reparseData) error {
+ var buf *windows.MountPointReparseBuffer
+ buflen := uint16(unsafe.Offsetof(buf.PathBuffer)) + target.pathBuffeLen() // see ReparseDataLength documentation
+ byteblob := make([]byte, buflen)
+ buf = (*windows.MountPointReparseBuffer)(unsafe.Pointer(&byteblob[0]))
+ buf.SubstituteNameOffset = target.substituteName.offset
+ buf.SubstituteNameLength = target.substituteName.length
+ buf.PrintNameOffset = target.printName.offset
+ buf.PrintNameLength = target.printName.length
+ pbuflen := len(target.pathBuf)
+ copy((*[2048]uint16)(unsafe.Pointer(&buf.PathBuffer[0]))[:pbuflen:pbuflen], target.pathBuf)
+
+ var rdb _REPARSE_DATA_BUFFER
+ rdb.header.ReparseTag = windows.IO_REPARSE_TAG_MOUNT_POINT
+ rdb.header.ReparseDataLength = buflen
+ copy(rdb.detail[:], byteblob)
+
+ return createDirLink(link, &rdb)
+}
+
+func TestDirectoryJunction(t *testing.T) {
+ var tests = []dirLinkTest{
+ {
+ // Create link similar to what mklink does, by inserting \??\ at the front of absolute target.
+ name: "standard",
+ mklink: func(link, target string) error {
+ var t reparseData
+ t.addSubstituteName(`\??\` + target)
+ t.addPrintName(target)
+ return createMountPoint(link, &t)
+ },
+ },
+ {
+ // Do as junction utility https://technet.microsoft.com/en-au/sysinternals/bb896768.aspx does - set PrintNameLength to 0.
+ name: "have_blank_print_name",
+ mklink: func(link, target string) error {
+ var t reparseData
+ t.addSubstituteName(`\??\` + target)
+ t.addPrintName("")
+ return createMountPoint(link, &t)
+ },
+ },
+ }
+ output, _ := osexec.Command("cmd", "/c", "mklink", "/?").Output()
+ mklinkSupportsJunctionLinks := strings.Contains(string(output), " /J ")
+ if mklinkSupportsJunctionLinks {
+ tests = append(tests,
+ dirLinkTest{
+ name: "use_mklink_cmd",
+ mklink: func(link, target string) error {
+ output, err := osexec.Command("cmd", "/c", "mklink", "/J", link, target).CombinedOutput()
+ if err != nil {
+ t.Errorf("failed to run mklink %v %v: %v %q", link, target, err, output)
+ }
+ return nil
+ },
+ },
+ )
+ } else {
+ t.Log(`skipping "use_mklink_cmd" test, mklink does not supports directory junctions`)
+ }
+ testDirLinks(t, tests)
+}
+
+func enableCurrentThreadPrivilege(privilegeName string) error {
+ ct, err := windows.GetCurrentThread()
+ if err != nil {
+ return err
+ }
+ var t syscall.Token
+ err = windows.OpenThreadToken(ct, syscall.TOKEN_QUERY|windows.TOKEN_ADJUST_PRIVILEGES, false, &t)
+ if err != nil {
+ return err
+ }
+ defer syscall.CloseHandle(syscall.Handle(t))
+
+ var tp windows.TOKEN_PRIVILEGES
+
+ privStr, err := syscall.UTF16PtrFromString(privilegeName)
+ if err != nil {
+ return err
+ }
+ err = windows.LookupPrivilegeValue(nil, privStr, &tp.Privileges[0].Luid)
+ if err != nil {
+ return err
+ }
+ tp.PrivilegeCount = 1
+ tp.Privileges[0].Attributes = windows.SE_PRIVILEGE_ENABLED
+ return windows.AdjustTokenPrivileges(t, false, &tp, 0, nil, nil)
+}
+
+func createSymbolicLink(link string, target *reparseData, isrelative bool) error {
+ var buf *windows.SymbolicLinkReparseBuffer
+ buflen := uint16(unsafe.Offsetof(buf.PathBuffer)) + target.pathBuffeLen() // see ReparseDataLength documentation
+ byteblob := make([]byte, buflen)
+ buf = (*windows.SymbolicLinkReparseBuffer)(unsafe.Pointer(&byteblob[0]))
+ buf.SubstituteNameOffset = target.substituteName.offset
+ buf.SubstituteNameLength = target.substituteName.length
+ buf.PrintNameOffset = target.printName.offset
+ buf.PrintNameLength = target.printName.length
+ if isrelative {
+ buf.Flags = windows.SYMLINK_FLAG_RELATIVE
+ }
+ pbuflen := len(target.pathBuf)
+ copy((*[2048]uint16)(unsafe.Pointer(&buf.PathBuffer[0]))[:pbuflen:pbuflen], target.pathBuf)
+
+ var rdb _REPARSE_DATA_BUFFER
+ rdb.header.ReparseTag = syscall.IO_REPARSE_TAG_SYMLINK
+ rdb.header.ReparseDataLength = buflen
+ copy(rdb.detail[:], byteblob)
+
+ return createDirLink(link, &rdb)
+}
+
+func TestDirectorySymbolicLink(t *testing.T) {
+ var tests []dirLinkTest
+ output, _ := osexec.Command("cmd", "/c", "mklink", "/?").Output()
+ mklinkSupportsDirectorySymbolicLinks := strings.Contains(string(output), " /D ")
+ if mklinkSupportsDirectorySymbolicLinks {
+ tests = append(tests,
+ dirLinkTest{
+ name: "use_mklink_cmd",
+ mklink: func(link, target string) error {
+ output, err := osexec.Command("cmd", "/c", "mklink", "/D", link, target).CombinedOutput()
+ if err != nil {
+ t.Errorf("failed to run mklink %v %v: %v %q", link, target, err, output)
+ }
+ return nil
+ },
+ },
+ )
+ } else {
+ t.Log(`skipping "use_mklink_cmd" test, mklink does not supports directory symbolic links`)
+ }
+
+ // The rest of these test requires SeCreateSymbolicLinkPrivilege to be held.
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ err := windows.ImpersonateSelf(windows.SecurityImpersonation)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer windows.RevertToSelf()
+
+ err = enableCurrentThreadPrivilege("SeCreateSymbolicLinkPrivilege")
+ if err != nil {
+ t.Skipf(`skipping some tests, could not enable "SeCreateSymbolicLinkPrivilege": %v`, err)
+ }
+ tests = append(tests,
+ dirLinkTest{
+ name: "use_os_pkg",
+ mklink: func(link, target string) error {
+ return os.Symlink(target, link)
+ },
+ },
+ dirLinkTest{
+ // Create link similar to what mklink does, by inserting \??\ at the front of absolute target.
+ name: "standard",
+ mklink: func(link, target string) error {
+ var t reparseData
+ t.addPrintName(target)
+ t.addSubstituteName(`\??\` + target)
+ return createSymbolicLink(link, &t, false)
+ },
+ },
+ dirLinkTest{
+ name: "relative",
+ mklink: func(link, target string) error {
+ var t reparseData
+ t.addSubstituteNameNoNUL(filepath.Base(target))
+ t.addPrintNameNoNUL(filepath.Base(target))
+ return createSymbolicLink(link, &t, true)
+ },
+ },
+ )
+ testDirLinks(t, tests)
+}
+
+func TestNetworkSymbolicLink(t *testing.T) {
+ testenv.MustHaveSymlink(t)
+
+ const _NERR_ServerNotStarted = syscall.Errno(2114)
+
+ dir, err := ioutil.TempDir("", "TestNetworkSymbolicLink")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ oldwd, err := os.Getwd()
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = os.Chdir(dir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.Chdir(oldwd)
+
+ shareName := "GoSymbolicLinkTestShare" // hope no conflictions
+ sharePath := filepath.Join(dir, shareName)
+ testDir := "TestDir"
+
+ err = os.MkdirAll(filepath.Join(sharePath, testDir), 0777)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ wShareName, err := syscall.UTF16PtrFromString(shareName)
+ if err != nil {
+ t.Fatal(err)
+ }
+ wSharePath, err := syscall.UTF16PtrFromString(sharePath)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ p := windows.SHARE_INFO_2{
+ Netname: wShareName,
+ Type: windows.STYPE_DISKTREE,
+ Remark: nil,
+ Permissions: 0,
+ MaxUses: 1,
+ CurrentUses: 0,
+ Path: wSharePath,
+ Passwd: nil,
+ }
+
+ err = windows.NetShareAdd(nil, 2, (*byte)(unsafe.Pointer(&p)), nil)
+ if err != nil {
+ if err == syscall.ERROR_ACCESS_DENIED {
+ t.Skip("you don't have enough privileges to add network share")
+ }
+ if err == _NERR_ServerNotStarted {
+ t.Skip(_NERR_ServerNotStarted.Error())
+ }
+ t.Fatal(err)
+ }
+ defer func() {
+ err := windows.NetShareDel(nil, wShareName, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }()
+
+ UNCPath := `\\localhost\` + shareName + `\`
+
+ fi1, err := os.Stat(sharePath)
+ if err != nil {
+ t.Fatal(err)
+ }
+ fi2, err := os.Stat(UNCPath)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !os.SameFile(fi1, fi2) {
+ t.Fatalf("%q and %q should be the same directory, but not", sharePath, UNCPath)
+ }
+
+ target := filepath.Join(UNCPath, testDir)
+ link := "link"
+
+ err = os.Symlink(target, link)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.Remove(link)
+
+ got, err := os.Readlink(link)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got != target {
+ t.Errorf(`os.Readlink("%s"): got %v, want %v`, link, got, target)
+ }
+
+ got, err = filepath.EvalSymlinks(link)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got != target {
+ t.Errorf(`filepath.EvalSymlinks("%s"): got %v, want %v`, link, got, target)
+ }
+}
+
+func TestStartProcessAttr(t *testing.T) {
+ p, err := os.StartProcess(os.Getenv("COMSPEC"), []string{"/c", "cd"}, new(os.ProcAttr))
+ if err != nil {
+ return
+ }
+ defer p.Wait()
+ t.Fatalf("StartProcess expected to fail, but succeeded.")
+}
+
+func TestShareNotExistError(t *testing.T) {
+ if testing.Short() {
+ t.Skip("slow test that uses network; skipping")
+ }
+ _, err := os.Stat(`\\no_such_server\no_such_share\no_such_file`)
+ if err == nil {
+ t.Fatal("stat succeeded, but expected to fail")
+ }
+ if !os.IsNotExist(err) {
+ t.Fatalf("os.Stat failed with %q, but os.IsNotExist(err) is false", err)
+ }
+}
+
+func TestBadNetPathError(t *testing.T) {
+ const ERROR_BAD_NETPATH = syscall.Errno(53)
+ if !os.IsNotExist(ERROR_BAD_NETPATH) {
+ t.Fatal("os.IsNotExist(syscall.Errno(53)) is false, but want true")
+ }
+}
+
+func TestStatDir(t *testing.T) {
+ defer chtmpdir(t)()
+
+ f, err := os.Open(".")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+
+ fi, err := f.Stat()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = os.Chdir("..")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ fi2, err := f.Stat()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !os.SameFile(fi, fi2) {
+ t.Fatal("race condition occurred")
+ }
+}
+
+func TestOpenVolumeName(t *testing.T) {
+ tmpdir, err := ioutil.TempDir("", "TestOpenVolumeName")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = os.Chdir(tmpdir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.Chdir(wd)
+
+ want := []string{"file1", "file2", "file3", "gopher.txt"}
+ sort.Strings(want)
+ for _, name := range want {
+ err := ioutil.WriteFile(filepath.Join(tmpdir, name), nil, 0777)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ f, err := os.Open(filepath.VolumeName(tmpdir))
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+
+ have, err := f.Readdirnames(-1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ sort.Strings(have)
+
+ if strings.Join(want, "/") != strings.Join(have, "/") {
+ t.Fatalf("unexpected file list %q, want %q", have, want)
+ }
+}
+
+func TestDeleteReadOnly(t *testing.T) {
+ tmpdir, err := ioutil.TempDir("", "TestDeleteReadOnly")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+ p := filepath.Join(tmpdir, "a")
+ // This sets FILE_ATTRIBUTE_READONLY.
+ f, err := os.OpenFile(p, os.O_CREATE, 0400)
+ if err != nil {
+ t.Fatal(err)
+ }
+ f.Close()
+
+ if err = os.Chmod(p, 0400); err != nil {
+ t.Fatal(err)
+ }
+ if err = os.Remove(p); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestStatSymlinkLoop(t *testing.T) {
+ testenv.MustHaveSymlink(t)
+
+ defer chtmpdir(t)()
+
+ err := os.Symlink("x", "y")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.Remove("y")
+
+ err = os.Symlink("y", "x")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.Remove("x")
+
+ _, err = os.Stat("x")
+ if _, ok := err.(*os.PathError); !ok {
+ t.Errorf("expected *PathError, got %T: %v\n", err, err)
+ }
+}
+
+func TestReadStdin(t *testing.T) {
+ old := poll.ReadConsole
+ defer func() {
+ poll.ReadConsole = old
+ }()
+
+ testConsole := os.NewConsoleFile(syscall.Stdin, "test")
+
+ var tests = []string{
+ "abc",
+ "äöü",
+ "\u3042",
+ "“hi”™",
+ "hello\x1aworld",
+ "\U0001F648\U0001F649\U0001F64A",
+ }
+
+ for _, consoleSize := range []int{1, 2, 3, 10, 16, 100, 1000} {
+ for _, readSize := range []int{1, 2, 3, 4, 5, 8, 10, 16, 20, 50, 100} {
+ for _, s := range tests {
+ t.Run(fmt.Sprintf("c%d/r%d/%s", consoleSize, readSize, s), func(t *testing.T) {
+ s16 := utf16.Encode([]rune(s))
+ poll.ReadConsole = func(h syscall.Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) error {
+ if inputControl != nil {
+ t.Fatalf("inputControl not nil")
+ }
+ n := int(toread)
+ if n > consoleSize {
+ n = consoleSize
+ }
+ n = copy((*[10000]uint16)(unsafe.Pointer(buf))[:n:n], s16)
+ s16 = s16[n:]
+ *read = uint32(n)
+ t.Logf("read %d -> %d", toread, *read)
+ return nil
+ }
+
+ var all []string
+ var buf []byte
+ chunk := make([]byte, readSize)
+ for {
+ n, err := testConsole.Read(chunk)
+ buf = append(buf, chunk[:n]...)
+ if err == io.EOF {
+ all = append(all, string(buf))
+ if len(all) >= 5 {
+ break
+ }
+ buf = buf[:0]
+ } else if err != nil {
+ t.Fatalf("reading %q: error: %v", s, err)
+ }
+ if len(buf) >= 2000 {
+ t.Fatalf("reading %q: stuck in loop: %q", s, buf)
+ }
+ }
+
+ want := strings.Split(s, "\x1a")
+ for len(want) < 5 {
+ want = append(want, "")
+ }
+ if !reflect.DeepEqual(all, want) {
+ t.Errorf("reading %q:\nhave %x\nwant %x", s, all, want)
+ }
+ })
+ }
+ }
+ }
+}
+
+func TestStatPagefile(t *testing.T) {
+ fi, err := os.Stat(`c:\pagefile.sys`)
+ if err == nil {
+ if fi.Name() == "" {
+ t.Fatal(`FileInfo of c:\pagefile.sys has empty name`)
+ }
+ return
+ }
+ if os.IsNotExist(err) {
+ t.Skip(`skipping because c:\pagefile.sys is not found`)
+ }
+ t.Fatal(err)
+}
+
+// syscallCommandLineToArgv calls syscall.CommandLineToArgv
+// and converts returned result into []string.
+func syscallCommandLineToArgv(cmd string) ([]string, error) {
+ var argc int32
+ argv, err := syscall.CommandLineToArgv(&syscall.StringToUTF16(cmd)[0], &argc)
+ if err != nil {
+ return nil, err
+ }
+ defer syscall.LocalFree(syscall.Handle(uintptr(unsafe.Pointer(argv))))
+
+ var args []string
+ for _, v := range (*argv)[:argc] {
+ args = append(args, syscall.UTF16ToString((*v)[:]))
+ }
+ return args, nil
+}
+
+// compareCommandLineToArgvWithSyscall ensures that
+// os.CommandLineToArgv(cmd) and syscall.CommandLineToArgv(cmd)
+// return the same result.
+func compareCommandLineToArgvWithSyscall(t *testing.T, cmd string) {
+ syscallArgs, err := syscallCommandLineToArgv(cmd)
+ if err != nil {
+ t.Fatal(err)
+ }
+ args := os.CommandLineToArgv(cmd)
+ if want, have := fmt.Sprintf("%q", syscallArgs), fmt.Sprintf("%q", args); want != have {
+ t.Errorf("testing os.commandLineToArgv(%q) failed: have %q want %q", cmd, args, syscallArgs)
+ return
+ }
+}
+
+func TestCmdArgs(t *testing.T) {
+ tmpdir, err := ioutil.TempDir("", "TestCmdArgs")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ const prog = `
+package main
+
+import (
+ "fmt"
+ "os"
+)
+
+func main() {
+ fmt.Printf("%q", os.Args)
+}
+`
+ src := filepath.Join(tmpdir, "main.go")
+ err = ioutil.WriteFile(src, []byte(prog), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ exe := filepath.Join(tmpdir, "main.exe")
+ cmd := osexec.Command(testenv.GoToolPath(t), "build", "-o", exe, src)
+ cmd.Dir = tmpdir
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("building main.exe failed: %v\n%s", err, out)
+ }
+
+ var cmds = []string{
+ ``,
+ ` a b c`,
+ ` "`,
+ ` ""`,
+ ` """`,
+ ` "" a`,
+ ` "123"`,
+ ` \"123\"`,
+ ` \"123 456\"`,
+ ` \\"`,
+ ` \\\"`,
+ ` \\\\\"`,
+ ` \\\"x`,
+ ` """"\""\\\"`,
+ ` abc`,
+ ` \\\\\""x"""y z`,
+ "\tb\t\"x\ty\"",
+ ` "Брад" d e`,
+ // examples from https://msdn.microsoft.com/en-us/library/17w5ykft.aspx
+ ` "abc" d e`,
+ ` a\\b d"e f"g h`,
+ ` a\\\"b c d`,
+ ` a\\\\"b c" d e`,
+ // http://daviddeley.com/autohotkey/parameters/parameters.htm#WINARGV
+ // from 5.4 Examples
+ ` CallMeIshmael`,
+ ` "Call Me Ishmael"`,
+ ` Cal"l Me I"shmael`,
+ ` CallMe\"Ishmael`,
+ ` "CallMe\"Ishmael"`,
+ ` "Call Me Ishmael\\"`,
+ ` "CallMe\\\"Ishmael"`,
+ ` a\\\b`,
+ ` "a\\\b"`,
+ // from 5.5 Some Common Tasks
+ ` "\"Call Me Ishmael\""`,
+ ` "C:\TEST A\\"`,
+ ` "\"C:\TEST A\\\""`,
+ // from 5.6 The Microsoft Examples Explained
+ ` "a b c" d e`,
+ ` "ab\"c" "\\" d`,
+ ` a\\\b d"e f"g h`,
+ ` a\\\"b c d`,
+ ` a\\\\"b c" d e`,
+ // from 5.7 Double Double Quote Examples (pre 2008)
+ ` "a b c""`,
+ ` """CallMeIshmael""" b c`,
+ ` """Call Me Ishmael"""`,
+ ` """"Call Me Ishmael"" b c`,
+ }
+ for _, cmd := range cmds {
+ compareCommandLineToArgvWithSyscall(t, "test"+cmd)
+ compareCommandLineToArgvWithSyscall(t, `"cmd line"`+cmd)
+ compareCommandLineToArgvWithSyscall(t, exe+cmd)
+
+ // test both syscall.EscapeArg and os.commandLineToArgv
+ args := os.CommandLineToArgv(exe + cmd)
+ out, err := osexec.Command(args[0], args[1:]...).CombinedOutput()
+ if err != nil {
+ t.Fatalf("running %q failed: %v\n%v", args, err, string(out))
+ }
+ if want, have := fmt.Sprintf("%q", args), string(out); want != have {
+ t.Errorf("wrong output of executing %q: have %q want %q", args, have, want)
+ continue
+ }
+ }
+}
+
+func findOneDriveDir() (string, error) {
+ // as per https://stackoverflow.com/questions/42519624/how-to-determine-location-of-onedrive-on-windows-7-and-8-in-c
+ const onedrivekey = `SOFTWARE\Microsoft\OneDrive`
+ k, err := registry.OpenKey(registry.CURRENT_USER, onedrivekey, registry.READ)
+ if err != nil {
+ return "", fmt.Errorf("OpenKey(%q) failed: %v", onedrivekey, err)
+ }
+ defer k.Close()
+
+ path, _, err := k.GetStringValue("UserFolder")
+ if err != nil {
+ return "", fmt.Errorf("reading UserFolder failed: %v", err)
+ }
+ return path, nil
+}
+
+// TestOneDrive verifies that OneDrive folder is a directory and not a symlink.
+func TestOneDrive(t *testing.T) {
+ dir, err := findOneDriveDir()
+ if err != nil {
+ t.Skipf("Skipping, because we did not find OneDrive directory: %v", err)
+ }
+ testDirStats(t, dir)
+}
+
+func TestWindowsDevNullFile(t *testing.T) {
+ testDevNullFile(t, "NUL", true)
+ testDevNullFile(t, "nul", true)
+ testDevNullFile(t, "Nul", true)
+
+ f1, err := os.Open("NUL")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f1.Close()
+
+ fi1, err := f1.Stat()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ f2, err := os.Open("nul")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f2.Close()
+
+ fi2, err := f2.Stat()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !os.SameFile(fi1, fi2) {
+ t.Errorf(`"NUL" and "nul" are not the same file`)
+ }
+}
+
+// TestSymlinkCreation verifies that creating a symbolic link
+// works on Windows when developer mode is active.
+// This is supported starting Windows 10 (1703, v10.0.14972).
+func TestSymlinkCreation(t *testing.T) {
+ if !isWindowsDeveloperModeActive() {
+ t.Skip("Windows developer mode is not active")
+ }
+
+ temp, err := ioutil.TempDir("", "TestSymlinkCreation")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(temp)
+
+ dummyFile := filepath.Join(temp, "file")
+ err = ioutil.WriteFile(dummyFile, []byte(""), 0644)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ linkFile := filepath.Join(temp, "link")
+ err = os.Symlink(dummyFile, linkFile)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+// isWindowsDeveloperModeActive checks whether or not the developer mode is active on Windows 10.
+// Returns false for prior Windows versions.
+// see https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development
+func isWindowsDeveloperModeActive() bool {
+ key, err := registry.OpenKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", registry.READ)
+ if err != nil {
+ return false
+ }
+
+ val, _, err := key.GetIntegerValue("AllowDevelopmentWithoutDevLicense")
+ if err != nil {
+ return false
+ }
+
+ return val != 0
+}
+
+// TestStatOfInvalidName is regression test for issue #24999.
+func TestStatOfInvalidName(t *testing.T) {
+ _, err := os.Stat("*.go")
+ if err == nil {
+ t.Fatal(`os.Stat("*.go") unexpectedly succeeded`)
+ }
+}
+
+// findUnusedDriveLetter searches mounted drive list on the system
+// (starting from Z: and ending at D:) for unused drive letter.
+// It returns path to the found drive root directory (like Z:\) or error.
+func findUnusedDriveLetter() (string, error) {
+ // Do not use A: and B:, because they are reserved for floppy drive.
+ // Do not use C:, because it is normally used for main drive.
+ for l := 'Z'; l >= 'D'; l-- {
+ p := string(l) + `:\`
+ _, err := os.Stat(p)
+ if os.IsNotExist(err) {
+ return p, nil
+ }
+ }
+ return "", errors.New("Could not find unused drive letter.")
+}
+
+func TestRootDirAsTemp(t *testing.T) {
+ testenv.MustHaveExec(t)
+
+ if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
+ fmt.Print(os.TempDir())
+ os.Exit(0)
+ }
+
+ newtmp, err := findUnusedDriveLetter()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ cmd := osexec.Command(os.Args[0], "-test.run=TestRootDirAsTemp")
+ cmd.Env = os.Environ()
+ cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
+ cmd.Env = append(cmd.Env, "TMP="+newtmp)
+ cmd.Env = append(cmd.Env, "TEMP="+newtmp)
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("Failed to spawn child process: %v %q", err, string(output))
+ }
+ if want, have := newtmp, string(output); have != want {
+ t.Fatalf("unexpected child process output %q, want %q", have, want)
+ }
+}
+
+func testReadlink(t *testing.T, path, want string) {
+ got, err := os.Readlink(path)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if got != want {
+ t.Errorf(`Readlink(%q): got %q, want %q`, path, got, want)
+ }
+}
+
+func mklink(t *testing.T, link, target string) {
+ output, err := osexec.Command("cmd", "/c", "mklink", link, target).CombinedOutput()
+ if err != nil {
+ t.Fatalf("failed to run mklink %v %v: %v %q", link, target, err, output)
+ }
+}
+
+func mklinkj(t *testing.T, link, target string) {
+ output, err := osexec.Command("cmd", "/c", "mklink", "/J", link, target).CombinedOutput()
+ if err != nil {
+ t.Fatalf("failed to run mklink %v %v: %v %q", link, target, err, output)
+ }
+}
+
+func mklinkd(t *testing.T, link, target string) {
+ output, err := osexec.Command("cmd", "/c", "mklink", "/D", link, target).CombinedOutput()
+ if err != nil {
+ t.Fatalf("failed to run mklink %v %v: %v %q", link, target, err, output)
+ }
+}
+
+func TestWindowsReadlink(t *testing.T) {
+ tmpdir, err := ioutil.TempDir("", "TestWindowsReadlink")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ // Make sure tmpdir is not a symlink, otherwise tests will fail.
+ tmpdir, err = filepath.EvalSymlinks(tmpdir)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = os.Chdir(tmpdir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.Chdir(wd)
+
+ vol := filepath.VolumeName(tmpdir)
+ output, err := osexec.Command("cmd", "/c", "mountvol", vol, "/L").CombinedOutput()
+ if err != nil {
+ t.Fatalf("failed to run mountvol %v /L: %v %q", vol, err, output)
+ }
+ ntvol := strings.Trim(string(output), " \n\r")
+
+ dir := filepath.Join(tmpdir, "dir")
+ err = os.MkdirAll(dir, 0777)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ absdirjlink := filepath.Join(tmpdir, "absdirjlink")
+ mklinkj(t, absdirjlink, dir)
+ testReadlink(t, absdirjlink, dir)
+
+ ntdirjlink := filepath.Join(tmpdir, "ntdirjlink")
+ mklinkj(t, ntdirjlink, ntvol+absdirjlink[len(filepath.VolumeName(absdirjlink)):])
+ testReadlink(t, ntdirjlink, absdirjlink)
+
+ ntdirjlinktolink := filepath.Join(tmpdir, "ntdirjlinktolink")
+ mklinkj(t, ntdirjlinktolink, ntvol+absdirjlink[len(filepath.VolumeName(absdirjlink)):])
+ testReadlink(t, ntdirjlinktolink, absdirjlink)
+
+ mklinkj(t, "reldirjlink", "dir")
+ testReadlink(t, "reldirjlink", dir) // relative directory junction resolves to absolute path
+
+ // Make sure we have sufficient privilege to run mklink command.
+ testenv.MustHaveSymlink(t)
+
+ absdirlink := filepath.Join(tmpdir, "absdirlink")
+ mklinkd(t, absdirlink, dir)
+ testReadlink(t, absdirlink, dir)
+
+ ntdirlink := filepath.Join(tmpdir, "ntdirlink")
+ mklinkd(t, ntdirlink, ntvol+absdirlink[len(filepath.VolumeName(absdirlink)):])
+ testReadlink(t, ntdirlink, absdirlink)
+
+ mklinkd(t, "reldirlink", "dir")
+ testReadlink(t, "reldirlink", "dir")
+
+ file := filepath.Join(tmpdir, "file")
+ err = ioutil.WriteFile(file, []byte(""), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ filelink := filepath.Join(tmpdir, "filelink")
+ mklink(t, filelink, file)
+ testReadlink(t, filelink, file)
+
+ linktofilelink := filepath.Join(tmpdir, "linktofilelink")
+ mklink(t, linktofilelink, ntvol+filelink[len(filepath.VolumeName(filelink)):])
+ testReadlink(t, linktofilelink, filelink)
+
+ mklink(t, "relfilelink", "file")
+ testReadlink(t, "relfilelink", "file")
+}
+
+// os.Mkdir(os.DevNull) fails.
+func TestMkdirDevNull(t *testing.T) {
+ err := os.Mkdir(os.DevNull, 777)
+ oserr, ok := err.(*os.PathError)
+ if !ok {
+ t.Fatalf("error (%T) is not *os.PathError", err)
+ }
+ errno, ok := oserr.Err.(syscall.Errno)
+ if !ok {
+ t.Fatalf("error (%T) is not syscall.Errno", oserr)
+ }
+ if errno != syscall.ENOTDIR {
+ t.Fatalf("error %d is not syscall.ENOTDIR", errno)
+ }
+}
diff --git a/libgo/go/os/path.go b/libgo/go/os/path.go
index 9d7ecad..ba43ea3 100644
--- a/libgo/go/os/path.go
+++ b/libgo/go/os/path.go
@@ -58,9 +58,6 @@ func MkdirAll(path string, perm FileMode) error {
return nil
}
-// removeAllTestHook is a hook for testing.
-var removeAllTestHook = func(err error) error { return err }
-
// RemoveAll removes path and any children it contains.
// It removes everything it can but returns the first error
// it encounters. If the path does not exist, RemoveAll
diff --git a/libgo/go/os/path_unix.go b/libgo/go/os/path_unix.go
index 4c97f39..6ef6560 100644
--- a/libgo/go/os/path_unix.go
+++ b/libgo/go/os/path_unix.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix darwin dragonfly freebsd hurd js,wasm linux nacl netbsd openbsd solaris
+// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris
package os
diff --git a/libgo/go/os/path_windows_test.go b/libgo/go/os/path_windows_test.go
index f1745ad..862b404 100644
--- a/libgo/go/os/path_windows_test.go
+++ b/libgo/go/os/path_windows_test.go
@@ -74,3 +74,18 @@ func TestMkdirAllExtendedLength(t *testing.T) {
t.Fatalf("MkdirAll(%q) should have failed, but did not", path)
}
}
+
+func TestOpenRootSlash(t *testing.T) {
+ tests := []string{
+ `/`,
+ `\`,
+ }
+
+ for _, test := range tests {
+ dir, err := os.Open(test)
+ if err != nil {
+ t.Fatalf("Open(%q) failed: %v", test, err)
+ }
+ dir.Close()
+ }
+}
diff --git a/libgo/go/os/pipe_bsd.go b/libgo/go/os/pipe_bsd.go
index dc4c951..0d2d82f 100644
--- a/libgo/go/os/pipe_bsd.go
+++ b/libgo/go/os/pipe_bsd.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix darwin dragonfly js,wasm nacl solaris
+// +build aix darwin dragonfly js,wasm solaris
package os
diff --git a/libgo/go/os/pipe_test.go b/libgo/go/os/pipe_test.go
index 4c53bc9..2e93e39 100644
--- a/libgo/go/os/pipe_test.go
+++ b/libgo/go/os/pipe_test.go
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
// Test broken pipes on Unix systems.
-// +build !plan9,!nacl,!js
+// +build !plan9,!js
package os_test
@@ -428,7 +428,7 @@ func TestFdReadRace(t *testing.T) {
go func() {
defer wg.Done()
var buf [10]byte
- r.SetReadDeadline(time.Now().Add(time.Second))
+ r.SetReadDeadline(time.Now().Add(time.Minute))
c <- true
if _, err := r.Read(buf[:]); os.IsTimeout(err) {
t.Error("read timed out")
diff --git a/libgo/go/os/rawconn_test.go b/libgo/go/os/rawconn_test.go
index 820150d..2554f5b 100644
--- a/libgo/go/os/rawconn_test.go
+++ b/libgo/go/os/rawconn_test.go
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
// Test use of raw connections.
-// +build !plan9,!nacl,!js
+// +build !plan9,!js
package os_test
diff --git a/libgo/go/os/removeall_at.go b/libgo/go/os/removeall_at.go
index 377aa49..afee01d 100644
--- a/libgo/go/os/removeall_at.go
+++ b/libgo/go/os/removeall_at.go
@@ -153,7 +153,6 @@ func removeAllFrom(parent *File, base string) error {
// Remove the directory itself.
unlinkError := unix.Unlinkat(parentFd, base, unix.AT_REMOVEDIR)
- unlinkError = removeAllTestHook(unlinkError)
if unlinkError == nil || IsNotExist(unlinkError) {
return nil
}
diff --git a/libgo/go/os/removeall_noat.go b/libgo/go/os/removeall_noat.go
index cf26bdb..fb9b45f 100644
--- a/libgo/go/os/removeall_noat.go
+++ b/libgo/go/os/removeall_noat.go
@@ -8,6 +8,7 @@ package os
import (
"io"
+ "runtime"
"syscall"
)
@@ -124,10 +125,16 @@ func removeAll(path string) error {
// Remove directory.
err1 := Remove(path)
- err1 = removeAllTestHook(err1)
if err1 == nil || IsNotExist(err1) {
return nil
}
+ if runtime.GOOS == "windows" && IsPermission(err1) {
+ if fs, err := Stat(path); err == nil {
+ if err = Chmod(path, FileMode(0200|int(fs.Mode()))); err == nil {
+ err1 = Remove(path)
+ }
+ }
+ }
if err == nil {
err = err1
}
diff --git a/libgo/go/os/removeall_test.go b/libgo/go/os/removeall_test.go
index 4d556f9..8a71f68 100644
--- a/libgo/go/os/removeall_test.go
+++ b/libgo/go/os/removeall_test.go
@@ -5,7 +5,6 @@
package os_test
import (
- "errors"
"fmt"
"io/ioutil"
. "os"
@@ -289,7 +288,7 @@ func TestRemoveReadOnlyDir(t *testing.T) {
// Issue #29983.
func TestRemoveAllButReadOnlyAndPathError(t *testing.T) {
switch runtime.GOOS {
- case "nacl", "js", "windows":
+ case "js", "windows":
t.Skipf("skipping test on %s", runtime.GOOS)
}
@@ -379,7 +378,7 @@ func TestRemoveAllButReadOnlyAndPathError(t *testing.T) {
func TestRemoveUnreadableDir(t *testing.T) {
switch runtime.GOOS {
- case "nacl", "js", "windows":
+ case "js":
t.Skipf("skipping test on %s", runtime.GOOS)
}
@@ -413,14 +412,6 @@ func TestRemoveAllWithMoreErrorThanReqSize(t *testing.T) {
t.Skip("skipping in short mode")
}
- defer func(oldHook func(error) error) {
- *RemoveAllTestHook = oldHook
- }(*RemoveAllTestHook)
-
- *RemoveAllTestHook = func(err error) error {
- return errors.New("error from RemoveAllTestHook")
- }
-
tmpDir, err := ioutil.TempDir("", "TestRemoveAll-")
if err != nil {
t.Fatal(err)
@@ -429,7 +420,7 @@ func TestRemoveAllWithMoreErrorThanReqSize(t *testing.T) {
path := filepath.Join(tmpDir, "_TestRemoveAllWithMoreErrorThanReqSize_")
- // Make directory with 1025 files and remove.
+ // Make directory with 1025 read-only files.
if err := MkdirAll(path, 0777); err != nil {
t.Fatalf("MkdirAll %q: %s", path, err)
}
@@ -442,13 +433,38 @@ func TestRemoveAllWithMoreErrorThanReqSize(t *testing.T) {
fd.Close()
}
- // This call should not hang
- if err := RemoveAll(path); err == nil {
- t.Fatal("Want error from RemoveAllTestHook, got nil")
+ // Make the parent directory read-only. On some platforms, this is what
+ // prevents os.Remove from removing the files within that directory.
+ if err := Chmod(path, 0555); err != nil {
+ t.Fatal(err)
}
+ defer Chmod(path, 0755)
- // We hook to inject error, but the actual files must be deleted
- if _, err := Lstat(path); err == nil {
- t.Fatal("directory must be deleted even with removeAllTetHook run")
+ // This call should not hang, even on a platform that disallows file deletion
+ // from read-only directories.
+ err = RemoveAll(path)
+
+ if Getuid() == 0 {
+ // On many platforms, root can remove files from read-only directories.
+ return
+ }
+ if err == nil {
+ if runtime.GOOS == "windows" {
+ // Marking a directory as read-only in Windows does not prevent the RemoveAll
+ // from creating or removing files within it.
+ return
+ }
+ t.Fatal("RemoveAll(<read-only directory>) = nil; want error")
+ }
+
+ dir, err := Open(path)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer dir.Close()
+
+ names, _ := dir.Readdirnames(1025)
+ if len(names) < 1025 {
+ t.Fatalf("RemoveAll(<read-only directory>) unexpectedly removed %d read-only files from that directory", 1025-len(names))
}
}
diff --git a/libgo/go/os/signal/doc.go b/libgo/go/os/signal/doc.go
index 16f49c7..2229d36 100644
--- a/libgo/go/os/signal/doc.go
+++ b/libgo/go/os/signal/doc.go
@@ -211,6 +211,14 @@ will cause os.Interrupt to be sent on the channel, and the program will
not exit. If Reset is called, or Stop is called on all channels passed
to Notify, then the default behavior will be restored.
+Additionally, if Notify is called, and Windows sends CTRL_CLOSE_EVENT,
+CTRL_LOGOFF_EVENT or CTRL_SHUTDOWN_EVENT to the process, Notify will
+return syscall.SIGTERM. Unlike Control-C and Control-Break, Notify does
+not change process behavior when either CTRL_CLOSE_EVENT,
+CTRL_LOGOFF_EVENT or CTRL_SHUTDOWN_EVENT is received - the process will
+still get terminated unless it exits. But receiving syscall.SIGTERM will
+give the process an opportunity to clean up before termination.
+
Plan 9
On Plan 9, signals have type syscall.Note, which is a string. Calling
diff --git a/libgo/go/os/signal/signal.go b/libgo/go/os/signal/signal.go
index a0eba0d..136dd9c 100644
--- a/libgo/go/os/signal/signal.go
+++ b/libgo/go/os/signal/signal.go
@@ -92,6 +92,15 @@ func Ignored(sig os.Signal) bool {
return sn >= 0 && signalIgnored(sn)
}
+var (
+ // watchSignalLoopOnce guards calling the conditionally
+ // initialized watchSignalLoop. If watchSignalLoop is non-nil,
+ // it will be run in a goroutine lazily once Notify is invoked.
+ // See Issue 21576.
+ watchSignalLoopOnce sync.Once
+ watchSignalLoop func()
+)
+
// Notify causes package signal to relay incoming signals to c.
// If no signals are provided, all incoming signals will be relayed to c.
// Otherwise, just the provided signals will.
@@ -113,6 +122,12 @@ func Notify(c chan<- os.Signal, sig ...os.Signal) {
panic("os/signal: Notify using nil channel")
}
+ watchSignalLoopOnce.Do(func() {
+ if watchSignalLoop != nil {
+ go watchSignalLoop()
+ }
+ })
+
handlers.Lock()
defer handlers.Unlock()
diff --git a/libgo/go/os/signal/signal_plan9.go b/libgo/go/os/signal/signal_plan9.go
index a1eb688..8408607 100644
--- a/libgo/go/os/signal/signal_plan9.go
+++ b/libgo/go/os/signal/signal_plan9.go
@@ -20,7 +20,8 @@ func signal_recv() string
func init() {
signal_enable(0) // first call - initialize
- go loop()
+
+ watchSignalLoop = loop
}
func loop() {
diff --git a/libgo/go/os/signal/signal_test.go b/libgo/go/os/signal/signal_test.go
index fe5893e..0708d4c 100644
--- a/libgo/go/os/signal/signal_test.go
+++ b/libgo/go/os/signal/signal_test.go
@@ -22,15 +22,51 @@ import (
"time"
)
+var testDeadline time.Time
+
+func TestMain(m *testing.M) {
+ flag.Parse()
+
+ // TODO(golang.org/issue/28135): Remove this setup and use t.Deadline instead.
+ timeoutFlag := flag.Lookup("test.timeout")
+ if timeoutFlag != nil {
+ if d := timeoutFlag.Value.(flag.Getter).Get().(time.Duration); d != 0 {
+ testDeadline = time.Now().Add(d)
+ }
+ }
+
+ os.Exit(m.Run())
+}
+
func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) {
- select {
- case s := <-c:
- if s != sig {
- t.Fatalf("signal was %v, want %v", s, sig)
+ waitSig1(t, c, sig, false)
+}
+func waitSigAll(t *testing.T, c <-chan os.Signal, sig os.Signal) {
+ waitSig1(t, c, sig, true)
+}
+
+func waitSig1(t *testing.T, c <-chan os.Signal, sig os.Signal, all bool) {
+ // Sleep multiple times to give the kernel more tries to
+ // deliver the signal.
+ for i := 0; i < 10; i++ {
+ select {
+ case s := <-c:
+ // If the caller notified for all signals on
+ // c, filter out SIGURG, which is used for
+ // runtime preemption and can come at
+ // unpredictable times.
+ if all && s == syscall.SIGURG {
+ continue
+ }
+ if s != sig {
+ t.Fatalf("signal was %v, want %v", s, sig)
+ }
+ return
+
+ case <-time.After(100 * time.Millisecond):
}
- case <-time.After(1 * time.Second):
- t.Fatalf("timeout waiting for %v", sig)
}
+ t.Fatalf("timeout waiting for %v", sig)
}
// Test that basic signal handling works.
@@ -45,24 +81,26 @@ func TestSignal(t *testing.T) {
syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
waitSig(t, c, syscall.SIGHUP)
- // Ask for everything we can get.
- c1 := make(chan os.Signal, 1)
+ // Ask for everything we can get. The buffer size has to be
+ // more than 1, since the runtime might send SIGURG signals.
+ // Using 10 is arbitrary.
+ c1 := make(chan os.Signal, 10)
Notify(c1)
// Send this process a SIGWINCH
t.Logf("sigwinch...")
syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
- waitSig(t, c1, syscall.SIGWINCH)
+ waitSigAll(t, c1, syscall.SIGWINCH)
// Send two more SIGHUPs, to make sure that
// they get delivered on c1 and that not reading
// from c does not block everything.
t.Logf("sighup...")
syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
- waitSig(t, c1, syscall.SIGHUP)
+ waitSigAll(t, c1, syscall.SIGHUP)
t.Logf("sighup...")
syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
- waitSig(t, c1, syscall.SIGHUP)
+ waitSigAll(t, c1, syscall.SIGHUP)
// The first SIGHUP should be waiting for us on c.
waitSig(t, c, syscall.SIGHUP)
@@ -268,7 +306,15 @@ func TestStop(t *testing.T) {
if sig == syscall.SIGWINCH || (sig == syscall.SIGHUP && *sendUncaughtSighup == 1) {
syscall.Kill(syscall.Getpid(), sig)
}
- time.Sleep(100 * time.Millisecond)
+
+ // The kernel will deliver a signal as a thread returns
+ // from a syscall. If the only active thread is sleeping,
+ // and the system is busy, the kernel may not get around
+ // to waking up a thread to catch the signal.
+ // We try splitting up the sleep to give the kernel
+ // another chance to deliver the signal.
+ time.Sleep(50 * time.Millisecond)
+ time.Sleep(50 * time.Millisecond)
// Ask for signal
c := make(chan os.Signal, 1)
@@ -280,10 +326,11 @@ func TestStop(t *testing.T) {
waitSig(t, c, sig)
Stop(c)
+ time.Sleep(50 * time.Millisecond)
select {
case s := <-c:
t.Fatalf("unexpected signal %v", s)
- case <-time.After(100 * time.Millisecond):
+ case <-time.After(50 * time.Millisecond):
// nothing to read - good
}
@@ -294,10 +341,11 @@ func TestStop(t *testing.T) {
syscall.Kill(syscall.Getpid(), sig)
}
+ time.Sleep(50 * time.Millisecond)
select {
case s := <-c:
t.Fatalf("unexpected signal %v", s)
- case <-time.After(100 * time.Millisecond):
+ case <-time.After(50 * time.Millisecond):
// nothing to read - good
}
}
@@ -374,9 +422,26 @@ func TestAtomicStop(t *testing.T) {
testenv.MustHaveExec(t)
+ // Call Notify for SIGINT before starting the child process.
+ // That ensures that SIGINT is not ignored for the child.
+ // This is necessary because if SIGINT is ignored when a
+ // Go program starts, then it remains ignored, and closing
+ // the last notification channel for SIGINT will switch it
+ // back to being ignored. In that case the assumption of
+ // atomicStopTestProgram, that it will either die from SIGINT
+ // or have it be reported, breaks down, as there is a third
+ // option: SIGINT might be ignored.
+ cs := make(chan os.Signal, 1)
+ Notify(cs, syscall.SIGINT)
+ defer Stop(cs)
+
const execs = 10
for i := 0; i < execs; i++ {
- cmd := exec.Command(os.Args[0], "-test.run=TestAtomicStop")
+ timeout := "0"
+ if !testDeadline.IsZero() {
+ timeout = testDeadline.Sub(time.Now()).String()
+ }
+ cmd := exec.Command(os.Args[0], "-test.run=TestAtomicStop", "-test.timeout="+timeout)
cmd.Env = append(os.Environ(), "GO_TEST_ATOMIC_STOP=1")
out, err := cmd.CombinedOutput()
if err == nil {
@@ -414,7 +479,21 @@ func TestAtomicStop(t *testing.T) {
// It tries to trigger a signal delivery race. This function should
// either catch a signal or die from it.
func atomicStopTestProgram() {
+ // This test won't work if SIGINT is ignored here.
+ if Ignored(syscall.SIGINT) {
+ fmt.Println("SIGINT is ignored")
+ os.Exit(1)
+ }
+
const tries = 10
+
+ timeout := 2 * time.Second
+ if !testDeadline.IsZero() {
+ // Give each try an equal slice of the deadline, with one slice to spare for
+ // cleanup.
+ timeout = testDeadline.Sub(time.Now()) / (tries + 1)
+ }
+
pid := syscall.Getpid()
printed := false
for i := 0; i < tries; i++ {
@@ -437,7 +516,7 @@ func atomicStopTestProgram() {
select {
case <-cs:
- case <-time.After(2 * time.Second):
+ case <-time.After(timeout):
if !printed {
fmt.Print("lost signal on tries:")
printed = true
@@ -453,3 +532,52 @@ func atomicStopTestProgram() {
os.Exit(0)
}
+
+func TestTime(t *testing.T) {
+ // Test that signal works fine when we are in a call to get time,
+ // which on some platforms is using VDSO. See issue #34391.
+ dur := 3 * time.Second
+ if testing.Short() {
+ dur = 100 * time.Millisecond
+ }
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
+ done := make(chan bool)
+ finished := make(chan bool)
+ go func() {
+ sig := make(chan os.Signal, 1)
+ Notify(sig, syscall.SIGUSR1)
+ defer Stop(sig)
+ Loop:
+ for {
+ select {
+ case <-sig:
+ case <-done:
+ break Loop
+ }
+ }
+ finished <- true
+ }()
+ go func() {
+ Loop:
+ for {
+ select {
+ case <-done:
+ break Loop
+ default:
+ syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
+ runtime.Gosched()
+ }
+ }
+ finished <- true
+ }()
+ t0 := time.Now()
+ for t1 := t0; t1.Sub(t0) < dur; t1 = time.Now() {
+ } // hammering on getting time
+ close(done)
+ <-finished
+ <-finished
+ // When run with 'go test -cpu=1,2,4' SIGUSR1 from this test can slip
+ // into subsequent TestSignal() causing failure.
+ // Sleep for a while to reduce the possibility of the failure.
+ time.Sleep(10 * time.Millisecond)
+}
diff --git a/libgo/go/os/signal/signal_unix.go b/libgo/go/os/signal/signal_unix.go
index 8ed0cde..532c0b5 100644
--- a/libgo/go/os/signal/signal_unix.go
+++ b/libgo/go/os/signal/signal_unix.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix darwin dragonfly freebsd hurd js,wasm linux nacl netbsd openbsd solaris windows
+// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris windows
package signal
@@ -26,7 +26,8 @@ func loop() {
func init() {
signal_enable(0) // first call - initialize
- go loop()
+
+ watchSignalLoop = loop
}
const (
diff --git a/libgo/go/os/stat_nacljs.go b/libgo/go/os/stat_js.go
index f14add8..8d20ccd 100644
--- a/libgo/go/os/stat_nacljs.go
+++ b/libgo/go/os/stat_js.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build js,wasm nacl
+// +build js,wasm
package os
diff --git a/libgo/go/os/stat_unix.go b/libgo/go/os/stat_unix.go
index 2bdbc4e..d489ad1 100644
--- a/libgo/go/os/stat_unix.go
+++ b/libgo/go/os/stat_unix.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix darwin dragonfly freebsd hurd js,wasm linux nacl netbsd openbsd solaris
+// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris
package os
diff --git a/libgo/go/os/sys_bsd.go b/libgo/go/os/sys_bsd.go
index d820be2..b1698f5 100644
--- a/libgo/go/os/sys_bsd.go
+++ b/libgo/go/os/sys_bsd.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd js,wasm nacl netbsd openbsd
+// +build darwin dragonfly freebsd js,wasm netbsd openbsd
package os
diff --git a/libgo/go/os/sys_nacl.go b/libgo/go/os/sys_nacl.go
deleted file mode 100644
index 07907c8..0000000
--- a/libgo/go/os/sys_nacl.go
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package os
-
-// supportsCloseOnExec reports whether the platform supports the
-// O_CLOEXEC flag.
-const supportsCloseOnExec = false
diff --git a/libgo/go/os/timeout_test.go b/libgo/go/os/timeout_test.go
index 5d7ea7e..0fe03fa 100644
--- a/libgo/go/os/timeout_test.go
+++ b/libgo/go/os/timeout_test.go
@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build !nacl
// +build !js
// +build !plan9
// +build !windows
diff --git a/libgo/go/os/user/cgo_lookup_unix.go b/libgo/go/os/user/cgo_lookup_unix.go
index 5872d11..eb6ab50 100644
--- a/libgo/go/os/user/cgo_lookup_unix.go
+++ b/libgo/go/os/user/cgo_lookup_unix.go
@@ -118,10 +118,6 @@ func buildUser(pwd *syscall.Passwd) *User {
return u
}
-func currentGroup() (*Group, error) {
- return lookupUnixGid(syscall.Getgid())
-}
-
func lookupGroup(groupname string) (*Group, error) {
var grp syscall.Group
var result *syscall.Group
diff --git a/libgo/go/os/user/lookup_stubs.go b/libgo/go/os/user/lookup_stubs.go
index d3acbdd..178d814 100644
--- a/libgo/go/os/user/lookup_stubs.go
+++ b/libgo/go/os/user/lookup_stubs.go
@@ -35,15 +35,8 @@ func current() (*User, error) {
Name: "", // ignored
HomeDir: homeDir,
}
- // On NaCL and Android, return a dummy user instead of failing.
+ // On Android, return a dummy user instead of failing.
switch runtime.GOOS {
- case "nacl":
- if u.Uid == "" {
- u.Uid = "1"
- }
- if u.Username == "" {
- u.Username = "nacl"
- }
case "android":
if u.Uid == "" {
u.Uid = "1"
@@ -72,7 +65,7 @@ func current() (*User, error) {
func listGroups(*User) ([]string, error) {
if runtime.GOOS == "android" || runtime.GOOS == "aix" {
- return nil, errors.New(fmt.Sprintf("user: GroupIds not implemented on %s", runtime.GOOS))
+ return nil, fmt.Errorf("user: GroupIds not implemented on %s", runtime.GOOS)
}
return nil, errors.New("user: GroupIds requires cgo")
}
diff --git a/libgo/go/os/user/lookup_unix.go b/libgo/go/os/user/lookup_unix.go
index ba914f6..0f5e82d 100644
--- a/libgo/go/os/user/lookup_unix.go
+++ b/libgo/go/os/user/lookup_unix.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix darwin dragonfly freebsd hurd js,wasm !android,linux nacl netbsd openbsd solaris
+// +build aix darwin dragonfly freebsd hurd js,wasm !android,linux netbsd openbsd solaris
// +build !cgo osusergo
package user
diff --git a/libgo/go/os/user/lookup_unix_test.go b/libgo/go/os/user/lookup_unix_test.go
index 65fe065..72d3b47 100644
--- a/libgo/go/os/user/lookup_unix_test.go
+++ b/libgo/go/os/user/lookup_unix_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix darwin dragonfly freebsd !android,linux nacl netbsd openbsd solaris
+// +build aix darwin dragonfly freebsd !android,linux netbsd openbsd solaris
// +build !cgo
package user
diff --git a/libgo/go/os/user/lookup_windows.go b/libgo/go/os/user/lookup_windows.go
index 7499f6a..faaddd2 100644
--- a/libgo/go/os/user/lookup_windows.go
+++ b/libgo/go/os/user/lookup_windows.go
@@ -44,11 +44,7 @@ func lookupFullNameServer(servername, username string) (string, error) {
}
defer syscall.NetApiBufferFree(p)
i := (*syscall.UserInfo10)(unsafe.Pointer(p))
- if i.FullName == nil {
- return "", nil
- }
- name := syscall.UTF16ToString((*[1024]uint16)(unsafe.Pointer(i.FullName))[:])
- return name, nil
+ return windows.UTF16PtrToString(i.FullName, 1024), nil
}
func lookupFullName(domain, username, domainAndUser string) (string, error) {
@@ -165,14 +161,13 @@ func listGroupsForUsernameAndDomain(username, domain string) ([]string, error) {
if entriesRead == 0 {
return nil, fmt.Errorf("listGroupsForUsernameAndDomain: NetUserGetLocalGroups() returned an empty list for domain: %s, username: %s", domain, username)
}
- entries := (*[1024]windows.LocalGroupUserInfo0)(unsafe.Pointer(p0))[:entriesRead]
+ entries := (*[1024]windows.LocalGroupUserInfo0)(unsafe.Pointer(p0))[:entriesRead:entriesRead]
var sids []string
for _, entry := range entries {
if entry.Name == nil {
continue
}
- name := syscall.UTF16ToString((*[1024]uint16)(unsafe.Pointer(entry.Name))[:])
- sid, err := lookupGroupName(name)
+ sid, err := lookupGroupName(windows.UTF16PtrToString(entry.Name, 1024))
if err != nil {
return nil, err
}
diff --git a/libgo/go/os/wait_unimp.go b/libgo/go/os/wait_unimp.go
index 2551f21..cb875ab 100644
--- a/libgo/go/os/wait_unimp.go
+++ b/libgo/go/os/wait_unimp.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build aix darwin dragonfly hurd js,wasm nacl netbsd openbsd solaris
+// +build aix darwin dragonfly hurd js,wasm netbsd openbsd solaris
package os