aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/os
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2016-07-22 18:15:38 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2016-07-22 18:15:38 +0000
commit22b955cca564a9a3a5b8c9d9dd1e295b7943c128 (patch)
treeabdbd898676e1f853fca2d7e031d105d7ebcf676 /libgo/go/os
parent9d04a3af4c6491536badf6bde9707c907e4d196b (diff)
downloadgcc-22b955cca564a9a3a5b8c9d9dd1e295b7943c128.zip
gcc-22b955cca564a9a3a5b8c9d9dd1e295b7943c128.tar.gz
gcc-22b955cca564a9a3a5b8c9d9dd1e295b7943c128.tar.bz2
libgo: update to go1.7rc3
Reviewed-on: https://go-review.googlesource.com/25150 From-SVN: r238662
Diffstat (limited to 'libgo/go/os')
-rw-r--r--libgo/go/os/doc.go6
-rw-r--r--libgo/go/os/env.go6
-rw-r--r--libgo/go/os/error_test.go12
-rw-r--r--libgo/go/os/error_unix.go2
-rw-r--r--libgo/go/os/error_unix_test.go39
-rw-r--r--libgo/go/os/error_windows_test.go35
-rw-r--r--libgo/go/os/exec.go12
-rw-r--r--libgo/go/os/exec/exec.go45
-rw-r--r--libgo/go/os/exec/exec_test.go131
-rw-r--r--libgo/go/os/exec/lp_plan9.go10
-rw-r--r--libgo/go/os/exec/lp_unix.go12
-rw-r--r--libgo/go/os/exec/lp_unix_test.go2
-rw-r--r--libgo/go/os/exec/lp_windows.go86
-rw-r--r--libgo/go/os/exec_posix.go2
-rw-r--r--libgo/go/os/exec_unix.go18
-rw-r--r--libgo/go/os/exec_windows.go2
-rw-r--r--libgo/go/os/export_test.go2
-rw-r--r--libgo/go/os/file.go8
-rw-r--r--libgo/go/os/file_plan9.go12
-rw-r--r--libgo/go/os/file_unix.go10
-rw-r--r--libgo/go/os/getwd.go4
-rw-r--r--libgo/go/os/os_test.go106
-rw-r--r--libgo/go/os/os_unix_test.go15
-rw-r--r--libgo/go/os/path.go2
-rw-r--r--libgo/go/os/path_test.go2
-rw-r--r--libgo/go/os/pipe_test.go2
-rw-r--r--libgo/go/os/signal/doc.go6
-rw-r--r--libgo/go/os/signal/signal.go2
-rw-r--r--libgo/go/os/signal/signal_test.go13
-rw-r--r--libgo/go/os/stat_dragonfly.go4
-rw-r--r--libgo/go/os/stat_nacl.go2
-rw-r--r--libgo/go/os/stat_plan9.go4
-rw-r--r--libgo/go/os/str.go2
-rw-r--r--libgo/go/os/types.go2
-rw-r--r--libgo/go/os/types_windows.go6
-rw-r--r--libgo/go/os/user/decls_solaris.go6
-rw-r--r--libgo/go/os/user/decls_unix.go9
-rw-r--r--libgo/go/os/user/listgroups_solaris.go17
-rw-r--r--libgo/go/os/user/listgroups_unix.go57
-rw-r--r--libgo/go/os/user/lookup.go21
-rw-r--r--libgo/go/os/user/lookup_android.go38
-rw-r--r--libgo/go/os/user/lookup_plan9.go20
-rw-r--r--libgo/go/os/user/lookup_stubs.go70
-rw-r--r--libgo/go/os/user/lookup_unix.go253
-rw-r--r--libgo/go/os/user/lookup_windows.go23
-rw-r--r--libgo/go/os/user/user.go36
-rw-r--r--libgo/go/os/user/user_test.go76
-rw-r--r--libgo/go/os/wait_unimp.go16
-rw-r--r--libgo/go/os/wait_wait6.go40
-rw-r--r--libgo/go/os/wait_waitid.go34
50 files changed, 1110 insertions, 230 deletions
diff --git a/libgo/go/os/doc.go b/libgo/go/os/doc.go
index 869a28a..0313eac 100644
--- a/libgo/go/os/doc.go
+++ b/libgo/go/os/doc.go
@@ -77,14 +77,14 @@ func (p *ProcessState) Success() bool {
}
// Sys returns system-dependent exit information about
-// the process. Convert it to the appropriate underlying
+// the process. Convert it to the appropriate underlying
// type, such as syscall.WaitStatus on Unix, to access its contents.
func (p *ProcessState) Sys() interface{} {
return p.sys()
}
// SysUsage returns system-dependent resource usage information about
-// the exited process. Convert it to the appropriate underlying
+// the exited process. Convert it to the appropriate underlying
// type, such as *syscall.Rusage on Unix, to access its contents.
// (On Unix, *syscall.Rusage matches struct rusage as defined in the
// getrusage(2) manual page.)
@@ -112,7 +112,7 @@ func Hostname() (name string, err error) {
// nil error. If it encounters an error before the end of the
// directory, Readdir returns the FileInfo read until that point
// and a non-nil error.
-func (f *File) Readdir(n int) (fi []FileInfo, err error) {
+func (f *File) Readdir(n int) ([]FileInfo, error) {
if f == nil {
return nil, ErrInvalid
}
diff --git a/libgo/go/os/env.go b/libgo/go/os/env.go
index a4ede15..4a14714 100644
--- a/libgo/go/os/env.go
+++ b/libgo/go/os/env.go
@@ -1,4 +1,4 @@
-// Copyright 2010 The Go Authors. All rights reserved.
+// Copyright 2010 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.
@@ -27,7 +27,7 @@ func Expand(s string, mapping func(string) string) string {
}
// ExpandEnv replaces ${var} or $var in the string according to the values
-// of the current environment variables. References to undefined
+// of the current environment variables. References to undefined
// variables are replaced by the empty string.
func ExpandEnv(s string) string {
return Expand(s, Getenv)
@@ -49,7 +49,7 @@ func isAlphaNum(c uint8) bool {
}
// getShellName returns the name that begins the string and the number of bytes
-// consumed to extract it. If the name is enclosed in {}, it's part of a ${}
+// consumed to extract it. If the name is enclosed in {}, it's part of a ${}
// expansion and two more bytes are needed than the length of the name.
func getShellName(s string) (string, int) {
switch {
diff --git a/libgo/go/os/error_test.go b/libgo/go/os/error_test.go
index 5477e7e..a47c173 100644
--- a/libgo/go/os/error_test.go
+++ b/libgo/go/os/error_test.go
@@ -80,11 +80,13 @@ func checkErrorPredicate(predName string, pred func(error) bool, err error) stri
return ""
}
-var isExistTests = []struct {
+type isExistTest struct {
err error
is bool
isnot bool
-}{
+}
+
+var isExistTests = []isExistTest{
{&os.PathError{Err: os.ErrInvalid}, false, false},
{&os.PathError{Err: os.ErrPermission}, false, false},
{&os.PathError{Err: os.ErrExist}, true, false},
@@ -109,10 +111,12 @@ func TestIsExist(t *testing.T) {
}
}
-var isPermissionTests = []struct {
+type isPermissionTest struct {
err error
want bool
-}{
+}
+
+var isPermissionTests = []isPermissionTest{
{nil, false},
{&os.PathError{Err: os.ErrPermission}, true},
{&os.SyscallError{Err: os.ErrPermission}, true},
diff --git a/libgo/go/os/error_unix.go b/libgo/go/os/error_unix.go
index c600227..3c78eb4 100644
--- a/libgo/go/os/error_unix.go
+++ b/libgo/go/os/error_unix.go
@@ -19,7 +19,7 @@ func isExist(err error) bool {
case *SyscallError:
err = pe.Err
}
- return err == syscall.EEXIST || err == ErrExist
+ return err == syscall.EEXIST || err == syscall.ENOTEMPTY || err == ErrExist
}
func isNotExist(err error) bool {
diff --git a/libgo/go/os/error_unix_test.go b/libgo/go/os/error_unix_test.go
new file mode 100644
index 0000000..76fe015
--- /dev/null
+++ b/libgo/go/os/error_unix_test.go
@@ -0,0 +1,39 @@
+// Copyright 2016 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 nacl netbsd openbsd solaris
+
+package os_test
+
+import (
+ "os"
+ "syscall"
+)
+
+func init() {
+ isExistTests = append(isExistTests,
+ isExistTest{err: &os.PathError{Err: syscall.EEXIST}, is: true, isnot: false},
+ isExistTest{err: &os.PathError{Err: syscall.ENOTEMPTY}, is: true, isnot: false},
+
+ isExistTest{err: &os.LinkError{Err: syscall.EEXIST}, is: true, isnot: false},
+ isExistTest{err: &os.LinkError{Err: syscall.ENOTEMPTY}, is: true, isnot: false},
+
+ isExistTest{err: &os.SyscallError{Err: syscall.EEXIST}, is: true, isnot: false},
+ isExistTest{err: &os.SyscallError{Err: syscall.ENOTEMPTY}, is: true, isnot: false},
+ )
+ isPermissionTests = append(isPermissionTests,
+ isPermissionTest{err: &os.PathError{Err: syscall.EACCES}, want: true},
+ isPermissionTest{err: &os.PathError{Err: syscall.EPERM}, want: true},
+ isPermissionTest{err: &os.PathError{Err: syscall.EEXIST}, want: false},
+
+ isPermissionTest{err: &os.LinkError{Err: syscall.EACCES}, want: true},
+ isPermissionTest{err: &os.LinkError{Err: syscall.EPERM}, want: true},
+ isPermissionTest{err: &os.LinkError{Err: syscall.EEXIST}, want: false},
+
+ isPermissionTest{err: &os.SyscallError{Err: syscall.EACCES}, want: true},
+ isPermissionTest{err: &os.SyscallError{Err: syscall.EPERM}, want: true},
+ isPermissionTest{err: &os.SyscallError{Err: syscall.EEXIST}, want: false},
+ )
+
+}
diff --git a/libgo/go/os/error_windows_test.go b/libgo/go/os/error_windows_test.go
new file mode 100644
index 0000000..427dfdb
--- /dev/null
+++ b/libgo/go/os/error_windows_test.go
@@ -0,0 +1,35 @@
+// Copyright 2016 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_test
+
+import (
+ "os"
+ "syscall"
+)
+
+func init() {
+ const _ERROR_BAD_NETPATH = syscall.Errno(53)
+
+ isExistTests = append(isExistTests,
+ isExistTest{err: &os.PathError{Err: syscall.ERROR_FILE_NOT_FOUND}, is: false, isnot: true},
+ isExistTest{err: &os.LinkError{Err: syscall.ERROR_FILE_NOT_FOUND}, is: false, isnot: true},
+ isExistTest{err: &os.SyscallError{Err: syscall.ERROR_FILE_NOT_FOUND}, is: false, isnot: true},
+
+ isExistTest{err: &os.PathError{Err: _ERROR_BAD_NETPATH}, is: false, isnot: true},
+ isExistTest{err: &os.LinkError{Err: _ERROR_BAD_NETPATH}, is: false, isnot: true},
+ isExistTest{err: &os.SyscallError{Err: _ERROR_BAD_NETPATH}, is: false, isnot: true},
+
+ isExistTest{err: &os.PathError{Err: syscall.ERROR_PATH_NOT_FOUND}, is: false, isnot: true},
+ isExistTest{err: &os.LinkError{Err: syscall.ERROR_PATH_NOT_FOUND}, is: false, isnot: true},
+ isExistTest{err: &os.SyscallError{Err: syscall.ERROR_PATH_NOT_FOUND}, is: false, isnot: true},
+ )
+ isPermissionTests = append(isPermissionTests,
+ isPermissionTest{err: &os.PathError{Err: syscall.ERROR_ACCESS_DENIED}, want: true},
+ isPermissionTest{err: &os.LinkError{Err: syscall.ERROR_ACCESS_DENIED}, want: true},
+ isPermissionTest{err: &os.SyscallError{Err: syscall.ERROR_ACCESS_DENIED}, want: true},
+ )
+}
diff --git a/libgo/go/os/exec.go b/libgo/go/os/exec.go
index 15e95b9..bf32498 100644
--- a/libgo/go/os/exec.go
+++ b/libgo/go/os/exec.go
@@ -6,6 +6,7 @@ package os
import (
"runtime"
+ "sync"
"sync/atomic"
"syscall"
)
@@ -13,8 +14,9 @@ import (
// Process stores the information about a process created by StartProcess.
type Process struct {
Pid int
- handle uintptr // handle is accessed atomically on Windows
- isdone uint32 // process has been successfully waited on, non zero if true
+ handle uintptr // handle is accessed atomically on Windows
+ isdone uint32 // process has been successfully waited on, non zero if true
+ sigMu sync.RWMutex // avoid race between wait and signal
}
func newProcess(pid int, handle uintptr) *Process {
@@ -41,10 +43,10 @@ type ProcAttr struct {
// new process in the form returned by Environ.
// If it is nil, the result of Environ will be used.
Env []string
- // Files specifies the open files inherited by the new process. The
+ // Files specifies the open files inherited by the new process. The
// first three entries correspond to standard input, standard output, and
- // standard error. An implementation may support additional entries,
- // depending on the underlying operating system. A nil entry corresponds
+ // standard error. An implementation may support additional entries,
+ // depending on the underlying operating system. A nil entry corresponds
// to that file being closed when the process starts.
Files []*File
diff --git a/libgo/go/os/exec/exec.go b/libgo/go/os/exec/exec.go
index 340ebd4..d2c1b17 100644
--- a/libgo/go/os/exec/exec.go
+++ b/libgo/go/os/exec/exec.go
@@ -13,6 +13,7 @@ package exec
import (
"bytes"
+ "context"
"errors"
"io"
"os"
@@ -102,13 +103,15 @@ type Cmd struct {
// available after a call to Wait or Run.
ProcessState *os.ProcessState
- lookPathErr error // LookPath error, if any.
- finished bool // when Wait was called
+ ctx context.Context // nil means none
+ lookPathErr error // LookPath error, if any.
+ finished bool // when Wait was called
childFiles []*os.File
closeAfterStart []io.Closer
closeAfterWait []io.Closer
goroutine []func() error
errch chan error // one send per goroutine
+ waitDone chan struct{}
}
// Command returns the Cmd struct to execute the named program with
@@ -138,6 +141,20 @@ func Command(name string, arg ...string) *Cmd {
return cmd
}
+// CommandContext is like Command but includes a context.
+//
+// The provided context is used to kill the process (by calling
+// os.Process.Kill) if the context becomes done before the command
+// completes on its own.
+func CommandContext(ctx context.Context, name string, arg ...string) *Cmd {
+ if ctx == nil {
+ panic("nil Context")
+ }
+ cmd := Command(name, arg...)
+ cmd.ctx = ctx
+ return cmd
+}
+
// interfaceEqual protects against panics from doing equality tests on
// two interfaces with non-comparable underlying types.
func interfaceEqual(a, b interface{}) bool {
@@ -310,6 +327,15 @@ func (c *Cmd) Start() error {
if c.Process != nil {
return errors.New("exec: already started")
}
+ if c.ctx != nil {
+ select {
+ case <-c.ctx.Done():
+ c.closeDescriptors(c.closeAfterStart)
+ c.closeDescriptors(c.closeAfterWait)
+ return c.ctx.Err()
+ default:
+ }
+ }
type F func(*Cmd) (*os.File, error)
for _, setupFd := range []F{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} {
@@ -345,6 +371,17 @@ func (c *Cmd) Start() error {
}(fn)
}
+ if c.ctx != nil {
+ c.waitDone = make(chan struct{})
+ go func() {
+ select {
+ case <-c.ctx.Done():
+ c.Process.Kill()
+ case <-c.waitDone:
+ }
+ }()
+ }
+
return nil
}
@@ -393,7 +430,11 @@ func (c *Cmd) Wait() error {
return errors.New("exec: Wait was already called")
}
c.finished = true
+
state, err := c.Process.Wait()
+ if c.waitDone != nil {
+ close(c.waitDone)
+ }
c.ProcessState = state
var copyError error
diff --git a/libgo/go/os/exec/exec_test.go b/libgo/go/os/exec/exec_test.go
index 1576e8f..3a866a9 100644
--- a/libgo/go/os/exec/exec_test.go
+++ b/libgo/go/os/exec/exec_test.go
@@ -10,6 +10,7 @@ package exec_test
import (
"bufio"
"bytes"
+ "context"
"fmt"
"internal/testenv"
"io"
@@ -28,12 +29,16 @@ import (
"time"
)
-func helperCommand(t *testing.T, s ...string) *exec.Cmd {
+func helperCommandContext(t *testing.T, ctx context.Context, s ...string) (cmd *exec.Cmd) {
testenv.MustHaveExec(t)
cs := []string{"-test.run=TestHelperProcess", "--"}
cs = append(cs, s...)
- cmd := exec.Command(os.Args[0], cs...)
+ if ctx != nil {
+ cmd = exec.CommandContext(ctx, os.Args[0], cs...)
+ } else {
+ cmd = exec.Command(os.Args[0], cs...)
+ }
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
path := os.Getenv("LD_LIBRARY_PATH")
if path != "" {
@@ -42,6 +47,10 @@ func helperCommand(t *testing.T, s ...string) *exec.Cmd {
return cmd
}
+func helperCommand(t *testing.T, s ...string) *exec.Cmd {
+ return helperCommandContext(t, nil, s...)
+}
+
func TestEcho(t *testing.T) {
bs, err := helperCommand(t, "echo", "foo bar", "baz").Output()
if err != nil {
@@ -345,7 +354,7 @@ func TestExtraFilesFDShuffle(t *testing.T) {
//
// We want to test that FDs in the child do not get overwritten
// by one another as this shuffle occurs. The original implementation
- // was buggy in that in some data dependent cases it would ovewrite
+ // was buggy in that in some data dependent cases it would overwrite
// stderr in the child with one of the ExtraFile members.
// Testing for this case is difficult because it relies on using
// the same FD values as that case. In particular, an FD of 3
@@ -483,7 +492,7 @@ func TestExtraFiles(t *testing.T) {
if err != nil {
t.Fatalf("Write: %v", err)
}
- _, err = tf.Seek(0, os.SEEK_SET)
+ _, err = tf.Seek(0, io.SeekStart)
if err != nil {
t.Fatalf("Seek: %v", err)
}
@@ -663,10 +672,6 @@ func TestHelperProcess(*testing.T) {
// the cloned file descriptors that result from opening
// /dev/urandom.
// https://golang.org/issue/3955
- case "plan9":
- // TODO(0intro): Determine why Plan 9 is leaking
- // file descriptors.
- // https://golang.org/issue/7118
case "solaris":
// TODO(aram): This fails on Solaris because libc opens
// its own files, as it sees fit. Darwin does the same,
@@ -701,7 +706,7 @@ func TestHelperProcess(*testing.T) {
}
// Referring to fd3 here ensures that it is not
// garbage collected, and therefore closed, while
- // executing the wantfd loop above. It doesn't matter
+ // executing the wantfd loop above. It doesn't matter
// what we do with fd3 as long as we refer to it;
// closing it is the easy choice.
fd3.Close()
@@ -839,3 +844,111 @@ func TestOutputStderrCapture(t *testing.T) {
t.Errorf("ExitError.Stderr = %q; want %q", got, want)
}
}
+
+func TestContext(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ c := helperCommandContext(t, ctx, "pipetest")
+ stdin, err := c.StdinPipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ stdout, err := c.StdoutPipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := c.Start(); err != nil {
+ t.Fatal(err)
+ }
+
+ if _, err := stdin.Write([]byte("O:hi\n")); err != nil {
+ t.Fatal(err)
+ }
+ buf := make([]byte, 5)
+ n, err := io.ReadFull(stdout, buf)
+ if n != len(buf) || err != nil || string(buf) != "O:hi\n" {
+ t.Fatalf("ReadFull = %d, %v, %q", n, err, buf[:n])
+ }
+ waitErr := make(chan error, 1)
+ go func() {
+ waitErr <- c.Wait()
+ }()
+ cancel()
+ select {
+ case err := <-waitErr:
+ if err == nil {
+ t.Fatal("expected Wait failure")
+ }
+ case <-time.After(3 * time.Second):
+ t.Fatal("timeout waiting for child process death")
+ }
+}
+
+func TestContextCancel(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+ c := helperCommandContext(t, ctx, "cat")
+
+ r, w, err := os.Pipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ c.Stdin = r
+
+ stdout, err := c.StdoutPipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ readDone := make(chan struct{})
+ go func() {
+ defer close(readDone)
+ var a [1024]byte
+ for {
+ n, err := stdout.Read(a[:])
+ if err != nil {
+ if err != io.EOF {
+ t.Errorf("unexpected read error: %v", err)
+ }
+ return
+ }
+ t.Logf("%s", a[:n])
+ }
+ }()
+
+ if err := c.Start(); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := r.Close(); err != nil {
+ t.Fatal(err)
+ }
+
+ if _, err := io.WriteString(w, "echo"); err != nil {
+ t.Fatal(err)
+ }
+
+ cancel()
+
+ // Calling cancel should have killed the process, so writes
+ // should now fail. Give the process a little while to die.
+ start := time.Now()
+ for {
+ if _, err := io.WriteString(w, "echo"); err != nil {
+ break
+ }
+ if time.Since(start) > time.Second {
+ t.Fatal("cancelling context did not stop program")
+ }
+ time.Sleep(time.Millisecond)
+ }
+
+ if err := w.Close(); err != nil {
+ t.Error("error closing write end of pipe: %v", err)
+ }
+ <-readDone
+
+ if err := c.Wait(); err == nil {
+ t.Error("program unexpectedly exited successfully")
+ } else {
+ t.Logf("exit status: %v", err)
+ }
+}
diff --git a/libgo/go/os/exec/lp_plan9.go b/libgo/go/os/exec/lp_plan9.go
index 5aa8a54..142f87e 100644
--- a/libgo/go/os/exec/lp_plan9.go
+++ b/libgo/go/os/exec/lp_plan9.go
@@ -1,4 +1,4 @@
-// Copyright 2011 The Go Authors. All rights reserved.
+// 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.
@@ -7,6 +7,7 @@ package exec
import (
"errors"
"os"
+ "path/filepath"
"strings"
)
@@ -44,9 +45,10 @@ func LookPath(file string) (string, error) {
}
path := os.Getenv("path")
- for _, dir := range strings.Split(path, "\000") {
- if err := findExecutable(dir + "/" + file); err == nil {
- return dir + "/" + file, nil
+ for _, dir := range filepath.SplitList(path) {
+ path := filepath.Join(dir, file)
+ if err := findExecutable(path); err == nil {
+ return path, nil
}
}
return "", &Error{file, ErrNotFound}
diff --git a/libgo/go/os/exec/lp_unix.go b/libgo/go/os/exec/lp_unix.go
index 3f895d5..7a30275 100644
--- a/libgo/go/os/exec/lp_unix.go
+++ b/libgo/go/os/exec/lp_unix.go
@@ -1,4 +1,4 @@
-// Copyright 2010 The Go Authors. All rights reserved.
+// Copyright 2010 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.
@@ -9,6 +9,7 @@ package exec
import (
"errors"
"os"
+ "path/filepath"
"strings"
)
@@ -42,16 +43,13 @@ func LookPath(file string) (string, error) {
}
return "", &Error{file, err}
}
- pathenv := os.Getenv("PATH")
- if pathenv == "" {
- return "", &Error{file, ErrNotFound}
- }
- for _, dir := range strings.Split(pathenv, ":") {
+ path := os.Getenv("PATH")
+ for _, dir := range filepath.SplitList(path) {
if dir == "" {
// Unix shell semantics: path element "" means "."
dir = "."
}
- path := dir + "/" + file
+ path := filepath.Join(dir, file)
if err := findExecutable(path); err == nil {
return path, nil
}
diff --git a/libgo/go/os/exec/lp_unix_test.go b/libgo/go/os/exec/lp_unix_test.go
index 051db66..d467acf 100644
--- a/libgo/go/os/exec/lp_unix_test.go
+++ b/libgo/go/os/exec/lp_unix_test.go
@@ -1,4 +1,4 @@
-// Copyright 2013 The Go Authors. All rights reserved.
+// Copyright 2013 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.
diff --git a/libgo/go/os/exec/lp_windows.go b/libgo/go/os/exec/lp_windows.go
index c3efd67..793d4d9 100644
--- a/libgo/go/os/exec/lp_windows.go
+++ b/libgo/go/os/exec/lp_windows.go
@@ -1,4 +1,4 @@
-// Copyright 2010 The Go Authors. All rights reserved.
+// Copyright 2010 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.
@@ -7,6 +7,7 @@ package exec
import (
"errors"
"os"
+ "path/filepath"
"strings"
)
@@ -46,7 +47,7 @@ func findExecutable(file string, exts []string) (string, error) {
return f, nil
}
}
- return ``, os.ErrNotExist
+ return "", os.ErrNotExist
}
// LookPath searches for an executable binary named file
@@ -55,69 +56,38 @@ func findExecutable(file string, exts []string) (string, error) {
// LookPath also uses PATHEXT environment variable to match
// a suitable candidate.
// The result may be an absolute path or a path relative to the current directory.
-func LookPath(file string) (f string, err error) {
+func LookPath(file string) (string, error) {
+ var exts []string
x := os.Getenv(`PATHEXT`)
- if x == `` {
- x = `.COM;.EXE;.BAT;.CMD`
- }
- exts := []string{}
- for _, e := range strings.Split(strings.ToLower(x), `;`) {
- if e == "" {
- continue
- }
- if e[0] != '.' {
- e = "." + e
- }
- exts = append(exts, e)
- }
- if strings.IndexAny(file, `:\/`) != -1 {
- if f, err = findExecutable(file, exts); err == nil {
- return
- }
- return ``, &Error{file, err}
- }
- if f, err = findExecutable(`.\`+file, exts); err == nil {
- return
- }
- if pathenv := os.Getenv(`PATH`); pathenv != `` {
- for _, dir := range splitList(pathenv) {
- if f, err = findExecutable(dir+`\`+file, exts); err == nil {
- return
+ if x != "" {
+ for _, e := range strings.Split(strings.ToLower(x), `;`) {
+ if e == "" {
+ continue
}
+ if e[0] != '.' {
+ e = "." + e
+ }
+ exts = append(exts, e)
}
+ } else {
+ exts = []string{".com", ".exe", ".bat", ".cmd"}
}
- return ``, &Error{file, ErrNotFound}
-}
-func splitList(path string) []string {
- // The same implementation is used in SplitList in path/filepath;
- // consider changing path/filepath when changing this.
-
- if path == "" {
- return []string{}
- }
-
- // Split path, respecting but preserving quotes.
- list := []string{}
- start := 0
- quo := false
- for i := 0; i < len(path); i++ {
- switch c := path[i]; {
- case c == '"':
- quo = !quo
- case c == os.PathListSeparator && !quo:
- list = append(list, path[start:i])
- start = i + 1
+ if strings.ContainsAny(file, `:\/`) {
+ if f, err := findExecutable(file, exts); err == nil {
+ return f, nil
+ } else {
+ return "", &Error{file, err}
}
}
- list = append(list, path[start:])
-
- // Remove quotes.
- for i, s := range list {
- if strings.Contains(s, `"`) {
- list[i] = strings.Replace(s, `"`, ``, -1)
+ if f, err := findExecutable(filepath.Join(".", file), exts); err == nil {
+ return f, nil
+ }
+ path := os.Getenv("path")
+ for _, dir := range filepath.SplitList(path) {
+ if f, err := findExecutable(filepath.Join(dir, file), exts); err == nil {
+ return f, nil
}
}
-
- return list
+ return "", &Error{file, ErrNotFound}
}
diff --git a/libgo/go/os/exec_posix.go b/libgo/go/os/exec_posix.go
index 94dd04b..3cf38b6 100644
--- a/libgo/go/os/exec_posix.go
+++ b/libgo/go/os/exec_posix.go
@@ -21,7 +21,7 @@ var (
func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
// If there is no SysProcAttr (ie. no Chroot or changed
// UID/GID), double-check existence of the directory we want
- // to chdir into. We can make the error clearer this way.
+ // to chdir into. We can make the error clearer this way.
if attr != nil && attr.Sys == nil && attr.Dir != "" {
if _, err := Stat(attr.Dir); err != nil {
pe := err.(*PathError)
diff --git a/libgo/go/os/exec_unix.go b/libgo/go/os/exec_unix.go
index ed97f85..c4999db 100644
--- a/libgo/go/os/exec_unix.go
+++ b/libgo/go/os/exec_unix.go
@@ -17,6 +17,22 @@ func (p *Process) wait() (ps *ProcessState, err error) {
if p.Pid == -1 {
return nil, syscall.EINVAL
}
+
+ // If we can block until Wait4 will succeed immediately, do so.
+ ready, err := p.blockUntilWaitable()
+ if err != nil {
+ return nil, err
+ }
+ if ready {
+ // Mark the process done now, before the call to Wait4,
+ // so that Process.signal will not send a signal.
+ p.setDone()
+ // Acquire a write lock on sigMu to wait for any
+ // active call to the signal method to complete.
+ p.sigMu.Lock()
+ p.sigMu.Unlock()
+ }
+
var status syscall.WaitStatus
var rusage syscall.Rusage
pid1, e := syscall.Wait4(p.Pid, &status, 0, &rusage)
@@ -43,6 +59,8 @@ func (p *Process) signal(sig Signal) error {
if p.Pid == 0 {
return errors.New("os: process not initialized")
}
+ p.sigMu.RLock()
+ defer p.sigMu.RUnlock()
if p.done() {
return errFinished
}
diff --git a/libgo/go/os/exec_windows.go b/libgo/go/os/exec_windows.go
index 3264271..72b5a93 100644
--- a/libgo/go/os/exec_windows.go
+++ b/libgo/go/os/exec_windows.go
@@ -104,7 +104,7 @@ func init() {
defer syscall.LocalFree(syscall.Handle(uintptr(unsafe.Pointer(argv))))
Args = make([]string, argc)
for i, v := range (*argv)[:argc] {
- Args[i] = string(syscall.UTF16ToString((*v)[:]))
+ Args[i] = syscall.UTF16ToString((*v)[:])
}
}
diff --git a/libgo/go/os/export_test.go b/libgo/go/os/export_test.go
index 9fa7936..d735aee 100644
--- a/libgo/go/os/export_test.go
+++ b/libgo/go/os/export_test.go
@@ -1,4 +1,4 @@
-// Copyright 2011 The Go Authors. All rights reserved.
+// 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.
diff --git a/libgo/go/os/file.go b/libgo/go/os/file.go
index 4f8e3f3..e546441 100644
--- a/libgo/go/os/file.go
+++ b/libgo/go/os/file.go
@@ -46,6 +46,10 @@ func (f *File) Name() string { return f.name }
// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
// standard output, and standard error file descriptors.
+//
+// Note that the Go runtime writes to standard error for panics and crashes;
+// closing Stderr may cause those messages to go elsewhere, perhaps
+// to a file opened later.
var (
Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
@@ -66,6 +70,8 @@ const (
)
// Seek whence values.
+//
+// Deprecated: Use io.SeekStart, io.SeekCurrent, and io.SeekEnd.
const (
SEEK_SET int = 0 // seek relative to the origin of the file
SEEK_CUR int = 1 // seek relative to the current offset
@@ -236,7 +242,7 @@ func (f *File) Chdir() error {
return nil
}
-// Open opens the named file for reading. If successful, methods on
+// Open opens the named file for reading. If successful, methods on
// the returned file can be used for reading; the associated file
// descriptor has mode O_RDONLY.
// If there is an error, it will be of type *PathError.
diff --git a/libgo/go/os/file_plan9.go b/libgo/go/os/file_plan9.go
index c83fa02..9edb6bc 100644
--- a/libgo/go/os/file_plan9.go
+++ b/libgo/go/os/file_plan9.go
@@ -5,6 +5,7 @@
package os
import (
+ "io"
"runtime"
"syscall"
"time"
@@ -75,8 +76,8 @@ func syscallMode(i FileMode) (o uint32) {
}
// 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, (0666 etc.) if applicable. If successful,
+// or Create instead. It opens the named file with specified flag
+// (O_RDONLY etc.) and perm, (0666 etc.) if applicable. 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) {
@@ -123,7 +124,7 @@ func OpenFile(name string, flag int, perm FileMode) (*File, error) {
}
if append {
- if _, e = syscall.Seek(fd, 0, SEEK_END); e != nil {
+ if _, e = syscall.Seek(fd, 0, io.SeekEnd); e != nil {
return nil, &PathError{"seek", name, e}
}
}
@@ -145,11 +146,9 @@ func (file *file) close() error {
return ErrInvalid
}
var err error
- syscall.ForkLock.RLock()
if e := syscall.Close(file.fd); e != nil {
err = &PathError{"close", file.name, e}
}
- syscall.ForkLock.RUnlock()
file.fd = -1 // so it can't be closed again
// no need for a finalizer anymore
@@ -419,12 +418,9 @@ func Chtimes(name string, atime time.Time, mtime time.Time) error {
func Pipe() (r *File, w *File, err error) {
var p [2]int
- syscall.ForkLock.RLock()
if e := syscall.Pipe(p[0:]); e != nil {
- syscall.ForkLock.RUnlock()
return nil, nil, NewSyscallError("pipe", e)
}
- syscall.ForkLock.RUnlock()
return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil
}
diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go
index c3119cd..5d6c8af 100644
--- a/libgo/go/os/file_unix.go
+++ b/libgo/go/os/file_unix.go
@@ -78,8 +78,8 @@ func epipecheck(file *File, e error) {
const DevNull = "/dev/null"
// 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, (0666 etc.) if applicable. If successful,
+// or Create instead. It opens the named file with specified flag
+// (O_RDONLY etc.) and perm, (0666 etc.) if applicable. 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) {
@@ -114,7 +114,7 @@ func OpenFile(name string, flag int, perm FileMode) (*File, error) {
}
// There's a race here with fork/exec, which we are
- // content to live with. See ../syscall/exec_unix.go.
+ // content to live with. See ../syscall/exec_unix.go.
if !supportsCloseOnExec {
syscall.CloseOnExec(r)
}
@@ -187,7 +187,7 @@ func Stat(name string) (FileInfo, error) {
// Lstat returns a FileInfo describing the named file.
// If the file is a symbolic link, the returned FileInfo
-// describes the symbolic link. Lstat makes no attempt to follow the link.
+// describes the symbolic link. Lstat makes no attempt to follow the link.
// If there is an error, it will be of type *PathError.
func Lstat(name string) (FileInfo, error) {
var fs fileStat
@@ -322,7 +322,7 @@ func Remove(name string) error {
// Both failed: figure out which error to return.
// OS X and Linux differ on whether unlink(dir)
- // returns EISDIR, so can't use that. However,
+ // returns EISDIR, so can't use that. However,
// both agree that rmdir(file) returns ENOTDIR,
// so we can use that to decide which error is real.
// Rmdir might also return ENOTDIR if given a bad
diff --git a/libgo/go/os/getwd.go b/libgo/go/os/getwd.go
index d5da53b..4c3c0d9 100644
--- a/libgo/go/os/getwd.go
+++ b/libgo/go/os/getwd.go
@@ -20,7 +20,7 @@ var getwdCache struct {
var useSyscallwd = func(error) bool { return true }
// Getwd returns a rooted path name corresponding to the
-// current directory. If the current directory can be
+// current directory. If the current directory can be
// reached via multiple paths (due to symbolic links),
// Getwd may return any one of them.
func Getwd() (dir string, err error) {
@@ -74,7 +74,7 @@ func Getwd() (dir string, err error) {
}
// General algorithm: find name in parent
- // and then find name of parent. Each iteration
+ // and then find name of parent. Each iteration
// adds /name to the beginning of dir.
dir = ""
for parent := ".."; ; parent = "../" + parent {
diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go
index 5497704..abc638e 100644
--- a/libgo/go/os/os_test.go
+++ b/libgo/go/os/os_test.go
@@ -45,10 +45,10 @@ var sysdir = func() *sysDir {
switch runtime.GOOS {
case "android":
return &sysDir{
- "/system/framework",
+ "/system/lib",
[]string{
- "ext.jar",
- "framework.jar",
+ "libmedia.so",
+ "libpowermanager.so",
},
}
case "darwin":
@@ -536,7 +536,7 @@ func TestReaddirStatFailures(t *testing.T) {
return s
}
- if got, want := names(mustReadDir("inital readdir")),
+ if got, want := names(mustReadDir("initial readdir")),
[]string{"good1", "good2", "x"}; !reflect.DeepEqual(got, want) {
t.Errorf("initial readdir got %q; want %q", got, want)
}
@@ -1180,14 +1180,14 @@ func TestSeek(t *testing.T) {
out int64
}
var tests = []test{
- {0, 1, int64(len(data))},
- {0, 0, 0},
- {5, 0, 5},
- {0, 2, int64(len(data))},
- {0, 0, 0},
- {-1, 2, int64(len(data)) - 1},
- {1 << 33, 0, 1 << 33},
- {1 << 33, 2, 1<<33 + int64(len(data))},
+ {0, io.SeekCurrent, int64(len(data))},
+ {0, io.SeekStart, 0},
+ {5, io.SeekStart, 5},
+ {0, io.SeekEnd, int64(len(data))},
+ {0, io.SeekStart, 0},
+ {-1, io.SeekEnd, int64(len(data)) - 1},
+ {1 << 33, io.SeekStart, 1 << 33},
+ {1 << 33, io.SeekEnd, 1<<33 + int64(len(data))},
}
for i, tt := range tests {
off, err := f.Seek(tt.in, tt.whence)
@@ -1273,15 +1273,19 @@ func TestOpenNoName(t *testing.T) {
}
}
-func run(t *testing.T, cmd []string) string {
+func runBinHostname(t *testing.T) string {
// Run /bin/hostname and collect output.
r, w, err := Pipe()
if err != nil {
t.Fatal(err)
}
defer r.Close()
- p, err := StartProcess("/bin/hostname", []string{"hostname"}, &ProcAttr{Files: []*File{nil, w, Stderr}})
+ const path = "/bin/hostname"
+ p, err := StartProcess(path, []string{"hostname"}, &ProcAttr{Files: []*File{nil, w, Stderr}})
if err != nil {
+ if _, err := Stat(path); IsNotExist(err) {
+ t.Skipf("skipping test; test requires %s but it does not exist", path)
+ }
t.Fatal(err)
}
w.Close()
@@ -1301,7 +1305,7 @@ func run(t *testing.T, cmd []string) string {
output = output[0 : n-1]
}
if output == "" {
- t.Fatalf("%v produced no output", cmd)
+ t.Fatalf("/bin/hostname produced no output")
}
return output
@@ -1343,7 +1347,7 @@ func TestHostname(t *testing.T) {
if err != nil {
t.Fatalf("%v", err)
}
- want := run(t, []string{"/bin/hostname"})
+ want := runBinHostname(t)
if hostname != want {
i := strings.Index(hostname, ".")
if i < 0 || hostname[0:i] != want {
@@ -1370,6 +1374,38 @@ func TestReadAt(t *testing.T) {
}
}
+// Verify that ReadAt doesn't affect seek offset.
+// In the Plan 9 kernel, there used to be a bug in the implementation of
+// the pread syscall, where the channel offset was erroneously updated after
+// calling pread on a file.
+func TestReadAtOffset(t *testing.T) {
+ f := newFile("TestReadAtOffset", t)
+ defer Remove(f.Name())
+ defer f.Close()
+
+ const data = "hello, world\n"
+ io.WriteString(f, data)
+
+ f.Seek(0, 0)
+ b := make([]byte, 5)
+
+ n, err := f.ReadAt(b, 7)
+ if err != nil || n != len(b) {
+ t.Fatalf("ReadAt 7: %d, %v", n, err)
+ }
+ if string(b) != "world" {
+ t.Fatalf("ReadAt 7: have %q want %q", string(b), "world")
+ }
+
+ n, err = f.Read(b)
+ if err != nil || n != len(b) {
+ t.Fatalf("Read: %d, %v", n, err)
+ }
+ if string(b) != "hello" {
+ t.Fatalf("Read: have %q want %q", string(b), "hello")
+ }
+}
+
func TestWriteAt(t *testing.T) {
f := newFile("TestWriteAt", t)
defer Remove(f.Name())
@@ -1577,6 +1613,42 @@ func TestStatDirModeExec(t *testing.T) {
}
}
+func TestStatStdin(t *testing.T) {
+ switch runtime.GOOS {
+ case "android", "plan9":
+ t.Skipf("%s doesn't have /bin/sh", runtime.GOOS)
+ }
+
+ testenv.MustHaveExec(t)
+
+ if Getenv("GO_WANT_HELPER_PROCESS") == "1" {
+ st, err := Stdin.Stat()
+ if err != nil {
+ t.Fatalf("Stat failed: %v", err)
+ }
+ fmt.Println(st.Mode() & ModeNamedPipe)
+ Exit(0)
+ }
+
+ var cmd *osexec.Cmd
+ if runtime.GOOS == "windows" {
+ cmd = osexec.Command("cmd", "/c", "echo output | "+Args[0]+" -test.run=TestStatStdin")
+ } else {
+ cmd = osexec.Command("/bin/sh", "-c", "echo output | "+Args[0]+" -test.run=TestStatStdin")
+ }
+ cmd.Env = append(Environ(), "GO_WANT_HELPER_PROCESS=1")
+
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("Failed to spawn child process: %v %q", err, string(output))
+ }
+
+ // result will be like "prw-rw-rw"
+ if len(output) < 1 || output[0] != 'p' {
+ t.Fatalf("Child process reports stdin is not pipe '%v'", string(output))
+ }
+}
+
func TestReadAtEOF(t *testing.T) {
f := newFile("TestReadAtEOF", t)
defer Remove(f.Name())
@@ -1684,7 +1756,7 @@ var nilFileMethodTests = []struct {
{"ReadAt", func(f *File) error { _, err := f.ReadAt(make([]byte, 0), 0); return err }},
{"Readdir", func(f *File) error { _, err := f.Readdir(1); return err }},
{"Readdirnames", func(f *File) error { _, err := f.Readdirnames(1); return err }},
- {"Seek", func(f *File) error { _, err := f.Seek(0, 0); return err }},
+ {"Seek", func(f *File) error { _, err := f.Seek(0, io.SeekStart); return err }},
{"Stat", func(f *File) error { _, err := f.Stat(); return err }},
{"Sync", func(f *File) error { return f.Sync() }},
{"Truncate", func(f *File) error { return f.Truncate(0) }},
diff --git a/libgo/go/os/os_unix_test.go b/libgo/go/os/os_unix_test.go
index d02e07b..5c10154 100644
--- a/libgo/go/os/os_unix_test.go
+++ b/libgo/go/os/os_unix_test.go
@@ -39,7 +39,7 @@ func TestChown(t *testing.T) {
}
// 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
+ // on the file. On NFS, the Getgroups groups are
// basically useless.
f := newFile("TestChown", t)
defer Remove(f.Name())
@@ -50,7 +50,7 @@ func TestChown(t *testing.T) {
}
// Can't change uid unless root, but can try
- // changing the group id. First try our current group.
+ // changing the group id. First try our current group.
gid := Getgid()
t.Log("gid:", gid)
if err = Chown(f.Name(), -1, gid); err != nil {
@@ -86,7 +86,7 @@ func TestFileChown(t *testing.T) {
}
// 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
+ // on the file. On NFS, the Getgroups groups are
// basically useless.
f := newFile("TestFileChown", t)
defer Remove(f.Name())
@@ -97,7 +97,7 @@ func TestFileChown(t *testing.T) {
}
// Can't change uid unless root, but can try
- // changing the group id. First try our current group.
+ // changing the group id. First try our current group.
gid := Getgid()
t.Log("gid:", gid)
if err = f.Chown(-1, gid); err != nil {
@@ -133,7 +133,7 @@ func TestLchown(t *testing.T) {
}
// 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
+ // on the file. On NFS, the Getgroups groups are
// basically useless.
f := newFile("TestLchown", t)
defer Remove(f.Name())
@@ -145,12 +145,15 @@ func TestLchown(t *testing.T) {
linkname := f.Name() + "2"
if err := Symlink(f.Name(), linkname); err != nil {
+ if runtime.GOOS == "android" && IsPermission(err) {
+ t.Skip("skipping test on Android; permission error creating symlink")
+ }
t.Fatalf("link %s -> %s: %v", f.Name(), linkname, err)
}
defer Remove(linkname)
// Can't change uid unless root, but can try
- // changing the group id. First try our current group.
+ // changing the group id. First try our current group.
gid := Getgid()
t.Log("gid:", gid)
if err = Lchown(linkname, -1, gid); err != nil {
diff --git a/libgo/go/os/path.go b/libgo/go/os/path.go
index 84a3be3..146d7b6 100644
--- a/libgo/go/os/path.go
+++ b/libgo/go/os/path.go
@@ -61,7 +61,7 @@ func MkdirAll(path string, perm FileMode) error {
// 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
+// it encounters. If the path does not exist, RemoveAll
// returns nil (no error).
func RemoveAll(path string) error {
// Simple case: if Remove works, we're done.
diff --git a/libgo/go/os/path_test.go b/libgo/go/os/path_test.go
index b453131..51dc25b 100644
--- a/libgo/go/os/path_test.go
+++ b/libgo/go/os/path_test.go
@@ -147,7 +147,7 @@ func TestRemoveAll(t *testing.T) {
// No error checking here: either RemoveAll
// will or won't be able to remove dpath;
// either way we want to see if it removes fpath
- // and path/zzz. Reasons why RemoveAll might
+ // and path/zzz. Reasons why RemoveAll might
// succeed in removing dpath as well include:
// * running as root
// * running on a file system without permissions (FAT)
diff --git a/libgo/go/os/pipe_test.go b/libgo/go/os/pipe_test.go
index 82b792e..74cce80 100644
--- a/libgo/go/os/pipe_test.go
+++ b/libgo/go/os/pipe_test.go
@@ -88,7 +88,7 @@ func TestStdPipe(t *testing.T) {
}
}
-// This is a helper for TestStdPipe. It's not a test in itself.
+// This is a helper for TestStdPipe. It's not a test in itself.
func TestStdPipeHelper(t *testing.T) {
if os.Getenv("GO_TEST_STD_PIPE_HELPER_SIGNAL") != "" {
signal.Notify(make(chan os.Signal, 1), syscall.SIGPIPE)
diff --git a/libgo/go/os/signal/doc.go b/libgo/go/os/signal/doc.go
index 80e66cf..73b01a2 100644
--- a/libgo/go/os/signal/doc.go
+++ b/libgo/go/os/signal/doc.go
@@ -11,7 +11,7 @@ package on Windows and Plan 9, see below.
Types of signals
The signals SIGKILL and SIGSTOP may not be caught by a program, and
-therefore can not be affected by this package.
+therefore cannot be affected by this package.
Synchronous signals are signals triggered by errors in program
execution: SIGBUS, SIGFPE, and SIGSEGV. These are only considered
@@ -205,8 +205,8 @@ before raising the signal.
Windows
On Windows a ^C (Control-C) or ^BREAK (Control-Break) normally cause
-the program to exit. If Notify is called for os.SIGINT, ^C or ^BREAK
-will cause os.SIGINT to be sent on the channel, and the program will
+the program to exit. If Notify is called for os.Interrupt, ^C or ^BREAK
+will cause os.Interrupt to be sent on the channel, and the program will
not exit. If Reset is called, or Stop is called on all channels passed
to Notify, then the default behavior will be restored.
diff --git a/libgo/go/os/signal/signal.go b/libgo/go/os/signal/signal.go
index 2e6f186..c1376da 100644
--- a/libgo/go/os/signal/signal.go
+++ b/libgo/go/os/signal/signal.go
@@ -79,7 +79,7 @@ func Ignore(sig ...os.Signal) {
//
// Package signal will not block sending to c: the caller must ensure
// that c has sufficient buffer space to keep up with the expected
-// signal rate. For a channel used for notification of just one signal value,
+// signal rate. For a channel used for notification of just one signal value,
// a buffer of size 1 is sufficient.
//
// It is allowed to call Notify multiple times with the same channel:
diff --git a/libgo/go/os/signal/signal_test.go b/libgo/go/os/signal/signal_test.go
index 56d786e..406102c 100644
--- a/libgo/go/os/signal/signal_test.go
+++ b/libgo/go/os/signal/signal_test.go
@@ -139,6 +139,19 @@ func testCancel(t *testing.T, ignore bool) {
Reset(syscall.SIGWINCH, syscall.SIGHUP)
}
+ // At this point we do not expect any further signals on c1.
+ // However, it is just barely possible that the initial SIGWINCH
+ // at the start of this function was delivered after we called
+ // Notify on c1. In that case the waitSig for SIGWINCH may have
+ // picked up that initial SIGWINCH, and the second SIGWINCH may
+ // then have been delivered on the channel. This sequence of events
+ // may have caused issue 15661.
+ // So, read any possible signal from the channel now.
+ select {
+ case <-c1:
+ default:
+ }
+
// Send this process a SIGWINCH. It should be ignored.
syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
diff --git a/libgo/go/os/stat_dragonfly.go b/libgo/go/os/stat_dragonfly.go
index 69e6323..217bc67 100644
--- a/libgo/go/os/stat_dragonfly.go
+++ b/libgo/go/os/stat_dragonfly.go
@@ -11,7 +11,7 @@ import (
func fillFileStatFromSys(fs *fileStat, name string) {
fs.name = basename(name)
- fs.size = int64(fs.sys.Size)
+ fs.size = fs.sys.Size
fs.modTime = timespecToTime(fs.sys.Mtim)
fs.mode = FileMode(fs.sys.Mode & 0777)
switch fs.sys.Mode & syscall.S_IFMT {
@@ -42,7 +42,7 @@ func fillFileStatFromSys(fs *fileStat, name string) {
}
func timespecToTime(ts syscall.Timespec) time.Time {
- return time.Unix(int64(ts.Sec), int64(ts.Nsec))
+ return time.Unix(ts.Sec, ts.Nsec)
}
// For testing.
diff --git a/libgo/go/os/stat_nacl.go b/libgo/go/os/stat_nacl.go
index d3bed14..0c53f2f 100644
--- a/libgo/go/os/stat_nacl.go
+++ b/libgo/go/os/stat_nacl.go
@@ -11,7 +11,7 @@ import (
func fillFileStatFromSys(fs *fileStat, name string) {
fs.name = basename(name)
- fs.size = int64(fs.sys.Size)
+ fs.size = fs.sys.Size
fs.modTime = timespecToTime(fs.sys.Mtime, fs.sys.MtimeNsec)
fs.mode = FileMode(fs.sys.Mode & 0777)
switch fs.sys.Mode & syscall.S_IFMT {
diff --git a/libgo/go/os/stat_plan9.go b/libgo/go/os/stat_plan9.go
index fa4bd83..96f056c 100644
--- a/libgo/go/os/stat_plan9.go
+++ b/libgo/go/os/stat_plan9.go
@@ -20,7 +20,7 @@ func sameFile(fs1, fs2 *fileStat) bool {
func fileInfoFromStat(d *syscall.Dir) FileInfo {
fs := &fileStat{
name: d.Name,
- size: int64(d.Length),
+ size: d.Length,
modTime: time.Unix(int64(d.Mtime), 0),
sys: d,
}
@@ -100,7 +100,7 @@ func Stat(name string) (FileInfo, error) {
// Lstat returns a FileInfo describing the named file.
// If the file is a symbolic link, the returned FileInfo
-// describes the symbolic link. Lstat makes no attempt to follow the link.
+// describes the symbolic link. Lstat makes no attempt to follow the link.
// If there is an error, it will be of type *PathError.
func Lstat(name string) (FileInfo, error) {
return Stat(name)
diff --git a/libgo/go/os/str.go b/libgo/go/os/str.go
index d3e03e9..cba9fa3 100644
--- a/libgo/go/os/str.go
+++ b/libgo/go/os/str.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.
-// Simple converions to avoid depending on strconv.
+// Simple conversions to avoid depending on strconv.
package os
diff --git a/libgo/go/os/types.go b/libgo/go/os/types.go
index 9d6f8e1..12b593f 100644
--- a/libgo/go/os/types.go
+++ b/libgo/go/os/types.go
@@ -25,7 +25,7 @@ type FileInfo interface {
// A FileMode represents a file's mode and permission bits.
// The bits have the same definition on all systems, so that
// information about files can be moved from one system
-// to another portably. Not all bits apply to all systems.
+// to another portably. Not all bits apply to all systems.
// The only required bit is ModeDir for directories.
type FileMode uint32
diff --git a/libgo/go/os/types_windows.go b/libgo/go/os/types_windows.go
index 7b2e546..ad4e863 100644
--- a/libgo/go/os/types_windows.go
+++ b/libgo/go/os/types_windows.go
@@ -14,6 +14,7 @@ import (
type fileStat struct {
name string
sys syscall.Win32FileAttributeData
+ pipe bool
// used to implement SameFile
sync.Mutex
@@ -42,6 +43,9 @@ func (fs *fileStat) Mode() (m FileMode) {
if fs.sys.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 {
m |= ModeSymlink
}
+ if fs.pipe {
+ m |= ModeNamedPipe
+ }
return m
}
@@ -69,7 +73,7 @@ func (fs *fileStat) loadFileId() error {
}
defer syscall.CloseHandle(h)
var i syscall.ByHandleFileInformation
- err = syscall.GetFileInformationByHandle(syscall.Handle(h), &i)
+ err = syscall.GetFileInformationByHandle(h, &i)
if err != nil {
return err
}
diff --git a/libgo/go/os/user/decls_solaris.go b/libgo/go/os/user/decls_solaris.go
index 788a00f..969b48c 100644
--- a/libgo/go/os/user/decls_solaris.go
+++ b/libgo/go/os/user/decls_solaris.go
@@ -16,3 +16,9 @@ func libc_getpwnam_r(name *byte, pwd *syscall.Passwd, buf *byte, buflen syscall.
//extern __posix_getpwuid_r
func libc_getpwuid_r(uid syscall.Uid_t, pwd *syscall.Passwd, buf *byte, buflen syscall.Size_t, result **syscall.Passwd) int
+
+//extern __posix_getgrnam_r
+func libc_getgrnam_r(name *byte, grp *syscall.Group, buf *byte, buflen syscall.Size_t, result **syscall.Group) int
+
+//extern __posix_getgrgid_r
+func libc_getgrgid_r(gid syscall.Gid_t, grp *syscall.group, buf *byte, buflen syscall.Size_t, result **syscall.group) int
diff --git a/libgo/go/os/user/decls_unix.go b/libgo/go/os/user/decls_unix.go
index f76e4c9..c2eb51e 100644
--- a/libgo/go/os/user/decls_unix.go
+++ b/libgo/go/os/user/decls_unix.go
@@ -16,3 +16,12 @@ func libc_getpwnam_r(name *byte, pwd *syscall.Passwd, buf *byte, buflen syscall.
//extern getpwuid_r
func libc_getpwuid_r(uid syscall.Uid_t, pwd *syscall.Passwd, buf *byte, buflen syscall.Size_t, result **syscall.Passwd) int
+
+//extern getgrnam_r
+func libc_getgrnam_r(name *byte, grp *syscall.Group, buf *byte, buflen syscall.Size_t, result **syscall.Group) int
+
+//extern getgrgid_r
+func libc_getgrgid_r(gid syscall.Gid_t, grp *syscall.Group, buf *byte, buflen syscall.Size_t, result **syscall.Group) int
+
+//extern getgrouplist
+func libc_getgrouplist(user *byte, group syscall.Gid_t, groups *syscall.Gid_t, ngroups *int32) int
diff --git a/libgo/go/os/user/listgroups_solaris.go b/libgo/go/os/user/listgroups_solaris.go
new file mode 100644
index 0000000..28a8a78
--- /dev/null
+++ b/libgo/go/os/user/listgroups_solaris.go
@@ -0,0 +1,17 @@
+// Copyright 2016 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 cgo
+
+// Even though this file requires no C, it is used to provide a
+// listGroup stub because all the other Solaris calls work. Otherwise,
+// this stub will conflict with the lookup_stubs.go fallback.
+
+package user
+
+import "fmt"
+
+func listGroups(u *User) ([]string, error) {
+ return nil, fmt.Errorf("user: list groups for %s: not supported on Solaris", u.Username)
+}
diff --git a/libgo/go/os/user/listgroups_unix.go b/libgo/go/os/user/listgroups_unix.go
new file mode 100644
index 0000000..8b2b13d
--- /dev/null
+++ b/libgo/go/os/user/listgroups_unix.go
@@ -0,0 +1,57 @@
+// Copyright 2016 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 dragonfly darwin freebsd !android,linux netbsd openbsd
+
+package user
+
+import (
+ "fmt"
+ "strconv"
+ "syscall"
+)
+
+/*
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdlib.h>
+*/
+
+func listGroups(u *User) ([]string, error) {
+ ug, err := strconv.Atoi(u.Gid)
+ if err != nil {
+ return nil, fmt.Errorf("user: list groups for %s: invalid gid %q", u.Username, u.Gid)
+ }
+ userGID := syscall.Gid_t(ug)
+ nameC, err := syscall.BytePtrFromString(u.Username)
+ if err != nil {
+ return nil, fmt.Errorf("user: invalid user name %q: %v", strconv.Quote(u.Username), err)
+ }
+
+ n := int32(256)
+ gidsC := make([]syscall.Gid_t, n)
+ syscall.Entersyscall()
+ rv := libc_getgrouplist(nameC, userGID, &gidsC[0], &n)
+ syscall.Exitsyscall()
+ if rv == -1 {
+ // More than initial buffer, but now n contains the correct size.
+ const maxGroups = 2048
+ if n > maxGroups {
+ return nil, fmt.Errorf("user: list groups for %s: member of more than %d groups", u.Username, maxGroups)
+ }
+ gidsC = make([]syscall.Gid_t, n)
+ syscall.Entersyscall()
+ rv := libc_getgrouplist(nameC, userGID, &gidsC[0], &n)
+ syscall.Exitsyscall()
+ if rv == -1 {
+ return nil, fmt.Errorf("user: list groups for %s failed (changed groups?)", u.Username)
+ }
+ }
+ gidsC = gidsC[:n]
+ gids := make([]string, 0, n)
+ for _, g := range gidsC[:n] {
+ gids = append(gids, strconv.Itoa(int(g)))
+ }
+ return gids, nil
+}
diff --git a/libgo/go/os/user/lookup.go b/libgo/go/os/user/lookup.go
index 09f00c7..3b4421b 100644
--- a/libgo/go/os/user/lookup.go
+++ b/libgo/go/os/user/lookup.go
@@ -12,11 +12,28 @@ func Current() (*User, error) {
// Lookup looks up a user by username. If the user cannot be found, the
// returned error is of type UnknownUserError.
func Lookup(username string) (*User, error) {
- return lookup(username)
+ return lookupUser(username)
}
// LookupId looks up a user by userid. If the user cannot be found, the
// returned error is of type UnknownUserIdError.
func LookupId(uid string) (*User, error) {
- return lookupId(uid)
+ return lookupUserId(uid)
+}
+
+// LookupGroup looks up a group by name. If the group cannot be found, the
+// returned error is of type UnknownGroupError.
+func LookupGroup(name string) (*Group, error) {
+ return lookupGroup(name)
+}
+
+// LookupGroupId looks up a group by groupid. If the group cannot be found, the
+// returned error is of type UnknownGroupIdError.
+func LookupGroupId(gid string) (*Group, error) {
+ return lookupGroupId(gid)
+}
+
+// GroupIds returns the list of group IDs that the user is a member of.
+func (u *User) GroupIds() ([]string, error) {
+ return listGroups(u)
}
diff --git a/libgo/go/os/user/lookup_android.go b/libgo/go/os/user/lookup_android.go
new file mode 100644
index 0000000..b1be3dc
--- /dev/null
+++ b/libgo/go/os/user/lookup_android.go
@@ -0,0 +1,38 @@
+// Copyright 2016 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 android
+
+package user
+
+import "errors"
+
+func init() {
+ userImplemented = false
+ groupImplemented = false
+}
+
+func current() (*User, error) {
+ return nil, errors.New("user: Current not implemented on android")
+}
+
+func lookupUser(string) (*User, error) {
+ return nil, errors.New("user: Lookup not implemented on android")
+}
+
+func lookupUserId(string) (*User, error) {
+ return nil, errors.New("user: LookupId not implemented on android")
+}
+
+func lookupGroup(string) (*Group, error) {
+ return nil, errors.New("user: LookupGroup not implemented on android")
+}
+
+func lookupGroupId(string) (*Group, error) {
+ return nil, errors.New("user: LookupGroupId not implemented on android")
+}
+
+func listGroups(*User) ([]string, error) {
+ return nil, errors.New("user: GroupIds not implemented on android")
+}
diff --git a/libgo/go/os/user/lookup_plan9.go b/libgo/go/os/user/lookup_plan9.go
index f7ef348..ea3ce0b 100644
--- a/libgo/go/os/user/lookup_plan9.go
+++ b/libgo/go/os/user/lookup_plan9.go
@@ -18,6 +18,10 @@ const (
userFile = "/dev/user"
)
+func init() {
+ groupImplemented = false
+}
+
func current() (*User, error) {
ubytes, err := ioutil.ReadFile(userFile)
if err != nil {
@@ -37,10 +41,22 @@ func current() (*User, error) {
return u, nil
}
-func lookup(username string) (*User, error) {
+func lookupUser(username string) (*User, error) {
+ return nil, syscall.EPLAN9
+}
+
+func lookupUserId(uid string) (*User, error) {
+ return nil, syscall.EPLAN9
+}
+
+func lookupGroup(groupname string) (*Group, error) {
+ return nil, syscall.EPLAN9
+}
+
+func lookupGroupId(string) (*Group, error) {
return nil, syscall.EPLAN9
}
-func lookupId(uid string) (*User, error) {
+func listGroups(*User) ([]string, error) {
return nil, syscall.EPLAN9
}
diff --git a/libgo/go/os/user/lookup_stubs.go b/libgo/go/os/user/lookup_stubs.go
index 4fb0e3c..ebf24f7 100644
--- a/libgo/go/os/user/lookup_stubs.go
+++ b/libgo/go/os/user/lookup_stubs.go
@@ -2,27 +2,83 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build !cgo,!windows,!plan9 android
+// +build !cgo,!windows,!plan9,!android
package user
import (
+ "errors"
"fmt"
+ "os"
"runtime"
+ "strconv"
)
func init() {
- implemented = false
+ userImplemented = false
+ groupImplemented = false
}
func current() (*User, error) {
- return nil, fmt.Errorf("user: Current not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+ u := &User{
+ Uid: currentUID(),
+ Gid: currentGID(),
+ Username: os.Getenv("USER"),
+ Name: "", // ignored
+ HomeDir: os.Getenv("HOME"),
+ }
+ if runtime.GOOS == "nacl" {
+ if u.Uid == "" {
+ u.Uid = "1"
+ }
+ if u.Username == "" {
+ u.Username = "nacl"
+ }
+ if u.HomeDir == "" {
+ u.HomeDir = "/home/nacl"
+ }
+ }
+ // 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)
}
-func lookup(username string) (*User, error) {
- return nil, fmt.Errorf("user: Lookup not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+func lookupUser(username string) (*User, error) {
+ return nil, errors.New("user: Lookup requires cgo")
}
-func lookupId(uid string) (*User, error) {
- return nil, fmt.Errorf("user: LookupId not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+func lookupUserId(uid string) (*User, error) {
+ return nil, errors.New("user: LookupId requires cgo")
+}
+
+func lookupGroup(groupname string) (*Group, error) {
+ return nil, errors.New("user: LookupGroup requires cgo")
+}
+
+func lookupGroupId(string) (*Group, error) {
+ return nil, errors.New("user: LookupGroupId requires cgo")
+}
+
+func listGroups(*User) ([]string, error) {
+ return nil, errors.New("user: GroupIds requires cgo")
+}
+
+func currentUID() string {
+ if id := os.Getuid(); id >= 0 {
+ return strconv.Itoa(id)
+ }
+ // Note: Windows returns -1, but this file isn't used on
+ // Windows anyway, so this empty return path shouldn't be
+ // used.
+ return ""
+}
+
+func currentGID() string {
+ if id := os.Getgid(); id >= 0 {
+ return strconv.Itoa(id)
+ }
+ return ""
}
diff --git a/libgo/go/os/user/lookup_unix.go b/libgo/go/os/user/lookup_unix.go
index bebb9e8..8881366 100644
--- a/libgo/go/os/user/lookup_unix.go
+++ b/libgo/go/os/user/lookup_unix.go
@@ -15,24 +15,6 @@ import (
"unsafe"
)
-/*
-#cgo solaris CFLAGS: -D_POSIX_PTHREAD_SEMANTICS
-#include <unistd.h>
-#include <sys/types.h>
-#include <pwd.h>
-#include <stdlib.h>
-
-static int mygetpwuid_r(int uid, struct passwd *pwd,
- char *buf, size_t buflen, struct passwd **result) {
- return getpwuid_r(uid, pwd, buf, buflen, result);
-}
-
-static int mygetpwnam_r(const char *name, struct passwd *pwd,
- char *buf, size_t buflen, struct passwd **result) {
- return getpwnam_r(name, pwd, buf, buflen, result);
-}
-*/
-
// bytePtrToString takes a NUL-terminated array of bytes and convert
// it to a Go string.
func bytePtrToString(p *byte) string {
@@ -45,58 +27,77 @@ func bytePtrToString(p *byte) string {
}
func current() (*User, error) {
- return lookupUnix(syscall.Getuid(), "", false)
+ return lookupUnixUid(syscall.Getuid())
}
-func lookup(username string) (*User, error) {
- return lookupUnix(-1, username, true)
+func lookupUser(username string) (*User, error) {
+ var pwd syscall.Passwd
+ var result *syscall.Passwd
+ p := syscall.StringBytePtr(username)
+
+ buf := alloc(userBuffer)
+ defer buf.free()
+
+ err := retryWithBuffer(buf, func() syscall.Errno {
+ syscall.Entersyscall()
+ rv := libc_getpwnam_r(p,
+ &pwd,
+ buf.ptr,
+ buf.size,
+ &result)
+ syscall.Exitsyscall()
+ if rv != 0 {
+ return syscall.GetErrno()
+ }
+ return 0
+ })
+ if err != nil {
+ return nil, fmt.Errorf("user: lookup username %s: %v", username, err)
+ }
+ if result == nil {
+ return nil, UnknownUserError(username)
+ }
+ return buildUser(&pwd), err
}
-func lookupId(uid string) (*User, error) {
+func lookupUserId(uid string) (*User, error) {
i, e := strconv.Atoi(uid)
if e != nil {
return nil, e
}
- return lookupUnix(i, "", false)
+ return lookupUnixUid(i)
}
-func lookupUnix(uid int, username string, lookupByName bool) (*User, error) {
+func lookupUnixUid(uid int) (*User, error) {
var pwd syscall.Passwd
var result *syscall.Passwd
- // FIXME: Should let buf grow if necessary.
- const bufSize = 1024
- buf := make([]byte, bufSize)
- if lookupByName {
- nameC := syscall.StringBytePtr(username)
- syscall.Entersyscall()
- rv := libc_getpwnam_r(nameC,
- &pwd,
- &buf[0],
- bufSize,
- &result)
- syscall.Exitsyscall()
- if rv != 0 {
- return nil, fmt.Errorf("user: lookup username %s: %s", username, syscall.GetErrno())
- }
- if result == nil {
- return nil, UnknownUserError(username)
- }
- } else {
+ buf := alloc(userBuffer)
+ defer buf.free()
+
+ err := retryWithBuffer(buf, func() syscall.Errno {
syscall.Entersyscall()
rv := libc_getpwuid_r(syscall.Uid_t(uid),
&pwd,
- &buf[0],
- bufSize,
+ buf.ptr,
+ buf.size,
&result)
syscall.Exitsyscall()
if rv != 0 {
- return nil, fmt.Errorf("user: lookup userid %d: %s", uid, syscall.GetErrno())
- }
- if result == nil {
- return nil, UnknownUserIdError(uid)
+ return syscall.GetErrno()
}
+ return 0
+ })
+ if err != nil {
+ return nil, fmt.Errorf("user: lookup userid %d: %v", uid, err)
+ }
+ if result == nil {
+ return nil, UnknownUserIdError(uid)
}
+ return buildUser(&pwd), nil
+}
+
+func buildUser(pwd *syscall.Passwd) *User {
u := &User{
Uid: strconv.Itoa(int(pwd.Pw_uid)),
Gid: strconv.Itoa(int(pwd.Pw_gid)),
@@ -104,12 +105,162 @@ func lookupUnix(uid int, username string, lookupByName bool) (*User, error) {
Name: bytePtrToString((*byte)(unsafe.Pointer(pwd.Pw_gecos))),
HomeDir: bytePtrToString((*byte)(unsafe.Pointer(pwd.Pw_dir))),
}
- // The pw_gecos field isn't quite standardized. Some docs
+ // The pw_gecos field isn't quite standardized. Some docs
// say: "It is expected to be a comma separated list of
// personal data where the first item is the full name of the
// user."
if i := strings.Index(u.Name, ","); i >= 0 {
u.Name = u.Name[:i]
}
- return u, nil
+ return u
+}
+
+func currentGroup() (*Group, error) {
+ return lookupUnixGid(syscall.Getgid())
+}
+
+func lookupGroup(groupname string) (*Group, error) {
+ var grp syscall.Group
+ var result *syscall.Group
+
+ buf := alloc(groupBuffer)
+ defer buf.free()
+ p := syscall.StringBytePtr(groupname)
+
+ err := retryWithBuffer(buf, func() syscall.Errno {
+ syscall.Entersyscall()
+ rv := libc_getgrnam_r(p,
+ &grp,
+ buf.ptr,
+ buf.size,
+ &result)
+ syscall.Exitsyscall()
+ if rv != 0 {
+ return syscall.GetErrno()
+ }
+ return 0
+ })
+ if err != nil {
+ return nil, fmt.Errorf("user: lookup groupname %s: %v", groupname, err)
+ }
+ if result == nil {
+ return nil, UnknownGroupError(groupname)
+ }
+ return buildGroup(&grp), nil
+}
+
+func lookupGroupId(gid string) (*Group, error) {
+ i, e := strconv.Atoi(gid)
+ if e != nil {
+ return nil, e
+ }
+ return lookupUnixGid(i)
+}
+
+func lookupUnixGid(gid int) (*Group, error) {
+ var grp syscall.Group
+ var result *syscall.Group
+
+ buf := alloc(groupBuffer)
+ defer buf.free()
+
+ err := retryWithBuffer(buf, func() syscall.Errno {
+ syscall.Entersyscall()
+ rv := libc_getgrgid_r(syscall.Gid_t(gid),
+ &grp,
+ buf.ptr,
+ buf.size,
+ &result)
+ syscall.Exitsyscall()
+ if rv != 0 {
+ return syscall.GetErrno()
+ }
+ return 0
+ })
+ if err != nil {
+ return nil, fmt.Errorf("user: lookup groupid %d: %v", gid, err)
+ }
+ if result == nil {
+ return nil, UnknownGroupIdError(strconv.Itoa(gid))
+ }
+ return buildGroup(&grp), nil
+}
+
+func buildGroup(grp *syscall.Group) *Group {
+ g := &Group{
+ Gid: strconv.Itoa(int(grp.Gr_gid)),
+ Name: bytePtrToString((*byte)(unsafe.Pointer(grp.Gr_name))),
+ }
+ return g
+}
+
+type bufferKind int
+
+const (
+ userBuffer = bufferKind(syscall.SC_GETPW_R_SIZE_MAX)
+ groupBuffer = bufferKind(syscall.SC_GETGR_R_SIZE_MAX)
+)
+
+func (k bufferKind) initialSize() syscall.Size_t {
+ sz, _ := syscall.Sysconf(int(k))
+ if sz == -1 {
+ // DragonFly and FreeBSD do not have _SC_GETPW_R_SIZE_MAX.
+ // Additionally, not all Linux systems have it, either. For
+ // example, the musl libc returns -1.
+ return 1024
+ }
+ if !isSizeReasonable(int64(sz)) {
+ // Truncate. If this truly isn't enough, retryWithBuffer will error on the first run.
+ return maxBufferSize
+ }
+ return syscall.Size_t(sz)
+}
+
+type memBuffer struct {
+ ptr *byte
+ size syscall.Size_t
+}
+
+func alloc(kind bufferKind) *memBuffer {
+ sz := kind.initialSize()
+ b := make([]byte, sz)
+ return &memBuffer{
+ ptr: &b[0],
+ size: sz,
+ }
+}
+
+func (mb *memBuffer) resize(newSize syscall.Size_t) {
+ b := make([]byte, newSize)
+ mb.ptr = &b[0]
+ mb.size = newSize
+}
+
+func (mb *memBuffer) free() {
+ mb.ptr = nil
+}
+
+// retryWithBuffer repeatedly calls f(), increasing the size of the
+// buffer each time, until f succeeds, fails with a non-ERANGE error,
+// or the buffer exceeds a reasonable limit.
+func retryWithBuffer(buf *memBuffer, f func() syscall.Errno) error {
+ for {
+ errno := f()
+ if errno == 0 {
+ return nil
+ } else if errno != syscall.ERANGE {
+ return errno
+ }
+ newSize := buf.size * 2
+ if !isSizeReasonable(int64(newSize)) {
+ return fmt.Errorf("internal buffer exceeds %d bytes", maxBufferSize)
+ }
+ buf.resize(newSize)
+ }
+}
+
+const maxBufferSize = 1 << 20
+
+func isSizeReasonable(sz int64) bool {
+ return sz > 0 && sz <= maxBufferSize
}
diff --git a/libgo/go/os/user/lookup_windows.go b/libgo/go/os/user/lookup_windows.go
index 99c325f..4e36a5c 100644
--- a/libgo/go/os/user/lookup_windows.go
+++ b/libgo/go/os/user/lookup_windows.go
@@ -5,11 +5,16 @@
package user
import (
+ "errors"
"fmt"
"syscall"
"unsafe"
)
+func init() {
+ groupImplemented = false
+}
+
func isDomainJoined() (bool, error) {
var domain *uint16
var status uint32
@@ -61,7 +66,7 @@ func lookupFullName(domain, username, domainAndUser string) (string, error) {
if err == nil {
return name, nil
}
- // domain worked neigher as a domain nor as a server
+ // domain worked neither as a domain nor as a server
// could be domain server unavailable
// pretend username is fullname
return username, nil
@@ -129,7 +134,7 @@ func newUserFromSid(usid *syscall.SID) (*User, error) {
return newUser(usid, gid, dir)
}
-func lookup(username string) (*User, error) {
+func lookupUser(username string) (*User, error) {
sid, _, t, e := syscall.LookupSID("", username)
if e != nil {
return nil, e
@@ -140,10 +145,22 @@ func lookup(username string) (*User, error) {
return newUserFromSid(sid)
}
-func lookupId(uid string) (*User, error) {
+func lookupUserId(uid string) (*User, error) {
sid, e := syscall.StringToSid(uid)
if e != nil {
return nil, e
}
return newUserFromSid(sid)
}
+
+func lookupGroup(groupname string) (*Group, error) {
+ return nil, errors.New("user: LookupGroup not implemented on windows")
+}
+
+func lookupGroupId(string) (*Group, error) {
+ return nil, errors.New("user: LookupGroupId not implemented on windows")
+}
+
+func listGroups(*User) ([]string, error) {
+ return nil, errors.New("user: GroupIds not implemented on windows")
+}
diff --git a/libgo/go/os/user/user.go b/libgo/go/os/user/user.go
index e8680fe..7b44397 100644
--- a/libgo/go/os/user/user.go
+++ b/libgo/go/os/user/user.go
@@ -9,23 +9,35 @@ import (
"strconv"
)
-var implemented = true // set to false by lookup_stubs.go's init
+var (
+ userImplemented = true // set to false by lookup_stubs.go's init
+ groupImplemented = true // set to false by lookup_stubs.go's init
+)
// User represents a user account.
//
-// On posix systems Uid and Gid contain a decimal number
+// On POSIX systems Uid and Gid contain a decimal number
// representing uid and gid. On windows Uid and Gid
// contain security identifier (SID) in a string format.
// On Plan 9, Uid, Gid, Username, and Name will be the
// contents of /dev/user.
type User struct {
- Uid string // user id
- Gid string // primary group id
+ Uid string // user ID
+ Gid string // primary group ID
Username string
Name string
HomeDir string
}
+// Group represents a grouping of users.
+//
+// On POSIX systems Gid contains a decimal number
+// representing the group ID.
+type Group struct {
+ Gid string // group ID
+ Name string // group name
+}
+
// UnknownUserIdError is returned by LookupId when
// a user cannot be found.
type UnknownUserIdError int
@@ -41,3 +53,19 @@ type UnknownUserError string
func (e UnknownUserError) Error() string {
return "user: unknown user " + string(e)
}
+
+// UnknownGroupIdError is returned by LookupGroupId when
+// a group cannot be found.
+type UnknownGroupIdError string
+
+func (e UnknownGroupIdError) Error() string {
+ return "group: unknown groupid " + string(e)
+}
+
+// UnknownGroupError is returned by LookupGroup when
+// a group cannot be found.
+type UnknownGroupError string
+
+func (e UnknownGroupError) Error() string {
+ return "group: unknown group " + string(e)
+}
diff --git a/libgo/go/os/user/user_test.go b/libgo/go/os/user/user_test.go
index 9d9420e..9d8d94d 100644
--- a/libgo/go/os/user/user_test.go
+++ b/libgo/go/os/user/user_test.go
@@ -9,18 +9,19 @@ import (
"testing"
)
-func check(t *testing.T) {
- if !implemented {
+func checkUser(t *testing.T) {
+ if !userImplemented {
t.Skip("user: not implemented; skipping tests")
}
}
func TestCurrent(t *testing.T) {
- check(t)
-
+ if runtime.GOOS == "android" {
+ t.Skipf("skipping on %s", runtime.GOOS)
+ }
u, err := Current()
if err != nil {
- t.Fatalf("Current: %v", err)
+ t.Fatalf("Current: %v (got %#v)", err, u)
}
if u.HomeDir == "" {
t.Errorf("didn't get a HomeDir")
@@ -53,7 +54,7 @@ func compare(t *testing.T, want, got *User) {
}
func TestLookup(t *testing.T) {
- check(t)
+ checkUser(t)
if runtime.GOOS == "plan9" {
t.Skipf("Lookup not implemented on %q", runtime.GOOS)
@@ -71,7 +72,7 @@ func TestLookup(t *testing.T) {
}
func TestLookupId(t *testing.T) {
- check(t)
+ checkUser(t)
if runtime.GOOS == "plan9" {
t.Skipf("LookupId not implemented on %q", runtime.GOOS)
@@ -87,3 +88,64 @@ func TestLookupId(t *testing.T) {
}
compare(t, want, got)
}
+
+func checkGroup(t *testing.T) {
+ if !groupImplemented {
+ t.Skip("user: group not implemented; skipping test")
+ }
+}
+
+func TestLookupGroup(t *testing.T) {
+ checkGroup(t)
+ user, err := Current()
+ if err != nil {
+ t.Fatalf("Current(): %v", err)
+ }
+
+ g1, err := LookupGroupId(user.Gid)
+ if err != nil {
+ // NOTE(rsc): Maybe the group isn't defined. That's fine.
+ // On my OS X laptop, rsc logs in with group 5000 even
+ // though there's no name for group 5000. Such is Unix.
+ t.Logf("LookupGroupId(%q): %v", user.Gid, err)
+ return
+ }
+ if g1.Gid != user.Gid {
+ t.Errorf("LookupGroupId(%q).Gid = %s; want %s", user.Gid, g1.Gid, user.Gid)
+ }
+
+ g2, err := LookupGroup(g1.Name)
+ if err != nil {
+ t.Fatalf("LookupGroup(%q): %v", g1.Name, err)
+ }
+ if g1.Gid != g2.Gid || g1.Name != g2.Name {
+ t.Errorf("LookupGroup(%q) = %+v; want %+v", g1.Name, g2, g1)
+ }
+}
+
+func TestGroupIds(t *testing.T) {
+ checkGroup(t)
+ if runtime.GOOS == "solaris" {
+ t.Skip("skipping GroupIds, see golang.org/issue/14709")
+ }
+ user, err := Current()
+ if err != nil {
+ t.Fatalf("Current(): %v", err)
+ }
+ gids, err := user.GroupIds()
+ if err != nil {
+ t.Fatalf("%+v.GroupIds(): %v", user, err)
+ }
+ if !containsID(gids, user.Gid) {
+ t.Errorf("%+v.GroupIds() = %v; does not contain user GID %s", user, gids, user.Gid)
+ }
+}
+
+func containsID(ids []string, id string) bool {
+ for _, x := range ids {
+ if x == id {
+ return true
+ }
+ }
+ return false
+}
diff --git a/libgo/go/os/wait_unimp.go b/libgo/go/os/wait_unimp.go
new file mode 100644
index 0000000..7059e59
--- /dev/null
+++ b/libgo/go/os/wait_unimp.go
@@ -0,0 +1,16 @@
+// Copyright 2016 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 dragonfly nacl netbsd openbsd solaris
+
+package os
+
+// blockUntilWaitable attempts to block until a call to p.Wait will
+// succeed immediately, and returns whether it has done so.
+// It does not actually call p.Wait.
+// This version is used on systems that do not implement waitid,
+// or where we have not implemented it yet.
+func (p *Process) blockUntilWaitable() (bool, error) {
+ return false, nil
+}
diff --git a/libgo/go/os/wait_wait6.go b/libgo/go/os/wait_wait6.go
new file mode 100644
index 0000000..7f4780a
--- /dev/null
+++ b/libgo/go/os/wait_wait6.go
@@ -0,0 +1,40 @@
+// Copyright 2016 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 freebsd
+
+package os
+
+import (
+ "runtime"
+ "syscall"
+)
+
+const _P_PID = 0
+
+// blockUntilWaitable attempts to block until a call to p.Wait will
+// succeed immediately, and returns whether it has done so.
+// It does not actually call p.Wait.
+func (p *Process) blockUntilWaitable() (bool, error) {
+ var errno syscall.Errno
+ // The arguments on 32-bit FreeBSD look like the following:
+ // - freebsd32_wait6_args{ idtype, id1, id2, status, options, wrusage, info } or
+ // - freebsd32_wait6_args{ idtype, pad, id1, id2, status, options, wrusage, info } when PAD64_REQUIRED=1 on ARM, MIPS or PowerPC
+ if runtime.GOARCH == "386" {
+ _, _, errno = syscall.Syscall9(syscall.SYS_WAIT6, _P_PID, uintptr(p.Pid), 0, 0, syscall.WEXITED|syscall.WNOWAIT, 0, 0, 0, 0)
+ } else if runtime.GOARCH == "arm" {
+ _, _, errno = syscall.Syscall9(syscall.SYS_WAIT6, _P_PID, 0, uintptr(p.Pid), 0, 0, syscall.WEXITED|syscall.WNOWAIT, 0, 0, 0)
+ } else {
+ _, _, errno = syscall.Syscall6(syscall.SYS_WAIT6, _P_PID, uintptr(p.Pid), 0, syscall.WEXITED|syscall.WNOWAIT, 0, 0)
+ }
+ 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
+}
diff --git a/libgo/go/os/wait_waitid.go b/libgo/go/os/wait_waitid.go
new file mode 100644
index 0000000..5dbd7f9
--- /dev/null
+++ b/libgo/go/os/wait_waitid.go
@@ -0,0 +1,34 @@
+// Copyright 2016 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 linux
+
+package os
+
+import (
+ "runtime"
+ "syscall"
+ "unsafe"
+)
+
+const _P_PID = 1
+
+// blockUntilWaitable attempts to block until a call to p.Wait will
+// succeed immediately, and returns whether it has done so.
+// It does not actually call p.Wait.
+func (p *Process) blockUntilWaitable() (bool, error) {
+ // The waitid system call expects a pointer to a siginfo_t,
+ // which is 128 bytes on all GNU/Linux systems.
+ // On Darwin, it requires greater than or equal to 64 bytes
+ // for darwin/{386,arm} and 104 bytes for darwin/amd64.
+ // We don't care about the values it returns.
+ var siginfo [128]byte
+ psig := &siginfo[0]
+ _, _, e := syscall.Syscall6(syscall.SYS_WAITID, _P_PID, uintptr(p.Pid), uintptr(unsafe.Pointer(psig)), syscall.WEXITED|syscall.WNOWAIT, 0, 0)
+ runtime.KeepAlive(psig)
+ if e != 0 {
+ return false, NewSyscallError("waitid", e)
+ }
+ return true, nil
+}