diff options
author | Ian Lance Taylor <iant@golang.org> | 2017-01-14 00:05:42 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2017-01-14 00:05:42 +0000 |
commit | c2047754c300b68c05d65faa8dc2925fe67b71b4 (patch) | |
tree | e183ae81a1f48a02945cb6de463a70c5be1b06f6 /libgo/go/os | |
parent | 829afb8f05602bb31c9c597b24df7377fed4f059 (diff) | |
download | gcc-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')
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. |