aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/os
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2017-01-14 00:05:42 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2017-01-14 00:05:42 +0000
commitc2047754c300b68c05d65faa8dc2925fe67b71b4 (patch)
treee183ae81a1f48a02945cb6de463a70c5be1b06f6 /libgo/go/os
parent829afb8f05602bb31c9c597b24df7377fed4f059 (diff)
downloadgcc-c2047754c300b68c05d65faa8dc2925fe67b71b4.zip
gcc-c2047754c300b68c05d65faa8dc2925fe67b71b4.tar.gz
gcc-c2047754c300b68c05d65faa8dc2925fe67b71b4.tar.bz2
libgo: update to Go 1.8 release candidate 1
Compiler changes: * Change map assignment to use mapassign and assign value directly. * Change string iteration to use decoderune, faster for ASCII strings. * Change makeslice to take int, and use makeslice64 for larger values. * Add new noverflow field to hmap struct used for maps. Unresolved problems, to be fixed later: * Commented out test in go/types/sizes_test.go that doesn't compile. * Commented out reflect.TestStructOf test for padding after zero-sized field. Reviewed-on: https://go-review.googlesource.com/35231 gotools/: Updates for Go 1.8rc1. * Makefile.am (go_cmd_go_files): Add bug.go. (s-zdefaultcc): Write defaultPkgConfig. * Makefile.in: Rebuild. From-SVN: r244456
Diffstat (limited to 'libgo/go/os')
-rw-r--r--libgo/go/os/dir.go128
-rw-r--r--libgo/go/os/dir_gccgo.go102
-rw-r--r--libgo/go/os/dir_unix.go38
-rw-r--r--libgo/go/os/doc.go139
-rw-r--r--libgo/go/os/env.go3
-rw-r--r--libgo/go/os/env_test.go28
-rw-r--r--libgo/go/os/env_unix_test.go26
-rw-r--r--libgo/go/os/error.go14
-rw-r--r--libgo/go/os/error_plan9.go48
-rw-r--r--libgo/go/os/error_test.go2
-rw-r--r--libgo/go/os/error_unix.go33
-rw-r--r--libgo/go/os/error_windows.go34
-rw-r--r--libgo/go/os/error_windows_test.go4
-rw-r--r--libgo/go/os/example_test.go106
-rw-r--r--libgo/go/os/exec.go87
-rw-r--r--libgo/go/os/exec/exec.go63
-rw-r--r--libgo/go/os/exec/exec_test.go62
-rw-r--r--libgo/go/os/exec_windows.go4
-rw-r--r--libgo/go/os/executable.go23
-rw-r--r--libgo/go/os/executable_darwin.go24
-rw-r--r--libgo/go/os/executable_freebsd.go33
-rw-r--r--libgo/go/os/executable_plan9.go19
-rw-r--r--libgo/go/os/executable_procfs.go36
-rw-r--r--libgo/go/os/executable_solaris.go27
-rw-r--r--libgo/go/os/executable_test.go87
-rw-r--r--libgo/go/os/executable_windows.go32
-rw-r--r--libgo/go/os/export_windows_test.go13
-rw-r--r--libgo/go/os/file.go47
-rw-r--r--libgo/go/os/file_plan9.go14
-rw-r--r--libgo/go/os/file_posix.go22
-rw-r--r--libgo/go/os/file_unix.go97
-rw-r--r--libgo/go/os/os_test.go156
-rw-r--r--libgo/go/os/os_unix_test.go39
-rw-r--r--libgo/go/os/path_test.go10
-rw-r--r--libgo/go/os/path_unix.go18
-rw-r--r--libgo/go/os/path_windows.go193
-rw-r--r--libgo/go/os/path_windows_test.go46
-rw-r--r--libgo/go/os/stat_plan9.go10
-rw-r--r--libgo/go/os/stat_unix.go52
-rw-r--r--libgo/go/os/sys.go10
-rw-r--r--libgo/go/os/types.go5
-rw-r--r--libgo/go/os/types_plan9.go13
-rw-r--r--libgo/go/os/types_unix.go6
-rw-r--r--libgo/go/os/user/user.go36
-rw-r--r--libgo/go/os/wait_wait6.go1
-rw-r--r--libgo/go/os/wait_waitid.go2
46 files changed, 1474 insertions, 518 deletions
diff --git a/libgo/go/os/dir.go b/libgo/go/os/dir.go
index d811c9f..6c54456 100644
--- a/libgo/go/os/dir.go
+++ b/libgo/go/os/dir.go
@@ -1,102 +1,46 @@
-// Copyright 2009 The Go Authors. All rights reserved.
+// 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.
package os
-import (
- "io"
- "sync/atomic"
- "syscall"
- "unsafe"
-)
-
-//extern opendir
-func libc_opendir(*byte) *syscall.DIR
-
-//extern closedir
-func libc_closedir(*syscall.DIR) int
-
-// FIXME: pathconf returns long, not int.
-//extern pathconf
-func libc_pathconf(*byte, int) int
-
-func clen(n []byte) int {
- for i := 0; i < len(n); i++ {
- if n[i] == 0 {
- return i
- }
+// Readdir reads the contents of the directory associated with file and
+// returns a slice of up to n FileInfo values, as would be returned
+// by Lstat, in directory order. Subsequent calls on the same file will yield
+// further FileInfos.
+//
+// If n > 0, Readdir returns at most n FileInfo structures. In this case, if
+// Readdir returns an empty slice, it will return a non-nil error
+// explaining why. At the end of a directory, the error is io.EOF.
+//
+// If n <= 0, Readdir returns all the FileInfo from the directory in
+// a single slice. In this case, if Readdir succeeds (reads all
+// the way to the end of the directory), it returns the slice and a
+// 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) ([]FileInfo, error) {
+ if f == nil {
+ return nil, ErrInvalid
}
- return len(n)
+ return f.readdir(n)
}
-var nameMax int32
-
-func (file *File) readdirnames(n int) (names []string, err error) {
- if file.dirinfo == nil {
- p, err := syscall.BytePtrFromString(file.name)
- if err != nil {
- return nil, err
- }
-
- elen := int(atomic.LoadInt32(&nameMax))
- if elen == 0 {
- syscall.Entersyscall()
- plen := libc_pathconf(p, syscall.PC_NAME_MAX)
- syscall.Exitsyscall()
- if plen < 1024 {
- plen = 1024
- }
- var dummy syscall.Dirent
- elen = int(unsafe.Offsetof(dummy.Name)) + plen + 1
- atomic.StoreInt32(&nameMax, int32(elen))
- }
-
- syscall.Entersyscall()
- r := libc_opendir(p)
- errno := syscall.GetErrno()
- syscall.Exitsyscall()
- if r == nil {
- return nil, &PathError{"opendir", file.name, errno}
- }
-
- file.dirinfo = new(dirInfo)
- file.dirinfo.buf = make([]byte, elen)
- file.dirinfo.dir = r
- }
-
- entryDirent := (*syscall.Dirent)(unsafe.Pointer(&file.dirinfo.buf[0]))
-
- size := n
- if size <= 0 {
- size = 100
- n = -1
- }
-
- names = make([]string, 0, size) // Empty with room to grow.
-
- for n != 0 {
- var dirent *syscall.Dirent
- pr := &dirent
- syscall.Entersyscall()
- i := libc_readdir_r(file.dirinfo.dir, entryDirent, pr)
- syscall.Exitsyscall()
- if i != 0 {
- return names, NewSyscallError("readdir_r", i)
- }
- if dirent == nil {
- break // EOF
- }
- bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
- var name = string(bytes[0:clen(bytes[:])])
- if name == "." || name == ".." { // Useless names
- continue
- }
- names = append(names, name)
- n--
- }
- if n >= 0 && len(names) == 0 {
- return names, io.EOF
+// Readdirnames reads and returns a slice of names from the directory f.
+//
+// If n > 0, Readdirnames returns at most n names. In this case, if
+// Readdirnames returns an empty slice, it will return a non-nil error
+// explaining why. At the end of a directory, the error is io.EOF.
+//
+// If n <= 0, Readdirnames returns all the names from the directory in
+// a single slice. In this case, if Readdirnames succeeds (reads all
+// the way to the end of the directory), it returns the slice and a
+// nil error. If it encounters an error before the end of the
+// directory, Readdirnames returns the names read until that point and
+// a non-nil error.
+func (f *File) Readdirnames(n int) (names []string, err error) {
+ if f == nil {
+ return nil, ErrInvalid
}
- return names, nil
+ return f.readdirnames(n)
}
diff --git a/libgo/go/os/dir_gccgo.go b/libgo/go/os/dir_gccgo.go
new file mode 100644
index 0000000..d811c9f
--- /dev/null
+++ b/libgo/go/os/dir_gccgo.go
@@ -0,0 +1,102 @@
+// 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 os
+
+import (
+ "io"
+ "sync/atomic"
+ "syscall"
+ "unsafe"
+)
+
+//extern opendir
+func libc_opendir(*byte) *syscall.DIR
+
+//extern closedir
+func libc_closedir(*syscall.DIR) int
+
+// FIXME: pathconf returns long, not int.
+//extern pathconf
+func libc_pathconf(*byte, int) int
+
+func clen(n []byte) int {
+ for i := 0; i < len(n); i++ {
+ if n[i] == 0 {
+ return i
+ }
+ }
+ return len(n)
+}
+
+var nameMax int32
+
+func (file *File) readdirnames(n int) (names []string, err error) {
+ if file.dirinfo == nil {
+ p, err := syscall.BytePtrFromString(file.name)
+ if err != nil {
+ return nil, err
+ }
+
+ elen := int(atomic.LoadInt32(&nameMax))
+ if elen == 0 {
+ syscall.Entersyscall()
+ plen := libc_pathconf(p, syscall.PC_NAME_MAX)
+ syscall.Exitsyscall()
+ if plen < 1024 {
+ plen = 1024
+ }
+ var dummy syscall.Dirent
+ elen = int(unsafe.Offsetof(dummy.Name)) + plen + 1
+ atomic.StoreInt32(&nameMax, int32(elen))
+ }
+
+ syscall.Entersyscall()
+ r := libc_opendir(p)
+ errno := syscall.GetErrno()
+ syscall.Exitsyscall()
+ if r == nil {
+ return nil, &PathError{"opendir", file.name, errno}
+ }
+
+ file.dirinfo = new(dirInfo)
+ file.dirinfo.buf = make([]byte, elen)
+ file.dirinfo.dir = r
+ }
+
+ entryDirent := (*syscall.Dirent)(unsafe.Pointer(&file.dirinfo.buf[0]))
+
+ size := n
+ if size <= 0 {
+ size = 100
+ n = -1
+ }
+
+ names = make([]string, 0, size) // Empty with room to grow.
+
+ for n != 0 {
+ var dirent *syscall.Dirent
+ pr := &dirent
+ syscall.Entersyscall()
+ i := libc_readdir_r(file.dirinfo.dir, entryDirent, pr)
+ syscall.Exitsyscall()
+ if i != 0 {
+ return names, NewSyscallError("readdir_r", i)
+ }
+ if dirent == nil {
+ break // EOF
+ }
+ bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
+ var name = string(bytes[0:clen(bytes[:])])
+ if name == "." || name == ".." { // Useless names
+ continue
+ }
+ names = append(names, name)
+ n--
+ }
+ if n >= 0 && len(names) == 0 {
+ return names, io.EOF
+ }
+ return names, nil
+}
diff --git a/libgo/go/os/dir_unix.go b/libgo/go/os/dir_unix.go
new file mode 100644
index 0000000..cd42f59
--- /dev/null
+++ b/libgo/go/os/dir_unix.go
@@ -0,0 +1,38 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
+
+package os
+
+import (
+ "io"
+)
+
+func (f *File) readdir(n int) (fi []FileInfo, err error) {
+ dirname := f.name
+ if dirname == "" {
+ dirname = "."
+ }
+ names, err := f.Readdirnames(n)
+ fi = make([]FileInfo, 0, len(names))
+ for _, filename := range names {
+ fip, lerr := lstat(dirname + "/" + filename)
+ if IsNotExist(lerr) {
+ // File disappeared between readdir + stat.
+ // Just treat it as if it didn't exist.
+ continue
+ }
+ if lerr != nil {
+ return fi, lerr
+ }
+ fi = append(fi, fip)
+ }
+ if len(fi) == 0 && err == nil && n > 0 {
+ // Per File.Readdir, the slice must be non-empty or err
+ // must be non-nil if n > 0.
+ err = io.EOF
+ }
+ return fi, err
+}
diff --git a/libgo/go/os/doc.go b/libgo/go/os/doc.go
deleted file mode 100644
index 0313eac..0000000
--- a/libgo/go/os/doc.go
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package os
-
-import "time"
-
-// FindProcess looks for a running process by its pid.
-//
-// The Process it returns can be used to obtain information
-// about the underlying operating system process.
-//
-// On Unix systems, FindProcess always succeeds and returns a Process
-// for the given pid, regardless of whether the process exists.
-func FindProcess(pid int) (*Process, error) {
- return findProcess(pid)
-}
-
-// StartProcess starts a new process with the program, arguments and attributes
-// specified by name, argv and attr.
-//
-// StartProcess is a low-level interface. The os/exec package provides
-// higher-level interfaces.
-//
-// If there is an error, it will be of type *PathError.
-func StartProcess(name string, argv []string, attr *ProcAttr) (*Process, error) {
- return startProcess(name, argv, attr)
-}
-
-// Release releases any resources associated with the Process p,
-// rendering it unusable in the future.
-// Release only needs to be called if Wait is not.
-func (p *Process) Release() error {
- return p.release()
-}
-
-// Kill causes the Process to exit immediately.
-func (p *Process) Kill() error {
- return p.kill()
-}
-
-// Wait waits for the Process to exit, and then returns a
-// ProcessState describing its status and an error, if any.
-// Wait releases any resources associated with the Process.
-// On most operating systems, the Process must be a child
-// of the current process or an error will be returned.
-func (p *Process) Wait() (*ProcessState, error) {
- return p.wait()
-}
-
-// Signal sends a signal to the Process.
-// Sending Interrupt on Windows is not implemented.
-func (p *Process) Signal(sig Signal) error {
- return p.signal(sig)
-}
-
-// UserTime returns the user CPU time of the exited process and its children.
-func (p *ProcessState) UserTime() time.Duration {
- return p.userTime()
-}
-
-// SystemTime returns the system CPU time of the exited process and its children.
-func (p *ProcessState) SystemTime() time.Duration {
- return p.systemTime()
-}
-
-// Exited reports whether the program has exited.
-func (p *ProcessState) Exited() bool {
- return p.exited()
-}
-
-// Success reports whether the program exited successfully,
-// such as with exit status 0 on Unix.
-func (p *ProcessState) Success() bool {
- return p.success()
-}
-
-// Sys returns system-dependent exit information about
-// 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
-// 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.)
-func (p *ProcessState) SysUsage() interface{} {
- return p.sysUsage()
-}
-
-// Hostname returns the host name reported by the kernel.
-func Hostname() (name string, err error) {
- return hostname()
-}
-
-// Readdir reads the contents of the directory associated with file and
-// returns a slice of up to n FileInfo values, as would be returned
-// by Lstat, in directory order. Subsequent calls on the same file will yield
-// further FileInfos.
-//
-// If n > 0, Readdir returns at most n FileInfo structures. In this case, if
-// Readdir returns an empty slice, it will return a non-nil error
-// explaining why. At the end of a directory, the error is io.EOF.
-//
-// If n <= 0, Readdir returns all the FileInfo from the directory in
-// a single slice. In this case, if Readdir succeeds (reads all
-// the way to the end of the directory), it returns the slice and a
-// 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) ([]FileInfo, error) {
- if f == nil {
- return nil, ErrInvalid
- }
- return f.readdir(n)
-}
-
-// Readdirnames reads and returns a slice of names from the directory f.
-//
-// If n > 0, Readdirnames returns at most n names. In this case, if
-// Readdirnames returns an empty slice, it will return a non-nil error
-// explaining why. At the end of a directory, the error is io.EOF.
-//
-// If n <= 0, Readdirnames returns all the names from the directory in
-// a single slice. In this case, if Readdirnames succeeds (reads all
-// the way to the end of the directory), it returns the slice and a
-// nil error. If it encounters an error before the end of the
-// directory, Readdirnames returns the names read until that point and
-// a non-nil error.
-func (f *File) Readdirnames(n int) (names []string, err error) {
- if f == nil {
- return nil, ErrInvalid
- }
- return f.readdirnames(n)
-}
diff --git a/libgo/go/os/env.go b/libgo/go/os/env.go
index 4a14714..a03b8f6 100644
--- a/libgo/go/os/env.go
+++ b/libgo/go/os/env.go
@@ -37,7 +37,7 @@ func ExpandEnv(s string) string {
// shell variable such as $*.
func isShellSpecialVar(c uint8) bool {
switch c {
- case '*', '#', '$', '@', '!', '?', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ case '*', '#', '$', '@', '!', '?', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
return true
}
return false
@@ -76,6 +76,7 @@ func getShellName(s string) (string, int) {
// Getenv retrieves the value of the environment variable named by the key.
// It returns the value, which will be empty if the variable is not present.
+// To distinguish between an empty value and an unset value, use LookupEnv.
func Getenv(key string) string {
v, _ := syscall.Getenv(key)
return v
diff --git a/libgo/go/os/env_test.go b/libgo/go/os/env_test.go
index d1074cd..e5749f0 100644
--- a/libgo/go/os/env_test.go
+++ b/libgo/go/os/env_test.go
@@ -95,6 +95,34 @@ func TestUnsetenv(t *testing.T) {
}
}
+func TestClearenv(t *testing.T) {
+ const testKey = "GO_TEST_CLEARENV"
+ const testValue = "1"
+
+ // reset env
+ defer func(origEnv []string) {
+ for _, pair := range origEnv {
+ // Environment variables on Windows can begin with =
+ // http://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx
+ i := strings.Index(pair[1:], "=") + 1
+ if err := Setenv(pair[:i], pair[i+1:]); err != nil {
+ t.Errorf("Setenv(%q, %q) failed during reset: %v", pair[:i], pair[i+1:], err)
+ }
+ }
+ }(Environ())
+
+ if err := Setenv(testKey, testValue); err != nil {
+ t.Fatalf("Setenv(%q, %q) failed: %v", testKey, testValue, err)
+ }
+ if _, ok := LookupEnv(testKey); !ok {
+ t.Errorf("Setenv(%q, %q) didn't set $%s", testKey, testValue, testKey)
+ }
+ Clearenv()
+ if val, ok := LookupEnv(testKey); ok {
+ t.Errorf("Clearenv() didn't clear $%s, remained with value %q", testKey, val)
+ }
+}
+
func TestLookupEnv(t *testing.T) {
const smallpox = "SMALLPOX" // No one has smallpox.
value, ok := LookupEnv(smallpox) // Should not exist.
diff --git a/libgo/go/os/env_unix_test.go b/libgo/go/os/env_unix_test.go
index 5ec07ee..f7b67eb 100644
--- a/libgo/go/os/env_unix_test.go
+++ b/libgo/go/os/env_unix_test.go
@@ -7,6 +7,7 @@
package os_test
import (
+ "fmt"
. "os"
"testing"
)
@@ -28,3 +29,28 @@ func TestSetenvUnixEinval(t *testing.T) {
}
}
}
+
+var shellSpecialVarTests = []struct {
+ k, v string
+}{
+ {"*", "asterisk"},
+ {"#", "pound"},
+ {"$", "dollar"},
+ {"@", "at"},
+ {"!", "exclamation mark"},
+ {"?", "question mark"},
+ {"-", "dash"},
+}
+
+func TestExpandEnvShellSpecialVar(t *testing.T) {
+ for _, tt := range shellSpecialVarTests {
+ Setenv(tt.k, tt.v)
+ defer Unsetenv(tt.k)
+
+ argRaw := fmt.Sprintf("$%s", tt.k)
+ argWithBrace := fmt.Sprintf("${%s}", tt.k)
+ if gotRaw, gotBrace := ExpandEnv(argRaw), ExpandEnv(argWithBrace); gotRaw != gotBrace {
+ t.Errorf("ExpandEnv(%q) = %q, ExpandEnv(%q) = %q; expect them to be equal", argRaw, gotRaw, argWithBrace, gotBrace)
+ }
+ }
+}
diff --git a/libgo/go/os/error.go b/libgo/go/os/error.go
index e26ce27..7235bfb 100644
--- a/libgo/go/os/error.go
+++ b/libgo/go/os/error.go
@@ -14,6 +14,7 @@ var (
ErrPermission = errors.New("permission denied")
ErrExist = errors.New("file already exists")
ErrNotExist = errors.New("file does not exist")
+ ErrClosed = errors.New("file already closed")
)
// PathError records an error and the operation and file path that caused it.
@@ -63,3 +64,16 @@ func IsNotExist(err error) bool {
func IsPermission(err error) bool {
return isPermission(err)
}
+
+// underlyingError returns the underlying error for known os error types.
+func underlyingError(err error) error {
+ switch err := err.(type) {
+ case *PathError:
+ return err.Err
+ case *LinkError:
+ return err.Err
+ case *SyscallError:
+ return err.Err
+ }
+ return err
+}
diff --git a/libgo/go/os/error_plan9.go b/libgo/go/os/error_plan9.go
index 2dc6b39..a673439 100644
--- a/libgo/go/os/error_plan9.go
+++ b/libgo/go/os/error_plan9.go
@@ -5,46 +5,30 @@
package os
func isExist(err error) bool {
- switch pe := err.(type) {
- case nil:
- return false
- case *PathError:
- err = pe.Err
- case *LinkError:
- err = pe.Err
- case *SyscallError:
- err = pe.Err
- }
- return contains(err.Error(), " exists")
+ return checkErrMessageContent(err, " exists")
}
func isNotExist(err error) bool {
- switch pe := err.(type) {
- case nil:
- return false
- case *PathError:
- err = pe.Err
- case *LinkError:
- err = pe.Err
- case *SyscallError:
- err = pe.Err
- }
- return contains(err.Error(), "does not exist") || contains(err.Error(), "not found") ||
- contains(err.Error(), "has been removed") || contains(err.Error(), "no parent")
+ return checkErrMessageContent(err, "does not exist", "not found",
+ "has been removed", "no parent")
}
func isPermission(err error) bool {
- switch pe := err.(type) {
- case nil:
+ return checkErrMessageContent(err, "permission denied")
+}
+
+// checkErrMessageContent checks if err message contains one of msgs.
+func checkErrMessageContent(err error, msgs ...string) bool {
+ if err == nil {
return false
- case *PathError:
- err = pe.Err
- case *LinkError:
- err = pe.Err
- case *SyscallError:
- err = pe.Err
}
- return contains(err.Error(), "permission denied")
+ err = underlyingError(err)
+ for _, msg := range msgs {
+ if contains(err.Error(), msg) {
+ return true
+ }
+ }
+ return false
}
// contains is a local version of strings.Contains. It knows len(sep) > 1.
diff --git a/libgo/go/os/error_test.go b/libgo/go/os/error_test.go
index a47c173..3499cee 100644
--- a/libgo/go/os/error_test.go
+++ b/libgo/go/os/error_test.go
@@ -91,10 +91,12 @@ var isExistTests = []isExistTest{
{&os.PathError{Err: os.ErrPermission}, false, false},
{&os.PathError{Err: os.ErrExist}, true, false},
{&os.PathError{Err: os.ErrNotExist}, false, true},
+ {&os.PathError{Err: os.ErrClosed}, false, false},
{&os.LinkError{Err: os.ErrInvalid}, false, false},
{&os.LinkError{Err: os.ErrPermission}, false, false},
{&os.LinkError{Err: os.ErrExist}, true, false},
{&os.LinkError{Err: os.ErrNotExist}, false, true},
+ {&os.LinkError{Err: os.ErrClosed}, false, false},
{&os.SyscallError{Err: os.ErrNotExist}, false, true},
{&os.SyscallError{Err: os.ErrExist}, true, false},
{nil, false, false},
diff --git a/libgo/go/os/error_unix.go b/libgo/go/os/error_unix.go
index 3c78eb4..be1440c 100644
--- a/libgo/go/os/error_unix.go
+++ b/libgo/go/os/error_unix.go
@@ -9,43 +9,16 @@ package os
import "syscall"
func isExist(err error) bool {
- switch pe := err.(type) {
- case nil:
- return false
- case *PathError:
- err = pe.Err
- case *LinkError:
- err = pe.Err
- case *SyscallError:
- err = pe.Err
- }
+ err = underlyingError(err)
return err == syscall.EEXIST || err == syscall.ENOTEMPTY || err == ErrExist
}
func isNotExist(err error) bool {
- switch pe := err.(type) {
- case nil:
- return false
- case *PathError:
- err = pe.Err
- case *LinkError:
- err = pe.Err
- case *SyscallError:
- err = pe.Err
- }
+ err = underlyingError(err)
return err == syscall.ENOENT || err == ErrNotExist
}
func isPermission(err error) bool {
- switch pe := err.(type) {
- case nil:
- return false
- case *PathError:
- err = pe.Err
- case *LinkError:
- err = pe.Err
- case *SyscallError:
- err = pe.Err
- }
+ err = underlyingError(err)
return err == syscall.EACCES || err == syscall.EPERM || err == ErrPermission
}
diff --git a/libgo/go/os/error_windows.go b/libgo/go/os/error_windows.go
index 2c1c39c..02593b5 100644
--- a/libgo/go/os/error_windows.go
+++ b/libgo/go/os/error_windows.go
@@ -7,48 +7,22 @@ package os
import "syscall"
func isExist(err error) bool {
- switch pe := err.(type) {
- case nil:
- return false
- case *PathError:
- err = pe.Err
- case *LinkError:
- err = pe.Err
- case *SyscallError:
- err = pe.Err
- }
+ err = underlyingError(err)
return err == syscall.ERROR_ALREADY_EXISTS ||
+ err == syscall.ERROR_DIR_NOT_EMPTY ||
err == syscall.ERROR_FILE_EXISTS || err == ErrExist
}
const _ERROR_BAD_NETPATH = syscall.Errno(53)
func isNotExist(err error) bool {
- switch pe := err.(type) {
- case nil:
- return false
- case *PathError:
- err = pe.Err
- case *LinkError:
- err = pe.Err
- case *SyscallError:
- err = pe.Err
- }
+ err = underlyingError(err)
return err == syscall.ERROR_FILE_NOT_FOUND ||
err == _ERROR_BAD_NETPATH ||
err == syscall.ERROR_PATH_NOT_FOUND || err == ErrNotExist
}
func isPermission(err error) bool {
- switch pe := err.(type) {
- case nil:
- return false
- case *PathError:
- err = pe.Err
- case *LinkError:
- err = pe.Err
- case *SyscallError:
- err = pe.Err
- }
+ err = underlyingError(err)
return err == syscall.ERROR_ACCESS_DENIED || err == ErrPermission
}
diff --git a/libgo/go/os/error_windows_test.go b/libgo/go/os/error_windows_test.go
index 427dfdb..1635c10 100644
--- a/libgo/go/os/error_windows_test.go
+++ b/libgo/go/os/error_windows_test.go
@@ -26,6 +26,10 @@ func init() {
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},
+
+ isExistTest{err: &os.PathError{Err: syscall.ERROR_DIR_NOT_EMPTY}, is: true, isnot: false},
+ isExistTest{err: &os.LinkError{Err: syscall.ERROR_DIR_NOT_EMPTY}, is: true, isnot: false},
+ isExistTest{err: &os.SyscallError{Err: syscall.ERROR_DIR_NOT_EMPTY}, is: true, isnot: false},
)
isPermissionTests = append(isPermissionTests,
isPermissionTest{err: &os.PathError{Err: syscall.ERROR_ACCESS_DENIED}, want: true},
diff --git a/libgo/go/os/example_test.go b/libgo/go/os/example_test.go
new file mode 100644
index 0000000..07f9c76
--- /dev/null
+++ b/libgo/go/os/example_test.go
@@ -0,0 +1,106 @@
+// 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.
+
+package os_test
+
+import (
+ "fmt"
+ "log"
+ "os"
+ "time"
+)
+
+func ExampleOpenFile() {
+ f, err := os.OpenFile("notes.txt", os.O_RDWR|os.O_CREATE, 0755)
+ if err != nil {
+ log.Fatal(err)
+ }
+ if err := f.Close(); err != nil {
+ log.Fatal(err)
+ }
+}
+
+func ExampleChmod() {
+ if err := os.Chmod("some-filename", 0644); err != nil {
+ log.Fatal(err)
+ }
+}
+
+func ExampleChtimes() {
+ mtime := time.Date(2006, time.February, 1, 3, 4, 5, 0, time.UTC)
+ atime := time.Date(2007, time.March, 2, 4, 5, 6, 0, time.UTC)
+ if err := os.Chtimes("some-filename", atime, mtime); err != nil {
+ log.Fatal(err)
+ }
+}
+
+func ExampleFileMode() {
+ fi, err := os.Stat("some-filename")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ switch mode := fi.Mode(); {
+ case mode.IsRegular():
+ fmt.Println("regular file")
+ case mode.IsDir():
+ fmt.Println("directory")
+ case mode&os.ModeSymlink != 0:
+ fmt.Println("symbolic link")
+ case mode&os.ModeNamedPipe != 0:
+ fmt.Println("named pipe")
+ }
+}
+
+func ExampleIsNotExist() {
+ filename := "a-nonexistent-file"
+ if _, err := os.Stat(filename); os.IsNotExist(err) {
+ fmt.Printf("file does not exist")
+ }
+ // Output:
+ // file does not exist
+}
+
+func init() {
+ os.Setenv("USER", "gopher")
+ os.Setenv("HOME", "/usr/gopher")
+ os.Unsetenv("GOPATH")
+}
+
+func ExampleExpandEnv() {
+ fmt.Println(os.ExpandEnv("$USER lives in ${HOME}."))
+
+ // Output:
+ // gopher lives in /usr/gopher.
+}
+
+func ExampleLookupEnv() {
+ show := func(key string) {
+ val, ok := os.LookupEnv(key)
+ if !ok {
+ fmt.Printf("%s not set\n", key)
+ } else {
+ fmt.Printf("%s=%s\n", key, val)
+ }
+ }
+
+ show("USER")
+ show("GOPATH")
+
+ // Output:
+ // USER=gopher
+ // GOPATH not set
+}
+
+func ExampleGetenv() {
+ fmt.Printf("%s lives in %s.\n", os.Getenv("USER"), os.Getenv("HOME"))
+
+ // Output:
+ // gopher lives in /usr/gopher.
+}
+
+func ExampleUnsetenv() {
+ os.Setenv("TMPDIR", "/my/tmp")
+ defer os.Unsetenv("TMPDIR")
+}
diff --git a/libgo/go/os/exec.go b/libgo/go/os/exec.go
index bf32498..8a53e5d 100644
--- a/libgo/go/os/exec.go
+++ b/libgo/go/os/exec.go
@@ -9,6 +9,7 @@ import (
"sync"
"sync/atomic"
"syscall"
+ "time"
)
// Process stores the information about a process created by StartProcess.
@@ -70,3 +71,89 @@ func Getpid() int { return syscall.Getpid() }
// Getppid returns the process id of the caller's parent.
func Getppid() int { return syscall.Getppid() }
+
+// FindProcess looks for a running process by its pid.
+//
+// The Process it returns can be used to obtain information
+// about the underlying operating system process.
+//
+// On Unix systems, FindProcess always succeeds and returns a Process
+// for the given pid, regardless of whether the process exists.
+func FindProcess(pid int) (*Process, error) {
+ return findProcess(pid)
+}
+
+// StartProcess starts a new process with the program, arguments and attributes
+// specified by name, argv and attr.
+//
+// StartProcess is a low-level interface. The os/exec package provides
+// higher-level interfaces.
+//
+// If there is an error, it will be of type *PathError.
+func StartProcess(name string, argv []string, attr *ProcAttr) (*Process, error) {
+ return startProcess(name, argv, attr)
+}
+
+// Release releases any resources associated with the Process p,
+// rendering it unusable in the future.
+// Release only needs to be called if Wait is not.
+func (p *Process) Release() error {
+ return p.release()
+}
+
+// Kill causes the Process to exit immediately.
+func (p *Process) Kill() error {
+ return p.kill()
+}
+
+// Wait waits for the Process to exit, and then returns a
+// ProcessState describing its status and an error, if any.
+// Wait releases any resources associated with the Process.
+// On most operating systems, the Process must be a child
+// of the current process or an error will be returned.
+func (p *Process) Wait() (*ProcessState, error) {
+ return p.wait()
+}
+
+// Signal sends a signal to the Process.
+// Sending Interrupt on Windows is not implemented.
+func (p *Process) Signal(sig Signal) error {
+ return p.signal(sig)
+}
+
+// UserTime returns the user CPU time of the exited process and its children.
+func (p *ProcessState) UserTime() time.Duration {
+ return p.userTime()
+}
+
+// SystemTime returns the system CPU time of the exited process and its children.
+func (p *ProcessState) SystemTime() time.Duration {
+ return p.systemTime()
+}
+
+// Exited reports whether the program has exited.
+func (p *ProcessState) Exited() bool {
+ return p.exited()
+}
+
+// Success reports whether the program exited successfully,
+// such as with exit status 0 on Unix.
+func (p *ProcessState) Success() bool {
+ return p.success()
+}
+
+// Sys returns system-dependent exit information about
+// 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
+// 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.)
+func (p *ProcessState) SysUsage() interface{} {
+ return p.sysUsage()
+}
diff --git a/libgo/go/os/exec/exec.go b/libgo/go/os/exec/exec.go
index d2c1b17..c4c5168 100644
--- a/libgo/go/os/exec/exec.go
+++ b/libgo/go/os/exec/exec.go
@@ -120,12 +120,13 @@ type Cmd struct {
// It sets only the Path and Args in the returned structure.
//
// If name contains no path separators, Command uses LookPath to
-// resolve the path to a complete name if possible. Otherwise it uses
-// name directly.
+// resolve name to a complete path if possible. Otherwise it uses name
+// directly as Path.
//
// The returned Cmd's Args field is constructed from the command name
// followed by the elements of arg, so arg should not include the
-// command name itself. For example, Command("echo", "hello")
+// command name itself. For example, Command("echo", "hello").
+// Args[0] is always name, not the possibly resolved Path.
func Command(name string, arg ...string) *Cmd {
cmd := &Cmd{
Path: name,
@@ -515,15 +516,16 @@ func (c *Cmd) StdinPipe() (io.WriteCloser, error) {
c.Stdin = pr
c.closeAfterStart = append(c.closeAfterStart, pr)
wc := &closeOnce{File: pw}
- c.closeAfterWait = append(c.closeAfterWait, wc)
+ c.closeAfterWait = append(c.closeAfterWait, closerFunc(wc.safeClose))
return wc, nil
}
type closeOnce struct {
*os.File
- once sync.Once
- err error
+ writers sync.RWMutex // coordinate safeClose and Write
+ once sync.Once
+ err error
}
func (c *closeOnce) Close() error {
@@ -535,6 +537,55 @@ func (c *closeOnce) close() {
c.err = c.File.Close()
}
+type closerFunc func() error
+
+func (f closerFunc) Close() error { return f() }
+
+// safeClose closes c being careful not to race with any calls to c.Write.
+// See golang.org/issue/9307 and TestEchoFileRace in exec_test.go.
+// In theory other calls could also be excluded (by writing appropriate
+// wrappers like c.Write's implementation below), but since c is most
+// commonly used as a WriteCloser, Write is the main one to worry about.
+// See also #7970, for which this is a partial fix for this specific instance.
+// The idea is that we return a WriteCloser, and so the caller can be
+// relied upon not to call Write and Close simultaneously, but it's less
+// obvious that cmd.Wait calls Close and that the caller must not call
+// Write and cmd.Wait simultaneously. In fact that seems too onerous.
+// So we change the use of Close in cmd.Wait to use safeClose, which will
+// synchronize with any Write.
+//
+// It's important that we know this won't block forever waiting for the
+// operations being excluded. At the point where this is called,
+// the invoked command has exited and the parent copy of the read side
+// of the pipe has also been closed, so there should really be no read side
+// of the pipe left. Any active writes should return very shortly with an EPIPE,
+// making it reasonable to wait for them.
+// Technically it is possible that the child forked a sub-process or otherwise
+// handed off the read side of the pipe before exiting and the current holder
+// is not reading from the pipe, and the pipe is full, in which case the close here
+// might block waiting for the write to complete. That's probably OK.
+// It's a small enough problem to be outweighed by eliminating the race here.
+func (c *closeOnce) safeClose() error {
+ c.writers.Lock()
+ err := c.Close()
+ c.writers.Unlock()
+ return err
+}
+
+func (c *closeOnce) Write(b []byte) (int, error) {
+ c.writers.RLock()
+ n, err := c.File.Write(b)
+ c.writers.RUnlock()
+ return n, err
+}
+
+func (c *closeOnce) WriteString(s string) (int, error) {
+ c.writers.RLock()
+ n, err := c.File.WriteString(s)
+ c.writers.RUnlock()
+ return n, err
+}
+
// StdoutPipe returns a pipe that will be connected to the command's
// standard output when the command starts.
//
diff --git a/libgo/go/os/exec/exec_test.go b/libgo/go/os/exec/exec_test.go
index 3a866a9..a9cceec 100644
--- a/libgo/go/os/exec/exec_test.go
+++ b/libgo/go/os/exec/exec_test.go
@@ -105,6 +105,26 @@ func TestCatStdin(t *testing.T) {
}
}
+func TestEchoFileRace(t *testing.T) {
+ cmd := helperCommand(t, "echo")
+ stdin, err := cmd.StdinPipe()
+ if err != nil {
+ t.Fatalf("StdinPipe: %v", err)
+ }
+ if err := cmd.Start(); err != nil {
+ t.Fatalf("Start: %v", err)
+ }
+ wrote := make(chan bool)
+ go func() {
+ defer close(wrote)
+ fmt.Fprint(stdin, "echo\n")
+ }()
+ if err := cmd.Wait(); err != nil {
+ t.Fatalf("Wait: %v", err)
+ }
+ <-wrote
+}
+
func TestCatGoodAndBadFile(t *testing.T) {
// Testing combined output and error values.
bs, err := helperCommand(t, "cat", "/bogus/file.foo", "exec_test.go").CombinedOutput()
@@ -230,6 +250,42 @@ func TestStdinClose(t *testing.T) {
check("Wait", cmd.Wait())
}
+// Issue 17647.
+// It used to be the case that TestStdinClose, above, would fail when
+// run under the race detector. This test is a variant of TestStdinClose
+// that also used to fail when run under the race detector.
+// This test is run by cmd/dist under the race detector to verify that
+// the race detector no longer reports any problems.
+func TestStdinCloseRace(t *testing.T) {
+ cmd := helperCommand(t, "stdinClose")
+ stdin, err := cmd.StdinPipe()
+ if err != nil {
+ t.Fatalf("StdinPipe: %v", err)
+ }
+ if err := cmd.Start(); err != nil {
+ t.Fatalf("Start: %v", err)
+ }
+ go func() {
+ if err := cmd.Process.Kill(); err != nil {
+ t.Errorf("Kill: %v", err)
+ }
+ }()
+ go func() {
+ // Send the wrong string, so that the child fails even
+ // if the other goroutine doesn't manage to kill it first.
+ // This test is to check that the race detector does not
+ // falsely report an error, so it doesn't matter how the
+ // child process fails.
+ io.Copy(stdin, strings.NewReader("unexpected string"))
+ if err := stdin.Close(); err != nil {
+ t.Errorf("stdin.Close: %v", err)
+ }
+ }()
+ if err := cmd.Wait(); err == nil {
+ t.Fatalf("Wait: succeeded unexpectedly")
+ }
+}
+
// Issue 5071
func TestPipeLookPathLeak(t *testing.T) {
fd0, lsof0 := numOpenFDS(t)
@@ -416,7 +472,7 @@ func TestExtraFilesFDShuffle(t *testing.T) {
buf := make([]byte, 512)
n, err := stderr.Read(buf)
if err != nil {
- t.Fatalf("Read: %s", err)
+ t.Errorf("Read: %s", err)
ch <- err.Error()
} else {
ch <- string(buf[:n])
@@ -936,13 +992,13 @@ func TestContextCancel(t *testing.T) {
break
}
if time.Since(start) > time.Second {
- t.Fatal("cancelling context did not stop program")
+ t.Fatal("canceling 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)
+ t.Errorf("error closing write end of pipe: %v", err)
}
<-readDone
diff --git a/libgo/go/os/exec_windows.go b/libgo/go/os/exec_windows.go
index 72b5a93..d89db20 100644
--- a/libgo/go/os/exec_windows.go
+++ b/libgo/go/os/exec_windows.go
@@ -63,7 +63,9 @@ func (p *Process) signal(sig Signal) error {
return errors.New("os: process already finished")
}
if sig == Kill {
- return terminateProcess(p.Pid, 1)
+ err := terminateProcess(p.Pid, 1)
+ runtime.KeepAlive(p)
+ return err
}
// TODO(rsc): Handle Interrupt too?
return syscall.Errno(syscall.EWINDOWS)
diff --git a/libgo/go/os/executable.go b/libgo/go/os/executable.go
new file mode 100644
index 0000000..8c21246
--- /dev/null
+++ b/libgo/go/os/executable.go
@@ -0,0 +1,23 @@
+// 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.
+
+package os
+
+// Executable returns the path name for the executable that started
+// the current process. There is no guarantee that the path is still
+// pointing to the correct executable. If a symlink was used to start
+// the process, depending on the operating system, the result might
+// be the symlink or the path it pointed to. If a stable result is
+// needed, path/filepath.EvalSymlinks might help.
+//
+// Executable returns an absolute path unless an error occurred.
+//
+// The main use case is finding resources located relative to an
+// executable.
+//
+// Executable is not supported on nacl or OpenBSD (unless procfs is
+// mounted.)
+func Executable() (string, error) {
+ return executable()
+}
diff --git a/libgo/go/os/executable_darwin.go b/libgo/go/os/executable_darwin.go
new file mode 100644
index 0000000..ce5b814
--- /dev/null
+++ b/libgo/go/os/executable_darwin.go
@@ -0,0 +1,24 @@
+// 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.
+
+package os
+
+var executablePath string // set by ../runtime/os_darwin.go
+
+var initCwd, initCwdErr = Getwd()
+
+func executable() (string, error) {
+ ep := executablePath
+ if ep[0] != '/' {
+ if initCwdErr != nil {
+ return ep, initCwdErr
+ }
+ if len(ep) > 2 && ep[0:2] == "./" {
+ // skip "./"
+ ep = ep[2:]
+ }
+ ep = initCwd + "/" + ep
+ }
+ return ep, nil
+}
diff --git a/libgo/go/os/executable_freebsd.go b/libgo/go/os/executable_freebsd.go
new file mode 100644
index 0000000..ccaf8e6
--- /dev/null
+++ b/libgo/go/os/executable_freebsd.go
@@ -0,0 +1,33 @@
+// 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.
+
+package os
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+func executable() (string, error) {
+ mib := [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1}
+
+ n := uintptr(0)
+ // get length
+ _, _, err := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
+ if err != 0 {
+ return "", err
+ }
+ if n == 0 { // shouldn't happen
+ return "", nil
+ }
+ buf := make([]byte, n)
+ _, _, err = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0)
+ if err != 0 {
+ return "", err
+ }
+ if n == 0 { // shouldn't happen
+ return "", nil
+ }
+ return string(buf[:n-1]), nil
+}
diff --git a/libgo/go/os/executable_plan9.go b/libgo/go/os/executable_plan9.go
new file mode 100644
index 0000000..a5947ea
--- /dev/null
+++ b/libgo/go/os/executable_plan9.go
@@ -0,0 +1,19 @@
+// 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 plan9
+
+package os
+
+import "syscall"
+
+func executable() (string, error) {
+ fn := "/proc/" + itoa(Getpid()) + "/text"
+ f, err := Open(fn)
+ if err != nil {
+ return "", err
+ }
+ defer f.Close()
+ return syscall.Fd2path(int(f.Fd()))
+}
diff --git a/libgo/go/os/executable_procfs.go b/libgo/go/os/executable_procfs.go
new file mode 100644
index 0000000..69a70e1
--- /dev/null
+++ b/libgo/go/os/executable_procfs.go
@@ -0,0 +1,36 @@
+// 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 linux netbsd openbsd dragonfly nacl
+
+package os
+
+import (
+ "errors"
+ "runtime"
+)
+
+// We query the executable path at init time to avoid the problem of
+// readlink returns a path appended with " (deleted)" when the original
+// binary gets deleted.
+var executablePath, executablePathErr = func() (string, error) {
+ var procfn string
+ switch runtime.GOOS {
+ default:
+ return "", errors.New("Executable not implemented for " + runtime.GOOS)
+ case "linux", "android":
+ procfn = "/proc/self/exe"
+ case "netbsd":
+ procfn = "/proc/curproc/exe"
+ case "openbsd":
+ procfn = "/proc/curproc/file"
+ case "dragonfly":
+ procfn = "/proc/curproc/file"
+ }
+ return Readlink(procfn)
+}()
+
+func executable() (string, error) {
+ return executablePath, executablePathErr
+}
diff --git a/libgo/go/os/executable_solaris.go b/libgo/go/os/executable_solaris.go
new file mode 100644
index 0000000..80f9372
--- /dev/null
+++ b/libgo/go/os/executable_solaris.go
@@ -0,0 +1,27 @@
+// 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.
+
+package os
+
+import "syscall"
+
+var initCwd, initCwdErr = Getwd()
+
+func executable() (string, error) {
+ path, err := syscall.Getexecname()
+ if err != nil {
+ return path, err
+ }
+ if len(path) > 0 && path[0] != '/' {
+ if initCwdErr != nil {
+ return path, initCwdErr
+ }
+ if len(path) > 2 && path[0:2] == "./" {
+ // skip "./"
+ path = path[2:]
+ }
+ return initCwd + "/" + path, nil
+ }
+ return path, nil
+}
diff --git a/libgo/go/os/executable_test.go b/libgo/go/os/executable_test.go
new file mode 100644
index 0000000..a4d8909
--- /dev/null
+++ b/libgo/go/os/executable_test.go
@@ -0,0 +1,87 @@
+// 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.
+
+package os_test
+
+import (
+ "fmt"
+ "internal/testenv"
+ "os"
+ osexec "os/exec"
+ "path/filepath"
+ "runtime"
+ "testing"
+)
+
+const executable_EnvVar = "OSTEST_OUTPUT_EXECPATH"
+
+func TestExecutable(t *testing.T) {
+ testenv.MustHaveExec(t) // will also execlude nacl, which doesn't support Executable anyway
+ ep, err := os.Executable()
+ if err != nil {
+ switch goos := runtime.GOOS; goos {
+ case "openbsd": // procfs is not mounted by default
+ t.Skipf("Executable failed on %s: %v, expected", goos, err)
+ }
+ t.Fatalf("Executable failed: %v", err)
+ }
+ // we want fn to be of the form "dir/prog"
+ dir := filepath.Dir(filepath.Dir(ep))
+ fn, err := filepath.Rel(dir, ep)
+ if err != nil {
+ t.Fatalf("filepath.Rel: %v", err)
+ }
+ cmd := &osexec.Cmd{}
+ // make child start with a relative program path
+ cmd.Dir = dir
+ cmd.Path = fn
+ // forge argv[0] for child, so that we can verify we could correctly
+ // get real path of the executable without influenced by argv[0].
+ cmd.Args = []string{"-", "-test.run=XXXX"}
+ cmd.Env = append(os.Environ(), fmt.Sprintf("%s=1", executable_EnvVar))
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("exec(self) failed: %v", err)
+ }
+ outs := string(out)
+ if !filepath.IsAbs(outs) {
+ t.Fatalf("Child returned %q, want an absolute path", out)
+ }
+ if !sameFile(outs, ep) {
+ t.Fatalf("Child returned %q, not the same file as %q", out, ep)
+ }
+}
+
+func sameFile(fn1, fn2 string) bool {
+ fi1, err := os.Stat(fn1)
+ if err != nil {
+ return false
+ }
+ fi2, err := os.Stat(fn2)
+ if err != nil {
+ return false
+ }
+ return os.SameFile(fi1, fi2)
+}
+
+func init() {
+ if e := os.Getenv(executable_EnvVar); e != "" {
+ // first chdir to another path
+ dir := "/"
+ if runtime.GOOS == "windows" {
+ cwd, err := os.Getwd()
+ if err != nil {
+ panic(err)
+ }
+ dir = filepath.VolumeName(cwd)
+ }
+ os.Chdir(dir)
+ if ep, err := os.Executable(); err != nil {
+ fmt.Fprint(os.Stderr, "ERROR: ", err)
+ } else {
+ fmt.Fprint(os.Stderr, ep)
+ }
+ os.Exit(0)
+ }
+}
diff --git a/libgo/go/os/executable_windows.go b/libgo/go/os/executable_windows.go
new file mode 100644
index 0000000..fc5cf86
--- /dev/null
+++ b/libgo/go/os/executable_windows.go
@@ -0,0 +1,32 @@
+// 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.
+
+package os
+
+import (
+ "internal/syscall/windows"
+ "syscall"
+)
+
+func getModuleFileName(handle syscall.Handle) (string, error) {
+ n := uint32(1024)
+ var buf []uint16
+ for {
+ buf = make([]uint16, n)
+ r, err := windows.GetModuleFileName(handle, &buf[0], n)
+ if err != nil {
+ return "", err
+ }
+ if r < n {
+ break
+ }
+ // r == n means n not big enough
+ n += 1024
+ }
+ return syscall.UTF16ToString(buf), nil
+}
+
+func executable() (string, error) {
+ return getModuleFileName(0)
+}
diff --git a/libgo/go/os/export_windows_test.go b/libgo/go/os/export_windows_test.go
new file mode 100644
index 0000000..3bb2d20
--- /dev/null
+++ b/libgo/go/os/export_windows_test.go
@@ -0,0 +1,13 @@
+// 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.
+
+package os
+
+// Export for testing.
+
+var (
+ FixLongPath = fixLongPath
+ NewConsoleFile = newConsoleFile
+ ReadConsoleFunc = &readConsole
+)
diff --git a/libgo/go/os/file.go b/libgo/go/os/file.go
index e546441..d45a00b 100644
--- a/libgo/go/os/file.go
+++ b/libgo/go/os/file.go
@@ -92,11 +92,11 @@ func (e *LinkError) Error() string {
}
// Read reads up to len(b) bytes from the File.
-// It returns the number of bytes read and an error, if any.
-// EOF is signaled by a zero count with err set to io.EOF.
+// It returns the number of bytes read and any error encountered.
+// At end of file, Read returns 0, io.EOF.
func (f *File) Read(b []byte) (n int, err error) {
- if f == nil {
- return 0, ErrInvalid
+ if err := f.checkValid("read"); err != nil {
+ return 0, err
}
n, e := f.read(b)
if n == 0 && len(b) > 0 && e == nil {
@@ -113,8 +113,8 @@ func (f *File) Read(b []byte) (n int, err error) {
// ReadAt always returns a non-nil error when n < len(b).
// At end of file, that error is io.EOF.
func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
- if f == nil {
- return 0, ErrInvalid
+ if err := f.checkValid("read"); err != nil {
+ return 0, err
}
for len(b) > 0 {
m, e := f.pread(b, off)
@@ -136,8 +136,8 @@ func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
// It returns the number of bytes written and an error, if any.
// Write returns a non-nil error when n != len(b).
func (f *File) Write(b []byte) (n int, err error) {
- if f == nil {
- return 0, ErrInvalid
+ if err := f.checkValid("write"); err != nil {
+ return 0, err
}
n, e := f.write(b)
if n < 0 {
@@ -159,8 +159,8 @@ func (f *File) Write(b []byte) (n int, err error) {
// It returns the number of bytes written and an error, if any.
// WriteAt returns a non-nil error when n != len(b).
func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
- if f == nil {
- return 0, ErrInvalid
+ if err := f.checkValid("write"); err != nil {
+ return 0, err
}
for len(b) > 0 {
m, e := f.pwrite(b, off)
@@ -181,8 +181,8 @@ func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
// It returns the new offset and an error, if any.
// The behavior of Seek on a file opened with O_APPEND is not specified.
func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
- if f == nil {
- return 0, ErrInvalid
+ if err := f.checkValid("seek"); err != nil {
+ return 0, err
}
r, e := f.seek(offset, whence)
if e == nil && f.dirinfo != nil && r != 0 {
@@ -197,16 +197,13 @@ 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) (n int, err error) {
- if f == nil {
- return 0, ErrInvalid
- }
return f.Write([]byte(s))
}
// Mkdir creates a new directory with the specified name and permission bits.
// If there is an error, it will be of type *PathError.
func Mkdir(name string, perm FileMode) error {
- e := syscall.Mkdir(name, syscallMode(perm))
+ e := syscall.Mkdir(fixLongPath(name), syscallMode(perm))
if e != nil {
return &PathError{"mkdir", name, e}
@@ -233,8 +230,8 @@ func Chdir(dir string) error {
// which must be a directory.
// If there is an error, it will be of type *PathError.
func (f *File) Chdir() error {
- if f == nil {
- return ErrInvalid
+ if err := f.checkValid("chdir"); err != nil {
+ return err
}
if e := syscall.Fchdir(f.fd); e != nil {
return &PathError{"chdir", f.name, e}
@@ -263,7 +260,7 @@ func Create(name string) (*File, error) {
var lstat = Lstat
// Rename renames (moves) oldpath to newpath.
-// If newpath already exists, Rename replaces it.
+// If newpath already exists and is not a directory, Rename replaces it.
// OS-specific restrictions may apply when oldpath and newpath are in different directories.
// If there is an error, it will be of type *LinkError.
func Rename(oldpath, newpath string) error {
@@ -278,3 +275,15 @@ func fixCount(n int, err error) (int, error) {
}
return n, err
}
+
+// checkValid checks whether f is valid for use.
+// If not, it returns an appropriate error, perhaps incorporating the operation name op.
+func (f *File) checkValid(op string) error {
+ if f == nil {
+ return ErrInvalid
+ }
+ if f.fd == badFd {
+ return &PathError{op, f.name, ErrClosed}
+ }
+ return nil
+}
diff --git a/libgo/go/os/file_plan9.go b/libgo/go/os/file_plan9.go
index 9edb6bc..5276a7e 100644
--- a/libgo/go/os/file_plan9.go
+++ b/libgo/go/os/file_plan9.go
@@ -11,9 +11,9 @@ import (
"time"
)
-// File represents an open file descriptor.
-type File struct {
- *file
+// fixLongPath is a noop on non-Windows platforms.
+func fixLongPath(path string) string {
+ return path
}
// file is the real representation of *File.
@@ -135,21 +135,21 @@ func OpenFile(name string, flag int, perm FileMode) (*File, error) {
// Close closes the File, rendering it unusable for I/O.
// It returns an error, if any.
func (f *File) Close() error {
- if f == nil {
- return ErrInvalid
+ if err := f.checkValid("close"); err != nil {
+ return err
}
return f.file.close()
}
func (file *file) close() error {
- if file == nil || file.fd < 0 {
+ if file == nil || file.fd == badFd {
return ErrInvalid
}
var err error
if e := syscall.Close(file.fd); e != nil {
err = &PathError{"close", file.name, e}
}
- file.fd = -1 // so it can't be closed again
+ file.fd = badFd // so it can't be closed again
// no need for a finalizer anymore
runtime.SetFinalizer(file, nil)
diff --git a/libgo/go/os/file_posix.go b/libgo/go/os/file_posix.go
index 6d8076f..d817f34 100644
--- a/libgo/go/os/file_posix.go
+++ b/libgo/go/os/file_posix.go
@@ -18,7 +18,7 @@ func sigpipe() // implemented in package runtime
func Readlink(name string) (string, error) {
for len := 128; ; len *= 2 {
b := make([]byte, len)
- n, e := fixCount(syscall.Readlink(name, b))
+ n, e := fixCount(syscall.Readlink(fixLongPath(name), b))
if e != nil {
return "", &PathError{"readlink", name, e}
}
@@ -57,8 +57,8 @@ func Chmod(name string, mode FileMode) error {
// Chmod changes the mode of the file to mode.
// If there is an error, it will be of type *PathError.
func (f *File) Chmod(mode FileMode) error {
- if f == nil {
- return ErrInvalid
+ if err := f.checkValid("chmod"); err != nil {
+ return err
}
if e := syscall.Fchmod(f.fd, syscallMode(mode)); e != nil {
return &PathError{"chmod", f.name, e}
@@ -89,8 +89,8 @@ func Lchown(name string, uid, gid int) error {
// Chown changes the numeric uid and gid of the named file.
// If there is an error, it will be of type *PathError.
func (f *File) Chown(uid, gid int) error {
- if f == nil {
- return ErrInvalid
+ if err := f.checkValid("chown"); err != nil {
+ return err
}
if e := syscall.Fchown(f.fd, uid, gid); e != nil {
return &PathError{"chown", f.name, e}
@@ -102,8 +102,8 @@ func (f *File) Chown(uid, gid int) error {
// It does not change the I/O offset.
// If there is an error, it will be of type *PathError.
func (f *File) Truncate(size int64) error {
- if f == nil {
- return ErrInvalid
+ if err := f.checkValid("truncate"); err != nil {
+ return err
}
if e := syscall.Ftruncate(f.fd, size); e != nil {
return &PathError{"truncate", f.name, e}
@@ -115,11 +115,11 @@ func (f *File) Truncate(size int64) error {
// Typically, this means flushing the file system's in-memory copy
// of recently written data to disk.
func (f *File) Sync() error {
- if f == nil {
- return ErrInvalid
+ if err := f.checkValid("sync"); err != nil {
+ return err
}
if e := syscall.Fsync(f.fd); e != nil {
- return NewSyscallError("fsync", e)
+ return &PathError{"sync", f.name, e}
}
return nil
}
@@ -134,7 +134,7 @@ func Chtimes(name string, atime time.Time, mtime time.Time) error {
var utimes [2]syscall.Timespec
utimes[0] = syscall.NsecToTimespec(atime.UnixNano())
utimes[1] = syscall.NsecToTimespec(mtime.UnixNano())
- if e := syscall.UtimesNano(name, utimes[0:]); e != nil {
+ if e := syscall.UtimesNano(fixLongPath(name), utimes[0:]); e != nil {
return &PathError{"chtimes", name, e}
}
return nil
diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go
index 5d6c8af..54b5dfd 100644
--- a/libgo/go/os/file_unix.go
+++ b/libgo/go/os/file_unix.go
@@ -11,11 +11,16 @@ import (
"syscall"
)
-func sameFile(fs1, fs2 *fileStat) bool {
- return fs1.sys.Dev == fs2.sys.Dev && fs1.sys.Ino == fs2.sys.Ino
+// fixLongPath is a noop on non-Windows platforms.
+func fixLongPath(path string) string {
+ return path
}
func rename(oldname, newname string) error {
+ fi, err := Lstat(newname)
+ if err == nil && fi.IsDir() {
+ return &LinkError{"rename", oldname, newname, syscall.EEXIST}
+ }
e := syscall.Rename(oldname, newname)
if e != nil {
return &LinkError{"rename", oldname, newname, e}
@@ -23,11 +28,6 @@ func rename(oldname, newname string) error {
return nil
}
-// File represents an open file descriptor.
-type File struct {
- *file
-}
-
// file is the real representation of *File.
// The extra level of indirection ensures that no clients of os
// can overwrite this data, which could cause the finalizer
@@ -132,7 +132,7 @@ func (f *File) Close() error {
}
func (file *file) close() error {
- if file == nil || file.fd < 0 {
+ if file == nil || file.fd == badFd {
return syscall.EINVAL
}
var err error
@@ -158,69 +158,6 @@ func (file *file) close() error {
return err
}
-// Stat returns the FileInfo structure describing file.
-// If there is an error, it will be of type *PathError.
-func (f *File) Stat() (FileInfo, error) {
- if f == nil {
- return nil, ErrInvalid
- }
- var fs fileStat
- err := syscall.Fstat(f.fd, &fs.sys)
- if err != nil {
- return nil, &PathError{"stat", f.name, err}
- }
- fillFileStatFromSys(&fs, f.name)
- return &fs, nil
-}
-
-// Stat returns a FileInfo describing the named file.
-// If there is an error, it will be of type *PathError.
-func Stat(name string) (FileInfo, error) {
- var fs fileStat
- err := syscall.Stat(name, &fs.sys)
- if err != nil {
- return nil, &PathError{"stat", name, err}
- }
- fillFileStatFromSys(&fs, name)
- return &fs, nil
-}
-
-// 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.
-// If there is an error, it will be of type *PathError.
-func Lstat(name string) (FileInfo, error) {
- var fs fileStat
- err := syscall.Lstat(name, &fs.sys)
- if err != nil {
- return nil, &PathError{"lstat", name, err}
- }
- fillFileStatFromSys(&fs, name)
- return &fs, nil
-}
-
-func (f *File) readdir(n int) (fi []FileInfo, err error) {
- dirname := f.name
- if dirname == "" {
- dirname = "."
- }
- names, err := f.Readdirnames(n)
- fi = make([]FileInfo, 0, len(names))
- for _, filename := range names {
- fip, lerr := lstat(dirname + "/" + filename)
- if IsNotExist(lerr) {
- // File disappeared between readdir + stat.
- // Just treat it as if it didn't exist.
- continue
- }
- if lerr != nil {
- return fi, lerr
- }
- fi = append(fi, fip)
- }
- return fi, err
-}
-
// Darwin and FreeBSD can't read or write 2GB+ at a time,
// even on 64-bit systems. See golang.org/issue/7812.
// Use 1GB instead of, say, 2GB-1, to keep subsequent
@@ -335,24 +272,6 @@ func Remove(name string) error {
return &PathError{"remove", name, e}
}
-// basename removes trailing slashes and the leading directory name from path name
-func basename(name string) string {
- i := len(name) - 1
- // Remove trailing slashes
- for ; i > 0 && name[i] == '/'; i-- {
- name = name[:i]
- }
- // Remove leading directory name
- for i--; i >= 0; i-- {
- if name[i] == '/' {
- name = name[i+1:]
- break
- }
- }
-
- return name
-}
-
// TempDir returns the default directory to use for temporary files.
func TempDir() string {
dir := Getenv("TMPDIR")
diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go
index a3885a8..10a74c2 100644
--- a/libgo/go/os/os_test.go
+++ b/libgo/go/os/os_test.go
@@ -25,8 +25,6 @@ import (
"time"
)
-var supportsSymlinks = true
-
var dot = []string{
"dir.go",
"env.go",
@@ -229,6 +227,28 @@ func TestRead0(t *testing.T) {
}
}
+// Reading a closed file should should return ErrClosed error
+func TestReadClosed(t *testing.T) {
+ path := sfdir + "/" + sfname
+ file, err := Open(path)
+ if err != nil {
+ t.Fatal("open failed:", err)
+ }
+ file.Close() // close immediately
+
+ b := make([]byte, 100)
+ _, err = file.Read(b)
+
+ e, ok := err.(*PathError)
+ if !ok {
+ t.Fatalf("Read: %T(%v), want PathError", e, e)
+ }
+
+ if e.Err != ErrClosed {
+ t.Errorf("Read: %v, want PathError(ErrClosed)", e)
+ }
+}
+
func testReaddirnames(dir string, contents []string, t *testing.T) {
file, err := Open(dir)
if err != nil {
@@ -578,15 +598,8 @@ func TestReaddirOfFile(t *testing.T) {
}
func TestHardLink(t *testing.T) {
- if runtime.GOOS == "plan9" {
- t.Skip("skipping on plan9, hardlinks not supported")
- }
- // From Android release M (Marshmallow), hard linking files is blocked
- // and an attempt to call link() on a file will return EACCES.
- // - https://code.google.com/p/android-developer-preview/issues/detail?id=3150
- if runtime.GOOS == "android" {
- t.Skip("skipping on android, hardlinks not supported")
- }
+ testenv.MustHaveLink(t)
+
defer chtmpdir(t)()
from, to := "hardlinktestfrom", "hardlinktestto"
Remove(from) // Just in case.
@@ -650,14 +663,8 @@ func chtmpdir(t *testing.T) func() {
}
func TestSymlink(t *testing.T) {
- switch runtime.GOOS {
- case "android", "nacl", "plan9":
- t.Skipf("skipping on %s", runtime.GOOS)
- case "windows":
- if !supportsSymlinks {
- t.Skipf("skipping on %s", runtime.GOOS)
- }
- }
+ testenv.MustHaveSymlink(t)
+
defer chtmpdir(t)()
from, to := "symlinktestfrom", "symlinktestto"
Remove(from) // Just in case.
@@ -717,14 +724,8 @@ func TestSymlink(t *testing.T) {
}
func TestLongSymlink(t *testing.T) {
- switch runtime.GOOS {
- case "android", "plan9", "nacl":
- t.Skipf("skipping on %s", runtime.GOOS)
- case "windows":
- if !supportsSymlinks {
- t.Skipf("skipping on %s", runtime.GOOS)
- }
- }
+ testenv.MustHaveSymlink(t)
+
defer chtmpdir(t)()
s := "0123456789abcdef"
// Long, but not too long: a common limit is 255.
@@ -840,6 +841,39 @@ func TestRenameFailed(t *testing.T) {
}
}
+func TestRenameToDirFailed(t *testing.T) {
+ defer chtmpdir(t)()
+ from, to := "renamefrom", "renameto"
+
+ Remove(from)
+ Remove(to)
+ Mkdir(from, 0777)
+ Mkdir(to, 0777)
+ defer Remove(from)
+ defer 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 {
@@ -1028,7 +1062,7 @@ func testChtimes(t *testing.T, name string) {
}
if !pmt.Before(mt) {
- t.Errorf("ModTime didn't go backwards; was=%d, after=%d", mt, pmt)
+ t.Errorf("ModTime didn't go backwards; was=%v, after=%v", mt, pmt)
}
}
@@ -1665,6 +1699,72 @@ func TestReadAtEOF(t *testing.T) {
}
}
+func TestLongPath(t *testing.T) {
+ tmpdir := newDir("TestLongPath", t)
+ defer func(d string) {
+ if err := RemoveAll(d); err != nil {
+ t.Fatalf("RemoveAll failed: %v", err)
+ }
+ }(tmpdir)
+
+ // Test the boundary of 247 and fewer bytes (normal) and 248 and more bytes (adjusted).
+ sizes := []int{247, 248, 249, 400}
+ for len(tmpdir) < 400 {
+ tmpdir += "/dir3456789"
+ }
+ for _, sz := range sizes {
+ t.Run(fmt.Sprintf("length=%d", sz), func(t *testing.T) {
+ sizedTempDir := tmpdir[:sz-1] + "x" // Ensure it does not end with a slash.
+
+ // The various sized runs are for this call to trigger the boundary
+ // condition.
+ if err := MkdirAll(sizedTempDir, 0755); err != nil {
+ t.Fatalf("MkdirAll failed: %v", err)
+ }
+ data := []byte("hello world\n")
+ if err := ioutil.WriteFile(sizedTempDir+"/foo.txt", data, 0644); err != nil {
+ t.Fatalf("ioutil.WriteFile() failed: %v", err)
+ }
+ if err := Rename(sizedTempDir+"/foo.txt", sizedTempDir+"/bar.txt"); err != nil {
+ t.Fatalf("Rename failed: %v", err)
+ }
+ mtime := time.Now().Truncate(time.Minute)
+ if err := Chtimes(sizedTempDir+"/bar.txt", mtime, mtime); err != nil {
+ t.Fatalf("Chtimes failed: %v", err)
+ }
+ names := []string{"bar.txt"}
+ if testenv.HasSymlink() {
+ if err := Symlink(sizedTempDir+"/bar.txt", sizedTempDir+"/symlink.txt"); err != nil {
+ t.Fatalf("Symlink failed: %v", err)
+ }
+ names = append(names, "symlink.txt")
+ }
+ if testenv.HasLink() {
+ if err := Link(sizedTempDir+"/bar.txt", sizedTempDir+"/link.txt"); err != nil {
+ t.Fatalf("Link failed: %v", err)
+ }
+ names = append(names, "link.txt")
+ }
+ for _, wantSize := range []int64{int64(len(data)), 0} {
+ for _, name := range names {
+ path := sizedTempDir + "/" + name
+ dir, err := Stat(path)
+ if err != nil {
+ t.Fatalf("Stat(%q) failed: %v", path, err)
+ }
+ filesize := size(path, t)
+ if dir.Size() != filesize || filesize != wantSize {
+ t.Errorf("Size(%q) is %d, len(ReadFile()) is %d, want %d", path, dir.Size(), filesize, wantSize)
+ }
+ }
+ if err := Truncate(sizedTempDir+"/bar.txt", 0); err != nil {
+ t.Fatalf("Truncate failed: %v", err)
+ }
+ }
+ })
+ }
+}
+
func testKillProcess(t *testing.T, processKiller func(p *Process)) {
testenv.MustHaveExec(t)
diff --git a/libgo/go/os/os_unix_test.go b/libgo/go/os/os_unix_test.go
index 5c10154..e239835 100644
--- a/libgo/go/os/os_unix_test.go
+++ b/libgo/go/os/os_unix_test.go
@@ -7,8 +7,12 @@
package os_test
import (
+ "io"
+ "io/ioutil"
. "os"
+ "path/filepath"
"runtime"
+ "strings"
"syscall"
"testing"
)
@@ -178,3 +182,38 @@ func TestLchown(t *testing.T) {
checkUidGid(t, f.Name(), int(sys.Uid), int(sys.Gid))
}
}
+
+// Issue 16919: Readdir must return a non-empty slice or an error.
+func TestReaddirRemoveRace(t *testing.T) {
+ oldStat := *LstatP
+ defer func() { *LstatP = oldStat }()
+ *LstatP = func(name string) (FileInfo, error) {
+ if strings.HasSuffix(name, "some-file") {
+ // Act like it's been deleted.
+ return nil, ErrNotExist
+ }
+ return oldStat(name)
+ }
+ dir := newDir("TestReaddirRemoveRace", t)
+ defer RemoveAll(dir)
+ if err := ioutil.WriteFile(filepath.Join(dir, "some-file"), []byte("hello"), 0644); err != nil {
+ t.Fatal(err)
+ }
+ d, err := Open(dir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer d.Close()
+ fis, err := d.Readdir(2) // notably, greater than zero
+ if len(fis) == 0 && err == nil {
+ // This is what used to happen (Issue 16919)
+ t.Fatal("Readdir = empty slice & err == nil")
+ }
+ if len(fis) != 0 || err != io.EOF {
+ t.Errorf("Readdir = %d entries: %v; want 0, io.EOF", len(fis), err)
+ for i, fi := range fis {
+ t.Errorf(" entry[%d]: %q, %v", i, fi.Name(), fi.Mode())
+ }
+ t.FailNow()
+ }
+}
diff --git a/libgo/go/os/path_test.go b/libgo/go/os/path_test.go
index 51dc25b..6f5bfa5 100644
--- a/libgo/go/os/path_test.go
+++ b/libgo/go/os/path_test.go
@@ -5,6 +5,7 @@
package os_test
import (
+ "internal/testenv"
"io/ioutil"
. "os"
"path/filepath"
@@ -169,14 +170,7 @@ func TestRemoveAll(t *testing.T) {
}
func TestMkdirAllWithSymlink(t *testing.T) {
- switch runtime.GOOS {
- case "android", "nacl", "plan9":
- t.Skipf("skipping on %s", runtime.GOOS)
- case "windows":
- if !supportsSymlinks {
- t.Skipf("skipping on %s", runtime.GOOS)
- }
- }
+ testenv.MustHaveSymlink(t)
tmpDir, err := ioutil.TempDir("", "TestMkdirAllWithSymlink-")
if err != nil {
diff --git a/libgo/go/os/path_unix.go b/libgo/go/os/path_unix.go
index 36f8e61..ecf098c 100644
--- a/libgo/go/os/path_unix.go
+++ b/libgo/go/os/path_unix.go
@@ -15,3 +15,21 @@ const (
func IsPathSeparator(c uint8) bool {
return PathSeparator == c
}
+
+// basename removes trailing slashes and the leading directory name from path name
+func basename(name string) string {
+ i := len(name) - 1
+ // Remove trailing slashes
+ for ; i > 0 && name[i] == '/'; i-- {
+ name = name[:i]
+ }
+ // Remove leading directory name
+ for i--; i >= 0; i-- {
+ if name[i] == '/' {
+ name = name[i+1:]
+ break
+ }
+ }
+
+ return name
+}
diff --git a/libgo/go/os/path_windows.go b/libgo/go/os/path_windows.go
index c96f137..101b026 100644
--- a/libgo/go/os/path_windows.go
+++ b/libgo/go/os/path_windows.go
@@ -14,3 +14,196 @@ func IsPathSeparator(c uint8) bool {
// NOTE: Windows accept / as path separator.
return c == '\\' || c == '/'
}
+
+// basename removes trailing slashes and the leading
+// directory name and drive letter from path name.
+func basename(name string) string {
+ // Remove drive letter
+ if len(name) == 2 && name[1] == ':' {
+ name = "."
+ } else if len(name) > 2 && name[1] == ':' {
+ name = name[2:]
+ }
+ i := len(name) - 1
+ // Remove trailing slashes
+ for ; i > 0 && (name[i] == '/' || name[i] == '\\'); i-- {
+ name = name[:i]
+ }
+ // Remove leading directory name
+ for i--; i >= 0; i-- {
+ if name[i] == '/' || name[i] == '\\' {
+ name = name[i+1:]
+ break
+ }
+ }
+ return name
+}
+
+func isAbs(path string) (b bool) {
+ v := volumeName(path)
+ if v == "" {
+ return false
+ }
+ path = path[len(v):]
+ if path == "" {
+ return false
+ }
+ return IsPathSeparator(path[0])
+}
+
+func volumeName(path string) (v string) {
+ if len(path) < 2 {
+ return ""
+ }
+ // with drive letter
+ c := path[0]
+ if path[1] == ':' &&
+ ('0' <= c && c <= '9' || 'a' <= c && c <= 'z' ||
+ 'A' <= c && c <= 'Z') {
+ return path[:2]
+ }
+ // is it UNC
+ if l := len(path); l >= 5 && IsPathSeparator(path[0]) && IsPathSeparator(path[1]) &&
+ !IsPathSeparator(path[2]) && path[2] != '.' {
+ // first, leading `\\` and next shouldn't be `\`. its server name.
+ for n := 3; n < l-1; n++ {
+ // second, next '\' shouldn't be repeated.
+ if IsPathSeparator(path[n]) {
+ n++
+ // third, following something characters. its share name.
+ if !IsPathSeparator(path[n]) {
+ if path[n] == '.' {
+ break
+ }
+ for ; n < l; n++ {
+ if IsPathSeparator(path[n]) {
+ break
+ }
+ }
+ return path[:n]
+ }
+ break
+ }
+ }
+ }
+ return ""
+}
+
+func fromSlash(path string) string {
+ // Replace each '/' with '\\' if present
+ var pathbuf []byte
+ var lastSlash int
+ for i, b := range path {
+ if b == '/' {
+ if pathbuf == nil {
+ pathbuf = make([]byte, len(path))
+ }
+ copy(pathbuf[lastSlash:], path[lastSlash:i])
+ pathbuf[i] = '\\'
+ lastSlash = i + 1
+ }
+ }
+ if pathbuf == nil {
+ return path
+ }
+
+ copy(pathbuf[lastSlash:], path[lastSlash:])
+ return string(pathbuf)
+}
+
+func dirname(path string) string {
+ vol := volumeName(path)
+ i := len(path) - 1
+ for i >= len(vol) && !IsPathSeparator(path[i]) {
+ i--
+ }
+ dir := path[len(vol) : i+1]
+ last := len(dir) - 1
+ if last > 0 && IsPathSeparator(dir[last]) {
+ dir = dir[:last]
+ }
+ if dir == "" {
+ dir = "."
+ }
+ return vol + dir
+}
+
+// fixLongPath returns the extended-length (\\?\-prefixed) form of
+// path when needed, in order to avoid the default 260 character file
+// path limit imposed by Windows. If path is not easily converted to
+// the extended-length form (for example, if path is a relative path
+// or contains .. elements), or is short enough, fixLongPath returns
+// path unmodified.
+//
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath
+func fixLongPath(path string) string {
+ // Do nothing (and don't allocate) if the path is "short".
+ // Empirically (at least on the Windows Server 2013 builder),
+ // the kernel is arbitrarily okay with < 248 bytes. That
+ // matches what the docs above say:
+ // "When using an API to create a directory, the specified
+ // path cannot be so long that you cannot append an 8.3 file
+ // name (that is, the directory name cannot exceed MAX_PATH
+ // minus 12)." Since MAX_PATH is 260, 260 - 12 = 248.
+ //
+ // The MSDN docs appear to say that a normal path that is 248 bytes long
+ // will work; empirically the path must be less then 248 bytes long.
+ if len(path) < 248 {
+ // Don't fix. (This is how Go 1.7 and earlier worked,
+ // not automatically generating the \\?\ form)
+ return path
+ }
+
+ // The extended form begins with \\?\, as in
+ // \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt.
+ // The extended form disables evaluation of . and .. path
+ // elements and disables the interpretation of / as equivalent
+ // to \. The conversion here rewrites / to \ and elides
+ // . elements as well as trailing or duplicate separators. For
+ // simplicity it avoids the conversion entirely for relative
+ // paths or paths containing .. elements. For now,
+ // \\server\share paths are not converted to
+ // \\?\UNC\server\share paths because the rules for doing so
+ // are less well-specified.
+ if len(path) >= 2 && path[:2] == `\\` {
+ // Don't canonicalize UNC paths.
+ return path
+ }
+ if !isAbs(path) {
+ // Relative path
+ return path
+ }
+
+ const prefix = `\\?`
+
+ pathbuf := make([]byte, len(prefix)+len(path)+len(`\`))
+ copy(pathbuf, prefix)
+ n := len(path)
+ r, w := 0, len(prefix)
+ for r < n {
+ switch {
+ case IsPathSeparator(path[r]):
+ // empty block
+ r++
+ case path[r] == '.' && (r+1 == n || IsPathSeparator(path[r+1])):
+ // /./
+ r++
+ case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || IsPathSeparator(path[r+2])):
+ // /../ is currently unhandled
+ return path
+ default:
+ pathbuf[w] = '\\'
+ w++
+ for ; r < n && !IsPathSeparator(path[r]); r++ {
+ pathbuf[w] = path[r]
+ w++
+ }
+ }
+ }
+ // A drive's root directory needs a trailing \
+ if w == len(`\\?\c:`) {
+ pathbuf[w] = '\\'
+ w++
+ }
+ return string(pathbuf[:w])
+}
diff --git a/libgo/go/os/path_windows_test.go b/libgo/go/os/path_windows_test.go
new file mode 100644
index 0000000..cce0bdd
--- /dev/null
+++ b/libgo/go/os/path_windows_test.go
@@ -0,0 +1,46 @@
+// 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.
+
+package os_test
+
+import (
+ "os"
+ "strings"
+ "testing"
+)
+
+func TestFixLongPath(t *testing.T) {
+ // 248 is long enough to trigger the longer-than-248 checks in
+ // fixLongPath, but short enough not to make a path component
+ // longer than 255, which is illegal on Windows. (which
+ // doesn't really matter anyway, since this is purely a string
+ // function we're testing, and it's not actually being used to
+ // do a system call)
+ veryLong := "l" + strings.Repeat("o", 248) + "ng"
+ for _, test := range []struct{ in, want string }{
+ // Short; unchanged:
+ {`C:\short.txt`, `C:\short.txt`},
+ {`C:\`, `C:\`},
+ {`C:`, `C:`},
+ // The "long" substring is replaced by a looooooong
+ // string which triggers the rewriting. Except in the
+ // cases below where it doesn't.
+ {`C:\long\foo.txt`, `\\?\C:\long\foo.txt`},
+ {`C:/long/foo.txt`, `\\?\C:\long\foo.txt`},
+ {`C:\long\foo\\bar\.\baz\\`, `\\?\C:\long\foo\bar\baz`},
+ {`\\unc\path`, `\\unc\path`},
+ {`long.txt`, `long.txt`},
+ {`C:long.txt`, `C:long.txt`},
+ {`c:\long\..\bar\baz`, `c:\long\..\bar\baz`},
+ {`\\?\c:\long\foo.txt`, `\\?\c:\long\foo.txt`},
+ {`\\?\c:\long/foo.txt`, `\\?\c:\long/foo.txt`},
+ } {
+ in := strings.Replace(test.in, "long", veryLong, -1)
+ want := strings.Replace(test.want, "long", veryLong, -1)
+ if got := os.FixLongPath(in); got != want {
+ got = strings.Replace(got, veryLong, "long", -1)
+ t.Errorf("fixLongPath(%q) = %q; want %q", test.in, got, test.want)
+ }
+ }
+}
diff --git a/libgo/go/os/stat_plan9.go b/libgo/go/os/stat_plan9.go
index 96f056c..274d0d8 100644
--- a/libgo/go/os/stat_plan9.go
+++ b/libgo/go/os/stat_plan9.go
@@ -11,12 +11,6 @@ import (
const _BIT16SZ = 2
-func sameFile(fs1, fs2 *fileStat) bool {
- a := fs1.sys.(*syscall.Dir)
- b := fs2.sys.(*syscall.Dir)
- return a.Qid.Path == b.Qid.Path && a.Type == b.Type && a.Dev == b.Dev
-}
-
func fileInfoFromStat(d *syscall.Dir) FileInfo {
fs := &fileStat{
name: d.Name,
@@ -37,6 +31,10 @@ func fileInfoFromStat(d *syscall.Dir) FileInfo {
if d.Mode&syscall.DMTMP != 0 {
fs.mode |= ModeTemporary
}
+ // Consider all files not served by #M as device files.
+ if d.Type != 'M' {
+ fs.mode |= ModeDevice
+ }
return fs
}
diff --git a/libgo/go/os/stat_unix.go b/libgo/go/os/stat_unix.go
new file mode 100644
index 0000000..1733d3f
--- /dev/null
+++ b/libgo/go/os/stat_unix.go
@@ -0,0 +1,52 @@
+// 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
+
+import (
+ "syscall"
+)
+
+// Stat returns the FileInfo structure describing file.
+// If there is an error, it will be of type *PathError.
+func (f *File) Stat() (FileInfo, error) {
+ if f == nil {
+ return nil, ErrInvalid
+ }
+ var fs fileStat
+ err := syscall.Fstat(f.fd, &fs.sys)
+ if err != nil {
+ return nil, &PathError{"stat", f.name, err}
+ }
+ fillFileStatFromSys(&fs, f.name)
+ return &fs, nil
+}
+
+// Stat returns a FileInfo describing the named file.
+// If there is an error, it will be of type *PathError.
+func Stat(name string) (FileInfo, error) {
+ var fs fileStat
+ err := syscall.Stat(name, &fs.sys)
+ if err != nil {
+ return nil, &PathError{"stat", name, err}
+ }
+ fillFileStatFromSys(&fs, name)
+ return &fs, nil
+}
+
+// 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.
+// If there is an error, it will be of type *PathError.
+func Lstat(name string) (FileInfo, error) {
+ var fs fileStat
+ err := syscall.Lstat(name, &fs.sys)
+ if err != nil {
+ return nil, &PathError{"lstat", name, err}
+ }
+ fillFileStatFromSys(&fs, name)
+ return &fs, nil
+}
diff --git a/libgo/go/os/sys.go b/libgo/go/os/sys.go
new file mode 100644
index 0000000..28b0f6b
--- /dev/null
+++ b/libgo/go/os/sys.go
@@ -0,0 +1,10 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+// Hostname returns the host name reported by the kernel.
+func Hostname() (name string, err error) {
+ return hostname()
+}
diff --git a/libgo/go/os/types.go b/libgo/go/os/types.go
index 12b593f..c565483 100644
--- a/libgo/go/os/types.go
+++ b/libgo/go/os/types.go
@@ -12,6 +12,11 @@ import (
// Getpagesize returns the underlying system's memory page size.
func Getpagesize() int { return syscall.Getpagesize() }
+// File represents an open file descriptor.
+type File struct {
+ *file // os specific
+}
+
// A FileInfo describes a file and is returned by Stat and Lstat.
type FileInfo interface {
Name() string // base name of the file
diff --git a/libgo/go/os/types_plan9.go b/libgo/go/os/types_plan9.go
index 6d46ca9..125da66 100644
--- a/libgo/go/os/types_plan9.go
+++ b/libgo/go/os/types_plan9.go
@@ -4,7 +4,10 @@
package os
-import "time"
+import (
+ "syscall"
+ "time"
+)
// A fileStat is the implementation of FileInfo returned by Stat and Lstat.
type fileStat struct {
@@ -19,3 +22,11 @@ func (fs *fileStat) Size() int64 { return fs.size }
func (fs *fileStat) Mode() FileMode { return fs.mode }
func (fs *fileStat) ModTime() time.Time { return fs.modTime }
func (fs *fileStat) Sys() interface{} { return fs.sys }
+
+func sameFile(fs1, fs2 *fileStat) bool {
+ a := fs1.sys.(*syscall.Dir)
+ b := fs2.sys.(*syscall.Dir)
+ return a.Qid.Path == b.Qid.Path && a.Type == b.Type && a.Dev == b.Dev
+}
+
+const badFd = -1
diff --git a/libgo/go/os/types_unix.go b/libgo/go/os/types_unix.go
index 056220c..1f61481 100644
--- a/libgo/go/os/types_unix.go
+++ b/libgo/go/os/types_unix.go
@@ -25,3 +25,9 @@ func (fs *fileStat) Size() int64 { return fs.size }
func (fs *fileStat) Mode() FileMode { return fs.mode }
func (fs *fileStat) ModTime() time.Time { return fs.modTime }
func (fs *fileStat) Sys() interface{} { return &fs.sys }
+
+func sameFile(fs1, fs2 *fileStat) bool {
+ return fs1.sys.Dev == fs2.sys.Dev && fs1.sys.Ino == fs2.sys.Ino
+}
+
+const badFd = -1
diff --git a/libgo/go/os/user/user.go b/libgo/go/os/user/user.go
index 7b44397..ad61992 100644
--- a/libgo/go/os/user/user.go
+++ b/libgo/go/os/user/user.go
@@ -15,31 +15,39 @@ var (
)
// User represents a user account.
-//
-// 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 is the user ID.
+ // On POSIX systems, this is a decimal number representing the uid.
+ // On Windows, this is a security identifier (SID) in a string format.
+ // On Plan 9, this is the contents of /dev/user.
+ Uid string
+ // Gid is the primary group ID.
+ // On POSIX systems, this is a decimal number representing the gid.
+ // On Windows, this is a SID in a string format.
+ // On Plan 9, this is the contents of /dev/user.
+ Gid string
+ // Username is the login name.
Username string
- Name string
- HomeDir string
+ // Name is the user's real or display name.
+ // It might be blank.
+ // On POSIX systems, this is the first (or only) entry in the GECOS field
+ // list.
+ // On Windows, this is the user's display name.
+ // On Plan 9, this is the contents of /dev/user.
+ Name string
+ // HomeDir is the path to the user's home directory (if they have one).
+ HomeDir string
}
// Group represents a grouping of users.
//
-// On POSIX systems Gid contains a decimal number
-// representing the group ID.
+// 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.
+// UnknownUserIdError is returned by LookupId when a user cannot be found.
type UnknownUserIdError int
func (e UnknownUserIdError) Error() string {
diff --git a/libgo/go/os/wait_wait6.go b/libgo/go/os/wait_wait6.go
index 7f4780a..b309811 100644
--- a/libgo/go/os/wait_wait6.go
+++ b/libgo/go/os/wait_wait6.go
@@ -28,6 +28,7 @@ func (p *Process) blockUntilWaitable() (bool, error) {
} else {
_, _, errno = syscall.Syscall6(syscall.SYS_WAIT6, _P_PID, uintptr(p.Pid), 0, syscall.WEXITED|syscall.WNOWAIT, 0, 0)
}
+ runtime.KeepAlive(p)
if errno != 0 {
// The wait6 system call is supported only on FreeBSD
// 9.3 and above, so it may return an ENOSYS error.
diff --git a/libgo/go/os/wait_waitid.go b/libgo/go/os/wait_waitid.go
index 74b7494..653fce9 100644
--- a/libgo/go/os/wait_waitid.go
+++ b/libgo/go/os/wait_waitid.go
@@ -26,7 +26,7 @@ func (p *Process) blockUntilWaitable() (bool, error) {
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)
+ runtime.KeepAlive(p)
if e != 0 {
// waitid has been available since Linux 2.6.9, but
// reportedly is not available in Ubuntu on Windows.