staros/files_unix.go

185 lines
3.6 KiB
Go
Raw Normal View History

//go:build linux
// +build linux
2020-12-21 17:19:41 +08:00
package staros
import (
"golang.org/x/sys/unix"
2020-12-21 17:19:41 +08:00
"os"
"syscall"
"time"
)
2021-09-01 11:01:51 +08:00
type FileLock struct {
fd int
filepath string
locked bool
2021-09-01 11:01:51 +08:00
}
2020-12-21 17:19:41 +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 {
if fileinfo == nil {
return time.Time{}
}
// Linux os.FileInfo/syscall.Stat_t does not expose a stable birth time.
// Returning ctime here would be wrong because it tracks inode changes.
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 stat, ok := fileinfo.Sys().(*syscall.Stat_t); ok && stat != nil {
return timespecToTime(stat.Atim)
}
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.Atim)
mtime := info.ModTime()
return setFileTimes(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 setFileTimes(file.Name(), now, now)
}
func setFileTimes(path string, atime, mtime time.Time) error {
ts := [2]unix.Timespec{
unix.NsecToTimespec(atime.UnixNano()),
unix.NsecToTimespec(mtime.UnixNano()),
}
return unix.UtimesNanoAt(unix.AT_FDCWD, path, ts[:], unix.AT_SYMLINK_NOFOLLOW)
2021-06-04 10:44:53 +08:00
}
2021-09-01 11:01:51 +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
}
2021-09-01 13:52:43 +08:00
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)
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 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
}
2021-09-01 11:01:51 +08:00
if err := f.openFileForLock(); err != nil {
return err
}
err := syscall.Flock(f.fd, lockType)
2021-09-01 11:01:51 +08:00
if err != nil {
_ = syscall.Close(f.fd)
f.fd = 0
2021-09-01 11:01:51 +08:00
if err == syscall.EWOULDBLOCK {
return ERR_ALREADY_LOCKED
}
return err
2021-09-01 11:01:51 +08:00
}
f.locked = true
return nil
2021-09-01 11:01:51 +08:00
}
func (f *FileLock) Unlock() error {
if f == nil || !f.locked {
return errFileLockNotLocked
}
2021-09-01 11:01:51 +08:00
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
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 {
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 {
2021-09-01 11:01:51 +08:00
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)
}
}
2021-09-01 11:01:51 +08:00
}