summaryrefslogtreecommitdiff
path: root/src/pkg/os
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-01-17 12:40:45 +0100
committerOndřej Surý <ondrej@sury.org>2011-01-17 12:40:45 +0100
commit3e45412327a2654a77944249962b3652e6142299 (patch)
treebc3bf69452afa055423cbe0c5cfa8ca357df6ccf /src/pkg/os
parentc533680039762cacbc37db8dc7eed074c3e497be (diff)
downloadgolang-3e45412327a2654a77944249962b3652e6142299.tar.gz
Imported Upstream version 2011.01.12upstream/2011.01.12
Diffstat (limited to 'src/pkg/os')
-rw-r--r--src/pkg/os/Makefile9
-rw-r--r--src/pkg/os/dir_darwin.go12
-rw-r--r--src/pkg/os/dir_freebsd.go12
-rw-r--r--src/pkg/os/dir_linux.go12
-rw-r--r--src/pkg/os/dir_nacl.go77
-rw-r--r--src/pkg/os/env.go119
-rw-r--r--src/pkg/os/env_test.go59
-rw-r--r--[-rwxr-xr-x]src/pkg/os/env_unix.go83
-rw-r--r--[-rwxr-xr-x]src/pkg/os/env_windows.go98
-rw-r--r--src/pkg/os/exec.go23
-rw-r--r--src/pkg/os/file.go45
-rw-r--r--src/pkg/os/file_unix.go10
-rw-r--r--src/pkg/os/file_windows.go39
-rw-r--r--src/pkg/os/inotify/Makefile14
-rw-r--r--src/pkg/os/inotify/inotify_linux.go291
-rw-r--r--src/pkg/os/inotify/inotify_linux_test.go99
-rw-r--r--src/pkg/os/os_test.go420
-rw-r--r--src/pkg/os/path.go8
-rw-r--r--src/pkg/os/path_test.go79
-rw-r--r--src/pkg/os/signal/Makefile2
-rw-r--r--src/pkg/os/stat_nacl.go38
-rw-r--r--src/pkg/os/stat_windows.go10
-rw-r--r--src/pkg/os/sys_nacl.go7
-rw-r--r--src/pkg/os/types.go2
24 files changed, 1121 insertions, 447 deletions
diff --git a/src/pkg/os/Makefile b/src/pkg/os/Makefile
index 45954bbeb..f6caf084c 100644
--- a/src/pkg/os/Makefile
+++ b/src/pkg/os/Makefile
@@ -2,13 +2,13 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
-include ../../Make.$(GOARCH)
+include ../../Make.inc
TARG=os
GOFILES=\
dir_$(GOOS).go\
- env.go\
error.go\
+ env.go\
exec.go\
file.go\
getwd.go\
@@ -33,11 +33,6 @@ GOFILES_linux=\
file_unix.go\
sys_linux.go\
-GOFILES_nacl=\
- env_unix.go\
- file_unix.go\
- sys_nacl.go\
-
GOFILES_windows=\
env_windows.go\
file_windows.go\
diff --git a/src/pkg/os/dir_darwin.go b/src/pkg/os/dir_darwin.go
index 7917daec6..861bcef27 100644
--- a/src/pkg/os/dir_darwin.go
+++ b/src/pkg/os/dir_darwin.go
@@ -58,21 +58,13 @@ func (file *File) Readdirnames(count int) (names []string, err Error) {
if dirent.Ino == 0 { // File absent in directory.
continue
}
- bytes := (*[len(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0]))
+ bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
var name = string(bytes[0:dirent.Namlen])
if name == "." || name == ".." { // Useless names
continue
}
count--
- if len(names) == cap(names) {
- nnames := make([]string, len(names), 2*len(names))
- for i := 0; i < len(names); i++ {
- nnames[i] = names[i]
- }
- names = nnames
- }
- names = names[0 : len(names)+1]
- names[len(names)-1] = name
+ names = append(names, name)
}
}
return names, nil
diff --git a/src/pkg/os/dir_freebsd.go b/src/pkg/os/dir_freebsd.go
index 7a0290ac6..2ebe368a6 100644
--- a/src/pkg/os/dir_freebsd.go
+++ b/src/pkg/os/dir_freebsd.go
@@ -53,21 +53,13 @@ func (file *File) Readdirnames(count int) (names []string, err Error) {
if dirent.Fileno == 0 { // File absent in directory.
continue
}
- bytes := (*[len(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0]))
+ bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
var name = string(bytes[0:dirent.Namlen])
if name == "." || name == ".." { // Useless names
continue
}
count--
- if len(names) == cap(names) {
- nnames := make([]string, len(names), 2*len(names))
- for i := 0; i < len(names); i++ {
- nnames[i] = names[i]
- }
- names = nnames
- }
- names = names[0 : len(names)+1]
- names[len(names)-1] = name
+ names = append(names, name)
}
}
return names, nil
diff --git a/src/pkg/os/dir_linux.go b/src/pkg/os/dir_linux.go
index 84f87007e..09aad6367 100644
--- a/src/pkg/os/dir_linux.go
+++ b/src/pkg/os/dir_linux.go
@@ -56,21 +56,13 @@ func (file *File) Readdirnames(count int) (names []string, err Error) {
if dirent.Ino == 0 { // File absent in directory.
continue
}
- bytes := (*[len(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0]))
+ bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0]))
var name = string(bytes[0:clen(bytes[0:])])
if name == "." || name == ".." { // Useless names
continue
}
count--
- if len(names) == cap(names) {
- nnames := make([]string, len(names), 2*len(names))
- for i := 0; i < len(names); i++ {
- nnames[i] = names[i]
- }
- names = nnames
- }
- names = names[0 : len(names)+1]
- names[len(names)-1] = name
+ names = append(names, name)
}
}
return names, nil
diff --git a/src/pkg/os/dir_nacl.go b/src/pkg/os/dir_nacl.go
deleted file mode 100644
index e693794f0..000000000
--- a/src/pkg/os/dir_nacl.go
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package os
-
-import (
- "syscall"
- "unsafe"
-)
-
-const (
- blockSize = 4096 // TODO(r): use statfs
-)
-
-func clen(n []byte) int {
- for i := 0; i < len(n); i++ {
- if n[i] == 0 {
- return i
- }
- }
- return len(n)
-}
-
-func (file *File) Readdirnames(count int) (names []string, err Error) {
- // If this file has no dirinfo, create one.
- if file.dirinfo == nil {
- file.dirinfo = new(dirInfo)
- // The buffer must be at least a block long.
- // TODO(r): use fstatfs to find fs block size.
- file.dirinfo.buf = make([]byte, blockSize)
- }
- d := file.dirinfo
- size := count
- if size < 0 {
- size = 100
- }
- names = make([]string, 0, size) // Empty with room to grow.
- for count != 0 {
- // Refill the buffer if necessary
- if d.bufp >= d.nbuf {
- var errno int
- d.nbuf, errno = syscall.Getdents(file.fd, d.buf)
- if errno != 0 {
- return names, NewSyscallError("getdents", errno)
- }
- if d.nbuf <= 0 {
- break // EOF
- }
- d.bufp = 0
- }
- // Drain the buffer
- for count != 0 && d.bufp < d.nbuf {
- dirent := (*syscall.Dirent)(unsafe.Pointer(&d.buf[d.bufp]))
- d.bufp += int(dirent.Reclen)
- if dirent.Ino == 0 { // File absent in directory.
- continue
- }
- bytes := (*[len(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0]))
- var name = string(bytes[0:clen(bytes)])
- if name == "." || name == ".." { // Useless names
- continue
- }
- count--
- if len(names) == cap(names) {
- nnames := make([]string, len(names), 2*len(names))
- for i := 0; i < len(names); i++ {
- nnames[i] = names[i]
- }
- names = nnames
- }
- names = names[0 : len(names)+1]
- names[len(names)-1] = name
- }
- }
- return names, nil
-}
diff --git a/src/pkg/os/env.go b/src/pkg/os/env.go
index bdd2ac293..3a6d79dd0 100644
--- a/src/pkg/os/env.go
+++ b/src/pkg/os/env.go
@@ -1,86 +1,73 @@
-// Copyright 2009 The Go Authors. All rights reserved.
+// 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.
-// Environment variables.
+// General environment variables.
package os
-import (
- "once"
-)
-
-// ENOENV is the Error indicating that an environment variable does not exist.
-var ENOENV = NewError("no such environment variable")
-
-var env map[string]string
-
-
-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
- }
+// 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:]
}
-// 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
+// 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)
}
-// 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
+// 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
}
- env[key] = value
- return nil
+ return false
}
-// Clearenv deletes all environment variables.
-func Clearenv() {
- once.Do(copyenv) // prevent copyenv in Getenv/Setenv
- env = make(map[string]string)
+// 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'
}
-// 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++
+// 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 a[0:i]
+ return s[:i], i
}
diff --git a/src/pkg/os/env_test.go b/src/pkg/os/env_test.go
new file mode 100644
index 000000000..04ff39072
--- /dev/null
+++ b/src/pkg/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/src/pkg/os/env_unix.go b/src/pkg/os/env_unix.go
index 0c13bda0e..e7e1c3b90 100755..100644
--- a/src/pkg/os/env_unix.go
+++ b/src/pkg/os/env_unix.go
@@ -6,10 +6,87 @@
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.
-// On Unix-like systems, it uses the environment variable $TMPDIR
-// or, if that is empty, /tmp.
-// On Windows systems, it uses the Windows GetTempPath API.
func TempDir() string {
dir := Getenv("TMPDIR")
if dir == "" {
diff --git a/src/pkg/os/env_windows.go b/src/pkg/os/env_windows.go
index 7d5b007c9..d2b159dfb 100755..100644
--- a/src/pkg/os/env_windows.go
+++ b/src/pkg/os/env_windows.go
@@ -9,8 +9,92 @@ 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)
@@ -27,3 +111,17 @@ func TempDir() string {
}
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/src/pkg/os/exec.go b/src/pkg/os/exec.go
index d55acbaa7..501ebc270 100644
--- a/src/pkg/os/exec.go
+++ b/src/pkg/os/exec.go
@@ -8,14 +8,17 @@ import (
"syscall"
)
-// ForkExec forks the current process and invokes Exec with the file, arguments,
-// and environment specified by argv0, argv, and envv. It returns the process
+// 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(argv0 string, argv []string, envv []string, dir string, fd []*File) (pid int, err Error) {
+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 {
@@ -26,24 +29,24 @@ func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []*File
}
}
- p, e := syscall.ForkExec(argv0, argv, envv, dir, intfd)
+ p, e := syscall.ForkExec(name, argv, envv, dir, intfd)
if e != 0 {
- return 0, &PathError{"fork/exec", argv0, Errno(e)}
+ return 0, &PathError{"fork/exec", name, Errno(e)}
}
return p, nil
}
-// Exec replaces the current process with an execution of the program
-// named by argv0, with arguments argv and environment envv.
+// 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(argv0 string, argv []string, envv []string) Error {
+func Exec(name string, argv []string, envv []string) Error {
if envv == nil {
envv = Environ()
}
- e := syscall.Exec(argv0, argv, envv)
+ e := syscall.Exec(name, argv, envv)
if e != 0 {
- return &PathError{"exec", argv0, Errno(e)}
+ return &PathError{"exec", name, Errno(e)}
}
return nil
}
diff --git a/src/pkg/os/file.go b/src/pkg/os/file.go
index be2a30693..3f73f1dff 100644
--- a/src/pkg/os/file.go
+++ b/src/pkg/os/file.go
@@ -46,19 +46,19 @@ var (
// Flags to Open wrapping those of the underlying system. Not all flags
// may be implemented on a given system.
const (
- O_RDONLY = syscall.O_RDONLY // open the file read-only.
- O_WRONLY = syscall.O_WRONLY // open the file write-only.
- O_RDWR = syscall.O_RDWR // open the file read-write.
- O_APPEND = syscall.O_APPEND // open the file append-only.
- O_ASYNC = syscall.O_ASYNC // generate a signal when I/O is available.
- O_CREAT = syscall.O_CREAT // create a new file if none exists.
- O_EXCL = syscall.O_EXCL // used with O_CREAT, file must not exist
- O_NOCTTY = syscall.O_NOCTTY // do not make file the controlling tty.
- O_NONBLOCK = syscall.O_NONBLOCK // open in non-blocking mode.
- O_NDELAY = O_NONBLOCK // synonym for O_NONBLOCK
- O_SYNC = syscall.O_SYNC // open for synchronous I/O.
- O_TRUNC = syscall.O_TRUNC // if possible, truncate file when opened.
- O_CREATE = O_CREAT // create a new file if none exists.
+ 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
@@ -208,7 +208,7 @@ func Pipe() (r *File, w *File, err Error) {
// Mkdir creates a new directory with the specified name and permission bits.
// It returns an error, if any.
-func Mkdir(name string, perm int) Error {
+func Mkdir(name string, perm uint32) Error {
e := syscall.Mkdir(name, perm)
if e != 0 {
return &PathError{"mkdir", name, Errno(e)}
@@ -358,7 +358,7 @@ func Rename(oldname, newname string) Error {
// 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 int) Error {
+func Chmod(name string, mode uint32) Error {
if e := syscall.Chmod(name, mode); e != 0 {
return &PathError{"chmod", name, Errno(e)}
}
@@ -366,7 +366,7 @@ func Chmod(name string, mode int) Error {
}
// Chmod changes the mode of the file to mode.
-func (f *File) Chmod(mode int) Error {
+func (f *File) Chmod(mode uint32) Error {
if e := syscall.Fchmod(f.fd, mode); e != 0 {
return &PathError{"chmod", f.name, Errno(e)}
}
@@ -408,6 +408,19 @@ func (f *File) Truncate(size int64) Error {
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.
//
diff --git a/src/pkg/os/file_unix.go b/src/pkg/os/file_unix.go
index 6cf266140..df5894459 100644
--- a/src/pkg/os/file_unix.go
+++ b/src/pkg/os/file_unix.go
@@ -2,8 +2,6 @@
// 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 (
@@ -18,10 +16,14 @@ type dirInfo struct {
bufp int // location of next record in buf.
}
+// 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 int) (file *File, err Error) {
+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)}
@@ -66,7 +68,7 @@ func (file *File) Stat() (fi *FileInfo, err Error) {
// 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 Stat, in directory order. Subsequent calls on the same file will yield
+// 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.
diff --git a/src/pkg/os/file_windows.go b/src/pkg/os/file_windows.go
index 4f7acbb08..bf710bb67 100644
--- a/src/pkg/os/file_windows.go
+++ b/src/pkg/os/file_windows.go
@@ -2,8 +2,6 @@
// 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 (
@@ -17,9 +15,11 @@ type dirInfo struct {
usefirststat bool
}
+const DevNull = "NUL"
+
func (file *File) isdir() bool { return file != nil && file.dirinfo != nil }
-func openFile(name string, flag int, perm int) (file *File, err Error) {
+func openFile(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)}
@@ -49,16 +49,29 @@ func openDir(name string) (file *File, err Error) {
// 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 int) (file *File, err Error) {
+func Open(name string, flag int, perm uint32) (file *File, err Error) {
// TODO(brainman): not sure about my logic of assuming it is dir first, then fall back to file
r, e := openDir(name)
if e == nil {
+ if flag&O_WRONLY != 0 || flag&O_RDWR != 0 {
+ r.Close()
+ return nil, &PathError{"open", name, EISDIR}
+ }
return r, nil
}
r, e = openFile(name, flag, perm)
if e == nil {
return r, nil
}
+ // Imitating Unix behavior by replacing syscall.ERROR_PATH_NOT_FOUND with
+ // os.ENOTDIR. Not sure if we should go into that.
+ if e2, ok := e.(*PathError); ok {
+ if e3, ok := e2.Error.(Errno); ok {
+ if e3 == Errno(syscall.ERROR_PATH_NOT_FOUND) {
+ return nil, &PathError{"open", name, ENOTDIR}
+ }
+ }
+ }
return nil, e
}
@@ -108,11 +121,17 @@ func (file *File) Stat() (fi *FileInfo, err Error) {
// 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 Stat, in directory order. Subsequent calls on the same file will yield
+// 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) {
+ if file == nil || file.fd < 0 {
+ return nil, EINVAL
+ }
+ if !file.isdir() {
+ return nil, &PathError{"Readdir", file.name, ENOTDIR}
+ }
di := file.dirinfo
size := count
if size < 0 {
@@ -138,15 +157,7 @@ func (file *File) Readdir(count int) (fi []FileInfo, err Error) {
continue
}
count--
- if len(fi) == cap(fi) {
- nfi := make([]FileInfo, len(fi), 2*len(fi))
- for i := 0; i < len(fi); i++ {
- nfi[i] = fi[i]
- }
- fi = nfi
- }
- fi = fi[0 : len(fi)+1]
- fi[len(fi)-1] = f
+ fi = append(fi, f)
}
return fi, nil
}
diff --git a/src/pkg/os/inotify/Makefile b/src/pkg/os/inotify/Makefile
new file mode 100644
index 000000000..90e18da57
--- /dev/null
+++ b/src/pkg/os/inotify/Makefile
@@ -0,0 +1,14 @@
+# 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.
+
+include ../../../Make.inc
+
+TARG=os/inotify
+
+GOFILES_linux=\
+ inotify_linux.go\
+
+GOFILES+=$(GOFILES_$(GOOS))
+
+include ../../../Make.pkg
diff --git a/src/pkg/os/inotify/inotify_linux.go b/src/pkg/os/inotify/inotify_linux.go
new file mode 100644
index 000000000..1e74c7fbc
--- /dev/null
+++ b/src/pkg/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/src/pkg/os/inotify/inotify_linux_test.go b/src/pkg/os/inotify/inotify_linux_test.go
new file mode 100644
index 000000000..332edcb64
--- /dev/null
+++ b/src/pkg/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/src/pkg/os/os_test.go b/src/pkg/os/os_test.go
index 6827c3f60..49b58c83c 100644
--- a/src/pkg/os/os_test.go
+++ b/src/pkg/os/os_test.go
@@ -11,13 +11,14 @@ import (
"io/ioutil"
. "os"
"strings"
+ "syscall"
"testing"
)
var dot = []string{
"dir_darwin.go",
"dir_linux.go",
- "env.go",
+ "env_unix.go",
"error.go",
"file.go",
"os_test.go",
@@ -27,12 +28,36 @@ var dot = []string{
"stat_linux.go",
}
-var etc = []string{
- "group",
- "hosts",
- "passwd",
+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()
@@ -54,22 +79,52 @@ func size(name string, t *testing.T) int64 {
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) {
- dir, err := Stat("/etc/passwd")
+ path := sfdir + "/" + sfname
+ dir, err := Stat(path)
if err != nil {
t.Fatal("stat failed:", err)
}
- if dir.Name != "passwd" {
- t.Error("name should be passwd; is", dir.Name)
+ if !equal(sfname, dir.Name) {
+ t.Error("name should be ", sfname, "; is", dir.Name)
}
- filesize := size("/etc/passwd", t)
+ filesize := size(path, t)
if dir.Size != filesize {
t.Error("size should be", filesize, "; is", dir.Size)
}
}
func TestFstat(t *testing.T) {
- file, err1 := Open("/etc/passwd", O_RDONLY, 0)
+ path := sfdir + "/" + sfname
+ file, err1 := Open(path, O_RDONLY, 0)
defer file.Close()
if err1 != nil {
t.Fatal("open failed:", err1)
@@ -78,24 +133,25 @@ func TestFstat(t *testing.T) {
if err2 != nil {
t.Fatal("fstat failed:", err2)
}
- if dir.Name != "passwd" {
- t.Error("name should be passwd; is", dir.Name)
+ if !equal(sfname, dir.Name) {
+ t.Error("name should be ", sfname, "; is", dir.Name)
}
- filesize := size("/etc/passwd", t)
+ filesize := size(path, t)
if dir.Size != filesize {
t.Error("size should be", filesize, "; is", dir.Size)
}
}
func TestLstat(t *testing.T) {
- dir, err := Lstat("/etc/passwd")
+ path := sfdir + "/" + sfname
+ dir, err := Lstat(path)
if err != nil {
t.Fatal("lstat failed:", err)
}
- if dir.Name != "passwd" {
- t.Error("name should be passwd; is", dir.Name)
+ if !equal(sfname, dir.Name) {
+ t.Error("name should be ", sfname, "; is", dir.Name)
}
- filesize := size("/etc/passwd", t)
+ filesize := size(path, t)
if dir.Size != filesize {
t.Error("size should be", filesize, "; is", dir.Size)
}
@@ -109,7 +165,7 @@ func testReaddirnames(dir string, contents []string, t *testing.T) {
}
s, err2 := file.Readdirnames(-1)
if err2 != nil {
- t.Fatalf("readdirnames %q failed: %v", err2)
+ t.Fatalf("readdirnames %q failed: %v", dir, err2)
}
for _, m := range contents {
found := false
@@ -117,7 +173,7 @@ func testReaddirnames(dir string, contents []string, t *testing.T) {
if n == "." || n == ".." {
t.Errorf("got %s in directory", n)
}
- if m == n {
+ if equal(m, n) {
if found {
t.Error("present twice:", m)
}
@@ -143,7 +199,7 @@ func testReaddir(dir string, contents []string, t *testing.T) {
for _, m := range contents {
found := false
for _, n := range s {
- if m == n.Name {
+ if equal(m, n.Name) {
if found {
t.Error("present twice:", m)
}
@@ -158,12 +214,12 @@ func testReaddir(dir string, contents []string, t *testing.T) {
func TestReaddirnames(t *testing.T) {
testReaddirnames(".", dot, t)
- testReaddirnames("/etc", etc, t)
+ testReaddirnames(sysdir.name, sysdir.files, t)
}
func TestReaddir(t *testing.T) {
testReaddir(".", dot, t)
- testReaddir("/etc", etc, t)
+ testReaddir(sysdir.name, sysdir.files, t)
}
// Read the directory one entry at a time.
@@ -187,7 +243,11 @@ func smallReaddirnames(file *File, length int, t *testing.T) []string {
// 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) {
- dir := "/usr/bin" // big directory that doesn't change often.
+ // 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 {
@@ -204,12 +264,16 @@ func TestReaddirnamesOneAtATime(t *testing.T) {
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 %q mismatch: %v", 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)
@@ -239,6 +303,10 @@ func TestHardLink(t *testing.T) {
}
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)
@@ -280,7 +348,7 @@ func TestSymLink(t *testing.T) {
t.Fatalf("stat %q failed: %v", from, err)
}
if !fromstat.FollowedSymlink {
- t.Fatalf("stat %q did not follow symlink")
+ t.Fatalf("stat %q did not follow symlink", from)
}
s, err := Readlink(from)
if err != nil {
@@ -297,6 +365,10 @@ func TestSymLink(t *testing.T) {
}
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
@@ -338,11 +410,24 @@ func TestRename(t *testing.T) {
}
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)
}
- pid, err := ForkExec("/bin/pwd", []string{"pwd"}, nil, "/", []*File{nil, w, Stderr})
+ 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)
}
@@ -351,9 +436,9 @@ func TestForkExec(t *testing.T) {
var b bytes.Buffer
io.Copy(&b, r)
output := b.String()
- expect := "/\n"
if output != expect {
- t.Errorf("exec /bin/pwd returned %q wanted %q", output, expect)
+ args[0] = cmd
+ t.Errorf("exec %q returned %q wanted %q", strings.Join(args, " "), output, expect)
}
Wait(pid, 0)
}
@@ -369,25 +454,23 @@ func checkMode(t *testing.T, path string, mode uint32) {
}
func TestChmod(t *testing.T) {
- MkdirAll("_obj", 0777)
- const Path = "_obj/_TestChmod_"
- fd, err := Open(Path, O_WRONLY|O_CREAT, 0666)
- if err != nil {
- t.Fatalf("create %s: %s", Path, err)
+ // 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(Path, 0456); err != nil {
- t.Fatalf("chmod %s 0456: %s", Path, err)
+ if err := Chmod(f.Name(), 0456); err != nil {
+ t.Fatalf("chmod %s 0456: %s", f.Name(), err)
}
- checkMode(t, Path, 0456)
+ checkMode(t, f.Name(), 0456)
- if err = fd.Chmod(0123); err != nil {
- t.Fatalf("fchmod %s 0123: %s", Path, err)
+ if err := f.Chmod(0123); err != nil {
+ t.Fatalf("chmod %s 0123: %s", f.Name(), err)
}
- checkMode(t, Path, 0123)
-
- fd.Close()
- Remove(Path)
+ checkMode(t, f.Name(), 0123)
}
func checkUidGid(t *testing.T, path string, uid, gid int) {
@@ -404,31 +487,30 @@ func checkUidGid(t *testing.T, path string, uid, gid int) {
}
func TestChown(t *testing.T) {
- // Use /tmp, not _obj, to make sure we're on a local file system,
+ // 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. If _obj is on NFS, the Getgroups groups are
+ // on the file. On NFS, the Getgroups groups are
// basically useless.
-
- const Path = "/tmp/_TestChown_"
- fd, err := Open(Path, O_WRONLY|O_CREAT, 0666)
+ f := newFile("TestChown", t)
+ defer Remove(f.Name())
+ defer f.Close()
+ dir, err := f.Stat()
if err != nil {
- t.Fatalf("create %s: %s", Path, err)
+ t.Fatalf("stat %s: %s", f.Name(), err)
}
- dir, err := fd.Stat()
- if err != nil {
- t.Fatalf("fstat %s: %s", Path, err)
- }
- defer fd.Close()
- defer Remove(Path)
// 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(Path, -1, gid); err != nil {
- t.Fatalf("chown %s -1 %d: %s", Path, gid, err)
+ if err = Chown(f.Name(), -1, gid); err != nil {
+ t.Fatalf("chown %s -1 %d: %s", f.Name(), gid, err)
}
- checkUidGid(t, Path, dir.Uid, gid)
+ checkUidGid(t, f.Name(), dir.Uid, gid)
// Then try all the auxiliary groups.
groups, err := Getgroups()
@@ -437,77 +519,74 @@ func TestChown(t *testing.T) {
}
t.Log("groups: ", groups)
for _, g := range groups {
- if err = Chown(Path, -1, g); err != nil {
- t.Fatalf("chown %s -1 %d: %s", Path, g, err)
+ if err = Chown(f.Name(), -1, g); err != nil {
+ t.Fatalf("chown %s -1 %d: %s", f.Name(), g, err)
}
- checkUidGid(t, Path, dir.Uid, g)
+ checkUidGid(t, f.Name(), dir.Uid, g)
// change back to gid to test fd.Chown
- if err = fd.Chown(-1, gid); err != nil {
- t.Fatalf("fchown %s -1 %d: %s", Path, gid, err)
+ if err = f.Chown(-1, gid); err != nil {
+ t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err)
}
- checkUidGid(t, Path, dir.Uid, gid)
+ checkUidGid(t, f.Name(), dir.Uid, gid)
}
}
-func checkSize(t *testing.T, path string, size int64) {
- dir, err := Stat(path)
+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", path, size, err)
+ 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", path, dir.Size, size)
+ t.Errorf("Stat %q: size %d want %d", f.Name(), dir.Size, size)
}
}
func TestTruncate(t *testing.T) {
- MkdirAll("_obj", 0777)
- const Path = "_obj/_TestTruncate_"
- fd, err := Open(Path, O_WRONLY|O_CREAT, 0666)
- if err != nil {
- t.Fatalf("create %s: %s", Path, err)
- }
-
- checkSize(t, Path, 0)
- fd.Write([]byte("hello, world\n"))
- checkSize(t, Path, 13)
- fd.Truncate(10)
- checkSize(t, Path, 10)
- fd.Truncate(1024)
- checkSize(t, Path, 1024)
- fd.Truncate(0)
- checkSize(t, Path, 0)
- fd.Write([]byte("surprise!"))
- checkSize(t, Path, 13+9) // wrote at offset past where hello, world was.
- fd.Close()
- Remove(Path)
-}
-
+ 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) {
- MkdirAll("_obj", 0777)
- const Path = "_obj/_TestChtimes_"
- fd, err := Open(Path, O_WRONLY|O_CREAT, 0666)
- if err != nil {
- t.Fatalf("create %s: %s", Path, err)
- }
- fd.Write([]byte("hello, world\n"))
- fd.Close()
+ f := newFile("TestChtimes", t)
+ defer Remove(f.Name())
+ defer f.Close()
- preStat, err := Stat(Path)
+ f.Write([]byte("hello, world\n"))
+ f.Close()
+
+ preStat, err := Stat(f.Name())
if err != nil {
- t.Fatalf("Stat %s: %s", Path, err)
+ t.Fatalf("Stat %s: %s", f.Name(), err)
}
// Move access and modification time back a second
const OneSecond = 1e9 // in nanoseconds
- err = Chtimes(Path, preStat.Atime_ns-OneSecond, preStat.Mtime_ns-OneSecond)
+ err = Chtimes(f.Name(), preStat.Atime_ns-OneSecond, preStat.Mtime_ns-OneSecond)
if err != nil {
- t.Fatalf("Chtimes %s: %s", Path, err)
+ t.Fatalf("Chtimes %s: %s", f.Name(), err)
}
- postStat, err := Stat(Path)
+ postStat, err := Stat(f.Name())
if err != nil {
- t.Fatalf("second Stat %s: %s", Path, err)
+ t.Fatalf("second Stat %s: %s", f.Name(), err)
}
if postStat.Atime_ns >= preStat.Atime_ns {
@@ -521,11 +600,13 @@ func TestChtimes(t *testing.T) {
preStat.Mtime_ns,
postStat.Mtime_ns)
}
-
- Remove(Path)
}
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)
@@ -586,10 +667,9 @@ func TestTime(t *testing.T) {
}
func TestSeek(t *testing.T) {
- f, err := Open("_obj/seektest", O_CREAT|O_RDWR|O_TRUNC, 0666)
- if err != nil {
- t.Fatalf("open _obj/seektest: %s", err)
- }
+ f := newFile("TestSeek", t)
+ defer Remove(f.Name())
+ defer f.Close()
const data = "hello, world\n"
io.WriteString(f, data)
@@ -600,14 +680,14 @@ func TestSeek(t *testing.T) {
out int64
}
var tests = []test{
- test{0, 1, int64(len(data))},
- test{0, 0, 0},
- test{5, 0, 5},
- test{0, 2, int64(len(data))},
- test{0, 0, 0},
- test{-1, 2, int64(len(data)) - 1},
- test{1 << 33, 0, 1 << 33},
- test{1 << 33, 2, 1<<33 + int64(len(data))},
+ {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)
@@ -620,30 +700,29 @@ func TestSeek(t *testing.T) {
t.Errorf("#%d: Seek(%v, %v) = %v, %v want %v, nil", i, tt.in, tt.whence, off, err, tt.out)
}
}
- f.Close()
}
type openErrorTest struct {
path string
mode int
- error string
+ error Error
}
var openErrorTests = []openErrorTest{
- openErrorTest{
- "/etc/no-such-file",
+ {
+ sfdir + "/no-such-file",
O_RDONLY,
- "open /etc/no-such-file: no such file or directory",
+ ENOENT,
},
- openErrorTest{
- "/etc",
+ {
+ sfdir,
O_WRONLY,
- "open /etc: is a directory",
+ EISDIR,
},
- openErrorTest{
- "/etc/passwd/group",
+ {
+ sfdir + "/" + sfname + "/no-such-file",
O_WRONLY,
- "open /etc/passwd/group: not a directory",
+ ENOTDIR,
},
}
@@ -655,8 +734,12 @@ func TestOpenError(t *testing.T) {
f.Close()
continue
}
- if s := err.String(); s != tt.error {
- t.Errorf("Open(%q, %d) = _, %q; want %q", tt.path, tt.mode, s, tt.error)
+ 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())
}
}
}
@@ -689,6 +772,10 @@ 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.
+ 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
@@ -706,10 +793,10 @@ func TestHostname(t *testing.T) {
}
func TestReadAt(t *testing.T) {
- f, err := Open("_obj/readtest", O_CREAT|O_RDWR|O_TRUNC, 0666)
- if err != nil {
- t.Fatalf("open _obj/readtest: %s", err)
- }
+ f := newFile("TestReadAt", t)
+ defer Remove(f.Name())
+ defer f.Close()
+
const data = "hello, world\n"
io.WriteString(f, data)
@@ -724,10 +811,10 @@ func TestReadAt(t *testing.T) {
}
func TestWriteAt(t *testing.T) {
- f, err := Open("_obj/writetest", O_CREAT|O_RDWR|O_TRUNC, 0666)
- if err != nil {
- t.Fatalf("open _obj/writetest: %s", err)
- }
+ f := newFile("TestWriteAt", t)
+ defer Remove(f.Name())
+ defer f.Close()
+
const data = "hello, world\n"
io.WriteString(f, data)
@@ -736,11 +823,64 @@ func TestWriteAt(t *testing.T) {
t.Fatalf("WriteAt 7: %d, %v", n, err)
}
- b, err := ioutil.ReadFile("_obj/writetest")
+ b, err := ioutil.ReadFile(f.Name())
if err != nil {
- t.Fatalf("ReadFile _obj/writetest: %v", err)
+ 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/src/pkg/os/path.go b/src/pkg/os/path.go
index 36f497a1a..b762971d9 100644
--- a/src/pkg/os/path.go
+++ b/src/pkg/os/path.go
@@ -12,9 +12,9 @@ package os
// directories that MkdirAll creates.
// If path is already a directory, MkdirAll does nothing
// and returns nil.
-func MkdirAll(path string, perm int) Error {
+func MkdirAll(path string, perm uint32) Error {
// If path exists, stop with success or error.
- dir, err := Lstat(path)
+ dir, err := Stat(path)
if err == nil {
if dir.IsDirectory() {
return nil
@@ -84,7 +84,6 @@ func RemoveAll(path string) Error {
if err != nil {
return err
}
- defer fd.Close()
// Remove contents & return first error.
err = nil
@@ -105,6 +104,9 @@ func RemoveAll(path string) Error {
}
}
+ // Close directory, because windows won't remove opened directory.
+ fd.Close()
+
// Remove directory.
err1 := Remove(path)
if err == nil {
diff --git a/src/pkg/os/path_test.go b/src/pkg/os/path_test.go
index fcd4bac54..799e3ec2f 100644
--- a/src/pkg/os/path_test.go
+++ b/src/pkg/os/path_test.go
@@ -7,16 +7,19 @@ package os_test
import (
. "os"
"testing"
+ "runtime"
+ "syscall"
)
func TestMkdirAll(t *testing.T) {
- // Create new dir, in _obj so it will get
+ // Create new dir, in _test so it will get
// cleaned up by make if not by us.
- path := "_obj/_TestMkdirAll_/dir/./dir2"
+ 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)
@@ -34,7 +37,7 @@ func TestMkdirAll(t *testing.T) {
// Can't make directory named after file.
err = MkdirAll(fpath, 0777)
if err == nil {
- t.Fatalf("MkdirAll %q: no error")
+ t.Fatalf("MkdirAll %q: no error", fpath)
}
perr, ok := err.(*PathError)
if !ok {
@@ -48,7 +51,7 @@ func TestMkdirAll(t *testing.T) {
ffpath := fpath + "/subdir"
err = MkdirAll(ffpath, 0777)
if err == nil {
- t.Fatalf("MkdirAll %q: no error")
+ t.Fatalf("MkdirAll %q: no error", ffpath)
}
perr, ok = err.(*PathError)
if !ok {
@@ -57,13 +60,11 @@ func TestMkdirAll(t *testing.T) {
if perr.Path != fpath {
t.Fatalf("MkdirAll %q returned wrong error path: %q not %q", ffpath, perr.Path, fpath)
}
-
- RemoveAll("_obj/_TestMkdirAll_")
}
func TestRemoveAll(t *testing.T) {
// Work directory.
- path := "_obj/_TestRemoveAll_"
+ path := "_test/_TestRemoveAll_"
fpath := path + "/file"
dpath := path + "/dir"
@@ -104,7 +105,16 @@ func TestRemoveAll(t *testing.T) {
t.Fatalf("Lstat %q succeeded after RemoveAll (second)", path)
}
- if Getuid() != 0 { // Test fails as root
+ // 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)
@@ -120,23 +130,17 @@ func TestRemoveAll(t *testing.T) {
if err = Chmod(dpath, 0); err != nil {
t.Fatalf("Chmod %q 0: %s", dpath, err)
}
- if err = RemoveAll(path); err == nil {
- _, err := Lstat(path)
- if err == nil {
- t.Errorf("Can lstat %q after supposed RemoveAll", path)
- }
- t.Fatalf("RemoveAll %q succeeded with chmod 0 subdirectory", path, err)
- }
- perr, ok := err.(*PathError)
- if !ok {
- t.Fatalf("RemoveAll %q returned %T not *PathError", path, err)
- }
- if perr.Path != dpath {
- t.Fatalf("RemoveAll %q failed at %q not %q", path, perr.Path, dpath)
- }
- if err = Chmod(dpath, 0777); err != nil {
- t.Fatalf("Chmod %q 0777: %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)
@@ -150,3 +154,28 @@ func TestRemoveAll(t *testing.T) {
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/src/pkg/os/signal/Makefile b/src/pkg/os/signal/Makefile
index 5a245464a..013b91a85 100644
--- a/src/pkg/os/signal/Makefile
+++ b/src/pkg/os/signal/Makefile
@@ -2,7 +2,7 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
-include ../../../Make.$(GOARCH)
+include ../../../Make.inc
TARG=os/signal
GOFILES=\
diff --git a/src/pkg/os/stat_nacl.go b/src/pkg/os/stat_nacl.go
deleted file mode 100644
index a44d0b0b6..000000000
--- a/src/pkg/os/stat_nacl.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package os
-
-import "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 = 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) * 1e9
- fi.Mtime_ns = int64(stat.Mtime) * 1e9
- fi.Ctime_ns = int64(stat.Ctime) * 1e9
- 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/src/pkg/os/stat_windows.go b/src/pkg/os/stat_windows.go
index d7ff6faf4..11088436a 100644
--- a/src/pkg/os/stat_windows.go
+++ b/src/pkg/os/stat_windows.go
@@ -26,12 +26,12 @@ func fileInfoFromByHandleInfo(fi *FileInfo, name string, d *syscall.ByHandleFile
func setFileInfo(fi *FileInfo, name string, fa, sizehi, sizelo uint32, ctime, atime, wtime syscall.Filetime) *FileInfo {
fi.Mode = 0
- if fa == syscall.FILE_ATTRIBUTE_DIRECTORY {
+ if fa&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
fi.Mode = fi.Mode | syscall.S_IFDIR
} else {
fi.Mode = fi.Mode | syscall.S_IFREG
}
- if fa == syscall.FILE_ATTRIBUTE_READONLY {
+ if fa&syscall.FILE_ATTRIBUTE_READONLY != 0 {
fi.Mode = fi.Mode | 0444
} else {
fi.Mode = fi.Mode | 0666
@@ -39,8 +39,8 @@ func setFileInfo(fi *FileInfo, name string, fa, sizehi, sizelo uint32, ctime, at
fi.Size = int64(sizehi)<<32 + int64(sizelo)
fi.Name = name
fi.FollowedSymlink = false
- fi.Atime_ns = atime.Microseconds() * 1000
- fi.Mtime_ns = wtime.Microseconds() * 1000
- fi.Ctime_ns = ctime.Microseconds() * 1000
+ fi.Atime_ns = atime.Nanoseconds()
+ fi.Mtime_ns = wtime.Nanoseconds()
+ fi.Ctime_ns = ctime.Nanoseconds()
return fi
}
diff --git a/src/pkg/os/sys_nacl.go b/src/pkg/os/sys_nacl.go
deleted file mode 100644
index dfcccb3e8..000000000
--- a/src/pkg/os/sys_nacl.go
+++ /dev/null
@@ -1,7 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package os
-
-func Hostname() (name string, err Error) { return "nacl", nil }
diff --git a/src/pkg/os/types.go b/src/pkg/os/types.go
index 0e76e90be..79f6e9d49 100644
--- a/src/pkg/os/types.go
+++ b/src/pkg/os/types.go
@@ -53,4 +53,4 @@ func (f *FileInfo) IsSymlink() bool { return (f.Mode & syscall.S_IFMT) == syscal
func (f *FileInfo) IsSocket() bool { return (f.Mode & syscall.S_IFMT) == syscall.S_IFSOCK }
// Permission returns the file permission bits.
-func (f *FileInfo) Permission() int { return int(f.Mode & 0777) }
+func (f *FileInfo) Permission() uint32 { return f.Mode & 0777 }