summaryrefslogtreecommitdiff
path: root/src/pkg/os/file_windows.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/os/file_windows.go')
-rw-r--r--src/pkg/os/file_windows.go248
1 files changed, 139 insertions, 109 deletions
diff --git a/src/pkg/os/file_windows.go b/src/pkg/os/file_windows.go
index 70dd6e241..88fa77bb8 100644
--- a/src/pkg/os/file_windows.go
+++ b/src/pkg/os/file_windows.go
@@ -5,13 +5,23 @@
package os
import (
+ "io"
"runtime"
"sync"
"syscall"
+ "unicode/utf16"
)
// File represents an open file descriptor.
type File struct {
+ *file
+}
+
+// file is the real representation of *File.
+// The extra level of indirection ensures that no clients of os
+// can overwrite this data, which could cause the finalizer
+// to close the wrong file descriptor.
+type file struct {
fd syscall.Handle
name string
dirinfo *dirInfo // nil unless directory being read
@@ -20,37 +30,39 @@ type File struct {
}
// Fd returns the Windows handle referencing the open file.
-func (file *File) Fd() syscall.Handle {
+func (file *File) Fd() uintptr {
if file == nil {
- return syscall.InvalidHandle
+ return uintptr(syscall.InvalidHandle)
}
- return file.fd
+ return uintptr(file.fd)
}
// NewFile returns a new File with the given file descriptor and name.
-func NewFile(fd syscall.Handle, name string) *File {
- if fd < 0 {
+func NewFile(fd uintptr, name string) *File {
+ h := syscall.Handle(fd)
+ if h == syscall.InvalidHandle {
return nil
}
- f := &File{fd: fd, name: name}
- runtime.SetFinalizer(f, (*File).Close)
+ f := &File{&file{fd: h, name: name}}
+ runtime.SetFinalizer(f.file, (*file).close)
return f
}
// Auxiliary information if the File describes a directory
type dirInfo struct {
- stat syscall.Stat_t
- usefirststat bool
+ data syscall.Win32finddata
+ needdata bool
+ path string
}
const DevNull = "NUL"
-func (file *File) isdir() bool { return file != nil && file.dirinfo != nil }
+func (f *file) isdir() bool { return f != nil && f.dirinfo != nil }
-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)}
+func openFile(name string, flag int, perm FileMode) (file *File, err error) {
+ r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
+ if e != nil {
+ return nil, &PathError{"open", name, e}
}
// There's a race here with fork/exec, which we are
@@ -59,17 +71,21 @@ func openFile(name string, flag int, perm uint32) (file *File, err Error) {
syscall.CloseOnExec(r)
}
- return NewFile(r, name), nil
+ return NewFile(uintptr(r), name), nil
}
-func openDir(name string) (file *File, err Error) {
+func openDir(name string) (file *File, err error) {
d := new(dirInfo)
- r, e := syscall.FindFirstFile(syscall.StringToUTF16Ptr(name+"\\*"), &d.stat.Windata)
- if e != 0 {
- return nil, &PathError{"open", name, Errno(e)}
+ r, e := syscall.FindFirstFile(syscall.StringToUTF16Ptr(name+`\*`), &d.data)
+ if e != nil {
+ return nil, &PathError{"open", name, e}
+ }
+ d.path = name
+ if !isAbs(d.path) {
+ cwd, _ := Getwd()
+ d.path = cwd + `\` + d.path
}
- f := NewFile(r, name)
- d.usefirststat = true
+ f := NewFile(uintptr(r), name)
f.dirinfo = d
return f, nil
}
@@ -78,14 +94,17 @@ func openDir(name string) (file *File, err Error) {
// 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) {
+// If there is an error, it will be of type *PathError.
+func OpenFile(name string, flag int, perm FileMode) (file *File, err error) {
+ if name == "" {
+ return nil, &PathError{"open", name, syscall.ENOENT}
+ }
// 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 nil, &PathError{"open", name, syscall.EISDIR}
}
return r, nil
}
@@ -93,33 +112,28 @@ func OpenFile(name string, flag int, perm uint32) (file *File, err Error) {
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
}
// Close closes the File, rendering it unusable for I/O.
-// It returns an Error, if any.
-func (file *File) Close() Error {
- if file == nil || file.fd < 0 {
- return EINVAL
+// It returns an error, if any.
+func (file *File) Close() error {
+ return file.file.close()
+}
+
+func (file *file) close() error {
+ if file == nil || file.fd == syscall.InvalidHandle {
+ return syscall.EINVAL
}
- var e int
+ var e error
if file.isdir() {
e = syscall.FindClose(syscall.Handle(file.fd))
} else {
e = syscall.CloseHandle(syscall.Handle(file.fd))
}
- var err Error
- if e != 0 {
- err = &PathError{"close", file.name, Errno(e)}
+ var err error
+ if e != nil {
+ err = &PathError{"close", file.name, e}
}
file.fd = syscall.InvalidHandle // so it can't be closed again
@@ -128,51 +142,13 @@ func (file *File) Close() Error {
return err
}
-func (file *File) statFile(name string) (fi *FileInfo, err Error) {
- var stat syscall.ByHandleFileInformation
- e := syscall.GetFileInformationByHandle(syscall.Handle(file.fd), &stat)
- if e != 0 {
- return nil, &PathError{"stat", file.name, Errno(e)}
- }
- return fileInfoFromByHandleInfo(new(FileInfo), file.name, &stat), nil
-}
-
-// Stat returns the FileInfo structure describing file.
-// It returns the FileInfo and an error, if any.
-func (file *File) Stat() (fi *FileInfo, err Error) {
- if file == nil || file.fd < 0 {
- return nil, EINVAL
- }
- if file.isdir() {
- // I don't know any better way to do that for directory
- return Stat(file.name)
- }
- return file.statFile(file.name)
-}
-
-// Readdir reads the contents of the directory associated with file and
-// returns an array of up to n FileInfo structures, as would be returned
-// by Lstat, in directory order. Subsequent calls on the same file will yield
-// further FileInfos.
-//
-// If n > 0, Readdir returns at most n FileInfo structures. In this case, if
-// Readdir returns an empty slice, it will return a non-nil error
-// explaining why. At the end of a directory, the error is os.EOF.
-//
-// If n <= 0, Readdir returns all the FileInfo from the directory in
-// a single slice. In this case, if Readdir succeeds (reads all
-// the way to the end of the directory), it returns the slice and a
-// nil os.Error. If it encounters an error before the end of the
-// directory, Readdir returns the FileInfo read until that point
-// and a non-nil error.
-func (file *File) Readdir(n int) (fi []FileInfo, err Error) {
- if file == nil || file.fd < 0 {
- return nil, EINVAL
+func (file *File) readdir(n int) (fi []FileInfo, err error) {
+ if file == nil || file.fd == syscall.InvalidHandle {
+ return nil, syscall.EINVAL
}
if !file.isdir() {
- return nil, &PathError{"Readdir", file.name, ENOTDIR}
+ return nil, &PathError{"Readdir", file.name, syscall.ENOTDIR}
}
- di := file.dirinfo
wantAll := n <= 0
size := n
if wantAll {
@@ -180,16 +156,15 @@ func (file *File) Readdir(n int) (fi []FileInfo, err Error) {
size = 100
}
fi = make([]FileInfo, 0, size) // Empty with room to grow.
+ d := &file.dirinfo.data
for n != 0 {
- if di.usefirststat {
- di.usefirststat = false
- } else {
- e := syscall.FindNextFile(syscall.Handle(file.fd), &di.stat.Windata)
- if e != 0 {
+ if file.dirinfo.needdata {
+ e := syscall.FindNextFile(syscall.Handle(file.fd), d)
+ if e != nil {
if e == syscall.ERROR_NO_MORE_FILES {
break
} else {
- err = &PathError{"FindNextFile", file.name, Errno(e)}
+ err = &PathError{"FindNextFile", file.name, e}
if !wantAll {
fi = nil
}
@@ -197,23 +172,30 @@ func (file *File) Readdir(n int) (fi []FileInfo, err Error) {
}
}
}
- var f FileInfo
- fileInfoFromWin32finddata(&f, &di.stat.Windata)
- if f.Name == "." || f.Name == ".." { // Useless names
+ file.dirinfo.needdata = true
+ name := string(syscall.UTF16ToString(d.FileName[0:]))
+ if name == "." || name == ".." { // Useless names
continue
}
+ f := &fileStat{
+ name: name,
+ size: mkSize(d.FileSizeHigh, d.FileSizeLow),
+ modTime: mkModTime(d.LastWriteTime),
+ mode: mkMode(d.FileAttributes),
+ sys: mkSys(file.dirinfo.path+`\`+name, d.LastAccessTime, d.CreationTime),
+ }
n--
fi = append(fi, f)
}
if !wantAll && len(fi) == 0 {
- return fi, EOF
+ return fi, io.EOF
}
return fi, nil
}
// read reads up to len(b) bytes from the File.
// It returns the number of bytes read and an error, if any.
-func (f *File) read(b []byte) (n int, err int) {
+func (f *File) read(b []byte) (n int, err error) {
f.l.Lock()
defer f.l.Unlock()
return syscall.Read(f.fd, b)
@@ -222,11 +204,11 @@ func (f *File) read(b []byte) (n int, err int) {
// pread reads len(b) bytes from the File starting at byte offset off.
// It returns the number of bytes read and the error, if any.
// EOF is signaled by a zero count with err set to 0.
-func (f *File) pread(b []byte, off int64) (n int, err int) {
+func (f *File) pread(b []byte, off int64) (n int, err error) {
f.l.Lock()
defer f.l.Unlock()
curoffset, e := syscall.Seek(f.fd, 0, 1)
- if e != 0 {
+ if e != nil {
return 0, e
}
defer syscall.Seek(f.fd, curoffset, 0)
@@ -236,15 +218,15 @@ func (f *File) pread(b []byte, off int64) (n int, err int) {
}
var done uint32
e = syscall.ReadFile(syscall.Handle(f.fd), b, &done, &o)
- if e != 0 {
+ if e != nil {
return 0, e
}
- return int(done), 0
+ return int(done), nil
}
// write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
-func (f *File) write(b []byte) (n int, err int) {
+func (f *File) write(b []byte) (n int, err error) {
f.l.Lock()
defer f.l.Unlock()
return syscall.Write(f.fd, b)
@@ -252,11 +234,11 @@ func (f *File) write(b []byte) (n int, err int) {
// pwrite writes len(b) bytes to the File starting at byte offset off.
// It returns the number of bytes written and an error, if any.
-func (f *File) pwrite(b []byte, off int64) (n int, err int) {
+func (f *File) pwrite(b []byte, off int64) (n int, err error) {
f.l.Lock()
defer f.l.Unlock()
curoffset, e := syscall.Seek(f.fd, 0, 1)
- if e != 0 {
+ if e != nil {
return 0, e
}
defer syscall.Seek(f.fd, curoffset, 0)
@@ -266,17 +248,17 @@ func (f *File) pwrite(b []byte, off int64) (n int, err int) {
}
var done uint32
e = syscall.WriteFile(syscall.Handle(f.fd), b, &done, &o)
- if e != 0 {
+ if e != nil {
return 0, e
}
- return int(done), 0
+ return int(done), nil
}
// seek sets the offset for the next Read or Write on file to offset, interpreted
// according to whence: 0 means relative to the origin of the file, 1 means
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an error, if any.
-func (f *File) seek(offset int64, whence int) (ret int64, err int) {
+func (f *File) seek(offset int64, whence int) (ret int64, err error) {
f.l.Lock()
defer f.l.Unlock()
return syscall.Seek(f.fd, offset, whence)
@@ -284,7 +266,7 @@ func (f *File) seek(offset int64, whence int) (ret int64, err int) {
// 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 {
+func Truncate(name string, size int64) error {
f, e := OpenFile(name, O_WRONLY|O_CREATE, 0666)
if e != nil {
return e
@@ -297,15 +279,45 @@ func Truncate(name string, size int64) Error {
return nil
}
+// Remove removes the named file or directory.
+// If there is an error, it will be of type *PathError.
+func Remove(name string) error {
+ p := &syscall.StringToUTF16(name)[0]
+
+ // Go file interface forces us to know whether
+ // name is a file or directory. Try both.
+ e := syscall.DeleteFile(p)
+ if e == nil {
+ return nil
+ }
+ e1 := syscall.RemoveDirectory(p)
+ if e1 == nil {
+ return nil
+ }
+
+ // Both failed: figure out which error to return.
+ if e1 != e {
+ a, e2 := syscall.GetFileAttributes(p)
+ if e2 != nil {
+ e = e2
+ } else {
+ if a&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
+ e = e1
+ }
+ }
+ }
+ return &PathError{"remove", name, e}
+}
+
// 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) {
+// It returns the files and an error, if any.
+func Pipe() (r *File, w *File, err error) {
var p [2]syscall.Handle
// See ../syscall/exec.go for description of lock.
syscall.ForkLock.RLock()
e := syscall.Pipe(p[0:])
- if iserror(e) {
+ if e != nil {
syscall.ForkLock.RUnlock()
return nil, nil, NewSyscallError("pipe", e)
}
@@ -313,5 +325,23 @@ func Pipe() (r *File, w *File, err Error) {
syscall.CloseOnExec(p[1])
syscall.ForkLock.RUnlock()
- return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil
+ return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil
+}
+
+// TempDir returns the default directory to use for temporary files.
+func TempDir() string {
+ const pathSep = '\\'
+ dirw := make([]uint16, syscall.MAX_PATH)
+ n, _ := syscall.GetTempPath(uint32(len(dirw)), &dirw[0])
+ if n > uint32(len(dirw)) {
+ dirw = make([]uint16, n)
+ n, _ = syscall.GetTempPath(uint32(len(dirw)), &dirw[0])
+ if n > uint32(len(dirw)) {
+ n = 0
+ }
+ }
+ if n > 0 && dirw[n-1] == pathSep {
+ n--
+ }
+ return string(utf16.Decode(dirw[0:n]))
}