diff options
author | Ian Lance Taylor <iant@golang.org> | 2019-01-18 19:04:36 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2019-01-18 19:04:36 +0000 |
commit | 4f4a855d82a889cebcfca150a7a43909bcb6a346 (patch) | |
tree | f12bae0781920fa34669fe30b6f4615a86d9fb80 /libgo/go/os | |
parent | 225220d668dafb8262db7012bced688acbe63b33 (diff) | |
download | gcc-4f4a855d82a889cebcfca150a7a43909bcb6a346.zip gcc-4f4a855d82a889cebcfca150a7a43909bcb6a346.tar.gz gcc-4f4a855d82a889cebcfca150a7a43909bcb6a346.tar.bz2 |
libgo: update to Go1.12beta2
Reviewed-on: https://go-review.googlesource.com/c/158019
gotools/:
* Makefile.am (go_cmd_vet_files): Update for Go1.12beta2 release.
(GOTOOLS_TEST_TIMEOUT): Increase to 600.
(check-runtime): Export LD_LIBRARY_PATH before computing GOARCH
and GOOS.
(check-vet): Copy golang.org/x/tools into check-vet-dir.
* Makefile.in: Regenerate.
gcc/testsuite/:
* go.go-torture/execute/names-1.go: Stop using debug/xcoff, which
is no longer externally visible.
From-SVN: r268084
Diffstat (limited to 'libgo/go/os')
51 files changed, 1443 insertions, 464 deletions
diff --git a/libgo/go/os/dir_gccgo.go b/libgo/go/os/dir_gccgo.go index f660646..60f70aa 100644 --- a/libgo/go/os/dir_gccgo.go +++ b/libgo/go/os/dir_gccgo.go @@ -16,6 +16,9 @@ import ( //extern pathconf func libc_pathconf(*byte, int) int +//extern fdopendir +func libc_fdopendir(int32) *syscall.DIR + func clen(n []byte) int { for i := 0; i < len(n); i++ { if n[i] == 0 { @@ -48,11 +51,11 @@ func (file *File) readdirnames(n int) (names []string, err error) { } syscall.Entersyscall() - r := libc_opendir(p) + r := libc_fdopendir(int32(file.pfd.Sysfd)) errno := syscall.GetErrno() syscall.Exitsyscall() if r == nil { - return nil, &PathError{"opendir", file.name, errno} + return nil, &PathError{"fdopendir", file.name, errno} } file.dirinfo = new(dirInfo) diff --git a/libgo/go/os/dir_ios.go b/libgo/go/os/dir_ios.go new file mode 100644 index 0000000..8c14d89 --- /dev/null +++ b/libgo/go/os/dir_ios.go @@ -0,0 +1,87 @@ +// 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 +// +build arm arm64 + +package os + +import ( + "io" + "runtime" + "syscall" + "unsafe" +) + +// Auxiliary information if the File describes a directory +type dirInfo struct { + dir uintptr // Pointer to DIR structure from dirent.h +} + +func (d *dirInfo) close() { + if d.dir == 0 { + return + } + closedir(d.dir) + d.dir = 0 +} + +func (f *File) readdirnames(n int) (names []string, err error) { + if f.dirinfo == nil { + dir, call, errno := f.pfd.OpenDir() + if errno != nil { + return nil, wrapSyscallError(call, errno) + } + f.dirinfo = &dirInfo{ + dir: dir, + } + } + d := f.dirinfo + + size := n + if size <= 0 { + size = 100 + n = -1 + } + + names = make([]string, 0, size) + var dirent syscall.Dirent + var entptr uintptr + for len(names) < size { + if res := readdir_r(d.dir, uintptr(unsafe.Pointer(&dirent)), uintptr(unsafe.Pointer(&entptr))); res != 0 { + return names, wrapSyscallError("readdir", syscall.Errno(res)) + } + if entptr == 0 { // EOF + break + } + if dirent.Ino == 0 { + continue + } + name := (*[len(syscall.Dirent{}.Name)]byte)(unsafe.Pointer(&dirent.Name))[:] + for i, c := range name { + if c == 0 { + name = name[:i] + break + } + } + // Check for useless names before allocating a string. + if string(name) == "." || string(name) == ".." { + continue + } + names = append(names, string(name)) + runtime.KeepAlive(f) + } + if n >= 0 && len(names) == 0 { + return names, io.EOF + } + return names, nil +} + +// Implemented in syscall/syscall_darwin.go. + +//go:linkname closedir syscall.closedir +func closedir(dir uintptr) (err error) + +//go:linkname readdir_r syscall.readdir_r +func readdir_r(dir, entry, result uintptr) (res int) diff --git a/libgo/go/os/dir_libc64_gccgo.go b/libgo/go/os/dir_libc64_gccgo.go index 95d4663..dc3ba7f 100644 --- a/libgo/go/os/dir_libc64_gccgo.go +++ b/libgo/go/os/dir_libc64_gccgo.go @@ -8,8 +8,5 @@ package os import "syscall" -//extern opendir64 -func libc_opendir(*byte) *syscall.DIR - //extern closedir64 func libc_closedir(*syscall.DIR) int diff --git a/libgo/go/os/dir_libc_gccgo.go b/libgo/go/os/dir_libc_gccgo.go index 1ee253c..b46eb4c 100644 --- a/libgo/go/os/dir_libc_gccgo.go +++ b/libgo/go/os/dir_libc_gccgo.go @@ -8,8 +8,5 @@ package os import "syscall" -//extern opendir -func libc_opendir(*byte) *syscall.DIR - //extern closedir func libc_closedir(*syscall.DIR) int diff --git a/libgo/go/os/dir_unix.go b/libgo/go/os/dir_unix.go deleted file mode 100644 index edfc9ea..0000000 --- a/libgo/go/os/dir_unix.go +++ /dev/null @@ -1,38 +0,0 @@ -// 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 aix darwin dragonfly freebsd js,wasm 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/env_unix_test.go b/libgo/go/os/env_unix_test.go index f7b67eb..89430b3 100644 --- a/libgo/go/os/env_unix_test.go +++ b/libgo/go/os/env_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux netbsd openbsd solaris +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris package os_test diff --git a/libgo/go/os/error_posix.go b/libgo/go/os/error_posix.go index 3c81b41..0478ba6 100644 --- a/libgo/go/os/error_posix.go +++ b/libgo/go/os/error_posix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows package os diff --git a/libgo/go/os/error_unix_test.go b/libgo/go/os/error_unix_test.go index 8db9867..c47af56 100644 --- a/libgo/go/os/error_unix_test.go +++ b/libgo/go/os/error_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris package os_test diff --git a/libgo/go/os/example_test.go b/libgo/go/os/example_test.go index e21415a..8b6566e 100644 --- a/libgo/go/os/example_test.go +++ b/libgo/go/os/example_test.go @@ -55,6 +55,7 @@ func ExampleFileMode() { log.Fatal(err) } + fmt.Printf("permissions: %#o\n", fi.Mode().Perm()) // 0400, 0777, etc. switch mode := fi.Mode(); { case mode.IsRegular(): fmt.Println("regular file") @@ -70,38 +71,35 @@ func ExampleFileMode() { func ExampleIsNotExist() { filename := "a-nonexistent-file" if _, err := os.Stat(filename); os.IsNotExist(err) { - fmt.Printf("file does not exist") + fmt.Println("file does not exist") } // Output: // file does not exist } -func init() { - os.Setenv("USER", "gopher") - os.Setenv("HOME", "/usr/gopher") - os.Unsetenv("GOPATH") -} - func ExampleExpand() { mapper := func(placeholderName string) string { switch placeholderName { case "DAY_PART": return "morning" - case "USER": + case "NAME": return "Gopher" } return "" } - fmt.Println(os.Expand("Good ${DAY_PART}, $USER!", mapper)) + fmt.Println(os.Expand("Good ${DAY_PART}, $NAME!", mapper)) // Output: // Good morning, Gopher! } func ExampleExpandEnv() { - fmt.Println(os.ExpandEnv("$USER lives in ${HOME}.")) + os.Setenv("NAME", "gopher") + os.Setenv("BURROW", "/usr/gopher") + + fmt.Println(os.ExpandEnv("$NAME lives in ${BURROW}.")) // Output: // gopher lives in /usr/gopher. @@ -117,16 +115,24 @@ func ExampleLookupEnv() { } } - show("USER") - show("GOPATH") + os.Setenv("SOME_KEY", "value") + os.Setenv("EMPTY_KEY", "") + + show("SOME_KEY") + show("EMPTY_KEY") + show("MISSING_KEY") // Output: - // USER=gopher - // GOPATH not set + // SOME_KEY=value + // EMPTY_KEY= + // MISSING_KEY not set } func ExampleGetenv() { - fmt.Printf("%s lives in %s.\n", os.Getenv("USER"), os.Getenv("HOME")) + os.Setenv("NAME", "gopher") + os.Setenv("BURROW", "/usr/gopher") + + fmt.Printf("%s lives in %s.\n", os.Getenv("NAME"), os.Getenv("BURROW")) // Output: // gopher lives in /usr/gopher. diff --git a/libgo/go/os/exec/exec.go b/libgo/go/os/exec/exec.go index 88b0a91..1aa3ab9 100644 --- a/libgo/go/os/exec/exec.go +++ b/libgo/go/os/exec/exec.go @@ -152,6 +152,15 @@ type Cmd struct { // followed by the elements of arg, so arg should not include the // command name itself. For example, Command("echo", "hello"). // Args[0] is always name, not the possibly resolved Path. +// +// On Windows, processes receive the whole command line as a single string +// and do their own parsing. Command combines and quotes Args into a command +// line string with an algorithm compatible with applications using +// CommandLineToArgvW (which is the most common way). Notable exceptions are +// msiexec.exe and cmd.exe (and thus, all batch files), which have a different +// unquoting algorithm. In these or other similar cases, you can do the +// quoting yourself and provide the full command line in SysProcAttr.CmdLine, +// leaving Args empty. func Command(name string, arg ...string) *Cmd { cmd := &Cmd{ Path: name, diff --git a/libgo/go/os/exec/exec_posix_test.go b/libgo/go/os/exec/exec_posix_test.go index 865b6c3..46799cd 100644 --- a/libgo/go/os/exec/exec_posix_test.go +++ b/libgo/go/os/exec/exec_posix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux netbsd openbsd solaris +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris package exec_test diff --git a/libgo/go/os/exec/exec_test.go b/libgo/go/os/exec/exec_test.go index 1e38285..b7cc9da 100644 --- a/libgo/go/os/exec/exec_test.go +++ b/libgo/go/os/exec/exec_test.go @@ -172,6 +172,58 @@ func TestExitStatus(t *testing.T) { } } +func TestExitCode(t *testing.T) { + // Test that exit code are returned correctly + cmd := helperCommand(t, "exit", "42") + cmd.Run() + want := 42 + if runtime.GOOS == "plan9" { + want = 1 + } + got := cmd.ProcessState.ExitCode() + if want != got { + t.Errorf("ExitCode got %d, want %d", got, want) + } + + cmd = helperCommand(t, "/no-exist-executable") + cmd.Run() + want = 2 + if runtime.GOOS == "plan9" { + want = 1 + } + got = cmd.ProcessState.ExitCode() + if want != got { + t.Errorf("ExitCode got %d, want %d", got, want) + } + + cmd = helperCommand(t, "exit", "255") + cmd.Run() + want = 255 + if runtime.GOOS == "plan9" { + want = 1 + } + got = cmd.ProcessState.ExitCode() + if want != got { + t.Errorf("ExitCode got %d, want %d", got, want) + } + + cmd = helperCommand(t, "cat") + cmd.Run() + want = 0 + got = cmd.ProcessState.ExitCode() + if want != got { + t.Errorf("ExitCode got %d, want %d", got, want) + } + + // Test when command does not call Run(). + cmd = helperCommand(t, "cat") + want = -1 + got = cmd.ProcessState.ExitCode() + if want != got { + t.Errorf("ExitCode got %d, want %d", got, want) + } +} + func TestPipes(t *testing.T) { check := func(what string, err error) { if err != nil { @@ -411,7 +463,7 @@ func basefds() uintptr { // The poll (epoll/kqueue) descriptor can be numerically // either between stderr and the testlog-fd, or after // testlog-fd. - if poll.PollDescriptor() == n { + if poll.IsPollDescriptor(n) { n++ } for _, arg := range os.Args { @@ -424,7 +476,7 @@ func basefds() uintptr { func closeUnexpectedFds(t *testing.T, m string) { for fd := basefds(); fd <= 101; fd++ { - if fd == poll.PollDescriptor() { + if poll.IsPollDescriptor(fd) { continue } err := os.NewFile(fd, "").Close() @@ -686,6 +738,8 @@ func TestHelperProcess(*testing.T) { ofcmd = "fstat" case "plan9": ofcmd = "/bin/cat" + case "aix": + ofcmd = "procfiles" } args := os.Args @@ -789,7 +843,7 @@ func TestHelperProcess(*testing.T) { // Now verify that there are no other open fds. var files []*os.File for wantfd := basefds() + 1; wantfd <= 100; wantfd++ { - if wantfd == poll.PollDescriptor() { + if poll.IsPollDescriptor(wantfd) { continue } f, err := os.Open(os.Args[0]) @@ -803,6 +857,8 @@ func TestHelperProcess(*testing.T) { switch runtime.GOOS { case "plan9": args = []string{fmt.Sprintf("/proc/%d/fd", os.Getpid())} + case "aix": + args = []string{fmt.Sprint(os.Getpid())} default: args = []string{"-p", fmt.Sprint(os.Getpid())} } diff --git a/libgo/go/os/exec/lp_unix_test.go b/libgo/go/os/exec/lp_unix_test.go index d467acf..e4656ca 100644 --- a/libgo/go/os/exec/lp_unix_test.go +++ b/libgo/go/os/exec/lp_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux netbsd openbsd solaris +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris package exec diff --git a/libgo/go/os/exec_plan9.go b/libgo/go/os/exec_plan9.go index 6b4d28c..bab16cc 100644 --- a/libgo/go/os/exec_plan9.go +++ b/libgo/go/os/exec_plan9.go @@ -136,3 +136,13 @@ func (p *ProcessState) String() string { } return "exit status: " + p.status.Msg } + +// ExitCode returns the exit code of the exited process, or -1 +// if the process hasn't exited or was terminated by a signal. +func (p *ProcessState) ExitCode() int { + // return -1 if the process hasn't started. + if p == nil { + return -1 + } + return p.status.ExitStatus() +} diff --git a/libgo/go/os/exec_posix.go b/libgo/go/os/exec_posix.go index dbbb5df..4c82612 100644 --- a/libgo/go/os/exec_posix.go +++ b/libgo/go/os/exec_posix.go @@ -10,10 +10,11 @@ import ( "syscall" ) -// The only signal values guaranteed to be present in the os package -// on all systems are Interrupt (send the process an interrupt) and -// Kill (force the process to exit). Interrupt is not implemented on -// Windows; using it with os.Process.Signal will return an error. +// The only signal values guaranteed to be present in the os package on all +// systems are os.Interrupt (send the process an interrupt) and os.Kill (force +// the process to exit). On Windows, sending os.Interrupt to a process with +// os.Process.Signal is not implemented; it will return an error instead of +// sending a signal. var ( Interrupt Signal = syscall.SIGINT Kill Signal = syscall.SIGKILL @@ -106,3 +107,13 @@ func (p *ProcessState) String() string { } return res } + +// ExitCode returns the exit code of the exited process, or -1 +// if the process hasn't exited or was terminated by a signal. +func (p *ProcessState) ExitCode() int { + // return -1 if the process hasn't started. + if p == nil { + return -1 + } + return p.status.ExitStatus() +} diff --git a/libgo/go/os/exec_windows.go b/libgo/go/os/exec_windows.go index d5d553a..38293a0 100644 --- a/libgo/go/os/exec_windows.go +++ b/libgo/go/os/exec_windows.go @@ -38,7 +38,8 @@ func (p *Process) wait() (ps *ProcessState, err error) { // NOTE(brainman): It seems that sometimes process is not dead // when WaitForSingleObject returns. But we do not know any // other way to wait for it. Sleeping for a while seems to do - // the trick sometimes. So we will sleep and smell the roses. + // the trick sometimes. + // See https://golang.org/issue/25965 for details. defer time.Sleep(5 * time.Millisecond) defer p.Release() return &ProcessState{p.Pid, syscall.WaitStatus{ExitCode: ec}, &u}, nil diff --git a/libgo/go/os/executable_test.go b/libgo/go/os/executable_test.go index 4a9a883..d513c87 100644 --- a/libgo/go/os/executable_test.go +++ b/libgo/go/os/executable_test.go @@ -36,8 +36,8 @@ func TestExecutable(t *testing.T) { // 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"} - if runtime.GOOS == "openbsd" { - // OpenBSD relies on argv[0] + if runtime.GOOS == "openbsd" || runtime.GOOS == "aix" { + // OpenBSD and AIX rely on argv[0] cmd.Args[0] = fn } cmd.Env = append(os.Environ(), fmt.Sprintf("%s=1", executable_EnvVar)) diff --git a/libgo/go/os/file.go b/libgo/go/os/file.go index cba70d7..fdead63 100644 --- a/libgo/go/os/file.go +++ b/libgo/go/os/file.go @@ -73,7 +73,7 @@ const ( O_CREATE int = syscall.O_CREAT // create a new file if none exists. O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist. O_SYNC int = syscall.O_SYNC // open for synchronous I/O. - O_TRUNC int = syscall.O_TRUNC // if possible, truncate file when opened. + O_TRUNC int = syscall.O_TRUNC // truncate regular writable file when opened. ) // Seek whence values. @@ -381,6 +381,31 @@ func UserCacheDir() (string, error) { return dir, nil } +// UserHomeDir returns the current user's home directory. +// +// On Unix, including macOS, it returns the $HOME environment variable. +// On Windows, it returns %USERPROFILE%. +// On Plan 9, it returns the $home environment variable. +func UserHomeDir() (string, error) { + env, enverr := "HOME", "$HOME" + switch runtime.GOOS { + case "windows": + env, enverr = "USERPROFILE", "%userprofile%" + case "plan9": + env, enverr = "home", "$home" + case "nacl", "android": + return "/", nil + case "darwin": + if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" { + return "/", nil + } + } + if v := Getenv(env); v != "" { + return v, nil + } + return "", errors.New(enverr + " is not defined") +} + // Chmod changes the mode of the named file to mode. // If the file is a symbolic link, it changes the mode of the link's target. // If there is an error, it will be of type *PathError. @@ -448,3 +473,12 @@ func (f *File) SetReadDeadline(t time.Time) error { func (f *File) SetWriteDeadline(t time.Time) error { return f.setWriteDeadline(t) } + +// SyscallConn returns a raw file. +// This implements the syscall.Conn interface. +func (f *File) SyscallConn() (syscall.RawConn, error) { + if err := f.checkValid("SyscallConn"); err != nil { + return nil, err + } + return newRawConn(f) +} diff --git a/libgo/go/os/file_plan9.go b/libgo/go/os/file_plan9.go index 2c74403..3fa12e6 100644 --- a/libgo/go/os/file_plan9.go +++ b/libgo/go/os/file_plan9.go @@ -534,3 +534,21 @@ func (f *File) checkValid(op string) error { } return nil } + +type rawConn struct{} + +func (c *rawConn) Control(f func(uintptr)) error { + return syscall.EPLAN9 +} + +func (c *rawConn) Read(f func(uintptr) bool) error { + return syscall.EPLAN9 +} + +func (c *rawConn) Write(f func(uintptr) bool) error { + return syscall.EPLAN9 +} + +func newRawConn(file *File) (*rawConn, error) { + return nil, syscall.EPLAN9 +} diff --git a/libgo/go/os/file_posix.go b/libgo/go/os/file_posix.go index 9bd1cc7..1c0de5c 100644 --- a/libgo/go/os/file_posix.go +++ b/libgo/go/os/file_posix.go @@ -7,6 +7,7 @@ package os import ( + "runtime" "syscall" "time" ) @@ -20,7 +21,7 @@ func Readlink(name string) (string, error) { b := make([]byte, len) n, e := fixCount(syscall.Readlink(fixLongPath(name), b)) // buffer too small - if e == syscall.ERANGE { + if runtime.GOOS == "aix" && e == syscall.ERANGE { continue } if e != nil { diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go index b2aea33..e9ac774 100644 --- a/libgo/go/os/file_unix.go +++ b/libgo/go/os/file_unix.go @@ -9,6 +9,7 @@ package os import ( "internal/poll" "internal/syscall/unix" + "io" "runtime" "syscall" ) @@ -116,23 +117,39 @@ func newFile(fd uintptr, name string, kind newFileKind) *File { pollable := kind == kindOpenFile || kind == kindPipe || kind == kindNonBlock - // Don't try to use kqueue with regular files on FreeBSD. - // It crashes the system unpredictably while running all.bash. - // Issue 19093. // If the caller passed a non-blocking filedes (kindNonBlock), // we assume they know what they are doing so we allow it to be // used with kqueue. - if runtime.GOOS == "freebsd" && kind == kindOpenFile { - pollable = false - } - - // On Darwin, kqueue does not work properly with fifos: - // closing the last writer does not cause a kqueue event - // for any readers. See issue #24164. - if runtime.GOOS == "darwin" && kind == kindOpenFile { + if kind == kindOpenFile { var st syscall.Stat_t - if err := syscall.Fstat(fdi, &st); err == nil && st.Mode&syscall.S_IFMT == syscall.S_IFIFO { + switch runtime.GOOS { + case "freebsd": + // On FreeBSD before 10.4 it used to crash the + // system unpredictably while running all.bash. + // When we stop supporting FreeBSD 10 we can merge + // this into the dragonfly/netbsd/openbsd case. + // Issue 27619. pollable = false + + case "dragonfly", "netbsd", "openbsd": + // Don't try to use kqueue with regular files on *BSDs. + // On FreeBSD a regular file is always + // reported as ready for writing. + // On Dragonfly, NetBSD and OpenBSD the fd is signaled + // only once as ready (both read and write). + // Issue 19093. + if err := syscall.Fstat(fdi, &st); err == nil && st.Mode&syscall.S_IFMT == syscall.S_IFREG { + pollable = false + } + + case "darwin": + // In addition to the behavior described above for regular files, + // on Darwin, kqueue does not work properly with fifos: + // closing the last writer does not cause a kqueue event + // for any readers. See issue #24164. + if err := syscall.Fstat(fdi, &st); err == nil && (st.Mode&syscall.S_IFMT == syscall.S_IFIFO || st.Mode&syscall.S_IFMT == syscall.S_IFREG) { + pollable = false + } } } @@ -230,22 +247,22 @@ func (file *file) close() error { return syscall.EINVAL } var err error - if e := file.pfd.Close(); e != nil { - if e == poll.ErrFileClosing { - e = ErrClosed - } - err = &PathError{"close", file.name, e} - } - if file.dirinfo != nil { syscall.Entersyscall() i := libc_closedir(file.dirinfo.dir) errno := syscall.GetErrno() syscall.Exitsyscall() file.dirinfo = nil - if i < 0 && err == nil { + if i < 0 && errno != 0 { err = &PathError{"closedir", file.name, errno} } + } else { + if e := file.pfd.Close(); e != nil { + if e == poll.ErrFileClosing { + e = ErrClosed + } + err = &PathError{"close", file.name, e} + } } // no need for a finalizer anymore @@ -368,3 +385,30 @@ func Symlink(oldname, newname string) error { } return 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) + } + 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/os_test.go b/libgo/go/os/os_test.go index 5a77d66..98eba89 100644 --- a/libgo/go/os/os_test.go +++ b/libgo/go/os/os_test.go @@ -176,7 +176,6 @@ func TestStatError(t *testing.T) { defer chtmpdir(t)() path := "no-such-file" - Remove(path) // Just in case fi, err := Stat(path) if err == nil { @@ -192,12 +191,10 @@ func TestStatError(t *testing.T) { testenv.MustHaveSymlink(t) link := "symlink" - Remove(link) // Just in case err = Symlink(path, link) if err != nil { t.Fatal(err) } - defer Remove(link) fi, err = Stat(link) if err == nil { @@ -267,7 +264,7 @@ func TestRead0(t *testing.T) { } } -// Reading a closed file should should return ErrClosed error +// Reading a closed file should return ErrClosed error func TestReadClosed(t *testing.T) { path := sfdir + "/" + sfname file, err := Open(path) @@ -686,12 +683,10 @@ func TestHardLink(t *testing.T) { defer chtmpdir(t)() from, to := "hardlinktestfrom", "hardlinktestto" - Remove(from) // Just in case. file, err := Create(to) if err != nil { t.Fatalf("open %q failed: %v", to, err) } - defer Remove(to) if err = file.Close(); err != nil { t.Errorf("close %q failed: %v", to, err) } @@ -707,7 +702,6 @@ func TestHardLink(t *testing.T) { t.Errorf("link %q, %q failed to return a valid error", none, none) } - defer Remove(from) tostat, err := Stat(to) if err != nil { t.Fatalf("stat %q failed: %v", to, err) @@ -743,11 +737,8 @@ func TestHardLink(t *testing.T) { } // chtmpdir changes the working directory to a new temporary directory and -// provides a cleanup function. Used when PWD is read-only. +// provides a cleanup function. func chtmpdir(t *testing.T) func() { - if runtime.GOOS != "darwin" || (runtime.GOARCH != "arm" && runtime.GOARCH != "arm64") { - return func() {} // only needed on darwin/arm{,64} - } oldwd, err := Getwd() if err != nil { t.Fatalf("chtmpdir: %v", err) @@ -772,12 +763,10 @@ func TestSymlink(t *testing.T) { defer chtmpdir(t)() from, to := "symlinktestfrom", "symlinktestto" - Remove(from) // Just in case. file, err := Create(to) if err != nil { t.Fatalf("Create(%q) failed: %v", to, err) } - defer Remove(to) if err = file.Close(); err != nil { t.Errorf("Close(%q) failed: %v", to, err) } @@ -785,7 +774,6 @@ func TestSymlink(t *testing.T) { if err != nil { t.Fatalf("Symlink(%q, %q) failed: %v", to, from, err) } - defer Remove(from) tostat, err := Lstat(to) if err != nil { t.Fatalf("Lstat(%q) failed: %v", to, err) @@ -839,12 +827,10 @@ func TestLongSymlink(t *testing.T) { // Long, but not too long: a common limit is 255. s = s + s + s + s + s + s + s + s + s + s + s + s + s + s + s from := "longsymlinktestfrom" - Remove(from) // Just in case. err := Symlink(s, from) if err != nil { t.Fatalf("symlink %q, %q failed: %v", s, from, err) } - defer Remove(from) r, err := Readlink(from) if err != nil { t.Fatalf("readlink %q failed: %v", from, err) @@ -857,9 +843,6 @@ func TestLongSymlink(t *testing.T) { func TestRename(t *testing.T) { defer chtmpdir(t)() from, to := "renamefrom", "renameto" - // Ensure we are not testing the overwrite case here. - Remove(from) - Remove(to) file, err := Create(from) if err != nil { @@ -872,7 +855,6 @@ func TestRename(t *testing.T) { if err != nil { t.Fatalf("rename %q, %q failed: %v", to, from, err) } - defer Remove(to) _, err = Stat(to) if err != nil { t.Errorf("stat %q failed: %v", to, err) @@ -882,9 +864,6 @@ func TestRename(t *testing.T) { func TestRenameOverwriteDest(t *testing.T) { defer chtmpdir(t)() from, to := "renamefrom", "renameto" - // Just in case. - Remove(from) - Remove(to) toData := []byte("to") fromData := []byte("from") @@ -902,7 +881,6 @@ func TestRenameOverwriteDest(t *testing.T) { if err != nil { t.Fatalf("rename %q, %q failed: %v", to, from, err) } - defer Remove(to) _, err = Stat(from) if err == nil { @@ -923,9 +901,6 @@ func TestRenameOverwriteDest(t *testing.T) { func TestRenameFailed(t *testing.T) { defer chtmpdir(t)() from, to := "renamefrom", "renameto" - // Ensure we are not testing the overwrite case here. - Remove(from) - Remove(to) err := Rename(from, to) switch err := err.(type) { @@ -941,9 +916,6 @@ func TestRenameFailed(t *testing.T) { } 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) } @@ -954,7 +926,6 @@ func TestRenameNotExisting(t *testing.T) { from, to := "doesnt-exist", "dest" Mkdir(to, 0777) - defer Remove(to) if err := Rename(from, to); !IsNotExist(err) { t.Errorf("Rename(%q, %q) = %v; want an IsNotExist error", from, to, err) @@ -965,12 +936,8 @@ 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) { @@ -986,9 +953,6 @@ func TestRenameToDirFailed(t *testing.T) { } 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) } @@ -1483,7 +1447,7 @@ func TestOpenNoName(t *testing.T) { } } -func runBinHostname(t *testing.T, argv []string) string { +func runBinHostname(t *testing.T) string { // Run /bin/hostname and collect output. r, w, err := Pipe() if err != nil { @@ -1491,6 +1455,10 @@ func runBinHostname(t *testing.T, argv []string) string { } defer r.Close() const path = "/bin/hostname" + argv := []string{"hostname"} + if runtime.GOOS == "aix" { + argv = []string{"hostname", "-s"} + } p, err := StartProcess(path, argv, &ProcAttr{Files: []*File{nil, w, Stderr}}) if err != nil { if _, err := Stat(path); IsNotExist(err) { @@ -1561,12 +1529,7 @@ func TestHostname(t *testing.T) { // Check internal Hostname() against the output of /bin/hostname. // Allow that the internal Hostname returns a Fully Qualified Domain Name // and the /bin/hostname only returns the first component - var want string - if runtime.GOOS == "aix" { - want = runBinHostname(t, []string{"hostname", "-s"}) - } else { - want = runBinHostname(t, []string{"hostname"}) - } + want := runBinHostname(t) if hostname != want { i := strings.Index(hostname, ".") if i < 0 || hostname[0:i] != want { @@ -1701,7 +1664,6 @@ func writeFile(t *testing.T, fname string, flag int, text string) string { func TestAppend(t *testing.T) { defer chtmpdir(t)() const f = "append.txt" - defer Remove(f) s := writeFile(t, f, O_CREATE|O_TRUNC|O_RDWR, "new") if s != "new" { t.Fatalf("writeFile: have %q want %q", s, "new") @@ -1768,13 +1730,11 @@ func TestSameFile(t *testing.T) { if err != nil { t.Fatalf("Create(a): %v", err) } - defer Remove(fa.Name()) fa.Close() fb, err := Create("b") if err != nil { t.Fatalf("Create(b): %v", err) } - defer Remove(fb.Name()) fb.Close() ia1, err := Stat("a") @@ -2336,3 +2296,20 @@ func TestDoubleCloseError(t *testing.T) { t.Logf("second close returned expected error %q", err) } } + +func TestUserHomeDir(t *testing.T) { + dir, err := UserHomeDir() + if dir == "" && err == nil { + t.Fatal("UserHomeDir returned an empty string but no error") + } + if err != nil { + t.Skipf("UserHomeDir failed: %v", err) + } + fi, err := Stat(dir) + if err != nil { + t.Fatal(err) + } + if !fi.IsDir() { + t.Fatalf("dir %s is not directory; type = %v", dir, fi.Mode()) + } +} diff --git a/libgo/go/os/os_unix_test.go b/libgo/go/os/os_unix_test.go index 54f121e..2aa930e 100644 --- a/libgo/go/os/os_unix_test.go +++ b/libgo/go/os/os_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux netbsd openbsd solaris +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris package os_test @@ -22,6 +22,9 @@ func init() { isReadonlyError = func(err error) bool { return err == syscall.EROFS } } +// For TestRawConnReadWrite. +type syscallDescriptor = int + func checkUidGid(t *testing.T, path string, uid, gid int) { dir, err := Lstat(path) if err != nil { @@ -234,7 +237,7 @@ func newFileTest(t *testing.T, blocking bool) { } defer syscall.Close(p[1]) - // Set the the read-side to non-blocking. + // Set the read-side to non-blocking. if !blocking { if err := syscall.SetNonblock(p[0], true); err != nil { syscall.Close(p[0]) diff --git a/libgo/go/os/path.go b/libgo/go/os/path.go index cdfbc18..30cc6c8 100644 --- a/libgo/go/os/path.go +++ b/libgo/go/os/path.go @@ -5,7 +5,6 @@ package os import ( - "io" "syscall" ) @@ -59,100 +58,13 @@ func MkdirAll(path string, perm FileMode) error { return nil } -// RemoveAll removes path and any children it contains. -// It removes everything it can but returns the first error -// it encounters. If the path does not exist, RemoveAll -// returns nil (no error). -func RemoveAll(path string) error { - // Simple case: if Remove works, we're done. - err := Remove(path) - if err == nil || IsNotExist(err) { - return nil +// endsWithDot reports whether the final component of path is ".". +func endsWithDot(path string) bool { + if path == "." { + return true } - - // Otherwise, is this a directory we need to recurse into? - dir, serr := Lstat(path) - if serr != nil { - if serr, ok := serr.(*PathError); ok && (IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) { - return nil - } - return serr - } - if !dir.IsDir() { - // Not a directory; return the error from Remove. - return err - } - - // Remove contents & return first error. - err = nil - for { - fd, err := Open(path) - if err != nil { - if IsNotExist(err) { - // Already deleted by someone else. - return nil - } - return err - } - - const request = 1024 - names, err1 := fd.Readdirnames(request) - - // Removing files from the directory may have caused - // the OS to reshuffle it. Simply calling Readdirnames - // again may skip some entries. The only reliable way - // to avoid this is to close and re-open the - // directory. See issue 20841. - fd.Close() - - for _, name := range names { - err1 := RemoveAll(path + string(PathSeparator) + name) - if err == nil { - err = err1 - } - } - - if err1 == io.EOF { - break - } - // If Readdirnames returned an error, use it. - if err == nil { - err = err1 - } - if len(names) == 0 { - break - } - - // We don't want to re-open unnecessarily, so if we - // got fewer than request names from Readdirnames, try - // simply removing the directory now. If that - // succeeds, we are done. - if len(names) < request { - err1 := Remove(path) - if err1 == nil || IsNotExist(err1) { - return nil - } - - if err != nil { - // We got some error removing the - // directory contents, and since we - // read fewer names than we requested - // there probably aren't more files to - // remove. Don't loop around to read - // the directory again. We'll probably - // just get the same error. - return err - } - } - } - - // Remove directory. - err1 := Remove(path) - if err1 == nil || IsNotExist(err1) { - return nil - } - if err == nil { - err = err1 + if len(path) >= 2 && path[len(path)-1] == '.' && IsPathSeparator(path[len(path)-2]) { + return true } - return err + return false } diff --git a/libgo/go/os/path_test.go b/libgo/go/os/path_test.go index f58c7e7..6cb25bc 100644 --- a/libgo/go/os/path_test.go +++ b/libgo/go/os/path_test.go @@ -5,7 +5,6 @@ package os_test import ( - "fmt" "internal/testenv" "io/ioutil" . "os" @@ -76,130 +75,6 @@ func TestMkdirAll(t *testing.T) { } } -func TestRemoveAll(t *testing.T) { - tmpDir := TempDir() - // Work directory. - path := tmpDir + "/_TestRemoveAll_" - fpath := path + "/file" - dpath := path + "/dir" - - // Make directory with 1 file and remove. - if err := MkdirAll(path, 0777); err != nil { - t.Fatalf("MkdirAll %q: %s", path, err) - } - fd, err := Create(fpath) - if err != nil { - t.Fatalf("create %q: %s", fpath, err) - } - fd.Close() - if err = RemoveAll(path); err != nil { - t.Fatalf("RemoveAll %q (first): %s", path, err) - } - if _, err = Lstat(path); err == nil { - t.Fatalf("Lstat %q succeeded after RemoveAll (first)", path) - } - - // Make directory with file and subdirectory and remove. - if err = MkdirAll(dpath, 0777); err != nil { - t.Fatalf("MkdirAll %q: %s", dpath, err) - } - fd, err = Create(fpath) - if err != nil { - t.Fatalf("create %q: %s", fpath, err) - } - fd.Close() - fd, err = Create(dpath + "/file") - if err != nil { - t.Fatalf("create %q: %s", fpath, err) - } - fd.Close() - if err = RemoveAll(path); err != nil { - t.Fatalf("RemoveAll %q (second): %s", path, err) - } - if _, err := Lstat(path); err == nil { - t.Fatalf("Lstat %q succeeded after RemoveAll (second)", path) - } - - // Determine if we should run the following test. - testit := true - if runtime.GOOS == "windows" { - // Chmod is not supported under windows. - testit = false - } else { - // Test fails as root. - testit = Getuid() != 0 - } - if testit { - // Make directory with file and subdirectory and trigger error. - if err = MkdirAll(dpath, 0777); err != nil { - t.Fatalf("MkdirAll %q: %s", dpath, err) - } - - for _, s := range []string{fpath, dpath + "/file1", path + "/zzz"} { - fd, err = Create(s) - if err != nil { - t.Fatalf("create %q: %s", s, err) - } - fd.Close() - } - if err = Chmod(dpath, 0); err != nil { - t.Fatalf("Chmod %q 0: %s", dpath, err) - } - - // No error checking here: either RemoveAll - // will or won't be able to remove dpath; - // either way we want to see if it removes fpath - // and path/zzz. Reasons why RemoveAll might - // succeed in removing dpath as well include: - // * running as root - // * running on a file system without permissions (FAT) - RemoveAll(path) - Chmod(dpath, 0777) - - for _, s := range []string{fpath, path + "/zzz"} { - if _, err = Lstat(s); err == nil { - t.Fatalf("Lstat %q succeeded after partial RemoveAll", s) - } - } - } - if err = RemoveAll(path); err != nil { - t.Fatalf("RemoveAll %q after partial RemoveAll: %s", path, err) - } - if _, err = Lstat(path); err == nil { - t.Fatalf("Lstat %q succeeded after RemoveAll (final)", path) - } -} - -// Test RemoveAll on a large directory. -func TestRemoveAllLarge(t *testing.T) { - if testing.Short() { - t.Skip("skipping in short mode") - } - - tmpDir := TempDir() - // Work directory. - path := tmpDir + "/_TestRemoveAllLarge_" - - // Make directory with 1000 files and remove. - if err := MkdirAll(path, 0777); err != nil { - t.Fatalf("MkdirAll %q: %s", path, err) - } - for i := 0; i < 1000; i++ { - fpath := fmt.Sprintf("%s/file%d", path, i) - fd, err := Create(fpath) - if err != nil { - t.Fatalf("create %q: %s", fpath, err) - } - fd.Close() - } - if err := RemoveAll(path); err != nil { - t.Fatalf("RemoveAll %q: %s", path, err) - } - if _, err := Lstat(path); err == nil { - t.Fatalf("Lstat %q succeeded after RemoveAll", path) - } -} - func TestMkdirAllWithSymlink(t *testing.T) { testenv.MustHaveSymlink(t) diff --git a/libgo/go/os/path_unix.go b/libgo/go/os/path_unix.go index 3cb0e3a..be373a5 100644 --- a/libgo/go/os/path_unix.go +++ b/libgo/go/os/path_unix.go @@ -16,7 +16,7 @@ func IsPathSeparator(c uint8) bool { return PathSeparator == c } -// basename removes trailing slashes and the leading directory name from path name +// basename removes trailing slashes and the leading directory name from path name. func basename(name string) string { i := len(name) - 1 // Remove trailing slashes @@ -34,6 +34,32 @@ func basename(name string) string { return name } +// splitPath returns the base name and parent directory. +func splitPath(path string) (string, string) { + // if no better parent is found, the path is relative from "here" + dirname := "." + // if no slashes in path, base is path + basename := path + + i := len(path) - 1 + + // Remove trailing slashes + for ; i > 0 && path[i] == '/'; i-- { + path = path[:i] + } + + // Remove leading directory path + for i--; i >= 0; i-- { + if path[i] == '/' { + dirname = path[:i+1] + basename = path[i+1:] + break + } + } + + return dirname, basename +} + func fixRootDirectory(p string) string { return p } diff --git a/libgo/go/os/path_windows_test.go b/libgo/go/os/path_windows_test.go index 00a3e63..f1745ad 100644 --- a/libgo/go/os/path_windows_test.go +++ b/libgo/go/os/path_windows_test.go @@ -38,10 +38,10 @@ func TestFixLongPath(t *testing.T) { {`\\?\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) + in := strings.ReplaceAll(test.in, "long", veryLong) + want := strings.ReplaceAll(test.want, "long", veryLong) if got := os.FixLongPath(in); got != want { - got = strings.Replace(got, veryLong, "long", -1) + got = strings.ReplaceAll(got, veryLong, "long") t.Errorf("fixLongPath(%q) = %q; want %q", test.in, got, test.want) } } diff --git a/libgo/go/os/pipe_test.go b/libgo/go/os/pipe_test.go index 59d31e5..779b2bd 100644 --- a/libgo/go/os/pipe_test.go +++ b/libgo/go/os/pipe_test.go @@ -131,7 +131,7 @@ func testClosedPipeRace(t *testing.T, read bool) { if !read { // Get the amount we have to write to overload a pipe // with no reader. - limit = 65537 + limit = 131073 if b, err := ioutil.ReadFile("/proc/sys/fs/pipe-max-size"); err == nil { if i, err := strconv.Atoi(strings.TrimSpace(string(b))); err == nil { limit = i + 1 diff --git a/libgo/go/os/rawconn.go b/libgo/go/os/rawconn.go new file mode 100644 index 0000000..9e11cda --- /dev/null +++ b/libgo/go/os/rawconn.go @@ -0,0 +1,47 @@ +// Copyright 2018 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 ( + "runtime" +) + +// rawConn implements syscall.RawConn. +type rawConn struct { + file *File +} + +func (c *rawConn) Control(f func(uintptr)) error { + if err := c.file.checkValid("SyscallConn.Control"); err != nil { + return err + } + err := c.file.pfd.RawControl(f) + runtime.KeepAlive(c.file) + return err +} + +func (c *rawConn) Read(f func(uintptr) bool) error { + if err := c.file.checkValid("SyscallConn.Read"); err != nil { + return err + } + err := c.file.pfd.RawRead(f) + runtime.KeepAlive(c.file) + return err +} + +func (c *rawConn) Write(f func(uintptr) bool) error { + if err := c.file.checkValid("SyscallConn.Write"); err != nil { + return err + } + err := c.file.pfd.RawWrite(f) + runtime.KeepAlive(c.file) + return err +} + +func newRawConn(file *File) (*rawConn, error) { + return &rawConn{file: file}, nil +} diff --git a/libgo/go/os/rawconn_test.go b/libgo/go/os/rawconn_test.go new file mode 100644 index 0000000..820150d --- /dev/null +++ b/libgo/go/os/rawconn_test.go @@ -0,0 +1,65 @@ +// Copyright 2018 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. + +// Test use of raw connections. +// +build !plan9,!nacl,!js + +package os_test + +import ( + "os" + "syscall" + "testing" +) + +func TestRawConnReadWrite(t *testing.T) { + t.Parallel() + + r, w, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + defer r.Close() + defer w.Close() + + rconn, err := r.SyscallConn() + if err != nil { + t.Fatal(err) + } + wconn, err := w.SyscallConn() + if err != nil { + t.Fatal(err) + } + + var operr error + err = wconn.Write(func(s uintptr) bool { + _, operr = syscall.Write(syscallDescriptor(s), []byte{'b'}) + return operr != syscall.EAGAIN + }) + if err != nil { + t.Fatal(err) + } + if operr != nil { + t.Fatal(err) + } + + var n int + buf := make([]byte, 1) + err = rconn.Read(func(s uintptr) bool { + n, operr = syscall.Read(syscallDescriptor(s), buf) + return operr != syscall.EAGAIN + }) + if err != nil { + t.Fatal(err) + } + if operr != nil { + t.Fatal(operr) + } + if n != 1 { + t.Errorf("read %d bytes, expected 1", n) + } + if buf[0] != 'b' { + t.Errorf("read %q, expected %q", buf, "b") + } +} diff --git a/libgo/go/os/removeall_at.go b/libgo/go/os/removeall_at.go new file mode 100644 index 0000000..f0fed6d --- /dev/null +++ b/libgo/go/os/removeall_at.go @@ -0,0 +1,138 @@ +// Copyright 2018 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 aix darwin dragonfly freebsd linux netbsd openbsd solaris + +package os + +import ( + "internal/syscall/unix" + "io" + "syscall" +) + +func RemoveAll(path string) error { + if path == "" { + // fail silently to retain compatibility with previous behavior + // of RemoveAll. See issue 28830. + return nil + } + + // The rmdir system call does not permit removing ".", + // so we don't permit it either. + if endsWithDot(path) { + return &PathError{"RemoveAll", path, syscall.EINVAL} + } + + // Simple case: if Remove works, we're done. + err := Remove(path) + if err == nil || IsNotExist(err) { + return nil + } + + // RemoveAll recurses by deleting the path base from + // its parent directory + parentDir, base := splitPath(path) + + parent, err := Open(parentDir) + if IsNotExist(err) { + // If parent does not exist, base cannot exist. Fail silently + return nil + } + if err != nil { + return err + } + defer parent.Close() + + return removeAllFrom(parent, base) +} + +func removeAllFrom(parent *File, path string) error { + parentFd := int(parent.Fd()) + // Simple case: if Unlink (aka remove) works, we're done. + err := unix.Unlinkat(parentFd, path, 0) + if err == nil || IsNotExist(err) { + return nil + } + + // If not a "is directory" error, we have a problem + if err != syscall.EISDIR && err != syscall.EPERM { + return err + } + + // Is this a directory we need to recurse into? + var statInfo syscall.Stat_t + statErr := unix.Fstatat(parentFd, path, &statInfo, unix.AT_SYMLINK_NOFOLLOW) + if statErr != nil { + return statErr + } + if statInfo.Mode&syscall.S_IFMT != syscall.S_IFDIR { + // Not a directory; return the error from the Remove + return err + } + + // Remove the directory's entries + var recurseErr error + for { + const request = 1024 + + // Open the directory to recurse into + file, err := openFdAt(parentFd, path) + if err != nil { + if IsNotExist(err) { + return nil + } + return err + } + + names, readErr := file.Readdirnames(request) + // Errors other than EOF should stop us from continuing + if readErr != nil && readErr != io.EOF { + file.Close() + if IsNotExist(readErr) { + return nil + } + return readErr + } + + for _, name := range names { + err := removeAllFrom(file, name) + if err != nil { + recurseErr = err + } + } + + // Removing files from the directory may have caused + // the OS to reshuffle it. Simply calling Readdirnames + // again may skip some entries. The only reliable way + // to avoid this is to close and re-open the + // directory. See issue 20841. + file.Close() + + // Finish when the end of the directory is reached + if len(names) < request { + break + } + } + + // Remove the directory itself + unlinkError := unix.Unlinkat(parentFd, path, unix.AT_REMOVEDIR) + if unlinkError == nil || IsNotExist(unlinkError) { + return nil + } + + if recurseErr != nil { + return recurseErr + } + return unlinkError +} + +func openFdAt(fd int, path string) (*File, error) { + fd, err := unix.Openat(fd, path, O_RDONLY, 0) + if err != nil { + return nil, err + } + + return NewFile(uintptr(fd), path), nil +} diff --git a/libgo/go/os/removeall_noat.go b/libgo/go/os/removeall_noat.go new file mode 100644 index 0000000..80527e2 --- /dev/null +++ b/libgo/go/os/removeall_noat.go @@ -0,0 +1,123 @@ +// Copyright 2018 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 !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris + +package os + +import ( + "io" + "syscall" +) + +// RemoveAll removes path and any children it contains. +// It removes everything it can but returns the first error +// it encounters. If the path does not exist, RemoveAll +// returns nil (no error). +func RemoveAll(path string) error { + if path == "" { + // fail silently to retain compatibility with previous behavior + // of RemoveAll. See issue 28830. + return nil + } + + // The rmdir system call permits removing "." on Plan 9, + // so we don't permit it to remain consistent with the + // "at" implementation of RemoveAll. + if endsWithDot(path) { + return &PathError{"RemoveAll", path, syscall.EINVAL} + } + + // Simple case: if Remove works, we're done. + err := Remove(path) + if err == nil || IsNotExist(err) { + return nil + } + + // Otherwise, is this a directory we need to recurse into? + dir, serr := Lstat(path) + if serr != nil { + if serr, ok := serr.(*PathError); ok && (IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) { + return nil + } + return serr + } + if !dir.IsDir() { + // Not a directory; return the error from Remove. + return err + } + + // Remove contents & return first error. + err = nil + for { + fd, err := Open(path) + if err != nil { + if IsNotExist(err) { + // Already deleted by someone else. + return nil + } + return err + } + + const request = 1024 + names, err1 := fd.Readdirnames(request) + + // Removing files from the directory may have caused + // the OS to reshuffle it. Simply calling Readdirnames + // again may skip some entries. The only reliable way + // to avoid this is to close and re-open the + // directory. See issue 20841. + fd.Close() + + for _, name := range names { + err1 := RemoveAll(path + string(PathSeparator) + name) + if err == nil { + err = err1 + } + } + + if err1 == io.EOF { + break + } + // If Readdirnames returned an error, use it. + if err == nil { + err = err1 + } + if len(names) == 0 { + break + } + + // We don't want to re-open unnecessarily, so if we + // got fewer than request names from Readdirnames, try + // simply removing the directory now. If that + // succeeds, we are done. + if len(names) < request { + err1 := Remove(path) + if err1 == nil || IsNotExist(err1) { + return nil + } + + if err != nil { + // We got some error removing the + // directory contents, and since we + // read fewer names than we requested + // there probably aren't more files to + // remove. Don't loop around to read + // the directory again. We'll probably + // just get the same error. + return err + } + } + } + + // Remove directory. + err1 := Remove(path) + if err1 == nil || IsNotExist(err1) { + return nil + } + if err == nil { + err = err1 + } + return err +} diff --git a/libgo/go/os/removeall_test.go b/libgo/go/os/removeall_test.go new file mode 100644 index 0000000..0f7dce0 --- /dev/null +++ b/libgo/go/os/removeall_test.go @@ -0,0 +1,294 @@ +// Copyright 2018 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" + "io/ioutil" + . "os" + "path/filepath" + "runtime" + "strings" + "testing" +) + +func TestRemoveAll(t *testing.T) { + tmpDir, err := ioutil.TempDir("", "TestRemoveAll-") + if err != nil { + t.Fatal(err) + } + defer RemoveAll(tmpDir) + + if err := RemoveAll(""); err != nil { + t.Errorf("RemoveAll(\"\"): %v; want nil", err) + } + + file := filepath.Join(tmpDir, "file") + path := filepath.Join(tmpDir, "_TestRemoveAll_") + fpath := filepath.Join(path, "file") + dpath := filepath.Join(path, "dir") + + // Make a regular file and remove + fd, err := Create(file) + if err != nil { + t.Fatalf("create %q: %s", file, err) + } + fd.Close() + if err = RemoveAll(file); err != nil { + t.Fatalf("RemoveAll %q (first): %s", file, err) + } + if _, err = Lstat(file); err == nil { + t.Fatalf("Lstat %q succeeded after RemoveAll (first)", file) + } + + // Make directory with 1 file and remove. + if err := MkdirAll(path, 0777); err != nil { + t.Fatalf("MkdirAll %q: %s", path, err) + } + fd, err = Create(fpath) + if err != nil { + t.Fatalf("create %q: %s", fpath, err) + } + fd.Close() + if err = RemoveAll(path); err != nil { + t.Fatalf("RemoveAll %q (second): %s", path, err) + } + if _, err = Lstat(path); err == nil { + t.Fatalf("Lstat %q succeeded after RemoveAll (second)", path) + } + + // Make directory with file and subdirectory and remove. + if err = MkdirAll(dpath, 0777); err != nil { + t.Fatalf("MkdirAll %q: %s", dpath, err) + } + fd, err = Create(fpath) + if err != nil { + t.Fatalf("create %q: %s", fpath, err) + } + fd.Close() + fd, err = Create(dpath + "/file") + if err != nil { + t.Fatalf("create %q: %s", fpath, err) + } + fd.Close() + if err = RemoveAll(path); err != nil { + t.Fatalf("RemoveAll %q (third): %s", path, err) + } + if _, err := Lstat(path); err == nil { + t.Fatalf("Lstat %q succeeded after RemoveAll (third)", path) + } + + // Determine if we should run the following test. + testit := true + if runtime.GOOS == "windows" { + // Chmod is not supported under windows. + testit = false + } else { + // Test fails as root. + testit = Getuid() != 0 + } + if testit { + // Make directory with file and subdirectory and trigger error. + if err = MkdirAll(dpath, 0777); err != nil { + t.Fatalf("MkdirAll %q: %s", dpath, err) + } + + for _, s := range []string{fpath, dpath + "/file1", path + "/zzz"} { + fd, err = Create(s) + if err != nil { + t.Fatalf("create %q: %s", s, err) + } + fd.Close() + } + if err = Chmod(dpath, 0); err != nil { + t.Fatalf("Chmod %q 0: %s", dpath, err) + } + + // No error checking here: either RemoveAll + // will or won't be able to remove dpath; + // either way we want to see if it removes fpath + // and path/zzz. Reasons why RemoveAll might + // succeed in removing dpath as well include: + // * running as root + // * running on a file system without permissions (FAT) + RemoveAll(path) + Chmod(dpath, 0777) + + for _, s := range []string{fpath, path + "/zzz"} { + if _, err = Lstat(s); err == nil { + t.Fatalf("Lstat %q succeeded after partial RemoveAll", s) + } + } + } + if err = RemoveAll(path); err != nil { + t.Fatalf("RemoveAll %q after partial RemoveAll: %s", path, err) + } + if _, err = Lstat(path); err == nil { + t.Fatalf("Lstat %q succeeded after RemoveAll (final)", path) + } +} + +// Test RemoveAll on a large directory. +func TestRemoveAllLarge(t *testing.T) { + if testing.Short() { + t.Skip("skipping in short mode") + } + + tmpDir, err := ioutil.TempDir("", "TestRemoveAll-") + if err != nil { + t.Fatal(err) + } + defer RemoveAll(tmpDir) + + path := filepath.Join(tmpDir, "_TestRemoveAllLarge_") + + // Make directory with 1000 files and remove. + if err := MkdirAll(path, 0777); err != nil { + t.Fatalf("MkdirAll %q: %s", path, err) + } + for i := 0; i < 1000; i++ { + fpath := fmt.Sprintf("%s/file%d", path, i) + fd, err := Create(fpath) + if err != nil { + t.Fatalf("create %q: %s", fpath, err) + } + fd.Close() + } + if err := RemoveAll(path); err != nil { + t.Fatalf("RemoveAll %q: %s", path, err) + } + if _, err := Lstat(path); err == nil { + t.Fatalf("Lstat %q succeeded after RemoveAll", path) + } +} + +func TestRemoveAllLongPath(t *testing.T) { + switch runtime.GOOS { + case "aix", "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris": + break + default: + t.Skip("skipping for not implemented platforms") + } + + prevDir, err := Getwd() + if err != nil { + t.Fatalf("Could not get wd: %s", err) + } + + startPath, err := ioutil.TempDir("", "TestRemoveAllLongPath-") + if err != nil { + t.Fatalf("Could not create TempDir: %s", err) + } + defer RemoveAll(startPath) + + err = Chdir(startPath) + if err != nil { + t.Fatalf("Could not chdir %s: %s", startPath, err) + } + + // Removing paths with over 4096 chars commonly fails + for i := 0; i < 41; i++ { + name := strings.Repeat("a", 100) + + err = Mkdir(name, 0755) + if err != nil { + t.Fatalf("Could not mkdir %s: %s", name, err) + } + + err = Chdir(name) + if err != nil { + t.Fatalf("Could not chdir %s: %s", name, err) + } + } + + err = Chdir(prevDir) + if err != nil { + t.Fatalf("Could not chdir %s: %s", prevDir, err) + } + + err = RemoveAll(startPath) + if err != nil { + t.Errorf("RemoveAll could not remove long file path %s: %s", startPath, err) + } +} + +func TestRemoveAllDot(t *testing.T) { + prevDir, err := Getwd() + if err != nil { + t.Fatalf("Could not get wd: %s", err) + } + tempDir, err := ioutil.TempDir("", "TestRemoveAllDot-") + if err != nil { + t.Fatalf("Could not create TempDir: %s", err) + } + defer RemoveAll(tempDir) + + err = Chdir(tempDir) + if err != nil { + t.Fatalf("Could not chdir to tempdir: %s", err) + } + + err = RemoveAll(".") + if err == nil { + t.Errorf("RemoveAll succeed to remove .") + } + + err = Chdir(prevDir) + if err != nil { + t.Fatalf("Could not chdir %s: %s", prevDir, err) + } +} + +func TestRemoveAllDotDot(t *testing.T) { + t.Parallel() + + tempDir, err := ioutil.TempDir("", "TestRemoveAllDotDot-") + if err != nil { + t.Fatal(err) + } + defer RemoveAll(tempDir) + + subdir := filepath.Join(tempDir, "x") + subsubdir := filepath.Join(subdir, "y") + if err := MkdirAll(subsubdir, 0777); err != nil { + t.Fatal(err) + } + if err := RemoveAll(filepath.Join(subsubdir, "..")); err != nil { + t.Error(err) + } + for _, dir := range []string{subsubdir, subdir} { + if _, err := Stat(dir); err == nil { + t.Errorf("%s: exists after RemoveAll", dir) + } + } +} + +// Issue #29178. +func TestRemoveReadOnlyDir(t *testing.T) { + t.Parallel() + + tempDir, err := ioutil.TempDir("", "TestRemoveReadOnlyDir-") + if err != nil { + t.Fatal(err) + } + defer RemoveAll(tempDir) + + subdir := filepath.Join(tempDir, "x") + if err := Mkdir(subdir, 0); err != nil { + t.Fatal(err) + } + + // If an error occurs make it more likely that removing the + // temporary directory will succeed. + defer Chmod(subdir, 0777) + + if err := RemoveAll(subdir); err != nil { + t.Fatal(err) + } + + if _, err := Stat(subdir); err == nil { + t.Error("subdirectory was not removed") + } +} diff --git a/libgo/go/os/signal/internal/pty/pty.go b/libgo/go/os/signal/internal/pty/pty.go index e52d19a..a82cf05 100644 --- a/libgo/go/os/signal/internal/pty/pty.go +++ b/libgo/go/os/signal/internal/pty/pty.go @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. // +build aix darwin dragonfly freebsd linux,!android netbsd openbsd solaris +// +build cgo // Package pty is a simple pseudo-terminal package for Unix systems, // implemented by calling C functions via cgo. diff --git a/libgo/go/os/signal/signal_cgo_test.go b/libgo/go/os/signal/signal_cgo_test.go index 16aeea8..3c23090 100644 --- a/libgo/go/os/signal/signal_cgo_test.go +++ b/libgo/go/os/signal/signal_cgo_test.go @@ -22,6 +22,7 @@ import ( "os/signal/internal/pty" "strconv" "strings" + "sync" "syscall" "testing" "time" @@ -113,7 +114,11 @@ func TestTerminalSignal(t *testing.T) { const prompt = "prompt> " // Read data from master in the background. + var wg sync.WaitGroup + wg.Add(1) + defer wg.Wait() go func() { + defer wg.Done() input := bufio.NewReader(master) var line, handled []byte for { diff --git a/libgo/go/os/stat_aix.go b/libgo/go/os/stat_aix.go index 265761f..5301827 100644 --- a/libgo/go/os/stat_aix.go +++ b/libgo/go/os/stat_aix.go @@ -1,4 +1,4 @@ -// Copyright 2009 The Go Authors. All rights reserved. +// Copyright 2018 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/libgo/go/os/stat_test.go b/libgo/go/os/stat_test.go new file mode 100644 index 0000000..60f3b4c --- /dev/null +++ b/libgo/go/os/stat_test.go @@ -0,0 +1,292 @@ +// Copyright 2018 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 ( + "internal/testenv" + "io/ioutil" + "os" + "path/filepath" + "runtime" + "testing" +) + +// testStatAndLstat verifies that all os.Stat, os.Lstat os.File.Stat and os.Readdir work. +func testStatAndLstat(t *testing.T, path string, isLink bool, statCheck, lstatCheck func(*testing.T, string, os.FileInfo)) { + // test os.Stat + sfi, err := os.Stat(path) + if err != nil { + t.Error(err) + return + } + statCheck(t, path, sfi) + + // test os.Lstat + lsfi, err := os.Lstat(path) + if err != nil { + t.Error(err) + return + } + lstatCheck(t, path, lsfi) + + if isLink { + if os.SameFile(sfi, lsfi) { + t.Errorf("stat and lstat of %q should not be the same", path) + } + } else { + if !os.SameFile(sfi, lsfi) { + t.Errorf("stat and lstat of %q should be the same", path) + } + } + + // test os.File.Stat + f, err := os.Open(path) + if err != nil { + t.Error(err) + return + } + defer f.Close() + + sfi2, err := f.Stat() + if err != nil { + t.Error(err) + return + } + statCheck(t, path, sfi2) + + if !os.SameFile(sfi, sfi2) { + t.Errorf("stat of open %q file and stat of %q should be the same", path, path) + } + + if isLink { + if os.SameFile(sfi2, lsfi) { + t.Errorf("stat of opened %q file and lstat of %q should not be the same", path, path) + } + } else { + if !os.SameFile(sfi2, lsfi) { + t.Errorf("stat of opened %q file and lstat of %q should be the same", path, path) + } + } + + // test os.FileInfo returned by os.Readdir + if len(path) > 0 && os.IsPathSeparator(path[len(path)-1]) { + // skip os.Readdir test of directories with slash at the end + return + } + parentdir := filepath.Dir(path) + parent, err := os.Open(parentdir) + if err != nil { + t.Error(err) + return + } + defer parent.Close() + + fis, err := parent.Readdir(-1) + if err != nil { + t.Error(err) + return + } + var lsfi2 os.FileInfo + base := filepath.Base(path) + for _, fi2 := range fis { + if fi2.Name() == base { + lsfi2 = fi2 + break + } + } + if lsfi2 == nil { + t.Errorf("failed to find %q in its parent", path) + return + } + lstatCheck(t, path, lsfi2) + + if !os.SameFile(lsfi, lsfi2) { + t.Errorf("lstat of %q file in %q directory and %q should be the same", lsfi2.Name(), parentdir, path) + } +} + +// testIsDir verifies that fi refers to directory. +func testIsDir(t *testing.T, path string, fi os.FileInfo) { + t.Helper() + if !fi.IsDir() { + t.Errorf("%q should be a directory", path) + } + if fi.Mode()&os.ModeSymlink != 0 { + t.Errorf("%q should not be a symlink", path) + } +} + +// testIsSymlink verifies that fi refers to symlink. +func testIsSymlink(t *testing.T, path string, fi os.FileInfo) { + t.Helper() + if fi.IsDir() { + t.Errorf("%q should not be a directory", path) + } + if fi.Mode()&os.ModeSymlink == 0 { + t.Errorf("%q should be a symlink", path) + } +} + +// testIsFile verifies that fi refers to file. +func testIsFile(t *testing.T, path string, fi os.FileInfo) { + t.Helper() + if fi.IsDir() { + t.Errorf("%q should not be a directory", path) + } + if fi.Mode()&os.ModeSymlink != 0 { + t.Errorf("%q should not be a symlink", path) + } +} + +func testDirStats(t *testing.T, path string) { + testStatAndLstat(t, path, false, testIsDir, testIsDir) +} + +func testFileStats(t *testing.T, path string) { + testStatAndLstat(t, path, false, testIsFile, testIsFile) +} + +func testSymlinkStats(t *testing.T, path string, isdir bool) { + if isdir { + testStatAndLstat(t, path, true, testIsDir, testIsSymlink) + } else { + testStatAndLstat(t, path, true, testIsFile, testIsSymlink) + } +} + +func testSymlinkSameFile(t *testing.T, path, link string) { + pathfi, err := os.Stat(path) + if err != nil { + t.Error(err) + return + } + + linkfi, err := os.Stat(link) + if err != nil { + t.Error(err) + return + } + if !os.SameFile(pathfi, linkfi) { + t.Errorf("os.Stat(%q) and os.Stat(%q) are not the same file", path, link) + } + + linkfi, err = os.Lstat(link) + if err != nil { + t.Error(err) + return + } + if os.SameFile(pathfi, linkfi) { + t.Errorf("os.Stat(%q) and os.Lstat(%q) are the same file", path, link) + } +} + +func TestDirAndSymlinkStats(t *testing.T) { + testenv.MustHaveSymlink(t) + + tmpdir, err := ioutil.TempDir("", "TestDirAndSymlinkStats") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + dir := filepath.Join(tmpdir, "dir") + err = os.Mkdir(dir, 0777) + if err != nil { + t.Fatal(err) + } + testDirStats(t, dir) + + dirlink := filepath.Join(tmpdir, "link") + err = os.Symlink(dir, dirlink) + if err != nil { + t.Fatal(err) + } + testSymlinkStats(t, dirlink, true) + testSymlinkSameFile(t, dir, dirlink) + + linklink := filepath.Join(tmpdir, "linklink") + err = os.Symlink(dirlink, linklink) + if err != nil { + t.Fatal(err) + } + testSymlinkStats(t, linklink, true) + testSymlinkSameFile(t, dir, linklink) +} + +func TestFileAndSymlinkStats(t *testing.T) { + testenv.MustHaveSymlink(t) + + tmpdir, err := ioutil.TempDir("", "TestFileAndSymlinkStats") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + file := filepath.Join(tmpdir, "file") + err = ioutil.WriteFile(file, []byte(""), 0644) + if err != nil { + t.Fatal(err) + } + testFileStats(t, file) + + filelink := filepath.Join(tmpdir, "link") + err = os.Symlink(file, filelink) + if err != nil { + t.Fatal(err) + } + testSymlinkStats(t, filelink, false) + testSymlinkSameFile(t, file, filelink) + + linklink := filepath.Join(tmpdir, "linklink") + err = os.Symlink(filelink, linklink) + if err != nil { + t.Fatal(err) + } + testSymlinkStats(t, linklink, false) + testSymlinkSameFile(t, file, linklink) +} + +// see issue 27225 for details +func TestSymlinkWithTrailingSlash(t *testing.T) { + testenv.MustHaveSymlink(t) + + tmpdir, err := ioutil.TempDir("", "TestSymlinkWithTrailingSlash") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + dir := filepath.Join(tmpdir, "dir") + err = os.Mkdir(dir, 0777) + if err != nil { + t.Fatal(err) + } + dirlink := filepath.Join(tmpdir, "link") + err = os.Symlink(dir, dirlink) + if err != nil { + t.Fatal(err) + } + dirlinkWithSlash := dirlink + string(os.PathSeparator) + + if runtime.GOOS == "windows" { + testSymlinkStats(t, dirlinkWithSlash, true) + } else { + testDirStats(t, dirlinkWithSlash) + } + + fi1, err := os.Stat(dir) + if err != nil { + t.Error(err) + return + } + fi2, err := os.Stat(dirlinkWithSlash) + if err != nil { + t.Error(err) + return + } + if !os.SameFile(fi1, fi2) { + t.Errorf("os.Stat(%q) and os.Stat(%q) are not the same file", dir, dirlinkWithSlash) + } +} diff --git a/libgo/go/os/sticky_bsd.go b/libgo/go/os/sticky_bsd.go index 6b54c75..ae2744f 100644 --- a/libgo/go/os/sticky_bsd.go +++ b/libgo/go/os/sticky_bsd.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd netbsd openbsd solaris +// +build aix darwin dragonfly freebsd netbsd openbsd solaris package os diff --git a/libgo/go/os/sticky_notbsd.go b/libgo/go/os/sticky_notbsd.go index 834e79b..edb5f69 100644 --- a/libgo/go/os/sticky_notbsd.go +++ b/libgo/go/os/sticky_notbsd.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !aix // +build !darwin // +build !dragonfly // +build !freebsd diff --git a/libgo/go/os/sys_aix.go b/libgo/go/os/sys_aix.go new file mode 100644 index 0000000..53a40f2 --- /dev/null +++ b/libgo/go/os/sys_aix.go @@ -0,0 +1,26 @@ +// Copyright 2018 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" + +// gethostname syscall cannot be used because it also returns the domain. +// Therefore, hostname is retrieve with uname syscall and the Nodename field. + +func hostname() (name string, err error) { + var u syscall.Utsname + if errno := syscall.Uname(&u); errno != nil { + return "", NewSyscallError("uname", errno) + } + b := make([]byte, len(u.Nodename)) + i := 0 + for ; i < len(u.Nodename); i++ { + if u.Nodename[i] == 0 { + break + } + b[i] = byte(u.Nodename[i]) + } + return string(b[:i]), nil +} diff --git a/libgo/go/os/types.go b/libgo/go/os/types.go index b0b7d8d..4b6c084 100644 --- a/libgo/go/os/types.go +++ b/libgo/go/os/types.go @@ -57,7 +57,7 @@ const ( ModeIrregular // ?: non-regular file; nothing else is known about this file // Mask for the type bits. For regular files, none will be set. - ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice | ModeIrregular + ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice | ModeCharDevice | ModeIrregular ModePerm FileMode = 0777 // Unix permission bits ) diff --git a/libgo/go/os/types_windows.go b/libgo/go/os/types_windows.go index f3297c0..5e33292 100644 --- a/libgo/go/os/types_windows.go +++ b/libgo/go/os/types_windows.go @@ -47,6 +47,21 @@ func newFileStatFromGetFileInformationByHandle(path string, h syscall.Handle) (f if err != nil { return nil, &PathError{"GetFileInformationByHandle", path, err} } + + var ti windows.FILE_ATTRIBUTE_TAG_INFO + err = windows.GetFileInformationByHandleEx(h, windows.FileAttributeTagInfo, (*byte)(unsafe.Pointer(&ti)), uint32(unsafe.Sizeof(ti))) + if err != nil { + if errno, ok := err.(syscall.Errno); ok && errno == windows.ERROR_INVALID_PARAMETER { + // It appears calling GetFileInformationByHandleEx with + // FILE_ATTRIBUTE_TAG_INFO fails on FAT file system with + // ERROR_INVALID_PARAMETER. Clear ti.ReparseTag in that + // instance to indicate no symlinks are possible. + ti.ReparseTag = 0 + } else { + return nil, &PathError{"GetFileInformationByHandleEx", path, err} + } + } + return &fileStat{ name: basename(path), FileAttributes: d.FileAttributes, @@ -58,6 +73,7 @@ func newFileStatFromGetFileInformationByHandle(path string, h syscall.Handle) (f vol: d.VolumeSerialNumber, idxhi: d.FileIndexHigh, idxlo: d.FileIndexLow, + Reserved0: ti.ReparseTag, // fileStat.path is used by os.SameFile to decide if it needs // to fetch vol, idxhi and idxlo. But these are already set, // so set fileStat.path to "" to prevent os.SameFile doing it again. @@ -78,67 +94,6 @@ func newFileStatFromWin32finddata(d *syscall.Win32finddata) *fileStat { } } -// newFileStatFromGetFileAttributesExOrFindFirstFile calls GetFileAttributesEx -// and FindFirstFile to gather all required information about the provided file path pathp. -func newFileStatFromGetFileAttributesExOrFindFirstFile(path string, pathp *uint16) (*fileStat, error) { - // As suggested by Microsoft, use GetFileAttributes() to acquire the file information, - // and if it's a reparse point use FindFirstFile() to get the tag: - // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363940(v=vs.85).aspx - // Notice that always calling FindFirstFile can create performance problems - // (https://golang.org/issues/19922#issuecomment-300031421) - var fa syscall.Win32FileAttributeData - err := syscall.GetFileAttributesEx(pathp, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fa))) - if err == nil && fa.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 { - // Not a symlink. - return &fileStat{ - FileAttributes: fa.FileAttributes, - CreationTime: fa.CreationTime, - LastAccessTime: fa.LastAccessTime, - LastWriteTime: fa.LastWriteTime, - FileSizeHigh: fa.FileSizeHigh, - FileSizeLow: fa.FileSizeLow, - }, nil - } - // GetFileAttributesEx returns ERROR_INVALID_NAME if called - // for invalid file name like "*.txt". Do not attempt to call - // FindFirstFile with "*.txt", because FindFirstFile will - // succeed. So just return ERROR_INVALID_NAME instead. - // see https://golang.org/issue/24999 for details. - if errno, _ := err.(syscall.Errno); errno == windows.ERROR_INVALID_NAME { - return nil, &PathError{"GetFileAttributesEx", path, err} - } - // We might have symlink here. But some directories also have - // FileAttributes FILE_ATTRIBUTE_REPARSE_POINT bit set. - // For example, OneDrive directory is like that - // (see golang.org/issue/22579 for details). - // So use FindFirstFile instead to distinguish directories like - // OneDrive from real symlinks (see instructions described at - // https://blogs.msdn.microsoft.com/oldnewthing/20100212-00/?p=14963/ - // and in particular bits about using both FileAttributes and - // Reserved0 fields). - var fd syscall.Win32finddata - sh, err := syscall.FindFirstFile(pathp, &fd) - if err != nil { - return nil, &PathError{"FindFirstFile", path, err} - } - syscall.FindClose(sh) - - return newFileStatFromWin32finddata(&fd), nil -} - -func (fs *fileStat) updatePathAndName(name string) error { - fs.path = name - if !isAbs(fs.path) { - var err error - fs.path, err = syscall.FullPath(fs.path) - if err != nil { - return &PathError{"FullPath", name, err} - } - } - fs.name = basename(name) - return nil -} - func (fs *fileStat) isSymlink() bool { // Use instructions described at // https://blogs.msdn.microsoft.com/oldnewthing/20100212-00/?p=14963/ @@ -211,7 +166,13 @@ func (fs *fileStat) loadFileId() error { if err != nil { return err } - h, err := syscall.CreateFile(pathp, 0, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) + attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS) + if fs.isSymlink() { + // Use FILE_FLAG_OPEN_REPARSE_POINT, otherwise CreateFile will follow symlink. + // See https://docs.microsoft.com/en-us/windows/desktop/FileIO/symbolic-link-effects-on-file-systems-functions#createfile-and-createfiletransacted + attrs |= syscall.FILE_FLAG_OPEN_REPARSE_POINT + } + h, err := syscall.CreateFile(pathp, 0, 0, nil, syscall.OPEN_EXISTING, attrs, 0) if err != nil { return err } diff --git a/libgo/go/os/user/lookup.go b/libgo/go/os/user/lookup.go index 2243a25..b36b7c0 100644 --- a/libgo/go/os/user/lookup.go +++ b/libgo/go/os/user/lookup.go @@ -7,6 +7,10 @@ package user import "sync" // Current returns the current user. +// +// The first call will cache the current user information. +// Subsequent calls will return the cached value and will not reflect +// changes to the current user. func Current() (*User, error) { cache.Do(func() { cache.u, cache.err = current() }) if cache.err != nil { diff --git a/libgo/go/os/user/lookup_stubs.go b/libgo/go/os/user/lookup_stubs.go index f7d138f..61bf1dc 100644 --- a/libgo/go/os/user/lookup_stubs.go +++ b/libgo/go/os/user/lookup_stubs.go @@ -19,8 +19,15 @@ func init() { } func current() (*User, error) { - u := &User{ - Uid: currentUID(), + uid := currentUID() + // $USER and /etc/passwd may disagree; prefer the latter if we can get it. + // See issue 27524 for more information. + u, err := lookupUserId(uid) + if err == nil { + return u, nil + } + u = &User{ + Uid: uid, Gid: currentGID(), Username: os.Getenv("USER"), Name: "", // ignored @@ -58,8 +65,8 @@ func current() (*User, error) { } func listGroups(*User) ([]string, error) { - if runtime.GOOS == "android" { - return nil, errors.New("user: GroupIds not implemented on Android") + if runtime.GOOS == "android" || runtime.GOOS == "aix" { + return nil, errors.New(fmt.Sprintf("user: GroupIds not implemented on %s", runtime.GOOS)) } return nil, errors.New("user: GroupIds requires cgo") } diff --git a/libgo/go/os/user/lookup_unix.go b/libgo/go/os/user/lookup_unix.go index c4e9ba1..be62f4d 100644 --- a/libgo/go/os/user/lookup_unix.go +++ b/libgo/go/os/user/lookup_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd js,wasm !android,linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd js,wasm !android,linux nacl netbsd openbsd solaris // +build !cgo osusergo package user diff --git a/libgo/go/os/user/lookup_unix_test.go b/libgo/go/os/user/lookup_unix_test.go index 02c88ab..65fe065 100644 --- a/libgo/go/os/user/lookup_unix_test.go +++ b/libgo/go/os/user/lookup_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd !android,linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd !android,linux nacl netbsd openbsd solaris // +build !cgo package user diff --git a/libgo/go/os/user/user.go b/libgo/go/os/user/user.go index 1f733b8..c1b8101 100644 --- a/libgo/go/os/user/user.go +++ b/libgo/go/os/user/user.go @@ -11,7 +11,7 @@ parses /etc/passwd and /etc/group. The other is cgo-based and relies on the standard C library (libc) routines such as getpwuid_r and getgrnam_r. When cgo is available, cgo-based (libc-backed) code is used by default. -This can be overriden by using osusergo build tag, which enforces +This can be overridden by using osusergo build tag, which enforces the pure Go implementation. */ package user diff --git a/libgo/go/os/user/user_test.go b/libgo/go/os/user/user_test.go index 8fd760e..2563077 100644 --- a/libgo/go/os/user/user_test.go +++ b/libgo/go/os/user/user_test.go @@ -5,33 +5,18 @@ package user import ( - "internal/testenv" - "os" "runtime" "testing" ) func checkUser(t *testing.T) { + t.Helper() if !userImplemented { t.Skip("user: not implemented; skipping tests") } } func TestCurrent(t *testing.T) { - // The Go builders (in particular the ones using containers) - // often have minimal environments without $HOME or $USER set, - // which breaks Current which relies on those working as a - // fallback. - // TODO: we should fix that (Issue 24884) and remove these - // workarounds. - if testenv.Builder() != "" && runtime.GOOS != "windows" && runtime.GOOS != "plan9" { - if os.Getenv("HOME") == "" { - os.Setenv("HOME", "/tmp") - } - if os.Getenv("USER") == "" { - os.Setenv("USER", "gobuilder") - } - } u, err := Current() if err != nil { t.Fatalf("Current: %v (got %#v)", err, u) @@ -108,6 +93,7 @@ func TestLookupId(t *testing.T) { } func checkGroup(t *testing.T) { + t.Helper() if !groupImplemented { t.Skip("user: group not implemented; skipping test") } diff --git a/libgo/go/os/wait_unimp.go b/libgo/go/os/wait_unimp.go index d070604..469abf7 100644 --- a/libgo/go/os/wait_unimp.go +++ b/libgo/go/os/wait_unimp.go @@ -7,7 +7,7 @@ package os // blockUntilWaitable attempts to block until a call to p.Wait will -// succeed immediately, and returns whether it has done so. +// succeed immediately, and reports whether it has done so. // It does not actually call p.Wait. // This version is used on systems that do not implement waitid, // or where we have not implemented it yet. diff --git a/libgo/go/os/wait_wait6.go b/libgo/go/os/wait_wait6.go index 891f242..45bf649 100644 --- a/libgo/go/os/wait_wait6.go +++ b/libgo/go/os/wait_wait6.go @@ -14,7 +14,7 @@ import ( const _P_PID = 0 // blockUntilWaitable attempts to block until a call to p.Wait will -// succeed immediately, and returns whether it has done so. +// succeed immediately, and reports whether it has done so. // It does not actually call p.Wait. func (p *Process) blockUntilWaitable() (bool, error) { var errno syscall.Errno diff --git a/libgo/go/os/wait_waitid.go b/libgo/go/os/wait_waitid.go index a6284aa..4bb77f9 100644 --- a/libgo/go/os/wait_waitid.go +++ b/libgo/go/os/wait_waitid.go @@ -18,7 +18,7 @@ import ( const _P_PID = 1 // blockUntilWaitable attempts to block until a call to p.Wait will -// succeed immediately, and returns whether it has done so. +// succeed immediately, and reports whether it has done so. // It does not actually call p.Wait. func (p *Process) blockUntilWaitable() (bool, error) { // The waitid system call expects a pointer to a siginfo_t, |