aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/os
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2019-09-06 18:12:46 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2019-09-06 18:12:46 +0000
commitaa8901e9bb0399d2c16f988ba2fe46eb0c0c5d13 (patch)
tree7e63b06d1eec92beec6997c9d3ab47a5d6a835be /libgo/go/os
parent920ea3b8ba3164b61ac9490dfdfceb6936eda6dd (diff)
downloadgcc-aa8901e9bb0399d2c16f988ba2fe46eb0c0c5d13.zip
gcc-aa8901e9bb0399d2c16f988ba2fe46eb0c0c5d13.tar.gz
gcc-aa8901e9bb0399d2c16f988ba2fe46eb0c0c5d13.tar.bz2
libgo: update to Go 1.13beta1 release
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/193497 From-SVN: r275473
Diffstat (limited to 'libgo/go/os')
-rw-r--r--libgo/go/os/dir_ios.go87
-rw-r--r--libgo/go/os/env_default.go13
-rw-r--r--libgo/go/os/env_windows.go40
-rw-r--r--libgo/go/os/error.go53
-rw-r--r--libgo/go/os/error_plan9.go44
-rw-r--r--libgo/go/os/error_test.go28
-rw-r--r--libgo/go/os/error_unix.go24
-rw-r--r--libgo/go/os/error_windows.go28
-rw-r--r--libgo/go/os/example_test.go1
-rw-r--r--libgo/go/os/exec/bench_test.go23
-rw-r--r--libgo/go/os/exec/exec.go62
-rw-r--r--libgo/go/os/exec/exec_posix_test.go5
-rw-r--r--libgo/go/os/exec/exec_test.go58
-rw-r--r--libgo/go/os/exec_plan9.go1
-rw-r--r--libgo/go/os/exec_posix.go6
-rw-r--r--libgo/go/os/export_test.go2
-rw-r--r--libgo/go/os/export_unix_test.go9
-rw-r--r--libgo/go/os/file.go104
-rw-r--r--libgo/go/os/file_plan9.go8
-rw-r--r--libgo/go/os/file_posix.go20
-rw-r--r--libgo/go/os/file_unix.go41
-rw-r--r--libgo/go/os/os_test.go64
-rw-r--r--libgo/go/os/os_unix_test.go47
-rw-r--r--libgo/go/os/path.go3
-rw-r--r--libgo/go/os/path_unix.go18
-rw-r--r--libgo/go/os/pipe_test.go30
-rw-r--r--libgo/go/os/proc.go2
-rw-r--r--libgo/go/os/removeall_at.go48
-rw-r--r--libgo/go/os/removeall_noat.go40
-rw-r--r--libgo/go/os/removeall_test.go61
-rw-r--r--libgo/go/os/signal/internal/pty/pty.go2
-rw-r--r--libgo/go/os/signal/signal_cgo_test.go11
-rw-r--r--libgo/go/os/sticky_bsd.go2
-rw-r--r--libgo/go/os/sticky_notbsd.go1
-rw-r--r--libgo/go/os/timeout_test.go4
-rw-r--r--libgo/go/os/types_windows.go15
-rw-r--r--libgo/go/os/user/lookup_stubs.go22
-rw-r--r--libgo/go/os/user/user_test.go2
38 files changed, 692 insertions, 337 deletions
diff --git a/libgo/go/os/dir_ios.go b/libgo/go/os/dir_ios.go
deleted file mode 100644
index 8c14d89..0000000
--- a/libgo/go/os/dir_ios.go
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2009 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.
-
-// +build darwin
-// +build arm arm64
-
-package os
-
-import (
- "io"
- "runtime"
- "syscall"
- "unsafe"
-)
-
-// Auxiliary information if the File describes a directory
-type dirInfo struct {
- dir uintptr // Pointer to DIR structure from dirent.h
-}
-
-func (d *dirInfo) close() {
- if d.dir == 0 {
- return
- }
- closedir(d.dir)
- d.dir = 0
-}
-
-func (f *File) readdirnames(n int) (names []string, err error) {
- if f.dirinfo == nil {
- dir, call, errno := f.pfd.OpenDir()
- if errno != nil {
- return nil, wrapSyscallError(call, errno)
- }
- f.dirinfo = &dirInfo{
- dir: dir,
- }
- }
- d := f.dirinfo
-
- size := n
- if size <= 0 {
- size = 100
- n = -1
- }
-
- names = make([]string, 0, size)
- var dirent syscall.Dirent
- var entptr uintptr
- for len(names) < size {
- if res := readdir_r(d.dir, uintptr(unsafe.Pointer(&dirent)), uintptr(unsafe.Pointer(&entptr))); res != 0 {
- return names, wrapSyscallError("readdir", syscall.Errno(res))
- }
- if entptr == 0 { // EOF
- break
- }
- if dirent.Ino == 0 {
- continue
- }
- name := (*[len(syscall.Dirent{}.Name)]byte)(unsafe.Pointer(&dirent.Name))[:]
- for i, c := range name {
- if c == 0 {
- name = name[:i]
- break
- }
- }
- // Check for useless names before allocating a string.
- if string(name) == "." || string(name) == ".." {
- continue
- }
- names = append(names, string(name))
- runtime.KeepAlive(f)
- }
- if n >= 0 && len(names) == 0 {
- return names, io.EOF
- }
- return names, nil
-}
-
-// Implemented in syscall/syscall_darwin.go.
-
-//go:linkname closedir syscall.closedir
-func closedir(dir uintptr) (err error)
-
-//go:linkname readdir_r syscall.readdir_r
-func readdir_r(dir, entry, result uintptr) (res int)
diff --git a/libgo/go/os/env_default.go b/libgo/go/os/env_default.go
new file mode 100644
index 0000000..c11ccce
--- /dev/null
+++ b/libgo/go/os/env_default.go
@@ -0,0 +1,13 @@
+// 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.
+
+// +build !windows
+
+package os
+
+import "syscall"
+
+func environForSysProcAttr(sys *syscall.SysProcAttr) ([]string, error) {
+ return Environ(), nil
+}
diff --git a/libgo/go/os/env_windows.go b/libgo/go/os/env_windows.go
new file mode 100644
index 0000000..e8f647e
--- /dev/null
+++ b/libgo/go/os/env_windows.go
@@ -0,0 +1,40 @@
+// 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 os
+
+import (
+ "internal/syscall/windows"
+ "syscall"
+ "unicode/utf16"
+ "unsafe"
+)
+
+func environForSysProcAttr(sys *syscall.SysProcAttr) (env []string, err error) {
+ if sys == nil || sys.Token == 0 {
+ return Environ(), nil
+ }
+ var block *uint16
+ err = windows.CreateEnvironmentBlock(&block, sys.Token, false)
+ if err != nil {
+ return nil, err
+ }
+ 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
+ }
+ }
+ if len(entry) == 0 {
+ break
+ }
+ env = append(env, string(utf16.Decode(entry)))
+ blockp += 2 * (uintptr(len(entry)) + 1)
+ }
+ return
+}
diff --git a/libgo/go/os/error.go b/libgo/go/os/error.go
index b4242a4..0c2e6a7 100644
--- a/libgo/go/os/error.go
+++ b/libgo/go/os/error.go
@@ -5,20 +5,37 @@
package os
import (
- "errors"
+ "internal/oserror"
"internal/poll"
)
// Portable analogs of some common system call errors.
+//
+// Errors returned from this package may be tested against these errors
+// with errors.Is.
var (
- ErrInvalid = errors.New("invalid argument") // methods on File will return this error when the receiver is nil
- ErrPermission = errors.New("permission denied")
- ErrExist = errors.New("file already exists")
- ErrNotExist = errors.New("file does not exist")
- ErrClosed = errors.New("file already closed")
- ErrNoDeadline = poll.ErrNoDeadline
+ // ErrInvalid indicates an invalid argument.
+ // Methods on File will return this error when the receiver is nil.
+ ErrInvalid = errInvalid() // "invalid argument"
+
+ ErrPermission = errPermission() // "permission denied"
+ ErrExist = errExist() // "file already exists"
+ ErrNotExist = errNotExist() // "file does not exist"
+ ErrClosed = errClosed() // "file already closed"
+ ErrTimeout = errTimeout() // "deadline exceeded"
+ ErrTemporary = errTemporary() // "temporary error"
+ ErrNoDeadline = errNoDeadline() // "file type does not support deadline"
)
+func errInvalid() error { return oserror.ErrInvalid }
+func errPermission() error { return oserror.ErrPermission }
+func errExist() error { return oserror.ErrExist }
+func errNotExist() error { return oserror.ErrNotExist }
+func errClosed() error { return oserror.ErrClosed }
+func errTimeout() error { return oserror.ErrTimeout }
+func errTemporary() error { return oserror.ErrTemporary }
+func errNoDeadline() error { return poll.ErrNoDeadline }
+
type timeout interface {
Timeout() bool
}
@@ -32,6 +49,8 @@ type PathError struct {
func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }
+func (e *PathError) Unwrap() error { return e.Err }
+
// Timeout reports whether this error represents a timeout.
func (e *PathError) Timeout() bool {
t, ok := e.Err.(timeout)
@@ -46,6 +65,8 @@ type SyscallError struct {
func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Err.Error() }
+func (e *SyscallError) Unwrap() error { return e.Err }
+
// Timeout reports whether this error represents a timeout.
func (e *SyscallError) Timeout() bool {
t, ok := e.Err.(timeout)
@@ -66,21 +87,21 @@ func NewSyscallError(syscall string, err error) error {
// that a file or directory already exists. It is satisfied by ErrExist as
// well as some syscall errors.
func IsExist(err error) bool {
- return isExist(err)
+ return underlyingErrorIs(err, ErrExist)
}
// IsNotExist returns a boolean indicating whether the error is known to
// report that a file or directory does not exist. It is satisfied by
// ErrNotExist as well as some syscall errors.
func IsNotExist(err error) bool {
- return isNotExist(err)
+ return underlyingErrorIs(err, ErrNotExist)
}
// IsPermission returns a boolean indicating whether the error is known to
// report that permission is denied. It is satisfied by ErrPermission as well
// as some syscall errors.
func IsPermission(err error) bool {
- return isPermission(err)
+ return underlyingErrorIs(err, ErrPermission)
}
// IsTimeout returns a boolean indicating whether the error is known
@@ -90,6 +111,18 @@ func IsTimeout(err error) bool {
return ok && terr.Timeout()
}
+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.
+ err = underlyingError(err)
+ if err == target {
+ return true
+ }
+ e, ok := err.(interface{ Is(error) bool })
+ return ok && e.Is(target)
+}
+
// underlyingError returns the underlying error for known os error types.
func underlyingError(err error) error {
switch err := err.(type) {
diff --git a/libgo/go/os/error_plan9.go b/libgo/go/os/error_plan9.go
deleted file mode 100644
index b82bf0d..0000000
--- a/libgo/go/os/error_plan9.go
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2011 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
-
-func isExist(err error) bool {
- return checkErrMessageContent(err, "exists", "is a directory")
-}
-
-func isNotExist(err error) bool {
- return checkErrMessageContent(err, "does not exist", "not found",
- "has been removed", "no parent")
-}
-
-func isPermission(err error) bool {
- return checkErrMessageContent(err, "permission denied")
-}
-
-// checkErrMessageContent checks if err message contains one of msgs.
-func checkErrMessageContent(err error, msgs ...string) bool {
- if err == nil {
- return false
- }
- err = underlyingError(err)
- for _, msg := range msgs {
- if contains(err.Error(), msg) {
- return true
- }
- }
- return false
-}
-
-// contains is a local version of strings.Contains. It knows len(sep) > 1.
-func contains(s, sep string) bool {
- n := len(sep)
- c := sep[0]
- for i := 0; i+n <= len(s); i++ {
- if s[i] == c && s[i:i+n] == sep {
- return true
- }
- }
- return false
-}
diff --git a/libgo/go/os/error_test.go b/libgo/go/os/error_test.go
index 3499cee..a03bd28 100644
--- a/libgo/go/os/error_test.go
+++ b/libgo/go/os/error_test.go
@@ -5,6 +5,7 @@
package os_test
import (
+ "errors"
"fmt"
"io/ioutil"
"os"
@@ -26,7 +27,7 @@ func TestErrIsExist(t *testing.T) {
t.Fatal("Open should have failed")
return
}
- if s := checkErrorPredicate("os.IsExist", os.IsExist, err); s != "" {
+ if s := checkErrorPredicate("os.IsExist", os.IsExist, err, os.ErrExist); s != "" {
t.Fatal(s)
return
}
@@ -38,7 +39,7 @@ func testErrNotExist(name string) string {
f.Close()
return "Open should have failed"
}
- if s := checkErrorPredicate("os.IsNotExist", os.IsNotExist, err); s != "" {
+ if s := checkErrorPredicate("os.IsNotExist", os.IsNotExist, err, os.ErrNotExist); s != "" {
return s
}
@@ -46,7 +47,7 @@ func testErrNotExist(name string) string {
if err == nil {
return "Chdir should have failed"
}
- if s := checkErrorPredicate("os.IsNotExist", os.IsNotExist, err); s != "" {
+ if s := checkErrorPredicate("os.IsNotExist", os.IsNotExist, err, os.ErrNotExist); s != "" {
return s
}
return ""
@@ -73,10 +74,13 @@ func TestErrIsNotExist(t *testing.T) {
}
}
-func checkErrorPredicate(predName string, pred func(error) bool, err error) string {
+func checkErrorPredicate(predName string, pred func(error) bool, err, target error) string {
if !pred(err) {
return fmt.Sprintf("%s does not work as expected for %#v", predName, err)
}
+ if !errors.Is(err, target) {
+ return fmt.Sprintf("errors.Is(%#v, %#v) = false, want true", err, target)
+ }
return ""
}
@@ -107,9 +111,15 @@ func TestIsExist(t *testing.T) {
if is := os.IsExist(tt.err); is != tt.is {
t.Errorf("os.IsExist(%T %v) = %v, want %v", tt.err, tt.err, is, tt.is)
}
+ if is := errors.Is(tt.err, os.ErrExist); is != tt.is {
+ t.Errorf("errors.Is(%T %v, os.ErrExist) = %v, want %v", tt.err, tt.err, is, tt.is)
+ }
if isnot := os.IsNotExist(tt.err); isnot != tt.isnot {
t.Errorf("os.IsNotExist(%T %v) = %v, want %v", tt.err, tt.err, isnot, tt.isnot)
}
+ if isnot := errors.Is(tt.err, os.ErrNotExist); isnot != tt.isnot {
+ t.Errorf("errors.Is(%T %v, os.ErrNotExist) = %v, want %v", tt.err, tt.err, isnot, tt.isnot)
+ }
}
}
@@ -129,6 +139,9 @@ func TestIsPermission(t *testing.T) {
if got := os.IsPermission(tt.err); got != tt.want {
t.Errorf("os.IsPermission(%#v) = %v; want %v", tt.err, got, tt.want)
}
+ if got := errors.Is(tt.err, os.ErrPermission); got != tt.want {
+ t.Errorf("errors.Is(%#v, os.ErrPermission) = %v; want %v", tt.err, got, tt.want)
+ }
}
}
@@ -155,3 +168,10 @@ func TestErrPathNUL(t *testing.T) {
t.Fatal("Open should have failed")
}
}
+
+func TestPathErrorUnwrap(t *testing.T) {
+ pe := &os.PathError{Err: os.ErrInvalid}
+ if !errors.Is(pe, os.ErrInvalid) {
+ t.Error("errors.Is failed, wanted success")
+ }
+}
diff --git a/libgo/go/os/error_unix.go b/libgo/go/os/error_unix.go
deleted file mode 100644
index 7801057..0000000
--- a/libgo/go/os/error_unix.go
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2009 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.
-
-// +build aix darwin dragonfly freebsd hurd js,wasm linux nacl netbsd openbsd solaris
-
-package os
-
-import "syscall"
-
-func isExist(err error) bool {
- err = underlyingError(err)
- return err == syscall.EEXIST || err == syscall.ENOTEMPTY || err == ErrExist
-}
-
-func isNotExist(err error) bool {
- err = underlyingError(err)
- return err == syscall.ENOENT || err == ErrNotExist
-}
-
-func isPermission(err error) bool {
- err = underlyingError(err)
- return err == syscall.EACCES || err == syscall.EPERM || err == ErrPermission
-}
diff --git a/libgo/go/os/error_windows.go b/libgo/go/os/error_windows.go
deleted file mode 100644
index 02593b5..0000000
--- a/libgo/go/os/error_windows.go
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2012 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
-
-import "syscall"
-
-func isExist(err error) bool {
- err = underlyingError(err)
- return err == syscall.ERROR_ALREADY_EXISTS ||
- err == syscall.ERROR_DIR_NOT_EMPTY ||
- err == syscall.ERROR_FILE_EXISTS || err == ErrExist
-}
-
-const _ERROR_BAD_NETPATH = syscall.Errno(53)
-
-func isNotExist(err error) bool {
- err = underlyingError(err)
- return err == syscall.ERROR_FILE_NOT_FOUND ||
- err == _ERROR_BAD_NETPATH ||
- err == syscall.ERROR_PATH_NOT_FOUND || err == ErrNotExist
-}
-
-func isPermission(err error) bool {
- err = underlyingError(err)
- return err == syscall.ERROR_ACCESS_DENIED || err == ErrPermission
-}
diff --git a/libgo/go/os/example_test.go b/libgo/go/os/example_test.go
index 8b6566e..822886f 100644
--- a/libgo/go/os/example_test.go
+++ b/libgo/go/os/example_test.go
@@ -28,6 +28,7 @@ func ExampleOpenFile_append() {
log.Fatal(err)
}
if _, err := f.Write([]byte("appended some data\n")); err != nil {
+ f.Close() // ignore error; Write error takes precedence
log.Fatal(err)
}
if err := f.Close(); err != nil {
diff --git a/libgo/go/os/exec/bench_test.go b/libgo/go/os/exec/bench_test.go
new file mode 100644
index 0000000..9a94001
--- /dev/null
+++ b/libgo/go/os/exec/bench_test.go
@@ -0,0 +1,23 @@
+// 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 (
+ "testing"
+)
+
+func BenchmarkExecHostname(b *testing.B) {
+ b.ReportAllocs()
+ path, err := LookPath("hostname")
+ if err != nil {
+ b.Fatalf("could not find hostname: %v", err)
+ }
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ if err := Command(path).Run(); err != nil {
+ b.Fatalf("hostname: %v", err)
+ }
+ }
+}
diff --git a/libgo/go/os/exec/exec.go b/libgo/go/os/exec/exec.go
index 1aa3ab9..17ef003 100644
--- a/libgo/go/os/exec/exec.go
+++ b/libgo/go/os/exec/exec.go
@@ -47,6 +47,8 @@ func (e *Error) Error() string {
return "exec: " + strconv.Quote(e.Name) + ": " + e.Err.Error()
}
+func (e *Error) Unwrap() error { return e.Err }
+
// Cmd represents an external command being prepared or run.
//
// A Cmd cannot be reused after calling its Run, Output or CombinedOutput
@@ -71,6 +73,8 @@ type Cmd struct {
// environment.
// If Env contains duplicate environment keys, only the last
// value in the slice for each duplicate key is used.
+ // As a special case on Windows, SYSTEMROOT is always added if
+ // missing and not explicitly set to the empty string.
Env []string
// Dir specifies the working directory of the command.
@@ -190,6 +194,25 @@ func CommandContext(ctx context.Context, name string, arg ...string) *Cmd {
return cmd
}
+// String returns a human-readable description of c.
+// It is intended only for debugging.
+// In particular, it is not suitable for use as input to a shell.
+// The output of String may vary across Go releases.
+func (c *Cmd) String() string {
+ if c.lookPathErr != nil {
+ // failed to resolve path; report the original requested path (plus args)
+ return strings.Join(c.Args, " ")
+ }
+ // report the exact executable path (plus args)
+ b := new(strings.Builder)
+ b.WriteString(c.Path)
+ for _, a := range c.Args[1:] {
+ b.WriteByte(' ')
+ b.WriteString(a)
+ }
+ return b.String()
+}
+
// interfaceEqual protects against panics from doing equality tests on
// two interfaces with non-comparable underlying types.
func interfaceEqual(a, b interface{}) bool {
@@ -376,6 +399,7 @@ func (c *Cmd) Start() error {
}
}
+ c.childFiles = make([]*os.File, 0, 3+len(c.ExtraFiles))
type F func(*Cmd) (*os.File, error)
for _, setupFd := range []F{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} {
fd, err := setupFd(c)
@@ -392,7 +416,7 @@ func (c *Cmd) Start() error {
c.Process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{
Dir: c.Dir,
Files: c.childFiles,
- Env: dedupEnv(c.envv()),
+ Env: addCriticalEnv(dedupEnv(c.envv())),
Sys: c.SysProcAttr,
})
if err != nil {
@@ -403,11 +427,14 @@ func (c *Cmd) Start() error {
c.closeDescriptors(c.closeAfterStart)
- c.errch = make(chan error, len(c.goroutine))
- for _, fn := range c.goroutine {
- go func(fn func() error) {
- c.errch <- fn()
- }(fn)
+ // Don't allocate the channel unless there are goroutines to fire.
+ if len(c.goroutine) > 0 {
+ c.errch = make(chan error, len(c.goroutine))
+ for _, fn := range c.goroutine {
+ go func(fn func() error) {
+ c.errch <- fn()
+ }(fn)
+ }
}
if c.ctx != nil {
@@ -713,7 +740,7 @@ func dedupEnv(env []string) []string {
// If caseInsensitive is true, the case of keys is ignored.
func dedupEnvCase(caseInsensitive bool, env []string) []string {
out := make([]string, 0, len(env))
- saw := map[string]int{} // key => index into out
+ saw := make(map[string]int, len(env)) // key => index into out
for _, kv := range env {
eq := strings.Index(kv, "=")
if eq < 0 {
@@ -733,3 +760,24 @@ func dedupEnvCase(caseInsensitive bool, env []string) []string {
}
return out
}
+
+// addCriticalEnv adds any critical environment variables that are required
+// (or at least almost always required) on the operating system.
+// Currently this is only used for Windows.
+func addCriticalEnv(env []string) []string {
+ if runtime.GOOS != "windows" {
+ return env
+ }
+ for _, kv := range env {
+ eq := strings.Index(kv, "=")
+ if eq < 0 {
+ continue
+ }
+ k := kv[:eq]
+ if strings.EqualFold(k, "SYSTEMROOT") {
+ // We already have it.
+ return env
+ }
+ }
+ return append(env, "SYSTEMROOT="+os.Getenv("SYSTEMROOT"))
+}
diff --git a/libgo/go/os/exec/exec_posix_test.go b/libgo/go/os/exec/exec_posix_test.go
index 46799cd..d4d67ac 100644
--- a/libgo/go/os/exec/exec_posix_test.go
+++ b/libgo/go/os/exec/exec_posix_test.go
@@ -8,6 +8,7 @@ package exec_test
import (
"os/user"
+ "runtime"
"strconv"
"syscall"
"testing"
@@ -15,6 +16,10 @@ import (
)
func TestCredentialNoSetGroups(t *testing.T) {
+ if runtime.GOOS == "android" {
+ t.Skip("unsupported on Android")
+ }
+
u, err := user.Current()
if err != nil {
t.Fatalf("error getting current user: %v", err)
diff --git a/libgo/go/os/exec/exec_test.go b/libgo/go/os/exec/exec_test.go
index b7cc9da..cfbb87a 100644
--- a/libgo/go/os/exec/exec_test.go
+++ b/libgo/go/os/exec/exec_test.go
@@ -695,6 +695,9 @@ func TestExtraFilesRace(t *testing.T) {
}
for i := 0; i < 10; i++ {
+ if testing.Short() && i >= 3 {
+ break
+ }
la := listen()
ca := helperCommand(t, "describefiles")
ca.ExtraFiles = []*os.File{listenerFile(la)}
@@ -835,7 +838,7 @@ func TestHelperProcess(*testing.T) {
// the cloned file descriptors that result from opening
// /dev/urandom.
// https://golang.org/issue/3955
- case "solaris":
+ 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
@@ -1154,3 +1157,56 @@ func TestDedupEnvEcho(t *testing.T) {
t.Errorf("output = %q; want %q", got, want)
}
}
+
+func TestString(t *testing.T) {
+ echoPath, err := exec.LookPath("echo")
+ if err != nil {
+ t.Skip(err)
+ }
+ tests := [...]struct {
+ path string
+ args []string
+ want string
+ }{
+ {"echo", nil, echoPath},
+ {"echo", []string{"a"}, echoPath + " a"},
+ {"echo", []string{"a", "b"}, echoPath + " a b"},
+ }
+ for _, test := range tests {
+ cmd := exec.Command(test.path, test.args...)
+ if got := cmd.String(); got != test.want {
+ t.Errorf("String(%q, %q) = %q, want %q", test.path, test.args, got, test.want)
+ }
+ }
+}
+
+func TestStringPathNotResolved(t *testing.T) {
+ _, err := exec.LookPath("makemeasandwich")
+ if err == nil {
+ t.Skip("wow, thanks")
+ }
+ cmd := exec.Command("makemeasandwich", "-lettuce")
+ want := "makemeasandwich -lettuce"
+ if got := cmd.String(); got != want {
+ t.Errorf("String(%q, %q) = %q, want %q", "makemeasandwich", "-lettuce", got, want)
+ }
+}
+
+// start a child process without the user code explicitly starting
+// with a copy of the parent's. (The Windows SYSTEMROOT issue: Issue
+// 25210)
+func TestChildCriticalEnv(t *testing.T) {
+ testenv.MustHaveExec(t)
+ if runtime.GOOS != "windows" {
+ t.Skip("only testing on Windows")
+ }
+ cmd := helperCommand(t, "echoenv", "SYSTEMROOT")
+ cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if strings.TrimSpace(string(out)) == "" {
+ t.Error("no SYSTEMROOT found")
+ }
+}
diff --git a/libgo/go/os/exec_plan9.go b/libgo/go/os/exec_plan9.go
index bab16cc..b0abf74 100644
--- a/libgo/go/os/exec_plan9.go
+++ b/libgo/go/os/exec_plan9.go
@@ -27,6 +27,7 @@ func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
Sys: attr.Sys,
}
+ sysattr.Files = make([]uintptr, 0, len(attr.Files))
for _, f := range attr.Files {
sysattr.Files = append(sysattr.Files, f.Fd())
}
diff --git a/libgo/go/os/exec_posix.go b/libgo/go/os/exec_posix.go
index 4b1902f..bb47e83 100644
--- a/libgo/go/os/exec_posix.go
+++ b/libgo/go/os/exec_posix.go
@@ -38,8 +38,12 @@ func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
Sys: attr.Sys,
}
if sysattr.Env == nil {
- sysattr.Env = Environ()
+ sysattr.Env, err = environForSysProcAttr(sysattr.Sys)
+ if err != nil {
+ return nil, err
+ }
}
+ sysattr.Files = make([]uintptr, 0, len(attr.Files))
for _, f := range attr.Files {
sysattr.Files = append(sysattr.Files, f.Fd())
}
diff --git a/libgo/go/os/export_test.go b/libgo/go/os/export_test.go
index d735aee..d17d5e62 100644
--- a/libgo/go/os/export_test.go
+++ b/libgo/go/os/export_test.go
@@ -8,3 +8,5 @@ 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
new file mode 100644
index 0000000..032b1a9
--- /dev/null
+++ b/libgo/go/os/export_unix_test.go
@@ -0,0 +1,9 @@
+// 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.
+
+// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris
+
+package os
+
+var SplitPath = splitPath
diff --git a/libgo/go/os/file.go b/libgo/go/os/file.go
index fdead63..96df3fb 100644
--- a/libgo/go/os/file.go
+++ b/libgo/go/os/file.go
@@ -98,6 +98,10 @@ func (e *LinkError) Error() string {
return e.Op + " " + e.Old + " " + e.New + ": " + e.Err.Error()
}
+func (e *LinkError) Unwrap() error {
+ return e.Err
+}
+
// Read reads up to len(b) bytes from the File.
// It returns the number of bytes read and any error encountered.
// At end of file, Read returns 0, io.EOF.
@@ -159,13 +163,20 @@ func (f *File) Write(b []byte) (n int, err error) {
return n, err
}
+var errWriteAtInAppendMode = errors.New("os: invalid use of WriteAt on file opened with O_APPEND")
+
// WriteAt writes len(b) bytes to the File starting at byte offset off.
// It returns the number of bytes written and an error, if any.
// WriteAt returns a non-nil error when n != len(b).
+//
+// If file was opened with the O_APPEND flag, WriteAt returns an error.
func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
if err := f.checkValid("write"); err != nil {
return 0, err
}
+ if f.appendMode {
+ return 0, errWriteAtInAppendMode
+ }
if off < 0 {
return 0, &PathError{"writeat", f.name, errors.New("negative offset")}
@@ -265,10 +276,10 @@ func Open(name string) (*File, error) {
return OpenFile(name, O_RDONLY, 0)
}
-// Create creates the named file with mode 0666 (before umask), truncating
-// it if it already exists. If successful, methods on the returned
-// File can be used for I/O; the associated file descriptor has mode
-// O_RDWR.
+// Create creates or truncates the named file. If the file already exists,
+// it is truncated. If the file does not exist, it is created with mode 0666
+// (before umask). If successful, methods on the returned File can
+// be used for I/O; the associated file descriptor has mode O_RDWR.
// If there is an error, it will be of type *PathError.
func Create(name string) (*File, error) {
return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
@@ -276,12 +287,19 @@ func Create(name string) (*File, error) {
// OpenFile is the generalized open call; most users will use Open
// or Create instead. It opens the named file with specified flag
-// (O_RDONLY etc.) and perm (before umask), if applicable. If successful,
+// (O_RDONLY etc.). If the file does not exist, and the O_CREATE flag
+// is passed, it is created with mode perm (before umask). If successful,
// methods on the returned File can be used for I/O.
// If there is an error, it will be of type *PathError.
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
testlog.Open(name)
- return openFileNolog(name, flag, perm)
+ f, err := openFileNolog(name, flag, perm)
+ if err != nil {
+ return nil, err
+ }
+ f.appendMode = flag&O_APPEND != 0
+
+ return f, nil
}
// lstat is overridden in tests.
@@ -381,6 +399,57 @@ func UserCacheDir() (string, error) {
return dir, nil
}
+// UserConfigDir returns the default root directory to use for user-specific
+// configuration data. Users should create their own application-specific
+// 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
+// non-empty, else $HOME/.config.
+// On Darwin, it returns $HOME/Library/Application Support.
+// On Windows, it returns %AppData%.
+// On Plan 9, it returns $home/lib.
+//
+// If the location cannot be determined (for example, $HOME is not defined),
+// then it will return an error.
+func UserConfigDir() (string, error) {
+ var dir string
+
+ switch runtime.GOOS {
+ case "windows":
+ dir = Getenv("AppData")
+ if dir == "" {
+ return "", errors.New("%AppData% is not defined")
+ }
+
+ case "darwin":
+ dir = Getenv("HOME")
+ if dir == "" {
+ return "", errors.New("$HOME is not defined")
+ }
+ dir += "/Library/Application Support"
+
+ case "plan9":
+ dir = Getenv("home")
+ if dir == "" {
+ return "", errors.New("$home is not defined")
+ }
+ dir += "/lib"
+
+ default: // Unix
+ dir = Getenv("XDG_CONFIG_HOME")
+ if dir == "" {
+ dir = Getenv("HOME")
+ if dir == "" {
+ return "", errors.New("neither $XDG_CONFIG_HOME nor $HOME are defined")
+ }
+ dir += "/.config"
+ }
+ }
+
+ return dir, nil
+}
+
// UserHomeDir returns the current user's home directory.
//
// On Unix, including macOS, it returns the $HOME environment variable.
@@ -393,16 +462,21 @@ func UserHomeDir() (string, error) {
env, enverr = "USERPROFILE", "%userprofile%"
case "plan9":
env, enverr = "home", "$home"
- case "nacl", "android":
+ }
+ if v := Getenv(env); v != "" {
+ return v, nil
+ }
+ // On some geese the home directory is not always defined.
+ switch runtime.GOOS {
+ case "nacl":
return "/", nil
+ case "android":
+ return "/sdcard", nil
case "darwin":
if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" {
return "/", nil
}
}
- if v := Getenv(env); v != "" {
- return v, nil
- }
return "", errors.New(enverr + " is not defined")
}
@@ -416,11 +490,11 @@ func UserHomeDir() (string, error) {
// On Unix, the mode's permission bits, ModeSetuid, ModeSetgid, and
// ModeSticky are used.
//
-// On Windows, the mode must be non-zero but otherwise only the 0200
-// bit (owner writable) of mode is used; it controls whether the
-// file's read-only attribute is set or cleared. attribute. The other
-// bits are currently unused. Use mode 0400 for a read-only file and
-// 0600 for a readable+writable file.
+// On Windows, only the 0200 bit (owner writable) of mode is used; it
+// controls whether the file's read-only attribute is set or cleared.
+// The other bits are currently unused. For compatibility with Go 1.12
+// and earlier, use a non-zero mode. Use mode 0400 for a read-only
+// file and 0600 for a readable+writable file.
//
// On Plan 9, the mode's permission bits, ModeAppend, ModeExclusive,
// and ModeTemporary are used.
diff --git a/libgo/go/os/file_plan9.go b/libgo/go/os/file_plan9.go
index 3fa12e6..e0a3826 100644
--- a/libgo/go/os/file_plan9.go
+++ b/libgo/go/os/file_plan9.go
@@ -22,9 +22,10 @@ func fixLongPath(path string) string {
// can overwrite this data, which could cause the finalizer
// to close the wrong file descriptor.
type file struct {
- fd int
- name string
- dirinfo *dirInfo // nil unless directory being read
+ fd int
+ name string
+ dirinfo *dirInfo // nil unless directory being read
+ appendMode bool // whether file is opened for appending
}
// Fd returns the integer Plan 9 file descriptor referencing the open file.
@@ -135,6 +136,7 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
// Close closes the File, rendering it unusable for I/O.
// On files that support SetDeadline, any pending I/O operations will
// be canceled and return immediately with an error.
+// Close will return an error if it has already been called.
func (f *File) Close() error {
if err := f.checkValid("close"); err != nil {
return err
diff --git a/libgo/go/os/file_posix.go b/libgo/go/os/file_posix.go
index 85395d6..2220a44 100644
--- a/libgo/go/os/file_posix.go
+++ b/libgo/go/os/file_posix.go
@@ -7,32 +7,12 @@
package os
import (
- "runtime"
"syscall"
"time"
)
func sigpipe() // implemented in package runtime
-// Readlink returns the destination of the named symbolic link.
-// If there is an error, it will be of type *PathError.
-func Readlink(name string) (string, error) {
- for len := 128; ; len *= 2 {
- b := make([]byte, len)
- n, e := fixCount(syscall.Readlink(fixLongPath(name), b))
- // buffer too small
- if runtime.GOOS == "aix" && e == syscall.ERANGE {
- continue
- }
- if e != nil {
- return "", &PathError{"readlink", name, e}
- }
- if n < len {
- return string(b[0:n]), nil
- }
- }
-}
-
// syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
func syscallMode(i FileMode) (o uint32) {
o |= uint32(i.Perm())
diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go
index 912ba5a..750771f 100644
--- a/libgo/go/os/file_unix.go
+++ b/libgo/go/os/file_unix.go
@@ -52,6 +52,7 @@ type file struct {
dirinfo *dirInfo // nil unless directory being read
nonblock bool // whether we set nonblocking mode
stdoutOrErr bool // whether this is stdout or stderr
+ appendMode bool // whether file is opened for appending
}
// Fd returns the integer Unix file descriptor referencing the open file.
@@ -121,33 +122,27 @@ func newFile(fd uintptr, name string, kind newFileKind) *File {
// we assume they know what they are doing so we allow it to be
// used with kqueue.
if kind == kindOpenFile {
- var st syscall.Stat_t
switch runtime.GOOS {
- case "freebsd":
- // On FreeBSD before 10.4 it used to crash the
- // system unpredictably while running all.bash.
- // When we stop supporting FreeBSD 10 we can merge
- // this into the dragonfly/netbsd/openbsd case.
- // Issue 27619.
- pollable = false
-
- case "dragonfly", "netbsd", "openbsd":
+ case "darwin", "dragonfly", "freebsd", "netbsd", "openbsd":
+ var st syscall.Stat_t
+ err := syscall.Fstat(fdi, &st)
+ typ := st.Mode & syscall.S_IFMT
// Don't try to use kqueue with regular files on *BSDs.
// On FreeBSD a regular file is always
// reported as ready for writing.
// On Dragonfly, NetBSD and OpenBSD the fd is signaled
// only once as ready (both read and write).
// Issue 19093.
- if err := syscall.Fstat(fdi, &st); err == nil && st.Mode&syscall.S_IFMT == syscall.S_IFREG {
+ // Also don't add directories to the netpoller.
+ if err == nil && (typ == syscall.S_IFREG || typ == syscall.S_IFDIR) {
pollable = false
}
- case "darwin":
// In addition to the behavior described above for regular files,
// on Darwin, kqueue does not work properly with fifos:
// closing the last writer does not cause a kqueue event
// for any readers. See issue #24164.
- if err := syscall.Fstat(fdi, &st); err == nil && (st.Mode&syscall.S_IFMT == syscall.S_IFIFO || st.Mode&syscall.S_IFMT == syscall.S_IFREG) {
+ if runtime.GOOS == "darwin" && typ == syscall.S_IFIFO {
pollable = false
}
}
@@ -236,6 +231,7 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
// Close closes the File, rendering it unusable for I/O.
// On files that support SetDeadline, any pending I/O operations will
// be canceled and return immediately with an error.
+// Close will return an error if it has already been called.
func (f *File) Close() error {
if f == nil {
return ErrInvalid
@@ -413,3 +409,22 @@ func (f *File) readdir(n int) (fi []FileInfo, err error) {
}
return fi, err
}
+
+// Readlink returns the destination of the named symbolic link.
+// If there is an error, it will be of type *PathError.
+func Readlink(name string) (string, error) {
+ for len := 128; ; len *= 2 {
+ b := make([]byte, len)
+ n, e := fixCount(syscall.Readlink(name, b))
+ // buffer too small
+ if runtime.GOOS == "aix" && e == syscall.ERANGE {
+ continue
+ }
+ if e != nil {
+ return "", &PathError{"readlink", name, e}
+ }
+ if n < len {
+ return string(b[0:n]), nil
+ }
+ }
+}
diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go
index cf68cae..b6430d3 100644
--- a/libgo/go/os/os_test.go
+++ b/libgo/go/os/os_test.go
@@ -57,13 +57,26 @@ var sysdir = func() *sysDir {
if err != nil {
wd = err.Error()
}
- return &sysDir{
+ sd := &sysDir{
filepath.Join(wd, "..", ".."),
[]string{
"ResourceRules.plist",
"Info.plist",
},
}
+ found := true
+ for _, f := range sd.files {
+ path := filepath.Join(sd.name, f)
+ if _, err := Stat(path); err != nil {
+ found = false
+ break
+ }
+ }
+ if found {
+ return sd
+ }
+ // In a self-hosted iOS build the above files might
+ // not exist. Look for system files instead below.
}
case "windows":
return &sysDir{
@@ -1183,21 +1196,25 @@ func TestChdirAndGetwd(t *testing.T) {
// /usr/bin does not usually exist on Plan 9 or Android.
switch runtime.GOOS {
case "android":
- dirs = []string{"/", "/system/bin"}
+ dirs = []string{"/system/bin"}
case "plan9":
dirs = []string{"/", "/usr"}
case "darwin":
switch runtime.GOARCH {
case "arm", "arm64":
- d1, err := ioutil.TempDir("", "d1")
- if err != nil {
- t.Fatalf("TempDir: %v", err)
- }
- d2, err := ioutil.TempDir("", "d2")
- if err != nil {
- t.Fatalf("TempDir: %v", err)
+ dirs = nil
+ for _, d := range []string{"d1", "d2"} {
+ dir, err := ioutil.TempDir("", d)
+ if err != nil {
+ t.Fatalf("TempDir: %v", err)
+ }
+ // Expand symlinks so path equality tests work.
+ dir, err = filepath.EvalSymlinks(dir)
+ if err != nil {
+ t.Fatalf("EvalSymlinks: %v", err)
+ }
+ dirs = append(dirs, dir)
}
- dirs = []string{d1, d2}
}
}
oldwd := Getenv("PWD")
@@ -1648,6 +1665,21 @@ func TestWriteAtNegativeOffset(t *testing.T) {
}
}
+// Verify that WriteAt doesn't work in append mode.
+func TestWriteAtInAppendMode(t *testing.T) {
+ defer chtmpdir(t)()
+ f, err := OpenFile("write_at_in_append_mode.txt", O_APPEND|O_CREATE, 0666)
+ if err != nil {
+ t.Fatalf("OpenFile: %v", err)
+ }
+ defer f.Close()
+
+ _, err = f.WriteAt([]byte(""), 1)
+ if err != ErrWriteAtInAppendMode {
+ t.Fatalf("f.WriteAt returned %v, expected %v", err, ErrWriteAtInAppendMode)
+ }
+}
+
func writeFile(t *testing.T, fname string, flag int, text string) string {
f, err := OpenFile(fname, flag, 0666)
if err != nil {
@@ -2213,8 +2245,8 @@ func TestPipeThreads(t *testing.T) {
switch runtime.GOOS {
case "freebsd":
t.Skip("skipping on FreeBSD; issue 19093")
- case "solaris":
- t.Skip("skipping on Solaris; issue 19111")
+ case "illumos", "solaris":
+ t.Skip("skipping on Solaris and illumos; issue 19111")
case "windows":
t.Skip("skipping on Windows; issue 19098")
case "plan9":
@@ -2281,8 +2313,7 @@ func TestPipeThreads(t *testing.T) {
}
}
-func TestDoubleCloseError(t *testing.T) {
- path := sfdir + "/" + sfname
+func testDoubleCloseError(t *testing.T, path string) {
file, err := Open(path)
if err != nil {
t.Fatal(err)
@@ -2301,6 +2332,11 @@ func TestDoubleCloseError(t *testing.T) {
}
}
+func TestDoubleCloseError(t *testing.T) {
+ testDoubleCloseError(t, filepath.Join(sfdir, sfname))
+ testDoubleCloseError(t, sfdir)
+}
+
func TestUserHomeDir(t *testing.T) {
dir, err := UserHomeDir()
if dir == "" && err == nil {
diff --git a/libgo/go/os/os_unix_test.go b/libgo/go/os/os_unix_test.go
index f609660..7af20d7 100644
--- a/libgo/go/os/os_unix_test.go
+++ b/libgo/go/os/os_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 linux netbsd openbsd solaris
+// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris
package os_test
@@ -152,6 +152,9 @@ func TestLchown(t *testing.T) {
gid := Getgid()
t.Log("gid:", gid)
if err = Lchown(linkname, -1, gid); err != nil {
+ if err, ok := err.(*PathError); ok && err.Err == syscall.ENOSYS {
+ t.Skip("lchown is unavailable")
+ }
t.Fatalf("lchown %s -1 %d: %s", linkname, gid, err)
}
sys := dir.Sys().(*syscall.Stat_t)
@@ -231,6 +234,10 @@ func TestMkdirStickyUmask(t *testing.T) {
// See also issues: 22939, 24331
func newFileTest(t *testing.T, blocking bool) {
+ if runtime.GOOS == "js" {
+ t.Skipf("syscall.Pipe is not available on %s.", runtime.GOOS)
+ }
+
p := make([]int, 2)
if err := syscall.Pipe(p); err != nil {
t.Fatalf("pipe: %v", err)
@@ -252,12 +259,19 @@ func newFileTest(t *testing.T, blocking bool) {
}
defer file.Close()
+ timeToWrite := 100 * time.Millisecond
+ timeToDeadline := 1 * time.Millisecond
+ if !blocking {
+ // Use a longer time to avoid flakes.
+ // We won't be waiting this long anyhow.
+ timeToWrite = 1 * time.Second
+ }
+
// Try to read with deadline (but don't block forever).
b := make([]byte, 1)
- // Send something after 100ms.
- timer := time.AfterFunc(100*time.Millisecond, func() { syscall.Write(p[1], []byte("a")) })
+ timer := time.AfterFunc(timeToWrite, func() { syscall.Write(p[1], []byte("a")) })
defer timer.Stop()
- file.SetReadDeadline(time.Now().Add(10 * time.Millisecond))
+ file.SetReadDeadline(time.Now().Add(timeToDeadline))
_, err := file.Read(b)
if !blocking {
// We want it to fail with a timeout.
@@ -281,3 +295,28 @@ func TestNewFileNonBlock(t *testing.T) {
t.Parallel()
newFileTest(t, false)
}
+
+func TestSplitPath(t *testing.T) {
+ t.Parallel()
+ for _, tt := range []struct{ path, wantDir, wantBase string }{
+ {"a", ".", "a"},
+ {"a/", ".", "a"},
+ {"a//", ".", "a"},
+ {"a/b", "a", "b"},
+ {"a/b/", "a", "b"},
+ {"a/b/c", "a/b", "c"},
+ {"/a", "/", "a"},
+ {"/a/", "/", "a"},
+ {"/a/b", "/a", "b"},
+ {"/a/b/", "/a", "b"},
+ {"/a/b/c", "/a/b", "c"},
+ {"//a", "/", "a"},
+ {"//a/", "/", "a"},
+ {"///a", "/", "a"},
+ {"///a/", "/", "a"},
+ } {
+ if dir, base := SplitPath(tt.path); dir != tt.wantDir || base != tt.wantBase {
+ t.Errorf("splitPath(%q) = %q, %q, want %q, %q", tt.path, dir, base, tt.wantDir, tt.wantBase)
+ }
+ }
+}
diff --git a/libgo/go/os/path.go b/libgo/go/os/path.go
index ba43ea3..9d7ecad 100644
--- a/libgo/go/os/path.go
+++ b/libgo/go/os/path.go
@@ -58,6 +58,9 @@ 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 4864989..4c97f39 100644
--- a/libgo/go/os/path_unix.go
+++ b/libgo/go/os/path_unix.go
@@ -38,20 +38,30 @@ func basename(name string) string {
func splitPath(path string) (string, string) {
// if no better parent is found, the path is relative from "here"
dirname := "."
- // if no slashes in path, base is path
- basename := path
+
+ // Remove all but one leading slash.
+ for len(path) > 1 && path[0] == '/' && path[1] == '/' {
+ path = path[1:]
+ }
i := len(path) - 1
- // Remove trailing slashes
+ // Remove trailing slashes.
for ; i > 0 && path[i] == '/'; i-- {
path = path[:i]
}
+ // if no slashes in path, base is path
+ basename := path
+
// Remove leading directory path
for i--; i >= 0; i-- {
if path[i] == '/' {
- dirname = path[:i]
+ if i == 0 {
+ dirname = path[:1]
+ } else {
+ dirname = path[:i]
+ }
basename = path[i+1:]
break
}
diff --git a/libgo/go/os/pipe_test.go b/libgo/go/os/pipe_test.go
index 779b2bd..4c53bc9 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 !windows,!plan9,!nacl,!js
+// +build !plan9,!nacl,!js
package os_test
@@ -35,6 +35,11 @@ func TestEPIPE(t *testing.T) {
t.Fatal(err)
}
+ expect := syscall.EPIPE
+ if runtime.GOOS == "windows" {
+ // 232 is Windows error code ERROR_NO_DATA, "The pipe is being closed".
+ expect = syscall.Errno(232)
+ }
// Every time we write to the pipe we should get an EPIPE.
for i := 0; i < 20; i++ {
_, err = w.Write([]byte("hi"))
@@ -47,13 +52,17 @@ func TestEPIPE(t *testing.T) {
if se, ok := err.(*os.SyscallError); ok {
err = se.Err
}
- if err != syscall.EPIPE {
- t.Errorf("iteration %d: got %v, expected EPIPE", i, err)
+ if err != expect {
+ t.Errorf("iteration %d: got %v, expected %v", i, err, expect)
}
}
}
func TestStdPipe(t *testing.T) {
+ switch runtime.GOOS {
+ case "windows":
+ t.Skip("Windows doesn't support SIGPIPE")
+ }
testenv.MustHaveExec(t)
r, w, err := os.Pipe()
if err != nil {
@@ -195,8 +204,12 @@ func TestClosedPipeRaceWrite(t *testing.T) {
// for unsupported file type." Currently it returns EAGAIN; it is
// possible that in the future it will simply wait for data.
func TestReadNonblockingFd(t *testing.T) {
+ switch runtime.GOOS {
+ case "windows":
+ t.Skip("Windows doesn't support SetNonblock")
+ }
if os.Getenv("GO_WANT_READ_NONBLOCKING_FD") == "1" {
- fd := int(os.Stdin.Fd())
+ fd := syscallDescriptor(os.Stdin.Fd())
syscall.SetNonblock(fd, true)
defer syscall.SetNonblock(fd, false)
_, err := os.Stdin.Read(make([]byte, 1))
@@ -226,7 +239,7 @@ func TestReadNonblockingFd(t *testing.T) {
}
func TestCloseWithBlockingReadByNewFile(t *testing.T) {
- var p [2]int
+ var p [2]syscallDescriptor
err := syscall.Pipe(p[:])
if err != nil {
t.Fatal(err)
@@ -276,8 +289,11 @@ func testCloseWithBlockingRead(t *testing.T, r, w *os.File) {
if err == nil {
t.Error("I/O on closed pipe unexpectedly succeeded")
}
- if err != io.EOF {
- t.Errorf("got %v, expected io.EOF", err)
+ if pe, ok := err.(*os.PathError); ok {
+ err = pe.Err
+ }
+ if err != io.EOF && err != os.ErrClosed {
+ t.Errorf("got %v, expected EOF or closed", err)
}
}(c2)
diff --git a/libgo/go/os/proc.go b/libgo/go/os/proc.go
index 804128a..7364d63 100644
--- a/libgo/go/os/proc.go
+++ b/libgo/go/os/proc.go
@@ -56,6 +56,8 @@ func Getgroups() ([]int, error) {
// Exit causes the current program to exit with the given status code.
// Conventionally, code zero indicates success, non-zero an error.
// The program terminates immediately; deferred functions are not run.
+//
+// For portability, the status code should be in the range [0, 125].
func Exit(code int) {
if code == 0 {
// Give race detector a chance to fail the program.
diff --git a/libgo/go/os/removeall_at.go b/libgo/go/os/removeall_at.go
index 6fdd7e8..f3ecf57 100644
--- a/libgo/go/os/removeall_at.go
+++ b/libgo/go/os/removeall_at.go
@@ -91,7 +91,8 @@ func removeAllFrom(parent *File, base string) error {
// Remove the directory's entries.
var recurseErr error
for {
- const request = 1024
+ const reqSize = 1024
+ var respSize int
// Open the directory to recurse into
file, err := openFdAt(parentFd, base)
@@ -103,23 +104,37 @@ func removeAllFrom(parent *File, base string) error {
break
}
- names, readErr := file.Readdirnames(request)
- // Errors other than EOF should stop us from continuing.
- if readErr != nil && readErr != io.EOF {
- file.Close()
- if IsNotExist(readErr) {
- return nil
+ for {
+ numErr := 0
+
+ names, readErr := file.Readdirnames(reqSize)
+ // Errors other than EOF should stop us from continuing.
+ if readErr != nil && readErr != io.EOF {
+ file.Close()
+ if IsNotExist(readErr) {
+ return nil
+ }
+ return &PathError{"readdirnames", base, readErr}
}
- return &PathError{"readdirnames", base, readErr}
- }
- for _, name := range names {
- err := removeAllFrom(file, name)
- if err != nil {
- if pathErr, ok := err.(*PathError); ok {
- pathErr.Path = base + string(PathSeparator) + pathErr.Path
+ respSize = len(names)
+ for _, name := range names {
+ err := removeAllFrom(file, name)
+ if err != nil {
+ if pathErr, ok := err.(*PathError); ok {
+ pathErr.Path = base + string(PathSeparator) + pathErr.Path
+ }
+ numErr++
+ if recurseErr == nil {
+ recurseErr = err
+ }
}
- recurseErr = err
+ }
+
+ // If we can delete any entry, break to start new iteration.
+ // Otherwise, we discard current names, get next entries and try deleting them.
+ if numErr != reqSize {
+ break
}
}
@@ -131,13 +146,14 @@ func removeAllFrom(parent *File, base string) error {
file.Close()
// Finish when the end of the directory is reached
- if len(names) < request {
+ if respSize < reqSize {
break
}
}
// 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 7d9f73e..cf26bdb 100644
--- a/libgo/go/os/removeall_noat.go
+++ b/libgo/go/os/removeall_noat.go
@@ -56,8 +56,30 @@ func removeAll(path string) error {
return err
}
- const request = 1024
- names, err1 := fd.Readdirnames(request)
+ const reqSize = 1024
+ var names []string
+ var readErr error
+
+ for {
+ numErr := 0
+ names, readErr = fd.Readdirnames(reqSize)
+
+ for _, name := range names {
+ err1 := RemoveAll(path + string(PathSeparator) + name)
+ if err == nil {
+ err = err1
+ }
+ if err1 != nil {
+ numErr++
+ }
+ }
+
+ // If we can delete any entry, break to start new iteration.
+ // Otherwise, we discard current names, get next entries and try deleting them.
+ if numErr != reqSize {
+ break
+ }
+ }
// Removing files from the directory may have caused
// the OS to reshuffle it. Simply calling Readdirnames
@@ -66,19 +88,12 @@ func removeAll(path string) error {
// directory. See issue 20841.
fd.Close()
- for _, name := range names {
- err1 := RemoveAll(path + string(PathSeparator) + name)
- if err == nil {
- err = err1
- }
- }
-
- if err1 == io.EOF {
+ if readErr == io.EOF {
break
}
// If Readdirnames returned an error, use it.
if err == nil {
- err = err1
+ err = readErr
}
if len(names) == 0 {
break
@@ -88,7 +103,7 @@ func removeAll(path string) error {
// got fewer than request names from Readdirnames, try
// simply removing the directory now. If that
// succeeds, we are done.
- if len(names) < request {
+ if len(names) < reqSize {
err1 := Remove(path)
if err1 == nil || IsNotExist(err1) {
return nil
@@ -109,6 +124,7 @@ func removeAll(path string) error {
// Remove directory.
err1 := Remove(path)
+ err1 = removeAllTestHook(err1)
if err1 == nil || IsNotExist(err1) {
return nil
}
diff --git a/libgo/go/os/removeall_test.go b/libgo/go/os/removeall_test.go
index 945a38e..4d556f9 100644
--- a/libgo/go/os/removeall_test.go
+++ b/libgo/go/os/removeall_test.go
@@ -5,6 +5,7 @@
package os_test
import (
+ "errors"
"fmt"
"io/ioutil"
. "os"
@@ -80,16 +81,8 @@ func TestRemoveAll(t *testing.T) {
t.Fatalf("Lstat %q succeeded after RemoveAll (third)", path)
}
- // Determine if we should run the following test.
- testit := true
- if runtime.GOOS == "windows" {
- // Chmod is not supported under windows.
- testit = false
- } else {
- // Test fails as root.
- testit = Getuid() != 0
- }
- if testit {
+ // Chmod is not supported under Windows and test fails as root.
+ if runtime.GOOS != "windows" && Getuid() != 0 {
// Make directory with file and subdirectory and trigger error.
if err = MkdirAll(dpath, 0777); err != nil {
t.Fatalf("MkdirAll %q: %s", dpath, err)
@@ -166,7 +159,7 @@ func TestRemoveAllLarge(t *testing.T) {
func TestRemoveAllLongPath(t *testing.T) {
switch runtime.GOOS {
- case "aix", "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris":
+ case "aix", "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "illumos", "solaris":
break
default:
t.Skip("skipping for not implemented platforms")
@@ -413,3 +406,49 @@ func TestRemoveUnreadableDir(t *testing.T) {
t.Fatal(err)
}
}
+
+// Issue 29921
+func TestRemoveAllWithMoreErrorThanReqSize(t *testing.T) {
+ if testing.Short() {
+ 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)
+ }
+ defer RemoveAll(tmpDir)
+
+ path := filepath.Join(tmpDir, "_TestRemoveAllWithMoreErrorThanReqSize_")
+
+ // Make directory with 1025 files and remove.
+ if err := MkdirAll(path, 0777); err != nil {
+ t.Fatalf("MkdirAll %q: %s", path, err)
+ }
+ for i := 0; i < 1025; i++ {
+ fpath := filepath.Join(path, fmt.Sprintf("file%d", i))
+ fd, err := Create(fpath)
+ if err != nil {
+ t.Fatalf("create %q: %s", fpath, err)
+ }
+ fd.Close()
+ }
+
+ // This call should not hang
+ if err := RemoveAll(path); err == nil {
+ t.Fatal("Want error from RemoveAllTestHook, got nil")
+ }
+
+ // 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")
+ }
+}
diff --git a/libgo/go/os/signal/internal/pty/pty.go b/libgo/go/os/signal/internal/pty/pty.go
index 4f65ad8..f7d61f9 100644
--- a/libgo/go/os/signal/internal/pty/pty.go
+++ b/libgo/go/os/signal/internal/pty/pty.go
@@ -48,6 +48,8 @@ func (e *PtyError) Error() string {
return fmt.Sprintf("%s: %s", e.FuncName, e.ErrorString)
}
+func (e *PtyError) Unwrap() error { return e.Errno }
+
// Open returns a master pty and the name of the linked slave tty.
func Open() (master *os.File, slave string, err error) {
m := posix_openpt(_O_RDWR)
diff --git a/libgo/go/os/signal/signal_cgo_test.go b/libgo/go/os/signal/signal_cgo_test.go
index 3c23090..075e8c1 100644
--- a/libgo/go/os/signal/signal_cgo_test.go
+++ b/libgo/go/os/signal/signal_cgo_test.go
@@ -101,6 +101,17 @@ func TestTerminalSignal(t *testing.T) {
Ctty: int(slave.Fd()),
}
+ // Test ctty management by sending enough child fd to overlap the
+ // parent's fd intended for child's ctty.
+ for 2+len(cmd.ExtraFiles) < cmd.SysProcAttr.Ctty {
+ dummy, err := os.Open(os.DevNull)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer dummy.Close()
+ cmd.ExtraFiles = append(cmd.ExtraFiles, dummy)
+ }
+
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
diff --git a/libgo/go/os/sticky_bsd.go b/libgo/go/os/sticky_bsd.go
index ae2744f..c09b1ac 100644
--- a/libgo/go/os/sticky_bsd.go
+++ b/libgo/go/os/sticky_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 freebsd netbsd openbsd solaris
+// +build aix darwin dragonfly freebsd js,wasm netbsd openbsd solaris
package os
diff --git a/libgo/go/os/sticky_notbsd.go b/libgo/go/os/sticky_notbsd.go
index edb5f69..c158506 100644
--- a/libgo/go/os/sticky_notbsd.go
+++ b/libgo/go/os/sticky_notbsd.go
@@ -6,6 +6,7 @@
// +build !darwin
// +build !dragonfly
// +build !freebsd
+// +build !js !wasm
// +build !netbsd
// +build !openbsd
// +build !solaris
diff --git a/libgo/go/os/timeout_test.go b/libgo/go/os/timeout_test.go
index 4720738..5d7ea7e 100644
--- a/libgo/go/os/timeout_test.go
+++ b/libgo/go/os/timeout_test.go
@@ -514,7 +514,7 @@ func TestReadWriteDeadlineRace(t *testing.T) {
}
// TestRacyRead tests that it is safe to mutate the input Read buffer
-// immediately after cancelation has occurred.
+// immediately after cancellation has occurred.
func TestRacyRead(t *testing.T) {
t.Parallel()
@@ -553,7 +553,7 @@ func TestRacyRead(t *testing.T) {
}
// TestRacyWrite tests that it is safe to mutate the input Write buffer
-// immediately after cancelation has occurred.
+// immediately after cancellation has occurred.
func TestRacyWrite(t *testing.T) {
t.Parallel()
diff --git a/libgo/go/os/types_windows.go b/libgo/go/os/types_windows.go
index 5e33292..3d1a667 100644
--- a/libgo/go/os/types_windows.go
+++ b/libgo/go/os/types_windows.go
@@ -189,6 +189,21 @@ func (fs *fileStat) loadFileId() error {
return nil
}
+// saveInfoFromPath saves full path of the file to be used by os.SameFile later,
+// and set name from path.
+func (fs *fileStat) saveInfoFromPath(path string) error {
+ fs.path = path
+ if !isAbs(fs.path) {
+ var err error
+ fs.path, err = syscall.FullPath(fs.path)
+ if err != nil {
+ return &PathError{"FullPath", path, err}
+ }
+ }
+ fs.name = basename(path)
+ return nil
+}
+
// devNullStat is fileStat structure describing DevNull file ("NUL").
var devNullStat = fileStat{
name: DevNull,
diff --git a/libgo/go/os/user/lookup_stubs.go b/libgo/go/os/user/lookup_stubs.go
index 61bf1dc..d3acbdd 100644
--- a/libgo/go/os/user/lookup_stubs.go
+++ b/libgo/go/os/user/lookup_stubs.go
@@ -26,12 +26,14 @@ func current() (*User, error) {
if err == nil {
return u, nil
}
+
+ homeDir, _ := os.UserHomeDir()
u = &User{
Uid: uid,
Gid: currentGID(),
Username: os.Getenv("USER"),
Name: "", // ignored
- HomeDir: os.Getenv("HOME"),
+ HomeDir: homeDir,
}
// On NaCL and Android, return a dummy user instead of failing.
switch runtime.GOOS {
@@ -42,9 +44,6 @@ func current() (*User, error) {
if u.Username == "" {
u.Username = "nacl"
}
- if u.HomeDir == "" {
- u.HomeDir = "/"
- }
case "android":
if u.Uid == "" {
u.Uid = "1"
@@ -52,16 +51,23 @@ func current() (*User, error) {
if u.Username == "" {
u.Username = "android"
}
- if u.HomeDir == "" {
- u.HomeDir = "/sdcard"
- }
}
// cgo isn't available, but if we found the minimum information
// without it, use it:
if u.Uid != "" && u.Username != "" && u.HomeDir != "" {
return u, nil
}
- return u, fmt.Errorf("user: Current not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+ var missing string
+ if u.Username == "" {
+ missing = "$USER"
+ }
+ if u.HomeDir == "" {
+ if missing != "" {
+ missing += ", "
+ }
+ missing += "$HOME"
+ }
+ return u, fmt.Errorf("user: Current requires cgo or %s set in environment", missing)
}
func listGroups(*User) ([]string, error) {
diff --git a/libgo/go/os/user/user_test.go b/libgo/go/os/user/user_test.go
index eeb24dd..8c4c817 100644
--- a/libgo/go/os/user/user_test.go
+++ b/libgo/go/os/user/user_test.go
@@ -132,7 +132,7 @@ func TestGroupIds(t *testing.T) {
if runtime.GOOS == "aix" {
t.Skip("skipping GroupIds, see golang.org/issue/30563")
}
- if runtime.GOOS == "solaris" {
+ if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" {
t.Skip("skipping GroupIds, see golang.org/issue/14709")
}
user, err := Current()