diff options
Diffstat (limited to 'src/pkg/os')
-rw-r--r-- | src/pkg/os/Makefile | 9 | ||||
-rw-r--r-- | src/pkg/os/dir_darwin.go | 12 | ||||
-rw-r--r-- | src/pkg/os/dir_freebsd.go | 12 | ||||
-rw-r--r-- | src/pkg/os/dir_linux.go | 12 | ||||
-rw-r--r-- | src/pkg/os/dir_nacl.go | 77 | ||||
-rw-r--r-- | src/pkg/os/env.go | 119 | ||||
-rw-r--r-- | src/pkg/os/env_test.go | 59 | ||||
-rw-r--r--[-rwxr-xr-x] | src/pkg/os/env_unix.go | 83 | ||||
-rw-r--r--[-rwxr-xr-x] | src/pkg/os/env_windows.go | 98 | ||||
-rw-r--r-- | src/pkg/os/exec.go | 23 | ||||
-rw-r--r-- | src/pkg/os/file.go | 45 | ||||
-rw-r--r-- | src/pkg/os/file_unix.go | 10 | ||||
-rw-r--r-- | src/pkg/os/file_windows.go | 39 | ||||
-rw-r--r-- | src/pkg/os/inotify/Makefile | 14 | ||||
-rw-r--r-- | src/pkg/os/inotify/inotify_linux.go | 291 | ||||
-rw-r--r-- | src/pkg/os/inotify/inotify_linux_test.go | 99 | ||||
-rw-r--r-- | src/pkg/os/os_test.go | 420 | ||||
-rw-r--r-- | src/pkg/os/path.go | 8 | ||||
-rw-r--r-- | src/pkg/os/path_test.go | 79 | ||||
-rw-r--r-- | src/pkg/os/signal/Makefile | 2 | ||||
-rw-r--r-- | src/pkg/os/stat_nacl.go | 38 | ||||
-rw-r--r-- | src/pkg/os/stat_windows.go | 10 | ||||
-rw-r--r-- | src/pkg/os/sys_nacl.go | 7 | ||||
-rw-r--r-- | src/pkg/os/types.go | 2 |
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 } |