summaryrefslogtreecommitdiff
path: root/libgo/go/os
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/os')
-rw-r--r--libgo/go/os/dir.go72
-rw-r--r--libgo/go/os/dir_largefile.go12
-rw-r--r--libgo/go/os/dir_regfile.go12
-rw-r--r--libgo/go/os/env.go73
-rw-r--r--libgo/go/os/env_test.go59
-rw-r--r--libgo/go/os/env_unix.go96
-rw-r--r--libgo/go/os/env_windows.go127
-rw-r--r--libgo/go/os/error.go113
-rw-r--r--libgo/go/os/exec.go154
-rw-r--r--libgo/go/os/file.go438
-rw-r--r--libgo/go/os/file_unix.go110
-rw-r--r--libgo/go/os/getwd.go92
-rw-r--r--libgo/go/os/inotify/inotify_linux.go291
-rw-r--r--libgo/go/os/inotify/inotify_linux_test.go99
-rw-r--r--libgo/go/os/os_test.go882
-rw-r--r--libgo/go/os/path.go116
-rw-r--r--libgo/go/os/path_test.go181
-rw-r--r--libgo/go/os/proc.go35
-rw-r--r--libgo/go/os/signal/mkunix.sh24
-rw-r--r--libgo/go/os/signal/signal.go48
-rw-r--r--libgo/go/os/signal/signal_test.go19
-rw-r--r--libgo/go/os/stat.go40
-rw-r--r--libgo/go/os/sys_bsd.go19
-rw-r--r--libgo/go/os/sys_linux.go28
-rw-r--r--libgo/go/os/sys_uname.go25
-rw-r--r--libgo/go/os/time.go20
-rw-r--r--libgo/go/os/types.go56
27 files changed, 3241 insertions, 0 deletions
diff --git a/libgo/go/os/dir.go b/libgo/go/os/dir.go
new file mode 100644
index 000000000..b3b5d3e37
--- /dev/null
+++ b/libgo/go/os/dir.go
@@ -0,0 +1,72 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+func libc_dup(fd int) int __asm__ ("dup")
+func libc_opendir(*byte) *syscall.DIR __asm__ ("opendir")
+func libc_closedir(*syscall.DIR) int __asm__ ("closedir")
+
+// FIXME: pathconf returns long, not int.
+func libc_pathconf(*byte, int) int __asm__ ("pathconf")
+
+func clen(n []byte) int {
+ for i := 0; i < len(n); i++ {
+ if n[i] == 0 {
+ return i
+ }
+ }
+ return len(n)
+}
+
+var elen int;
+
+// Negative count means read until EOF.
+func (file *File) Readdirnames(count int) (names []string, err Error) {
+ if elen == 0 {
+ var dummy syscall.Dirent;
+ elen = (unsafe.Offsetof(dummy.Name) +
+ libc_pathconf(syscall.StringBytePtr(file.name), syscall.PC_NAME_MAX) +
+ 1);
+ }
+
+ if file.dirinfo == nil {
+ file.dirinfo = new(dirInfo)
+ file.dirinfo.buf = make([]byte, elen)
+ file.dirinfo.dir = libc_opendir(syscall.StringBytePtr(file.name))
+ }
+
+ entry_dirent := unsafe.Pointer(&file.dirinfo.buf[0]).(*syscall.Dirent)
+
+ size := count
+ if size < 0 {
+ size = 100
+ }
+ names = make([]string, 0, size) // Empty with room to grow.
+
+ dir := file.dirinfo.dir
+ if dir == nil {
+ return names, NewSyscallError("opendir", syscall.GetErrno())
+ }
+
+ for count != 0 {
+ var result *syscall.Dirent
+ i := libc_readdir_r(dir, entry_dirent, &result)
+ if result == nil {
+ break
+ }
+ var name = string(result.Name[0:clen(result.Name[0:])])
+ if name == "." || name == ".." { // Useless names
+ continue
+ }
+ count--
+ names = append(names, name)
+ }
+ return names, nil
+}
diff --git a/libgo/go/os/dir_largefile.go b/libgo/go/os/dir_largefile.go
new file mode 100644
index 000000000..c723ec924
--- /dev/null
+++ b/libgo/go/os/dir_largefile.go
@@ -0,0 +1,12 @@
+// dir_largefile.go -- For systems which use the large file interface for
+// readdir_r.
+
+// Copyright 2011 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"
+
+func libc_readdir_r(*syscall.DIR, *syscall.Dirent, **syscall.Dirent) int __asm__ ("readdir64_r")
diff --git a/libgo/go/os/dir_regfile.go b/libgo/go/os/dir_regfile.go
new file mode 100644
index 000000000..22fb5febb
--- /dev/null
+++ b/libgo/go/os/dir_regfile.go
@@ -0,0 +1,12 @@
+// dir_regfile.go -- For systems which do not use the large file interface
+// for readdir_r.
+
+// Copyright 2011 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"
+
+func libc_readdir_r(*syscall.DIR, *syscall.Dirent, **syscall.Dirent) int __asm__ ("readdir_r")
diff --git a/libgo/go/os/env.go b/libgo/go/os/env.go
new file mode 100644
index 000000000..3a6d79dd0
--- /dev/null
+++ b/libgo/go/os/env.go
@@ -0,0 +1,73 @@
+// Copyright 2010 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.
+
+// General environment variables.
+
+package os
+
+// Expand replaces ${var} or $var in the string based on the mapping function.
+// Invocations of undefined variables are replaced with the empty string.
+func Expand(s string, mapping func(string) string) string {
+ buf := make([]byte, 0, 2*len(s))
+ // ${} is all ASCII, so bytes are fine for this operation.
+ i := 0
+ for j := 0; j < len(s); j++ {
+ if s[j] == '$' && j+1 < len(s) {
+ buf = append(buf, []byte(s[i:j])...)
+ name, w := getShellName(s[j+1:])
+ buf = append(buf, []byte(mapping(name))...)
+ j += w
+ i = j + 1
+ }
+ }
+ return string(buf) + s[i:]
+}
+
+// ShellExpand replaces ${var} or $var in the string according to the values
+// of the operating system's environment variables. References to undefined
+// variables are replaced by the empty string.
+func ShellExpand(s string) string {
+ return Expand(s, Getenv)
+}
+
+// isSpellSpecialVar reports whether the character identifies a special
+// shell variable such as $*.
+func isShellSpecialVar(c uint8) bool {
+ switch c {
+ case '*', '#', '$', '@', '!', '?', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ return true
+ }
+ return false
+}
+
+// isAlphaNum reports whether the byte is an ASCII letter, number, or underscore
+func isAlphaNum(c uint8) bool {
+ return c == '_' || '0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z'
+}
+
+// getName returns the name that begins the string and the number of bytes
+// consumed to extract it. If the name is enclosed in {}, it's part of a ${}
+// expansion and two more bytes are needed than the length of the name.
+func getShellName(s string) (string, int) {
+ switch {
+ case s[0] == '{':
+ if len(s) > 2 && isShellSpecialVar(s[1]) && s[2] == '}' {
+ return s[1:2], 3
+ }
+ // Scan to closing brace
+ for i := 1; i < len(s); i++ {
+ if s[i] == '}' {
+ return s[1:i], i + 1
+ }
+ }
+ return "", 1 // Bad syntax; just eat the brace.
+ case isShellSpecialVar(s[0]):
+ return s[0:1], 1
+ }
+ // Scan alphanumerics.
+ var i int
+ for i = 0; i < len(s) && isAlphaNum(s[i]); i++ {
+ }
+ return s[:i], i
+}
diff --git a/libgo/go/os/env_test.go b/libgo/go/os/env_test.go
new file mode 100644
index 000000000..04ff39072
--- /dev/null
+++ b/libgo/go/os/env_test.go
@@ -0,0 +1,59 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os_test
+
+import (
+ . "os"
+ "testing"
+)
+
+// testGetenv gives us a controlled set of variables for testing Expand.
+func testGetenv(s string) string {
+ switch s {
+ case "*":
+ return "all the args"
+ case "#":
+ return "NARGS"
+ case "$":
+ return "PID"
+ case "1":
+ return "ARGUMENT1"
+ case "HOME":
+ return "/usr/gopher"
+ case "H":
+ return "(Value of H)"
+ case "home_1":
+ return "/usr/foo"
+ case "_":
+ return "underscore"
+ }
+ return ""
+}
+
+var expandTests = []struct {
+ in, out string
+}{
+ {"", ""},
+ {"$*", "all the args"},
+ {"$$", "PID"},
+ {"${*}", "all the args"},
+ {"$1", "ARGUMENT1"},
+ {"${1}", "ARGUMENT1"},
+ {"now is the time", "now is the time"},
+ {"$HOME", "/usr/gopher"},
+ {"$home_1", "/usr/foo"},
+ {"${HOME}", "/usr/gopher"},
+ {"${H}OME", "(Value of H)OME"},
+ {"A$$$#$1$H$home_1*B", "APIDNARGSARGUMENT1(Value of H)/usr/foo*B"},
+}
+
+func TestExpand(t *testing.T) {
+ for _, test := range expandTests {
+ result := Expand(test.in, testGetenv)
+ if result != test.out {
+ t.Errorf("Expand(%q)=%q; expected %q", test.in, result, test.out)
+ }
+ }
+}
diff --git a/libgo/go/os/env_unix.go b/libgo/go/os/env_unix.go
new file mode 100644
index 000000000..e7e1c3b90
--- /dev/null
+++ b/libgo/go/os/env_unix.go
@@ -0,0 +1,96 @@
+// Copyright 2010 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.
+
+// Unix environment variables.
+
+package os
+
+import (
+ "sync"
+)
+
+// ENOENV is the Error indicating that an environment variable does not exist.
+var ENOENV = NewError("no such environment variable")
+
+var env map[string]string
+var once sync.Once
+
+
+func copyenv() {
+ env = make(map[string]string)
+ for _, s := range Envs {
+ for j := 0; j < len(s); j++ {
+ if s[j] == '=' {
+ env[s[0:j]] = s[j+1:]
+ break
+ }
+ }
+ }
+}
+
+// Getenverror retrieves the value of the environment variable named by the key.
+// It returns the value and an error, if any.
+func Getenverror(key string) (value string, err Error) {
+ once.Do(copyenv)
+
+ if len(key) == 0 {
+ return "", EINVAL
+ }
+ v, ok := env[key]
+ if !ok {
+ return "", ENOENV
+ }
+ return v, nil
+}
+
+// Getenv retrieves the value of the environment variable named by the key.
+// It returns the value, which will be empty if the variable is not present.
+func Getenv(key string) string {
+ v, _ := Getenverror(key)
+ return v
+}
+
+// Setenv sets the value of the environment variable named by the key.
+// It returns an Error, if any.
+func Setenv(key, value string) Error {
+ once.Do(copyenv)
+
+ if len(key) == 0 {
+ return EINVAL
+ }
+ env[key] = value
+ return nil
+}
+
+// Clearenv deletes all environment variables.
+func Clearenv() {
+ once.Do(copyenv) // prevent copyenv in Getenv/Setenv
+ env = make(map[string]string)
+}
+
+// Environ returns an array of strings representing the environment,
+// in the form "key=value".
+func Environ() []string {
+ once.Do(copyenv)
+ a := make([]string, len(env))
+ i := 0
+ for k, v := range env {
+ // check i < len(a) for safety,
+ // in case env is changing underfoot.
+ if i < len(a) {
+ a[i] = k + "=" + v
+ i++
+ }
+ }
+ return a[0:i]
+}
+
+// TempDir returns the default directory to use for temporary files.
+func TempDir() string {
+ dir := Getenv("TMPDIR")
+ if dir == "" {
+ dir = "/tmp"
+ }
+ return dir
+}
diff --git a/libgo/go/os/env_windows.go b/libgo/go/os/env_windows.go
new file mode 100644
index 000000000..d2b159dfb
--- /dev/null
+++ b/libgo/go/os/env_windows.go
@@ -0,0 +1,127 @@
+// Copyright 2010 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.
+
+// Windows environment variables.
+
+package os
+
+import (
+ "syscall"
+ "utf16"
+ "unsafe"
+)
+
+// ENOENV is the Error indicating that an environment variable does not exist.
+var ENOENV = NewError("no such environment variable")
+
+// Getenverror retrieves the value of the environment variable named by the key.
+// It returns the value and an error, if any.
+func Getenverror(key string) (value string, err Error) {
+ b := make([]uint16, 100)
+ n, e := syscall.GetEnvironmentVariable(syscall.StringToUTF16Ptr(key), &b[0], uint32(len(b)))
+ if n == 0 && e == syscall.ERROR_ENVVAR_NOT_FOUND {
+ return "", ENOENV
+ }
+ if n > uint32(len(b)) {
+ b = make([]uint16, n)
+ n, e = syscall.GetEnvironmentVariable(syscall.StringToUTF16Ptr(key), &b[0], uint32(len(b)))
+ if n > uint32(len(b)) {
+ n = 0
+ }
+ }
+ if n == 0 {
+ return "", NewSyscallError("GetEnvironmentVariable", e)
+ }
+ return string(utf16.Decode(b[0:n])), nil
+}
+
+// Getenv retrieves the value of the environment variable named by the key.
+// It returns the value, which will be empty if the variable is not present.
+func Getenv(key string) string {
+ v, _ := Getenverror(key)
+ return v
+}
+
+// Setenv sets the value of the environment variable named by the key.
+// It returns an Error, if any.
+func Setenv(key, value string) Error {
+ var v *uint16
+ if len(value) > 0 {
+ v = syscall.StringToUTF16Ptr(value)
+ }
+ ok, e := syscall.SetEnvironmentVariable(syscall.StringToUTF16Ptr(key), v)
+ if !ok {
+ return NewSyscallError("SetEnvironmentVariable", e)
+ }
+ return nil
+}
+
+// Clearenv deletes all environment variables.
+func Clearenv() {
+ for _, s := range Environ() {
+ // Environment variables can begin with =
+ // so start looking for the separator = at j=1.
+ // http://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx
+ for j := 1; j < len(s); j++ {
+ if s[j] == '=' {
+ Setenv(s[0:j], "")
+ break
+ }
+ }
+ }
+}
+
+// Environ returns an array of strings representing the environment,
+// in the form "key=value".
+func Environ() []string {
+ s, e := syscall.GetEnvironmentStrings()
+ if e != 0 {
+ return nil
+ }
+ defer syscall.FreeEnvironmentStrings(s)
+ r := make([]string, 0, 50) // Empty with room to grow.
+ for from, i, p := 0, 0, (*[1 << 24]uint16)(unsafe.Pointer(s)); true; i++ {
+ if p[i] == 0 {
+ // empty string marks the end
+ if i <= from {
+ break
+ }
+ r = append(r, string(utf16.Decode(p[from:i])))
+ from = i + 1
+ }
+ }
+ return r
+}
+
+// TempDir returns the default directory to use for temporary files.
+func TempDir() string {
+ const pathSep = '\\'
+ dirw := make([]uint16, syscall.MAX_PATH)
+ n, _ := syscall.GetTempPath(uint32(len(dirw)), &dirw[0])
+ if n > uint32(len(dirw)) {
+ dirw = make([]uint16, n)
+ n, _ = syscall.GetTempPath(uint32(len(dirw)), &dirw[0])
+ if n > uint32(len(dirw)) {
+ n = 0
+ }
+ }
+ if n > 0 && dirw[n-1] == pathSep {
+ n--
+ }
+ return string(utf16.Decode(dirw[0:n]))
+}
+
+func init() {
+ var argc int32
+ cmd := syscall.GetCommandLine()
+ argv, e := syscall.CommandLineToArgv(cmd, &argc)
+ if e != 0 {
+ return
+ }
+ defer syscall.LocalFree(uint32(uintptr(unsafe.Pointer(argv))))
+ Args = make([]string, argc)
+ for i, v := range (*argv)[:argc] {
+ Args[i] = string(syscall.UTF16ToString((*v)[:]))
+ }
+}
diff --git a/libgo/go/os/error.go b/libgo/go/os/error.go
new file mode 100644
index 000000000..8cdf53254
--- /dev/null
+++ b/libgo/go/os/error.go
@@ -0,0 +1,113 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+import syscall "syscall"
+
+// An Error can represent any printable error condition.
+type Error interface {
+ String() string
+}
+
+// A helper type that can be embedded or wrapped to simplify satisfying
+// Error.
+type ErrorString string
+
+func (e ErrorString) String() string { return string(e) }
+func (e ErrorString) Temporary() bool { return false }
+func (e ErrorString) Timeout() bool { return false }
+
+// Note: If the name of the function NewError changes,
+// pkg/go/doc/doc.go should be adjusted since it hardwires
+// this name in a heuristic.
+
+// NewError converts s to an ErrorString, which satisfies the Error interface.
+func NewError(s string) Error { return ErrorString(s) }
+
+// Errno is the Unix error number. Names such as EINVAL are simple
+// wrappers to convert the error number into an Error.
+type Errno int64
+
+func (e Errno) String() string { return syscall.Errstr(int(e)) }
+
+func (e Errno) Temporary() bool {
+ return e == Errno(syscall.EINTR) || e.Timeout()
+}
+
+func (e Errno) Timeout() bool {
+ return e == Errno(syscall.EAGAIN) || e == Errno(syscall.EWOULDBLOCK)
+}
+
+// Commonly known Unix errors.
+var (
+ EPERM Error = Errno(syscall.EPERM)
+ ENOENT Error = Errno(syscall.ENOENT)
+ ESRCH Error = Errno(syscall.ESRCH)
+ EINTR Error = Errno(syscall.EINTR)
+ EIO Error = Errno(syscall.EIO)
+ ENXIO Error = Errno(syscall.ENXIO)
+ E2BIG Error = Errno(syscall.E2BIG)
+ ENOEXEC Error = Errno(syscall.ENOEXEC)
+ EBADF Error = Errno(syscall.EBADF)
+ ECHILD Error = Errno(syscall.ECHILD)
+ EDEADLK Error = Errno(syscall.EDEADLK)
+ ENOMEM Error = Errno(syscall.ENOMEM)
+ EACCES Error = Errno(syscall.EACCES)
+ EFAULT Error = Errno(syscall.EFAULT)
+ EBUSY Error = Errno(syscall.EBUSY)
+ EEXIST Error = Errno(syscall.EEXIST)
+ EXDEV Error = Errno(syscall.EXDEV)
+ ENODEV Error = Errno(syscall.ENODEV)
+ ENOTDIR Error = Errno(syscall.ENOTDIR)
+ EISDIR Error = Errno(syscall.EISDIR)
+ EINVAL Error = Errno(syscall.EINVAL)
+ ENFILE Error = Errno(syscall.ENFILE)
+ EMFILE Error = Errno(syscall.EMFILE)
+ ENOTTY Error = Errno(syscall.ENOTTY)
+ EFBIG Error = Errno(syscall.EFBIG)
+ ENOSPC Error = Errno(syscall.ENOSPC)
+ ESPIPE Error = Errno(syscall.ESPIPE)
+ EROFS Error = Errno(syscall.EROFS)
+ EMLINK Error = Errno(syscall.EMLINK)
+ EPIPE Error = Errno(syscall.EPIPE)
+ EAGAIN Error = Errno(syscall.EAGAIN)
+ EDOM Error = Errno(syscall.EDOM)
+ ERANGE Error = Errno(syscall.ERANGE)
+ EADDRINUSE Error = Errno(syscall.EADDRINUSE)
+ ECONNREFUSED Error = Errno(syscall.ECONNREFUSED)
+ ENAMETOOLONG Error = Errno(syscall.ENAMETOOLONG)
+ EAFNOSUPPORT Error = Errno(syscall.EAFNOSUPPORT)
+)
+
+// PathError records an error and the operation and file path that caused it.
+type PathError struct {
+ Op string
+ Path string
+ Error Error
+}
+
+func (e *PathError) String() string { return e.Op + " " + e.Path + ": " + e.Error.String() }
+
+// SyscallError records an error from a specific system call.
+type SyscallError struct {
+ Syscall string
+ Errno Errno
+}
+
+func (e *SyscallError) String() string { return e.Syscall + ": " + e.Errno.String() }
+
+// Note: If the name of the function NewSyscallError changes,
+// pkg/go/doc/doc.go should be adjusted since it hardwires
+// this name in a heuristic.
+
+// NewSyscallError returns, as an Error, a new SyscallError
+// with the given system call name and error number.
+// As a convenience, if errno is 0, NewSyscallError returns nil.
+func NewSyscallError(syscall string, errno int) Error {
+ if errno == 0 {
+ return nil
+ }
+ return &SyscallError{syscall, Errno(errno)}
+}
diff --git a/libgo/go/os/exec.go b/libgo/go/os/exec.go
new file mode 100644
index 000000000..100d984d1
--- /dev/null
+++ b/libgo/go/os/exec.go
@@ -0,0 +1,154 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+import (
+ "syscall"
+)
+
+// ForkExec forks the current process and invokes Exec with the program, arguments,
+// and environment specified by name, argv, and envv. It returns the process
+// id of the forked process and an Error, if any. The fd array specifies the
+// file descriptors to be set up in the new process: fd[0] will be Unix file
+// descriptor 0 (standard input), fd[1] descriptor 1, and so on. A nil entry
+// will cause the child to have no open file descriptor with that index.
+// If dir is not empty, the child chdirs into the directory before execing the program.
+func ForkExec(name string, argv []string, envv []string, dir string, fd []*File) (pid int, err Error) {
+ if envv == nil {
+ envv = Environ()
+ }
+ // Create array of integer (system) fds.
+ intfd := make([]int, len(fd))
+ for i, f := range fd {
+ if f == nil {
+ intfd[i] = -1
+ } else {
+ intfd[i] = f.Fd()
+ }
+ }
+
+ p, e := syscall.ForkExec(name, argv, envv, dir, intfd)
+ if e != 0 {
+ return 0, &PathError{"fork/exec", name, Errno(e)}
+ }
+ return p, nil
+}
+
+// Exec replaces the current process with an execution of the
+// named binary, with arguments argv and environment envv.
+// If successful, Exec never returns. If it fails, it returns an Error.
+// ForkExec is almost always a better way to execute a program.
+func Exec(name string, argv []string, envv []string) Error {
+ if envv == nil {
+ envv = Environ()
+ }
+ e := syscall.Exec(name, argv, envv)
+ if e != 0 {
+ return &PathError{"exec", name, Errno(e)}
+ }
+ return nil
+}
+
+// TODO(rsc): Should os implement its own syscall.WaitStatus
+// wrapper with the methods, or is exposing the underlying one enough?
+//
+// TODO(rsc): Certainly need to have Rusage struct,
+// since syscall one might have different field types across
+// different OS.
+
+// Waitmsg stores the information about an exited process as reported by Wait.
+type Waitmsg struct {
+ Pid int // The process's id.
+ syscall.WaitStatus // System-dependent status info.
+ Rusage *syscall.Rusage // System-dependent resource usage info.
+}
+
+// Options for Wait.
+const (
+ WNOHANG = syscall.WNOHANG // Don't wait if no process has exited.
+ WSTOPPED = syscall.WSTOPPED // If set, status of stopped subprocesses is also reported.
+ WUNTRACED = syscall.WUNTRACED // Usually an alias for WSTOPPED.
+ WRUSAGE = 1 << 20 // Record resource usage.
+)
+
+// WRUSAGE must not be too high a bit, to avoid clashing with Linux's
+// WCLONE, WALL, and WNOTHREAD flags, which sit in the top few bits of
+// the options
+
+// Wait waits for process pid to exit or stop, and then returns a
+// Waitmsg describing its status and an Error, if any. The options
+// (WNOHANG etc.) affect the behavior of the Wait call.
+func Wait(pid int, options int) (w *Waitmsg, err Error) {
+ var status syscall.WaitStatus
+ var rusage *syscall.Rusage
+ if options&WRUSAGE != 0 {
+ rusage = new(syscall.Rusage)
+ options ^= WRUSAGE
+ }
+ pid1, e := syscall.Wait4(pid, &status, options, rusage)
+ if e != 0 {
+ return nil, NewSyscallError("wait", e)
+ }
+ w = new(Waitmsg)
+ w.Pid = pid1
+ w.WaitStatus = status
+ w.Rusage = rusage
+ return w, nil
+}
+
+// Convert i to decimal string.
+func itod(i int) string {
+ if i == 0 {
+ return "0"
+ }
+
+ u := uint64(i)
+ if i < 0 {
+ u = -u
+ }
+
+ // Assemble decimal in reverse order.
+ var b [32]byte
+ bp := len(b)
+ for ; u > 0; u /= 10 {
+ bp--
+ b[bp] = byte(u%10) + '0'
+ }
+
+ if i < 0 {
+ bp--
+ b[bp] = '-'
+ }
+
+ return string(b[bp:])
+}
+
+func (w Waitmsg) String() string {
+ // TODO(austin) Use signal names when possible?
+ res := ""
+ switch {
+ case w.Exited():
+ res = "exit status " + itod(w.ExitStatus())
+ case w.Signaled():
+ res = "signal " + itod(w.Signal())
+ case w.Stopped():
+ res = "stop signal " + itod(w.StopSignal())
+ if w.StopSignal() == syscall.SIGTRAP && w.TrapCause() != 0 {
+ res += " (trap " + itod(w.TrapCause()) + ")"
+ }
+ case w.Continued():
+ res = "continued"
+ }
+ if w.CoreDump() {
+ res += " (core dumped)"
+ }
+ return res
+}
+
+// Getpid returns the process id of the caller.
+func Getpid() int { return syscall.Getpid() }
+
+// Getppid returns the process id of the caller's parent.
+func Getppid() int { return syscall.Getppid() }
diff --git a/libgo/go/os/file.go b/libgo/go/os/file.go
new file mode 100644
index 000000000..3f73f1dff
--- /dev/null
+++ b/libgo/go/os/file.go
@@ -0,0 +1,438 @@
+// 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.
+
+// The os package provides a platform-independent interface to operating
+// system functionality. The design is Unix-like.
+package os
+
+import (
+ "runtime"
+ "syscall"
+)
+
+// File represents an open file descriptor.
+type File struct {
+ fd int
+ name string
+ dirinfo *dirInfo // nil unless directory being read
+ nepipe int // number of consecutive EPIPE in Write
+}
+
+// Fd returns the integer Unix file descriptor referencing the open file.
+func (file *File) Fd() int { return file.fd }
+
+// Name returns the name of the file as presented to Open.
+func (file *File) Name() string { return file.name }
+
+// NewFile returns a new File with the given file descriptor and name.
+func NewFile(fd int, name string) *File {
+ if fd < 0 {
+ return nil
+ }
+ f := &File{fd, name, nil, 0}
+ runtime.SetFinalizer(f, (*File).Close)
+ return f
+}
+
+// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
+// standard output, and standard error file descriptors.
+var (
+ Stdin = NewFile(syscall.Stdin, "/dev/stdin")
+ Stdout = NewFile(syscall.Stdout, "/dev/stdout")
+ Stderr = NewFile(syscall.Stderr, "/dev/stderr")
+)
+
+// Flags to Open wrapping those of the underlying system. Not all flags
+// may be implemented on a given system.
+const (
+ O_RDONLY int = syscall.O_RDONLY // open the file read-only.
+ O_WRONLY int = syscall.O_WRONLY // open the file write-only.
+ O_RDWR int = syscall.O_RDWR // open the file read-write.
+ O_APPEND int = syscall.O_APPEND // append data to the file when writing.
+ O_ASYNC int = syscall.O_ASYNC // generate a signal when I/O is available.
+ O_CREAT int = syscall.O_CREAT // create a new file if none exists.
+ O_EXCL int = syscall.O_EXCL // used with O_CREAT, file must not exist
+ O_NOCTTY int = syscall.O_NOCTTY // do not make file the controlling tty.
+ O_NONBLOCK int = syscall.O_NONBLOCK // open in non-blocking mode.
+ O_NDELAY int = O_NONBLOCK // synonym for O_NONBLOCK
+ O_SYNC int = syscall.O_SYNC // open for synchronous I/O.
+ O_TRUNC int = syscall.O_TRUNC // if possible, truncate file when opened.
+ O_CREATE int = O_CREAT // create a new file if none exists.
+)
+
+type eofError int
+
+func (eofError) String() string { return "EOF" }
+
+// EOF is the Error returned by Read when no more input is available.
+// Functions should return EOF only to signal a graceful end of input.
+// If the EOF occurs unexpectedly in a structured data stream,
+// the appropriate error is either io.ErrUnexpectedEOF or some other error
+// giving more detail.
+var EOF Error = eofError(0)
+
+// Read reads up to len(b) bytes from the File.
+// It returns the number of bytes read and an Error, if any.
+// EOF is signaled by a zero count with err set to EOF.
+func (file *File) Read(b []byte) (n int, err Error) {
+ if file == nil {
+ return 0, EINVAL
+ }
+ n, e := syscall.Read(file.fd, b)
+ if n < 0 {
+ n = 0
+ }
+ if n == 0 && e == 0 {
+ return 0, EOF
+ }
+ if e != 0 {
+ err = &PathError{"read", file.name, Errno(e)}
+ }
+ return n, err
+}
+
+// ReadAt 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 EOF.
+// ReadAt always returns a non-nil Error when n != len(b).
+func (file *File) ReadAt(b []byte, off int64) (n int, err Error) {
+ if file == nil {
+ return 0, EINVAL
+ }
+ for len(b) > 0 {
+ m, e := syscall.Pread(file.fd, b, off)
+ if m == 0 && e == 0 {
+ return n, EOF
+ }
+ if e != 0 {
+ err = &PathError{"read", file.name, Errno(e)}
+ break
+ }
+ n += m
+ b = b[m:]
+ off += int64(m)
+ }
+ return
+}
+
+// Write writes len(b) bytes to the File.
+// It returns the number of bytes written and an Error, if any.
+// Write returns a non-nil Error when n != len(b).
+func (file *File) Write(b []byte) (n int, err Error) {
+ if file == nil {
+ return 0, EINVAL
+ }
+ n, e := syscall.Write(file.fd, b)
+ if n < 0 {
+ n = 0
+ }
+ if e == syscall.EPIPE {
+ file.nepipe++
+ if file.nepipe >= 10 {
+ Exit(syscall.EPIPE)
+ }
+ } else {
+ file.nepipe = 0
+ }
+ if e != 0 {
+ err = &PathError{"write", file.name, Errno(e)}
+ }
+ return n, err
+}
+
+// WriteAt writes len(b) bytes to the File starting at byte offset off.
+// It returns the number of bytes written and an Error, if any.
+// WriteAt returns a non-nil Error when n != len(b).
+func (file *File) WriteAt(b []byte, off int64) (n int, err Error) {
+ if file == nil {
+ return 0, EINVAL
+ }
+ for len(b) > 0 {
+ m, e := syscall.Pwrite(file.fd, b, off)
+ if e != 0 {
+ err = &PathError{"write", file.name, Errno(e)}
+ break
+ }
+ n += m
+ b = b[m:]
+ off += int64(m)
+ }
+ return
+}
+
+// Seek sets the offset for the next Read or Write on file to offset, interpreted
+// according to whence: 0 means relative to the origin of the file, 1 means
+// relative to the current offset, and 2 means relative to the end.
+// It returns the new offset and an Error, if any.
+func (file *File) Seek(offset int64, whence int) (ret int64, err Error) {
+ r, e := syscall.Seek(file.fd, offset, whence)
+ if e == 0 && file.dirinfo != nil && r != 0 {
+ e = syscall.EISDIR
+ }
+ if e != 0 {
+ return 0, &PathError{"seek", file.name, Errno(e)}
+ }
+ return r, nil
+}
+
+// WriteString is like Write, but writes the contents of string s rather than
+// an array of bytes.
+func (file *File) WriteString(s string) (ret int, err Error) {
+ if file == nil {
+ return 0, EINVAL
+ }
+ b := syscall.StringByteSlice(s)
+ b = b[0 : len(b)-1]
+ return file.Write(b)
+}
+
+// Pipe returns a connected pair of Files; reads from r return bytes written to w.
+// It returns the files and an Error, if any.
+func Pipe() (r *File, w *File, err Error) {
+ var p [2]int
+
+ // See ../syscall/exec.go for description of lock.
+ syscall.ForkLock.RLock()
+ e := syscall.Pipe(p[0:])
+ if e != 0 {
+ syscall.ForkLock.RUnlock()
+ return nil, nil, NewSyscallError("pipe", e)
+ }
+ syscall.CloseOnExec(p[0])
+ syscall.CloseOnExec(p[1])
+ syscall.ForkLock.RUnlock()
+
+ return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil
+}
+
+// Mkdir creates a new directory with the specified name and permission bits.
+// It returns an error, if any.
+func Mkdir(name string, perm uint32) Error {
+ e := syscall.Mkdir(name, perm)
+ if e != 0 {
+ return &PathError{"mkdir", name, Errno(e)}
+ }
+ return nil
+}
+
+// Stat returns a FileInfo structure describing the named file and an error, if any.
+// If name names a valid symbolic link, the returned FileInfo describes
+// the file pointed at by the link and has fi.FollowedSymlink set to true.
+// If name names an invalid symbolic link, the returned FileInfo describes
+// the link itself and has fi.FollowedSymlink set to false.
+func Stat(name string) (fi *FileInfo, err Error) {
+ var lstat, stat syscall.Stat_t
+ e := syscall.Lstat(name, &lstat)
+ if e != 0 {
+ return nil, &PathError{"stat", name, Errno(e)}
+ }
+ statp := &lstat
+ if lstat.Mode&syscall.S_IFMT == syscall.S_IFLNK {
+ e := syscall.Stat(name, &stat)
+ if e == 0 {
+ statp = &stat
+ }
+ }
+ return fileInfoFromStat(name, new(FileInfo), &lstat, statp), nil
+}
+
+// Lstat returns the FileInfo structure describing the named file and an
+// error, if any. If the file is a symbolic link, the returned FileInfo
+// describes the symbolic link. Lstat makes no attempt to follow the link.
+func Lstat(name string) (fi *FileInfo, err Error) {
+ var stat syscall.Stat_t
+ e := syscall.Lstat(name, &stat)
+ if e != 0 {
+ return nil, &PathError{"lstat", name, Errno(e)}
+ }
+ return fileInfoFromStat(name, new(FileInfo), &stat, &stat), nil
+}
+
+// Chdir changes the current working directory to the named directory.
+func Chdir(dir string) Error {
+ if e := syscall.Chdir(dir); e != 0 {
+ return &PathError{"chdir", dir, Errno(e)}
+ }
+ return nil
+}
+
+// Chdir changes the current working directory to the file,
+// which must be a directory.
+func (f *File) Chdir() Error {
+ if e := syscall.Fchdir(f.fd); e != 0 {
+ return &PathError{"chdir", f.name, Errno(e)}
+ }
+ return nil
+}
+
+// Remove removes the named file or directory.
+func Remove(name string) Error {
+ // System call interface forces us to know
+ // whether name is a file or directory.
+ // Try both: it is cheaper on average than
+ // doing a Stat plus the right one.
+ e := syscall.Unlink(name)
+ if e == 0 {
+ return nil
+ }
+ e1 := syscall.Rmdir(name)
+ if e1 == 0 {
+ return nil
+ }
+
+ // Both failed: figure out which error to return.
+ // OS X and Linux differ on whether unlink(dir)
+ // returns EISDIR, so can't use that. However,
+ // both agree that rmdir(file) returns ENOTDIR,
+ // so we can use that to decide which error is real.
+ // Rmdir might also return ENOTDIR if given a bad
+ // file path, like /etc/passwd/foo, but in that case,
+ // both errors will be ENOTDIR, so it's okay to
+ // use the error from unlink.
+ // For windows syscall.ENOTDIR is set
+ // to syscall.ERROR_DIRECTORY, hopefully it should
+ // do the trick.
+ if e1 != syscall.ENOTDIR {
+ e = e1
+ }
+ return &PathError{"remove", name, Errno(e)}
+}
+
+// LinkError records an error during a link or symlink or rename
+// system call and the paths that caused it.
+type LinkError struct {
+ Op string
+ Old string
+ New string
+ Error Error
+}
+
+func (e *LinkError) String() string {
+ return e.Op + " " + e.Old + " " + e.New + ": " + e.Error.String()
+}
+
+// Link creates a hard link.
+func Link(oldname, newname string) Error {
+ e := syscall.Link(oldname, newname)
+ if e != 0 {
+ return &LinkError{"link", oldname, newname, Errno(e)}
+ }
+ return nil
+}
+
+// Symlink creates a symbolic link.
+func Symlink(oldname, newname string) Error {
+ e := syscall.Symlink(oldname, newname)
+ if e != 0 {
+ return &LinkError{"symlink", oldname, newname, Errno(e)}
+ }
+ return nil
+}
+
+// Readlink reads the contents of a symbolic link: the destination of
+// the link. It returns the contents and an Error, if any.
+func Readlink(name string) (string, Error) {
+ for len := 128; ; len *= 2 {
+ b := make([]byte, len)
+ n, e := syscall.Readlink(name, b)
+ if e != 0 {
+ return "", &PathError{"readlink", name, Errno(e)}
+ }
+ if n < len {
+ return string(b[0:n]), nil
+ }
+ }
+ // Silence 6g.
+ return "", nil
+}
+
+// Rename renames a file.
+func Rename(oldname, newname string) Error {
+ e := syscall.Rename(oldname, newname)
+ if e != 0 {
+ return &LinkError{"rename", oldname, newname, Errno(e)}
+ }
+ return nil
+}
+
+// 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.
+func Chmod(name string, mode uint32) Error {
+ if e := syscall.Chmod(name, mode); e != 0 {
+ return &PathError{"chmod", name, Errno(e)}
+ }
+ return nil
+}
+
+// Chmod changes the mode of the file to mode.
+func (f *File) Chmod(mode uint32) Error {
+ if e := syscall.Fchmod(f.fd, mode); e != 0 {
+ return &PathError{"chmod", f.name, Errno(e)}
+ }
+ return nil
+}
+
+// Chown changes the numeric uid and gid of the named file.
+// If the file is a symbolic link, it changes the uid and gid of the link's target.
+func Chown(name string, uid, gid int) Error {
+ if e := syscall.Chown(name, uid, gid); e != 0 {
+ return &PathError{"chown", name, Errno(e)}
+ }
+ return nil
+}
+
+// Lchown changes the numeric uid and gid of the named file.
+// If the file is a symbolic link, it changes the uid and gid of the link itself.
+func Lchown(name string, uid, gid int) Error {
+ if e := syscall.Lchown(name, uid, gid); e != 0 {
+ return &PathError{"lchown", name, Errno(e)}
+ }
+ return nil
+}
+
+// Chown changes the numeric uid and gid of the named file.
+func (f *File) Chown(uid, gid int) Error {
+ if e := syscall.Fchown(f.fd, uid, gid); e != 0 {
+ return &PathError{"chown", f.name, Errno(e)}
+ }
+ return nil
+}
+
+// Truncate changes the size of the file.
+// It does not change the I/O offset.
+func (f *File) Truncate(size int64) Error {
+ if e := syscall.Ftruncate(f.fd, size); e != 0 {
+ return &PathError{"truncate", f.name, Errno(e)}
+ }
+ return nil
+}
+
+// Sync commits the current contents of the file to stable storage.
+// Typically, this means flushing the file system's in-memory copy
+// of recently written data to disk.
+func (file *File) Sync() (err Error) {
+ if file == nil {
+ return EINVAL
+ }
+ if e := syscall.Fsync(file.fd); e != 0 {
+ return NewSyscallError("fsync", e)
+ }
+ return nil
+}
+
+// Chtimes changes the access and modification times of the named
+// file, similar to the Unix utime() or utimes() functions.
+//
+// The argument times are in nanoseconds, although the underlying
+// filesystem may truncate or round the values to a more
+// coarse time unit.
+func Chtimes(name string, atime_ns int64, mtime_ns int64) Error {
+ var utimes [2]syscall.Timeval
+ utimes[0] = syscall.NsecToTimeval(atime_ns)
+ utimes[1] = syscall.NsecToTimeval(mtime_ns)
+ if e := syscall.Utimes(name, utimes[0:]); e != 0 {
+ return &PathError{"chtimes", name, Errno(e)}
+ }
+ return nil
+}
diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go
new file mode 100644
index 000000000..57d4a477f
--- /dev/null
+++ b/libgo/go/os/file_unix.go
@@ -0,0 +1,110 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+import (
+ "runtime"
+ "syscall"
+)
+
+// Auxiliary information if the File describes a directory
+type dirInfo struct {
+ buf []byte // buffer for directory I/O
+ dir *syscall.DIR // from opendir
+}
+
+// DevNull is the name of the operating system's ``null device.''
+// On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
+const DevNull = "/dev/null"
+
+// Open opens the named file with specified flag (O_RDONLY etc.) and perm, (0666 etc.)
+// if applicable. If successful, methods on the returned File can be used for I/O.
+// It returns the File and an Error, if any.
+func Open(name string, flag int, perm uint32) (file *File, err Error) {
+ r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, perm)
+ if e != 0 {
+ return nil, &PathError{"open", name, Errno(e)}
+ }
+
+ // There's a race here with fork/exec, which we are
+ // content to live with. See ../syscall/exec.go
+ if syscall.O_CLOEXEC == 0 { // O_CLOEXEC not supported
+ syscall.CloseOnExec(r)
+ }
+
+ return NewFile(r, name), nil
+}
+
+// Close closes the File, rendering it unusable for I/O.
+// It returns an Error, if any.
+func (file *File) Close() Error {
+ if file == nil || file.fd < 0 {
+ return EINVAL
+ }
+ var err Error
+ if e := syscall.Close(file.fd); e != 0 {
+ err = &PathError{"close", file.name, Errno(e)}
+ }
+
+ if file.dirinfo != nil {
+ if libc_closedir(file.dirinfo.dir) < 0 && err == nil {
+ err = &PathError{"closedir", file.name, Errno(syscall.GetErrno())}
+ }
+ }
+
+ file.fd = -1 // so it can't be closed again
+
+ // no need for a finalizer anymore
+ runtime.SetFinalizer(file, nil)
+ return err
+}
+
+// Stat returns the FileInfo structure describing file.
+// It returns the FileInfo and an error, if any.
+func (file *File) Stat() (fi *FileInfo, err Error) {
+ var stat syscall.Stat_t
+ e := syscall.Fstat(file.fd, &stat)
+ if e != 0 {
+ return nil, &PathError{"stat", file.name, Errno(e)}
+ }
+ return fileInfoFromStat(file.name, new(FileInfo), &stat, &stat), nil
+}
+
+// Readdir reads the contents of the directory associated with file and
+// returns an array of up to count FileInfo structures, as would be returned
+// by Lstat, in directory order. Subsequent calls on the same file will yield
+// further FileInfos.
+// A negative count means to read until EOF.
+// Readdir returns the array and an Error, if any.
+func (file *File) Readdir(count int) (fi []FileInfo, err Error) {
+ dirname := file.name
+ if dirname == "" {
+ dirname = "."
+ }
+ dirname += "/"
+ names, err1 := file.Readdirnames(count)
+ if err1 != nil {
+ return nil, err1
+ }
+ fi = make([]FileInfo, len(names))
+ for i, filename := range names {
+ fip, err := Lstat(dirname + filename)
+ if fip == nil || err != nil {
+ fi[i].Name = filename // rest is already zeroed out
+ } else {
+ fi[i] = *fip
+ }
+ }
+ return
+}
+
+// Truncate changes the size of the named file.
+// If the file is a symbolic link, it changes the size of the link's target.
+func Truncate(name string, size int64) Error {
+ if e := syscall.Truncate(name, size); e != 0 {
+ return &PathError{"truncate", name, Errno(e)}
+ }
+ return nil
+}
diff --git a/libgo/go/os/getwd.go b/libgo/go/os/getwd.go
new file mode 100644
index 000000000..49aaea865
--- /dev/null
+++ b/libgo/go/os/getwd.go
@@ -0,0 +1,92 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+import (
+ "syscall"
+)
+
+// Getwd returns a rooted path name corresponding to the
+// current directory. If the current directory can be
+// reached via multiple paths (due to symbolic links),
+// Getwd may return any one of them.
+func Getwd() (string, Error) {
+ // If the operating system provides a Getwd call, use it.
+ if syscall.ImplementsGetwd {
+ s, e := syscall.Getwd()
+ return s, NewSyscallError("getwd", e)
+ }
+
+ // Otherwise, we're trying to find our way back to ".".
+ dot, err := Stat(".")
+ if err != nil {
+ return "", err
+ }
+
+ // Clumsy but widespread kludge:
+ // if $PWD is set and matches ".", use it.
+ pwd := Getenv("PWD")
+ if len(pwd) > 0 && pwd[0] == '/' {
+ d, err := Stat(pwd)
+ if err == nil && d.Dev == dot.Dev && d.Ino == dot.Ino {
+ return pwd, nil
+ }
+ }
+
+ // Root is a special case because it has no parent
+ // and ends in a slash.
+ root, err := Stat("/")
+ if err != nil {
+ // Can't stat root - no hope.
+ return "", err
+ }
+ if root.Dev == dot.Dev && root.Ino == dot.Ino {
+ return "/", nil
+ }
+
+ // General algorithm: find name in parent
+ // and then find name of parent. Each iteration
+ // adds /name to the beginning of pwd.
+ pwd = ""
+ for parent := ".."; ; parent = "../" + parent {
+ if len(parent) >= 1024 { // Sanity check
+ return "", ENAMETOOLONG
+ }
+ fd, err := Open(parent, O_RDONLY, 0)
+ if err != nil {
+ return "", err
+ }
+
+ for {
+ names, err := fd.Readdirnames(100)
+ if err != nil {
+ fd.Close()
+ return "", err
+ }
+ for _, name := range names {
+ d, _ := Lstat(parent + "/" + name)
+ if d.Dev == dot.Dev && d.Ino == dot.Ino {
+ pwd = "/" + name + pwd
+ goto Found
+ }
+ }
+ }
+ fd.Close()
+ return "", ENOENT
+
+ Found:
+ pd, err := fd.Stat()
+ if err != nil {
+ return "", err
+ }
+ fd.Close()
+ if pd.Dev == root.Dev && pd.Ino == root.Ino {
+ break
+ }
+ // Set up for next round.
+ dot = pd
+ }
+ return pwd, nil
+}
diff --git a/libgo/go/os/inotify/inotify_linux.go b/libgo/go/os/inotify/inotify_linux.go
new file mode 100644
index 000000000..1e74c7fbc
--- /dev/null
+++ b/libgo/go/os/inotify/inotify_linux.go
@@ -0,0 +1,291 @@
+// Copyright 2010 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.
+
+/*
+This package implements a wrapper for the Linux inotify system.
+
+Example:
+ watcher, err := inotify.NewWatcher()
+ if err != nil {
+ log.Exit(err)
+ }
+ err = watcher.Watch("/tmp")
+ if err != nil {
+ log.Exit(err)
+ }
+ for {
+ select {
+ case ev := <-watcher.Event:
+ log.Println("event:", ev)
+ case err := <-watcher.Error:
+ log.Println("error:", err)
+ }
+ }
+
+*/
+package inotify
+
+import (
+ "fmt"
+ "os"
+ "strings"
+ "syscall"
+ "unsafe"
+)
+
+
+type Event struct {
+ Mask uint32 // Mask of events
+ Cookie uint32 // Unique cookie associating related events (for rename(2))
+ Name string // File name (optional)
+}
+
+type watch struct {
+ wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
+ flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
+}
+
+type Watcher struct {
+ fd int // File descriptor (as returned by the inotify_init() syscall)
+ watches map[string]*watch // Map of inotify watches (key: path)
+ paths map[int]string // Map of watched paths (key: watch descriptor)
+ Error chan os.Error // Errors are sent on this channel
+ Event chan *Event // Events are returned on this channel
+ done chan bool // Channel for sending a "quit message" to the reader goroutine
+ isClosed bool // Set to true when Close() is first called
+}
+
+
+// NewWatcher creates and returns a new inotify instance using inotify_init(2)
+func NewWatcher() (*Watcher, os.Error) {
+ fd, errno := syscall.InotifyInit()
+ if fd == -1 {
+ return nil, os.NewSyscallError("inotify_init", errno)
+ }
+ w := &Watcher{
+ fd: fd,
+ watches: make(map[string]*watch),
+ paths: make(map[int]string),
+ Event: make(chan *Event),
+ Error: make(chan os.Error),
+ done: make(chan bool, 1),
+ }
+
+ go w.readEvents()
+ return w, nil
+}
+
+
+// Close closes an inotify watcher instance
+// It sends a message to the reader goroutine to quit and removes all watches
+// associated with the inotify instance
+func (w *Watcher) Close() os.Error {
+ if w.isClosed {
+ return nil
+ }
+ w.isClosed = true
+
+ // Send "quit" message to the reader goroutine
+ w.done <- true
+ for path := range w.watches {
+ w.RemoveWatch(path)
+ }
+
+ return nil
+}
+
+// AddWatch adds path to the watched file set.
+// The flags are interpreted as described in inotify_add_watch(2).
+func (w *Watcher) AddWatch(path string, flags uint32) os.Error {
+ if w.isClosed {
+ return os.NewError("inotify instance already closed")
+ }
+
+ watchEntry, found := w.watches[path]
+ if found {
+ watchEntry.flags |= flags
+ flags |= syscall.IN_MASK_ADD
+ }
+ wd, errno := syscall.InotifyAddWatch(w.fd, path, flags)
+ if wd == -1 {
+ return os.NewSyscallError("inotify_add_watch", errno)
+ }
+
+ if !found {
+ w.watches[path] = &watch{wd: uint32(wd), flags: flags}
+ w.paths[wd] = path
+ }
+ return nil
+}
+
+
+// Watch adds path to the watched file set, watching all events.
+func (w *Watcher) Watch(path string) os.Error {
+ return w.AddWatch(path, IN_ALL_EVENTS)
+}
+
+
+// RemoveWatch removes path from the watched file set.
+func (w *Watcher) RemoveWatch(path string) os.Error {
+ watch, ok := w.watches[path]
+ if !ok {
+ return os.NewError(fmt.Sprintf("can't remove non-existent inotify watch for: %s", path))
+ }
+ success, errno := syscall.InotifyRmWatch(w.fd, watch.wd)
+ if success == -1 {
+ return os.NewSyscallError("inotify_rm_watch", errno)
+ }
+ w.watches[path] = nil, false
+ return nil
+}
+
+
+// readEvents reads from the inotify file descriptor, converts the
+// received events into Event objects and sends them via the Event channel
+func (w *Watcher) readEvents() {
+ var (
+ buf [syscall.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
+ n int // Number of bytes read with read()
+ errno int // Syscall errno
+ )
+
+ for {
+ n, errno = syscall.Read(w.fd, buf[0:])
+ // See if there is a message on the "done" channel
+ _, done := <-w.done
+
+ // If EOF or a "done" message is received
+ if n == 0 || done {
+ errno := syscall.Close(w.fd)
+ if errno == -1 {
+ w.Error <- os.NewSyscallError("close", errno)
+ }
+ close(w.Event)
+ close(w.Error)
+ return
+ }
+ if n < 0 {
+ w.Error <- os.NewSyscallError("read", errno)
+ continue
+ }
+ if n < syscall.SizeofInotifyEvent {
+ w.Error <- os.NewError("inotify: short read in readEvents()")
+ continue
+ }
+
+ var offset uint32 = 0
+ // We don't know how many events we just read into the buffer
+ // While the offset points to at least one whole event...
+ for offset <= uint32(n-syscall.SizeofInotifyEvent) {
+ // Point "raw" to the event in the buffer
+ raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset]))
+ event := new(Event)
+ event.Mask = uint32(raw.Mask)
+ event.Cookie = uint32(raw.Cookie)
+ nameLen := uint32(raw.Len)
+ // If the event happened to the watched directory or the watched file, the kernel
+ // doesn't append the filename to the event, but we would like to always fill the
+ // the "Name" field with a valid filename. We retrieve the path of the watch from
+ // the "paths" map.
+ event.Name = w.paths[int(raw.Wd)]
+ if nameLen > 0 {
+ // Point "bytes" at the first byte of the filename
+ bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent]))
+ // The filename is padded with NUL bytes. TrimRight() gets rid of those.
+ event.Name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
+ }
+ // Send the event on the events channel
+ w.Event <- event
+
+ // Move to the next event in the buffer
+ offset += syscall.SizeofInotifyEvent + nameLen
+ }
+ }
+}
+
+
+// String formats the event e in the form
+// "filename: 0xEventMask = IN_ACCESS|IN_ATTRIB_|..."
+func (e *Event) String() string {
+ var events string = ""
+
+ m := e.Mask
+ for _, b := range eventBits {
+ if m&b.Value != 0 {
+ m &^= b.Value
+ events += "|" + b.Name
+ }
+ }
+
+ if m != 0 {
+ events += fmt.Sprintf("|%#x", m)
+ }
+ if len(events) > 0 {
+ events = " == " + events[1:]
+ }
+
+ return fmt.Sprintf("%q: %#x%s", e.Name, e.Mask, events)
+}
+
+const (
+ // Options for inotify_init() are not exported
+ // IN_CLOEXEC uint32 = syscall.IN_CLOEXEC
+ // IN_NONBLOCK uint32 = syscall.IN_NONBLOCK
+
+ // Options for AddWatch
+ IN_DONT_FOLLOW uint32 = syscall.IN_DONT_FOLLOW
+ IN_ONESHOT uint32 = syscall.IN_ONESHOT
+ IN_ONLYDIR uint32 = syscall.IN_ONLYDIR
+
+ // The "IN_MASK_ADD" option is not exported, as AddWatch
+ // adds it automatically, if there is already a watch for the given path
+ // IN_MASK_ADD uint32 = syscall.IN_MASK_ADD
+
+ // Events
+ IN_ACCESS uint32 = syscall.IN_ACCESS
+ IN_ALL_EVENTS uint32 = syscall.IN_ALL_EVENTS
+ IN_ATTRIB uint32 = syscall.IN_ATTRIB
+ IN_CLOSE uint32 = syscall.IN_CLOSE
+ IN_CLOSE_NOWRITE uint32 = syscall.IN_CLOSE_NOWRITE
+ IN_CLOSE_WRITE uint32 = syscall.IN_CLOSE_WRITE
+ IN_CREATE uint32 = syscall.IN_CREATE
+ IN_DELETE uint32 = syscall.IN_DELETE
+ IN_DELETE_SELF uint32 = syscall.IN_DELETE_SELF
+ IN_MODIFY uint32 = syscall.IN_MODIFY
+ IN_MOVE uint32 = syscall.IN_MOVE
+ IN_MOVED_FROM uint32 = syscall.IN_MOVED_FROM
+ IN_MOVED_TO uint32 = syscall.IN_MOVED_TO
+ IN_MOVE_SELF uint32 = syscall.IN_MOVE_SELF
+ IN_OPEN uint32 = syscall.IN_OPEN
+
+ // Special events
+ IN_ISDIR uint32 = syscall.IN_ISDIR
+ IN_IGNORED uint32 = syscall.IN_IGNORED
+ IN_Q_OVERFLOW uint32 = syscall.IN_Q_OVERFLOW
+ IN_UNMOUNT uint32 = syscall.IN_UNMOUNT
+)
+
+var eventBits = []struct {
+ Value uint32
+ Name string
+}{
+ {IN_ACCESS, "IN_ACCESS"},
+ {IN_ATTRIB, "IN_ATTRIB"},
+ {IN_CLOSE, "IN_CLOSE"},
+ {IN_CLOSE_NOWRITE, "IN_CLOSE_NOWRITE"},
+ {IN_CLOSE_WRITE, "IN_CLOSE_WRITE"},
+ {IN_CREATE, "IN_CREATE"},
+ {IN_DELETE, "IN_DELETE"},
+ {IN_DELETE_SELF, "IN_DELETE_SELF"},
+ {IN_MODIFY, "IN_MODIFY"},
+ {IN_MOVE, "IN_MOVE"},
+ {IN_MOVED_FROM, "IN_MOVED_FROM"},
+ {IN_MOVED_TO, "IN_MOVED_TO"},
+ {IN_MOVE_SELF, "IN_MOVE_SELF"},
+ {IN_OPEN, "IN_OPEN"},
+ {IN_ISDIR, "IN_ISDIR"},
+ {IN_IGNORED, "IN_IGNORED"},
+ {IN_Q_OVERFLOW, "IN_Q_OVERFLOW"},
+ {IN_UNMOUNT, "IN_UNMOUNT"},
+}
diff --git a/libgo/go/os/inotify/inotify_linux_test.go b/libgo/go/os/inotify/inotify_linux_test.go
new file mode 100644
index 000000000..332edcb64
--- /dev/null
+++ b/libgo/go/os/inotify/inotify_linux_test.go
@@ -0,0 +1,99 @@
+// Copyright 2010 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 inotify
+
+import (
+ "os"
+ "time"
+ "testing"
+)
+
+func TestInotifyEvents(t *testing.T) {
+ // Create an inotify watcher instance and initialize it
+ watcher, err := NewWatcher()
+ if err != nil {
+ t.Fatalf("NewWatcher() failed: %s", err)
+ }
+
+ // Add a watch for "_obj"
+ err = watcher.Watch("_obj")
+ if err != nil {
+ t.Fatalf("Watcher.Watch() failed: %s", err)
+ }
+
+ // Receive errors on the error channel on a separate goroutine
+ go func() {
+ for err := range watcher.Error {
+ t.Fatalf("error received: %s", err)
+ }
+ }()
+
+ const testFile string = "_obj/TestInotifyEvents.testfile"
+
+ // Receive events on the event channel on a separate goroutine
+ eventstream := watcher.Event
+ var eventsReceived = 0
+ go func() {
+ for event := range eventstream {
+ // Only count relevant events
+ if event.Name == testFile {
+ eventsReceived++
+ t.Logf("event received: %s", event)
+ } else {
+ t.Logf("unexpected event received: %s", event)
+ }
+ }
+ }()
+
+ // Create a file
+ // This should add at least one event to the inotify event queue
+ _, err = os.Open(testFile, os.O_WRONLY|os.O_CREAT, 0666)
+ if err != nil {
+ t.Fatalf("creating test file failed: %s", err)
+ }
+
+ // We expect this event to be received almost immediately, but let's wait 1 s to be sure
+ time.Sleep(1000e6) // 1000 ms
+ if eventsReceived == 0 {
+ t.Fatal("inotify event hasn't been received after 1 second")
+ }
+
+ // Try closing the inotify instance
+ t.Log("calling Close()")
+ watcher.Close()
+ t.Log("waiting for the event channel to become closed...")
+ var i = 0
+ for !closed(eventstream) {
+ if i >= 20 {
+ t.Fatal("event stream was not closed after 1 second, as expected")
+ }
+ t.Log("waiting for 50 ms...")
+ time.Sleep(50e6) // 50 ms
+ i++
+ }
+ t.Log("event channel closed")
+}
+
+
+func TestInotifyClose(t *testing.T) {
+ watcher, _ := NewWatcher()
+ watcher.Close()
+
+ done := false
+ go func() {
+ watcher.Close()
+ done = true
+ }()
+
+ time.Sleep(50e6) // 50 ms
+ if !done {
+ t.Fatal("double Close() test failed: second Close() call didn't return")
+ }
+
+ err := watcher.Watch("_obj")
+ if err == nil {
+ t.Fatal("expected error on Watch() after Close(), got nil")
+ }
+}
diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go
new file mode 100644
index 000000000..5b577065a
--- /dev/null
+++ b/libgo/go/os/os_test.go
@@ -0,0 +1,882 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os_test
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "io/ioutil"
+ . "os"
+ "strings"
+ "syscall"
+ "testing"
+)
+
+var dot = []string{
+ "env_unix.go",
+ "error.go",
+ "file.go",
+ "os_test.go",
+ "time.go",
+ "types.go",
+}
+
+type sysDir struct {
+ name string
+ files []string
+}
+
+var sysdir = func() (sd *sysDir) {
+ switch syscall.OS {
+ case "windows":
+ sd = &sysDir{
+ Getenv("SystemRoot") + "\\system32\\drivers\\etc",
+ []string{
+ "hosts",
+ "networks",
+ "protocol",
+ "services",
+ },
+ }
+ default:
+ sd = &sysDir{
+ "/etc",
+ []string{
+ "group",
+ "hosts",
+ "passwd",
+ },
+ }
+ }
+ return
+}()
+
+func size(name string, t *testing.T) int64 {
+ file, err := Open(name, O_RDONLY, 0)
+ defer file.Close()
+ if err != nil {
+ t.Fatal("open failed:", err)
+ }
+ var buf [100]byte
+ len := 0
+ for {
+ n, e := file.Read(buf[0:])
+ len += n
+ if e == EOF {
+ break
+ }
+ if e != nil {
+ t.Fatal("read failed:", err)
+ }
+ }
+ return int64(len)
+}
+
+func equal(name1, name2 string) (r bool) {
+ switch syscall.OS {
+ case "windows":
+ r = strings.ToLower(name1) == strings.ToLower(name2)
+ default:
+ r = name1 == name2
+ }
+ return
+}
+
+func newFile(testName string, t *testing.T) (f *File) {
+ // Use a local file system, not NFS.
+ // On Unix, override $TMPDIR in case the user
+ // has it set to an NFS-mounted directory.
+ dir := ""
+ if syscall.OS != "windows" {
+ dir = "/tmp"
+ }
+ f, err := ioutil.TempFile(dir, "_Go_"+testName)
+ if err != nil {
+ t.Fatalf("open %s: %s", testName, err)
+ }
+ return
+}
+
+var sfdir = sysdir.name
+var sfname = sysdir.files[0]
+
+func TestStat(t *testing.T) {
+ path := sfdir + "/" + sfname
+ dir, err := Stat(path)
+ if err != nil {
+ t.Fatal("stat failed:", err)
+ }
+ if !equal(sfname, dir.Name) {
+ t.Error("name should be ", sfname, "; is", dir.Name)
+ }
+ filesize := size(path, t)
+ if dir.Size != filesize {
+ t.Error("size should be", filesize, "; is", dir.Size)
+ }
+}
+
+func TestFstat(t *testing.T) {
+ path := sfdir + "/" + sfname
+ file, err1 := Open(path, O_RDONLY, 0)
+ defer file.Close()
+ if err1 != nil {
+ t.Fatal("open failed:", err1)
+ }
+ dir, err2 := file.Stat()
+ if err2 != nil {
+ t.Fatal("fstat failed:", err2)
+ }
+ if !equal(sfname, dir.Name) {
+ t.Error("name should be ", sfname, "; is", dir.Name)
+ }
+ filesize := size(path, t)
+ if dir.Size != filesize {
+ t.Error("size should be", filesize, "; is", dir.Size)
+ }
+}
+
+func TestLstat(t *testing.T) {
+ path := sfdir + "/" + sfname
+ dir, err := Lstat(path)
+ if err != nil {
+ t.Fatal("lstat failed:", err)
+ }
+ if !equal(sfname, dir.Name) {
+ t.Error("name should be ", sfname, "; is", dir.Name)
+ }
+ filesize := size(path, t)
+ if dir.Size != filesize {
+ t.Error("size should be", filesize, "; is", dir.Size)
+ }
+}
+
+func testReaddirnames(dir string, contents []string, t *testing.T) {
+ file, err := Open(dir, O_RDONLY, 0)
+ defer file.Close()
+ if err != nil {
+ t.Fatalf("open %q failed: %v", dir, err)
+ }
+ s, err2 := file.Readdirnames(-1)
+ if err2 != nil {
+ t.Fatalf("readdirnames %q failed: %v", dir, err2)
+ }
+ for _, m := range contents {
+ found := false
+ for _, n := range s {
+ if n == "." || n == ".." {
+ t.Errorf("got %s in directory", n)
+ }
+ if equal(m, n) {
+ if found {
+ t.Error("present twice:", m)
+ }
+ found = true
+ }
+ }
+ if !found {
+ t.Error("could not find", m)
+ }
+ }
+}
+
+func testReaddir(dir string, contents []string, t *testing.T) {
+ file, err := Open(dir, O_RDONLY, 0)
+ defer file.Close()
+ if err != nil {
+ t.Fatalf("open %q failed: %v", dir, err)
+ }
+ s, err2 := file.Readdir(-1)
+ if err2 != nil {
+ t.Fatalf("readdir %q failed: %v", dir, err2)
+ }
+ for _, m := range contents {
+ found := false
+ for _, n := range s {
+ if equal(m, n.Name) {
+ if found {
+ t.Error("present twice:", m)
+ }
+ found = true
+ }
+ }
+ if !found {
+ t.Error("could not find", m)
+ }
+ }
+}
+
+func TestReaddirnames(t *testing.T) {
+ testReaddirnames(".", dot, t)
+ testReaddirnames(sysdir.name, sysdir.files, t)
+}
+
+func TestReaddir(t *testing.T) {
+ testReaddir(".", dot, t)
+ testReaddir(sysdir.name, sysdir.files, t)
+}
+
+// Read the directory one entry at a time.
+func smallReaddirnames(file *File, length int, t *testing.T) []string {
+ names := make([]string, length)
+ count := 0
+ for {
+ d, err := file.Readdirnames(1)
+ if err != nil {
+ t.Fatalf("readdir %q failed: %v", file.Name(), err)
+ }
+ if len(d) == 0 {
+ break
+ }
+ names[count] = d[0]
+ count++
+ }
+ return names[0:count]
+}
+
+// Check that reading a directory one entry at a time gives the same result
+// as reading it all at once.
+func TestReaddirnamesOneAtATime(t *testing.T) {
+ // big directory that doesn't change often.
+ dir := "/usr/bin"
+ if syscall.OS == "windows" {
+ dir = Getenv("SystemRoot") + "\\system32"
+ }
+ file, err := Open(dir, O_RDONLY, 0)
+ defer file.Close()
+ if err != nil {
+ t.Fatalf("open %q failed: %v", dir, err)
+ }
+ all, err1 := file.Readdirnames(-1)
+ if err1 != nil {
+ t.Fatalf("readdirnames %q failed: %v", dir, err1)
+ }
+ file1, err2 := Open(dir, O_RDONLY, 0)
+ if err2 != nil {
+ t.Fatalf("open %q failed: %v", dir, err2)
+ }
+ small := smallReaddirnames(file1, len(all)+100, t) // +100 in case we screw up
+ for i, n := range all {
+ if small[i] != n {
+ t.Errorf("small read %q mismatch: %v", small[i], n)
+ }
+ }
+}
+
+func TestHardLink(t *testing.T) {
+ // Hardlinks are not supported under windows.
+ if syscall.OS == "windows" {
+ return
+ }
+ from, to := "hardlinktestfrom", "hardlinktestto"
+ Remove(from) // Just in case.
+ file, err := Open(to, O_CREAT|O_WRONLY, 0666)
+ 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)
+ }
+ err = Link(to, from)
+ if err != nil {
+ t.Fatalf("link %q, %q failed: %v", to, from, err)
+ }
+ defer Remove(from)
+ tostat, err := Stat(to)
+ if err != nil {
+ t.Fatalf("stat %q failed: %v", to, err)
+ }
+ fromstat, err := Stat(from)
+ if err != nil {
+ t.Fatalf("stat %q failed: %v", from, err)
+ }
+ if tostat.Dev != fromstat.Dev || tostat.Ino != fromstat.Ino {
+ t.Errorf("link %q, %q did not create hard link", to, from)
+ }
+}
+
+func TestSymLink(t *testing.T) {
+ // Symlinks are not supported under windows.
+ if syscall.OS == "windows" {
+ return
+ }
+ from, to := "symlinktestfrom", "symlinktestto"
+ Remove(from) // Just in case.
+ file, err := Open(to, O_CREAT|O_WRONLY, 0666)
+ 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)
+ }
+ err = Symlink(to, from)
+ if err != nil {
+ t.Fatalf("symlink %q, %q failed: %v", to, from, err)
+ }
+ defer Remove(from)
+ tostat, err := Stat(to)
+ if err != nil {
+ t.Fatalf("stat %q failed: %v", to, err)
+ }
+ if tostat.FollowedSymlink {
+ t.Fatalf("stat %q claims to have followed a symlink", to)
+ }
+ fromstat, err := Stat(from)
+ if err != nil {
+ t.Fatalf("stat %q failed: %v", from, err)
+ }
+ if tostat.Dev != fromstat.Dev || tostat.Ino != fromstat.Ino {
+ t.Errorf("symlink %q, %q did not create symlink", to, from)
+ }
+ fromstat, err = Lstat(from)
+ if err != nil {
+ t.Fatalf("lstat %q failed: %v", from, err)
+ }
+ if !fromstat.IsSymlink() {
+ t.Fatalf("symlink %q, %q did not create symlink", to, from)
+ }
+ fromstat, err = Stat(from)
+ if err != nil {
+ t.Fatalf("stat %q failed: %v", from, err)
+ }
+ if !fromstat.FollowedSymlink {
+ t.Fatalf("stat %q did not follow symlink", from)
+ }
+ s, err := Readlink(from)
+ if err != nil {
+ t.Fatalf("readlink %q failed: %v", from, err)
+ }
+ if s != to {
+ t.Fatalf("after symlink %q != %q", s, to)
+ }
+ file, err = Open(from, O_RDONLY, 0)
+ if err != nil {
+ t.Fatalf("open %q failed: %v", from, err)
+ }
+ file.Close()
+}
+
+func TestLongSymlink(t *testing.T) {
+ // Symlinks are not supported under windows.
+ if syscall.OS == "windows" {
+ return
+ }
+ s := "0123456789abcdef"
+ // Long, but not too long: a common limit is 255.
+ s = s + s + s + s + s + s + s + s + s + s + s + s + s + s + s
+ 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)
+ }
+ if r != s {
+ t.Fatalf("after symlink %q != %q", r, s)
+ }
+}
+
+func TestRename(t *testing.T) {
+ from, to := "renamefrom", "renameto"
+ Remove(to) // Just in case.
+ file, err := Open(from, O_CREAT|O_WRONLY, 0666)
+ if err != nil {
+ t.Fatalf("open %q failed: %v", to, err)
+ }
+ if err = file.Close(); err != nil {
+ t.Errorf("close %q failed: %v", to, err)
+ }
+ err = Rename(from, to)
+ 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)
+ }
+}
+
+func TestForkExec(t *testing.T) {
+ var cmd, adir, expect string
+ var args []string
+ r, w, err := Pipe()
+ if err != nil {
+ t.Fatalf("Pipe: %v", err)
+ }
+ if syscall.OS == "windows" {
+ cmd = Getenv("COMSPEC")
+ args = []string{Getenv("COMSPEC"), "/c cd"}
+ adir = Getenv("SystemRoot")
+ expect = Getenv("SystemRoot") + "\r\n"
+ } else {
+ cmd = "/bin/pwd"
+ args = []string{"pwd"}
+ adir = "/"
+ expect = "/\n"
+ }
+ pid, err := ForkExec(cmd, args, nil, adir, []*File{nil, w, Stderr})
+ if err != nil {
+ t.Fatalf("ForkExec: %v", err)
+ }
+ w.Close()
+
+ var b bytes.Buffer
+ io.Copy(&b, r)
+ output := b.String()
+ if output != expect {
+ args[0] = cmd
+ t.Errorf("exec %q returned %q wanted %q", strings.Join(args, " "), output, expect)
+ }
+ Wait(pid, 0)
+}
+
+func checkMode(t *testing.T, path string, mode uint32) {
+ dir, err := Stat(path)
+ if err != nil {
+ t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err)
+ }
+ if dir.Mode&0777 != mode {
+ t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode, mode)
+ }
+}
+
+func TestChmod(t *testing.T) {
+ // Chmod is not supported under windows.
+ if syscall.OS == "windows" {
+ return
+ }
+ f := newFile("TestChmod", t)
+ defer Remove(f.Name())
+ defer f.Close()
+
+ if err := Chmod(f.Name(), 0456); err != nil {
+ t.Fatalf("chmod %s 0456: %s", f.Name(), err)
+ }
+ checkMode(t, f.Name(), 0456)
+
+ if err := f.Chmod(0123); err != nil {
+ t.Fatalf("chmod %s 0123: %s", f.Name(), err)
+ }
+ checkMode(t, f.Name(), 0123)
+}
+
+func checkUidGid(t *testing.T, path string, uid, gid int) {
+ dir, err := Stat(path)
+ if err != nil {
+ t.Fatalf("Stat %q (looking for uid/gid %d/%d): %s", path, uid, gid, err)
+ }
+ if dir.Uid != uid {
+ t.Errorf("Stat %q: uid %d want %d", path, dir.Uid, uid)
+ }
+ if dir.Gid != gid {
+ t.Errorf("Stat %q: gid %d want %d", path, dir.Gid, gid)
+ }
+}
+
+func TestChown(t *testing.T) {
+ // Chown is not supported under windows.
+ if syscall.OS == "windows" {
+ return
+ }
+ // Use TempDir() to make sure we're on a local file system,
+ // so that the group ids returned by Getgroups will be allowed
+ // on the file. On NFS, the Getgroups groups are
+ // basically useless.
+ f := newFile("TestChown", t)
+ defer Remove(f.Name())
+ defer f.Close()
+ dir, err := f.Stat()
+ if err != nil {
+ t.Fatalf("stat %s: %s", f.Name(), err)
+ }
+
+ // Can't change uid unless root, but can try
+ // changing the group id. First try our current group.
+ gid := Getgid()
+ t.Log("gid:", gid)
+ if err = Chown(f.Name(), -1, gid); err != nil {
+ t.Fatalf("chown %s -1 %d: %s", f.Name(), gid, err)
+ }
+ checkUidGid(t, f.Name(), dir.Uid, gid)
+
+ // Then try all the auxiliary groups.
+ groups, err := Getgroups()
+ if err != nil {
+ t.Fatalf("getgroups: %s", err)
+ }
+ t.Log("groups: ", groups)
+ for _, g := range groups {
+ if err = Chown(f.Name(), -1, g); err != nil {
+ t.Fatalf("chown %s -1 %d: %s", f.Name(), g, err)
+ }
+ checkUidGid(t, f.Name(), dir.Uid, g)
+
+ // change back to gid to test fd.Chown
+ if err = f.Chown(-1, gid); err != nil {
+ t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err)
+ }
+ checkUidGid(t, f.Name(), dir.Uid, gid)
+ }
+}
+
+func checkSize(t *testing.T, f *File, size int64) {
+ dir, err := f.Stat()
+ if err != nil {
+ t.Fatalf("Stat %q (looking for size %d): %s", f.Name(), size, err)
+ }
+ if dir.Size != size {
+ t.Errorf("Stat %q: size %d want %d", f.Name(), dir.Size, size)
+ }
+}
+
+func TestTruncate(t *testing.T) {
+ f := newFile("TestTruncate", t)
+ defer Remove(f.Name())
+ defer f.Close()
+
+ checkSize(t, f, 0)
+ f.Write([]byte("hello, world\n"))
+ checkSize(t, f, 13)
+ f.Truncate(10)
+ checkSize(t, f, 10)
+ f.Truncate(1024)
+ checkSize(t, f, 1024)
+ f.Truncate(0)
+ checkSize(t, f, 0)
+ f.Write([]byte("surprise!"))
+ checkSize(t, f, 13+9) // wrote at offset past where hello, world was.
+}
+
+// Use TempDir() 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()
+
+ preStat, err := Stat(f.Name())
+ if err != nil {
+ t.Fatalf("Stat %s: %s", f.Name(), err)
+ }
+
+ // Move access and modification time back a second
+ const OneSecond = 1e9 // in nanoseconds
+ err = Chtimes(f.Name(), preStat.Atime_ns-OneSecond, preStat.Mtime_ns-OneSecond)
+ if err != nil {
+ t.Fatalf("Chtimes %s: %s", f.Name(), err)
+ }
+
+ postStat, err := Stat(f.Name())
+ if err != nil {
+ t.Fatalf("second Stat %s: %s", f.Name(), err)
+ }
+
+ if postStat.Atime_ns >= preStat.Atime_ns {
+ t.Errorf("Atime_ns didn't go backwards; was=%d, after=%d",
+ preStat.Atime_ns,
+ postStat.Atime_ns)
+ }
+
+ if postStat.Mtime_ns >= preStat.Mtime_ns {
+ t.Errorf("Mtime_ns didn't go backwards; was=%d, after=%d",
+ preStat.Mtime_ns,
+ postStat.Mtime_ns)
+ }
+}
+
+func TestChdirAndGetwd(t *testing.T) {
+ // TODO(brainman): file.Chdir() is not implemented on windows.
+ if syscall.OS == "windows" {
+ return
+ }
+ fd, err := Open(".", O_RDONLY, 0)
+ if err != nil {
+ 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"}
+ for mode := 0; mode < 2; mode++ {
+ for _, d := range dirs {
+ if mode == 0 {
+ err = Chdir(d)
+ } else {
+ fd1, err := Open(d, O_RDONLY, 0)
+ if err != nil {
+ t.Errorf("Open %s: %s", d, err)
+ continue
+ }
+ err = fd1.Chdir()
+ fd1.Close()
+ }
+ pwd, err1 := Getwd()
+ err2 := fd.Chdir()
+ if err2 != nil {
+ // We changed the current directory and cannot go back.
+ // Don't let the tests continue; they'll scribble
+ // all over some other directory.
+ fmt.Fprintf(Stderr, "fchdir back to dot failed: %s\n", err2)
+ Exit(1)
+ }
+ if err != nil {
+ fd.Close()
+ t.Fatalf("Chdir %s: %s", d, err)
+ }
+ if err1 != nil {
+ fd.Close()
+ t.Fatalf("Getwd in %s: %s", d, err1)
+ }
+ if pwd != d {
+ fd.Close()
+ t.Fatalf("Getwd returned %q want %q", pwd, d)
+ }
+ }
+ }
+ fd.Close()
+}
+
+func TestTime(t *testing.T) {
+ // Just want to check that Time() is getting something.
+ // A common failure mode on Darwin is to get 0, 0,
+ // because it returns the time in registers instead of
+ // filling in the structure passed to the system call.
+ // Too bad the compiler doesn't know that
+ // 365.24*86400 is an integer.
+ sec, nsec, err := Time()
+ if sec < (2009-1970)*36524*864 {
+ t.Errorf("Time() = %d, %d, %s; not plausible", sec, nsec, err)
+ }
+}
+
+func TestSeek(t *testing.T) {
+ f := newFile("TestSeek", t)
+ defer Remove(f.Name())
+ defer f.Close()
+
+ const data = "hello, world\n"
+ io.WriteString(f, data)
+
+ type test struct {
+ in int64
+ whence int
+ out int64
+ }
+ var tests = []test{
+ {0, 1, int64(len(data))},
+ {0, 0, 0},
+ {5, 0, 5},
+ {0, 2, int64(len(data))},
+ {0, 0, 0},
+ {-1, 2, int64(len(data)) - 1},
+ {1 << 33, 0, 1 << 33},
+ {1 << 33, 2, 1<<33 + int64(len(data))},
+ }
+ for i, tt := range tests {
+ off, err := f.Seek(tt.in, tt.whence)
+ if off != tt.out || err != nil {
+ if e, ok := err.(*PathError); ok && e.Error == EINVAL && tt.out > 1<<32 {
+ // Reiserfs rejects the big seeks.
+ // http://code.google.com/p/go/issues/detail?id=91
+ break
+ }
+ t.Errorf("#%d: Seek(%v, %v) = %v, %v want %v, nil", i, tt.in, tt.whence, off, err, tt.out)
+ }
+ }
+}
+
+type openErrorTest struct {
+ path string
+ mode int
+ error Error
+}
+
+var openErrorTests = []openErrorTest{
+ {
+ sfdir + "/no-such-file",
+ O_RDONLY,
+ ENOENT,
+ },
+ {
+ sfdir,
+ O_WRONLY,
+ EISDIR,
+ },
+ {
+ sfdir + "/" + sfname + "/no-such-file",
+ O_WRONLY,
+ ENOTDIR,
+ },
+}
+
+func TestOpenError(t *testing.T) {
+ for _, tt := range openErrorTests {
+ f, err := Open(tt.path, tt.mode, 0)
+ if err == nil {
+ t.Errorf("Open(%q, %d) succeeded", tt.path, tt.mode)
+ f.Close()
+ continue
+ }
+ perr, ok := err.(*PathError)
+ if !ok {
+ t.Errorf("Open(%q, %d) returns error of %T type; want *os.PathError", tt.path, tt.mode, err)
+ }
+ if perr.Error != tt.error {
+ t.Errorf("Open(%q, %d) = _, %q; want %q", tt.path, tt.mode, perr.Error.String(), tt.error.String())
+ }
+ }
+}
+
+func run(t *testing.T, cmd []string) string {
+ // Run /bin/hostname and collect output.
+ r, w, err := Pipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ pid, err := ForkExec("/bin/hostname", []string{"hostname"}, nil, "/", []*File{nil, w, Stderr})
+ if err != nil {
+ t.Fatal(err)
+ }
+ w.Close()
+
+ var b bytes.Buffer
+ io.Copy(&b, r)
+ Wait(pid, 0)
+ output := b.String()
+ if n := len(output); n > 0 && output[n-1] == '\n' {
+ output = output[0 : n-1]
+ }
+ if output == "" {
+ t.Fatalf("%v produced no output", cmd)
+ }
+
+ return output
+}
+
+
+func TestHostname(t *testing.T) {
+ // There is no other way to fetch hostname on windows, but via winapi.
+ if syscall.OS == "windows" {
+ return
+ }
+ // 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
+ hostname, err := Hostname()
+ if err != nil {
+ t.Fatalf("%v", err)
+ }
+ want := run(t, []string{"/bin/hostname"})
+ if hostname != want {
+ i := strings.Index(hostname, ".")
+ if i < 0 || hostname[0:i] != want {
+ t.Errorf("Hostname() = %q, want %q", hostname, want)
+ }
+ }
+}
+
+func TestReadAt(t *testing.T) {
+ f := newFile("TestReadAt", t)
+ defer Remove(f.Name())
+ defer f.Close()
+
+ const data = "hello, world\n"
+ io.WriteString(f, data)
+
+ b := make([]byte, 5)
+ n, err := f.ReadAt(b, 7)
+ if err != nil || n != len(b) {
+ t.Fatalf("ReadAt 7: %d, %r", n, err)
+ }
+ if string(b) != "world" {
+ t.Fatalf("ReadAt 7: have %q want %q", string(b), "world")
+ }
+}
+
+func TestWriteAt(t *testing.T) {
+ f := newFile("TestWriteAt", t)
+ defer Remove(f.Name())
+ defer f.Close()
+
+ const data = "hello, world\n"
+ io.WriteString(f, data)
+
+ n, err := f.WriteAt([]byte("WORLD"), 7)
+ if err != nil || n != 5 {
+ t.Fatalf("WriteAt 7: %d, %v", n, err)
+ }
+
+ b, err := ioutil.ReadFile(f.Name())
+ if err != nil {
+ t.Fatalf("ReadFile %s: %v", f.Name(), err)
+ }
+ if string(b) != "hello, WORLD\n" {
+ t.Fatalf("after write: have %q want %q", string(b), "hello, WORLD\n")
+ }
+}
+
+func writeFile(t *testing.T, fname string, flag int, text string) string {
+ f, err := Open(fname, flag, 0666)
+ if err != nil {
+ t.Fatalf("Open: %v", err)
+ }
+ n, err := io.WriteString(f, text)
+ if err != nil {
+ t.Fatalf("WriteString: %d, %v", n, err)
+ }
+ f.Close()
+ data, err := ioutil.ReadFile(fname)
+ if err != nil {
+ t.Fatalf("ReadFile: %v", err)
+ }
+ return string(data)
+}
+
+func TestAppend(t *testing.T) {
+ const f = "append.txt"
+ defer Remove(f)
+ s := writeFile(t, f, O_CREAT|O_TRUNC|O_RDWR, "new")
+ if s != "new" {
+ t.Fatalf("writeFile: have %q want %q", s, "new")
+ }
+ s = writeFile(t, f, O_APPEND|O_RDWR, "|append")
+ if s != "new|append" {
+ t.Fatalf("writeFile: have %q want %q", s, "new|append")
+ }
+}
+
+func TestStatDirWithTrailingSlash(t *testing.T) {
+ // Create new dir, in _test so it will get
+ // cleaned up by make if not by us.
+ path := "_test/_TestStatDirWithSlash_"
+ err := MkdirAll(path, 0777)
+ if err != nil {
+ t.Fatalf("MkdirAll %q: %s", path, err)
+ }
+ defer RemoveAll(path)
+
+ // Stat of path should succeed.
+ _, err = Stat(path)
+ if err != nil {
+ t.Fatal("stat failed:", err)
+ }
+
+ // Stat of path+"/" should succeed too.
+ _, err = Stat(path + "/")
+ if err != nil {
+ t.Fatal("stat failed:", err)
+ }
+}
diff --git a/libgo/go/os/path.go b/libgo/go/os/path.go
new file mode 100644
index 000000000..b762971d9
--- /dev/null
+++ b/libgo/go/os/path.go
@@ -0,0 +1,116 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+
+// MkdirAll creates a directory named path,
+// along with any necessary parents, and returns nil,
+// or else returns an error.
+// The permission bits perm are used for all
+// directories that MkdirAll creates.
+// If path is already a directory, MkdirAll does nothing
+// and returns nil.
+func MkdirAll(path string, perm uint32) Error {
+ // If path exists, stop with success or error.
+ dir, err := Stat(path)
+ if err == nil {
+ if dir.IsDirectory() {
+ return nil
+ }
+ return &PathError{"mkdir", path, ENOTDIR}
+ }
+
+ // Doesn't already exist; make sure parent does.
+ i := len(path)
+ for i > 0 && path[i-1] == '/' { // Skip trailing slashes.
+ i--
+ }
+
+ j := i
+ for j > 0 && path[j-1] != '/' { // Scan backward over element.
+ j--
+ }
+
+ if j > 0 {
+ // Create parent
+ err = MkdirAll(path[0:j-1], perm)
+ if err != nil {
+ return err
+ }
+ }
+
+ // Now parent exists, try to create.
+ err = Mkdir(path, perm)
+ if err != nil {
+ // Handle arguments like "foo/." by
+ // double-checking that directory doesn't exist.
+ dir, err1 := Lstat(path)
+ if err1 == nil && dir.IsDirectory() {
+ return nil
+ }
+ return err
+ }
+ 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 {
+ 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 && serr.Error == ENOENT {
+ return nil
+ }
+ return serr
+ }
+ if !dir.IsDirectory() {
+ // Not a directory; return the error from Remove.
+ return err
+ }
+
+ // Directory.
+ fd, err := Open(path, O_RDONLY, 0)
+ if err != nil {
+ return err
+ }
+
+ // Remove contents & return first error.
+ err = nil
+ for {
+ names, err1 := fd.Readdirnames(100)
+ for _, name := range names {
+ err1 := RemoveAll(path + "/" + name)
+ if err == nil {
+ err = err1
+ }
+ }
+ // If Readdirnames returned an error, use it.
+ if err == nil {
+ err = err1
+ }
+ if len(names) == 0 {
+ break
+ }
+ }
+
+ // Close directory, because windows won't remove opened directory.
+ fd.Close()
+
+ // Remove directory.
+ err1 := Remove(path)
+ if err == nil {
+ err = err1
+ }
+ return err
+}
diff --git a/libgo/go/os/path_test.go b/libgo/go/os/path_test.go
new file mode 100644
index 000000000..799e3ec2f
--- /dev/null
+++ b/libgo/go/os/path_test.go
@@ -0,0 +1,181 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os_test
+
+import (
+ . "os"
+ "testing"
+ "runtime"
+ "syscall"
+)
+
+func TestMkdirAll(t *testing.T) {
+ // Create new dir, in _test so it will get
+ // cleaned up by make if not by us.
+ path := "_test/_TestMkdirAll_/dir/./dir2"
+ err := MkdirAll(path, 0777)
+ if err != nil {
+ t.Fatalf("MkdirAll %q: %s", path, err)
+ }
+ defer RemoveAll("_test/_TestMkdirAll_")
+
+ // Already exists, should succeed.
+ err = MkdirAll(path, 0777)
+ if err != nil {
+ t.Fatalf("MkdirAll %q (second time): %s", path, err)
+ }
+
+ // Make file.
+ fpath := path + "/file"
+ _, err = Open(fpath, O_WRONLY|O_CREAT, 0666)
+ if err != nil {
+ t.Fatalf("create %q: %s", fpath, err)
+ }
+
+ // Can't make directory named after file.
+ err = MkdirAll(fpath, 0777)
+ if err == nil {
+ t.Fatalf("MkdirAll %q: no error", fpath)
+ }
+ perr, ok := err.(*PathError)
+ if !ok {
+ t.Fatalf("MkdirAll %q returned %T, not *PathError", fpath, err)
+ }
+ if perr.Path != fpath {
+ t.Fatalf("MkdirAll %q returned wrong error path: %q not %q", fpath, perr.Path, fpath)
+ }
+
+ // Can't make subdirectory of file.
+ ffpath := fpath + "/subdir"
+ err = MkdirAll(ffpath, 0777)
+ if err == nil {
+ t.Fatalf("MkdirAll %q: no error", ffpath)
+ }
+ perr, ok = err.(*PathError)
+ if !ok {
+ t.Fatalf("MkdirAll %q returned %T, not *PathError", ffpath, err)
+ }
+ if perr.Path != fpath {
+ t.Fatalf("MkdirAll %q returned wrong error path: %q not %q", ffpath, perr.Path, fpath)
+ }
+}
+
+func TestRemoveAll(t *testing.T) {
+ // Work directory.
+ path := "_test/_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 := Open(fpath, O_WRONLY|O_CREAT, 0666)
+ 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 = Open(fpath, O_WRONLY|O_CREAT, 0666)
+ if err != nil {
+ t.Fatalf("create %q: %s", fpath, err)
+ }
+ fd.Close()
+ fd, err = Open(dpath+"/file", O_WRONLY|O_CREAT, 0666)
+ 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 syscall.OS == "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 = Open(s, O_WRONLY|O_CREAT, 0666)
+ 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)
+ }
+}
+
+func TestMkdirAllWithSymlink(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Log("Skipping test: symlinks don't exist under Windows")
+ return
+ }
+
+ err := Mkdir("_test/dir", 0755)
+ if err != nil {
+ t.Fatal(`Mkdir "_test/dir":`, err)
+ }
+ defer RemoveAll("_test/dir")
+
+ err = Symlink("dir", "_test/link")
+ if err != nil {
+ t.Fatal(`Symlink "dir", "_test/link":`, err)
+ }
+ defer RemoveAll("_test/link")
+
+ path := "_test/link/foo"
+ err = MkdirAll(path, 0755)
+ if err != nil {
+ t.Errorf("MkdirAll %q: %s", path, err)
+ }
+}
diff --git a/libgo/go/os/proc.go b/libgo/go/os/proc.go
new file mode 100644
index 000000000..dfddab6cb
--- /dev/null
+++ b/libgo/go/os/proc.go
@@ -0,0 +1,35 @@
+// 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.
+
+// Process etc.
+
+package os
+
+import "syscall"
+
+var Args []string // provided by runtime
+var Envs []string // provided by runtime
+
+
+// Getuid returns the numeric user id of the caller.
+func Getuid() int { return syscall.Getuid() }
+
+// Geteuid returns the numeric effective user id of the caller.
+func Geteuid() int { return syscall.Geteuid() }
+
+// Getgid returns the numeric group id of the caller.
+func Getgid() int { return syscall.Getgid() }
+
+// Getegid returns the numeric effective group id of the caller.
+func Getegid() int { return syscall.Getegid() }
+
+// Getgroups returns a list of the numeric ids of groups that the caller belongs to.
+func Getgroups() ([]int, Error) {
+ gids, errno := syscall.Getgroups()
+ return gids, NewSyscallError("getgroups", errno)
+}
+
+// Exit causes the current program to exit with the given status code.
+// Conventionally, code zero indicates success, non-zero an error.
+func Exit(code int) { syscall.Exit(code) }
diff --git a/libgo/go/os/signal/mkunix.sh b/libgo/go/os/signal/mkunix.sh
new file mode 100644
index 000000000..ec5c9d680
--- /dev/null
+++ b/libgo/go/os/signal/mkunix.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+# Copyright 2010 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.
+
+echo '// ./mkunix.sh' "$1"
+echo '// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT'
+echo
+
+cat <<EOH
+package signal
+
+import (
+ "syscall"
+)
+
+var _ = syscall.Syscall // in case there are zero signals
+
+const (
+EOH
+
+sed -n 's/^const[ ]*\(SIG[A-Z0-9][A-Z0-9]*\)[ ].*/ \1 = UnixSignal(syscall.\1)/p' "$1"
+
+echo ")"
diff --git a/libgo/go/os/signal/signal.go b/libgo/go/os/signal/signal.go
new file mode 100644
index 000000000..666c03e73
--- /dev/null
+++ b/libgo/go/os/signal/signal.go
@@ -0,0 +1,48 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package signal implements operating system-independent signal handling.
+package signal
+
+import (
+ "runtime"
+ "strconv"
+)
+
+// A Signal can represent any operating system signal.
+type Signal interface {
+ String() string
+}
+
+type UnixSignal int32
+
+func (sig UnixSignal) String() string {
+ s := runtime.Signame(int32(sig))
+ if len(s) > 0 {
+ return s
+ }
+ return "Signal " + strconv.Itoa(int(sig))
+}
+
+// Incoming is the global signal channel.
+// All signals received by the program will be delivered to this channel.
+var Incoming <-chan Signal
+
+func process(ch chan<- Signal) {
+ for {
+ var mask uint32 = runtime.Sigrecv()
+ for sig := uint(0); sig < 32; sig++ {
+ if mask&(1<<sig) != 0 {
+ ch <- UnixSignal(sig)
+ }
+ }
+ }
+}
+
+func init() {
+ runtime.Siginit()
+ ch := make(chan Signal) // Done here so Incoming can have type <-chan Signal
+ Incoming = ch
+ go process(ch)
+}
diff --git a/libgo/go/os/signal/signal_test.go b/libgo/go/os/signal/signal_test.go
new file mode 100644
index 000000000..f2679f14d
--- /dev/null
+++ b/libgo/go/os/signal/signal_test.go
@@ -0,0 +1,19 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package signal
+
+import (
+ "syscall"
+ "testing"
+)
+
+func TestSignal(t *testing.T) {
+ // Send this process a SIGHUP.
+ syscall.Syscall(syscall.SYS_KILL, uintptr(syscall.Getpid()), syscall.SIGHUP, 0)
+
+ if sig := (<-Incoming).(UnixSignal); sig != SIGHUP {
+ t.Errorf("signal was %v, want %v", sig, SIGHUP)
+ }
+}
diff --git a/libgo/go/os/stat.go b/libgo/go/os/stat.go
new file mode 100644
index 000000000..d6c7a54ed
--- /dev/null
+++ b/libgo/go/os/stat.go
@@ -0,0 +1,40 @@
+// 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.
+
+// AMD64, Linux
+
+package os
+
+import syscall "syscall"
+
+func isSymlink(stat *syscall.Stat_t) bool {
+ return stat.Mode & syscall.S_IFMT == syscall.S_IFLNK
+}
+
+func fileInfoFromStat(name string, fi *FileInfo, lstat, stat *syscall.Stat_t) *FileInfo {
+ fi.Dev = uint64(stat.Dev)
+ fi.Ino = uint64(stat.Ino)
+ fi.Nlink = uint64(stat.Nlink)
+ fi.Mode = uint32(stat.Mode)
+ fi.Uid = int(stat.Uid)
+ fi.Gid = int(stat.Gid)
+ fi.Rdev = uint64(stat.Rdev)
+ fi.Size = int64(stat.Size)
+ fi.Blksize = int64(stat.Blksize)
+ fi.Blocks = int64(stat.Blocks)
+ fi.Atime_ns = int64(stat.Atime.Sec)*1e9 + int64(stat.Atime.Nsec)
+ fi.Mtime_ns = int64(stat.Mtime.Sec)*1e9 + int64(stat.Mtime.Nsec)
+ fi.Ctime_ns = int64(stat.Ctime.Sec)*1e9 + int64(stat.Atime.Nsec)
+ for i := len(name)-1; i >= 0; i-- {
+ if name[i] == '/' {
+ name = name[i+1:]
+ break
+ }
+ }
+ fi.Name = name
+ if isSymlink(lstat) && !isSymlink(stat) {
+ fi.FollowedSymlink = true
+ }
+ return fi
+}
diff --git a/libgo/go/os/sys_bsd.go b/libgo/go/os/sys_bsd.go
new file mode 100644
index 000000000..188993b69
--- /dev/null
+++ b/libgo/go/os/sys_bsd.go
@@ -0,0 +1,19 @@
+// 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.
+
+// os code shared between *BSD systems including OS X (Darwin)
+// and FreeBSD.
+
+package os
+
+import "syscall"
+
+func Hostname() (name string, err Error) {
+ var errno int
+ name, errno = syscall.Sysctl("kern.hostname")
+ if errno != 0 {
+ return "", NewSyscallError("sysctl kern.hostname", errno)
+ }
+ return name, nil
+}
diff --git a/libgo/go/os/sys_linux.go b/libgo/go/os/sys_linux.go
new file mode 100644
index 000000000..b82d295d3
--- /dev/null
+++ b/libgo/go/os/sys_linux.go
@@ -0,0 +1,28 @@
+// 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.
+
+// Linux-specific
+
+package os
+
+
+// Hostname returns the host name reported by the kernel.
+func Hostname() (name string, err Error) {
+ f, err := Open("/proc/sys/kernel/hostname", O_RDONLY, 0)
+ if err != nil {
+ return "", err
+ }
+ defer f.Close()
+
+ var buf [512]byte // Enough for a DNS name.
+ n, err := f.Read(buf[0:])
+ if err != nil {
+ return "", err
+ }
+
+ if n > 0 && buf[n-1] == '\n' {
+ n--
+ }
+ return string(buf[0:n]), nil
+}
diff --git a/libgo/go/os/sys_uname.go b/libgo/go/os/sys_uname.go
new file mode 100644
index 000000000..ea46ad01e
--- /dev/null
+++ b/libgo/go/os/sys_uname.go
@@ -0,0 +1,25 @@
+// Copyright 2011 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.
+
+// For systems which only store the hostname in uname (Solaris).
+
+package os
+
+import "syscall"
+
+func Hostname() (name string, err Error) {
+ var u syscall.Utsname
+ if errno := syscall.Uname(&u); errno != 0 {
+ 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/time.go b/libgo/go/os/time.go
new file mode 100644
index 000000000..380345f1b
--- /dev/null
+++ b/libgo/go/os/time.go
@@ -0,0 +1,20 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+import "syscall"
+
+
+// Time returns the current time, in whole seconds and
+// fractional nanoseconds, plus an Error if any. The current
+// time is thus 1e9*sec+nsec, in nanoseconds. The zero of
+// time is the Unix epoch.
+func Time() (sec int64, nsec int64, err Error) {
+ var tv syscall.Timeval
+ if errno := syscall.Gettimeofday(&tv); errno != 0 {
+ return 0, 0, NewSyscallError("gettimeofday", errno)
+ }
+ return int64(tv.Sec), int64(tv.Usec) * 1000, err
+}
diff --git a/libgo/go/os/types.go b/libgo/go/os/types.go
new file mode 100644
index 000000000..79f6e9d49
--- /dev/null
+++ b/libgo/go/os/types.go
@@ -0,0 +1,56 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+import "syscall"
+
+// An operating-system independent representation of Unix data structures.
+// OS-specific routines in this directory convert the OS-local versions to these.
+
+// Getpagesize returns the underlying system's memory page size.
+func Getpagesize() int { return syscall.Getpagesize() }
+
+// A FileInfo describes a file and is returned by Stat, Fstat, and Lstat
+type FileInfo struct {
+ Dev uint64 // device number of file system holding file.
+ Ino uint64 // inode number.
+ Nlink uint64 // number of hard links.
+ Mode uint32 // permission and mode bits.
+ Uid int // user id of owner.
+ Gid int // group id of owner.
+ Rdev uint64 // device type for special file.
+ Size int64 // length in bytes.
+ Blksize int64 // size of blocks, in bytes.
+ Blocks int64 // number of blocks allocated for file.
+ Atime_ns int64 // access time; nanoseconds since epoch.
+ Mtime_ns int64 // modified time; nanoseconds since epoch.
+ Ctime_ns int64 // status change time; nanoseconds since epoch.
+ Name string // name of file as presented to Open.
+ FollowedSymlink bool // followed a symlink to get this information
+}
+
+// IsFifo reports whether the FileInfo describes a FIFO file.
+func (f *FileInfo) IsFifo() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFIFO }
+
+// IsChar reports whether the FileInfo describes a character special file.
+func (f *FileInfo) IsChar() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFCHR }
+
+// IsDirectory reports whether the FileInfo describes a directory.
+func (f *FileInfo) IsDirectory() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFDIR }
+
+// IsBlock reports whether the FileInfo describes a block special file.
+func (f *FileInfo) IsBlock() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFBLK }
+
+// IsRegular reports whether the FileInfo describes a regular file.
+func (f *FileInfo) IsRegular() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFREG }
+
+// IsSymlink reports whether the FileInfo describes a symbolic link.
+func (f *FileInfo) IsSymlink() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFLNK }
+
+// IsSocket reports whether the FileInfo describes a socket.
+func (f *FileInfo) IsSocket() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFSOCK }
+
+// Permission returns the file permission bits.
+func (f *FileInfo) Permission() uint32 { return f.Mode & 0777 }