You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
130 lines
3.1 KiB
Go
130 lines
3.1 KiB
Go
3 years ago
|
package wincmd
|
||
|
|
||
|
import (
|
||
|
"b612.me/win32api"
|
||
|
"golang.org/x/sys/windows"
|
||
|
"os"
|
||
|
"sync"
|
||
|
"syscall"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
const DevNull = "NUL"
|
||
|
|
||
|
// A fileStat is the implementation of FileInfo returned by Stat and Lstat.
|
||
|
type FileStat struct {
|
||
|
name string
|
||
|
|
||
|
// from ByHandleFileInformation, Win32FileAttributeData and Win32finddata
|
||
|
FileAttributes uint32
|
||
|
CreationTime syscall.Filetime
|
||
|
LastAccessTime syscall.Filetime
|
||
|
LastWriteTime syscall.Filetime
|
||
|
FileSizeHigh uint32
|
||
|
FileSizeLow uint32
|
||
|
|
||
|
// from Win32finddata
|
||
|
Reserved0 uint32
|
||
|
|
||
|
// what syscall.GetFileType returns
|
||
|
filetype uint32
|
||
|
|
||
|
// used to implement SameFile
|
||
|
sync.Mutex
|
||
|
path string
|
||
|
vol uint32
|
||
|
idxhi uint32
|
||
|
idxlo uint32
|
||
|
appendNameToPath bool
|
||
|
}
|
||
|
|
||
|
// newFileStatFromWin32finddata copies all required information
|
||
|
// from syscall.Win32finddata d into the newly created fileStat.
|
||
|
func newFileStatFromInformation(d *syscall.ByHandleFileInformation, name string, path string) *FileStat {
|
||
|
return &FileStat{
|
||
|
name: name,
|
||
|
path: path,
|
||
|
FileAttributes: d.FileAttributes,
|
||
|
CreationTime: d.CreationTime,
|
||
|
LastAccessTime: d.LastAccessTime,
|
||
|
LastWriteTime: d.LastWriteTime,
|
||
|
FileSizeHigh: d.FileSizeHigh,
|
||
|
FileSizeLow: d.FileSizeLow,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (fs *FileStat) Name() string {
|
||
|
return fs.name
|
||
|
}
|
||
|
|
||
|
func (fs *FileStat) IsDir() bool {
|
||
|
return fs.FileAttributes&win32api.FILE_ATTRIBUTE_DIRECTORY != 0
|
||
|
}
|
||
|
|
||
|
func (fs *FileStat) isSymlink() bool {
|
||
|
// Use instructions described at
|
||
|
// https://blogs.msdn.microsoft.com/oldnewthing/20100212-00/?p=14963/
|
||
|
// to recognize whether it's a symlink.
|
||
|
if fs.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
|
||
|
return false
|
||
|
}
|
||
|
return fs.Reserved0 == syscall.IO_REPARSE_TAG_SYMLINK ||
|
||
|
fs.Reserved0 == windows.IO_REPARSE_TAG_MOUNT_POINT
|
||
|
}
|
||
|
|
||
|
func (fs *FileStat) Size() int64 {
|
||
|
return int64(fs.FileSizeHigh)<<32 + int64(fs.FileSizeLow)
|
||
|
}
|
||
|
|
||
|
func (fs *FileStat) Mode() (m os.FileMode) {
|
||
|
if fs == &devNullStat {
|
||
|
return os.ModeDevice | os.ModeCharDevice | 0666
|
||
|
}
|
||
|
if fs.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 {
|
||
|
m |= 0444
|
||
|
} else {
|
||
|
m |= 0666
|
||
|
}
|
||
|
if fs.isSymlink() {
|
||
|
return m | os.ModeSymlink
|
||
|
}
|
||
|
if fs.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||
|
m |= os.ModeDir | 0111
|
||
|
}
|
||
|
switch fs.filetype {
|
||
|
case syscall.FILE_TYPE_PIPE:
|
||
|
m |= os.ModeNamedPipe
|
||
|
case syscall.FILE_TYPE_CHAR:
|
||
|
m |= os.ModeDevice | os.ModeCharDevice
|
||
|
}
|
||
|
return m
|
||
|
}
|
||
|
|
||
|
func (fs *FileStat) ModTime() time.Time {
|
||
|
return time.Unix(0, fs.LastWriteTime.Nanoseconds())
|
||
|
}
|
||
|
|
||
|
// Sys returns syscall.Win32FileAttributeData for file fs.
|
||
|
func (fs *FileStat) Sys() interface{} {
|
||
|
return &syscall.Win32FileAttributeData{
|
||
|
FileAttributes: fs.FileAttributes,
|
||
|
CreationTime: fs.CreationTime,
|
||
|
LastAccessTime: fs.LastAccessTime,
|
||
|
LastWriteTime: fs.LastWriteTime,
|
||
|
FileSizeHigh: fs.FileSizeHigh,
|
||
|
FileSizeLow: fs.FileSizeLow,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// saveInfoFromPath saves full path of the file to be used by os.SameFile later,
|
||
|
// and set name from path.
|
||
|
|
||
|
// 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,
|
||
|
}
|