2026-06-09 18:10:19 +08:00
|
|
|
//go:build darwin
|
|
|
|
|
// +build darwin
|
2021-06-04 10:44:53 +08:00
|
|
|
|
|
|
|
|
package staros
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"os"
|
|
|
|
|
"syscall"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
2022-03-18 17:22:38 +08:00
|
|
|
type FileLock struct {
|
|
|
|
|
fd int
|
|
|
|
|
filepath string
|
2026-06-09 18:10:19 +08:00
|
|
|
locked bool
|
2022-03-18 17:22:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f *FileLock) openFileForLock() error {
|
|
|
|
|
fd, err := syscall.Open(f.filepath, syscall.O_CREAT|syscall.O_RDONLY, 0600)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
return f.lockWithFlags(lockType)
|
2022-03-18 17:22:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f *FileLock) LockNoBlocking(Exclusive bool) error {
|
|
|
|
|
var lockType int
|
|
|
|
|
if Exclusive {
|
|
|
|
|
lockType = syscall.LOCK_EX
|
|
|
|
|
} else {
|
|
|
|
|
lockType = syscall.LOCK_SH
|
|
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
return f.lockWithFlags(lockType | syscall.LOCK_NB)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f *FileLock) lockWithFlags(lockType int) error {
|
|
|
|
|
if f.locked {
|
|
|
|
|
return ERR_ALREADY_LOCKED
|
|
|
|
|
}
|
2022-03-18 17:22:38 +08:00
|
|
|
if err := f.openFileForLock(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
err := syscall.Flock(f.fd, lockType)
|
2022-03-18 17:22:38 +08:00
|
|
|
if err != nil {
|
2026-06-09 18:10:19 +08:00
|
|
|
_ = syscall.Close(f.fd)
|
|
|
|
|
f.fd = 0
|
2022-03-18 17:22:38 +08:00
|
|
|
if err == syscall.EWOULDBLOCK {
|
|
|
|
|
return ERR_ALREADY_LOCKED
|
|
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
return err
|
2022-03-18 17:22:38 +08:00
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
f.locked = true
|
|
|
|
|
return nil
|
2022-03-18 17:22:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f *FileLock) Unlock() error {
|
2026-06-09 18:10:19 +08:00
|
|
|
if f == nil || !f.locked {
|
|
|
|
|
return errFileLockNotLocked
|
|
|
|
|
}
|
2022-03-18 17:22:38 +08:00
|
|
|
err := syscall.Flock(f.fd, syscall.LOCK_UN)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
if err := syscall.Close(f.fd); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
f.fd = 0
|
|
|
|
|
f.locked = false
|
|
|
|
|
return nil
|
2022-03-18 17:22:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f *FileLock) LockWithTimeout(tm time.Duration, Exclusive bool) error {
|
2026-06-09 18:10:19 +08:00
|
|
|
if f.locked {
|
|
|
|
|
return ERR_ALREADY_LOCKED
|
|
|
|
|
}
|
|
|
|
|
var lockType int
|
|
|
|
|
if Exclusive {
|
|
|
|
|
lockType = syscall.LOCK_EX
|
|
|
|
|
} else {
|
|
|
|
|
lockType = syscall.LOCK_SH
|
|
|
|
|
}
|
|
|
|
|
if tm < 0 {
|
|
|
|
|
return f.Lock(Exclusive)
|
|
|
|
|
}
|
|
|
|
|
deadline := time.Now().Add(tm)
|
|
|
|
|
for {
|
|
|
|
|
err := f.lockWithFlags(lockType | syscall.LOCK_NB)
|
|
|
|
|
if err == nil {
|
2022-03-18 17:22:38 +08:00
|
|
|
return nil
|
|
|
|
|
}
|
2026-06-09 18:10:19 +08:00
|
|
|
if err != ERR_ALREADY_LOCKED {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if !time.Now().Before(deadline) {
|
|
|
|
|
return ERR_TIMEOUT
|
|
|
|
|
}
|
|
|
|
|
sleep := time.Millisecond * 10
|
|
|
|
|
if remaining := time.Until(deadline); remaining < sleep {
|
|
|
|
|
sleep = remaining
|
|
|
|
|
}
|
|
|
|
|
if sleep > 0 {
|
|
|
|
|
time.Sleep(sleep)
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-18 17:22:38 +08:00
|
|
|
}
|
|
|
|
|
|
2021-06-04 10:44:53 +08:00
|
|
|
func timespecToTime(ts syscall.Timespec) time.Time {
|
|
|
|
|
return time.Unix(int64(ts.Sec), int64(ts.Nsec))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func GetFileCreationTime(fileinfo os.FileInfo) time.Time {
|
2026-06-09 18:10:19 +08:00
|
|
|
if fileinfo == nil {
|
|
|
|
|
return time.Time{}
|
|
|
|
|
}
|
|
|
|
|
if stat, ok := fileinfo.Sys().(*syscall.Stat_t); ok && stat != nil {
|
|
|
|
|
return timespecToTime(stat.Birthtimespec)
|
|
|
|
|
}
|
|
|
|
|
return time.Time{}
|
2021-06-04 10:44:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func GetFileAccessTime(fileinfo os.FileInfo) time.Time {
|
2026-06-09 18:10:19 +08:00
|
|
|
if fileinfo == nil {
|
|
|
|
|
return time.Time{}
|
|
|
|
|
}
|
|
|
|
|
if stat, ok := fileinfo.Sys().(*syscall.Stat_t); ok && stat != nil {
|
|
|
|
|
return timespecToTime(stat.Atimespec)
|
|
|
|
|
}
|
|
|
|
|
return time.Time{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func SetFileTimes(file *os.File, info os.FileInfo) {
|
|
|
|
|
_ = SetFileTimesE(file, info)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
stat, ok := info.Sys().(*syscall.Stat_t)
|
|
|
|
|
if !ok || stat == nil {
|
|
|
|
|
return errUnsupportedFileInfo
|
|
|
|
|
}
|
|
|
|
|
atime := timespecToTime(stat.Atimespec)
|
|
|
|
|
mtime := info.ModTime()
|
|
|
|
|
return os.Chtimes(file.Name(), 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 os.Chtimes(file.Name(), now, now)
|
2021-06-04 10:44:53 +08:00
|
|
|
}
|