aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/os
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2018-01-09 01:23:08 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2018-01-09 01:23:08 +0000
commit1a2f01efa63036a5104f203a4789e682c0e0915d (patch)
tree373e15778dc8295354584e1f86915ae493b604ff /libgo/go/os
parent8799df67f2dab88f9fda11739c501780a85575e2 (diff)
downloadgcc-1a2f01efa63036a5104f203a4789e682c0e0915d.zip
gcc-1a2f01efa63036a5104f203a4789e682c0e0915d.tar.gz
gcc-1a2f01efa63036a5104f203a4789e682c0e0915d.tar.bz2
libgo: update to Go1.10beta1
Update the Go library to the 1.10beta1 release. Requires a few changes to the compiler for modifications to the map runtime code, and to handle some nowritebarrier cases in the runtime. Reviewed-on: https://go-review.googlesource.com/86455 gotools/: * Makefile.am (go_cmd_vet_files): New variable. (go_cmd_buildid_files, go_cmd_test2json_files): New variables. (s-zdefaultcc): Change from constants to functions. (noinst_PROGRAMS): Add vet, buildid, and test2json. (cgo$(EXEEXT)): Link against $(LIBGOTOOL). (vet$(EXEEXT)): New target. (buildid$(EXEEXT)): New target. (test2json$(EXEEXT)): New target. (install-exec-local): Install all $(noinst_PROGRAMS). (uninstall-local): Uninstasll all $(noinst_PROGRAMS). (check-go-tool): Depend on $(noinst_PROGRAMS). Copy down objabi.go. (check-runtime): Depend on $(noinst_PROGRAMS). (check-cgo-test, check-carchive-test): Likewise. (check-vet): New target. (check): Depend on check-vet. Look at cmd_vet-testlog. (.PHONY): Add check-vet. * Makefile.in: Rebuild. From-SVN: r256365
Diffstat (limited to 'libgo/go/os')
-rw-r--r--libgo/go/os/env_test.go2
-rw-r--r--libgo/go/os/error.go25
-rw-r--r--libgo/go/os/exec/exec.go78
-rw-r--r--libgo/go/os/exec_plan9.go7
-rw-r--r--libgo/go/os/exec_posix.go7
-rw-r--r--libgo/go/os/file.go49
-rw-r--r--libgo/go/os/file_plan9.go26
-rw-r--r--libgo/go/os/file_posix.go24
-rw-r--r--libgo/go/os/file_unix.go40
-rw-r--r--libgo/go/os/os_test.go66
-rw-r--r--libgo/go/os/os_unix_test.go13
-rw-r--r--libgo/go/os/path.go6
-rw-r--r--libgo/go/os/path_test.go31
-rw-r--r--libgo/go/os/pipe_bsd.go2
-rw-r--r--libgo/go/os/pipe_freebsd.go18
-rw-r--r--libgo/go/os/pipe_linux.go2
-rw-r--r--libgo/go/os/signal/internal/pty/pty.go62
-rw-r--r--libgo/go/os/signal/signal_cgo_test.go228
-rw-r--r--libgo/go/os/signal/signal_test.go6
-rw-r--r--libgo/go/os/sys_freebsd.go17
-rw-r--r--libgo/go/os/timeout_test.go589
-rw-r--r--libgo/go/os/user/cgo_lookup_unix.go15
-rw-r--r--libgo/go/os/user/cgo_unix_test.go24
-rw-r--r--libgo/go/os/user/listgroups_unix.go3
-rw-r--r--libgo/go/os/wait_wait6.go5
25 files changed, 1189 insertions, 156 deletions
diff --git a/libgo/go/os/env_test.go b/libgo/go/os/env_test.go
index e5749f0..16f1945 100644
--- a/libgo/go/os/env_test.go
+++ b/libgo/go/os/env_test.go
@@ -134,7 +134,7 @@ func TestLookupEnv(t *testing.T) {
if err != nil {
t.Fatalf("failed to release smallpox virus")
}
- value, ok = LookupEnv(smallpox)
+ _, ok = LookupEnv(smallpox)
if !ok {
t.Errorf("smallpox release failed; world remains safe but LookupEnv is broken")
}
diff --git a/libgo/go/os/error.go b/libgo/go/os/error.go
index 7235bfb..b4242a4 100644
--- a/libgo/go/os/error.go
+++ b/libgo/go/os/error.go
@@ -6,6 +6,7 @@ package os
import (
"errors"
+ "internal/poll"
)
// Portable analogs of some common system call errors.
@@ -15,8 +16,13 @@ var (
ErrExist = errors.New("file already exists")
ErrNotExist = errors.New("file does not exist")
ErrClosed = errors.New("file already closed")
+ ErrNoDeadline = poll.ErrNoDeadline
)
+type timeout interface {
+ Timeout() bool
+}
+
// PathError records an error and the operation and file path that caused it.
type PathError struct {
Op string
@@ -26,6 +32,12 @@ type PathError struct {
func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }
+// Timeout reports whether this error represents a timeout.
+func (e *PathError) Timeout() bool {
+ t, ok := e.Err.(timeout)
+ return ok && t.Timeout()
+}
+
// SyscallError records an error from a specific system call.
type SyscallError struct {
Syscall string
@@ -34,6 +46,12 @@ type SyscallError struct {
func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Err.Error() }
+// Timeout reports whether this error represents a timeout.
+func (e *SyscallError) Timeout() bool {
+ t, ok := e.Err.(timeout)
+ return ok && t.Timeout()
+}
+
// NewSyscallError returns, as an error, a new SyscallError
// with the given system call name and error details.
// As a convenience, if err is nil, NewSyscallError returns nil.
@@ -65,6 +83,13 @@ func IsPermission(err error) bool {
return isPermission(err)
}
+// IsTimeout returns a boolean indicating whether the error is known
+// to report that a timeout occurred.
+func IsTimeout(err error) bool {
+ terr, ok := underlyingError(err).(timeout)
+ return ok && terr.Timeout()
+}
+
// 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/exec/exec.go b/libgo/go/os/exec/exec.go
index 893d8ee..8a49fe3 100644
--- a/libgo/go/os/exec/exec.go
+++ b/libgo/go/os/exec/exec.go
@@ -77,9 +77,12 @@ type Cmd struct {
Dir string
// Stdin specifies the process's standard input.
+ //
// If Stdin is nil, the process reads from the null device (os.DevNull).
+ //
// If Stdin is an *os.File, the process's standard input is connected
// directly to that file.
+ //
// Otherwise, during the execution of the command a separate
// goroutine reads from Stdin and delivers that data to the command
// over a pipe. In this case, Wait does not complete until the goroutine
@@ -92,8 +95,16 @@ type Cmd struct {
// If either is nil, Run connects the corresponding file descriptor
// to the null device (os.DevNull).
//
- // If Stdout and Stderr are the same writer, and have a type that can be compared with ==,
- // at most one goroutine at a time will call Write.
+ // If either is an *os.File, the corresponding output from the process
+ // is connected directly to that file.
+ //
+ // Otherwise, during the execution of the command a separate goroutine
+ // reads from the process over a pipe and delivers that data to the
+ // corresponding Writer. In this case, Wait does not complete until the
+ // goroutine reaches EOF or encounters an error.
+ //
+ // If Stdout and Stderr are the same writer, and have a type that can
+ // be compared with ==, at most one goroutine at a time will call Write.
Stdout io.Writer
Stderr io.Writer
@@ -190,7 +201,7 @@ func (c *Cmd) argv() []string {
}
// skipStdinCopyError optionally specifies a function which reports
-// whether the provided the stdin copy error should be ignored.
+// 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
@@ -429,9 +440,8 @@ func (e *ExitError) Error() string {
// error is of type *ExitError. Other error types may be
// returned for I/O problems.
//
-// If c.Stdin is not an *os.File, Wait also waits for the I/O loop
-// copying from c.Stdin into the process's standard input
-// to complete.
+// If any of c.Stdin, c.Stdout or c.Stderr are not an *os.File, Wait also waits
+// for the respective I/O loop copying to or from the process to complete.
//
// Wait releases any resources associated with the Cmd.
func (c *Cmd) Wait() error {
@@ -527,16 +537,15 @@ func (c *Cmd) StdinPipe() (io.WriteCloser, error) {
c.Stdin = pr
c.closeAfterStart = append(c.closeAfterStart, pr)
wc := &closeOnce{File: pw}
- c.closeAfterWait = append(c.closeAfterWait, closerFunc(wc.safeClose))
+ c.closeAfterWait = append(c.closeAfterWait, wc)
return wc, nil
}
type closeOnce struct {
*os.File
- writers sync.RWMutex // coordinate safeClose and Write
- once sync.Once
- err error
+ once sync.Once
+ err error
}
func (c *closeOnce) Close() error {
@@ -548,55 +557,6 @@ func (c *closeOnce) close() {
c.err = c.File.Close()
}
-type closerFunc func() error
-
-func (f closerFunc) Close() error { return f() }
-
-// safeClose closes c being careful not to race with any calls to c.Write.
-// See golang.org/issue/9307 and TestEchoFileRace in exec_test.go.
-// In theory other calls could also be excluded (by writing appropriate
-// wrappers like c.Write's implementation below), but since c is most
-// commonly used as a WriteCloser, Write is the main one to worry about.
-// See also #7970, for which this is a partial fix for this specific instance.
-// The idea is that we return a WriteCloser, and so the caller can be
-// relied upon not to call Write and Close simultaneously, but it's less
-// obvious that cmd.Wait calls Close and that the caller must not call
-// Write and cmd.Wait simultaneously. In fact that seems too onerous.
-// So we change the use of Close in cmd.Wait to use safeClose, which will
-// synchronize with any Write.
-//
-// It's important that we know this won't block forever waiting for the
-// operations being excluded. At the point where this is called,
-// the invoked command has exited and the parent copy of the read side
-// of the pipe has also been closed, so there should really be no read side
-// of the pipe left. Any active writes should return very shortly with an EPIPE,
-// making it reasonable to wait for them.
-// Technically it is possible that the child forked a sub-process or otherwise
-// handed off the read side of the pipe before exiting and the current holder
-// is not reading from the pipe, and the pipe is full, in which case the close here
-// might block waiting for the write to complete. That's probably OK.
-// It's a small enough problem to be outweighed by eliminating the race here.
-func (c *closeOnce) safeClose() error {
- c.writers.Lock()
- err := c.Close()
- c.writers.Unlock()
- return err
-}
-
-func (c *closeOnce) Write(b []byte) (int, error) {
- c.writers.RLock()
- n, err := c.File.Write(b)
- c.writers.RUnlock()
- return n, err
-}
-
-func (c *closeOnce) WriteString(s string) (int, error) {
- c.writers.RLock()
- n, err := c.File.WriteString(s)
- c.writers.RUnlock()
- return n, err
-}
-
// StdoutPipe returns a pipe that will be connected to the command's
// standard output when the command starts.
//
diff --git a/libgo/go/os/exec_plan9.go b/libgo/go/os/exec_plan9.go
index 676be36..6b4d28c 100644
--- a/libgo/go/os/exec_plan9.go
+++ b/libgo/go/os/exec_plan9.go
@@ -11,9 +11,10 @@ import (
"time"
)
-// The only signal values guaranteed to be present on all systems
-// are Interrupt (send the process an interrupt) and Kill (force
-// the process to exit).
+// The only signal values guaranteed to be present in the os package
+// on all systems are Interrupt (send the process an interrupt) and
+// Kill (force the process to exit). Interrupt is not implemented on
+// Windows; using it with os.Process.Signal will return an error.
var (
Interrupt Signal = syscall.Note("interrupt")
Kill Signal = syscall.Note("kill")
diff --git a/libgo/go/os/exec_posix.go b/libgo/go/os/exec_posix.go
index 9e792b4..056f139 100644
--- a/libgo/go/os/exec_posix.go
+++ b/libgo/go/os/exec_posix.go
@@ -10,9 +10,10 @@ import (
"syscall"
)
-// The only signal values guaranteed to be present on all systems
-// are Interrupt (send the process an interrupt) and Kill (force
-// the process to exit).
+// The only signal values guaranteed to be present in the os package
+// on all systems are Interrupt (send the process an interrupt) and
+// Kill (force the process to exit). Interrupt is not implemented on
+// Windows; using it with os.Process.Signal will return an error.
var (
Interrupt Signal = syscall.SIGINT
Kill Signal = syscall.SIGKILL
diff --git a/libgo/go/os/file.go b/libgo/go/os/file.go
index 4b4d8fb..542b074 100644
--- a/libgo/go/os/file.go
+++ b/libgo/go/os/file.go
@@ -41,6 +41,7 @@ import (
"internal/poll"
"io"
"syscall"
+ "time"
)
// Name returns the name of the file as presented to Open.
@@ -61,12 +62,14 @@ var (
// Flags to OpenFile wrapping those of the underlying system. Not all
// flags may be implemented on a given system.
const (
+ // Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
O_RDONLY int = syscall.O_RDONLY // open the file read-only.
O_WRONLY int = syscall.O_WRONLY // open the file write-only.
O_RDWR int = syscall.O_RDWR // open the file read-write.
+ // The remaining values may be or'ed in to control behavior.
O_APPEND int = syscall.O_APPEND // append data to the file when writing.
O_CREATE int = syscall.O_CREAT // create a new file if none exists.
- O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist
+ O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist.
O_SYNC int = syscall.O_SYNC // open for synchronous I/O.
O_TRUNC int = syscall.O_TRUNC // if possible, truncate file when opened.
)
@@ -316,3 +319,47 @@ func Chmod(name string, mode FileMode) error { return chmod(name, mode) }
// Chmod changes the mode of the file to mode.
// If there is an error, it will be of type *PathError.
func (f *File) Chmod(mode FileMode) error { return f.chmod(mode) }
+
+// SetDeadline sets the read and write deadlines for a File.
+// It is equivalent to calling both SetReadDeadline and SetWriteDeadline.
+//
+// Only some kinds of files support setting a deadline. Calls to SetDeadline
+// for files that do not support deadlines will return ErrNoDeadline.
+// On most systems ordinary files do not support deadlines, but pipes do.
+//
+// A deadline is an absolute time after which I/O operations fail with an
+// error instead of blocking. The deadline applies to all future and pending
+// I/O, not just the immediately following call to Read or Write.
+// After a deadline has been exceeded, the connection can be refreshed
+// by setting a deadline in the future.
+//
+// An error returned after a timeout fails will implement the
+// Timeout method, and calling the Timeout method will return true.
+// The PathError and SyscallError types implement the Timeout method.
+// In general, call IsTimeout to test whether an error indicates a timeout.
+//
+// An idle timeout can be implemented by repeatedly extending
+// the deadline after successful Read or Write calls.
+//
+// A zero value for t means I/O operations will not time out.
+func (f *File) SetDeadline(t time.Time) error {
+ return f.setDeadline(t)
+}
+
+// SetReadDeadline sets the deadline for future Read calls and any
+// currently-blocked Read call.
+// A zero value for t means Read will not time out.
+// Not all files support setting deadlines; see SetDeadline.
+func (f *File) SetReadDeadline(t time.Time) error {
+ return f.setReadDeadline(t)
+}
+
+// SetWriteDeadline sets the deadline for any future Write calls and any
+// currently-blocked Write call.
+// Even if Write times out, it may return n > 0, indicating that
+// some of the data was successfully written.
+// A zero value for t means Write will not time out.
+// Not all files support setting deadlines; see SetDeadline.
+func (f *File) SetWriteDeadline(t time.Time) error {
+ return f.setWriteDeadline(t)
+}
diff --git a/libgo/go/os/file_plan9.go b/libgo/go/os/file_plan9.go
index 0f4a736..e4f8fd9 100644
--- a/libgo/go/os/file_plan9.go
+++ b/libgo/go/os/file_plan9.go
@@ -5,6 +5,7 @@
package os
import (
+ "internal/poll"
"io"
"runtime"
"syscall"
@@ -28,6 +29,7 @@ type file struct {
// Fd returns the integer Plan 9 file descriptor referencing the open file.
// The file descriptor is valid only until f.Close is called or f is garbage collected.
+// On Unix systems this will cause the SetDeadline methods to stop working.
func (f *File) Fd() uintptr {
if f == nil {
return ^(uintptr(0))
@@ -491,6 +493,30 @@ func (f *File) Chdir() error {
return nil
}
+// setDeadline sets the read and write deadline.
+func (f *File) setDeadline(time.Time) error {
+ if err := f.checkValid("SetDeadline"); err != nil {
+ return err
+ }
+ return poll.ErrNoDeadline
+}
+
+// setReadDeadline sets the read deadline.
+func (f *File) setReadDeadline(time.Time) error {
+ if err := f.checkValid("SetReadDeadline"); err != nil {
+ return err
+ }
+ return poll.ErrNoDeadline
+}
+
+// setWriteDeadline sets the write deadline.
+func (f *File) setWriteDeadline(time.Time) error {
+ if err := f.checkValid("SetWriteDeadline"); err != nil {
+ return err
+ }
+ return poll.ErrNoDeadline
+}
+
// checkValid checks whether f is valid for use.
// If not, it returns an appropriate error, perhaps incorporating the operation name op.
func (f *File) checkValid(op string) error {
diff --git a/libgo/go/os/file_posix.go b/libgo/go/os/file_posix.go
index 51cae9d..67da384 100644
--- a/libgo/go/os/file_posix.go
+++ b/libgo/go/os/file_posix.go
@@ -163,6 +163,30 @@ func (f *File) Chdir() error {
return nil
}
+// setDeadline sets the read and write deadline.
+func (f *File) setDeadline(t time.Time) error {
+ if err := f.checkValid("SetDeadline"); err != nil {
+ return err
+ }
+ return f.pfd.SetDeadline(t)
+}
+
+// setReadDeadline sets the read deadline.
+func (f *File) setReadDeadline(t time.Time) error {
+ if err := f.checkValid("SetReadDeadline"); err != nil {
+ return err
+ }
+ return f.pfd.SetReadDeadline(t)
+}
+
+// setWriteDeadline sets the write deadline.
+func (f *File) setWriteDeadline(t time.Time) error {
+ if err := f.checkValid("SetWriteDeadline"); err != nil {
+ return err
+ }
+ return f.pfd.SetWriteDeadline(t)
+}
+
// checkValid checks whether f is valid for use.
// If not, it returns an appropriate error, perhaps incorporating the operation name op.
func (f *File) checkValid(op string) error {
diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go
index 8199994..af9c37f9 100644
--- a/libgo/go/os/file_unix.go
+++ b/libgo/go/os/file_unix.go
@@ -45,14 +45,16 @@ func rename(oldname, newname string) error {
// can overwrite this data, which could cause the finalizer
// to close the wrong file descriptor.
type file struct {
- pfd poll.FD
- name string
- dirinfo *dirInfo // nil unless directory being read
- nonblock bool // whether we set nonblocking mode
+ pfd poll.FD
+ name string
+ dirinfo *dirInfo // nil unless directory being read
+ nonblock bool // whether we set nonblocking mode
+ stdoutOrErr bool // whether this is stdout or stderr
}
// Fd returns the integer Unix file descriptor referencing the open file.
// The file descriptor is valid only until f.Close is called or f is garbage collected.
+// On Unix systems this will cause the SetDeadline methods to stop working.
func (f *File) Fd() uintptr {
if f == nil {
return ^(uintptr(0))
@@ -74,12 +76,22 @@ func (f *File) Fd() uintptr {
// name. The returned value will be nil if fd is not a valid file
// descriptor.
func NewFile(fd uintptr, name string) *File {
- return newFile(fd, name, false)
+ return newFile(fd, name, kindNewFile)
}
-// newFile is like NewFile, but if pollable is true it tries to add the
-// file to the runtime poller.
-func newFile(fd uintptr, name string, pollable bool) *File {
+// newFileKind describes the kind of file to newFile.
+type newFileKind int
+
+const (
+ kindNewFile newFileKind = iota
+ kindOpenFile
+ kindPipe
+)
+
+// newFile is like NewFile, but if called from OpenFile or Pipe
+// (as passed in the kind parameter) it tries to add the file to
+// the runtime poller.
+func newFile(fd uintptr, name string, kind newFileKind) *File {
fdi := int(fd)
if fdi < 0 {
return nil
@@ -90,16 +102,18 @@ func newFile(fd uintptr, name string, pollable bool) *File {
IsStream: true,
ZeroReadIsEOF: true,
},
- name: name,
+ name: name,
+ stdoutOrErr: fdi == 1 || fdi == 2,
}}
// Don't try to use kqueue with regular files on FreeBSD.
// It crashes the system unpredictably while running all.bash.
// Issue 19093.
- if runtime.GOOS == "freebsd" {
- pollable = false
+ if runtime.GOOS == "freebsd" && kind == kindOpenFile {
+ kind = kindNewFile
}
+ pollable := kind == kindOpenFile || kind == kindPipe
if err := f.pfd.Init("file", pollable); err != nil {
// An error here indicates a failure to register
// with the netpoll system. That can happen for
@@ -129,7 +143,7 @@ type dirInfo struct {
// output or standard error. See the SIGPIPE docs in os/signal, and
// issue 11845.
func epipecheck(file *File, e error) {
- if e == syscall.EPIPE && (file.pfd.Sysfd == 1 || file.pfd.Sysfd == 2) {
+ if e == syscall.EPIPE && file.stdoutOrErr {
sigpipe()
}
}
@@ -180,7 +194,7 @@ func OpenFile(name string, flag int, perm FileMode) (*File, error) {
syscall.CloseOnExec(r)
}
- return newFile(uintptr(r), name, true), nil
+ return newFile(uintptr(r), name, kindOpenFile), nil
}
// Close closes the File, rendering it unusable for I/O.
diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go
index 9033c4f..e1c5b45 100644
--- a/libgo/go/os/os_test.go
+++ b/libgo/go/os/os_test.go
@@ -719,6 +719,27 @@ func TestHardLink(t *testing.T) {
if !SameFile(tostat, fromstat) {
t.Errorf("link %q, %q did not create hard link", to, from)
}
+ // We should not be able to perform the same Link() a second time
+ err = Link(to, from)
+ switch err := err.(type) {
+ case *LinkError:
+ if err.Op != "link" {
+ t.Errorf("Link(%q, %q) err.Op = %q; want %q", to, from, err.Op, "link")
+ }
+ if err.Old != to {
+ t.Errorf("Link(%q, %q) err.Old = %q; want %q", to, from, err.Old, to)
+ }
+ if err.New != from {
+ t.Errorf("Link(%q, %q) err.New = %q; want %q", to, from, err.New, from)
+ }
+ if !IsExist(err.Err) {
+ t.Errorf("Link(%q, %q) err.Err = %q; want %q", to, from, err.Err, "file exists error")
+ }
+ case nil:
+ t.Errorf("link %q, %q: expected error, got nil", from, to)
+ default:
+ t.Errorf("link %q, %q: expected %T, got %T %v", from, to, new(LinkError), err, err)
+ }
}
// chtmpdir changes the working directory to a new temporary directory and
@@ -1012,9 +1033,14 @@ func TestStartProcess(t *testing.T) {
dir = Getenv("SystemRoot")
args = []string{"/c", "cd"}
default:
- cmd = "/bin/pwd"
+ var err error
+ cmd, err = osexec.LookPath("pwd")
+ if err != nil {
+ t.Fatalf("Can't find pwd: %v", err)
+ }
dir = "/"
args = []string{}
+ t.Logf("Testing with %v", cmd)
}
cmddir, cmdbase := filepath.Split(cmd)
args = append([]string{cmdbase}, args...)
@@ -1162,9 +1188,14 @@ func testChtimes(t *testing.T, name string) {
// the contents are accessed; also, it is set
// whenever mtime is set.
case "netbsd":
- t.Logf("AccessTime didn't go backwards; was=%d, after=%d (Ignoring. See NetBSD issue golang.org/issue/19293)", at, pat)
+ mounts, _ := ioutil.ReadFile("/proc/mounts")
+ if strings.Contains(string(mounts), "noatime") {
+ t.Logf("AccessTime didn't go backwards, but see a filesystem mounted noatime; ignoring. Issue 19293.")
+ } else {
+ t.Logf("AccessTime didn't go backwards; was=%v, after=%v (Ignoring on NetBSD, assuming noatime, Issue 19293)", at, pat)
+ }
default:
- t.Errorf("AccessTime didn't go backwards; was=%d, after=%d", at, pat)
+ t.Errorf("AccessTime didn't go backwards; was=%v, after=%v", at, pat)
}
}
@@ -1211,9 +1242,9 @@ func TestChdirAndGetwd(t *testing.T) {
if mode == 0 {
err = Chdir(d)
} else {
- fd1, err := Open(d)
- if err != nil {
- t.Errorf("Open %s: %s", d, err)
+ fd1, err1 := Open(d)
+ if err1 != nil {
+ t.Errorf("Open %s: %s", d, err1)
continue
}
err = fd1.Chdir()
@@ -2204,22 +2235,24 @@ func TestPipeThreads(t *testing.T) {
defer debug.SetMaxThreads(debug.SetMaxThreads(threads / 2))
- var wg sync.WaitGroup
- wg.Add(threads)
- c := make(chan bool, threads)
+ creading := make(chan bool, threads)
+ cdone := make(chan bool, threads)
for i := 0; i < threads; i++ {
go func(i int) {
- defer wg.Done()
var b [1]byte
- c <- true
+ creading <- true
if _, err := r[i].Read(b[:]); err != nil {
t.Error(err)
}
+ if err := r[i].Close(); err != nil {
+ t.Error(err)
+ }
+ cdone <- true
}(i)
}
for i := 0; i < threads; i++ {
- <-c
+ <-creading
}
// If we are still alive, it means that the 100 goroutines did
@@ -2232,14 +2265,7 @@ func TestPipeThreads(t *testing.T) {
if err := w[i].Close(); err != nil {
t.Error(err)
}
- }
-
- wg.Wait()
-
- for i := 0; i < threads; i++ {
- if err := r[i].Close(); err != nil {
- t.Error(err)
- }
+ <-cdone
}
}
diff --git a/libgo/go/os/os_unix_test.go b/libgo/go/os/os_unix_test.go
index e239835..56c885c 100644
--- a/libgo/go/os/os_unix_test.go
+++ b/libgo/go/os/os_unix_test.go
@@ -36,11 +36,6 @@ func checkUidGid(t *testing.T, path string, uid, gid int) {
}
func TestChown(t *testing.T) {
- // Chown is not supported under windows or Plan 9.
- // Plan9 provides a native ChownPlan9 version instead.
- if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
- t.Skipf("%s does not support syscall.Chown", runtime.GOOS)
- }
// Use TempDir() to make sure we're on a local file system,
// so that the group ids returned by Getgroups will be allowed
// on the file. On NFS, the Getgroups groups are
@@ -84,10 +79,6 @@ func TestChown(t *testing.T) {
}
func TestFileChown(t *testing.T) {
- // Fchown is not supported under windows or Plan 9.
- if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
- t.Skipf("%s does not support syscall.Fchown", runtime.GOOS)
- }
// Use TempDir() to make sure we're on a local file system,
// so that the group ids returned by Getgroups will be allowed
// on the file. On NFS, the Getgroups groups are
@@ -131,10 +122,6 @@ func TestFileChown(t *testing.T) {
}
func TestLchown(t *testing.T) {
- // Lchown is not supported under windows or Plan 9.
- if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
- t.Skipf("%s does not support syscall.Lchown", runtime.GOOS)
- }
// Use TempDir() to make sure we're on a local file system,
// so that the group ids returned by Getgroups will be allowed
// on the file. On NFS, the Getgroups groups are
diff --git a/libgo/go/os/path.go b/libgo/go/os/path.go
index 146d7b6..17c49c8 100644
--- a/libgo/go/os/path.go
+++ b/libgo/go/os/path.go
@@ -6,6 +6,7 @@ package os
import (
"io"
+ "runtime"
"syscall"
)
@@ -97,6 +98,11 @@ func RemoveAll(path string) error {
// Remove contents & return first error.
err = nil
for {
+ if err == nil && (runtime.GOOS == "plan9" || runtime.GOOS == "nacl") {
+ // Reset read offset after removing directory entries.
+ // See golang.org/issue/22572.
+ fd.Seek(0, 0)
+ }
names, err1 := fd.Readdirnames(100)
for _, name := range names {
err1 := RemoveAll(path + string(PathSeparator) + name)
diff --git a/libgo/go/os/path_test.go b/libgo/go/os/path_test.go
index 6f5bfa5..f58c7e7 100644
--- a/libgo/go/os/path_test.go
+++ b/libgo/go/os/path_test.go
@@ -5,6 +5,7 @@
package os_test
import (
+ "fmt"
"internal/testenv"
"io/ioutil"
. "os"
@@ -169,6 +170,36 @@ func TestRemoveAll(t *testing.T) {
}
}
+// Test RemoveAll on a large directory.
+func TestRemoveAllLarge(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping in short mode")
+ }
+
+ tmpDir := TempDir()
+ // Work directory.
+ path := tmpDir + "/_TestRemoveAllLarge_"
+
+ // Make directory with 1000 files and remove.
+ if err := MkdirAll(path, 0777); err != nil {
+ t.Fatalf("MkdirAll %q: %s", path, err)
+ }
+ for i := 0; i < 1000; i++ {
+ fpath := fmt.Sprintf("%s/file%d", path, i)
+ fd, err := Create(fpath)
+ if err != nil {
+ t.Fatalf("create %q: %s", fpath, err)
+ }
+ fd.Close()
+ }
+ if err := RemoveAll(path); err != nil {
+ t.Fatalf("RemoveAll %q: %s", path, err)
+ }
+ if _, err := Lstat(path); err == nil {
+ t.Fatalf("Lstat %q succeeded after RemoveAll", path)
+ }
+}
+
func TestMkdirAllWithSymlink(t *testing.T) {
testenv.MustHaveSymlink(t)
diff --git a/libgo/go/os/pipe_bsd.go b/libgo/go/os/pipe_bsd.go
index ae153fa..8398a24 100644
--- a/libgo/go/os/pipe_bsd.go
+++ b/libgo/go/os/pipe_bsd.go
@@ -24,5 +24,5 @@ func Pipe() (r *File, w *File, err error) {
syscall.CloseOnExec(p[1])
syscall.ForkLock.RUnlock()
- return newFile(uintptr(p[0]), "|0", true), newFile(uintptr(p[1]), "|1", true), nil
+ return newFile(uintptr(p[0]), "|0", kindPipe), newFile(uintptr(p[1]), "|1", kindPipe), nil
}
diff --git a/libgo/go/os/pipe_freebsd.go b/libgo/go/os/pipe_freebsd.go
index ea6622c..93bd869 100644
--- a/libgo/go/os/pipe_freebsd.go
+++ b/libgo/go/os/pipe_freebsd.go
@@ -13,22 +13,8 @@ func Pipe() (r *File, w *File, err error) {
e := syscall.Pipe2(p[0:], syscall.O_CLOEXEC)
if e != nil {
- // Fallback support for FreeBSD 9, which lacks Pipe2.
- //
- // TODO: remove this for Go 1.10 when FreeBSD 9
- // is removed (Issue 19072).
-
- // See ../syscall/exec.go for description of lock.
- syscall.ForkLock.RLock()
- e := syscall.Pipe(p[0:])
- if e != nil {
- syscall.ForkLock.RUnlock()
- return nil, nil, NewSyscallError("pipe", e)
- }
- syscall.CloseOnExec(p[0])
- syscall.CloseOnExec(p[1])
- syscall.ForkLock.RUnlock()
+ return nil, nil, NewSyscallError("pipe", e)
}
- return newFile(uintptr(p[0]), "|0", true), newFile(uintptr(p[1]), "|1", true), nil
+ return newFile(uintptr(p[0]), "|0", kindPipe), newFile(uintptr(p[1]), "|1", kindPipe), nil
}
diff --git a/libgo/go/os/pipe_linux.go b/libgo/go/os/pipe_linux.go
index 96f2ce9..acd7b88 100644
--- a/libgo/go/os/pipe_linux.go
+++ b/libgo/go/os/pipe_linux.go
@@ -29,5 +29,5 @@ func Pipe() (r *File, w *File, err error) {
return nil, nil, NewSyscallError("pipe2", e)
}
- return newFile(uintptr(p[0]), "|0", true), newFile(uintptr(p[1]), "|1", true), nil
+ return newFile(uintptr(p[0]), "|0", kindPipe), newFile(uintptr(p[1]), "|1", kindPipe), nil
}
diff --git a/libgo/go/os/signal/internal/pty/pty.go b/libgo/go/os/signal/internal/pty/pty.go
new file mode 100644
index 0000000..4728de7
--- /dev/null
+++ b/libgo/go/os/signal/internal/pty/pty.go
@@ -0,0 +1,62 @@
+// Copyright 2017 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 dragonfly freebsd linux,!android netbsd openbsd
+
+// Package pty is a simple pseudo-terminal package for Unix systems,
+// implemented by calling C functions via cgo.
+// This is only used for testing the os/signal package.
+package pty
+
+import (
+ "fmt"
+ "os"
+ "syscall"
+ "unsafe"
+)
+
+//extern posix_openpt
+func posix_openpt(int32) int32
+
+//extern grantpt
+func grantpt(int32) int32
+
+//extern unlockpt
+func unlockpt(int32) int32
+
+//extern ptsname
+func ptsname(int32) *byte
+
+//extern close
+func close(int32) int32
+
+const _O_RDWR = 2
+
+// 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)
+ if m < 0 {
+ return nil, "", fmt.Errorf("posix_openpt: %v", syscall.GetErrno())
+ }
+ if grantpt(m) < 0 {
+ errno := syscall.GetErrno()
+ close(m)
+ return nil, "", fmt.Errorf("grantpt: %v", errno)
+ }
+ if unlockpt(m) < 0 {
+ errno := syscall.GetErrno()
+ close(m)
+ return nil, "", fmt.Errorf("unlockpt: %v", errno)
+ }
+ p := ptsname(m)
+ s := (*[32000]byte)(unsafe.Pointer(p))[:]
+ for i, v := range s {
+ if v == 0 {
+ s = s[:i:i]
+ break
+ }
+ }
+ slave = string(s)
+ return os.NewFile(uintptr(m), "pty-master"), slave, nil
+}
diff --git a/libgo/go/os/signal/signal_cgo_test.go b/libgo/go/os/signal/signal_cgo_test.go
new file mode 100644
index 0000000..27707fa
--- /dev/null
+++ b/libgo/go/os/signal/signal_cgo_test.go
@@ -0,0 +1,228 @@
+// Copyright 2017 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 dragonfly freebsd linux,!android netbsd openbsd
+// +build cgo
+
+// Note that this test does not work on Solaris: issue #22849.
+// Don't run the test on Android because at least some versions of the
+// C library do not define the posix_openpt function.
+
+package signal_test
+
+import (
+ "bufio"
+ "bytes"
+ "context"
+ "fmt"
+ "io"
+ "os"
+ "os/exec"
+ "os/signal/internal/pty"
+ "strconv"
+ "strings"
+ "syscall"
+ "testing"
+ "time"
+)
+
+func TestTerminalSignal(t *testing.T) {
+ const enteringRead = "test program entering read"
+ if os.Getenv("GO_TEST_TERMINAL_SIGNALS") != "" {
+ var b [1]byte
+ fmt.Println(enteringRead)
+ n, err := os.Stdin.Read(b[:])
+ if n == 1 {
+ if b[0] == '\n' {
+ // This is what we expect
+ fmt.Println("read newline")
+ } else {
+ fmt.Printf("read 1 byte: %q\n", b)
+ }
+ } else {
+ fmt.Printf("read %d bytes\n", n)
+ }
+ if err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+ os.Exit(0)
+ }
+
+ t.Parallel()
+
+ // The test requires a shell that uses job control.
+ bash, err := exec.LookPath("bash")
+ if err != nil {
+ t.Skipf("could not find bash: %v", err)
+ }
+
+ scale := 1
+ if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
+ if sc, err := strconv.Atoi(s); err == nil {
+ scale = sc
+ }
+ }
+ pause := time.Duration(scale) * 10 * time.Millisecond
+ wait := time.Duration(scale) * 5 * time.Second
+
+ // The test only fails when using a "slow device," in this
+ // case a pseudo-terminal.
+
+ master, sname, err := pty.Open()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer master.Close()
+ slave, err := os.OpenFile(sname, os.O_RDWR, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer slave.Close()
+
+ // Start an interactive shell.
+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ defer cancel()
+ cmd := exec.CommandContext(ctx, bash, "--norc", "--noprofile", "-i")
+ cmd.Stdin = slave
+ cmd.Stdout = slave
+ cmd.Stderr = slave
+ cmd.SysProcAttr = &syscall.SysProcAttr{
+ Setsid: true,
+ Setctty: true,
+ Ctty: int(slave.Fd()),
+ }
+
+ if err := cmd.Start(); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := slave.Close(); err != nil {
+ t.Errorf("closing slave: %v", err)
+ }
+
+ progReady := make(chan bool)
+ sawPrompt := make(chan bool, 10)
+ const prompt = "prompt> "
+
+ // Read data from master in the background.
+ go func() {
+ input := bufio.NewReader(master)
+ var line, handled []byte
+ for {
+ b, err := input.ReadByte()
+ if err != nil {
+ if len(line) > 0 || len(handled) > 0 {
+ t.Logf("%q", append(handled, line...))
+ }
+ if perr, ok := err.(*os.PathError); ok {
+ err = perr.Err
+ }
+ // EOF means master is closed.
+ // EIO means child process is done.
+ // "file already closed" means deferred close of master has happened.
+ if err != io.EOF && err != syscall.EIO && !strings.Contains(err.Error(), "file already closed") {
+ t.Logf("error reading from master: %v", err)
+ }
+ return
+ }
+
+ line = append(line, b)
+
+ if b == '\n' {
+ t.Logf("%q", append(handled, line...))
+ line = nil
+ handled = nil
+ continue
+ }
+
+ if bytes.Contains(line, []byte(enteringRead)) {
+ close(progReady)
+ handled = append(handled, line...)
+ line = nil
+ } else if bytes.Contains(line, []byte(prompt)) && !bytes.Contains(line, []byte("PS1=")) {
+ sawPrompt <- true
+ handled = append(handled, line...)
+ line = nil
+ }
+ }
+ }()
+
+ // Set the bash prompt so that we can see it.
+ if _, err := master.Write([]byte("PS1='" + prompt + "'\n")); err != nil {
+ t.Fatalf("setting prompt: %v", err)
+ }
+ select {
+ case <-sawPrompt:
+ case <-time.After(wait):
+ t.Fatal("timed out waiting for shell prompt")
+ }
+
+ // Start a small program that reads from stdin
+ // (namely the code at the top of this function).
+ if _, err := master.Write([]byte("GO_TEST_TERMINAL_SIGNALS=1 " + os.Args[0] + " -test.run=TestTerminalSignal\n")); err != nil {
+ t.Fatal(err)
+ }
+
+ // Wait for the program to print that it is starting.
+ select {
+ case <-progReady:
+ case <-time.After(wait):
+ t.Fatal("timed out waiting for program to start")
+ }
+
+ // Give the program time to enter the read call.
+ // It doesn't matter much if we occasionally don't wait long enough;
+ // we won't be testing what we want to test, but the overall test
+ // will pass.
+ time.Sleep(pause)
+
+ // Send a ^Z to stop the program.
+ if _, err := master.Write([]byte{26}); err != nil {
+ t.Fatalf("writing ^Z to pty: %v", err)
+ }
+
+ // Wait for the program to stop and return to the shell.
+ select {
+ case <-sawPrompt:
+ case <-time.After(wait):
+ t.Fatal("timed out waiting for shell prompt")
+ }
+
+ // Restart the stopped program.
+ if _, err := master.Write([]byte("fg\n")); err != nil {
+ t.Fatalf("writing %q to pty: %v", "fg", err)
+ }
+
+ // Give the process time to restart.
+ // This is potentially racy: if the process does not restart
+ // quickly enough then the byte we send will go to bash rather
+ // than the program. Unfortunately there isn't anything we can
+ // look for to know that the program is running again.
+ // bash will print the program name, but that happens before it
+ // restarts the program.
+ time.Sleep(10 * pause)
+
+ // Write some data for the program to read,
+ // which should cause it to exit.
+ if _, err := master.Write([]byte{'\n'}); err != nil {
+ t.Fatalf("writing %q to pty: %v", "\n", err)
+ }
+
+ // Wait for the program to exit.
+ select {
+ case <-sawPrompt:
+ case <-time.After(wait):
+ t.Fatal("timed out waiting for shell prompt")
+ }
+
+ // Exit the shell with the program's exit status.
+ if _, err := master.Write([]byte("exit $?\n")); err != nil {
+ t.Fatalf("writing %q to pty: %v", "exit", err)
+ }
+
+ if err = cmd.Wait(); err != nil {
+ t.Errorf("subprogram failed: %v", err)
+ }
+}
diff --git a/libgo/go/os/signal/signal_test.go b/libgo/go/os/signal/signal_test.go
index 10a4146..d27ff0b 100644
--- a/libgo/go/os/signal/signal_test.go
+++ b/libgo/go/os/signal/signal_test.go
@@ -321,7 +321,9 @@ func TestAtomicStop(t *testing.T) {
cmd.Env = append(os.Environ(), "GO_TEST_ATOMIC_STOP=1")
out, err := cmd.CombinedOutput()
if err == nil {
- t.Logf("iteration %d: output %s", i, out)
+ if len(out) > 0 {
+ t.Logf("iteration %d: output %s", i, out)
+ }
} else {
t.Logf("iteration %d: exit status %q: output: %s", i, err, out)
}
@@ -378,7 +380,7 @@ func atomicStopTestProgram() {
case <-cs:
case <-time.After(1 * time.Second):
if !printed {
- fmt.Print("lost signal on iterations:")
+ fmt.Print("lost signal on tries:")
printed = true
}
fmt.Printf(" %d", i)
diff --git a/libgo/go/os/sys_freebsd.go b/libgo/go/os/sys_freebsd.go
index 273c2df..3ec49fa 100644
--- a/libgo/go/os/sys_freebsd.go
+++ b/libgo/go/os/sys_freebsd.go
@@ -4,20 +4,7 @@
package os
-import "syscall"
-
// supportsCloseOnExec reports whether the platform supports the
// O_CLOEXEC flag.
-var supportsCloseOnExec bool
-
-func init() {
- osrel, err := syscall.SysctlUint32("kern.osreldate")
- if err != nil {
- return
- }
- // The O_CLOEXEC flag was introduced in FreeBSD 8.3.
- // See http://www.freebsd.org/doc/en/books/porters-handbook/freebsd-versions.html.
- if osrel >= 803000 {
- supportsCloseOnExec = true
- }
-}
+// The O_CLOEXEC flag was introduced in FreeBSD 8.3.
+const supportsCloseOnExec bool = true
diff --git a/libgo/go/os/timeout_test.go b/libgo/go/os/timeout_test.go
new file mode 100644
index 0000000..6f47ed0
--- /dev/null
+++ b/libgo/go/os/timeout_test.go
@@ -0,0 +1,589 @@
+// Copyright 2017 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 !nacl
+// +build !plan9
+// +build !windows
+
+package os_test
+
+import (
+ "fmt"
+ "internal/poll"
+ "io"
+ "io/ioutil"
+ "math/rand"
+ "os"
+ "runtime"
+ "sync"
+ "testing"
+ "time"
+)
+
+func TestNonpollableDeadline(t *testing.T) {
+ // On BSD systems regular files seem to be pollable,
+ // so just run this test on Linux.
+ if runtime.GOOS != "linux" {
+ t.Skipf("skipping on %s", runtime.GOOS)
+ }
+
+ f, err := ioutil.TempFile("", "ostest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.Remove(f.Name())
+ defer f.Close()
+ deadline := time.Now().Add(10 * time.Second)
+ if err := f.SetDeadline(deadline); err != os.ErrNoDeadline {
+ t.Errorf("SetDeadline on file returned %v, wanted %v", err, os.ErrNoDeadline)
+ }
+ if err := f.SetReadDeadline(deadline); err != os.ErrNoDeadline {
+ t.Errorf("SetReadDeadline on file returned %v, wanted %v", err, os.ErrNoDeadline)
+ }
+ if err := f.SetWriteDeadline(deadline); err != os.ErrNoDeadline {
+ t.Errorf("SetWriteDeadline on file returned %v, wanted %v", err, os.ErrNoDeadline)
+ }
+}
+
+// noDeadline is a zero time.Time value, which cancels a deadline.
+var noDeadline time.Time
+
+var readTimeoutTests = []struct {
+ timeout time.Duration
+ xerrs [2]error // expected errors in transition
+}{
+ // Tests that read deadlines work, even if there's data ready
+ // to be read.
+ {-5 * time.Second, [2]error{poll.ErrTimeout, poll.ErrTimeout}},
+
+ {50 * time.Millisecond, [2]error{nil, poll.ErrTimeout}},
+}
+
+func TestReadTimeout(t *testing.T) {
+ t.Parallel()
+
+ r, w, err := os.Pipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r.Close()
+ defer w.Close()
+
+ if _, err := w.Write([]byte("READ TIMEOUT TEST")); err != nil {
+ t.Fatal(err)
+ }
+
+ for i, tt := range readTimeoutTests {
+ if err := r.SetReadDeadline(time.Now().Add(tt.timeout)); err != nil {
+ t.Fatalf("#%d: %v", i, err)
+ }
+ var b [1]byte
+ for j, xerr := range tt.xerrs {
+ for {
+ n, err := r.Read(b[:])
+ if xerr != nil {
+ if !os.IsTimeout(err) {
+ t.Fatalf("#%d/%d: %v", i, j, err)
+ }
+ }
+ if err == nil {
+ time.Sleep(tt.timeout / 3)
+ continue
+ }
+ if n != 0 {
+ t.Fatalf("#%d/%d: read %d; want 0", i, j, n)
+ }
+ break
+ }
+ }
+ }
+}
+
+func TestReadTimeoutMustNotReturn(t *testing.T) {
+ t.Parallel()
+
+ r, w, err := os.Pipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r.Close()
+ defer w.Close()
+
+ max := time.NewTimer(100 * time.Millisecond)
+ defer max.Stop()
+ ch := make(chan error)
+ go func() {
+ if err := r.SetDeadline(time.Now().Add(-5 * time.Second)); err != nil {
+ t.Error(err)
+ }
+ if err := r.SetWriteDeadline(time.Now().Add(-5 * time.Second)); err != nil {
+ t.Error(err)
+ }
+ if err := r.SetReadDeadline(noDeadline); err != nil {
+ t.Error(err)
+ }
+ var b [1]byte
+ _, err := r.Read(b[:])
+ ch <- err
+ }()
+
+ select {
+ case err := <-ch:
+ t.Fatalf("expected Read to not return, but it returned with %v", err)
+ case <-max.C:
+ w.Close()
+ err := <-ch // wait for tester goroutine to stop
+ if os.IsTimeout(err) {
+ t.Fatal(err)
+ }
+ }
+}
+
+var writeTimeoutTests = []struct {
+ timeout time.Duration
+ xerrs [2]error // expected errors in transition
+}{
+ // Tests that write deadlines work, even if there's buffer
+ // space available to write.
+ {-5 * time.Second, [2]error{poll.ErrTimeout, poll.ErrTimeout}},
+
+ {10 * time.Millisecond, [2]error{nil, poll.ErrTimeout}},
+}
+
+func TestWriteTimeout(t *testing.T) {
+ t.Parallel()
+
+ for i, tt := range writeTimeoutTests {
+ t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
+ r, w, err := os.Pipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r.Close()
+ defer w.Close()
+
+ if err := w.SetWriteDeadline(time.Now().Add(tt.timeout)); err != nil {
+ t.Fatalf("%v", err)
+ }
+ for j, xerr := range tt.xerrs {
+ for {
+ n, err := w.Write([]byte("WRITE TIMEOUT TEST"))
+ if xerr != nil {
+ if !os.IsTimeout(err) {
+ t.Fatalf("%d: %v", j, err)
+ }
+ }
+ if err == nil {
+ time.Sleep(tt.timeout / 3)
+ continue
+ }
+ if n != 0 {
+ t.Fatalf("%d: wrote %d; want 0", j, n)
+ }
+ break
+ }
+ }
+ })
+ }
+}
+
+func TestWriteTimeoutMustNotReturn(t *testing.T) {
+ t.Parallel()
+
+ r, w, err := os.Pipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r.Close()
+ defer w.Close()
+
+ max := time.NewTimer(100 * time.Millisecond)
+ defer max.Stop()
+ ch := make(chan error)
+ go func() {
+ if err := w.SetDeadline(time.Now().Add(-5 * time.Second)); err != nil {
+ t.Error(err)
+ }
+ if err := w.SetReadDeadline(time.Now().Add(-5 * time.Second)); err != nil {
+ t.Error(err)
+ }
+ if err := w.SetWriteDeadline(noDeadline); err != nil {
+ t.Error(err)
+ }
+ var b [1]byte
+ for {
+ if _, err := w.Write(b[:]); err != nil {
+ ch <- err
+ break
+ }
+ }
+ }()
+
+ select {
+ case err := <-ch:
+ t.Fatalf("expected Write to not return, but it returned with %v", err)
+ case <-max.C:
+ r.Close()
+ err := <-ch // wait for tester goroutine to stop
+ if os.IsTimeout(err) {
+ t.Fatal(err)
+ }
+ }
+}
+
+func timeoutReader(r *os.File, d, min, max time.Duration, ch chan<- error) {
+ var err error
+ defer func() { ch <- err }()
+
+ t0 := time.Now()
+ if err = r.SetReadDeadline(time.Now().Add(d)); err != nil {
+ return
+ }
+ b := make([]byte, 256)
+ var n int
+ n, err = r.Read(b)
+ t1 := time.Now()
+ if n != 0 || err == nil || !os.IsTimeout(err) {
+ err = fmt.Errorf("Read did not return (0, timeout): (%d, %v)", n, err)
+ return
+ }
+ if dt := t1.Sub(t0); min > dt || dt > max && !testing.Short() {
+ err = fmt.Errorf("Read took %s; expected %s", dt, d)
+ return
+ }
+}
+
+func TestReadTimeoutFluctuation(t *testing.T) {
+ t.Parallel()
+
+ r, w, err := os.Pipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r.Close()
+ defer w.Close()
+
+ max := time.NewTimer(time.Second)
+ defer max.Stop()
+ ch := make(chan error)
+ go timeoutReader(r, 100*time.Millisecond, 50*time.Millisecond, 250*time.Millisecond, ch)
+
+ select {
+ case <-max.C:
+ t.Fatal("Read took over 1s; expected 0.1s")
+ case err := <-ch:
+ if !os.IsTimeout(err) {
+ t.Fatal(err)
+ }
+ }
+}
+
+func timeoutWriter(w *os.File, d, min, max time.Duration, ch chan<- error) {
+ var err error
+ defer func() { ch <- err }()
+
+ t0 := time.Now()
+ if err = w.SetWriteDeadline(time.Now().Add(d)); err != nil {
+ return
+ }
+ var n int
+ for {
+ n, err = w.Write([]byte("TIMEOUT WRITER"))
+ if err != nil {
+ break
+ }
+ }
+ t1 := time.Now()
+ if err == nil || !os.IsTimeout(err) {
+ err = fmt.Errorf("Write did not return (any, timeout): (%d, %v)", n, err)
+ return
+ }
+ if dt := t1.Sub(t0); min > dt || dt > max && !testing.Short() {
+ err = fmt.Errorf("Write took %s; expected %s", dt, d)
+ return
+ }
+}
+
+func TestWriteTimeoutFluctuation(t *testing.T) {
+ t.Parallel()
+
+ r, w, err := os.Pipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r.Close()
+ defer w.Close()
+
+ d := time.Second
+ max := time.NewTimer(d)
+ defer max.Stop()
+ ch := make(chan error)
+ go timeoutWriter(w, 100*time.Millisecond, 50*time.Millisecond, 250*time.Millisecond, ch)
+
+ select {
+ case <-max.C:
+ t.Fatalf("Write took over %v; expected 0.1s", d)
+ case err := <-ch:
+ if !os.IsTimeout(err) {
+ t.Fatal(err)
+ }
+ }
+}
+
+func TestVariousDeadlines(t *testing.T) {
+ t.Parallel()
+ testVariousDeadlines(t)
+}
+
+func TestVariousDeadlines1Proc(t *testing.T) {
+ // Cannot use t.Parallel - modifies global GOMAXPROCS.
+ if testing.Short() {
+ t.Skip("skipping in short mode")
+ }
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
+ testVariousDeadlines(t)
+}
+
+func TestVariousDeadlines4Proc(t *testing.T) {
+ // Cannot use t.Parallel - modifies global GOMAXPROCS.
+ if testing.Short() {
+ t.Skip("skipping in short mode")
+ }
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
+ testVariousDeadlines(t)
+}
+
+type neverEnding byte
+
+func (b neverEnding) Read(p []byte) (int, error) {
+ for i := range p {
+ p[i] = byte(b)
+ }
+ return len(p), nil
+}
+
+func testVariousDeadlines(t *testing.T) {
+ type result struct {
+ n int64
+ err error
+ d time.Duration
+ }
+
+ handler := func(w *os.File, pasvch chan result) {
+ // The writer, with no timeouts of its own,
+ // sending bytes to clients as fast as it can.
+ t0 := time.Now()
+ n, err := io.Copy(w, neverEnding('a'))
+ dt := time.Since(t0)
+ pasvch <- result{n, err, dt}
+ }
+
+ for _, timeout := range []time.Duration{
+ 1 * time.Nanosecond,
+ 2 * time.Nanosecond,
+ 5 * time.Nanosecond,
+ 50 * time.Nanosecond,
+ 100 * time.Nanosecond,
+ 200 * time.Nanosecond,
+ 500 * time.Nanosecond,
+ 750 * time.Nanosecond,
+ 1 * time.Microsecond,
+ 5 * time.Microsecond,
+ 25 * time.Microsecond,
+ 250 * time.Microsecond,
+ 500 * time.Microsecond,
+ 1 * time.Millisecond,
+ 5 * time.Millisecond,
+ 100 * time.Millisecond,
+ 250 * time.Millisecond,
+ 500 * time.Millisecond,
+ 1 * time.Second,
+ } {
+ numRuns := 3
+ if testing.Short() {
+ numRuns = 1
+ if timeout > 500*time.Microsecond {
+ continue
+ }
+ }
+ for run := 0; run < numRuns; run++ {
+ t.Run(fmt.Sprintf("%v-%d", timeout, run+1), func(t *testing.T) {
+ r, w, err := os.Pipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r.Close()
+ defer w.Close()
+
+ pasvch := make(chan result)
+ go handler(w, pasvch)
+
+ tooLong := 5 * time.Second
+ max := time.NewTimer(tooLong)
+ defer max.Stop()
+ actvch := make(chan result)
+ go func() {
+ t0 := time.Now()
+ if err := r.SetDeadline(t0.Add(timeout)); err != nil {
+ t.Error(err)
+ }
+ n, err := io.Copy(ioutil.Discard, r)
+ dt := time.Since(t0)
+ r.Close()
+ actvch <- result{n, err, dt}
+ }()
+
+ select {
+ case res := <-actvch:
+ if os.IsTimeout(res.err) {
+ t.Logf("good client timeout after %v, reading %d bytes", res.d, res.n)
+ } else {
+ t.Fatalf("client Copy = %d, %v; want timeout", res.n, res.err)
+ }
+ case <-max.C:
+ t.Fatalf("timeout (%v) waiting for client to timeout (%v) reading", tooLong, timeout)
+ }
+
+ select {
+ case res := <-pasvch:
+ t.Logf("writer in %v wrote %d: %v", res.d, res.n, res.err)
+ case <-max.C:
+ t.Fatalf("timeout waiting for writer to finish writing")
+ }
+ })
+ }
+ }
+}
+
+func TestReadWriteDeadlineRace(t *testing.T) {
+ t.Parallel()
+
+ N := 1000
+ if testing.Short() {
+ N = 50
+ }
+
+ r, w, err := os.Pipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r.Close()
+ defer w.Close()
+
+ var wg sync.WaitGroup
+ wg.Add(3)
+ go func() {
+ defer wg.Done()
+ tic := time.NewTicker(2 * time.Microsecond)
+ defer tic.Stop()
+ for i := 0; i < N; i++ {
+ if err := r.SetReadDeadline(time.Now().Add(2 * time.Microsecond)); err != nil {
+ break
+ }
+ if err := w.SetWriteDeadline(time.Now().Add(2 * time.Microsecond)); err != nil {
+ break
+ }
+ <-tic.C
+ }
+ }()
+ go func() {
+ defer wg.Done()
+ var b [1]byte
+ for i := 0; i < N; i++ {
+ _, err := r.Read(b[:])
+ if err != nil && !os.IsTimeout(err) {
+ t.Error("Read returned non-timeout error", err)
+ }
+ }
+ }()
+ go func() {
+ defer wg.Done()
+ var b [1]byte
+ for i := 0; i < N; i++ {
+ _, err := w.Write(b[:])
+ if err != nil && !os.IsTimeout(err) {
+ t.Error("Write returned non-timeout error", err)
+ }
+ }
+ }()
+ wg.Wait() // wait for tester goroutine to stop
+}
+
+// TestRacyRead tests that it is safe to mutate the input Read buffer
+// immediately after cancelation has occurred.
+func TestRacyRead(t *testing.T) {
+ t.Parallel()
+
+ r, w, err := os.Pipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r.Close()
+ defer w.Close()
+
+ var wg sync.WaitGroup
+ defer wg.Wait()
+
+ go io.Copy(w, rand.New(rand.NewSource(0)))
+
+ r.SetReadDeadline(time.Now().Add(time.Millisecond))
+ for i := 0; i < 10; i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+
+ b1 := make([]byte, 1024)
+ b2 := make([]byte, 1024)
+ for j := 0; j < 100; j++ {
+ _, err := r.Read(b1)
+ copy(b1, b2) // Mutate b1 to trigger potential race
+ if err != nil {
+ if !os.IsTimeout(err) {
+ t.Error(err)
+ }
+ r.SetReadDeadline(time.Now().Add(time.Millisecond))
+ }
+ }
+ }()
+ }
+}
+
+// TestRacyWrite tests that it is safe to mutate the input Write buffer
+// immediately after cancelation has occurred.
+func TestRacyWrite(t *testing.T) {
+ t.Parallel()
+
+ r, w, err := os.Pipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r.Close()
+ defer w.Close()
+
+ var wg sync.WaitGroup
+ defer wg.Wait()
+
+ go io.Copy(ioutil.Discard, r)
+
+ w.SetWriteDeadline(time.Now().Add(time.Millisecond))
+ for i := 0; i < 10; i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+
+ b1 := make([]byte, 1024)
+ b2 := make([]byte, 1024)
+ for j := 0; j < 100; j++ {
+ _, err := w.Write(b1)
+ copy(b1, b2) // Mutate b1 to trigger potential race
+ if err != nil {
+ if !os.IsTimeout(err) {
+ t.Error(err)
+ }
+ w.SetWriteDeadline(time.Now().Add(time.Millisecond))
+ }
+ }
+ }()
+ }
+}
diff --git a/libgo/go/os/user/cgo_lookup_unix.go b/libgo/go/os/user/cgo_lookup_unix.go
index 9670ada..6c815b4 100644
--- a/libgo/go/os/user/cgo_lookup_unix.go
+++ b/libgo/go/os/user/cgo_lookup_unix.go
@@ -18,6 +18,9 @@ import (
// bytePtrToString takes a NUL-terminated array of bytes and convert
// it to a Go string.
func bytePtrToString(p *byte) string {
+ if p == nil {
+ return ""
+ }
a := (*[10000]byte)(unsafe.Pointer(p))
i := 0
for a[i] != 0 {
@@ -99,8 +102,8 @@ func lookupUnixUid(uid int) (*User, error) {
func buildUser(pwd *syscall.Passwd) *User {
u := &User{
- Uid: strconv.Itoa(int(pwd.Pw_uid)),
- Gid: strconv.Itoa(int(pwd.Pw_gid)),
+ Uid: strconv.FormatUint(uint64(pwd.Pw_uid), 10),
+ Gid: strconv.FormatUint(uint64(pwd.Pw_gid), 10),
Username: bytePtrToString((*byte)(unsafe.Pointer(pwd.Pw_name))),
Name: bytePtrToString((*byte)(unsafe.Pointer(pwd.Pw_gecos))),
HomeDir: bytePtrToString((*byte)(unsafe.Pointer(pwd.Pw_dir))),
@@ -264,3 +267,11 @@ const maxBufferSize = 1 << 20
func isSizeReasonable(sz int64) bool {
return sz > 0 && sz <= maxBufferSize
}
+
+// Because we can't use cgo in tests:
+func structPasswdForNegativeTest() syscall.Passwd {
+ sp := syscall.Passwd{}
+ sp.Pw_uid = 1<<32 - 2
+ sp.Pw_gid = 1<<32 - 3
+ return sp
+}
diff --git a/libgo/go/os/user/cgo_unix_test.go b/libgo/go/os/user/cgo_unix_test.go
new file mode 100644
index 0000000..6741118
--- /dev/null
+++ b/libgo/go/os/user/cgo_unix_test.go
@@ -0,0 +1,24 @@
+// Copyright 2017 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 dragonfly freebsd !android,linux netbsd openbsd solaris
+// +build cgo
+
+package user
+
+import (
+ "testing"
+)
+
+// Issue 22739
+func TestNegativeUid(t *testing.T) {
+ sp := structPasswdForNegativeTest()
+ u := buildUser(&sp)
+ if g, w := u.Uid, "4294967294"; g != w {
+ t.Errorf("Uid = %q; want %q", g, w)
+ }
+ if g, w := u.Gid, "4294967293"; g != w {
+ t.Errorf("Gid = %q; want %q", g, w)
+ }
+}
diff --git a/libgo/go/os/user/listgroups_unix.go b/libgo/go/os/user/listgroups_unix.go
index 8b2b13d..b142e2b 100644
--- a/libgo/go/os/user/listgroups_unix.go
+++ b/libgo/go/os/user/listgroups_unix.go
@@ -15,9 +15,10 @@ import (
/*
#include <unistd.h>
#include <sys/types.h>
-#include <stdlib.h>
*/
+const maxGroups = 2048
+
func listGroups(u *User) ([]string, error) {
ug, err := strconv.Atoi(u.Gid)
if err != nil {
diff --git a/libgo/go/os/wait_wait6.go b/libgo/go/os/wait_wait6.go
index b309811..891f242 100644
--- a/libgo/go/os/wait_wait6.go
+++ b/libgo/go/os/wait_wait6.go
@@ -30,11 +30,6 @@ func (p *Process) blockUntilWaitable() (bool, error) {
}
runtime.KeepAlive(p)
if errno != 0 {
- // The wait6 system call is supported only on FreeBSD
- // 9.3 and above, so it may return an ENOSYS error.
- if errno == syscall.ENOSYS {
- return false, nil
- }
return false, NewSyscallError("wait6", errno)
}
return true, nil