2024-11-07 03:16:28 +03:00
|
|
|
//go:build sqlite3_dotlk
|
|
|
|
|
|
|
|
package vfs
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"io/fs"
|
|
|
|
"os"
|
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// +checklocks:vfsDotLocksMtx
|
|
|
|
vfsDotLocks = map[string]*vfsDotLocker{}
|
|
|
|
vfsDotLocksMtx sync.Mutex
|
|
|
|
)
|
|
|
|
|
|
|
|
type vfsDotLocker struct {
|
|
|
|
shared int // +checklocks:vfsDotLocksMtx
|
|
|
|
pending *os.File // +checklocks:vfsDotLocksMtx
|
|
|
|
reserved *os.File // +checklocks:vfsDotLocksMtx
|
|
|
|
}
|
|
|
|
|
|
|
|
func osGetSharedLock(file *os.File) _ErrorCode {
|
|
|
|
vfsDotLocksMtx.Lock()
|
|
|
|
defer vfsDotLocksMtx.Unlock()
|
|
|
|
|
|
|
|
name := file.Name()
|
|
|
|
locker := vfsDotLocks[name]
|
|
|
|
if locker == nil {
|
2024-11-26 19:25:48 +03:00
|
|
|
f, err := os.OpenFile(name+".lock", os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
|
|
|
|
f.Close()
|
2024-11-07 03:16:28 +03:00
|
|
|
if errors.Is(err, fs.ErrExist) {
|
|
|
|
return _BUSY // Another process has the lock.
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return _IOERR_LOCK
|
|
|
|
}
|
|
|
|
locker = &vfsDotLocker{}
|
|
|
|
vfsDotLocks[name] = locker
|
|
|
|
}
|
|
|
|
|
|
|
|
if locker.pending != nil {
|
|
|
|
return _BUSY
|
|
|
|
}
|
|
|
|
locker.shared++
|
|
|
|
return _OK
|
|
|
|
}
|
|
|
|
|
|
|
|
func osGetReservedLock(file *os.File) _ErrorCode {
|
|
|
|
vfsDotLocksMtx.Lock()
|
|
|
|
defer vfsDotLocksMtx.Unlock()
|
|
|
|
|
|
|
|
name := file.Name()
|
|
|
|
locker := vfsDotLocks[name]
|
|
|
|
if locker == nil {
|
|
|
|
return _IOERR_LOCK
|
|
|
|
}
|
|
|
|
|
|
|
|
if locker.reserved != nil && locker.reserved != file {
|
|
|
|
return _BUSY
|
|
|
|
}
|
|
|
|
locker.reserved = file
|
|
|
|
return _OK
|
|
|
|
}
|
|
|
|
|
|
|
|
func osGetExclusiveLock(file *os.File, _ *LockLevel) _ErrorCode {
|
|
|
|
vfsDotLocksMtx.Lock()
|
|
|
|
defer vfsDotLocksMtx.Unlock()
|
|
|
|
|
|
|
|
name := file.Name()
|
|
|
|
locker := vfsDotLocks[name]
|
|
|
|
if locker == nil {
|
|
|
|
return _IOERR_LOCK
|
|
|
|
}
|
|
|
|
|
|
|
|
if locker.pending != nil && locker.pending != file {
|
|
|
|
return _BUSY
|
|
|
|
}
|
|
|
|
locker.pending = file
|
|
|
|
if locker.shared > 1 {
|
|
|
|
return _BUSY
|
|
|
|
}
|
|
|
|
return _OK
|
|
|
|
}
|
|
|
|
|
|
|
|
func osDowngradeLock(file *os.File, _ LockLevel) _ErrorCode {
|
|
|
|
vfsDotLocksMtx.Lock()
|
|
|
|
defer vfsDotLocksMtx.Unlock()
|
|
|
|
|
|
|
|
name := file.Name()
|
|
|
|
locker := vfsDotLocks[name]
|
|
|
|
if locker == nil {
|
|
|
|
return _IOERR_UNLOCK
|
|
|
|
}
|
|
|
|
|
|
|
|
if locker.reserved == file {
|
|
|
|
locker.reserved = nil
|
|
|
|
}
|
|
|
|
if locker.pending == file {
|
|
|
|
locker.pending = nil
|
|
|
|
}
|
|
|
|
return _OK
|
|
|
|
}
|
|
|
|
|
|
|
|
func osReleaseLock(file *os.File, state LockLevel) _ErrorCode {
|
|
|
|
vfsDotLocksMtx.Lock()
|
|
|
|
defer vfsDotLocksMtx.Unlock()
|
|
|
|
|
|
|
|
name := file.Name()
|
|
|
|
locker := vfsDotLocks[name]
|
|
|
|
if locker == nil {
|
|
|
|
return _IOERR_UNLOCK
|
|
|
|
}
|
|
|
|
|
|
|
|
if locker.shared == 1 {
|
|
|
|
err := os.Remove(name + ".lock")
|
|
|
|
if err != nil && !errors.Is(err, fs.ErrNotExist) {
|
|
|
|
return _IOERR_UNLOCK
|
|
|
|
}
|
|
|
|
delete(vfsDotLocks, name)
|
|
|
|
}
|
|
|
|
|
|
|
|
if locker.reserved == file {
|
|
|
|
locker.reserved = nil
|
|
|
|
}
|
|
|
|
if locker.pending == file {
|
|
|
|
locker.pending = nil
|
|
|
|
}
|
|
|
|
locker.shared--
|
|
|
|
return _OK
|
|
|
|
}
|
|
|
|
|
|
|
|
func osCheckReservedLock(file *os.File) (bool, _ErrorCode) {
|
|
|
|
vfsDotLocksMtx.Lock()
|
|
|
|
defer vfsDotLocksMtx.Unlock()
|
|
|
|
|
|
|
|
name := file.Name()
|
|
|
|
locker := vfsDotLocks[name]
|
|
|
|
if locker == nil {
|
|
|
|
return false, _OK
|
|
|
|
}
|
|
|
|
return locker.reserved != nil, _OK
|
|
|
|
}
|