|  | // 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 | 
|  | } | 
|  | if fs.sys.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 { | 
|  | m |= ModeSymlink | 
|  | } | 
|  | 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()) | 
|  | } |