From 9adabd6723a59e08462780a9a5f34776961326ae Mon Sep 17 00:00:00 2001 From: starainrt Date: Sun, 10 Mar 2024 15:37:20 +0800 Subject: [PATCH] update archive method --- archive_test.go | 1 + files.go | 44 +++++++++++++++++ files_darwin.go | 91 +++++++++++++++++++++++++++++++++++ files_unix.go | 91 +++++++++++++++++++++++++++++++++++ files_windows.go | 123 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 350 insertions(+) create mode 100644 archive_test.go create mode 100644 files.go create mode 100644 files_darwin.go create mode 100644 files_unix.go create mode 100644 files_windows.go diff --git a/archive_test.go b/archive_test.go new file mode 100644 index 0000000..77a7a39 --- /dev/null +++ b/archive_test.go @@ -0,0 +1 @@ +package starlog diff --git a/files.go b/files.go new file mode 100644 index 0000000..ba0b51b --- /dev/null +++ b/files.go @@ -0,0 +1,44 @@ +package staros + +import ( + "errors" + "os" +) + +var ERR_ALREADY_LOCKED = errors.New("ALREADY LOCKED") +var ERR_TIMEOUT = errors.New("TIME OUT") + +func NewFileLock(filepath string) FileLock { + return FileLock{ + filepath: filepath, + } +} + +// 检测文件/文件夹是否存在 +func Exists(path string) bool { + _, err := os.Stat(path) + if err != nil && os.IsNotExist(err) { + return false + } + return true +} + +// IsFile 返回给定文件地址是否是一个文件, +//True为是一个文件,False为不是文件或路径无效 +func IsFile(fpath string) bool { + s, err := os.Stat(fpath) + if err != nil { + return false + } + return !s.IsDir() +} + +// IsFolder 返回给定文件地址是否是一个文件夹, +//True为是一个文件夹,False为不是文件夹或路径无效 +func IsFolder(fpath string) bool { + s, err := os.Stat(fpath) + if err != nil { + return false + } + return s.IsDir() +} diff --git a/files_darwin.go b/files_darwin.go new file mode 100644 index 0000000..919dcee --- /dev/null +++ b/files_darwin.go @@ -0,0 +1,91 @@ +//+build darwin + +package staros + +import ( + "b612.me/stario" + "os" + "syscall" + "time" +) + +type FileLock struct { + fd int + filepath string +} + +func (f *FileLock) openFileForLock() error { + fd, err := syscall.Open(f.filepath, syscall.O_CREAT|syscall.O_RDONLY, 0600) + if err != nil { + return err + } + f.filepath = f.filepath + f.fd = fd + return nil +} + +func (f *FileLock) Lock(Exclusive bool) error { + var lockType int + if Exclusive { + lockType = syscall.LOCK_EX + } else { + lockType = syscall.LOCK_SH + } + if err := f.openFileForLock(); err != nil { + return err + } + return syscall.Flock(f.fd, lockType) +} + +func (f *FileLock) LockNoBlocking(Exclusive bool) error { + var lockType int + if Exclusive { + lockType = syscall.LOCK_EX + } else { + lockType = syscall.LOCK_SH + } + if err := f.openFileForLock(); err != nil { + return err + } + err := syscall.Flock(f.fd, lockType|syscall.LOCK_NB) + if err != nil { + syscall.Close(f.fd) + if err == syscall.EWOULDBLOCK { + return ERR_ALREADY_LOCKED + } + } + return err +} + +func (f *FileLock) Unlock() error { + err := syscall.Flock(f.fd, syscall.LOCK_UN) + if err != nil { + return err + } + return syscall.Close(f.fd) +} + +func (f *FileLock) LockWithTimeout(tm time.Duration, Exclusive bool) error { + return stario.WaitUntilTimeout(tm, func(tmout chan struct{}) error { + err := f.Lock(Exclusive) + select { + case <-tmout: + f.Unlock() + return nil + default: + } + return err + }) +} + +func timespecToTime(ts syscall.Timespec) time.Time { + return time.Unix(int64(ts.Sec), int64(ts.Nsec)) +} + +func GetFileCreationTime(fileinfo os.FileInfo) time.Time { + return timespecToTime(fileinfo.Sys().(*syscall.Stat_t).Ctimespec) +} + +func GetFileAccessTime(fileinfo os.FileInfo) time.Time { + return timespecToTime(fileinfo.Sys().(*syscall.Stat_t).Atimespec) +} diff --git a/files_unix.go b/files_unix.go new file mode 100644 index 0000000..132abcd --- /dev/null +++ b/files_unix.go @@ -0,0 +1,91 @@ +//+build linux + +package staros + +import ( + "b612.me/stario" + "os" + "syscall" + "time" +) + +type FileLock struct { + fd int + filepath string +} + +func timespecToTime(ts syscall.Timespec) time.Time { + return time.Unix(int64(ts.Sec), int64(ts.Nsec)) +} + +func GetFileCreationTime(fileinfo os.FileInfo) time.Time { + return timespecToTime(fileinfo.Sys().(*syscall.Stat_t).Ctim) +} + +func GetFileAccessTime(fileinfo os.FileInfo) time.Time { + return timespecToTime(fileinfo.Sys().(*syscall.Stat_t).Atim) +} + +func (f *FileLock) openFileForLock() error { + fd, err := syscall.Open(f.filepath, syscall.O_CREAT|syscall.O_RDONLY, 0600) + if err != nil { + return err + } + f.filepath = f.filepath + f.fd = fd + return nil +} + +func (f *FileLock) Lock(Exclusive bool) error { + var lockType int + if Exclusive { + lockType = syscall.LOCK_EX + } else { + lockType = syscall.LOCK_SH + } + if err := f.openFileForLock(); err != nil { + return err + } + return syscall.Flock(f.fd, lockType) +} + +func (f *FileLock) LockNoBlocking(Exclusive bool) error { + var lockType int + if Exclusive { + lockType = syscall.LOCK_EX + } else { + lockType = syscall.LOCK_SH + } + if err := f.openFileForLock(); err != nil { + return err + } + err := syscall.Flock(f.fd, lockType|syscall.LOCK_NB) + if err != nil { + syscall.Close(f.fd) + if err == syscall.EWOULDBLOCK { + return ERR_ALREADY_LOCKED + } + } + return err +} + +func (f *FileLock) Unlock() error { + err := syscall.Flock(f.fd, syscall.LOCK_UN) + if err != nil { + return err + } + return syscall.Close(f.fd) +} + +func (f *FileLock) LockWithTimeout(tm time.Duration, Exclusive bool) error { + return stario.WaitUntilTimeout(tm, func(tmout chan struct{}) error { + err := f.Lock(Exclusive) + select { + case <-tmout: + f.Unlock() + return nil + default: + } + return err + }) +} diff --git a/files_windows.go b/files_windows.go new file mode 100644 index 0000000..6ca87d9 --- /dev/null +++ b/files_windows.go @@ -0,0 +1,123 @@ +// +build windows + +package staros + +import ( + "b612.me/win32api" + "os" + "syscall" + "time" +) + +type FileLock struct { + filepath string + handle win32api.HANDLE +} + +func GetFileCreationTime(fileinfo os.FileInfo) time.Time { + d := fileinfo.Sys().(*syscall.Win32FileAttributeData) + return time.Unix(0, d.CreationTime.Nanoseconds()) +} + +func GetFileAccessTime(fileinfo os.FileInfo) time.Time { + d := fileinfo.Sys().(*syscall.Win32FileAttributeData) + return time.Unix(0, d.LastAccessTime.Nanoseconds()) +} + +func SetFileTimes(file *os.File, info os.FileInfo) { + +} + +func SetFileTimesbyTime(file *os.File) { + +} + +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 { + var err error + if err = f.openFileForLock(); err != nil { + return err + } + event, err := win32api.CreateEventW(nil, true, false, nil) + if err != nil { + 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 { + return nil + } + if err != syscall.ERROR_IO_PENDING { + 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! + return nil + case syscall.WAIT_TIMEOUT: + f.Unlock() + return ERR_TIMEOUT + default: + f.Unlock() + return err + } +} + +func (f *FileLock) Lock(Exclusive bool) error { + var lockType win32api.DWORD + if Exclusive { + lockType = win32api.LOCKFILE_EXCLUSIVE_LOCK + } else { + lockType = 0 + } + return f.lockForTimeout(0, lockType) +} + +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) +} + +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) +} + +func (f *FileLock) Unlock() error { + return syscall.Close(syscall.Handle(f.handle)) +}