staros/files_windows.go

218 lines
4.7 KiB
Go
Raw Permalink Normal View History

//go:build windows
2020-12-21 17:19:41 +08:00
// +build windows
package staros
import (
2021-09-01 11:01:51 +08:00
"b612.me/win32api"
"golang.org/x/sys/windows"
2020-12-21 17:19:41 +08:00
"os"
"syscall"
"time"
)
2021-09-01 11:01:51 +08:00
type FileLock struct {
filepath string
handle win32api.HANDLE
locked bool
2021-09-01 11:01:51 +08:00
}
2020-12-21 17:19:41 +08:00
func GetFileCreationTime(fileinfo os.FileInfo) time.Time {
if fileinfo == nil {
return time.Time{}
}
if data, ok := fileinfo.Sys().(*syscall.Win32FileAttributeData); ok && data != nil {
return time.Unix(0, data.CreationTime.Nanoseconds())
}
return time.Time{}
2020-12-21 17:19:41 +08:00
}
func GetFileAccessTime(fileinfo os.FileInfo) time.Time {
if fileinfo == nil {
return time.Time{}
}
if data, ok := fileinfo.Sys().(*syscall.Win32FileAttributeData); ok && data != nil {
return time.Unix(0, data.LastAccessTime.Nanoseconds())
}
return time.Time{}
2020-12-21 17:19:41 +08:00
}
2021-05-17 15:09:04 +08:00
2021-09-01 11:01:51 +08:00
func SetFileTimes(file *os.File, info os.FileInfo) {
_ = SetFileTimesE(file, info)
2021-05-17 15:09:04 +08:00
}
func SetFileTimesbyTime(file *os.File) {
_ = SetFileTimesbyTimeE(file)
}
func SetFileTimesE(file *os.File, info os.FileInfo) error {
if file == nil {
return errNilFile
}
if info == nil {
return errNilFileInfo
}
data, ok := info.Sys().(*syscall.Win32FileAttributeData)
if !ok || data == nil {
return errUnsupportedFileInfo
}
ctime := time.Unix(0, data.CreationTime.Nanoseconds())
atime := time.Unix(0, data.LastAccessTime.Nanoseconds())
mtime := info.ModTime()
return setFileTimes(file.Name(), ctime, atime, mtime)
}
func SetFileTimesByTimeE(file *os.File) error {
return SetFileTimesbyTimeE(file)
}
func SetFileTimesbyTimeE(file *os.File) error {
if file == nil {
return errNilFile
}
now := time.Now()
return setFileTimes(file.Name(), now, now, now)
}
func setFileTimes(path string, ctime, atime, mtime time.Time) error {
path16, err := windows.UTF16PtrFromString(path)
if err != nil {
return err
}
handle, err := windows.CreateFile(
path16,
windows.FILE_WRITE_ATTRIBUTES,
windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE,
nil,
windows.OPEN_EXISTING,
windows.FILE_FLAG_BACKUP_SEMANTICS,
0,
)
if err != nil {
return err
}
defer windows.CloseHandle(handle)
ctimeFt := windows.NsecToFiletime(ctime.UnixNano())
atimeFt := windows.NsecToFiletime(atime.UnixNano())
mtimeFt := windows.NsecToFiletime(mtime.UnixNano())
return windows.SetFileTime(handle, &ctimeFt, &atimeFt, &mtimeFt)
2021-06-04 10:44:53 +08:00
2021-09-01 11:01:51 +08:00
}
func (f *FileLock) openFileForLock() error {
name, err := syscall.UTF16PtrFromString(f.filepath)
if err != nil {
return err
}
handle, err := syscall.CreateFile(
name,
syscall.GENERIC_READ,
syscall.FILE_SHARE_READ,
nil,
syscall.OPEN_ALWAYS,
syscall.FILE_FLAG_OVERLAPPED|0x00000080,
0)
if err != nil {
return err
}
f.handle = win32api.HANDLE(handle)
return nil
}
func (f *FileLock) lockForTimeout(timeout time.Duration, lockType win32api.DWORD) error {
if f.locked {
return ERR_ALREADY_LOCKED
}
2021-09-01 11:01:51 +08:00
var err error
if err = f.openFileForLock(); err != nil {
return err
}
event, err := win32api.CreateEventW(nil, true, false, nil)
if err != nil {
_ = f.closeHandle()
2021-09-01 11:01:51 +08:00
return err
}
myEvent := &syscall.Overlapped{HEvent: syscall.Handle(event)}
defer syscall.CloseHandle(myEvent.HEvent)
_, err = win32api.LockFileEx(f.handle, lockType, 0, 1, 0, myEvent)
if err == nil {
f.locked = true
2021-09-01 11:01:51 +08:00
return nil
}
if err != syscall.ERROR_IO_PENDING {
_ = f.closeHandle()
2021-09-01 11:01:51 +08:00
return err
}
millis := uint32(syscall.INFINITE)
if timeout >= 0 {
millis = uint32(timeout.Nanoseconds() / 1000000)
}
s, err := syscall.WaitForSingleObject(myEvent.HEvent, millis)
switch s {
case syscall.WAIT_OBJECT_0:
// success!
f.locked = true
2021-09-01 11:01:51 +08:00
return nil
case syscall.WAIT_TIMEOUT:
_ = f.closeHandle()
2021-09-01 11:01:51 +08:00
return ERR_TIMEOUT
default:
_ = f.closeHandle()
2021-09-01 11:01:51 +08:00
return err
}
}
2021-09-01 13:52:43 +08:00
func (f *FileLock) Lock(Exclusive bool) error {
var lockType win32api.DWORD
if Exclusive {
lockType = win32api.LOCKFILE_EXCLUSIVE_LOCK
} else {
lockType = 0
}
return f.lockForTimeout(-1, lockType)
2021-09-01 11:01:51 +08:00
}
2021-09-01 13:52:43 +08:00
func (f *FileLock) LockWithTimeout(tm time.Duration, Exclusive bool) error {
var lockType win32api.DWORD
if Exclusive {
lockType = win32api.LOCKFILE_EXCLUSIVE_LOCK
} else {
lockType = 0
}
return f.lockForTimeout(tm, lockType)
2021-09-01 11:01:51 +08:00
}
2021-09-01 13:52:43 +08:00
func (f *FileLock) LockNoBlocking(Exclusive bool) error {
var lockType win32api.DWORD
if Exclusive {
lockType = win32api.LOCKFILE_EXCLUSIVE_LOCK
} else {
lockType = 0
}
return f.lockForTimeout(0, lockType|win32api.LOCKFILE_FAIL_IMMEDIATELY)
2021-09-01 11:01:51 +08:00
}
func (f *FileLock) Unlock() error {
if f == nil || !f.locked {
return errFileLockNotLocked
}
if err := f.closeHandle(); err != nil {
return err
}
f.locked = false
return nil
}
func (f *FileLock) closeHandle() error {
if f == nil || f.handle == 0 {
return nil
}
err := syscall.Close(syscall.Handle(f.handle))
if err != nil {
return err
}
f.handle = 0
return nil
2021-09-01 11:01:51 +08:00
}