summaryrefslogtreecommitdiff
path: root/libgo/go/os
diff options
context:
space:
mode:
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2015-10-31 00:59:47 +0000
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2015-10-31 00:59:47 +0000
commit4a3da3a8a45d5496118798146de1fa4e5798ed5a (patch)
tree13beeaed3698c61903fe93fb1ce70bd9b18d4e7f /libgo/go/os
parentcd529f4d86a17a3e8959f2cb5ac7132a841ab6f1 (diff)
downloadgcc-4a3da3a8a45d5496118798146de1fa4e5798ed5a.tar.gz
runtime: Remove now unnecessary pad field from ParFor.
It is not needed due to the removal of the ctx field. Reviewed-on: https://go-review.googlesource.com/16525 git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@229616 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/os')
-rw-r--r--libgo/go/os/env.go13
-rw-r--r--libgo/go/os/env_test.go17
-rw-r--r--libgo/go/os/exec.go4
-rw-r--r--libgo/go/os/exec/exec.go20
-rw-r--r--libgo/go/os/exec/exec_posix.go24
-rw-r--r--libgo/go/os/exec/exec_test.go123
-rw-r--r--libgo/go/os/exec_posix.go31
-rw-r--r--libgo/go/os/exec_windows.go18
-rw-r--r--libgo/go/os/file.go18
-rw-r--r--libgo/go/os/file_plan9.go8
-rw-r--r--libgo/go/os/file_posix.go10
-rw-r--r--libgo/go/os/file_unix.go34
-rw-r--r--libgo/go/os/os_test.go400
-rw-r--r--libgo/go/os/os_unix_test.go114
-rw-r--r--libgo/go/os/path_plan9.go2
-rw-r--r--libgo/go/os/path_test.go16
-rw-r--r--libgo/go/os/path_unix.go2
-rw-r--r--libgo/go/os/path_windows.go2
-rw-r--r--libgo/go/os/proc.go14
-rw-r--r--libgo/go/os/signal/signal.go59
-rw-r--r--libgo/go/os/signal/signal_plan9.go60
-rw-r--r--libgo/go/os/signal/signal_plan9_test.go181
-rw-r--r--libgo/go/os/signal/signal_stub.go17
-rw-r--r--libgo/go/os/signal/signal_test.go66
-rw-r--r--libgo/go/os/signal/signal_unix.go5
-rw-r--r--libgo/go/os/stat_plan9.go40
-rw-r--r--libgo/go/os/sticky_bsd.go11
-rw-r--r--libgo/go/os/sticky_notbsd.go14
-rw-r--r--libgo/go/os/str.go25
-rw-r--r--libgo/go/os/types.go2
-rw-r--r--libgo/go/os/user/lookup_unix.go12
31 files changed, 1133 insertions, 229 deletions
diff --git a/libgo/go/os/env.go b/libgo/go/os/env.go
index d0494a47634..a4ede15e613 100644
--- a/libgo/go/os/env.go
+++ b/libgo/go/os/env.go
@@ -33,7 +33,7 @@ func ExpandEnv(s string) string {
return Expand(s, Getenv)
}
-// isSpellSpecialVar reports whether the character identifies a special
+// isShellSpecialVar reports whether the character identifies a special
// shell variable such as $*.
func isShellSpecialVar(c uint8) bool {
switch c {
@@ -48,7 +48,7 @@ func isAlphaNum(c uint8) bool {
return c == '_' || '0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z'
}
-// getName returns the name that begins the string and the number of bytes
+// 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 ${}
// expansion and two more bytes are needed than the length of the name.
func getShellName(s string) (string, int) {
@@ -81,6 +81,15 @@ func Getenv(key string) string {
return v
}
+// LookupEnv retrieves the value of the environment variable named
+// by the key. If the variable is present in the environment the
+// value (which may be empty) is returned and the boolean is true.
+// Otherwise the returned value will be empty and the boolean will
+// be false.
+func LookupEnv(key string) (string, bool) {
+ return syscall.Getenv(key)
+}
+
// Setenv sets the value of the environment variable named by the key.
// It returns an error, if any.
func Setenv(key, value string) error {
diff --git a/libgo/go/os/env_test.go b/libgo/go/os/env_test.go
index e6180675137..d1074cdc60a 100644
--- a/libgo/go/os/env_test.go
+++ b/libgo/go/os/env_test.go
@@ -94,3 +94,20 @@ func TestUnsetenv(t *testing.T) {
t.Fatal("Unsetenv didn't clear TestUnsetenv")
}
}
+
+func TestLookupEnv(t *testing.T) {
+ const smallpox = "SMALLPOX" // No one has smallpox.
+ value, ok := LookupEnv(smallpox) // Should not exist.
+ if ok || value != "" {
+ t.Fatalf("%s=%q", smallpox, value)
+ }
+ defer Unsetenv(smallpox)
+ err := Setenv(smallpox, "virus")
+ if err != nil {
+ t.Fatalf("failed to release smallpox virus")
+ }
+ value, ok = LookupEnv(smallpox)
+ if !ok {
+ t.Errorf("smallpox release failed; world remains safe but LookupEnv is broken")
+ }
+}
diff --git a/libgo/go/os/exec.go b/libgo/go/os/exec.go
index 5aea3098b54..15e95b91722 100644
--- a/libgo/go/os/exec.go
+++ b/libgo/go/os/exec.go
@@ -13,8 +13,8 @@ import (
// Process stores the information about a process created by StartProcess.
type Process struct {
Pid int
- handle uintptr
- 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
}
func newProcess(pid int, handle uintptr) *Process {
diff --git a/libgo/go/os/exec/exec.go b/libgo/go/os/exec/exec.go
index 72b4905d560..8a84e263dc8 100644
--- a/libgo/go/os/exec/exec.go
+++ b/libgo/go/os/exec/exec.go
@@ -32,6 +32,9 @@ func (e *Error) Error() string {
}
// Cmd represents an external command being prepared or run.
+//
+// A Cmd cannot be reused after calling its Run, Output or CombinedOutput
+// methods.
type Cmd struct {
// Path is the path of the command to run.
//
@@ -80,8 +83,8 @@ type Cmd struct {
// new process. It does not include standard input, standard output, or
// standard error. If non-nil, entry i becomes file descriptor 3+i.
//
- // BUG: on OS X 10.6, child processes may sometimes inherit unwanted fds.
- // http://golang.org/issue/2603
+ // BUG(rsc): On OS X 10.6, child processes may sometimes inherit unwanted fds.
+ // https://golang.org/issue/2603
ExtraFiles []*os.File
// SysProcAttr holds optional, operating system-specific attributes.
@@ -154,6 +157,11 @@ func (c *Cmd) argv() []string {
return []string{c.Path}
}
+// skipStdinCopyError optionally specifies a function which reports
+// whether the provided the stdin copy error should be ignored.
+// It is non-nil everywhere but Plan 9, which lacks EPIPE. See exec_posix.go.
+var skipStdinCopyError func(error) bool
+
func (c *Cmd) stdin() (f *os.File, err error) {
if c.Stdin == nil {
f, err = os.Open(os.DevNull)
@@ -177,6 +185,9 @@ func (c *Cmd) stdin() (f *os.File, err error) {
c.closeAfterWait = append(c.closeAfterWait, pw)
c.goroutine = append(c.goroutine, func() error {
_, err := io.Copy(pw, c.Stdin)
+ if skip := skipStdinCopyError; skip != nil && skip(err) {
+ err = nil
+ }
if err1 := pw.Close(); err == nil {
err = err1
}
@@ -219,6 +230,7 @@ func (c *Cmd) writerDescriptor(w io.Writer) (f *os.File, err error) {
c.closeAfterWait = append(c.closeAfterWait, pr)
c.goroutine = append(c.goroutine, func() error {
_, err := io.Copy(w, pr)
+ pr.Close() // in case io.Copy stopped due to write error
return err
})
return pw, nil
@@ -352,6 +364,10 @@ 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.
+//
// Wait releases any resources associated with the Cmd.
func (c *Cmd) Wait() error {
if c.Process == nil {
diff --git a/libgo/go/os/exec/exec_posix.go b/libgo/go/os/exec/exec_posix.go
new file mode 100644
index 00000000000..5e1113748cd
--- /dev/null
+++ b/libgo/go/os/exec/exec_posix.go
@@ -0,0 +1,24 @@
+// Copyright 2015 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 !plan9
+
+package exec
+
+import (
+ "os"
+ "syscall"
+)
+
+func init() {
+ skipStdinCopyError = func(err error) bool {
+ // Ignore EPIPE errors copying to stdin if the program
+ // completed successfully otherwise.
+ // See Issue 9173.
+ pe, ok := err.(*os.PathError)
+ return ok &&
+ pe.Op == "write" && pe.Path == "|1" &&
+ pe.Err == syscall.EPIPE
+ }
+}
diff --git a/libgo/go/os/exec/exec_test.go b/libgo/go/os/exec/exec_test.go
index f9ffde602ca..f4c025e839f 100644
--- a/libgo/go/os/exec/exec_test.go
+++ b/libgo/go/os/exec/exec_test.go
@@ -11,6 +11,7 @@ import (
"bufio"
"bytes"
"fmt"
+ "internal/testenv"
"io"
"io/ioutil"
"log"
@@ -28,9 +29,8 @@ import (
)
func helperCommand(t *testing.T, s ...string) *exec.Cmd {
- if runtime.GOOS == "nacl" {
- t.Skip("skipping on nacl")
- }
+ testenv.MustHaveExec(t)
+
cs := []string{"-test.run=TestHelperProcess", "--"}
cs = append(cs, s...)
cmd := exec.Command(os.Args[0], cs...)
@@ -53,6 +53,8 @@ func TestEcho(t *testing.T) {
}
func TestCommandRelativeName(t *testing.T) {
+ testenv.MustHaveExec(t)
+
// Run our own binary as a relative path
// (e.g. "_test/exec.test") our parent directory.
base := filepath.Base(os.Args[0]) // "exec.test"
@@ -250,6 +252,12 @@ func TestPipeLookPathLeak(t *testing.T) {
}
func numOpenFDS(t *testing.T) (n int, lsof []byte) {
+ if runtime.GOOS == "android" {
+ // Android's stock lsof does not obey the -p option,
+ // so extra filtering is needed. (golang.org/issue/10206)
+ return numOpenFDsAndroid(t)
+ }
+
lsof, err := exec.Command("lsof", "-b", "-n", "-p", strconv.Itoa(os.Getpid())).Output()
if err != nil {
t.Skip("skipping test; error finding or running lsof")
@@ -257,6 +265,45 @@ func numOpenFDS(t *testing.T) (n int, lsof []byte) {
return bytes.Count(lsof, []byte("\n")), lsof
}
+func numOpenFDsAndroid(t *testing.T) (n int, lsof []byte) {
+ raw, err := exec.Command("lsof").Output()
+ if err != nil {
+ t.Skip("skipping test; error finding or running lsof")
+ }
+
+ // First find the PID column index by parsing the first line, and
+ // select lines containing pid in the column.
+ pid := []byte(strconv.Itoa(os.Getpid()))
+ pidCol := -1
+
+ s := bufio.NewScanner(bytes.NewReader(raw))
+ for s.Scan() {
+ line := s.Bytes()
+ fields := bytes.Fields(line)
+ if pidCol < 0 {
+ for i, v := range fields {
+ if bytes.Equal(v, []byte("PID")) {
+ pidCol = i
+ break
+ }
+ }
+ lsof = append(lsof, line...)
+ continue
+ }
+ if bytes.Equal(fields[pidCol], pid) {
+ lsof = append(lsof, '\n')
+ lsof = append(lsof, line...)
+ }
+ }
+ if pidCol < 0 {
+ t.Fatal("error processing lsof output: unexpected header format")
+ }
+ if err := s.Err(); err != nil {
+ t.Fatalf("error processing lsof output: %v", err)
+ }
+ return bytes.Count(lsof, []byte("\n")), lsof
+}
+
var testedAlreadyLeaked = false
// basefds returns the number of expected file descriptors
@@ -275,15 +322,15 @@ func closeUnexpectedFds(t *testing.T, m string) {
}
func TestExtraFilesFDShuffle(t *testing.T) {
- t.Skip("flaky test; see http://golang.org/issue/5780")
+ t.Skip("flaky test; see https://golang.org/issue/5780")
switch runtime.GOOS {
case "darwin":
- // TODO(cnicolaou): http://golang.org/issue/2603
+ // TODO(cnicolaou): https://golang.org/issue/2603
// leads to leaked file descriptors in this test when it's
// run from a builder.
closeUnexpectedFds(t, "TestExtraFilesFDShuffle")
case "netbsd":
- // http://golang.org/issue/3955
+ // https://golang.org/issue/3955
closeUnexpectedFds(t, "TestExtraFilesFDShuffle")
case "windows":
t.Skip("no operating system support; skipping")
@@ -379,8 +426,9 @@ func TestExtraFilesFDShuffle(t *testing.T) {
}
func TestExtraFiles(t *testing.T) {
- switch runtime.GOOS {
- case "nacl", "windows":
+ testenv.MustHaveExec(t)
+
+ if runtime.GOOS == "windows" {
t.Skipf("skipping test on %q", runtime.GOOS)
}
@@ -608,21 +656,21 @@ func TestHelperProcess(*testing.T) {
// file descriptors...
case "darwin":
// TODO(bradfitz): broken? Sometimes.
- // http://golang.org/issue/2603
+ // https://golang.org/issue/2603
// Skip this additional part of the test for now.
case "netbsd":
// TODO(jsing): This currently fails on NetBSD due to
// the cloned file descriptors that result from opening
// /dev/urandom.
- // http://golang.org/issue/3955
+ // https://golang.org/issue/3955
case "plan9":
// TODO(0intro): Determine why Plan 9 is leaking
// file descriptors.
- // http://golang.org/issue/7118
+ // 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,
- // see: http://golang.org/issue/2603
+ // see: https://golang.org/issue/2603
default:
// Now verify that there are no other open fds.
var files []*os.File
@@ -721,3 +769,54 @@ func TestHelperProcess(*testing.T) {
os.Exit(2)
}
}
+
+// Issue 9173: ignore stdin pipe writes if the program completes successfully.
+func TestIgnorePipeErrorOnSuccess(t *testing.T) {
+ testenv.MustHaveExec(t)
+
+ // We really only care about testing this on Unixy things.
+ if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
+ t.Skipf("skipping test on %q", runtime.GOOS)
+ }
+
+ cmd := helperCommand(t, "echo", "foo")
+ var out bytes.Buffer
+ cmd.Stdin = strings.NewReader(strings.Repeat("x", 10<<20))
+ cmd.Stdout = &out
+ if err := cmd.Run(); err != nil {
+ t.Fatal(err)
+ }
+ if got, want := out.String(), "foo\n"; got != want {
+ t.Errorf("output = %q; want %q", got, want)
+ }
+}
+
+type badWriter struct{}
+
+func (w *badWriter) Write(data []byte) (int, error) {
+ return 0, io.ErrUnexpectedEOF
+}
+
+func TestClosePipeOnCopyError(t *testing.T) {
+ testenv.MustHaveExec(t)
+
+ if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
+ t.Skipf("skipping test on %s - no yes command", runtime.GOOS)
+ }
+ cmd := exec.Command("yes")
+ cmd.Stdout = new(badWriter)
+ c := make(chan int, 1)
+ go func() {
+ err := cmd.Run()
+ if err == nil {
+ t.Errorf("yes completed successfully")
+ }
+ c <- 1
+ }()
+ select {
+ case <-c:
+ // ok
+ case <-time.After(5 * time.Second):
+ t.Fatalf("yes got stuck writing to bad writer")
+ }
+}
diff --git a/libgo/go/os/exec_posix.go b/libgo/go/os/exec_posix.go
index fb9d291e664..94dd04beb23 100644
--- a/libgo/go/os/exec_posix.go
+++ b/libgo/go/os/exec_posix.go
@@ -81,33 +81,6 @@ func (p *ProcessState) sysUsage() interface{} {
return p.rusage
}
-// Convert i to decimal string.
-func itod(i int) string {
- if i == 0 {
- return "0"
- }
-
- u := uint64(i)
- if i < 0 {
- u = -u
- }
-
- // Assemble decimal in reverse order.
- var b [32]byte
- bp := len(b)
- for ; u > 0; u /= 10 {
- bp--
- b[bp] = byte(u%10) + '0'
- }
-
- if i < 0 {
- bp--
- b[bp] = '-'
- }
-
- return string(b[bp:])
-}
-
func (p *ProcessState) String() string {
if p == nil {
return "<nil>"
@@ -116,13 +89,13 @@ func (p *ProcessState) String() string {
res := ""
switch {
case status.Exited():
- res = "exit status " + itod(status.ExitStatus())
+ res = "exit status " + itoa(status.ExitStatus())
case status.Signaled():
res = "signal: " + status.Signal().String()
case status.Stopped():
res = "stop signal: " + status.StopSignal().String()
if status.StopSignal() == syscall.SIGTRAP && status.TrapCause() != 0 {
- res += " (trap " + itod(status.TrapCause()) + ")"
+ res += " (trap " + itoa(status.TrapCause()) + ")"
}
case status.Continued():
res = "continued"
diff --git a/libgo/go/os/exec_windows.go b/libgo/go/os/exec_windows.go
index 393393b2375..3264271b2e9 100644
--- a/libgo/go/os/exec_windows.go
+++ b/libgo/go/os/exec_windows.go
@@ -7,13 +7,15 @@ package os
import (
"errors"
"runtime"
+ "sync/atomic"
"syscall"
"time"
"unsafe"
)
func (p *Process) wait() (ps *ProcessState, err error) {
- s, e := syscall.WaitForSingleObject(syscall.Handle(p.handle), syscall.INFINITE)
+ handle := atomic.LoadUintptr(&p.handle)
+ s, e := syscall.WaitForSingleObject(syscall.Handle(handle), syscall.INFINITE)
switch s {
case syscall.WAIT_OBJECT_0:
break
@@ -23,12 +25,12 @@ func (p *Process) wait() (ps *ProcessState, err error) {
return nil, errors.New("os: unexpected result from WaitForSingleObject")
}
var ec uint32
- e = syscall.GetExitCodeProcess(syscall.Handle(p.handle), &ec)
+ e = syscall.GetExitCodeProcess(syscall.Handle(handle), &ec)
if e != nil {
return nil, NewSyscallError("GetExitCodeProcess", e)
}
var u syscall.Rusage
- e = syscall.GetProcessTimes(syscall.Handle(p.handle), &u.CreationTime, &u.ExitTime, &u.KernelTime, &u.UserTime)
+ e = syscall.GetProcessTimes(syscall.Handle(handle), &u.CreationTime, &u.ExitTime, &u.KernelTime, &u.UserTime)
if e != nil {
return nil, NewSyscallError("GetProcessTimes", e)
}
@@ -53,7 +55,8 @@ func terminateProcess(pid, exitcode int) error {
}
func (p *Process) signal(sig Signal) error {
- if p.handle == uintptr(syscall.InvalidHandle) {
+ handle := atomic.LoadUintptr(&p.handle)
+ if handle == uintptr(syscall.InvalidHandle) {
return syscall.EINVAL
}
if p.done() {
@@ -67,14 +70,15 @@ func (p *Process) signal(sig Signal) error {
}
func (p *Process) release() error {
- if p.handle == uintptr(syscall.InvalidHandle) {
+ handle := atomic.LoadUintptr(&p.handle)
+ if handle == uintptr(syscall.InvalidHandle) {
return syscall.EINVAL
}
- e := syscall.CloseHandle(syscall.Handle(p.handle))
+ e := syscall.CloseHandle(syscall.Handle(handle))
if e != nil {
return NewSyscallError("CloseHandle", e)
}
- p.handle = uintptr(syscall.InvalidHandle)
+ atomic.StoreUintptr(&p.handle, uintptr(syscall.InvalidHandle))
// no need for a finalizer anymore
runtime.SetFinalizer(p, nil)
return nil
diff --git a/libgo/go/os/file.go b/libgo/go/os/file.go
index e12428cbe12..8c0e3ffe1ba 100644
--- a/libgo/go/os/file.go
+++ b/libgo/go/os/file.go
@@ -192,7 +192,7 @@ func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
// WriteString is like Write, but writes the contents of string s rather than
// a slice of bytes.
-func (f *File) WriteString(s string) (ret int, err error) {
+func (f *File) WriteString(s string) (n int, err error) {
if f == nil {
return 0, ErrInvalid
}
@@ -203,9 +203,16 @@ func (f *File) WriteString(s string) (ret int, err error) {
// If there is an error, it will be of type *PathError.
func Mkdir(name string, perm FileMode) error {
e := syscall.Mkdir(name, syscallMode(perm))
+
if e != nil {
return &PathError{"mkdir", name, e}
}
+
+ // mkdir(2) itself won't handle the sticky bit on *BSD and Solaris
+ if !supportsCreateWithStickyBit && perm&ModeSticky != 0 {
+ Chmod(name, perm)
+ }
+
return nil
}
@@ -235,16 +242,16 @@ func (f *File) Chdir() error {
// 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.
-func Open(name string) (file *File, err error) {
+func Open(name string) (*File, error) {
return OpenFile(name, O_RDONLY, 0)
}
-// Create creates the named file mode 0666 (before umask), truncating
-// it if it already exists. If successful, methods on the returned
+// Create creates the named file with mode 0666 (before umask), truncating
+// it if it already exists. If successful, methods on the returned
// File can be used for I/O; the associated file descriptor has mode
// O_RDWR.
// If there is an error, it will be of type *PathError.
-func Create(name string) (file *File, err error) {
+func Create(name string) (*File, error) {
return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}
@@ -252,6 +259,7 @@ func Create(name string) (file *File, err error) {
var lstat = Lstat
// Rename renames (moves) a file. OS-specific restrictions might apply.
+// If there is an error, it will be of type *LinkError.
func Rename(oldpath, newpath string) error {
return rename(oldpath, newpath)
}
diff --git a/libgo/go/os/file_plan9.go b/libgo/go/os/file_plan9.go
index 132594eede9..085ebc4c8a6 100644
--- a/libgo/go/os/file_plan9.go
+++ b/libgo/go/os/file_plan9.go
@@ -79,7 +79,7 @@ func syscallMode(i FileMode) (o uint32) {
// (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 *File, err error) {
+func OpenFile(name string, flag int, perm FileMode) (*File, error) {
var (
fd int
e error
@@ -159,7 +159,7 @@ func (file *file) close() error {
// Stat returns the FileInfo structure describing file.
// If there is an error, it will be of type *PathError.
-func (f *File) Stat() (fi FileInfo, err error) {
+func (f *File) Stat() (FileInfo, error) {
if f == nil {
return nil, ErrInvalid
}
@@ -224,7 +224,7 @@ func (f *File) Chmod(mode FileMode) error {
// Sync commits the current contents of the file to stable storage.
// Typically, this means flushing the file system's in-memory copy
// of recently written data to disk.
-func (f *File) Sync() (err error) {
+func (f *File) Sync() error {
if f == nil {
return ErrInvalid
}
@@ -319,7 +319,7 @@ func hasPrefix(s, prefix string) bool {
return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
}
-// Variant of LastIndex from the strings package.
+// LastIndexByte from the strings package.
func lastIndex(s string, sep byte) int {
for i := len(s) - 1; i >= 0; i-- {
if s[i] == sep {
diff --git a/libgo/go/os/file_posix.go b/libgo/go/os/file_posix.go
index fbb3b5e4d81..6d8076fdf5c 100644
--- a/libgo/go/os/file_posix.go
+++ b/libgo/go/os/file_posix.go
@@ -28,14 +28,6 @@ func Readlink(name string) (string, error) {
}
}
-func rename(oldname, newname string) error {
- e := syscall.Rename(oldname, newname)
- if e != nil {
- return &LinkError{"rename", oldname, newname, e}
- }
- return nil
-}
-
// syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
func syscallMode(i FileMode) (o uint32) {
o |= uint32(i.Perm())
@@ -122,7 +114,7 @@ func (f *File) Truncate(size int64) error {
// Sync commits the current contents of the file to stable storage.
// Typically, this means flushing the file system's in-memory copy
// of recently written data to disk.
-func (f *File) Sync() (err error) {
+func (f *File) Sync() error {
if f == nil {
return ErrInvalid
}
diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go
index b25e62ff003..7a18987d283 100644
--- a/libgo/go/os/file_unix.go
+++ b/libgo/go/os/file_unix.go
@@ -12,6 +12,14 @@ import (
"syscall"
)
+func rename(oldname, newname string) error {
+ e := syscall.Rename(oldname, newname)
+ if e != nil {
+ return &LinkError{"rename", oldname, newname, e}
+ }
+ return nil
+}
+
// File represents an open file descriptor.
type File struct {
*file
@@ -73,12 +81,24 @@ const DevNull = "/dev/null"
// (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 *File, err error) {
+func OpenFile(name string, flag int, perm FileMode) (*File, error) {
+ chmod := false
+ if !supportsCreateWithStickyBit && flag&O_CREATE != 0 && perm&ModeSticky != 0 {
+ if _, err := Stat(name); IsNotExist(err) {
+ chmod = true
+ }
+ }
+
r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
if e != nil {
return nil, &PathError{"open", name, e}
}
+ // open(2) itself won't handle the sticky bit on *BSD and Solaris
+ if chmod {
+ Chmod(name, perm)
+ }
+
// There's a race here with fork/exec, which we are
// content to live with. See ../syscall/exec_unix.go.
if !supportsCloseOnExec {
@@ -126,12 +146,12 @@ func (file *file) close() error {
// Stat returns the FileInfo structure describing file.
// If there is an error, it will be of type *PathError.
-func (f *File) Stat() (fi FileInfo, err error) {
+func (f *File) Stat() (FileInfo, error) {
if f == nil {
return nil, ErrInvalid
}
var stat syscall.Stat_t
- err = syscall.Fstat(f.fd, &stat)
+ err := syscall.Fstat(f.fd, &stat)
if err != nil {
return nil, &PathError{"stat", f.name, err}
}
@@ -140,9 +160,9 @@ func (f *File) Stat() (fi FileInfo, err error) {
// Stat returns a FileInfo describing the named file.
// If there is an error, it will be of type *PathError.
-func Stat(name string) (fi FileInfo, err error) {
+func Stat(name string) (FileInfo, error) {
var stat syscall.Stat_t
- err = syscall.Stat(name, &stat)
+ err := syscall.Stat(name, &stat)
if err != nil {
return nil, &PathError{"stat", name, err}
}
@@ -153,9 +173,9 @@ func Stat(name string) (fi FileInfo, err error) {
// If the file is a symbolic link, the returned FileInfo
// 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) (fi FileInfo, err error) {
+func Lstat(name string) (FileInfo, error) {
var stat syscall.Stat_t
- err = syscall.Lstat(name, &stat)
+ err := syscall.Lstat(name, &stat)
if err != nil {
return nil, &PathError{"lstat", name, err}
}
diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go
index 7a86efac218..78201f2c27e 100644
--- a/libgo/go/os/os_test.go
+++ b/libgo/go/os/os_test.go
@@ -9,6 +9,7 @@ import (
"errors"
"flag"
"fmt"
+ "internal/testenv"
"io"
"io/ioutil"
. "os"
@@ -21,7 +22,6 @@ import (
"sync"
"syscall"
"testing"
- "text/template"
"time"
)
@@ -41,18 +41,33 @@ type sysDir struct {
files []string
}
-var sysdir = func() (sd *sysDir) {
+var sysdir = func() *sysDir {
switch runtime.GOOS {
case "android":
- sd = &sysDir{
+ return &sysDir{
"/system/etc",
[]string{
"audio_policy.conf",
"system_fonts.xml",
},
}
+ case "darwin":
+ switch runtime.GOARCH {
+ case "arm", "arm64":
+ wd, err := syscall.Getwd()
+ if err != nil {
+ wd = err.Error()
+ }
+ return &sysDir{
+ filepath.Join(wd, "..", ".."),
+ []string{
+ "ResourceRules.plist",
+ "Info.plist",
+ },
+ }
+ }
case "windows":
- sd = &sysDir{
+ return &sysDir{
Getenv("SystemRoot") + "\\system32\\drivers\\etc",
[]string{
"networks",
@@ -61,24 +76,22 @@ var sysdir = func() (sd *sysDir) {
},
}
case "plan9":
- sd = &sysDir{
+ return &sysDir{
"/lib/ndb",
[]string{
"common",
"local",
},
}
- default:
- sd = &sysDir{
- "/etc",
- []string{
- "group",
- "hosts",
- "passwd",
- },
- }
}
- return
+ return &sysDir{
+ "/etc",
+ []string{
+ "group",
+ "hosts",
+ "passwd",
+ },
+ }
}()
func size(name string, t *testing.T) int64 {
@@ -112,15 +125,22 @@ func equal(name1, name2 string) (r bool) {
return
}
-func newFile(testName string, t *testing.T) (f *File) {
- // Use a local file system, not NFS.
- // On Unix, override $TMPDIR in case the user
- // has it set to an NFS-mounted directory.
- dir := ""
- if runtime.GOOS != "android" && runtime.GOOS != "windows" {
- dir = "/tmp"
+// localTmp returns a local temporary directory not on NFS.
+func localTmp() string {
+ switch runtime.GOOS {
+ case "android", "windows":
+ return TempDir()
+ case "darwin":
+ switch runtime.GOARCH {
+ case "arm", "arm64":
+ return TempDir()
+ }
}
- f, err := ioutil.TempFile(dir, "_Go_"+testName)
+ return "/tmp"
+}
+
+func newFile(testName string, t *testing.T) (f *File) {
+ f, err := ioutil.TempFile(localTmp(), "_Go_"+testName)
if err != nil {
t.Fatalf("TempFile %s: %s", testName, err)
}
@@ -128,14 +148,7 @@ func newFile(testName string, t *testing.T) (f *File) {
}
func newDir(testName string, t *testing.T) (name string) {
- // Use a local file system, not NFS.
- // On Unix, override $TMPDIR in case the user
- // has it set to an NFS-mounted directory.
- dir := ""
- if runtime.GOOS != "android" && runtime.GOOS != "windows" {
- dir = "/tmp"
- }
- name, err := ioutil.TempDir(dir, "_Go_"+testName)
+ name, err := ioutil.TempDir(localTmp(), "_Go_"+testName)
if err != nil {
t.Fatalf("TempDir %s: %s", testName, err)
}
@@ -310,6 +323,15 @@ func TestReaddirnamesOneAtATime(t *testing.T) {
switch runtime.GOOS {
case "android":
dir = "/system/bin"
+ case "darwin":
+ switch runtime.GOARCH {
+ case "arm", "arm64":
+ wd, err := Getwd()
+ if err != nil {
+ t.Fatal(err)
+ }
+ dir = wd
+ }
case "plan9":
dir = "/bin"
case "windows":
@@ -489,11 +511,35 @@ func TestReaddirStatFailures(t *testing.T) {
}
}
+// Readdir on a regular file should fail.
+func TestReaddirOfFile(t *testing.T) {
+ f, err := ioutil.TempFile("", "_Go_ReaddirOfFile")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer Remove(f.Name())
+ f.Write([]byte("foo"))
+ f.Close()
+ reg, err := Open(f.Name())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer reg.Close()
+
+ names, err := reg.Readdirnames(-1)
+ if err == nil {
+ t.Error("Readdirnames succeeded; want non-nil error")
+ }
+ if len(names) > 0 {
+ t.Errorf("unexpected dir names in regular file: %q", names)
+ }
+}
+
func TestHardLink(t *testing.T) {
- // Hardlinks are not supported under windows or Plan 9.
if runtime.GOOS == "plan9" {
- return
+ t.Skip("skipping on plan9, hardlinks not supported")
}
+ defer chtmpdir(t)()
from, to := "hardlinktestfrom", "hardlinktestto"
Remove(from) // Just in case.
file, err := Create(to)
@@ -508,6 +554,14 @@ func TestHardLink(t *testing.T) {
if err != nil {
t.Fatalf("link %q, %q failed: %v", to, from, err)
}
+
+ none := "hardlinktestnone"
+ err = Link(none, none)
+ // Check the returned error is well-formed.
+ if lerr, ok := err.(*LinkError); !ok || lerr.Error() == "" {
+ t.Errorf("link %q, %q failed to return a valid error", none, none)
+ }
+
defer Remove(from)
tostat, err := Stat(to)
if err != nil {
@@ -522,6 +576,31 @@ func TestHardLink(t *testing.T) {
}
}
+// chtmpdir changes the working directory to a new temporary directory and
+// provides a cleanup function. Used when PWD is read-only.
+func chtmpdir(t *testing.T) func() {
+ if runtime.GOOS != "darwin" || (runtime.GOARCH != "arm" && runtime.GOARCH != "arm64") {
+ return func() {} // only needed on darwin/arm{,64}
+ }
+ oldwd, err := Getwd()
+ if err != nil {
+ t.Fatalf("chtmpdir: %v", err)
+ }
+ d, err := ioutil.TempDir("", "test")
+ if err != nil {
+ t.Fatalf("chtmpdir: %v", err)
+ }
+ if err := Chdir(d); err != nil {
+ t.Fatalf("chtmpdir: %v", err)
+ }
+ return func() {
+ if err := Chdir(oldwd); err != nil {
+ t.Fatalf("chtmpdir: %v", err)
+ }
+ RemoveAll(d)
+ }
+}
+
func TestSymlink(t *testing.T) {
switch runtime.GOOS {
case "android", "nacl", "plan9":
@@ -531,6 +610,7 @@ func TestSymlink(t *testing.T) {
t.Skipf("skipping on %s", runtime.GOOS)
}
}
+ defer chtmpdir(t)()
from, to := "symlinktestfrom", "symlinktestto"
Remove(from) // Just in case.
file, err := Create(to)
@@ -597,6 +677,7 @@ func TestLongSymlink(t *testing.T) {
t.Skipf("skipping on %s", runtime.GOOS)
}
}
+ defer chtmpdir(t)()
s := "0123456789abcdef"
// Long, but not too long: a common limit is 255.
s = s + s + s + s + s + s + s + s + s + s + s + s + s + s + s
@@ -617,14 +698,18 @@ func TestLongSymlink(t *testing.T) {
}
func TestRename(t *testing.T) {
+ defer chtmpdir(t)()
from, to := "renamefrom", "renameto"
- Remove(to) // Just in case.
+ // Ensure we are not testing the overwrite case here.
+ Remove(from)
+ Remove(to)
+
file, err := Create(from)
if err != nil {
- t.Fatalf("open %q failed: %v", to, err)
+ t.Fatalf("open %q failed: %v", from, err)
}
if err = file.Close(); err != nil {
- t.Errorf("close %q failed: %v", to, err)
+ t.Errorf("close %q failed: %v", from, err)
}
err = Rename(from, to)
if err != nil {
@@ -637,6 +722,79 @@ func TestRename(t *testing.T) {
}
}
+func TestRenameOverwriteDest(t *testing.T) {
+ if runtime.GOOS == "plan9" {
+ t.Skip("skipping on plan9")
+ }
+ defer chtmpdir(t)()
+ from, to := "renamefrom", "renameto"
+ // Just in case.
+ Remove(from)
+ Remove(to)
+
+ toData := []byte("to")
+ fromData := []byte("from")
+
+ err := ioutil.WriteFile(to, toData, 0777)
+ if err != nil {
+ t.Fatalf("write file %q failed: %v", to, err)
+ }
+
+ err = ioutil.WriteFile(from, fromData, 0777)
+ if err != nil {
+ t.Fatalf("write file %q failed: %v", from, err)
+ }
+ err = Rename(from, to)
+ if err != nil {
+ t.Fatalf("rename %q, %q failed: %v", to, from, err)
+ }
+ defer Remove(to)
+
+ _, err = Stat(from)
+ if err == nil {
+ t.Errorf("from file %q still exists", from)
+ }
+ if err != nil && !IsNotExist(err) {
+ t.Fatalf("stat from: %v", err)
+ }
+ toFi, err := Stat(to)
+ if err != nil {
+ t.Fatalf("stat %q failed: %v", to, err)
+ }
+ if toFi.Size() != int64(len(fromData)) {
+ t.Errorf(`"to" size = %d; want %d (old "from" size)`, toFi.Size(), len(fromData))
+ }
+}
+
+func TestRenameFailed(t *testing.T) {
+ defer chtmpdir(t)()
+ from, to := "renamefrom", "renameto"
+ // Ensure we are not testing the overwrite case here.
+ Remove(from)
+ Remove(to)
+
+ err := Rename(from, to)
+ switch err := err.(type) {
+ case *LinkError:
+ if err.Op != "rename" {
+ t.Errorf("rename %q, %q: err.Op: want %q, got %q", from, to, "rename", err.Op)
+ }
+ if err.Old != from {
+ t.Errorf("rename %q, %q: err.Old: want %q, got %q", from, to, from, err.Old)
+ }
+ if err.New != to {
+ t.Errorf("rename %q, %q: err.New: want %q, got %q", from, to, to, err.New)
+ }
+ case nil:
+ t.Errorf("rename %q, %q: expected error, got nil", from, to)
+
+ // cleanup whatever was placed in "renameto"
+ Remove(to)
+ default:
+ t.Errorf("rename %q, %q: expected %T, got %T %v", from, to, new(LinkError), err, err)
+ }
+}
+
func exec(t *testing.T, dir, cmd string, args []string, expect string) {
r, w, err := Pipe()
if err != nil {
@@ -664,18 +822,18 @@ func exec(t *testing.T, dir, cmd string, args []string, expect string) {
}
func TestStartProcess(t *testing.T) {
- switch runtime.GOOS {
- case "android", "nacl":
- t.Skipf("skipping on %s", runtime.GOOS)
- }
+ testenv.MustHaveExec(t)
var dir, cmd string
var args []string
- if runtime.GOOS == "windows" {
+ switch runtime.GOOS {
+ case "android":
+ t.Skip("android doesn't have /bin/pwd")
+ case "windows":
cmd = Getenv("COMSPEC")
dir = Getenv("SystemRoot")
args = []string{"/c", "cd"}
- } else {
+ default:
cmd = "/bin/pwd"
dir = "/"
args = []string{}
@@ -847,6 +1005,19 @@ func TestChdirAndGetwd(t *testing.T) {
dirs = []string{"/", "/system/bin"}
case "plan9":
dirs = []string{"/", "/usr"}
+ case "darwin":
+ switch runtime.GOARCH {
+ case "arm", "arm64":
+ d1, err := ioutil.TempDir("", "d1")
+ if err != nil {
+ t.Fatalf("TempDir: %v", err)
+ }
+ d2, err := ioutil.TempDir("", "d2")
+ if err != nil {
+ t.Fatalf("TempDir: %v", err)
+ }
+ dirs = []string{d1, d2}
+ }
}
oldwd := Getenv("PWD")
for mode := 0; mode < 2; mode++ {
@@ -892,6 +1063,64 @@ func TestChdirAndGetwd(t *testing.T) {
fd.Close()
}
+// Test that Chdir+Getwd is program-wide.
+func TestProgWideChdir(t *testing.T) {
+ const N = 10
+ c := make(chan bool)
+ cpwd := make(chan string)
+ for i := 0; i < N; i++ {
+ go func(i int) {
+ // Lock half the goroutines in their own operating system
+ // thread to exercise more scheduler possibilities.
+ if i%2 == 1 {
+ // On Plan 9, after calling LockOSThread, the goroutines
+ // run on different processes which don't share the working
+ // directory. This used to be an issue because Go expects
+ // the working directory to be program-wide.
+ // See issue 9428.
+ runtime.LockOSThread()
+ }
+ <-c
+ pwd, err := Getwd()
+ if err != nil {
+ t.Errorf("Getwd on goroutine %d: %v", i, err)
+ return
+ }
+ cpwd <- pwd
+ }(i)
+ }
+ oldwd, err := Getwd()
+ if err != nil {
+ t.Fatalf("Getwd: %v", err)
+ }
+ d, err := ioutil.TempDir("", "test")
+ if err != nil {
+ t.Fatalf("TempDir: %v", err)
+ }
+ defer func() {
+ if err := Chdir(oldwd); err != nil {
+ t.Fatalf("Chdir: %v", err)
+ }
+ RemoveAll(d)
+ }()
+ if err := Chdir(d); err != nil {
+ t.Fatalf("Chdir: %v", err)
+ }
+ // OS X sets TMPDIR to a symbolic link.
+ // So we resolve our working directory again before the test.
+ d, err = Getwd()
+ if err != nil {
+ t.Fatalf("Getwd: %v", err)
+ }
+ close(c)
+ for i := 0; i < N; i++ {
+ pwd := <-cpwd
+ if pwd != d {
+ t.Errorf("Getwd returned %q; want %q", pwd, d)
+ }
+ }
+}
+
func TestSeek(t *testing.T) {
f := newFile("TestSeek", t)
defer Remove(f.Name())
@@ -920,7 +1149,7 @@ func TestSeek(t *testing.T) {
if off != tt.out || err != nil {
if e, ok := err.(*PathError); ok && e.Err == syscall.EINVAL && tt.out > 1<<32 {
// Reiserfs rejects the big seeks.
- // http://code.google.com/p/go/issues/detail?id=91
+ // https://golang.org/issue/91
break
}
t.Errorf("#%d: Seek(%v, %v) = %v, %v want %v, nil", i, tt.in, tt.whence, off, err, tt.out)
@@ -1033,14 +1262,35 @@ func run(t *testing.T, cmd []string) string {
return output
}
+func testWindowsHostname(t *testing.T) {
+ hostname, err := Hostname()
+ if err != nil {
+ t.Fatal(err)
+ }
+ cmd := osexec.Command("hostname")
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("Failed to execute hostname command: %v %s", err, out)
+ }
+ want := strings.Trim(string(out), "\r\n")
+ if hostname != want {
+ t.Fatalf("Hostname() = %q, want %q", hostname, want)
+ }
+}
+
func TestHostname(t *testing.T) {
// There is no other way to fetch hostname on windows, but via winapi.
// On Plan 9 it can be taken from #c/sysname as Hostname() does.
switch runtime.GOOS {
- case "android", "nacl", "plan9", "windows":
- t.Skipf("skipping on %s", runtime.GOOS)
+ case "android", "plan9":
+ t.Skipf("%s doesn't have /bin/hostname", runtime.GOOS)
+ case "windows":
+ testWindowsHostname(t)
+ return
}
+ testenv.MustHaveExec(t)
+
// Check internal Hostname() against the output of /bin/hostname.
// Allow that the internal Hostname returns a Fully Qualified Domain Name
// and the /bin/hostname only returns the first component
@@ -1115,6 +1365,7 @@ func writeFile(t *testing.T, fname string, flag int, text string) string {
}
func TestAppend(t *testing.T) {
+ defer chtmpdir(t)()
const f = "append.txt"
defer Remove(f)
s := writeFile(t, f, O_CREATE|O_TRUNC|O_RDWR, "new")
@@ -1178,6 +1429,7 @@ func TestNilProcessStateString(t *testing.T) {
}
func TestSameFile(t *testing.T) {
+ defer chtmpdir(t)()
fa, err := Create("a")
if err != nil {
t.Fatalf("Create(a): %v", err)
@@ -1297,45 +1549,11 @@ func TestReadAtEOF(t *testing.T) {
}
func testKillProcess(t *testing.T, processKiller func(p *Process)) {
- t.Skip("gccgo does not have a go command")
- switch runtime.GOOS {
- case "android", "nacl":
- t.Skipf("skipping on %s", runtime.GOOS)
- }
-
- dir, err := ioutil.TempDir("", "go-build")
- if err != nil {
- t.Fatalf("Failed to create temp directory: %v", err)
- }
- defer RemoveAll(dir)
-
- src := filepath.Join(dir, "main.go")
- f, err := Create(src)
- if err != nil {
- t.Fatalf("Failed to create %v: %v", src, err)
- }
- st := template.Must(template.New("source").Parse(`
-package main
-import "time"
-func main() {
- time.Sleep(time.Second)
-}
-`))
- err = st.Execute(f, nil)
- if err != nil {
- f.Close()
- t.Fatalf("Failed to execute template: %v", err)
- }
- f.Close()
-
- exe := filepath.Join(dir, "main.exe")
- output, err := osexec.Command("go", "build", "-o", exe, src).CombinedOutput()
- if err != nil {
- t.Fatalf("Failed to build exe %v: %v %v", exe, err, string(output))
- }
+ testenv.MustHaveExec(t)
- cmd := osexec.Command(exe)
- err = cmd.Start()
+ // Re-exec the test binary itself to emulate "sleep 1".
+ cmd := osexec.Command(Args[0], "-test.run", "TestSleep")
+ err := cmd.Start()
if err != nil {
t.Fatalf("Failed to start test process: %v", err)
}
@@ -1349,6 +1567,15 @@ func main() {
}
}
+// TestSleep emulates "sleep 1". It is a helper for testKillProcess, so we
+// don't have to rely on an external "sleep" command being available.
+func TestSleep(t *testing.T) {
+ if testing.Short() {
+ t.Skip("Skipping in short mode")
+ }
+ time.Sleep(time.Second)
+}
+
func TestKillStartProcess(t *testing.T) {
testKillProcess(t, func(p *Process) {
err := p.Kill()
@@ -1359,14 +1586,13 @@ func TestKillStartProcess(t *testing.T) {
}
func TestGetppid(t *testing.T) {
- switch runtime.GOOS {
- case "nacl":
- t.Skip("skipping on nacl")
- case "plan9":
+ if runtime.GOOS == "plan9" {
// TODO: golang.org/issue/8206
t.Skipf("skipping test on plan9; see issue 8206")
}
+ testenv.MustHaveExec(t)
+
if Getenv("GO_WANT_HELPER_PROCESS") == "1" {
fmt.Print(Getppid())
Exit(0)
diff --git a/libgo/go/os/os_unix_test.go b/libgo/go/os/os_unix_test.go
index 21d40ccaf84..2adc3b50e72 100644
--- a/libgo/go/os/os_unix_test.go
+++ b/libgo/go/os/os_unix_test.go
@@ -13,6 +13,10 @@ import (
"testing"
)
+func init() {
+ isReadonlyError = func(err error) bool { return err == syscall.EROFS }
+}
+
func checkUidGid(t *testing.T, path string, uid, gid int) {
dir, err := Stat(path)
if err != nil {
@@ -28,10 +32,10 @@ func checkUidGid(t *testing.T, path string, uid, gid int) {
}
func TestChown(t *testing.T) {
- // Chown is not supported under windows os Plan 9.
+ // Chown is not supported under windows or Plan 9.
// Plan9 provides a native ChownPlan9 version instead.
if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
- return
+ 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
@@ -74,3 +78,109 @@ func TestChown(t *testing.T) {
checkUidGid(t, f.Name(), int(sys.Uid), gid)
}
}
+
+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
+ // basically useless.
+ f := newFile("TestFileChown", t)
+ defer Remove(f.Name())
+ defer f.Close()
+ dir, err := f.Stat()
+ if err != nil {
+ t.Fatalf("stat %s: %s", f.Name(), err)
+ }
+
+ // Can't change uid unless root, but can try
+ // changing the group id. First try our current group.
+ gid := Getgid()
+ t.Log("gid:", gid)
+ if err = f.Chown(-1, gid); err != nil {
+ t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err)
+ }
+ sys := dir.Sys().(*syscall.Stat_t)
+ checkUidGid(t, f.Name(), int(sys.Uid), gid)
+
+ // Then try all the auxiliary groups.
+ groups, err := Getgroups()
+ if err != nil {
+ t.Fatalf("getgroups: %s", err)
+ }
+ t.Log("groups: ", groups)
+ for _, g := range groups {
+ if err = f.Chown(-1, g); err != nil {
+ t.Fatalf("fchown %s -1 %d: %s", f.Name(), g, err)
+ }
+ checkUidGid(t, f.Name(), int(sys.Uid), g)
+
+ // change back to gid to test fd.Chown
+ if err = f.Chown(-1, gid); err != nil {
+ t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err)
+ }
+ checkUidGid(t, f.Name(), int(sys.Uid), gid)
+ }
+}
+
+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
+ // basically useless.
+ f := newFile("TestLchown", t)
+ defer Remove(f.Name())
+ defer f.Close()
+ dir, err := f.Stat()
+ if err != nil {
+ t.Fatalf("stat %s: %s", f.Name(), err)
+ }
+
+ linkname := f.Name() + "2"
+ if err := Link(f.Name(), linkname); err != nil {
+ t.Fatalf("link %s -> %s: %v", f.Name(), linkname, err)
+ }
+ defer Remove(linkname)
+
+ f2, err := Open(linkname)
+ if err != nil {
+ t.Fatalf("open %s: %v", linkname, err)
+ }
+ defer f2.Close()
+
+ // Can't change uid unless root, but can try
+ // changing the group id. First try our current group.
+ gid := Getgid()
+ t.Log("gid:", gid)
+ if err = Lchown(linkname, -1, gid); err != nil {
+ t.Fatalf("lchown %s -1 %d: %s", linkname, gid, err)
+ }
+ sys := dir.Sys().(*syscall.Stat_t)
+ checkUidGid(t, linkname, int(sys.Uid), gid)
+
+ // Then try all the auxiliary groups.
+ groups, err := Getgroups()
+ if err != nil {
+ t.Fatalf("getgroups: %s", err)
+ }
+ t.Log("groups: ", groups)
+ for _, g := range groups {
+ if err = Lchown(linkname, -1, g); err != nil {
+ t.Fatalf("lchown %s -1 %d: %s", linkname, g, err)
+ }
+ checkUidGid(t, linkname, int(sys.Uid), g)
+
+ // change back to gid to test fd.Chown
+ if err = f2.Chown(-1, gid); err != nil {
+ t.Fatalf("fchown %s -1 %d: %s", linkname, gid, err)
+ }
+ checkUidGid(t, linkname, int(sys.Uid), gid)
+ }
+}
diff --git a/libgo/go/os/path_plan9.go b/libgo/go/os/path_plan9.go
index 64bad500a67..b09b53a3d82 100644
--- a/libgo/go/os/path_plan9.go
+++ b/libgo/go/os/path_plan9.go
@@ -9,7 +9,7 @@ const (
PathListSeparator = '\000' // OS-specific path list separator
)
-// IsPathSeparator returns true if c is a directory separator character.
+// IsPathSeparator reports whether c is a directory separator character.
func IsPathSeparator(c uint8) bool {
return PathSeparator == c
}
diff --git a/libgo/go/os/path_test.go b/libgo/go/os/path_test.go
index 6f24a43132a..f9853810c6f 100644
--- a/libgo/go/os/path_test.go
+++ b/libgo/go/os/path_test.go
@@ -13,6 +13,8 @@ import (
"testing"
)
+var isReadonlyError = func(error) bool { return false }
+
func TestMkdirAll(t *testing.T) {
tmpDir := TempDir()
path := tmpDir + "/_TestMkdirAll_/dir/./dir2"
@@ -205,16 +207,22 @@ func TestMkdirAllAtSlash(t *testing.T) {
switch runtime.GOOS {
case "android", "plan9", "windows":
t.Skipf("skipping on %s", runtime.GOOS)
+ case "darwin":
+ switch runtime.GOARCH {
+ case "arm", "arm64":
+ t.Skipf("skipping on darwin/%s, mkdir returns EPERM", runtime.GOARCH)
+ }
}
RemoveAll("/_go_os_test")
- err := MkdirAll("/_go_os_test/dir", 0777)
+ const dir = "/_go_os_test/dir"
+ err := MkdirAll(dir, 0777)
if err != nil {
pathErr, ok := err.(*PathError)
// common for users not to be able to write to /
- if ok && pathErr.Err == syscall.EACCES {
- return
+ if ok && (pathErr.Err == syscall.EACCES || isReadonlyError(pathErr.Err)) {
+ t.Skipf("could not create %v: %v", dir, err)
}
- t.Fatalf(`MkdirAll "/_go_os_test/dir": %v`, err)
+ t.Fatalf(`MkdirAll "/_go_os_test/dir": %v, %s`, err, pathErr.Err)
}
RemoveAll("/_go_os_test")
}
diff --git a/libgo/go/os/path_unix.go b/libgo/go/os/path_unix.go
index 0211107ddfc..36f8e61bf92 100644
--- a/libgo/go/os/path_unix.go
+++ b/libgo/go/os/path_unix.go
@@ -11,7 +11,7 @@ const (
PathListSeparator = ':' // OS-specific path list separator
)
-// IsPathSeparator returns true if c is a directory separator character.
+// IsPathSeparator reports whether c is a directory separator character.
func IsPathSeparator(c uint8) bool {
return PathSeparator == c
}
diff --git a/libgo/go/os/path_windows.go b/libgo/go/os/path_windows.go
index 61f2ca59ff4..c96f1376860 100644
--- a/libgo/go/os/path_windows.go
+++ b/libgo/go/os/path_windows.go
@@ -9,7 +9,7 @@ const (
PathListSeparator = ';' // OS-specific path list separator
)
-// IsPathSeparator returns true if c is a directory separator character.
+// IsPathSeparator reports whether c is a directory separator character.
func IsPathSeparator(c uint8) bool {
// NOTE: Windows accept / as path separator.
return c == '\\' || c == '/'
diff --git a/libgo/go/os/proc.go b/libgo/go/os/proc.go
index 774f09900ec..33a8b26f78d 100644
--- a/libgo/go/os/proc.go
+++ b/libgo/go/os/proc.go
@@ -44,6 +44,14 @@ func Getgroups() ([]int, error) {
// Exit causes the current program to exit with the given status code.
// Conventionally, code zero indicates success, non-zero an error.
-// The program terminates immediately; deferred functions are
-// not run.
-func Exit(code int) { syscall.Exit(code) }
+// The program terminates immediately; deferred functions are not run.
+func Exit(code int) {
+ if code == 0 {
+ // Give race detector a chance to fail the program.
+ // Racy programs do not have the right to finish successfully.
+ runtime_beforeExit()
+ }
+ syscall.Exit(code)
+}
+
+func runtime_beforeExit() // implemented in runtime
diff --git a/libgo/go/os/signal/signal.go b/libgo/go/os/signal/signal.go
index 30042754953..1625786d490 100644
--- a/libgo/go/os/signal/signal.go
+++ b/libgo/go/os/signal/signal.go
@@ -5,8 +5,6 @@
// Package signal implements access to incoming signals.
package signal
-// BUG(rsc): This package is not yet implemented on Plan 9.
-
import (
"os"
"sync"
@@ -30,9 +28,55 @@ func (h *handler) set(sig int) {
h.mask[sig/32] |= 1 << uint(sig&31)
}
+func (h *handler) clear(sig int) {
+ h.mask[sig/32] &^= 1 << uint(sig&31)
+}
+
+// Stop relaying the signals, sigs, to any channels previously registered to
+// receive them and either reset the signal handlers to their original values
+// (action=disableSignal) or ignore the signals (action=ignoreSignal).
+func cancel(sigs []os.Signal, action func(int)) {
+ handlers.Lock()
+ defer handlers.Unlock()
+
+ remove := func(n int) {
+ var zerohandler handler
+
+ for c, h := range handlers.m {
+ if h.want(n) {
+ handlers.ref[n]--
+ h.clear(n)
+ if h.mask == zerohandler.mask {
+ delete(handlers.m, c)
+ }
+ }
+ }
+
+ action(n)
+ }
+
+ if len(sigs) == 0 {
+ for n := 0; n < numSig; n++ {
+ remove(n)
+ }
+ } else {
+ for _, s := range sigs {
+ remove(signum(s))
+ }
+ }
+}
+
+// Ignore causes the provided signals to be ignored. If they are received by
+// the program, nothing will happen. Ignore undoes the effect of any prior
+// calls to Notify for the provided signals.
+// If no signals are provided, all incoming signals will be ignored.
+func Ignore(sig ...os.Signal) {
+ cancel(sig, ignoreSignal)
+}
+
// Notify causes package signal to relay incoming signals to c.
-// If no signals are listed, all incoming signals will be relayed to c.
-// Otherwise, just the listed signals will.
+// If no signals are provided, all incoming signals will be relayed to c.
+// Otherwise, just the provided signals will.
//
// Package signal will not block sending to c: the caller must ensure
// that c has sufficient buffer space to keep up with the expected
@@ -87,6 +131,13 @@ func Notify(c chan<- os.Signal, sig ...os.Signal) {
}
}
+// Reset undoes the effect of any prior calls to Notify for the provided
+// signals.
+// If no signals are provided, all signal handlers will be reset.
+func Reset(sig ...os.Signal) {
+ cancel(sig, disableSignal)
+}
+
// Stop causes package signal to stop relaying incoming signals to c.
// It undoes the effect of all prior calls to Notify using c.
// When Stop returns, it is guaranteed that c will receive no more signals.
diff --git a/libgo/go/os/signal/signal_plan9.go b/libgo/go/os/signal/signal_plan9.go
new file mode 100644
index 00000000000..b065ae520d7
--- /dev/null
+++ b/libgo/go/os/signal/signal_plan9.go
@@ -0,0 +1,60 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package signal
+
+import (
+ "os"
+ "syscall"
+)
+
+var sigtab = make(map[os.Signal]int)
+
+// In sig.s; jumps to runtime.
+func signal_disable(uint32)
+func signal_enable(uint32)
+func signal_ignore(uint32)
+func signal_recv() string
+
+func init() {
+ signal_enable(0) // first call - initialize
+ go loop()
+}
+
+func loop() {
+ for {
+ process(syscall.Note(signal_recv()))
+ }
+}
+
+const numSig = 256
+
+func signum(sig os.Signal) int {
+ switch sig := sig.(type) {
+ case syscall.Note:
+ n, ok := sigtab[sig]
+ if !ok {
+ n = len(sigtab) + 1
+ if n > numSig {
+ return -1
+ }
+ sigtab[sig] = n
+ }
+ return n
+ default:
+ return -1
+ }
+}
+
+func enableSignal(sig int) {
+ signal_enable(uint32(sig))
+}
+
+func disableSignal(sig int) {
+ signal_disable(uint32(sig))
+}
+
+func ignoreSignal(sig int) {
+ signal_ignore(uint32(sig))
+}
diff --git a/libgo/go/os/signal/signal_plan9_test.go b/libgo/go/os/signal/signal_plan9_test.go
new file mode 100644
index 00000000000..10bfdc3ff10
--- /dev/null
+++ b/libgo/go/os/signal/signal_plan9_test.go
@@ -0,0 +1,181 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package signal
+
+import (
+ "os"
+ "runtime"
+ "syscall"
+ "testing"
+ "time"
+)
+
+func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) {
+ select {
+ case s := <-c:
+ if s != sig {
+ t.Fatalf("signal was %v, want %v", s, sig)
+ }
+ case <-time.After(1 * time.Second):
+ t.Fatalf("timeout waiting for %v", sig)
+ }
+}
+
+// Test that basic signal handling works.
+func TestSignal(t *testing.T) {
+ // Ask for hangup
+ c := make(chan os.Signal, 1)
+ Notify(c, syscall.Note("hangup"))
+ defer Stop(c)
+
+ // Send this process a hangup
+ t.Logf("hangup...")
+ postNote(syscall.Getpid(), "hangup")
+ waitSig(t, c, syscall.Note("hangup"))
+
+ // Ask for everything we can get.
+ c1 := make(chan os.Signal, 1)
+ Notify(c1)
+
+ // Send this process an alarm
+ t.Logf("alarm...")
+ postNote(syscall.Getpid(), "alarm")
+ waitSig(t, c1, syscall.Note("alarm"))
+
+ // Send two more hangups, to make sure that
+ // they get delivered on c1 and that not reading
+ // from c does not block everything.
+ t.Logf("hangup...")
+ postNote(syscall.Getpid(), "hangup")
+ waitSig(t, c1, syscall.Note("hangup"))
+ t.Logf("hangup...")
+ postNote(syscall.Getpid(), "hangup")
+ waitSig(t, c1, syscall.Note("hangup"))
+
+ // The first SIGHUP should be waiting for us on c.
+ waitSig(t, c, syscall.Note("hangup"))
+}
+
+func TestStress(t *testing.T) {
+ dur := 3 * time.Second
+ if testing.Short() {
+ dur = 100 * time.Millisecond
+ }
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
+ done := make(chan bool)
+ finished := make(chan bool)
+ go func() {
+ sig := make(chan os.Signal, 1)
+ Notify(sig, syscall.Note("alarm"))
+ defer Stop(sig)
+ Loop:
+ for {
+ select {
+ case <-sig:
+ case <-done:
+ break Loop
+ }
+ }
+ finished <- true
+ }()
+ go func() {
+ Loop:
+ for {
+ select {
+ case <-done:
+ break Loop
+ default:
+ postNote(syscall.Getpid(), "alarm")
+ runtime.Gosched()
+ }
+ }
+ finished <- true
+ }()
+ time.Sleep(dur)
+ close(done)
+ <-finished
+ <-finished
+ // When run with 'go test -cpu=1,2,4' alarm from this test can slip
+ // into subsequent TestSignal() causing failure.
+ // Sleep for a while to reduce the possibility of the failure.
+ time.Sleep(10 * time.Millisecond)
+}
+
+// Test that Stop cancels the channel's registrations.
+func TestStop(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping in short mode")
+ }
+ sigs := []string{
+ "alarm",
+ "hangup",
+ }
+
+ for _, sig := range sigs {
+ // Send the signal.
+ // If it's alarm, we should not see it.
+ // If it's hangup, maybe we'll die. Let the flag tell us what to do.
+ if sig != "hangup" {
+ postNote(syscall.Getpid(), sig)
+ }
+ time.Sleep(100 * time.Millisecond)
+
+ // Ask for signal
+ c := make(chan os.Signal, 1)
+ Notify(c, syscall.Note(sig))
+ defer Stop(c)
+
+ // Send this process that signal
+ postNote(syscall.Getpid(), sig)
+ waitSig(t, c, syscall.Note(sig))
+
+ Stop(c)
+ select {
+ case s := <-c:
+ t.Fatalf("unexpected signal %v", s)
+ case <-time.After(100 * time.Millisecond):
+ // nothing to read - good
+ }
+
+ // Send the signal.
+ // If it's alarm, we should not see it.
+ // If it's hangup, maybe we'll die. Let the flag tell us what to do.
+ if sig != "hangup" {
+ postNote(syscall.Getpid(), sig)
+ }
+
+ select {
+ case s := <-c:
+ t.Fatalf("unexpected signal %v", s)
+ case <-time.After(100 * time.Millisecond):
+ // nothing to read - good
+ }
+ }
+}
+
+func itoa(val int) string {
+ if val < 0 {
+ return "-" + itoa(-val)
+ }
+ var buf [32]byte // big enough for int64
+ i := len(buf) - 1
+ for val >= 10 {
+ buf[i] = byte(val%10 + '0')
+ i--
+ val /= 10
+ }
+ buf[i] = byte(val + '0')
+ return string(buf[i:])
+}
+
+func postNote(pid int, note string) error {
+ f, err := os.OpenFile("/proc/"+itoa(pid)+"/note", os.O_WRONLY, 0)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ _, err = f.Write([]byte(note))
+ return err
+}
diff --git a/libgo/go/os/signal/signal_stub.go b/libgo/go/os/signal/signal_stub.go
deleted file mode 100644
index d0a6935ff2c..00000000000
--- a/libgo/go/os/signal/signal_stub.go
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build plan9
-
-package signal
-
-import "os"
-
-const numSig = 0
-
-func signum(sig os.Signal) int { return -1 }
-
-func disableSignal(int) {}
-
-func enableSignal(int) {}
diff --git a/libgo/go/os/signal/signal_test.go b/libgo/go/os/signal/signal_test.go
index 22337a72d4b..a71633c8907 100644
--- a/libgo/go/os/signal/signal_test.go
+++ b/libgo/go/os/signal/signal_test.go
@@ -109,6 +109,72 @@ func TestStress(t *testing.T) {
time.Sleep(10 * time.Millisecond)
}
+func testCancel(t *testing.T, ignore bool) {
+ // Send SIGWINCH. By default this signal should be ignored.
+ syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
+ time.Sleep(100 * time.Millisecond)
+
+ // Ask to be notified on c1 when a SIGWINCH is received.
+ c1 := make(chan os.Signal, 1)
+ Notify(c1, syscall.SIGWINCH)
+ defer Stop(c1)
+
+ // Ask to be notified on c2 when a SIGHUP is received.
+ c2 := make(chan os.Signal, 1)
+ Notify(c2, syscall.SIGHUP)
+ defer Stop(c2)
+
+ // Send this process a SIGWINCH and wait for notification on c1.
+ syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
+ waitSig(t, c1, syscall.SIGWINCH)
+
+ // Send this process a SIGHUP and wait for notification on c2.
+ syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
+ waitSig(t, c2, syscall.SIGHUP)
+
+ // Ignore, or reset the signal handlers for, SIGWINCH and SIGHUP.
+ if ignore {
+ Ignore(syscall.SIGWINCH, syscall.SIGHUP)
+ } else {
+ Reset(syscall.SIGWINCH, syscall.SIGHUP)
+ }
+
+ // Send this process a SIGWINCH. It should be ignored.
+ syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
+
+ // If ignoring, Send this process a SIGHUP. It should be ignored.
+ if ignore {
+ syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
+ }
+
+ select {
+ case s := <-c1:
+ t.Fatalf("unexpected signal %v", s)
+ case <-time.After(100 * time.Millisecond):
+ // nothing to read - good
+ }
+
+ select {
+ case s := <-c2:
+ t.Fatalf("unexpected signal %v", s)
+ case <-time.After(100 * time.Millisecond):
+ // nothing to read - good
+ }
+
+ // Reset the signal handlers for all signals.
+ Reset()
+}
+
+// Test that Reset cancels registration for listed signals on all channels.
+func TestReset(t *testing.T) {
+ testCancel(t, false)
+}
+
+// Test that Ignore cancels registration for listed signals on all channels.
+func TestIgnore(t *testing.T) {
+ testCancel(t, true)
+}
+
var sendUncaughtSighup = flag.Int("send_uncaught_sighup", 0, "send uncaught SIGHUP during TestStop")
// Test that Stop cancels the channel's registrations.
diff --git a/libgo/go/os/signal/signal_unix.go b/libgo/go/os/signal/signal_unix.go
index 94b8ab3ddbf..1bdf1d72718 100644
--- a/libgo/go/os/signal/signal_unix.go
+++ b/libgo/go/os/signal/signal_unix.go
@@ -14,6 +14,7 @@ import (
// In assembly.
func signal_disable(uint32)
func signal_enable(uint32)
+func signal_ignore(uint32)
func signal_recv() uint32
func loop() {
@@ -51,3 +52,7 @@ func enableSignal(sig int) {
func disableSignal(sig int) {
signal_disable(uint32(sig))
}
+
+func ignoreSignal(sig int) {
+ signal_ignore(uint32(sig))
+}
diff --git a/libgo/go/os/stat_plan9.go b/libgo/go/os/stat_plan9.go
index 25c9a8c14b3..fa4bd83aefb 100644
--- a/libgo/go/os/stat_plan9.go
+++ b/libgo/go/os/stat_plan9.go
@@ -9,6 +9,8 @@ import (
"time"
)
+const _BIT16SZ = 2
+
func sameFile(fs1, fs2 *fileStat) bool {
a := fs1.sys.(*syscall.Dir)
b := fs2.sys.(*syscall.Dir)
@@ -41,16 +43,14 @@ func fileInfoFromStat(d *syscall.Dir) FileInfo {
// arg is an open *File or a path string.
func dirstat(arg interface{}) (*syscall.Dir, error) {
var name string
+ var err error
- // This is big enough for most stat messages
- // and rounded to a multiple of 128 bytes.
- size := (syscall.STATFIXLEN + 16*4 + 128) &^ 128
+ size := syscall.STATFIXLEN + 16*4
for i := 0; i < 2; i++ {
- buf := make([]byte, size)
+ buf := make([]byte, _BIT16SZ+size)
var n int
- var err error
switch a := arg.(type) {
case *File:
name = a.name
@@ -61,34 +61,36 @@ func dirstat(arg interface{}) (*syscall.Dir, error) {
default:
panic("phase error in dirstat")
}
- if err != nil {
+
+ if n < _BIT16SZ {
return nil, &PathError{"stat", name, err}
}
- if n < syscall.STATFIXLEN {
- return nil, &PathError{"stat", name, syscall.ErrShortStat}
- }
// Pull the real size out of the stat message.
size = int(uint16(buf[0]) | uint16(buf[1])<<8)
// If the stat message is larger than our buffer we will
// go around the loop and allocate one that is big enough.
- if size > n {
- continue
+ if size <= n {
+ d, err := syscall.UnmarshalDir(buf[:n])
+ if err != nil {
+ return nil, &PathError{"stat", name, err}
+ }
+ return d, nil
}
- d, err := syscall.UnmarshalDir(buf[:n])
- if err != nil {
- return nil, &PathError{"stat", name, err}
- }
- return d, nil
}
- return nil, &PathError{"stat", name, syscall.ErrBadStat}
+
+ if err == nil {
+ err = syscall.ErrBadStat
+ }
+
+ return nil, &PathError{"stat", name, err}
}
// Stat returns a FileInfo describing the named file.
// If there is an error, it will be of type *PathError.
-func Stat(name string) (fi FileInfo, err error) {
+func Stat(name string) (FileInfo, error) {
d, err := dirstat(name)
if err != nil {
return nil, err
@@ -100,7 +102,7 @@ func Stat(name string) (fi FileInfo, err error) {
// If the file is a symbolic link, the returned FileInfo
// 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) (fi FileInfo, err error) {
+func Lstat(name string) (FileInfo, error) {
return Stat(name)
}
diff --git a/libgo/go/os/sticky_bsd.go b/libgo/go/os/sticky_bsd.go
new file mode 100644
index 00000000000..6b54c758c70
--- /dev/null
+++ b/libgo/go/os/sticky_bsd.go
@@ -0,0 +1,11 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd netbsd openbsd solaris
+
+package os
+
+// According to sticky(8), neither open(2) nor mkdir(2) will create
+// a file with the sticky bit set.
+const supportsCreateWithStickyBit = false
diff --git a/libgo/go/os/sticky_notbsd.go b/libgo/go/os/sticky_notbsd.go
new file mode 100644
index 00000000000..834e79b0b59
--- /dev/null
+++ b/libgo/go/os/sticky_notbsd.go
@@ -0,0 +1,14 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !darwin
+// +build !dragonfly
+// +build !freebsd
+// +build !netbsd
+// +build !openbsd
+// +build !solaris
+
+package os
+
+const supportsCreateWithStickyBit = true
diff --git a/libgo/go/os/str.go b/libgo/go/os/str.go
index e3606b61eb4..d3e03e98495 100644
--- a/libgo/go/os/str.go
+++ b/libgo/go/os/str.go
@@ -2,21 +2,32 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build plan9
+// Simple converions to avoid depending on strconv.
package os
-func itoa(val int) string { // do it here rather than with fmt to avoid dependency
+// Convert integer to decimal string
+func itoa(val int) string {
if val < 0 {
- return "-" + itoa(-val)
+ return "-" + uitoa(uint(-val))
}
- var buf [32]byte // big enough for int64
+ return uitoa(uint(val))
+}
+
+// Convert unsigned integer to decimal string
+func uitoa(val uint) string {
+ if val == 0 { // avoid string allocation
+ return "0"
+ }
+ var buf [20]byte // big enough for 64bit value base 10
i := len(buf) - 1
for val >= 10 {
- buf[i] = byte(val%10 + '0')
+ q := val / 10
+ buf[i] = byte('0' + val - q*10)
i--
- val /= 10
+ val = q
}
- buf[i] = byte(val + '0')
+ // val < 10
+ buf[i] = byte('0' + val)
return string(buf[i:])
}
diff --git a/libgo/go/os/types.go b/libgo/go/os/types.go
index 473d431d4d2..9d6f8e13d6f 100644
--- a/libgo/go/os/types.go
+++ b/libgo/go/os/types.go
@@ -53,7 +53,7 @@ const (
// Mask for the type bits. For regular files, none will be set.
ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice
- ModePerm FileMode = 0777 // permission bits
+ ModePerm FileMode = 0777 // Unix permission bits
)
func (m FileMode) String() string {
diff --git a/libgo/go/os/user/lookup_unix.go b/libgo/go/os/user/lookup_unix.go
index 64f4576f69e..bebb9e8f2b0 100644
--- a/libgo/go/os/user/lookup_unix.go
+++ b/libgo/go/os/user/lookup_unix.go
@@ -16,6 +16,7 @@ import (
)
/*
+#cgo solaris CFLAGS: -D_POSIX_PTHREAD_SEMANTICS
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
@@ -23,7 +24,12 @@ import (
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);
+ 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);
}
*/
@@ -62,9 +68,9 @@ func lookupUnix(uid int, username string, lookupByName bool) (*User, error) {
const bufSize = 1024
buf := make([]byte, bufSize)
if lookupByName {
- p := syscall.StringBytePtr(username)
+ nameC := syscall.StringBytePtr(username)
syscall.Entersyscall()
- rv := libc_getpwnam_r(p,
+ rv := libc_getpwnam_r(nameC,
&pwd,
&buf[0],
bufSize,