diff options
Diffstat (limited to 'libgo/go/os')
-rw-r--r-- | libgo/go/os/dir_unix.go | 2 | ||||
-rw-r--r-- | libgo/go/os/env.go | 5 | ||||
-rw-r--r-- | libgo/go/os/env_test.go | 26 | ||||
-rw-r--r-- | libgo/go/os/error_plan9.go | 3 | ||||
-rw-r--r-- | libgo/go/os/exec/exec.go | 13 | ||||
-rw-r--r-- | libgo/go/os/exec/exec_test.go | 17 | ||||
-rw-r--r-- | libgo/go/os/exec_unix.go | 14 | ||||
-rw-r--r-- | libgo/go/os/exec_windows.go | 3 | ||||
-rw-r--r-- | libgo/go/os/file.go | 9 | ||||
-rw-r--r-- | libgo/go/os/file_plan9.go | 11 | ||||
-rw-r--r-- | libgo/go/os/file_posix.go | 22 | ||||
-rw-r--r-- | libgo/go/os/file_unix.go | 35 | ||||
-rw-r--r-- | libgo/go/os/getwd.go | 24 | ||||
-rw-r--r-- | libgo/go/os/os_test.go | 195 | ||||
-rw-r--r-- | libgo/go/os/path.go | 16 | ||||
-rw-r--r-- | libgo/go/os/path_test.go | 11 | ||||
-rw-r--r-- | libgo/go/os/proc.go | 15 | ||||
-rw-r--r-- | libgo/go/os/signal/signal_test.go | 6 | ||||
-rw-r--r-- | libgo/go/os/types_windows.go | 3 | ||||
-rw-r--r-- | libgo/go/os/user/lookup_stubs.go | 2 | ||||
-rw-r--r-- | libgo/go/os/user/lookup_unix.go | 2 |
21 files changed, 335 insertions, 99 deletions
diff --git a/libgo/go/os/dir_unix.go b/libgo/go/os/dir_unix.go index d353e405e54..589db852740 100644 --- a/libgo/go/os/dir_unix.go +++ b/libgo/go/os/dir_unix.go @@ -36,7 +36,7 @@ func (f *File) readdirnames(n int) (names []string, err error) { if d.bufp >= d.nbuf { d.bufp = 0 var errno error - d.nbuf, errno = syscall.ReadDirent(f.fd, d.buf) + d.nbuf, errno = fixCount(syscall.ReadDirent(f.fd, d.buf)) if errno != nil { return names, NewSyscallError("readdirent", errno) } diff --git a/libgo/go/os/env.go b/libgo/go/os/env.go index db7fc72b8a4..d0494a47634 100644 --- a/libgo/go/os/env.go +++ b/libgo/go/os/env.go @@ -91,6 +91,11 @@ func Setenv(key, value string) error { return nil } +// Unsetenv unsets a single environment variable. +func Unsetenv(key string) error { + return syscall.Unsetenv(key) +} + // Clearenv deletes all environment variables. func Clearenv() { syscall.Clearenv() diff --git a/libgo/go/os/env_test.go b/libgo/go/os/env_test.go index 991fa4d0578..e6180675137 100644 --- a/libgo/go/os/env_test.go +++ b/libgo/go/os/env_test.go @@ -7,6 +7,7 @@ package os_test import ( . "os" "reflect" + "strings" "testing" ) @@ -68,3 +69,28 @@ func TestConsistentEnviron(t *testing.T) { } } } + +func TestUnsetenv(t *testing.T) { + const testKey = "GO_TEST_UNSETENV" + set := func() bool { + prefix := testKey + "=" + for _, key := range Environ() { + if strings.HasPrefix(key, prefix) { + return true + } + } + return false + } + if err := Setenv(testKey, "1"); err != nil { + t.Fatalf("Setenv: %v", err) + } + if !set() { + t.Error("Setenv didn't set TestUnsetenv") + } + if err := Unsetenv(testKey); err != nil { + t.Fatalf("Unsetenv: %v", err) + } + if set() { + t.Fatal("Unsetenv didn't clear TestUnsetenv") + } +} diff --git a/libgo/go/os/error_plan9.go b/libgo/go/os/error_plan9.go index 85260c82aea..001cdfcf2e3 100644 --- a/libgo/go/os/error_plan9.go +++ b/libgo/go/os/error_plan9.go @@ -25,7 +25,8 @@ func isNotExist(err error) bool { case *LinkError: err = pe.Err } - return contains(err.Error(), "does not exist") + return contains(err.Error(), "does not exist") || contains(err.Error(), "not found") || + contains(err.Error(), "has been removed") || contains(err.Error(), "no parent") } func isPermission(err error) bool { diff --git a/libgo/go/os/exec/exec.go b/libgo/go/os/exec/exec.go index a70ed0d20cb..72b4905d560 100644 --- a/libgo/go/os/exec/exec.go +++ b/libgo/go/os/exec/exec.go @@ -55,8 +55,15 @@ type Cmd struct { // calling process's current directory. Dir string - // Stdin specifies the process's standard input. If Stdin is - // nil, the process reads from the null device (os.DevNull). + // Stdin specifies the process's standard input. + // If Stdin is nil, the process reads from the null device (os.DevNull). + // If Stdin is an *os.File, the process's standard input is connected + // directly to that file. + // Otherwise, during the execution of the command a separate + // goroutine reads from Stdin and delivers that data to the command + // over a pipe. In this case, Wait does not complete until the goroutine + // stops copying, either because it has reached the end of Stdin + // (EOF or a read error) or because writing to the pipe returned an error. Stdin io.Reader // Stdout and Stderr specify the process's standard output and error. @@ -358,7 +365,7 @@ func (c *Cmd) Wait() error { c.ProcessState = state var copyError error - for _ = range c.goroutine { + for range c.goroutine { if err := <-c.errch; err != nil && copyError == nil { copyError = err } diff --git a/libgo/go/os/exec/exec_test.go b/libgo/go/os/exec/exec_test.go index 8521bfda3f9..f9ffde602ca 100644 --- a/libgo/go/os/exec/exec_test.go +++ b/libgo/go/os/exec/exec_test.go @@ -250,7 +250,7 @@ func TestPipeLookPathLeak(t *testing.T) { } func numOpenFDS(t *testing.T) (n int, lsof []byte) { - lsof, err := exec.Command("lsof", "-n", "-p", strconv.Itoa(os.Getpid())).Output() + lsof, err := exec.Command("lsof", "-b", "-n", "-p", strconv.Itoa(os.Getpid())).Output() if err != nil { t.Skip("skipping test; error finding or running lsof") } @@ -262,15 +262,7 @@ var testedAlreadyLeaked = false // basefds returns the number of expected file descriptors // to be present in a process at start. func basefds() uintptr { - n := os.Stderr.Fd() + 1 - - // Go runtime for 32-bit Plan 9 requires that /dev/bintime - // be kept open. - // See ../../runtime/time_plan9_386.c:/^runtime·nanotime - if runtime.GOOS == "plan9" && runtime.GOARCH == "386" { - n++ - } - return n + return os.Stderr.Fd() + 1 } func closeUnexpectedFds(t *testing.T, m string) { @@ -387,8 +379,9 @@ func TestExtraFilesFDShuffle(t *testing.T) { } func TestExtraFiles(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("no operating system support; skipping") + switch runtime.GOOS { + case "nacl", "windows": + t.Skipf("skipping test on %q", runtime.GOOS) } // Ensure that file descriptors have not already been leaked into diff --git a/libgo/go/os/exec_unix.go b/libgo/go/os/exec_unix.go index 1b1e3350b84..ed97f85e22f 100644 --- a/libgo/go/os/exec_unix.go +++ b/libgo/go/os/exec_unix.go @@ -34,18 +34,26 @@ func (p *Process) wait() (ps *ProcessState, err error) { return ps, nil } +var errFinished = errors.New("os: process already finished") + func (p *Process) signal(sig Signal) error { - if p.done() { - return errors.New("os: process already finished") - } if p.Pid == -1 { return errors.New("os: process already released") } + if p.Pid == 0 { + return errors.New("os: process not initialized") + } + if p.done() { + return errFinished + } s, ok := sig.(syscall.Signal) if !ok { return errors.New("os: unsupported signal type") } if e := syscall.Kill(p.Pid, s); e != nil { + if e == syscall.ESRCH { + return errFinished + } return e } return nil diff --git a/libgo/go/os/exec_windows.go b/libgo/go/os/exec_windows.go index c4f3d4f8530..393393b2375 100644 --- a/libgo/go/os/exec_windows.go +++ b/libgo/go/os/exec_windows.go @@ -53,6 +53,9 @@ func terminateProcess(pid, exitcode int) error { } func (p *Process) signal(sig Signal) error { + if p.handle == uintptr(syscall.InvalidHandle) { + return syscall.EINVAL + } if p.done() { return errors.New("os: process already finished") } diff --git a/libgo/go/os/file.go b/libgo/go/os/file.go index b4a74580162..e12428cbe12 100644 --- a/libgo/go/os/file.go +++ b/libgo/go/os/file.go @@ -255,3 +255,12 @@ var lstat = Lstat func Rename(oldpath, newpath string) error { return rename(oldpath, newpath) } + +// Many functions in package syscall return a count of -1 instead of 0. +// Using fixCount(call()) instead of call() corrects the count. +func fixCount(n int, err error) (int, error) { + if n < 0 { + n = 0 + } + return n, err +} diff --git a/libgo/go/os/file_plan9.go b/libgo/go/os/file_plan9.go index a804b819731..132594eede9 100644 --- a/libgo/go/os/file_plan9.go +++ b/libgo/go/os/file_plan9.go @@ -25,7 +25,8 @@ type file struct { dirinfo *dirInfo // nil unless directory being read } -// Fd returns the integer Unix file descriptor referencing the open file. +// Fd returns the integer Plan 9 file descriptor referencing the open file. +// The file descriptor is valid only until f.Close is called or f is garbage collected. func (f *File) Fd() uintptr { if f == nil { return ^(uintptr(0)) @@ -244,14 +245,14 @@ func (f *File) Sync() (err error) { // read reads up to len(b) bytes from the File. // It returns the number of bytes read and an error, if any. func (f *File) read(b []byte) (n int, err error) { - return syscall.Read(f.fd, b) + return fixCount(syscall.Read(f.fd, b)) } // pread reads len(b) bytes from the File starting at byte offset off. // It returns the number of bytes read and the error, if any. // EOF is signaled by a zero count with err set to nil. func (f *File) pread(b []byte, off int64) (n int, err error) { - return syscall.Pread(f.fd, b, off) + return fixCount(syscall.Pread(f.fd, b, off)) } // write writes len(b) bytes to the File. @@ -262,7 +263,7 @@ func (f *File) write(b []byte) (n int, err error) { if len(b) == 0 { return 0, nil } - return syscall.Write(f.fd, b) + return fixCount(syscall.Write(f.fd, b)) } // pwrite writes len(b) bytes to the File starting at byte offset off. @@ -273,7 +274,7 @@ func (f *File) pwrite(b []byte, off int64) (n int, err error) { if len(b) == 0 { return 0, nil } - return syscall.Pwrite(f.fd, b, off) + return fixCount(syscall.Pwrite(f.fd, b, off)) } // seek sets the offset for the next Read or Write on file to offset, interpreted diff --git a/libgo/go/os/file_posix.go b/libgo/go/os/file_posix.go index b3466b15cc8..fbb3b5e4d81 100644 --- a/libgo/go/os/file_posix.go +++ b/libgo/go/os/file_posix.go @@ -13,32 +13,12 @@ import ( func sigpipe() // implemented in package runtime -// Link creates newname as a hard link to the oldname file. -// If there is an error, it will be of type *LinkError. -func Link(oldname, newname string) error { - e := syscall.Link(oldname, newname) - if e != nil { - return &LinkError{"link", oldname, newname, e} - } - return nil -} - -// Symlink creates newname as a symbolic link to oldname. -// If there is an error, it will be of type *LinkError. -func Symlink(oldname, newname string) error { - e := syscall.Symlink(oldname, newname) - if e != nil { - return &LinkError{"symlink", oldname, newname, e} - } - return nil -} - // Readlink returns the destination of the named symbolic link. // If there is an error, it will be of type *PathError. func Readlink(name string) (string, error) { for len := 128; ; len *= 2 { b := make([]byte, len) - n, e := syscall.Readlink(name, b) + n, e := fixCount(syscall.Readlink(name, b)) if e != nil { return "", &PathError{"readlink", name, e} } diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go index 7959091995f..b25e62ff003 100644 --- a/libgo/go/os/file_unix.go +++ b/libgo/go/os/file_unix.go @@ -29,6 +29,7 @@ type file struct { } // Fd returns the integer Unix file descriptor referencing the open file. +// The file descriptor is valid only until f.Close is called or f is garbage collected. func (f *File) Fd() uintptr { if f == nil { return ^(uintptr(0)) @@ -198,7 +199,7 @@ func (f *File) read(b []byte) (n int, err error) { if needsMaxRW && len(b) > maxRW { b = b[:maxRW] } - return syscall.Read(f.fd, b) + return fixCount(syscall.Read(f.fd, b)) } // pread reads len(b) bytes from the File starting at byte offset off. @@ -208,7 +209,7 @@ func (f *File) pread(b []byte, off int64) (n int, err error) { if needsMaxRW && len(b) > maxRW { b = b[:maxRW] } - return syscall.Pread(f.fd, b, off) + return fixCount(syscall.Pread(f.fd, b, off)) } // write writes len(b) bytes to the File. @@ -219,7 +220,7 @@ func (f *File) write(b []byte) (n int, err error) { if needsMaxRW && len(bcap) > maxRW { bcap = bcap[:maxRW] } - m, err := syscall.Write(f.fd, bcap) + m, err := fixCount(syscall.Write(f.fd, bcap)) n += m // If the syscall wrote some data but not all (short write) @@ -245,7 +246,7 @@ func (f *File) pwrite(b []byte, off int64) (n int, err error) { if needsMaxRW && len(b) > maxRW { b = b[:maxRW] } - return syscall.Pwrite(f.fd, b, off) + return fixCount(syscall.Pwrite(f.fd, b, off)) } // seek sets the offset for the next Read or Write on file to offset, interpreted @@ -319,7 +320,31 @@ func basename(name string) string { func TempDir() string { dir := Getenv("TMPDIR") if dir == "" { - dir = "/tmp" + if runtime.GOOS == "android" { + dir = "/data/local/tmp" + } else { + dir = "/tmp" + } } return dir } + +// Link creates newname as a hard link to the oldname file. +// If there is an error, it will be of type *LinkError. +func Link(oldname, newname string) error { + e := syscall.Link(oldname, newname) + if e != nil { + return &LinkError{"link", oldname, newname, e} + } + return nil +} + +// Symlink creates newname as a symbolic link to oldname. +// If there is an error, it will be of type *LinkError. +func Symlink(oldname, newname string) error { + e := syscall.Symlink(oldname, newname) + if e != nil { + return &LinkError{"symlink", oldname, newname, e} + } + return nil +} diff --git a/libgo/go/os/getwd.go b/libgo/go/os/getwd.go index a72edeaee6e..d5da53b34bf 100644 --- a/libgo/go/os/getwd.go +++ b/libgo/go/os/getwd.go @@ -5,6 +5,7 @@ package os import ( + "runtime" "sync" "syscall" ) @@ -23,22 +24,16 @@ var useSyscallwd = func(error) bool { return true } // reached via multiple paths (due to symbolic links), // Getwd may return any one of them. func Getwd() (dir string, err error) { - // If the operating system provides a Getwd call, use it. - if syscall.ImplementsGetwd { - s, e := syscall.Getwd() - if useSyscallwd(e) { - return s, NewSyscallError("getwd", e) - } + if runtime.GOOS == "windows" { + return syscall.Getwd() } - // Otherwise, we're trying to find our way back to ".". + // Clumsy but widespread kludge: + // if $PWD is set and matches ".", use it. dot, err := Stat(".") if err != nil { return "", err } - - // Clumsy but widespread kludge: - // if $PWD is set and matches ".", use it. dir = Getenv("PWD") if len(dir) > 0 && dir[0] == '/' { d, err := Stat(dir) @@ -47,6 +42,15 @@ func Getwd() (dir string, err error) { } } + // If the operating system provides a Getwd call, use it. + // Otherwise, we're trying to find our way back to ".". + if syscall.ImplementsGetwd { + s, e := syscall.Getwd() + if useSyscallwd(e) { + return s, NewSyscallError("getwd", e) + } + } + // Apply same kludge but to cached dir instead of $PWD. getwdCache.Lock() dir = getwdCache.dir diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go index a34e328b814..7a86efac218 100644 --- a/libgo/go/os/os_test.go +++ b/libgo/go/os/os_test.go @@ -18,12 +18,15 @@ import ( "runtime" "sort" "strings" + "sync" "syscall" "testing" "text/template" "time" ) +var supportsSymlinks = true + var dot = []string{ "dir_unix.go", "env.go", @@ -40,6 +43,14 @@ type sysDir struct { var sysdir = func() (sd *sysDir) { switch runtime.GOOS { + case "android": + sd = &sysDir{ + "/system/etc", + []string{ + "audio_policy.conf", + "system_fonts.xml", + }, + } case "windows": sd = &sysDir{ Getenv("SystemRoot") + "\\system32\\drivers\\etc", @@ -106,12 +117,27 @@ func newFile(testName string, t *testing.T) (f *File) { // On Unix, override $TMPDIR in case the user // has it set to an NFS-mounted directory. dir := "" - if runtime.GOOS != "windows" { + if runtime.GOOS != "android" && runtime.GOOS != "windows" { dir = "/tmp" } f, err := ioutil.TempFile(dir, "_Go_"+testName) if err != nil { - t.Fatalf("open %s: %s", testName, err) + t.Fatalf("TempFile %s: %s", testName, err) + } + return +} + +func newDir(testName string, t *testing.T) (name string) { + // Use a local file system, not NFS. + // On Unix, override $TMPDIR in case the user + // has it set to an NFS-mounted directory. + dir := "" + if runtime.GOOS != "android" && runtime.GOOS != "windows" { + dir = "/tmp" + } + name, err := ioutil.TempDir(dir, "_Go_"+testName) + if err != nil { + t.Fatalf("TempDir %s: %s", testName, err) } return } @@ -282,10 +308,12 @@ func TestReaddirnamesOneAtATime(t *testing.T) { // big directory that doesn't change often. dir := "/usr/bin" switch runtime.GOOS { - case "windows": - dir = Getenv("SystemRoot") + "\\system32" + case "android": + dir = "/system/bin" case "plan9": dir = "/bin" + case "windows": + dir = Getenv("SystemRoot") + "\\system32" } file, err := Open(dir) if err != nil { @@ -463,7 +491,7 @@ func TestReaddirStatFailures(t *testing.T) { func TestHardLink(t *testing.T) { // Hardlinks are not supported under windows or Plan 9. - if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { + if runtime.GOOS == "plan9" { return } from, to := "hardlinktestfrom", "hardlinktestto" @@ -496,8 +524,12 @@ func TestHardLink(t *testing.T) { func TestSymlink(t *testing.T) { switch runtime.GOOS { - case "windows", "plan9", "nacl": + case "android", "nacl", "plan9": t.Skipf("skipping on %s", runtime.GOOS) + case "windows": + if !supportsSymlinks { + t.Skipf("skipping on %s", runtime.GOOS) + } } from, to := "symlinktestfrom", "symlinktestto" Remove(from) // Just in case. @@ -558,8 +590,12 @@ func TestSymlink(t *testing.T) { func TestLongSymlink(t *testing.T) { switch runtime.GOOS { - case "windows", "plan9", "nacl": + case "plan9", "nacl": t.Skipf("skipping on %s", runtime.GOOS) + case "windows": + if !supportsSymlinks { + t.Skipf("skipping on %s", runtime.GOOS) + } } s := "0123456789abcdef" // Long, but not too long: a common limit is 255. @@ -628,8 +664,9 @@ func exec(t *testing.T, dir, cmd string, args []string, expect string) { } func TestStartProcess(t *testing.T) { - if runtime.GOOS == "nacl" { - t.Skip("skipping on nacl") + switch runtime.GOOS { + case "android", "nacl": + t.Skipf("skipping on %s", runtime.GOOS) } var dir, cmd string @@ -731,35 +768,49 @@ func TestTruncate(t *testing.T) { } } -// Use TempDir() to make sure we're on a local file system, +// Use TempDir (via newFile) to make sure we're on a local file system, // so that timings are not distorted by latency and caching. // On NFS, timings can be off due to caching of meta-data on // NFS servers (Issue 848). func TestChtimes(t *testing.T) { f := newFile("TestChtimes", t) defer Remove(f.Name()) - defer f.Close() f.Write([]byte("hello, world\n")) f.Close() - st, err := Stat(f.Name()) + testChtimes(t, f.Name()) +} + +// Use TempDir (via newDir) to make sure we're on a local file system, +// so that timings are not distorted by latency and caching. +// On NFS, timings can be off due to caching of meta-data on +// NFS servers (Issue 848). +func TestChtimesDir(t *testing.T) { + name := newDir("TestChtimes", t) + defer RemoveAll(name) + + testChtimes(t, name) +} + +func testChtimes(t *testing.T, name string) { + st, err := Stat(name) if err != nil { - t.Fatalf("Stat %s: %s", f.Name(), err) + t.Fatalf("Stat %s: %s", name, err) } preStat := st // Move access and modification time back a second at := Atime(preStat) mt := preStat.ModTime() - err = Chtimes(f.Name(), at.Add(-time.Second), mt.Add(-time.Second)) + err = Chtimes(name, at.Add(-time.Second), mt.Add(-time.Second)) if err != nil { - t.Fatalf("Chtimes %s: %s", f.Name(), err) + t.Fatalf("Chtimes %s: %s", name, err) } - st, err = Stat(f.Name()) + st, err = Stat(name) if err != nil { - t.Fatalf("second Stat %s: %s", f.Name(), err) + t.Fatalf("second Stat %s: %s", name, err) } postStat := st @@ -788,12 +839,16 @@ func TestChdirAndGetwd(t *testing.T) { t.Fatalf("Open .: %s", err) } // These are chosen carefully not to be symlinks on a Mac - // (unlike, say, /var, /etc, and /tmp). - dirs := []string{"/", "/usr/bin"} - // /usr/bin does not usually exist on Plan 9. - if runtime.GOOS == "plan9" { + // (unlike, say, /var, /etc), except /tmp, which we handle below. + dirs := []string{"/", "/usr/bin", "/tmp"} + // /usr/bin does not usually exist on Plan 9 or Android. + switch runtime.GOOS { + case "android": + dirs = []string{"/", "/system/bin"} + case "plan9": dirs = []string{"/", "/usr"} } + oldwd := Getenv("PWD") for mode := 0; mode < 2; mode++ { for _, d := range dirs { if mode == 0 { @@ -807,7 +862,11 @@ func TestChdirAndGetwd(t *testing.T) { err = fd1.Chdir() fd1.Close() } + if d == "/tmp" { + Setenv("PWD", "/tmp") + } pwd, err1 := Getwd() + Setenv("PWD", oldwd) err2 := fd.Chdir() if err2 != nil { // We changed the current directory and cannot go back. @@ -910,6 +969,12 @@ func TestOpenError(t *testing.T) { syscallErrStr := perr.Err.Error() expectedErrStr := strings.Replace(tt.error.Error(), "file ", "", 1) if !strings.HasSuffix(syscallErrStr, expectedErrStr) { + // Some Plan 9 file servers incorrectly return + // EACCES rather than EISDIR when a directory is + // opened for write. + if tt.error == syscall.EISDIR && strings.HasSuffix(syscallErrStr, syscall.EACCES.Error()) { + continue + } t.Errorf("Open(%q, %d) = _, %q; want suffix %q", tt.path, tt.mode, syscallErrStr, expectedErrStr) } continue @@ -970,9 +1035,9 @@ func run(t *testing.T, cmd []string) string { func TestHostname(t *testing.T) { // There is no other way to fetch hostname on windows, but via winapi. - // On Plan 9 it is can be taken from #c/sysname as Hostname() does. + // On Plan 9 it can be taken from #c/sysname as Hostname() does. switch runtime.GOOS { - case "windows", "plan9", "nacl": + case "android", "nacl", "plan9", "windows": t.Skipf("skipping on %s", runtime.GOOS) } @@ -1233,8 +1298,9 @@ func TestReadAtEOF(t *testing.T) { func testKillProcess(t *testing.T, processKiller func(p *Process)) { t.Skip("gccgo does not have a go command") - if runtime.GOOS == "nacl" { - t.Skip("skipping on nacl") + switch runtime.GOOS { + case "android", "nacl": + t.Skipf("skipping on %s", runtime.GOOS) } dir, err := ioutil.TempDir("", "go-build") @@ -1292,6 +1358,36 @@ func TestKillStartProcess(t *testing.T) { }) } +func TestGetppid(t *testing.T) { + switch runtime.GOOS { + case "nacl": + t.Skip("skipping on nacl") + case "plan9": + // TODO: golang.org/issue/8206 + t.Skipf("skipping test on plan9; see issue 8206") + } + + if Getenv("GO_WANT_HELPER_PROCESS") == "1" { + fmt.Print(Getppid()) + Exit(0) + } + + cmd := osexec.Command(Args[0], "-test.run=TestGetppid") + cmd.Env = append(Environ(), "GO_WANT_HELPER_PROCESS=1") + + // verify that Getppid() from the forked process reports our process id + output, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("Failed to spawn child process: %v %q", err, string(output)) + } + + childPpid := string(output) + ourPid := fmt.Sprintf("%d", Getpid()) + if childPpid != ourPid { + t.Fatalf("Child process reports parent process id '%v', expected '%v'", childPpid, ourPid) + } +} + func TestKillFindProcess(t *testing.T) { testKillProcess(t, func(p *Process) { p2, err := FindProcess(p.Pid) @@ -1336,3 +1432,52 @@ func TestNilFileMethods(t *testing.T) { } } } + +func mkdirTree(t *testing.T, root string, level, max int) { + if level >= max { + return + } + level++ + for i := 'a'; i < 'c'; i++ { + dir := filepath.Join(root, string(i)) + if err := Mkdir(dir, 0700); err != nil { + t.Fatal(err) + } + mkdirTree(t, dir, level, max) + } +} + +// Test that simultaneous RemoveAll do not report an error. +// As long as it gets removed, we should be happy. +func TestRemoveAllRace(t *testing.T) { + if runtime.GOOS == "windows" { + // Windows has very strict rules about things like + // removing directories while someone else has + // them open. The racing doesn't work out nicely + // like it does on Unix. + t.Skip("skipping on windows") + } + + n := runtime.GOMAXPROCS(16) + defer runtime.GOMAXPROCS(n) + root, err := ioutil.TempDir("", "issue") + if err != nil { + t.Fatal(err) + } + mkdirTree(t, root, 1, 6) + hold := make(chan struct{}) + var wg sync.WaitGroup + for i := 0; i < 4; i++ { + wg.Add(1) + go func() { + defer wg.Done() + <-hold + err := RemoveAll(root) + if err != nil { + t.Errorf("unexpected error: %T, %q", err, err) + } + }() + } + close(hold) // let workers race to remove root + wg.Wait() +} diff --git a/libgo/go/os/path.go b/libgo/go/os/path.go index 02a77ec8051..84a3be3348f 100644 --- a/libgo/go/os/path.go +++ b/libgo/go/os/path.go @@ -17,7 +17,7 @@ import ( // If path is already a directory, MkdirAll does nothing // and returns nil. func MkdirAll(path string, perm FileMode) error { - // If path exists, stop with success or error. + // Fast path: if we can tell whether path is a directory or file, stop with success or error. dir, err := Stat(path) if err == nil { if dir.IsDir() { @@ -26,7 +26,7 @@ func MkdirAll(path string, perm FileMode) error { return &PathError{"mkdir", path, syscall.ENOTDIR} } - // Doesn't already exist; make sure parent does. + // Slow path: make sure parent exists and then call Mkdir for path. i := len(path) for i > 0 && IsPathSeparator(path[i-1]) { // Skip trailing path separator. i-- @@ -45,7 +45,7 @@ func MkdirAll(path string, perm FileMode) error { } } - // Now parent exists, try to create. + // Parent now exists; invoke Mkdir and use its result. err = Mkdir(path, perm) if err != nil { // Handle arguments like "foo/." by @@ -66,7 +66,7 @@ func MkdirAll(path string, perm FileMode) error { func RemoveAll(path string) error { // Simple case: if Remove works, we're done. err := Remove(path) - if err == nil { + if err == nil || IsNotExist(err) { return nil } @@ -86,6 +86,11 @@ func RemoveAll(path string) error { // Directory. fd, err := Open(path) if err != nil { + if IsNotExist(err) { + // Race. It was deleted between the Lstat and Open. + // Return nil per RemoveAll's docs. + return nil + } return err } @@ -116,6 +121,9 @@ func RemoveAll(path string) error { // Remove directory. err1 := Remove(path) + if err1 == nil || IsNotExist(err1) { + return nil + } if err == nil { err = err1 } diff --git a/libgo/go/os/path_test.go b/libgo/go/os/path_test.go index 3af21cde9af..6f24a43132a 100644 --- a/libgo/go/os/path_test.go +++ b/libgo/go/os/path_test.go @@ -168,8 +168,12 @@ func TestRemoveAll(t *testing.T) { func TestMkdirAllWithSymlink(t *testing.T) { switch runtime.GOOS { - case "nacl", "plan9", "windows": + case "nacl", "plan9": t.Skipf("skipping on %s", runtime.GOOS) + case "windows": + if !supportsSymlinks { + t.Skipf("skipping on %s", runtime.GOOS) + } } tmpDir, err := ioutil.TempDir("", "TestMkdirAllWithSymlink-") @@ -198,8 +202,9 @@ func TestMkdirAllWithSymlink(t *testing.T) { } func TestMkdirAllAtSlash(t *testing.T) { - if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { - return + switch runtime.GOOS { + case "android", "plan9", "windows": + t.Skipf("skipping on %s", runtime.GOOS) } RemoveAll("/_go_os_test") err := MkdirAll("/_go_os_test/dir", 0777) diff --git a/libgo/go/os/proc.go b/libgo/go/os/proc.go index 38c436ec54d..774f09900ec 100644 --- a/libgo/go/os/proc.go +++ b/libgo/go/os/proc.go @@ -6,11 +6,24 @@ package os -import "syscall" +import ( + "runtime" + "syscall" +) // Args hold the command-line arguments, starting with the program name. var Args []string +func init() { + if runtime.GOOS == "windows" { + // Initialized in exec_windows.go. + return + } + Args = runtime_args() +} + +func runtime_args() []string // in package runtime + // Getuid returns the numeric user id of the caller. func Getuid() int { return syscall.Getuid() } diff --git a/libgo/go/os/signal/signal_test.go b/libgo/go/os/signal/signal_test.go index 076fe3f93bd..22337a72d4b 100644 --- a/libgo/go/os/signal/signal_test.go +++ b/libgo/go/os/signal/signal_test.go @@ -125,7 +125,7 @@ func TestStop(t *testing.T) { if sig != syscall.SIGHUP || *sendUncaughtSighup == 1 { syscall.Kill(syscall.Getpid(), sig) } - time.Sleep(10 * time.Millisecond) + time.Sleep(100 * time.Millisecond) // Ask for signal c := make(chan os.Signal, 1) @@ -140,7 +140,7 @@ func TestStop(t *testing.T) { select { case s := <-c: t.Fatalf("unexpected signal %v", s) - case <-time.After(10 * time.Millisecond): + case <-time.After(100 * time.Millisecond): // nothing to read - good } @@ -154,7 +154,7 @@ func TestStop(t *testing.T) { select { case s := <-c: t.Fatalf("unexpected signal %v", s) - case <-time.After(10 * time.Millisecond): + case <-time.After(100 * time.Millisecond): // nothing to read - good } } diff --git a/libgo/go/os/types_windows.go b/libgo/go/os/types_windows.go index 38901681e67..7b2e54698c3 100644 --- a/libgo/go/os/types_windows.go +++ b/libgo/go/os/types_windows.go @@ -39,6 +39,9 @@ func (fs *fileStat) Mode() (m FileMode) { } else { m |= 0666 } + if fs.sys.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 { + m |= ModeSymlink + } return m } diff --git a/libgo/go/os/user/lookup_stubs.go b/libgo/go/os/user/lookup_stubs.go index 86f0e6e645a..4fb0e3c6edd 100644 --- a/libgo/go/os/user/lookup_stubs.go +++ b/libgo/go/os/user/lookup_stubs.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 !cgo,!windows,!plan9 +// +build !cgo,!windows,!plan9 android package user diff --git a/libgo/go/os/user/lookup_unix.go b/libgo/go/os/user/lookup_unix.go index 0db08067201..64f4576f69e 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 linux netbsd openbsd solaris +// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris // +build cgo package user |