diff --git a/src/pkg/os/file_windows.go b/src/pkg/os/file_windows.go index f11b273f63..839d14627f 100644 --- a/src/pkg/os/file_windows.go +++ b/src/pkg/os/file_windows.go @@ -222,11 +222,16 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) { 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), + name: name, + sys: syscall.Win32FileAttributeData{ + FileAttributes: d.FileAttributes, + CreationTime: d.CreationTime, + LastAccessTime: d.LastAccessTime, + LastWriteTime: d.LastWriteTime, + FileSizeHigh: d.FileSizeHigh, + FileSizeLow: d.FileSizeLow, + }, + path: file.dirinfo.path + `\` + name, } n-- fi = append(fi, f) diff --git a/src/pkg/os/stat_darwin.go b/src/pkg/os/stat_darwin.go index 2e5967d5c8..0eea522015 100644 --- a/src/pkg/os/stat_darwin.go +++ b/src/pkg/os/stat_darwin.go @@ -9,9 +9,9 @@ import ( "time" ) -func sameFile(sys1, sys2 interface{}) bool { - stat1 := sys1.(*syscall.Stat_t) - stat2 := sys2.(*syscall.Stat_t) +func sameFile(fs1, fs2 *fileStat) bool { + stat1 := fs1.sys.(*syscall.Stat_t) + stat2 := fs2.sys.(*syscall.Stat_t) return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino } diff --git a/src/pkg/os/stat_freebsd.go b/src/pkg/os/stat_freebsd.go index 6ba84f438a..2ffb60fe25 100644 --- a/src/pkg/os/stat_freebsd.go +++ b/src/pkg/os/stat_freebsd.go @@ -9,9 +9,9 @@ import ( "time" ) -func sameFile(sys1, sys2 interface{}) bool { - stat1 := sys1.(*syscall.Stat_t) - stat2 := sys2.(*syscall.Stat_t) +func sameFile(fs1, fs2 *fileStat) bool { + stat1 := fs1.sys.(*syscall.Stat_t) + stat2 := fs2.sys.(*syscall.Stat_t) return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino } diff --git a/src/pkg/os/stat_linux.go b/src/pkg/os/stat_linux.go index 00506b2b60..605c1d9b64 100644 --- a/src/pkg/os/stat_linux.go +++ b/src/pkg/os/stat_linux.go @@ -9,9 +9,9 @@ import ( "time" ) -func sameFile(sys1, sys2 interface{}) bool { - stat1 := sys1.(*syscall.Stat_t) - stat2 := sys2.(*syscall.Stat_t) +func sameFile(fs1, fs2 *fileStat) bool { + stat1 := fs1.sys.(*syscall.Stat_t) + stat2 := fs2.sys.(*syscall.Stat_t) return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino } diff --git a/src/pkg/os/stat_netbsd.go b/src/pkg/os/stat_netbsd.go index 6ba84f438a..2ffb60fe25 100644 --- a/src/pkg/os/stat_netbsd.go +++ b/src/pkg/os/stat_netbsd.go @@ -9,9 +9,9 @@ import ( "time" ) -func sameFile(sys1, sys2 interface{}) bool { - stat1 := sys1.(*syscall.Stat_t) - stat2 := sys2.(*syscall.Stat_t) +func sameFile(fs1, fs2 *fileStat) bool { + stat1 := fs1.sys.(*syscall.Stat_t) + stat2 := fs2.sys.(*syscall.Stat_t) return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino } diff --git a/src/pkg/os/stat_openbsd.go b/src/pkg/os/stat_openbsd.go index 00506b2b60..605c1d9b64 100644 --- a/src/pkg/os/stat_openbsd.go +++ b/src/pkg/os/stat_openbsd.go @@ -9,9 +9,9 @@ import ( "time" ) -func sameFile(sys1, sys2 interface{}) bool { - stat1 := sys1.(*syscall.Stat_t) - stat2 := sys2.(*syscall.Stat_t) +func sameFile(fs1, fs2 *fileStat) bool { + stat1 := fs1.sys.(*syscall.Stat_t) + stat2 := fs2.sys.(*syscall.Stat_t) return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino } diff --git a/src/pkg/os/stat_plan9.go b/src/pkg/os/stat_plan9.go index 6822cc019e..25c9a8c14b 100644 --- a/src/pkg/os/stat_plan9.go +++ b/src/pkg/os/stat_plan9.go @@ -9,9 +9,9 @@ import ( "time" ) -func sameFile(sys1, sys2 interface{}) bool { - a := sys1.(*syscall.Dir) - b := sys2.(*syscall.Dir) +func sameFile(fs1, fs2 *fileStat) bool { + a := fs1.sys.(*syscall.Dir) + b := fs2.sys.(*syscall.Dir) return a.Qid.Path == b.Qid.Path && a.Type == b.Type && a.Dev == b.Dev } diff --git a/src/pkg/os/stat_windows.go b/src/pkg/os/stat_windows.go index c0441a42ae..8394c2b320 100644 --- a/src/pkg/os/stat_windows.go +++ b/src/pkg/os/stat_windows.go @@ -5,9 +5,7 @@ package os import ( - "sync" "syscall" - "time" "unsafe" ) @@ -22,7 +20,7 @@ func (file *File) Stat() (fi FileInfo, err error) { return Stat(file.name) } if file.name == DevNull { - return statDevNull() + return &devNullStat, nil } var d syscall.ByHandleFileInformation e := syscall.GetFileInformationByHandle(syscall.Handle(file.fd), &d) @@ -30,11 +28,18 @@ func (file *File) Stat() (fi FileInfo, err error) { return nil, &PathError{"GetFileInformationByHandle", file.name, e} } return &fileStat{ - name: basename(file.name), - size: mkSize(d.FileSizeHigh, d.FileSizeLow), - modTime: mkModTime(d.LastWriteTime), - mode: mkMode(d.FileAttributes), - sys: mkSysFromFI(&d), + name: basename(file.name), + sys: syscall.Win32FileAttributeData{ + FileAttributes: d.FileAttributes, + CreationTime: d.CreationTime, + LastAccessTime: d.LastAccessTime, + LastWriteTime: d.LastWriteTime, + FileSizeHigh: d.FileSizeHigh, + FileSizeLow: d.FileSizeLow, + }, + vol: d.VolumeSerialNumber, + idxhi: d.FileIndexHigh, + idxlo: d.FileIndexLow, }, nil } @@ -45,29 +50,23 @@ func Stat(name string) (fi FileInfo, err error) { return nil, &PathError{"Stat", name, syscall.Errno(syscall.ERROR_PATH_NOT_FOUND)} } if name == DevNull { - return statDevNull() + return &devNullStat, nil } - var d syscall.Win32FileAttributeData + fs := &fileStat{name: basename(name)} namep, e := syscall.UTF16PtrFromString(name) if e != nil { return nil, &PathError{"Stat", name, e} } - e = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&d))) + e = syscall.GetFileAttributesEx(namep, syscall.GetFileExInfoStandard, (*byte)(unsafe.Pointer(&fs.sys))) if e != nil { return nil, &PathError{"GetFileAttributesEx", name, e} } - path := name - if !isAbs(path) { + fs.path = name + if !isAbs(fs.path) { cwd, _ := Getwd() - path = cwd + `\` + path + fs.path = cwd + `\` + fs.path } - return &fileStat{ - name: basename(name), - size: mkSize(d.FileSizeHigh, d.FileSizeLow), - modTime: mkModTime(d.LastWriteTime), - mode: mkMode(d.FileAttributes), - sys: mkSys(path, d.LastAccessTime, d.CreationTime), - }, nil + return fs, nil } // Lstat returns the FileInfo structure describing the named file. @@ -79,22 +78,6 @@ func Lstat(name string) (fi FileInfo, err error) { return Stat(name) } -// statDevNull return FileInfo structure describing DevNull file ("NUL"). -// It creates invented data, since none of windows api will return -// that information. -func statDevNull() (fi FileInfo, err error) { - return &fileStat{ - name: DevNull, - mode: ModeDevice | ModeCharDevice | 0666, - sys: &winSys{ - // hopefully this will work for SameFile - vol: 0, - idxhi: 0, - idxlo: 0, - }, - }, nil -} - // basename removes trailing slashes and the leading // directory name and drive letter from path name. func basename(name string) string { @@ -172,95 +155,3 @@ func volumeName(path string) (v string) { } return "" } - -type winSys struct { - sync.Mutex - path string - atime, ctime syscall.Filetime - vol, idxhi, idxlo uint32 -} - -func mkSize(hi, lo uint32) int64 { - return int64(hi)<<32 + int64(lo) -} - -func mkModTime(mtime syscall.Filetime) time.Time { - return time.Unix(0, mtime.Nanoseconds()) -} - -func mkMode(fa uint32) (m FileMode) { - if fa&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { - m |= ModeDir | 0111 - } - if fa&syscall.FILE_ATTRIBUTE_READONLY != 0 { - m |= 0444 - } else { - m |= 0666 - } - return m -} - -func mkSys(path string, atime, ctime syscall.Filetime) *winSys { - return &winSys{ - path: path, - atime: atime, - ctime: ctime, - } -} - -func mkSysFromFI(i *syscall.ByHandleFileInformation) *winSys { - return &winSys{ - atime: i.LastAccessTime, - ctime: i.CreationTime, - vol: i.VolumeSerialNumber, - idxhi: i.FileIndexHigh, - idxlo: i.FileIndexLow, - } -} - -func (s *winSys) loadFileId() error { - if s.path == "" { - // already done - return nil - } - s.Lock() - defer s.Unlock() - pathp, e := syscall.UTF16PtrFromString(s.path) - if e != nil { - return e - } - h, e := syscall.CreateFile(pathp, 0, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) - if e != nil { - return e - } - defer syscall.CloseHandle(h) - var i syscall.ByHandleFileInformation - e = syscall.GetFileInformationByHandle(syscall.Handle(h), &i) - if e != nil { - return e - } - s.path = "" - s.vol = i.VolumeSerialNumber - s.idxhi = i.FileIndexHigh - s.idxlo = i.FileIndexLow - return nil -} - -func sameFile(sys1, sys2 interface{}) bool { - s1 := sys1.(*winSys) - s2 := sys2.(*winSys) - e := s1.loadFileId() - if e != nil { - panic(e) - } - e = s2.loadFileId() - if e != nil { - panic(e) - } - return s1.vol == s2.vol && s1.idxhi == s2.idxhi && s1.idxlo == s2.idxlo -} - -// For testing. -func atime(fi FileInfo) time.Time { - return time.Unix(0, fi.Sys().(*winSys).atime.Nanoseconds()) -} diff --git a/src/pkg/os/types.go b/src/pkg/os/types.go index c561ea04fb..473d431d4d 100644 --- a/src/pkg/os/types.go +++ b/src/pkg/os/types.go @@ -99,21 +99,8 @@ func (m FileMode) Perm() FileMode { return m & ModePerm } -// A fileStat is the implementation of FileInfo returned by Stat and Lstat. -type fileStat struct { - name string - size int64 - mode FileMode - modTime time.Time - sys interface{} -} - -func (fs *fileStat) Name() string { return fs.name } -func (fs *fileStat) Size() int64 { return fs.size } -func (fs *fileStat) Mode() FileMode { return fs.mode } -func (fs *fileStat) ModTime() time.Time { return fs.modTime } -func (fs *fileStat) IsDir() bool { return fs.mode.IsDir() } -func (fs *fileStat) Sys() interface{} { return fs.sys } +func (fs *fileStat) Name() string { return fs.name } +func (fs *fileStat) IsDir() bool { return fs.Mode().IsDir() } // SameFile reports whether fi1 and fi2 describe the same file. // For example, on Unix this means that the device and inode fields @@ -127,5 +114,5 @@ func SameFile(fi1, fi2 FileInfo) bool { if !ok1 || !ok2 { return false } - return sameFile(fs1.sys, fs2.sys) + return sameFile(fs1, fs2) } diff --git a/src/pkg/os/types_notwin.go b/src/pkg/os/types_notwin.go new file mode 100644 index 0000000000..ea1a073930 --- /dev/null +++ b/src/pkg/os/types_notwin.go @@ -0,0 +1,25 @@ +// 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. + +// +build !windows + +package os + +import ( + "time" +) + +// A fileStat is the implementation of FileInfo returned by Stat and Lstat. +type fileStat struct { + name string + size int64 + mode FileMode + modTime time.Time + sys interface{} +} + +func (fs *fileStat) Size() int64 { return fs.size } +func (fs *fileStat) Mode() FileMode { return fs.mode } +func (fs *fileStat) ModTime() time.Time { return fs.modTime } +func (fs *fileStat) Sys() interface{} { return fs.sys } diff --git a/src/pkg/os/types_windows.go b/src/pkg/os/types_windows.go new file mode 100644 index 0000000000..38901681e6 --- /dev/null +++ b/src/pkg/os/types_windows.go @@ -0,0 +1,104 @@ +// 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 ( + "sync" + "syscall" + "time" +) + +// A fileStat is the implementation of FileInfo returned by Stat and Lstat. +type fileStat struct { + name string + sys syscall.Win32FileAttributeData + + // used to implement SameFile + sync.Mutex + path string + vol uint32 + idxhi uint32 + idxlo uint32 +} + +func (fs *fileStat) Size() int64 { + return int64(fs.sys.FileSizeHigh)<<32 + int64(fs.sys.FileSizeLow) +} + +func (fs *fileStat) Mode() (m FileMode) { + if fs == &devNullStat { + return ModeDevice | ModeCharDevice | 0666 + } + if fs.sys.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { + m |= ModeDir | 0111 + } + if fs.sys.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 { + m |= 0444 + } else { + m |= 0666 + } + return m +} + +func (fs *fileStat) ModTime() time.Time { + return time.Unix(0, fs.sys.LastWriteTime.Nanoseconds()) +} + +// Sys returns syscall.Win32FileAttributeData for file fs. +func (fs *fileStat) Sys() interface{} { return &fs.sys } + +func (fs *fileStat) loadFileId() error { + fs.Lock() + defer fs.Unlock() + if fs.path == "" { + // already done + return nil + } + pathp, err := syscall.UTF16PtrFromString(fs.path) + if err != nil { + return err + } + h, err := syscall.CreateFile(pathp, 0, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) + if err != nil { + return err + } + defer syscall.CloseHandle(h) + var i syscall.ByHandleFileInformation + err = syscall.GetFileInformationByHandle(syscall.Handle(h), &i) + if err != nil { + return err + } + fs.path = "" + fs.vol = i.VolumeSerialNumber + fs.idxhi = i.FileIndexHigh + fs.idxlo = i.FileIndexLow + return nil +} + +// devNullStat is fileStat structure describing DevNull file ("NUL"). +var devNullStat = fileStat{ + name: DevNull, + // hopefully this will work for SameFile + vol: 0, + idxhi: 0, + idxlo: 0, +} + +func sameFile(fs1, fs2 *fileStat) bool { + e := fs1.loadFileId() + if e != nil { + return false + } + e = fs2.loadFileId() + if e != nil { + return false + } + return fs1.vol == fs2.vol && fs1.idxhi == fs2.idxhi && fs1.idxlo == fs2.idxlo +} + +// For testing. +func atime(fi FileInfo) time.Time { + return time.Unix(0, fi.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds()) +}