//go:build darwin // +build darwin package staros import ( "os" "syscall" "time" ) type FileLock struct { fd int filepath string locked bool } 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 } return f.lockWithFlags(lockType) } func (f *FileLock) LockNoBlocking(Exclusive bool) error { var lockType int if Exclusive { lockType = syscall.LOCK_EX } else { lockType = syscall.LOCK_SH } return f.lockWithFlags(lockType | syscall.LOCK_NB) } func (f *FileLock) lockWithFlags(lockType int) error { if f.locked { return ERR_ALREADY_LOCKED } if err := f.openFileForLock(); err != nil { return err } err := syscall.Flock(f.fd, lockType) if err != nil { _ = syscall.Close(f.fd) f.fd = 0 if err == syscall.EWOULDBLOCK { return ERR_ALREADY_LOCKED } return err } f.locked = true return nil } func (f *FileLock) Unlock() error { if f == nil || !f.locked { return errFileLockNotLocked } err := syscall.Flock(f.fd, syscall.LOCK_UN) if err != nil { return err } if err := syscall.Close(f.fd); err != nil { return err } f.fd = 0 f.locked = false return nil } func (f *FileLock) LockWithTimeout(tm time.Duration, Exclusive bool) error { 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 { return nil } 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) } } } func timespecToTime(ts syscall.Timespec) time.Time { return time.Unix(int64(ts.Sec), int64(ts.Nsec)) } func GetFileCreationTime(fileinfo os.FileInfo) time.Time { if fileinfo == nil { return time.Time{} } if stat, ok := fileinfo.Sys().(*syscall.Stat_t); ok && stat != nil { return timespecToTime(stat.Birthtimespec) } return time.Time{} } func GetFileAccessTime(fileinfo os.FileInfo) time.Time { 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) }