diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-04-26 09:55:32 +0200 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-04-26 09:55:32 +0200 |
commit | 7b15ed9ef455b6b66c6b376898a88aef5d6a9970 (patch) | |
tree | 3ef530baa80cdf29436ba981f5783be6b4d2202b /src/pkg/os | |
parent | 50104cc32a498f7517a51c8dc93106c51c7a54b4 (diff) | |
download | golang-7b15ed9ef455b6b66c6b376898a88aef5d6a9970.tar.gz |
Imported Upstream version 2011.04.13upstream/2011.04.13
Diffstat (limited to 'src/pkg/os')
31 files changed, 1584 insertions, 694 deletions
diff --git a/src/pkg/os/Makefile b/src/pkg/os/Makefile index 3a81afe39..cd9284079 100644 --- a/src/pkg/os/Makefile +++ b/src/pkg/os/Makefile @@ -6,7 +6,6 @@ include ../../Make.inc TARG=os GOFILES=\ - dir_$(GOOS).go\ error.go\ env.go\ exec.go\ @@ -19,29 +18,53 @@ GOFILES=\ types.go\ GOFILES_freebsd=\ + dir_unix.go\ + error_posix.go\ env_unix.go\ + file_posix.go\ file_unix.go\ sys_bsd.go\ + exec_posix.go\ exec_unix.go\ GOFILES_darwin=\ + dir_unix.go\ + error_posix.go\ env_unix.go\ + file_posix.go\ file_unix.go\ sys_bsd.go\ + exec_posix.go\ exec_unix.go\ GOFILES_linux=\ + dir_unix.go\ + error_posix.go\ env_unix.go\ + file_posix.go\ file_unix.go\ sys_linux.go\ + exec_posix.go\ exec_unix.go\ GOFILES_windows=\ + dir_windows.go\ + error_posix.go\ env_windows.go\ + file_posix.go\ file_windows.go\ sys_windows.go\ + exec_posix.go\ exec_windows.go\ +GOFILES_plan9=\ + dir_plan9.go\ + error_plan9.go\ + env_plan9.go\ + file_plan9.go\ + sys_plan9.go\ + exec_plan9.go\ + GOFILES+=$(GOFILES_$(GOOS)) include ../../Make.pkg diff --git a/src/pkg/os/dir_freebsd.go b/src/pkg/os/dir_freebsd.go deleted file mode 100644 index 2ebe368a6..000000000 --- a/src/pkg/os/dir_freebsd.go +++ /dev/null @@ -1,66 +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 (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.bufp = 0 - // Final argument is (basep *uintptr) and the syscall doesn't take nil. - d.nbuf, errno = syscall.Getdirentries(file.fd, d.buf, new(uintptr)) - if errno != 0 { - d.nbuf = 0 - return names, NewSyscallError("getdirentries", errno) - } - if d.nbuf <= 0 { - break // EOF - } - } - // Drain the buffer - for count != 0 && d.bufp < d.nbuf { - dirent := (*syscall.Dirent)(unsafe.Pointer(&d.buf[d.bufp])) - if dirent.Reclen == 0 { - d.bufp = d.nbuf - break - } - d.bufp += int(dirent.Reclen) - if dirent.Fileno == 0 { // File absent in directory. - continue - } - bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0])) - var name = string(bytes[0:dirent.Namlen]) - if name == "." || name == ".." { // Useless names - continue - } - count-- - names = append(names, name) - } - } - return names, nil -} diff --git a/src/pkg/os/dir_linux.go b/src/pkg/os/dir_linux.go deleted file mode 100644 index 09aad6367..000000000 --- a/src/pkg/os/dir_linux.go +++ /dev/null @@ -1,69 +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 := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0])) - var name = string(bytes[0:clen(bytes[0:])]) - if name == "." || name == ".." { // Useless names - continue - } - count-- - names = append(names, name) - } - } - return names, nil -} diff --git a/src/pkg/os/dir_plan9.go b/src/pkg/os/dir_plan9.go new file mode 100644 index 000000000..d9514191d --- /dev/null +++ b/src/pkg/os/dir_plan9.go @@ -0,0 +1,284 @@ +// 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" +) + +// Readdir reads the contents of the directory associated with file and +// returns an array of up to count FileInfo structures, 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 this file has no dirinfo, create one. + if file.dirinfo == nil { + file.dirinfo = new(dirInfo) + } + d := file.dirinfo + size := count + if size < 0 { + size = 100 + } + result := make([]FileInfo, 0, size) // Empty with room to grow. + for count != 0 { + // Refill the buffer if necessary + if d.bufp >= d.nbuf { + d.bufp = 0 + var e Error + d.nbuf, e = file.Read(d.buf[:]) + if e != nil && e != EOF { + return nil, &PathError{"readdir", file.name, e} + } + if e == EOF { + break + } + if d.nbuf < syscall.STATFIXLEN { + return nil, &PathError{"readdir", file.name, Eshortstat} + } + } + + // Get a record from buffer + m, _ := gbit16(d.buf[d.bufp:]) + m += 2 + if m < syscall.STATFIXLEN { + return nil, &PathError{"readdir", file.name, Eshortstat} + } + dir, e := UnmarshalDir(d.buf[d.bufp : d.bufp+int(m)]) + if e != nil { + return nil, &PathError{"readdir", file.name, e} + } + var f FileInfo + fileInfoFromStat(&f, dir) + result = append(result, f) + + d.bufp += int(m) + count-- + } + return result, nil +} + +// Readdirnames returns an array of up to count file names residing in the +// directory associated with file. A negative count will return all of them. +// Readdir returns the array and an Error, if any. +func (file *File) Readdirnames(count int) (names []string, err Error) { + fi, e := file.Readdir(count) + + if e != nil { + return []string{}, e + } + + names = make([]string, len(fi)) + err = nil + + for i := range fi { + names[i] = fi[i].Name + } + + return +} + +type Dir struct { + // system-modified data + Type uint16 // server type + Dev uint32 // server subtype + // file data + Qid Qid // unique id from server + Mode uint32 // permissions + Atime uint32 // last read time + Mtime uint32 // last write time + Length uint64 // file length + Name string // last element of path + Uid string // owner name + Gid string // group name + Muid string // last modifier name +} + +type Qid struct { + Path uint64 // the file server's unique identification for the file + Vers uint32 // version number for given Path + Type uint8 // the type of the file (syscall.QTDIR for example) +} + +var nullDir = Dir{ + ^uint16(0), + ^uint32(0), + Qid{^uint64(0), ^uint32(0), ^uint8(0)}, + ^uint32(0), + ^uint32(0), + ^uint32(0), + ^uint64(0), + "", + "", + "", + "", +} + +// Null assigns members of d with special "don't care" values indicating +// they should not be written by syscall.Wstat. +func (d *Dir) Null() { + *d = nullDir +} + +// pdir appends a 9P Stat message based on the contents of Dir d to a byte slice b. +func pdir(b []byte, d *Dir) []byte { + n := len(b) + b = pbit16(b, 0) // length, filled in later + b = pbit16(b, d.Type) + b = pbit32(b, d.Dev) + b = pqid(b, d.Qid) + b = pbit32(b, d.Mode) + b = pbit32(b, d.Atime) + b = pbit32(b, d.Mtime) + b = pbit64(b, d.Length) + b = pstring(b, d.Name) + b = pstring(b, d.Uid) + b = pstring(b, d.Gid) + b = pstring(b, d.Muid) + pbit16(b[0:n], uint16(len(b)-(n+2))) + return b +} + +// UnmarshalDir reads a 9P Stat message from a 9P protocol message strored in b, +// returning the corresponding Dir struct. +func UnmarshalDir(b []byte) (d *Dir, err Error) { + n := uint16(0) + n, b = gbit16(b) + + if int(n) != len(b) { + return nil, Ebadstat + } + + d = new(Dir) + d.Type, b = gbit16(b) + d.Dev, b = gbit32(b) + d.Qid, b = gqid(b) + d.Mode, b = gbit32(b) + d.Atime, b = gbit32(b) + d.Mtime, b = gbit32(b) + d.Length, b = gbit64(b) + d.Name, b = gstring(b) + d.Uid, b = gstring(b) + d.Gid, b = gstring(b) + d.Muid, b = gstring(b) + + if len(b) != 0 { + return nil, Ebadstat + } + + return d, nil +} + +// gqid reads the qid part of a 9P Stat message from a 9P protocol message strored in b, +// returning the corresponding Qid struct and the remaining slice of b. +func gqid(b []byte) (Qid, []byte) { + var q Qid + q.Path, b = gbit64(b) + q.Vers, b = gbit32(b) + q.Type, b = gbit8(b) + return q, b +} + +// pqid appends a Qid struct q to a 9P message b. +func pqid(b []byte, q Qid) []byte { + b = pbit64(b, q.Path) + b = pbit32(b, q.Vers) + b = pbit8(b, q.Type) + return b +} + +// gbit8 reads a byte-sized numeric value from a 9P protocol message strored in b, +// returning the value and the remaining slice of b. +func gbit8(b []byte) (uint8, []byte) { + return uint8(b[0]), b[1:] +} + +// gbit16 reads a 16-bit numeric value from a 9P protocol message strored in b, +// returning the value and the remaining slice of b. +func gbit16(b []byte) (uint16, []byte) { + return uint16(b[0]) | uint16(b[1])<<8, b[2:] +} + +// gbit32 reads a 32-bit numeric value from a 9P protocol message strored in b, +// returning the value and the remaining slice of b. +func gbit32(b []byte) (uint32, []byte) { + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:] +} + +// gbit64 reads a 64-bit numeric value from a 9P protocol message strored in b, +// returning the value and the remaining slice of b. +func gbit64(b []byte) (uint64, []byte) { + lo, b := gbit32(b) + hi, b := gbit32(b) + return uint64(hi)<<32 | uint64(lo), b +} + +// gstring reads a string from a 9P protocol message strored in b, +// returning the value as a Go string and the remaining slice of b. +func gstring(b []byte) (string, []byte) { + n, b := gbit16(b) + return string(b[0:n]), b[n:] +} + +// pbit8 appends a byte-sized numeric value x to a 9P message b. +func pbit8(b []byte, x uint8) []byte { + n := len(b) + if n+1 > cap(b) { + nb := make([]byte, n, 100+2*cap(b)) + copy(nb, b) + b = nb + } + b = b[0 : n+1] + b[n] = x + return b +} + +// pbit16 appends a 16-bit numeric value x to a 9P message b. +func pbit16(b []byte, x uint16) []byte { + n := len(b) + if n+2 > cap(b) { + nb := make([]byte, n, 100+2*cap(b)) + copy(nb, b) + b = nb + } + b = b[0 : n+2] + b[n] = byte(x) + b[n+1] = byte(x >> 8) + return b +} + +// pbit32 appends a 32-bit numeric value x to a 9P message b. +func pbit32(b []byte, x uint32) []byte { + n := len(b) + if n+4 > cap(b) { + nb := make([]byte, n, 100+2*cap(b)) + copy(nb, b) + b = nb + } + b = b[0 : n+4] + b[n] = byte(x) + b[n+1] = byte(x >> 8) + b[n+2] = byte(x >> 16) + b[n+3] = byte(x >> 24) + return b +} + +// pbit64 appends a 64-bit numeric value x to a 9P message b. +func pbit64(b []byte, x uint64) []byte { + b = pbit32(b, uint32(x)) + b = pbit32(b, uint32(x>>32)) + return b +} + +// pstring appends a Go string s to a 9P message b. +func pstring(b []byte, s string) []byte { + if len(s) >= 1<<16 { + panic(NewError("string too long")) + } + b = pbit16(b, uint16(len(s))) + b = append(b, []byte(s)...) + return b +} diff --git a/src/pkg/os/dir_darwin.go b/src/pkg/os/dir_unix.go index 861bcef27..f5b82230d 100644 --- a/src/pkg/os/dir_darwin.go +++ b/src/pkg/os/dir_unix.go @@ -6,11 +6,10 @@ package os import ( "syscall" - "unsafe" ) const ( - blockSize = 4096 // TODO(r): use statfs + blockSize = 4096 ) // Readdirnames reads the contents of the directory associated with file and @@ -23,7 +22,6 @@ func (file *File) Readdirnames(count int) (names []string, err Error) { 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 @@ -35,37 +33,22 @@ func (file *File) Readdirnames(count int) (names []string, err Error) { for count != 0 { // Refill the buffer if necessary if d.bufp >= d.nbuf { - var errno int d.bufp = 0 - // Final argument is (basep *uintptr) and the syscall doesn't take nil. - d.nbuf, errno = syscall.Getdirentries(file.fd, d.buf, new(uintptr)) + var errno int + d.nbuf, errno = syscall.ReadDirent(file.fd, d.buf) if errno != 0 { - d.nbuf = 0 - return names, NewSyscallError("getdirentries", errno) + return names, NewSyscallError("readdirent", errno) } if d.nbuf <= 0 { break // EOF } } + // Drain the buffer - for count != 0 && d.bufp < d.nbuf { - dirent := (*syscall.Dirent)(unsafe.Pointer(&d.buf[d.bufp])) - if dirent.Reclen == 0 { - d.bufp = d.nbuf - break - } - d.bufp += int(dirent.Reclen) - if dirent.Ino == 0 { // File absent in directory. - continue - } - bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0])) - var name = string(bytes[0:dirent.Namlen]) - if name == "." || name == ".." { // Useless names - continue - } - count-- - names = append(names, name) - } + var nb, nc int + nb, nc, names = syscall.ParseDirent(d.buf[d.bufp:d.nbuf], count, names) + d.bufp += nb + count -= nc } return names, nil } diff --git a/src/pkg/os/env_plan9.go b/src/pkg/os/env_plan9.go new file mode 100644 index 000000000..14df55ed0 --- /dev/null +++ b/src/pkg/os/env_plan9.go @@ -0,0 +1,91 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Plan 9 environment variables. + +package os + +import "syscall" + +// 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) { + if len(key) == 0 { + return "", EINVAL + } + f, e := Open("/env/" + key) + if iserror(e) { + return "", ENOENV + } + defer f.Close() + + var buf [4096]byte + n, e := f.Read(buf[:len(buf)-1]) + if iserror(e) { + return "", ENOENV + } + buf[n] = 0 + return string(buf[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 { + if len(key) == 0 { + return EINVAL + } + + f, e := Create("/env/" + key) + if iserror(e) { + return e + } + defer f.Close() + + _, e = f.Write(syscall.StringByteSlice(value)) + return nil +} + +// Clearenv deletes all environment variables. +func Clearenv() { + syscall.RawSyscall(syscall.SYS_RFORK, syscall.RFCENVG, 0, 0) +} + +// Environ returns an array of strings representing the environment, +// in the form "key=value". +func Environ() []string { + env := make([]string, 0, 100) + + f, e := Open("/env") + if iserror(e) { + panic(e) + } + defer f.Close() + + names, e := f.Readdirnames(-1) + if iserror(e) { + panic(e) + } + + for _, k := range names { + if v, e := Getenverror(k); !iserror(e) { + env = append(env, k+"="+v) + } + } + return env[0:len(env)] +} + +// TempDir returns the default directory to use for temporary files. +func TempDir() string { + return "/tmp" +} diff --git a/src/pkg/os/error.go b/src/pkg/os/error.go index 635a3fe50..2c4516ca7 100644 --- a/src/pkg/os/error.go +++ b/src/pkg/os/error.go @@ -4,8 +4,6 @@ package os -import syscall "syscall" - // An Error can represent any printable error condition. type Error interface { String() string @@ -26,63 +24,6 @@ func (e ErrorString) Timeout() bool { return false } // NewError converts s to an ErrorString, which satisfies the Error interface. func NewError(s string) Error { return ErrorString(s) } -// Errno is the Unix error number. Names such as EINVAL are simple -// wrappers to convert the error number into an Error. -type Errno int64 - -func (e Errno) String() string { return syscall.Errstr(int(e)) } - -func (e Errno) Temporary() bool { - return e == Errno(syscall.EINTR) || e.Timeout() -} - -func (e Errno) Timeout() bool { - return e == Errno(syscall.EAGAIN) || e == Errno(syscall.EWOULDBLOCK) || e == Errno(syscall.ETIMEDOUT) -} - -// Commonly known Unix errors. -var ( - EPERM Error = Errno(syscall.EPERM) - ENOENT Error = Errno(syscall.ENOENT) - ESRCH Error = Errno(syscall.ESRCH) - EINTR Error = Errno(syscall.EINTR) - EIO Error = Errno(syscall.EIO) - ENXIO Error = Errno(syscall.ENXIO) - E2BIG Error = Errno(syscall.E2BIG) - ENOEXEC Error = Errno(syscall.ENOEXEC) - EBADF Error = Errno(syscall.EBADF) - ECHILD Error = Errno(syscall.ECHILD) - EDEADLK Error = Errno(syscall.EDEADLK) - ENOMEM Error = Errno(syscall.ENOMEM) - EACCES Error = Errno(syscall.EACCES) - EFAULT Error = Errno(syscall.EFAULT) - EBUSY Error = Errno(syscall.EBUSY) - EEXIST Error = Errno(syscall.EEXIST) - EXDEV Error = Errno(syscall.EXDEV) - ENODEV Error = Errno(syscall.ENODEV) - ENOTDIR Error = Errno(syscall.ENOTDIR) - EISDIR Error = Errno(syscall.EISDIR) - EINVAL Error = Errno(syscall.EINVAL) - ENFILE Error = Errno(syscall.ENFILE) - EMFILE Error = Errno(syscall.EMFILE) - ENOTTY Error = Errno(syscall.ENOTTY) - EFBIG Error = Errno(syscall.EFBIG) - ENOSPC Error = Errno(syscall.ENOSPC) - ESPIPE Error = Errno(syscall.ESPIPE) - EROFS Error = Errno(syscall.EROFS) - EMLINK Error = Errno(syscall.EMLINK) - EPIPE Error = Errno(syscall.EPIPE) - EAGAIN Error = Errno(syscall.EAGAIN) - EDOM Error = Errno(syscall.EDOM) - ERANGE Error = Errno(syscall.ERANGE) - EADDRINUSE Error = Errno(syscall.EADDRINUSE) - ECONNREFUSED Error = Errno(syscall.ECONNREFUSED) - ENAMETOOLONG Error = Errno(syscall.ENAMETOOLONG) - EAFNOSUPPORT Error = Errno(syscall.EAFNOSUPPORT) - ETIMEDOUT Error = Errno(syscall.ETIMEDOUT) - ENOTCONN Error = Errno(syscall.ENOTCONN) -) - // PathError records an error and the operation and file path that caused it. type PathError struct { Op string @@ -91,25 +32,3 @@ type PathError struct { } func (e *PathError) String() string { return e.Op + " " + e.Path + ": " + e.Error.String() } - -// SyscallError records an error from a specific system call. -type SyscallError struct { - Syscall string - Errno Errno -} - -func (e *SyscallError) String() string { return e.Syscall + ": " + e.Errno.String() } - -// Note: If the name of the function NewSyscallError changes, -// pkg/go/doc/doc.go should be adjusted since it hardwires -// this name in a heuristic. - -// NewSyscallError returns, as an Error, a new SyscallError -// with the given system call name and error number. -// As a convenience, if errno is 0, NewSyscallError returns nil. -func NewSyscallError(syscall string, errno int) Error { - if errno == 0 { - return nil - } - return &SyscallError{syscall, Errno(errno)} -} diff --git a/src/pkg/os/error_plan9.go b/src/pkg/os/error_plan9.go new file mode 100644 index 000000000..3374775b8 --- /dev/null +++ b/src/pkg/os/error_plan9.go @@ -0,0 +1,60 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package os + +import syscall "syscall" + +// SyscallError records an error from a specific system call. +type SyscallError struct { + Syscall string + Err string +} + +func (e *SyscallError) String() string { return e.Syscall + ": " + e.Err } + +// Note: If the name of the function NewSyscallError changes, +// pkg/go/doc/doc.go should be adjusted since it hardwires +// this name in a heuristic. + +// NewSyscallError returns, as an Error, a new SyscallError +// with the given system call name and error details. +// As a convenience, if err is nil, NewSyscallError returns nil. +func NewSyscallError(syscall string, err syscall.Error) Error { + if err == nil { + return nil + } + return &SyscallError{syscall, err.String()} +} + +var ( + Eshortstat = NewError("stat buffer too small") + Ebadstat = NewError("malformed stat buffer") + Ebadfd = NewError("fd out of range or not open") + Ebadarg = NewError("bad arg in system call") + Enotdir = NewError("not a directory") + Enonexist = NewError("file does not exist") + Eexist = NewError("file already exists") + Eio = NewError("i/o error") + Eperm = NewError("permission denied") + + EINVAL = Ebadarg + ENOTDIR = Enotdir + ENOENT = Enonexist + EEXIST = Eexist + EIO = Eio + EACCES = Eperm + EISDIR = syscall.EISDIR + + ENAMETOOLONG = NewError("file name too long") + ERANGE = NewError("math result not representable") + EPIPE = NewError("Broken Pipe") + EPLAN9 = NewError("not supported by plan 9") +) + +func iserror(err syscall.Error) bool { + return err != nil +} + +func Errno(e syscall.Error) syscall.Error { return e } diff --git a/src/pkg/os/error_posix.go b/src/pkg/os/error_posix.go new file mode 100644 index 000000000..0ee34e4b0 --- /dev/null +++ b/src/pkg/os/error_posix.go @@ -0,0 +1,90 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package os + +import syscall "syscall" + +// Errno is the Unix error number. Names such as EINVAL are simple +// wrappers to convert the error number into an Error. +type Errno int64 + +func (e Errno) String() string { return syscall.Errstr(int(e)) } + +func (e Errno) Temporary() bool { + return e == Errno(syscall.EINTR) || e.Timeout() +} + +func (e Errno) Timeout() bool { + return e == Errno(syscall.EAGAIN) || e == Errno(syscall.EWOULDBLOCK) || e == Errno(syscall.ETIMEDOUT) +} + +// Commonly known Unix errors. +var ( + EPERM Error = Errno(syscall.EPERM) + ENOENT Error = Errno(syscall.ENOENT) + ESRCH Error = Errno(syscall.ESRCH) + EINTR Error = Errno(syscall.EINTR) + EIO Error = Errno(syscall.EIO) + ENXIO Error = Errno(syscall.ENXIO) + E2BIG Error = Errno(syscall.E2BIG) + ENOEXEC Error = Errno(syscall.ENOEXEC) + EBADF Error = Errno(syscall.EBADF) + ECHILD Error = Errno(syscall.ECHILD) + EDEADLK Error = Errno(syscall.EDEADLK) + ENOMEM Error = Errno(syscall.ENOMEM) + EACCES Error = Errno(syscall.EACCES) + EFAULT Error = Errno(syscall.EFAULT) + EBUSY Error = Errno(syscall.EBUSY) + EEXIST Error = Errno(syscall.EEXIST) + EXDEV Error = Errno(syscall.EXDEV) + ENODEV Error = Errno(syscall.ENODEV) + ENOTDIR Error = Errno(syscall.ENOTDIR) + EISDIR Error = Errno(syscall.EISDIR) + EINVAL Error = Errno(syscall.EINVAL) + ENFILE Error = Errno(syscall.ENFILE) + EMFILE Error = Errno(syscall.EMFILE) + ENOTTY Error = Errno(syscall.ENOTTY) + EFBIG Error = Errno(syscall.EFBIG) + ENOSPC Error = Errno(syscall.ENOSPC) + ESPIPE Error = Errno(syscall.ESPIPE) + EROFS Error = Errno(syscall.EROFS) + EMLINK Error = Errno(syscall.EMLINK) + EPIPE Error = Errno(syscall.EPIPE) + EAGAIN Error = Errno(syscall.EAGAIN) + EDOM Error = Errno(syscall.EDOM) + ERANGE Error = Errno(syscall.ERANGE) + EADDRINUSE Error = Errno(syscall.EADDRINUSE) + ECONNREFUSED Error = Errno(syscall.ECONNREFUSED) + ENAMETOOLONG Error = Errno(syscall.ENAMETOOLONG) + EAFNOSUPPORT Error = Errno(syscall.EAFNOSUPPORT) + ETIMEDOUT Error = Errno(syscall.ETIMEDOUT) + ENOTCONN Error = Errno(syscall.ENOTCONN) +) + +// SyscallError records an error from a specific system call. +type SyscallError struct { + Syscall string + Errno Errno +} + +func (e *SyscallError) String() string { return e.Syscall + ": " + e.Errno.String() } + +// Note: If the name of the function NewSyscallError changes, +// pkg/go/doc/doc.go should be adjusted since it hardwires +// this name in a heuristic. + +// NewSyscallError returns, as an Error, a new SyscallError +// with the given system call name and error details. +// As a convenience, if errno is 0, NewSyscallError returns nil. +func NewSyscallError(syscall string, errno int) Error { + if errno == 0 { + return nil + } + return &SyscallError{syscall, Errno(errno)} +} + +func iserror(errno int) bool { + return errno != 0 +} diff --git a/src/pkg/os/exec.go b/src/pkg/os/exec.go index dbdfacc58..f62caf9a0 100644 --- a/src/pkg/os/exec.go +++ b/src/pkg/os/exec.go @@ -21,123 +21,22 @@ func newProcess(pid, handle int) *Process { return p } -// StartProcess starts a new process with the program, arguments, -// and environment specified by name, argv, and envv. 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 StartProcess(name string, argv []string, envv []string, dir string, fd []*File) (p *Process, err Error) { - if envv == nil { - envv = Environ() - } - // Create array of integer (system) fds. - intfd := make([]int, len(fd)) - for i, f := range fd { - if f == nil { - intfd[i] = -1 - } else { - intfd[i] = f.Fd() - } - } - - pid, h, e := syscall.StartProcess(name, argv, envv, dir, intfd) - if e != 0 { - return nil, &PathError{"fork/exec", name, Errno(e)} - } - return newProcess(pid, h), nil -} - -// Exec replaces the current process with an execution of the -// named binary, with arguments argv and environment envv. -// If successful, Exec never returns. If it fails, it returns an Error. -// StartProcess is almost always a better way to execute a program. -func Exec(name string, argv []string, envv []string) Error { - if envv == nil { - envv = Environ() - } - e := syscall.Exec(name, argv, envv) - if e != 0 { - return &PathError{"exec", name, Errno(e)} - } - return nil -} - -// TODO(rsc): Should os implement its own syscall.WaitStatus -// wrapper with the methods, or is exposing the underlying one enough? -// -// TODO(rsc): Certainly need to have Rusage struct, -// since syscall one might have different field types across -// different OS. - -// Waitmsg stores the information about an exited process as reported by Wait. -type Waitmsg struct { - Pid int // The process's id. - syscall.WaitStatus // System-dependent status info. - Rusage *syscall.Rusage // System-dependent resource usage info. -} - -// Wait waits for process pid to exit or stop, and then returns a -// Waitmsg describing its status and an Error, if any. The options -// (WNOHANG etc.) affect the behavior of the Wait call. -// Wait is equivalent to calling FindProcess and then Wait -// and Release on the result. -func Wait(pid int, options int) (w *Waitmsg, err Error) { - p, e := FindProcess(pid) - if e != nil { - return nil, e - } - defer p.Release() - return p.Wait(options) -} - -// Convert i to decimal string. -func itod(i int) string { - if i == 0 { - return "0" - } - - u := uint64(i) - if i < 0 { - u = -u - } - - // Assemble decimal in reverse order. - var b [32]byte - bp := len(b) - for ; u > 0; u /= 10 { - bp-- - b[bp] = byte(u%10) + '0' - } - - if i < 0 { - bp-- - b[bp] = '-' - } - - return string(b[bp:]) -} - -func (w Waitmsg) String() string { - // TODO(austin) Use signal names when possible? - res := "" - switch { - case w.Exited(): - res = "exit status " + itod(w.ExitStatus()) - case w.Signaled(): - res = "signal " + itod(w.Signal()) - case w.Stopped(): - res = "stop signal " + itod(w.StopSignal()) - if w.StopSignal() == syscall.SIGTRAP && w.TrapCause() != 0 { - res += " (trap " + itod(w.TrapCause()) + ")" - } - case w.Continued(): - res = "continued" - } - if w.CoreDump() { - res += " (core dumped)" - } - return res +// ProcAttr holds the attributes that will be applied to a new process +// started by StartProcess. +type ProcAttr struct { + // If Dir is non-empty, the child changes into the directory before + // creating the process. + Dir string + // If Env is non-nil, it gives the environment variables for the + // new process in the form returned by Environ. + // If it is nil, the result of Environ will be used. + Env []string + // Files specifies the open files inherited by the new process. The + // first three entries correspond to standard input, standard output, and + // standard error. An implementation may support additional entries, + // depending on the underlying operating system. A nil entry corresponds + // to that file being closed when the process starts. + Files []*File } // Getpid returns the process id of the caller. diff --git a/src/pkg/os/exec_plan9.go b/src/pkg/os/exec_plan9.go new file mode 100644 index 000000000..11874aba6 --- /dev/null +++ b/src/pkg/os/exec_plan9.go @@ -0,0 +1,114 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package os + +import ( + "runtime" + "syscall" +) + +// StartProcess starts a new process with the program, arguments and attributes +// specified by name, argv and attr. +func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err Error) { + sysattr := &syscall.ProcAttr{ + Dir: attr.Dir, + Env: attr.Env, + } + + // Create array of integer (system) fds. + intfd := make([]int, len(attr.Files)) + for i, f := range attr.Files { + if f == nil { + intfd[i] = -1 + } else { + intfd[i] = f.Fd() + } + } + + sysattr.Files = intfd + + pid, h, e := syscall.StartProcess(name, argv, sysattr) + if iserror(e) { + return nil, &PathError{"fork/exec", name, e} + } + + return newProcess(pid, h), nil +} + +// Exec replaces the current process with an execution of the +// named binary, with arguments argv and environment envv. +// If successful, Exec never returns. If it fails, it returns an Error. +// ForkExec is almost always a better way to execute a program. +func Exec(name string, argv []string, envv []string) Error { + e := syscall.Exec(name, argv, envv) + if iserror(e) { + return &PathError{"exec", name, e} + } + + return nil +} + +// Waitmsg stores the information about an exited process as reported by Wait. +type Waitmsg syscall.Waitmsg + +// Wait waits for the Process to exit or stop, and then returns a +// Waitmsg describing its status and an Error, if any. The options +// (WNOHANG etc.) affect the behavior of the Wait call. +func (p *Process) Wait(options int) (w *Waitmsg, err Error) { + var waitmsg syscall.Waitmsg + + if p.Pid == -1 { + return nil, EINVAL + } + + for true { + err = syscall.Await(&waitmsg) + + if iserror(err) { + return nil, NewSyscallError("wait", err) + } + + if waitmsg.Pid == p.Pid { + break + } + } + + return (*Waitmsg)(&waitmsg), nil +} + +// Wait waits for process pid to exit or stop, and then returns a +// Waitmsg describing its status and an Error, if any. The options +// (WNOHANG etc.) affect the behavior of the Wait call. +// Wait is equivalent to calling FindProcess and then Wait +// and Release on the result. +func Wait(pid int, options int) (w *Waitmsg, err Error) { + p, e := FindProcess(pid) + if e != nil { + return nil, e + } + defer p.Release() + return p.Wait(options) +} + +// Release releases any resources associated with the Process. +func (p *Process) Release() Error { + // NOOP for Plan 9. + p.Pid = -1 + // no need for a finalizer anymore + runtime.SetFinalizer(p, nil) + return nil +} + +// FindProcess looks for a running process by its pid. +// The Process it returns can be used to obtain information +// about the underlying operating system process. +func FindProcess(pid int) (p *Process, err Error) { + // NOOP for Plan 9. + return newProcess(pid, 0), nil +} + +func (w Waitmsg) String() string { + return "exit status: " + w.Msg +} diff --git a/src/pkg/os/exec_posix.go b/src/pkg/os/exec_posix.go new file mode 100644 index 000000000..9102dc0a4 --- /dev/null +++ b/src/pkg/os/exec_posix.go @@ -0,0 +1,127 @@ +// 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" + +// StartProcess starts a new process with the program, arguments and attributes +// specified by name, argv and attr. +func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err Error) { + sysattr := &syscall.ProcAttr{ + Dir: attr.Dir, + Env: attr.Env, + } + if sysattr.Env == nil { + sysattr.Env = Environ() + } + // Create array of integer (system) fds. + intfd := make([]int, len(attr.Files)) + for i, f := range attr.Files { + if f == nil { + intfd[i] = -1 + } else { + intfd[i] = f.Fd() + } + } + sysattr.Files = intfd + + pid, h, e := syscall.StartProcess(name, argv, sysattr) + if iserror(e) { + return nil, &PathError{"fork/exec", name, Errno(e)} + } + return newProcess(pid, h), nil +} + +// Exec replaces the current process with an execution of the +// named binary, with arguments argv and environment envv. +// If successful, Exec never returns. If it fails, it returns an Error. +// StartProcess is almost always a better way to execute a program. +func Exec(name string, argv []string, envv []string) Error { + if envv == nil { + envv = Environ() + } + e := syscall.Exec(name, argv, envv) + if iserror(e) { + return &PathError{"exec", name, Errno(e)} + } + return nil +} + +// TODO(rsc): Should os implement its own syscall.WaitStatus +// wrapper with the methods, or is exposing the underlying one enough? +// +// TODO(rsc): Certainly need to have Rusage struct, +// since syscall one might have different field types across +// different OS. + +// Waitmsg stores the information about an exited process as reported by Wait. +type Waitmsg struct { + Pid int // The process's id. + syscall.WaitStatus // System-dependent status info. + Rusage *syscall.Rusage // System-dependent resource usage info. +} + +// Wait waits for process pid to exit or stop, and then returns a +// Waitmsg describing its status and an Error, if any. The options +// (WNOHANG etc.) affect the behavior of the Wait call. +// Wait is equivalent to calling FindProcess and then Wait +// and Release on the result. +func Wait(pid int, options int) (w *Waitmsg, err Error) { + p, e := FindProcess(pid) + if e != nil { + return nil, e + } + defer p.Release() + return p.Wait(options) +} + +// Convert i to decimal string. +func itod(i int) string { + if i == 0 { + return "0" + } + + u := uint64(i) + if i < 0 { + u = -u + } + + // Assemble decimal in reverse order. + var b [32]byte + bp := len(b) + for ; u > 0; u /= 10 { + bp-- + b[bp] = byte(u%10) + '0' + } + + if i < 0 { + bp-- + b[bp] = '-' + } + + return string(b[bp:]) +} + +func (w Waitmsg) String() string { + // TODO(austin) Use signal names when possible? + res := "" + switch { + case w.Exited(): + res = "exit status " + itod(w.ExitStatus()) + case w.Signaled(): + res = "signal " + itod(w.Signal()) + case w.Stopped(): + res = "stop signal " + itod(w.StopSignal()) + if w.StopSignal() == syscall.SIGTRAP && w.TrapCause() != 0 { + res += " (trap " + itod(w.TrapCause()) + ")" + } + case w.Continued(): + res = "continued" + } + if w.CoreDump() { + res += " (core dumped)" + } + return res +} diff --git a/src/pkg/os/file.go b/src/pkg/os/file.go index 3f73f1dff..3aad80234 100644 --- a/src/pkg/os/file.go +++ b/src/pkg/os/file.go @@ -51,14 +51,20 @@ const ( 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_CREATE int = syscall.O_CREAT // create a new file if none exists. + O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist O_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. +) + +// Seek whence values. +const ( + SEEK_SET int = 0 // seek relative to the origin of the file + SEEK_CUR int = 1 // seek relative to the current offset + SEEK_END int = 2 // seek relative to the end ) type eofError int @@ -83,10 +89,10 @@ func (file *File) Read(b []byte) (n int, err Error) { if n < 0 { n = 0 } - if n == 0 && e == 0 { + if n == 0 && !iserror(e) { return 0, EOF } - if e != 0 { + if iserror(e) { err = &PathError{"read", file.name, Errno(e)} } return n, err @@ -102,10 +108,10 @@ func (file *File) ReadAt(b []byte, off int64) (n int, err Error) { } for len(b) > 0 { m, e := syscall.Pread(file.fd, b, off) - if m == 0 && e == 0 { + if m == 0 && !iserror(e) { return n, EOF } - if e != 0 { + if iserror(e) { err = &PathError{"read", file.name, Errno(e)} break } @@ -127,15 +133,10 @@ func (file *File) Write(b []byte) (n int, err Error) { if n < 0 { n = 0 } - if e == syscall.EPIPE { - file.nepipe++ - if file.nepipe >= 10 { - Exit(syscall.EPIPE) - } - } else { - file.nepipe = 0 - } - if e != 0 { + + epipecheck(file, e) + + if iserror(e) { err = &PathError{"write", file.name, Errno(e)} } return n, err @@ -150,7 +151,7 @@ func (file *File) WriteAt(b []byte, off int64) (n int, err Error) { } for len(b) > 0 { m, e := syscall.Pwrite(file.fd, b, off) - if e != 0 { + if iserror(e) { err = &PathError{"write", file.name, Errno(e)} break } @@ -167,10 +168,10 @@ func (file *File) WriteAt(b []byte, off int64) (n int, err Error) { // It returns the new offset and an Error, if any. func (file *File) Seek(offset int64, whence int) (ret int64, err Error) { r, e := syscall.Seek(file.fd, offset, whence) - if e == 0 && file.dirinfo != nil && r != 0 { + if !iserror(e) && file.dirinfo != nil && r != 0 { e = syscall.EISDIR } - if e != 0 { + if iserror(e) { return 0, &PathError{"seek", file.name, Errno(e)} } return r, nil @@ -187,71 +188,19 @@ func (file *File) WriteString(s string) (ret int, err Error) { return file.Write(b) } -// Pipe returns a connected pair of Files; reads from r return bytes written to w. -// It returns the files and an Error, if any. -func Pipe() (r *File, w *File, err Error) { - var p [2]int - - // See ../syscall/exec.go for description of lock. - syscall.ForkLock.RLock() - e := syscall.Pipe(p[0:]) - if e != 0 { - syscall.ForkLock.RUnlock() - return nil, nil, NewSyscallError("pipe", e) - } - syscall.CloseOnExec(p[0]) - syscall.CloseOnExec(p[1]) - syscall.ForkLock.RUnlock() - - return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil -} - // Mkdir creates a new directory with the specified name and permission bits. // It returns an error, if any. func Mkdir(name string, perm uint32) Error { e := syscall.Mkdir(name, perm) - if e != 0 { + if iserror(e) { return &PathError{"mkdir", name, Errno(e)} } return nil } -// Stat returns a FileInfo structure describing the named file and an error, if any. -// If name names a valid symbolic link, the returned FileInfo describes -// the file pointed at by the link and has fi.FollowedSymlink set to true. -// If name names an invalid symbolic link, the returned FileInfo describes -// the link itself and has fi.FollowedSymlink set to false. -func Stat(name string) (fi *FileInfo, err Error) { - var lstat, stat syscall.Stat_t - e := syscall.Lstat(name, &lstat) - if e != 0 { - return nil, &PathError{"stat", name, Errno(e)} - } - statp := &lstat - if lstat.Mode&syscall.S_IFMT == syscall.S_IFLNK { - e := syscall.Stat(name, &stat) - if e == 0 { - statp = &stat - } - } - return fileInfoFromStat(name, new(FileInfo), &lstat, statp), nil -} - -// Lstat returns the FileInfo structure describing the named file and an -// error, if any. If the file is a symbolic link, the returned FileInfo -// describes the symbolic link. Lstat makes no attempt to follow the link. -func Lstat(name string) (fi *FileInfo, err Error) { - var stat syscall.Stat_t - e := syscall.Lstat(name, &stat) - if e != 0 { - return nil, &PathError{"lstat", name, Errno(e)} - } - return fileInfoFromStat(name, new(FileInfo), &stat, &stat), nil -} - // Chdir changes the current working directory to the named directory. func Chdir(dir string) Error { - if e := syscall.Chdir(dir); e != 0 { + if e := syscall.Chdir(dir); iserror(e) { return &PathError{"chdir", dir, Errno(e)} } return nil @@ -260,179 +209,25 @@ func Chdir(dir string) Error { // Chdir changes the current working directory to the file, // which must be a directory. func (f *File) Chdir() Error { - if e := syscall.Fchdir(f.fd); e != 0 { + if e := syscall.Fchdir(f.fd); iserror(e) { return &PathError{"chdir", f.name, Errno(e)} } return nil } -// Remove removes the named file or directory. -func Remove(name string) Error { - // System call interface forces us to know - // whether name is a file or directory. - // Try both: it is cheaper on average than - // doing a Stat plus the right one. - e := syscall.Unlink(name) - if e == 0 { - return nil - } - e1 := syscall.Rmdir(name) - if e1 == 0 { - return nil - } - - // Both failed: figure out which error to return. - // OS X and Linux differ on whether unlink(dir) - // returns EISDIR, so can't use that. However, - // both agree that rmdir(file) returns ENOTDIR, - // so we can use that to decide which error is real. - // Rmdir might also return ENOTDIR if given a bad - // file path, like /etc/passwd/foo, but in that case, - // both errors will be ENOTDIR, so it's okay to - // use the error from unlink. - // For windows syscall.ENOTDIR is set - // to syscall.ERROR_DIRECTORY, hopefully it should - // do the trick. - if e1 != syscall.ENOTDIR { - e = e1 - } - return &PathError{"remove", name, Errno(e)} -} - -// LinkError records an error during a link or symlink or rename -// system call and the paths that caused it. -type LinkError struct { - Op string - Old string - New string - Error Error -} - -func (e *LinkError) String() string { - return e.Op + " " + e.Old + " " + e.New + ": " + e.Error.String() +// Open opens the named file for reading. If successful, methods on +// the returned file can be used for reading; the associated file +// descriptor has mode O_RDONLY. +// It returns the File and an Error, if any. +func Open(name string) (file *File, err Error) { + return OpenFile(name, O_RDONLY, 0) } -// Link creates a hard link. -func Link(oldname, newname string) Error { - e := syscall.Link(oldname, newname) - if e != 0 { - return &LinkError{"link", oldname, newname, Errno(e)} - } - return nil -} - -// Symlink creates a symbolic link. -func Symlink(oldname, newname string) Error { - e := syscall.Symlink(oldname, newname) - if e != 0 { - return &LinkError{"symlink", oldname, newname, Errno(e)} - } - return nil -} - -// Readlink reads the contents of a symbolic link: the destination of -// the link. It returns the contents and an Error, if any. -func Readlink(name string) (string, Error) { - for len := 128; ; len *= 2 { - b := make([]byte, len) - n, e := syscall.Readlink(name, b) - if e != 0 { - return "", &PathError{"readlink", name, Errno(e)} - } - if n < len { - return string(b[0:n]), nil - } - } - // Silence 6g. - return "", nil -} - -// Rename renames a file. -func Rename(oldname, newname string) Error { - e := syscall.Rename(oldname, newname) - if e != 0 { - return &LinkError{"rename", oldname, newname, Errno(e)} - } - return nil -} - -// Chmod changes the mode of the named file to mode. -// If the file is a symbolic link, it changes the mode of the link's target. -func Chmod(name string, mode uint32) Error { - if e := syscall.Chmod(name, mode); e != 0 { - return &PathError{"chmod", name, Errno(e)} - } - return nil -} - -// Chmod changes the mode of the file to mode. -func (f *File) Chmod(mode uint32) Error { - if e := syscall.Fchmod(f.fd, mode); e != 0 { - return &PathError{"chmod", f.name, Errno(e)} - } - return nil -} - -// Chown changes the numeric uid and gid of the named file. -// If the file is a symbolic link, it changes the uid and gid of the link's target. -func Chown(name string, uid, gid int) Error { - if e := syscall.Chown(name, uid, gid); e != 0 { - return &PathError{"chown", name, Errno(e)} - } - return nil -} - -// Lchown changes the numeric uid and gid of the named file. -// If the file is a symbolic link, it changes the uid and gid of the link itself. -func Lchown(name string, uid, gid int) Error { - if e := syscall.Lchown(name, uid, gid); e != 0 { - return &PathError{"lchown", name, Errno(e)} - } - return nil -} - -// Chown changes the numeric uid and gid of the named file. -func (f *File) Chown(uid, gid int) Error { - if e := syscall.Fchown(f.fd, uid, gid); e != 0 { - return &PathError{"chown", f.name, Errno(e)} - } - return nil -} - -// Truncate changes the size of the file. -// It does not change the I/O offset. -func (f *File) Truncate(size int64) Error { - if e := syscall.Ftruncate(f.fd, size); e != 0 { - return &PathError{"truncate", f.name, Errno(e)} - } - return nil -} - -// Sync commits the current contents of the file to stable storage. -// Typically, this means flushing the file system's in-memory copy -// of recently written data to disk. -func (file *File) Sync() (err Error) { - if file == nil { - return EINVAL - } - if e := syscall.Fsync(file.fd); e != 0 { - return NewSyscallError("fsync", e) - } - return nil -} - -// Chtimes changes the access and modification times of the named -// file, similar to the Unix utime() or utimes() functions. -// -// The argument times are in nanoseconds, although the underlying -// filesystem may truncate or round the values to a more -// coarse time unit. -func Chtimes(name string, atime_ns int64, mtime_ns int64) Error { - var utimes [2]syscall.Timeval - utimes[0] = syscall.NsecToTimeval(atime_ns) - utimes[1] = syscall.NsecToTimeval(mtime_ns) - if e := syscall.Utimes(name, utimes[0:]); e != 0 { - return &PathError{"chtimes", name, Errno(e)} - } - return nil +// Create creates the named file mode 0666 (before umask), truncating +// it if it already exists. If successful, methods on the returned +// File can be used for I/O; the associated file descriptor has mode +// O_RDWR. +// It returns the File and an Error, if any. +func Create(name string) (file *File, err Error) { + return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666) } diff --git a/src/pkg/os/file_plan9.go b/src/pkg/os/file_plan9.go new file mode 100644 index 000000000..c8d0efba4 --- /dev/null +++ b/src/pkg/os/file_plan9.go @@ -0,0 +1,240 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package os + +import ( + "runtime" + "syscall" +) + +// Auxiliary information if the File describes a directory +type dirInfo struct { + buf [syscall.STATMAX]byte // buffer for directory I/O + nbuf int // length of buf; return value from Read + bufp int // location of next record in buf. +} + +func epipecheck(file *File, e syscall.Error) { +} + + +// 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" + +// OpenFile is the generalized open call; most users will use Open +// or Create instead. It 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 OpenFile(name string, flag int, perm uint32) (file *File, err Error) { + var fd int + var e syscall.Error + + syscall.ForkLock.RLock() + if flag&O_CREATE == O_CREATE { + fd, e = syscall.Create(name, flag & ^O_CREATE, perm) + } else { + fd, e = syscall.Open(name, flag) + } + syscall.ForkLock.RUnlock() + + if e != nil { + return nil, &PathError{"open", name, e} + } + + return NewFile(fd, name), nil +} + +// Close closes the File, rendering it unusable for I/O. +// It returns an Error, if any. +func (file *File) Close() Error { + if file == nil || file.fd < 0 { + return Ebadfd + } + var err Error + syscall.ForkLock.RLock() + if e := syscall.Close(file.fd); e != nil { + err = &PathError{"close", file.name, e} + } + syscall.ForkLock.RUnlock() + file.fd = -1 // so it can't be closed again + + // no need for a finalizer anymore + runtime.SetFinalizer(file, nil) + return err +} + +// Stat returns the FileInfo structure describing file. +// It returns the FileInfo and an error, if any. +func (file *File) Stat() (fi *FileInfo, err Error) { + return dirstat(file) +} + +// Truncate changes the size of the file. +// It does not change the I/O offset. +func (f *File) Truncate(size int64) Error { + var d Dir + d.Null() + + d.Length = uint64(size) + + if e := syscall.Fwstat(f.fd, pdir(nil, &d)); iserror(e) { + return &PathError{"truncate", f.name, e} + } + return nil +} + +// Chmod changes the mode of the file to mode. +func (f *File) Chmod(mode uint32) Error { + var d Dir + d.Null() + + d.Mode = mode & 0777 + + if e := syscall.Fwstat(f.fd, pdir(nil, &d)); iserror(e) { + return &PathError{"chmod", f.name, e} + } + return nil +} + +// Sync commits the current contents of the file to stable storage. +// Typically, this means flushing the file system's in-memory copy +// of recently written data to disk. +func (f *File) Sync() (err Error) { + if f == nil { + return EINVAL + } + + var d Dir + d.Null() + + if e := syscall.Fwstat(f.fd, pdir(nil, &d)); iserror(e) { + return NewSyscallError("fsync", e) + } + return nil +} + +// Truncate changes the size of the named file. +// If the file is a symbolic link, it changes the size of the link's target. +func Truncate(name string, size int64) Error { + var d Dir + d.Null() + + d.Length = uint64(size) + + if e := syscall.Wstat(name, pdir(nil, &d)); iserror(e) { + return &PathError{"truncate", name, e} + } + return nil +} + +// Remove removes the named file or directory. +func Remove(name string) Error { + if e := syscall.Remove(name); iserror(e) { + return &PathError{"remove", name, e} + } + return nil +} + +// Rename renames a file. +func Rename(oldname, newname string) Error { + var d Dir + d.Null() + + d.Name = newname + + if e := syscall.Wstat(oldname, pdir(nil, &d)); iserror(e) { + return &PathError{"rename", oldname, e} + } + return nil +} + +// Chmod changes the mode of the named file to mode. +func Chmod(name string, mode uint32) Error { + var d Dir + d.Null() + + d.Mode = mode & 0777 + + if e := syscall.Wstat(name, pdir(nil, &d)); iserror(e) { + return &PathError{"chmod", name, e} + } + return nil +} + +// ChownPlan9 changes the uid and gid strings of the named file. +func ChownPlan9(name, uid, gid string) Error { + var d Dir + d.Null() + + d.Uid = uid + d.Gid = gid + + if e := syscall.Wstat(name, pdir(nil, &d)); iserror(e) { + return &PathError{"chown_plan9", name, e} + } + return nil +} + +// Chtimes changes the access and modification times of the named +// file, similar to the Unix utime() or utimes() functions. +// +// The argument times are in nanoseconds, although the underlying +// filesystem may truncate or round the values to a more +// coarse time unit. +func Chtimes(name string, atimeNs int64, mtimeNs int64) Error { + var d Dir + d.Null() + + d.Atime = uint32(atimeNs / 1e9) + d.Mtime = uint32(mtimeNs / 1e9) + + if e := syscall.Wstat(name, pdir(nil, &d)); iserror(e) { + return &PathError{"chtimes", name, e} + } + return nil +} + +func Pipe() (r *File, w *File, err Error) { + var p [2]int + + syscall.ForkLock.RLock() + if e := syscall.Pipe(p[0:]); iserror(e) { + syscall.ForkLock.RUnlock() + return nil, nil, NewSyscallError("pipe", e) + } + syscall.ForkLock.RUnlock() + + return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil +} + + +// not supported on Plan 9 + +// Link creates a hard link. +func Link(oldname, newname string) Error { + return EPLAN9 +} + +func Symlink(oldname, newname string) Error { + return EPLAN9 +} + +func Readlink(name string) (string, Error) { + return "", EPLAN9 +} + +func Chown(name string, uid, gid int) Error { + return EPLAN9 +} + +func Lchown(name string, uid, gid int) Error { + return EPLAN9 +} + +func (f *File) Chown(uid, gid int) Error { + return EPLAN9 +} diff --git a/src/pkg/os/file_posix.go b/src/pkg/os/file_posix.go new file mode 100644 index 000000000..5151df498 --- /dev/null +++ b/src/pkg/os/file_posix.go @@ -0,0 +1,246 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The os package provides a platform-independent interface to operating +// system functionality. The design is Unix-like. +package os + +import ( + "syscall" +) + +func epipecheck(file *File, e int) { + if e == syscall.EPIPE { + file.nepipe++ + if file.nepipe >= 10 { + Exit(syscall.EPIPE) + } + } else { + file.nepipe = 0 + } +} + + +// Pipe returns a connected pair of Files; reads from r return bytes written to w. +// It returns the files and an Error, if any. +func Pipe() (r *File, w *File, err Error) { + var p [2]int + + // See ../syscall/exec.go for description of lock. + syscall.ForkLock.RLock() + e := syscall.Pipe(p[0:]) + if iserror(e) { + syscall.ForkLock.RUnlock() + return nil, nil, NewSyscallError("pipe", e) + } + syscall.CloseOnExec(p[0]) + syscall.CloseOnExec(p[1]) + syscall.ForkLock.RUnlock() + + return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil +} + +// Stat returns a FileInfo structure describing the named file and an error, if any. +// If name names a valid symbolic link, the returned FileInfo describes +// the file pointed at by the link and has fi.FollowedSymlink set to true. +// If name names an invalid symbolic link, the returned FileInfo describes +// the link itself and has fi.FollowedSymlink set to false. +func Stat(name string) (fi *FileInfo, err Error) { + var lstat, stat syscall.Stat_t + e := syscall.Lstat(name, &lstat) + if iserror(e) { + return nil, &PathError{"stat", name, Errno(e)} + } + statp := &lstat + if lstat.Mode&syscall.S_IFMT == syscall.S_IFLNK { + e := syscall.Stat(name, &stat) + if !iserror(e) { + statp = &stat + } + } + return fileInfoFromStat(name, new(FileInfo), &lstat, statp), nil +} + +// Lstat returns the FileInfo structure describing the named file and an +// error, if any. If the file is a symbolic link, the returned FileInfo +// describes the symbolic link. Lstat makes no attempt to follow the link. +func Lstat(name string) (fi *FileInfo, err Error) { + var stat syscall.Stat_t + e := syscall.Lstat(name, &stat) + if iserror(e) { + return nil, &PathError{"lstat", name, Errno(e)} + } + return fileInfoFromStat(name, new(FileInfo), &stat, &stat), nil +} + +// Remove removes the named file or directory. +func Remove(name string) Error { + // System call interface forces us to know + // whether name is a file or directory. + // Try both: it is cheaper on average than + // doing a Stat plus the right one. + e := syscall.Unlink(name) + if !iserror(e) { + return nil + } + e1 := syscall.Rmdir(name) + if !iserror(e1) { + return nil + } + + // Both failed: figure out which error to return. + // OS X and Linux differ on whether unlink(dir) + // returns EISDIR, so can't use that. However, + // both agree that rmdir(file) returns ENOTDIR, + // so we can use that to decide which error is real. + // Rmdir might also return ENOTDIR if given a bad + // file path, like /etc/passwd/foo, but in that case, + // both errors will be ENOTDIR, so it's okay to + // use the error from unlink. + // For windows syscall.ENOTDIR is set + // to syscall.ERROR_DIRECTORY, hopefully it should + // do the trick. + if e1 != syscall.ENOTDIR { + e = e1 + } + return &PathError{"remove", name, Errno(e)} +} + +// LinkError records an error during a link or symlink or rename +// system call and the paths that caused it. +type LinkError struct { + Op string + Old string + New string + Error Error +} + +func (e *LinkError) String() string { + return e.Op + " " + e.Old + " " + e.New + ": " + e.Error.String() +} + +// Link creates a hard link. +func Link(oldname, newname string) Error { + e := syscall.Link(oldname, newname) + if iserror(e) { + return &LinkError{"link", oldname, newname, Errno(e)} + } + return nil +} + +// Symlink creates a symbolic link. +func Symlink(oldname, newname string) Error { + e := syscall.Symlink(oldname, newname) + if iserror(e) { + return &LinkError{"symlink", oldname, newname, Errno(e)} + } + return nil +} + +// Readlink reads the contents of a symbolic link: the destination of +// the link. It returns the contents and an Error, if any. +func Readlink(name string) (string, Error) { + for len := 128; ; len *= 2 { + b := make([]byte, len) + n, e := syscall.Readlink(name, b) + if iserror(e) { + return "", &PathError{"readlink", name, Errno(e)} + } + if n < len { + return string(b[0:n]), nil + } + } + // Silence 6g. + return "", nil +} + +// Rename renames a file. +func Rename(oldname, newname string) Error { + e := syscall.Rename(oldname, newname) + if iserror(e) { + return &LinkError{"rename", oldname, newname, Errno(e)} + } + return nil +} + +// Chmod changes the mode of the named file to mode. +// If the file is a symbolic link, it changes the mode of the link's target. +func Chmod(name string, mode uint32) Error { + if e := syscall.Chmod(name, mode); iserror(e) { + return &PathError{"chmod", name, Errno(e)} + } + return nil +} + +// Chmod changes the mode of the file to mode. +func (f *File) Chmod(mode uint32) Error { + if e := syscall.Fchmod(f.fd, mode); iserror(e) { + return &PathError{"chmod", f.name, Errno(e)} + } + return nil +} + +// Chown changes the numeric uid and gid of the named file. +// If the file is a symbolic link, it changes the uid and gid of the link's target. +func Chown(name string, uid, gid int) Error { + if e := syscall.Chown(name, uid, gid); iserror(e) { + return &PathError{"chown", name, Errno(e)} + } + return nil +} + +// Lchown changes the numeric uid and gid of the named file. +// If the file is a symbolic link, it changes the uid and gid of the link itself. +func Lchown(name string, uid, gid int) Error { + if e := syscall.Lchown(name, uid, gid); iserror(e) { + return &PathError{"lchown", name, Errno(e)} + } + return nil +} + +// Chown changes the numeric uid and gid of the named file. +func (f *File) Chown(uid, gid int) Error { + if e := syscall.Fchown(f.fd, uid, gid); iserror(e) { + return &PathError{"chown", f.name, Errno(e)} + } + return nil +} + +// Truncate changes the size of the file. +// It does not change the I/O offset. +func (f *File) Truncate(size int64) Error { + if e := syscall.Ftruncate(f.fd, size); iserror(e) { + return &PathError{"truncate", f.name, Errno(e)} + } + return nil +} + +// Sync commits the current contents of the file to stable storage. +// Typically, this means flushing the file system's in-memory copy +// of recently written data to disk. +func (file *File) Sync() (err Error) { + if file == nil { + return EINVAL + } + if e := syscall.Fsync(file.fd); iserror(e) { + return NewSyscallError("fsync", e) + } + return nil +} + +// Chtimes changes the access and modification times of the named +// file, similar to the Unix utime() or utimes() functions. +// +// The argument times are in nanoseconds, although the underlying +// filesystem may truncate or round the values to a more +// coarse time unit. +func Chtimes(name string, atime_ns int64, mtime_ns int64) Error { + var utimes [2]syscall.Timeval + utimes[0] = syscall.NsecToTimeval(atime_ns) + utimes[1] = syscall.NsecToTimeval(mtime_ns) + if e := syscall.Utimes(name, utimes[0:]); iserror(e) { + return &PathError{"chtimes", name, Errno(e)} + } + return nil +} diff --git a/src/pkg/os/file_unix.go b/src/pkg/os/file_unix.go index df5894459..f2b94f4c2 100644 --- a/src/pkg/os/file_unix.go +++ b/src/pkg/os/file_unix.go @@ -20,10 +20,12 @@ type dirInfo struct { // 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. +// OpenFile is the generalized open call; most users will use Open +// or Create instead. It opens the named file with specified flag +// (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful, +// methods on the returned File can be used for I/O. // It returns the File and an Error, if any. -func Open(name string, flag int, perm uint32) (file *File, err Error) { +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)} @@ -102,3 +104,21 @@ func Truncate(name string, size int64) Error { } return nil } + +// basename removes trailing slashes and the leading directory name from path name +func basename(name string) string { + i := len(name) - 1 + // Remove trailing slashes + for ; i > 0 && name[i] == '/'; i-- { + name = name[:i] + } + // Remove leading directory name + for i--; i >= 0; i-- { + if name[i] == '/' { + name = name[i+1:] + break + } + } + + return name +} diff --git a/src/pkg/os/file_windows.go b/src/pkg/os/file_windows.go index d14c38e17..862baf6b9 100644 --- a/src/pkg/os/file_windows.go +++ b/src/pkg/os/file_windows.go @@ -46,10 +46,12 @@ func openDir(name string) (file *File, err Error) { return f, nil } -// 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. +// OpenFile is the generalized open call; most users will use Open +// or Create instead. It opens the named file with specified flag +// (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful, +// methods on the returned File can be used for I/O. // It returns the File and an Error, if any. -func Open(name string, flag int, perm uint32) (file *File, err Error) { +func OpenFile(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 { @@ -166,7 +168,7 @@ func (file *File) Readdir(count int) (fi []FileInfo, err Error) { // Truncate changes the size of the named file. // If the file is a symbolic link, it changes the size of the link's target. func Truncate(name string, size int64) Error { - f, e := Open(name, O_WRONLY|O_CREAT, 0666) + f, e := OpenFile(name, O_WRONLY|O_CREATE, 0666) if e != nil { return e } diff --git a/src/pkg/os/getwd.go b/src/pkg/os/getwd.go index 49aaea865..4c142ad3a 100644 --- a/src/pkg/os/getwd.go +++ b/src/pkg/os/getwd.go @@ -54,7 +54,7 @@ func Getwd() (string, Error) { if len(parent) >= 1024 { // Sanity check return "", ENAMETOOLONG } - fd, err := Open(parent, O_RDONLY, 0) + fd, err := Open(parent) if err != nil { return "", err } diff --git a/src/pkg/os/inotify/inotify_linux.go b/src/pkg/os/inotify/inotify_linux.go index 96c229e7b..8b5c30e0d 100644 --- a/src/pkg/os/inotify/inotify_linux.go +++ b/src/pkg/os/inotify/inotify_linux.go @@ -109,7 +109,7 @@ func (w *Watcher) AddWatch(path string, flags uint32) os.Error { } wd, errno := syscall.InotifyAddWatch(w.fd, path, flags) if wd == -1 { - return os.NewSyscallError("inotify_add_watch", errno) + return &os.PathError{"inotify_add_watch", path, os.Errno(errno)} } if !found { diff --git a/src/pkg/os/inotify/inotify_linux_test.go b/src/pkg/os/inotify/inotify_linux_test.go index 332edcb64..e29a46d6c 100644 --- a/src/pkg/os/inotify/inotify_linux_test.go +++ b/src/pkg/os/inotify/inotify_linux_test.go @@ -17,8 +17,8 @@ func TestInotifyEvents(t *testing.T) { t.Fatalf("NewWatcher() failed: %s", err) } - // Add a watch for "_obj" - err = watcher.Watch("_obj") + // Add a watch for "_test" + err = watcher.Watch("_test") if err != nil { t.Fatalf("Watcher.Watch() failed: %s", err) } @@ -30,11 +30,12 @@ func TestInotifyEvents(t *testing.T) { } }() - const testFile string = "_obj/TestInotifyEvents.testfile" + const testFile string = "_test/TestInotifyEvents.testfile" // Receive events on the event channel on a separate goroutine eventstream := watcher.Event var eventsReceived = 0 + done := make(chan bool) go func() { for event := range eventstream { // Only count relevant events @@ -45,11 +46,12 @@ func TestInotifyEvents(t *testing.T) { t.Logf("unexpected event received: %s", event) } } + done <- true }() // 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) + _, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) if err != nil { t.Fatalf("creating test file failed: %s", err) } @@ -64,16 +66,12 @@ func TestInotifyEvents(t *testing.T) { 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++ + select { + case <-done: + t.Log("event channel closed") + case <-time.After(1e9): + t.Fatal("event stream was not closed after 1 second") } - t.Log("event channel closed") } @@ -92,7 +90,7 @@ func TestInotifyClose(t *testing.T) { t.Fatal("double Close() test failed: second Close() call didn't return") } - err := watcher.Watch("_obj") + err := watcher.Watch("_test") 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 2ea8acdc4..551b86508 100644 --- a/src/pkg/os/os_test.go +++ b/src/pkg/os/os_test.go @@ -10,14 +10,14 @@ import ( "io" "io/ioutil" . "os" + "path/filepath" "strings" "syscall" "testing" ) var dot = []string{ - "dir_darwin.go", - "dir_linux.go", + "dir_unix.go", "env_unix.go", "error.go", "file.go", @@ -45,6 +45,14 @@ var sysdir = func() (sd *sysDir) { "services", }, } + case "plan9": + sd = &sysDir{ + "/lib/ndb", + []string{ + "common", + "local", + }, + } default: sd = &sysDir{ "/etc", @@ -59,7 +67,7 @@ var sysdir = func() (sd *sysDir) { }() func size(name string, t *testing.T) int64 { - file, err := Open(name, O_RDONLY, 0) + file, err := Open(name) defer file.Close() if err != nil { t.Fatal("open failed:", err) @@ -124,7 +132,7 @@ func TestStat(t *testing.T) { func TestFstat(t *testing.T) { path := sfdir + "/" + sfname - file, err1 := Open(path, O_RDONLY, 0) + file, err1 := Open(path) defer file.Close() if err1 != nil { t.Fatal("open failed:", err1) @@ -158,7 +166,7 @@ func TestLstat(t *testing.T) { } func testReaddirnames(dir string, contents []string, t *testing.T) { - file, err := Open(dir, O_RDONLY, 0) + file, err := Open(dir) defer file.Close() if err != nil { t.Fatalf("open %q failed: %v", dir, err) @@ -187,7 +195,7 @@ func testReaddirnames(dir string, contents []string, t *testing.T) { } func testReaddir(dir string, contents []string, t *testing.T) { - file, err := Open(dir, O_RDONLY, 0) + file, err := Open(dir) defer file.Close() if err != nil { t.Fatalf("open %q failed: %v", dir, err) @@ -245,10 +253,13 @@ func smallReaddirnames(file *File, length int, t *testing.T) []string { func TestReaddirnamesOneAtATime(t *testing.T) { // big directory that doesn't change often. dir := "/usr/bin" - if syscall.OS == "windows" { + switch syscall.OS { + case "windows": dir = Getenv("SystemRoot") + "\\system32" + case "plan9": + dir = "/bin" } - file, err := Open(dir, O_RDONLY, 0) + file, err := Open(dir) defer file.Close() if err != nil { t.Fatalf("open %q failed: %v", dir, err) @@ -257,11 +268,14 @@ func TestReaddirnamesOneAtATime(t *testing.T) { if err1 != nil { t.Fatalf("readdirnames %q failed: %v", dir, err1) } - file1, err2 := Open(dir, O_RDONLY, 0) + file1, err2 := Open(dir) if err2 != nil { t.Fatalf("open %q failed: %v", dir, err2) } small := smallReaddirnames(file1, len(all)+100, t) // +100 in case we screw up + if len(small) < len(all) { + t.Fatalf("len(small) is %d, less than %d", len(small), len(all)) + } for i, n := range all { if small[i] != n { t.Errorf("small read %q mismatch: %v", small[i], n) @@ -276,7 +290,7 @@ func TestHardLink(t *testing.T) { } from, to := "hardlinktestfrom", "hardlinktestto" Remove(from) // Just in case. - file, err := Open(to, O_CREAT|O_WRONLY, 0666) + file, err := Create(to) if err != nil { t.Fatalf("open %q failed: %v", to, err) } @@ -309,7 +323,7 @@ func TestSymLink(t *testing.T) { } from, to := "symlinktestfrom", "symlinktestto" Remove(from) // Just in case. - file, err := Open(to, O_CREAT|O_WRONLY, 0666) + file, err := Create(to) if err != nil { t.Fatalf("open %q failed: %v", to, err) } @@ -357,7 +371,7 @@ func TestSymLink(t *testing.T) { if s != to { t.Fatalf("after symlink %q != %q", s, to) } - file, err = Open(from, O_RDONLY, 0) + file, err = Open(from) if err != nil { t.Fatalf("open %q failed: %v", from, err) } @@ -391,7 +405,7 @@ func TestLongSymlink(t *testing.T) { func TestRename(t *testing.T) { from, to := "renamefrom", "renameto" Remove(to) // Just in case. - file, err := Open(from, O_CREAT|O_WRONLY, 0666) + file, err := Create(from) if err != nil { t.Fatalf("open %q failed: %v", to, err) } @@ -409,25 +423,13 @@ func TestRename(t *testing.T) { } } -func TestForkExec(t *testing.T) { - var cmd, adir, expect string - var args []string +func exec(t *testing.T, dir, cmd string, args []string, expect string) { r, w, err := Pipe() if err != nil { t.Fatalf("Pipe: %v", err) } - if syscall.OS == "windows" { - cmd = Getenv("COMSPEC") - args = []string{Getenv("COMSPEC"), "/c cd"} - adir = Getenv("SystemRoot") - expect = Getenv("SystemRoot") + "\r\n" - } else { - cmd = "/bin/pwd" - args = []string{"pwd"} - adir = "/" - expect = "/\n" - } - p, err := StartProcess(cmd, args, nil, adir, []*File{nil, w, Stderr}) + attr := &ProcAttr{Dir: dir, Files: []*File{nil, w, Stderr}} + p, err := StartProcess(cmd, args, attr) if err != nil { t.Fatalf("StartProcess: %v", err) } @@ -438,12 +440,34 @@ func TestForkExec(t *testing.T) { io.Copy(&b, r) output := b.String() if output != expect { - args[0] = cmd - t.Errorf("exec %q returned %q wanted %q", strings.Join(args, " "), output, expect) + t.Errorf("exec %q returned %q wanted %q", + strings.Join(append([]string{cmd}, args...), " "), output, expect) } p.Wait(0) } +func TestStartProcess(t *testing.T) { + var dir, cmd, le string + var args []string + if syscall.OS == "windows" { + le = "\r\n" + cmd = Getenv("COMSPEC") + dir = Getenv("SystemRoot") + args = []string{"/c", "cd"} + } else { + le = "\n" + cmd = "/bin/pwd" + dir = "/" + args = []string{} + } + cmddir, cmdbase := filepath.Split(cmd) + args = append([]string{cmdbase}, args...) + // Test absolute executable path. + exec(t, dir, cmd, args, dir+le) + // Test relative executable path. + exec(t, cmddir, cmdbase, args, filepath.Clean(cmddir)+le) +} + func checkMode(t *testing.T, path string, mode uint32) { dir, err := Stat(path) if err != nil { @@ -608,19 +632,19 @@ func TestChdirAndGetwd(t *testing.T) { if syscall.OS == "windows" { return } - fd, err := Open(".", O_RDONLY, 0) + fd, err := Open(".") if err != nil { t.Fatalf("Open .: %s", err) } // These are chosen carefully not to be symlinks on a Mac // (unlike, say, /var, /etc, and /tmp). - dirs := []string{"/bin", "/", "/usr/bin"} + dirs := []string{"/", "/usr/bin"} for mode := 0; mode < 2; mode++ { for _, d := range dirs { if mode == 0 { err = Chdir(d) } else { - fd1, err := Open(d, O_RDONLY, 0) + fd1, err := Open(d) if err != nil { t.Errorf("Open %s: %s", d, err) continue @@ -729,7 +753,7 @@ var openErrorTests = []openErrorTest{ func TestOpenError(t *testing.T) { for _, tt := range openErrorTests { - f, err := Open(tt.path, tt.mode, 0) + f, err := OpenFile(tt.path, tt.mode, 0) if err == nil { t.Errorf("Open(%q, %d) succeeded", tt.path, tt.mode) f.Close() @@ -751,7 +775,7 @@ func run(t *testing.T, cmd []string) string { if err != nil { t.Fatal(err) } - p, err := StartProcess("/bin/hostname", []string{"hostname"}, nil, "/", []*File{nil, w, Stderr}) + p, err := StartProcess("/bin/hostname", []string{"hostname"}, &ProcAttr{Files: []*File{nil, w, Stderr}}) if err != nil { t.Fatal(err) } @@ -835,7 +859,7 @@ func TestWriteAt(t *testing.T) { } func writeFile(t *testing.T, fname string, flag int, text string) string { - f, err := Open(fname, flag, 0666) + f, err := OpenFile(fname, flag, 0666) if err != nil { t.Fatalf("Open: %v", err) } @@ -854,7 +878,7 @@ func writeFile(t *testing.T, fname string, flag int, text string) string { func TestAppend(t *testing.T) { const f = "append.txt" defer Remove(f) - s := writeFile(t, f, O_CREAT|O_TRUNC|O_RDWR, "new") + s := writeFile(t, f, O_CREATE|O_TRUNC|O_RDWR, "new") if s != "new" { t.Fatalf("writeFile: have %q want %q", s, "new") } diff --git a/src/pkg/os/path.go b/src/pkg/os/path.go index b762971d9..0eb3ee503 100644 --- a/src/pkg/os/path.go +++ b/src/pkg/os/path.go @@ -33,7 +33,7 @@ func MkdirAll(path string, perm uint32) Error { j-- } - if j > 0 { + if j > 1 { // Create parent err = MkdirAll(path[0:j-1], perm) if err != nil { @@ -80,7 +80,7 @@ func RemoveAll(path string) Error { } // Directory. - fd, err := Open(path, O_RDONLY, 0) + fd, err := Open(path) if err != nil { return err } diff --git a/src/pkg/os/path_test.go b/src/pkg/os/path_test.go index 799e3ec2f..483bb6395 100644 --- a/src/pkg/os/path_test.go +++ b/src/pkg/os/path_test.go @@ -29,7 +29,7 @@ func TestMkdirAll(t *testing.T) { // Make file. fpath := path + "/file" - _, err = Open(fpath, O_WRONLY|O_CREAT, 0666) + _, err = Create(fpath) if err != nil { t.Fatalf("create %q: %s", fpath, err) } @@ -72,7 +72,7 @@ func TestRemoveAll(t *testing.T) { if err := MkdirAll(path, 0777); err != nil { t.Fatalf("MkdirAll %q: %s", path, err) } - fd, err := Open(fpath, O_WRONLY|O_CREAT, 0666) + fd, err := Create(fpath) if err != nil { t.Fatalf("create %q: %s", fpath, err) } @@ -88,12 +88,12 @@ func TestRemoveAll(t *testing.T) { if err = MkdirAll(dpath, 0777); err != nil { t.Fatalf("MkdirAll %q: %s", dpath, err) } - fd, err = Open(fpath, O_WRONLY|O_CREAT, 0666) + fd, err = Create(fpath) if err != nil { t.Fatalf("create %q: %s", fpath, err) } fd.Close() - fd, err = Open(dpath+"/file", O_WRONLY|O_CREAT, 0666) + fd, err = Create(dpath + "/file") if err != nil { t.Fatalf("create %q: %s", fpath, err) } @@ -121,7 +121,7 @@ func TestRemoveAll(t *testing.T) { } for _, s := range []string{fpath, dpath + "/file1", path + "/zzz"} { - fd, err = Open(s, O_WRONLY|O_CREAT, 0666) + fd, err = Create(s) if err != nil { t.Fatalf("create %q: %s", s, err) } @@ -179,3 +179,20 @@ func TestMkdirAllWithSymlink(t *testing.T) { t.Errorf("MkdirAll %q: %s", path, err) } } + +func TestMkdirAllAtSlash(t *testing.T) { + if runtime.GOOS == "windows" { + return + } + RemoveAll("/_go_os_test") + err := MkdirAll("/_go_os_test/dir", 0777) + if err != nil { + pathErr, ok := err.(*PathError) + // common for users not to be able to write to / + if ok && pathErr.Error == EACCES { + return + } + t.Fatalf(`MkdirAll "/_go_os_test/dir": %v`, err) + } + RemoveAll("/_go_os_test") +} diff --git a/src/pkg/os/proc.go b/src/pkg/os/proc.go index dfddab6cb..481ef6033 100644 --- a/src/pkg/os/proc.go +++ b/src/pkg/os/proc.go @@ -26,8 +26,8 @@ func Getegid() int { return syscall.Getegid() } // Getgroups returns a list of the numeric ids of groups that the caller belongs to. func Getgroups() ([]int, Error) { - gids, errno := syscall.Getgroups() - return gids, NewSyscallError("getgroups", errno) + gids, e := syscall.Getgroups() + return gids, NewSyscallError("getgroups", e) } // Exit causes the current program to exit with the given status code. diff --git a/src/pkg/os/stat_darwin.go b/src/pkg/os/stat_darwin.go index 8f4e6bafa..0661a6d59 100644 --- a/src/pkg/os/stat_darwin.go +++ b/src/pkg/os/stat_darwin.go @@ -24,13 +24,7 @@ func fileInfoFromStat(name string, fi *FileInfo, lstat, stat *syscall.Stat_t) *F fi.Atime_ns = syscall.TimespecToNsec(stat.Atimespec) fi.Mtime_ns = syscall.TimespecToNsec(stat.Mtimespec) fi.Ctime_ns = syscall.TimespecToNsec(stat.Ctimespec) - for i := len(name) - 1; i >= 0; i-- { - if name[i] == '/' { - name = name[i+1:] - break - } - } - fi.Name = name + fi.Name = basename(name) if isSymlink(lstat) && !isSymlink(stat) { fi.FollowedSymlink = true } diff --git a/src/pkg/os/stat_freebsd.go b/src/pkg/os/stat_freebsd.go index aa15d4b63..454165d4e 100644 --- a/src/pkg/os/stat_freebsd.go +++ b/src/pkg/os/stat_freebsd.go @@ -24,13 +24,7 @@ func fileInfoFromStat(name string, fi *FileInfo, lstat, stat *syscall.Stat_t) *F fi.Atime_ns = syscall.TimespecToNsec(stat.Atimespec) fi.Mtime_ns = syscall.TimespecToNsec(stat.Mtimespec) fi.Ctime_ns = syscall.TimespecToNsec(stat.Ctimespec) - for i := len(name) - 1; i >= 0; i-- { - if name[i] == '/' { - name = name[i+1:] - break - } - } - fi.Name = name + fi.Name = basename(name) if isSymlink(lstat) && !isSymlink(stat) { fi.FollowedSymlink = true } diff --git a/src/pkg/os/stat_linux.go b/src/pkg/os/stat_linux.go index ebfa1721c..7a3cf794d 100644 --- a/src/pkg/os/stat_linux.go +++ b/src/pkg/os/stat_linux.go @@ -24,13 +24,7 @@ func fileInfoFromStat(name string, fi *FileInfo, lstat, stat *syscall.Stat_t) *F fi.Atime_ns = syscall.TimespecToNsec(stat.Atim) fi.Mtime_ns = syscall.TimespecToNsec(stat.Mtim) fi.Ctime_ns = syscall.TimespecToNsec(stat.Ctim) - for i := len(name) - 1; i >= 0; i-- { - if name[i] == '/' { - name = name[i+1:] - break - } - } - fi.Name = name + fi.Name = basename(name) if isSymlink(lstat) && !isSymlink(stat) { fi.FollowedSymlink = true } diff --git a/src/pkg/os/stat_plan9.go b/src/pkg/os/stat_plan9.go new file mode 100644 index 000000000..e96749d33 --- /dev/null +++ b/src/pkg/os/stat_plan9.go @@ -0,0 +1,84 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package os + +import "syscall" + +func fileInfoFromStat(fi *FileInfo, d *Dir) *FileInfo { + fi.Dev = uint64(d.Qid.Vers) | uint64(d.Qid.Type<<32) + fi.Ino = d.Qid.Path + + fi.Mode = uint32(d.Mode) & 0777 + if (d.Mode & syscall.DMDIR) == syscall.DMDIR { + fi.Mode |= syscall.S_IFDIR + } else { + fi.Mode |= syscall.S_IFREG + } + + fi.Size = int64(d.Length) + fi.Atime_ns = 1e9 * int64(d.Atime) + fi.Mtime_ns = 1e9 * int64(d.Mtime) + fi.Name = d.Name + fi.FollowedSymlink = false + return fi +} + +// arg is an open *File or a path string. +func dirstat(arg interface{}) (fi *FileInfo, err Error) { + var name string + nd := syscall.STATFIXLEN + 16*4 + + for i := 0; i < 2; i++ { /* should work by the second try */ + buf := make([]byte, nd) + + var n int + var e syscall.Error + + switch syscallArg := arg.(type) { + case *File: + name = syscallArg.name + n, e = syscall.Fstat(syscallArg.fd, buf) + case string: + name = syscallArg + n, e = syscall.Stat(name, buf) + } + + if e != nil { + return nil, &PathError{"stat", name, e} + } + + if n < syscall.STATFIXLEN { + return nil, &PathError{"stat", name, Eshortstat} + } + + ntmp, _ := gbit16(buf) + nd = int(ntmp) + + if nd <= n { + d, e := UnmarshalDir(buf[:n]) + + if e != nil { + return nil, &PathError{"stat", name, e} + } + + return fileInfoFromStat(new(FileInfo), d), nil + } + } + + return nil, &PathError{"stat", name, Ebadstat} +} + + +// Stat returns a FileInfo structure describing the named file and an error, if any. +func Stat(name string) (fi *FileInfo, err Error) { + return dirstat(name) +} + +// Lstat returns the FileInfo structure describing the named file and an +// error, if any. If the file is a symbolic link (though Plan 9 does not have symbolic links), +// the returned FileInfo describes the symbolic link. Lstat makes no attempt to follow the link. +func Lstat(name string) (fi *FileInfo, err Error) { + return dirstat(name) +} diff --git a/src/pkg/os/sys_linux.go b/src/pkg/os/sys_linux.go index b82d295d3..408d667c7 100644 --- a/src/pkg/os/sys_linux.go +++ b/src/pkg/os/sys_linux.go @@ -9,7 +9,7 @@ package os // Hostname returns the host name reported by the kernel. func Hostname() (name string, err Error) { - f, err := Open("/proc/sys/kernel/hostname", O_RDONLY, 0) + f, err := Open("/proc/sys/kernel/hostname") if err != nil { return "", err } diff --git a/src/pkg/os/sys_plan9.go b/src/pkg/os/sys_plan9.go new file mode 100644 index 000000000..f6af28b61 --- /dev/null +++ b/src/pkg/os/sys_plan9.go @@ -0,0 +1,27 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Plan 9-specific + +package os + + +func Hostname() (name string, err Error) { + f, err := Open("#c/sysname") + if err != nil { + return "", err + } + defer f.Close() + + var buf [128]byte + n, err := f.Read(buf[:len(buf)-1]) + + if err != nil { + return "", err + } + if n > 0 { + buf[n] = 0 + } + return string(buf[0:n]), nil +} diff --git a/src/pkg/os/time.go b/src/pkg/os/time.go index 380345f1b..8e87a49e1 100644 --- a/src/pkg/os/time.go +++ b/src/pkg/os/time.go @@ -13,8 +13,8 @@ import "syscall" // time is the Unix epoch. func Time() (sec int64, nsec int64, err Error) { var tv syscall.Timeval - if errno := syscall.Gettimeofday(&tv); errno != 0 { - return 0, 0, NewSyscallError("gettimeofday", errno) + if e := syscall.Gettimeofday(&tv); iserror(e) { + return 0, 0, NewSyscallError("gettimeofday", e) } return int64(tv.Sec), int64(tv.Usec) * 1000, err } |