package os
import (
+ "sync"
"syscall"
"time"
"unsafe"
if e != nil {
return nil, &PathError{"GetFileInformationByHandle", file.name, e}
}
- return toFileInfo(basename(file.name), d.FileAttributes, d.FileSizeHigh, d.FileSizeLow, d.CreationTime, d.LastAccessTime, d.LastWriteTime), nil
+ return &fileStat{
+ name: basename(file.name),
+ size: mkSize(d.FileSizeHigh, d.FileSizeLow),
+ modTime: mkModTime(d.LastWriteTime),
+ mode: mkMode(d.FileAttributes),
+ sys: mkSysFromFI(&d),
+ }, nil
}
// Stat returns a FileInfo structure describing the named file.
if e != nil {
return nil, &PathError{"GetFileAttributesEx", name, e}
}
- return toFileInfo(basename(name), d.FileAttributes, d.FileSizeHigh, d.FileSizeLow, d.CreationTime, d.LastAccessTime, d.LastWriteTime), nil
+ path := name
+ if !isAbs(path) {
+ cwd, _ := Getwd()
+ path = cwd + `\` + 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
}
// Lstat returns the FileInfo structure describing the named file.
return name
}
-type winTimes struct {
- atime, ctime syscall.Filetime
+func isSlash(c uint8) bool {
+ return c == '\\' || c == '/'
+}
+
+func isAbs(path string) (b bool) {
+ v := volumeName(path)
+ if v == "" {
+ return false
+ }
+ path = path[len(v):]
+ if path == "" {
+ return false
+ }
+ return isSlash(path[0])
}
-func toFileInfo(name string, fa, sizehi, sizelo uint32, ctime, atime, mtime syscall.Filetime) FileInfo {
- fs := &fileStat{
- name: name,
- size: int64(sizehi)<<32 + int64(sizelo),
- modTime: time.Unix(0, mtime.Nanoseconds()),
- sys: &winTimes{atime, ctime},
+func volumeName(path string) (v string) {
+ if len(path) < 2 {
+ return ""
}
+ // with drive letter
+ c := path[0]
+ if path[1] == ':' &&
+ ('0' <= c && c <= '9' || 'a' <= c && c <= 'z' ||
+ 'A' <= c && c <= 'Z') {
+ return path[:2]
+ }
+ // is it UNC
+ if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) &&
+ !isSlash(path[2]) && path[2] != '.' {
+ // first, leading `\\` and next shouldn't be `\`. its server name.
+ for n := 3; n < l-1; n++ {
+ // second, next '\' shouldn't be repeated.
+ if isSlash(path[n]) {
+ n++
+ // third, following something characters. its share name.
+ if !isSlash(path[n]) {
+ if path[n] == '.' {
+ break
+ }
+ for ; n < l; n++ {
+ if isSlash(path[n]) {
+ break
+ }
+ }
+ return path[:n]
+ }
+ break
+ }
+ }
+ }
+ 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 {
- fs.mode |= ModeDir
+ m |= ModeDir
}
if fa&syscall.FILE_ATTRIBUTE_READONLY != 0 {
- fs.mode |= 0444
+ m |= 0444
} else {
- fs.mode |= 0666
+ m |= 0666
}
- return fs
+ 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()
+ h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(s.path), syscall.GENERIC_READ, syscall.FILE_SHARE_READ, nil, syscall.OPEN_EXISTING, 0, 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 {
- // TODO(rsc): Do better than this, but this matches what
- // used to happen when code compared .Dev and .Ino,
- // which were both always zero. Obviously not all files
- // are the same.
- return true
+ 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().(*winTimes).atime.Nanoseconds())
+ return time.Unix(0, fi.Sys().(*winSys).atime.Nanoseconds())
}