mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-11 21:49:13 +00:00
Require OFD locks.
This commit is contained in:
1
error.go
1
error.go
@@ -73,6 +73,7 @@ const (
|
||||
noGlobalErr = errorString("sqlite3: could not find global: ")
|
||||
noFuncErr = errorString("sqlite3: could not find function: ")
|
||||
timeErr = errorString("sqlite3: invalid time value")
|
||||
notImplErr = errorString("sqlite3: not implemented")
|
||||
)
|
||||
|
||||
func assertErr() errorString {
|
||||
|
||||
20
vfs.go
20
vfs.go
@@ -52,6 +52,10 @@ func vfsInstantiate(ctx context.Context, r wazero.Runtime) {
|
||||
}
|
||||
}
|
||||
|
||||
type vfsOSMethods bool
|
||||
|
||||
const vfsOS vfsOSMethods = false
|
||||
|
||||
func vfsExit(ctx context.Context, mod api.Module, exitCode uint32) {
|
||||
// Ensure other callers see the exit code.
|
||||
_ = mod.CloseWithExitCode(ctx, exitCode)
|
||||
@@ -220,20 +224,10 @@ func vfsOpen(ctx context.Context, mod api.Module, pVfs, zName, pFile uint32, fla
|
||||
}
|
||||
|
||||
if flags&OPEN_DELETEONCLOSE != 0 {
|
||||
deleteOnClose(file)
|
||||
vfsOS.DeleteOnClose(file)
|
||||
}
|
||||
|
||||
var info fs.FileInfo
|
||||
if flags&OPEN_MAIN_DB != 0 {
|
||||
info, err = file.Stat()
|
||||
if err != nil {
|
||||
return uint32(CANTOPEN)
|
||||
}
|
||||
if info.IsDir() {
|
||||
return uint32(CANTOPEN_ISDIR)
|
||||
}
|
||||
}
|
||||
id := vfsGetOpenFileID(file, info)
|
||||
id := vfsGetFileID(file)
|
||||
vfsFilePtr{mod, pFile}.SetID(id).SetLock(_NO_LOCK)
|
||||
|
||||
if pOutFlags != 0 {
|
||||
@@ -244,7 +238,7 @@ func vfsOpen(ctx context.Context, mod api.Module, pVfs, zName, pFile uint32, fla
|
||||
|
||||
func vfsClose(ctx context.Context, mod api.Module, pFile uint32) uint32 {
|
||||
id := vfsFilePtr{mod, pFile}.ID()
|
||||
err := vfsReleaseOpenFile(id)
|
||||
err := vfsCloseFile(id)
|
||||
if err != nil {
|
||||
return uint32(IOERR_CLOSE)
|
||||
}
|
||||
|
||||
58
vfs_files.go
58
vfs_files.go
@@ -7,68 +7,35 @@ import (
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
)
|
||||
|
||||
type vfsOpenFile struct {
|
||||
file *os.File
|
||||
info os.FileInfo
|
||||
nref int
|
||||
locker vfsFileLocker
|
||||
}
|
||||
|
||||
var (
|
||||
vfsOpenFiles []*vfsOpenFile
|
||||
vfsOpenFiles []*os.File
|
||||
vfsOpenFilesMtx sync.Mutex
|
||||
)
|
||||
|
||||
func vfsGetOpenFileID(file *os.File, info os.FileInfo) uint32 {
|
||||
func vfsGetFileID(file *os.File) uint32 {
|
||||
vfsOpenFilesMtx.Lock()
|
||||
defer vfsOpenFilesMtx.Unlock()
|
||||
|
||||
// Reuse an already opened file.
|
||||
if info != nil {
|
||||
for id, of := range vfsOpenFiles {
|
||||
if of == nil {
|
||||
continue
|
||||
}
|
||||
if os.SameFile(info, of.info) {
|
||||
of.nref++
|
||||
_ = file.Close()
|
||||
return uint32(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
of := &vfsOpenFile{
|
||||
file: file,
|
||||
info: info,
|
||||
nref: 1,
|
||||
locker: vfsFileLocker{file: file},
|
||||
}
|
||||
|
||||
// Find an empty slot.
|
||||
for id, ptr := range vfsOpenFiles {
|
||||
if ptr == nil {
|
||||
vfsOpenFiles[id] = of
|
||||
vfsOpenFiles[id] = file
|
||||
return uint32(id)
|
||||
}
|
||||
}
|
||||
|
||||
// Add a new slot.
|
||||
id := len(vfsOpenFiles)
|
||||
vfsOpenFiles = append(vfsOpenFiles, of)
|
||||
return uint32(id)
|
||||
vfsOpenFiles = append(vfsOpenFiles, file)
|
||||
return uint32(len(vfsOpenFiles) - 1)
|
||||
}
|
||||
|
||||
func vfsReleaseOpenFile(id uint32) error {
|
||||
func vfsCloseFile(id uint32) error {
|
||||
vfsOpenFilesMtx.Lock()
|
||||
defer vfsOpenFilesMtx.Unlock()
|
||||
|
||||
of := vfsOpenFiles[id]
|
||||
if of.nref--; of.nref > 0 {
|
||||
return nil
|
||||
}
|
||||
err := of.file.Close()
|
||||
file := vfsOpenFiles[id]
|
||||
vfsOpenFiles[id] = nil
|
||||
return err
|
||||
return file.Close()
|
||||
}
|
||||
|
||||
type vfsFilePtr struct {
|
||||
@@ -80,14 +47,7 @@ func (p vfsFilePtr) OSFile() *os.File {
|
||||
id := p.ID()
|
||||
vfsOpenFilesMtx.Lock()
|
||||
defer vfsOpenFilesMtx.Unlock()
|
||||
return vfsOpenFiles[id].file
|
||||
}
|
||||
|
||||
func (p vfsFilePtr) Locker() *vfsFileLocker {
|
||||
id := p.ID()
|
||||
vfsOpenFilesMtx.Lock()
|
||||
defer vfsOpenFilesMtx.Unlock()
|
||||
return &vfsOpenFiles[id].locker
|
||||
return vfsOpenFiles[id]
|
||||
}
|
||||
|
||||
func (p vfsFilePtr) ID() uint32 {
|
||||
|
||||
169
vfs_lock.go
169
vfs_lock.go
@@ -3,7 +3,6 @@ package sqlite3
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
)
|
||||
@@ -56,13 +55,6 @@ const (
|
||||
|
||||
type vfsLockState uint32
|
||||
|
||||
type vfsFileLocker struct {
|
||||
sync.Mutex
|
||||
file *os.File
|
||||
state vfsLockState
|
||||
shared int
|
||||
}
|
||||
|
||||
func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockState) uint32 {
|
||||
// Argument check. SQLite never explicitly requests a pendig lock.
|
||||
if eLock != _SHARED_LOCK && eLock != _RESERVED_LOCK && eLock != _EXCLUSIVE_LOCK {
|
||||
@@ -70,6 +62,7 @@ func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockSta
|
||||
}
|
||||
|
||||
ptr := vfsFilePtr{mod, pFile}
|
||||
file := ptr.OSFile()
|
||||
cLock := ptr.Lock()
|
||||
|
||||
switch {
|
||||
@@ -89,93 +82,49 @@ func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockSta
|
||||
return _OK
|
||||
}
|
||||
|
||||
fLock := ptr.Locker()
|
||||
fLock.Lock()
|
||||
defer fLock.Unlock()
|
||||
|
||||
// File state check.
|
||||
switch {
|
||||
case fLock.state < _NO_LOCK || fLock.state > _EXCLUSIVE_LOCK:
|
||||
panic(assertErr())
|
||||
case fLock.state == _NO_LOCK && fLock.shared != 0:
|
||||
panic(assertErr())
|
||||
case fLock.state == _EXCLUSIVE_LOCK && fLock.shared != 1:
|
||||
panic(assertErr())
|
||||
case fLock.state != _NO_LOCK && fLock.shared <= 0:
|
||||
panic(assertErr())
|
||||
case fLock.state < cLock:
|
||||
panic(assertErr())
|
||||
}
|
||||
|
||||
// If some other connection has a lock that precludes the requested lock, return BUSY.
|
||||
if cLock != fLock.state && (eLock > _SHARED_LOCK || fLock.state >= _PENDING_LOCK) {
|
||||
return uint32(BUSY)
|
||||
}
|
||||
|
||||
switch eLock {
|
||||
case _SHARED_LOCK:
|
||||
// Test the PENDING lock before acquiring a new SHARED lock.
|
||||
if locked, _ := fLock.CheckPending(); locked {
|
||||
return uint32(BUSY)
|
||||
}
|
||||
|
||||
// If some other connection has a SHARED or RESERVED lock,
|
||||
// increment the reference count and return OK.
|
||||
if fLock.state == _SHARED_LOCK || fLock.state == _RESERVED_LOCK {
|
||||
ptr.SetLock(_SHARED_LOCK)
|
||||
fLock.shared++
|
||||
return _OK
|
||||
}
|
||||
|
||||
// Must be unlocked to get SHARED.
|
||||
if fLock.state != _NO_LOCK {
|
||||
if cLock != _NO_LOCK {
|
||||
panic(assertErr())
|
||||
}
|
||||
if rc := fLock.GetShared(); rc != _OK {
|
||||
// Test the PENDING lock before acquiring a new SHARED lock.
|
||||
if locked, _ := vfsOS.CheckPendingLock(file); locked {
|
||||
return uint32(BUSY)
|
||||
}
|
||||
if rc := vfsOS.GetSharedLock(file); rc != _OK {
|
||||
return uint32(rc)
|
||||
}
|
||||
ptr.SetLock(_SHARED_LOCK)
|
||||
fLock.state = _SHARED_LOCK
|
||||
fLock.shared = 1
|
||||
return _OK
|
||||
|
||||
case _RESERVED_LOCK:
|
||||
// Must be SHARED to get RESERVED.
|
||||
if fLock.state != _SHARED_LOCK {
|
||||
if cLock != _SHARED_LOCK {
|
||||
panic(assertErr())
|
||||
}
|
||||
if rc := fLock.GetReserved(); rc != _OK {
|
||||
if rc := vfsOS.GetReservedLock(file); rc != _OK {
|
||||
return uint32(rc)
|
||||
}
|
||||
ptr.SetLock(_RESERVED_LOCK)
|
||||
fLock.state = _RESERVED_LOCK
|
||||
return _OK
|
||||
|
||||
case _EXCLUSIVE_LOCK:
|
||||
// Must be SHARED, PENDING or RESERVED to get EXCLUSIVE.
|
||||
if fLock.state <= _NO_LOCK || fLock.state >= _EXCLUSIVE_LOCK {
|
||||
// Must be SHARED, RESERVED or PENDING to get EXCLUSIVE.
|
||||
if cLock <= _NO_LOCK || cLock >= _EXCLUSIVE_LOCK {
|
||||
panic(assertErr())
|
||||
}
|
||||
|
||||
// A PENDING lock is needed before acquiring an EXCLUSIVE lock.
|
||||
if fLock.state == _RESERVED_LOCK {
|
||||
if rc := fLock.GetPending(); rc != _OK {
|
||||
if cLock == _RESERVED_LOCK {
|
||||
if rc := vfsOS.GetPendingLock(file); rc != _OK {
|
||||
return uint32(rc)
|
||||
}
|
||||
ptr.SetLock(_PENDING_LOCK)
|
||||
fLock.state = _PENDING_LOCK
|
||||
}
|
||||
|
||||
// We are trying for an EXCLUSIVE lock but another connection is still holding a shared lock.
|
||||
if fLock.shared > 1 {
|
||||
return uint32(BUSY)
|
||||
}
|
||||
|
||||
if rc := fLock.GetExclusive(); rc != _OK {
|
||||
if rc := vfsOS.GetExclusiveLock(file); rc != _OK {
|
||||
return uint32(rc)
|
||||
}
|
||||
ptr.SetLock(_EXCLUSIVE_LOCK)
|
||||
fLock.state = _EXCLUSIVE_LOCK
|
||||
return _OK
|
||||
|
||||
default:
|
||||
@@ -190,6 +139,7 @@ func vfsUnlock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockS
|
||||
}
|
||||
|
||||
ptr := vfsFilePtr{mod, pFile}
|
||||
file := ptr.OSFile()
|
||||
cLock := ptr.Lock()
|
||||
|
||||
// Connection state check.
|
||||
@@ -202,51 +152,22 @@ func vfsUnlock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockS
|
||||
return _OK
|
||||
}
|
||||
|
||||
fLock := ptr.Locker()
|
||||
fLock.Lock()
|
||||
defer fLock.Unlock()
|
||||
|
||||
// File state check.
|
||||
switch {
|
||||
case fLock.state <= _NO_LOCK || fLock.state > _EXCLUSIVE_LOCK:
|
||||
panic(assertErr())
|
||||
case fLock.state == _EXCLUSIVE_LOCK && fLock.shared != 1:
|
||||
panic(assertErr())
|
||||
case fLock.shared <= 0:
|
||||
panic(assertErr())
|
||||
case fLock.state < cLock:
|
||||
panic(assertErr())
|
||||
}
|
||||
|
||||
if cLock > _SHARED_LOCK {
|
||||
// The connection must own the lock to release it.
|
||||
if cLock != fLock.state {
|
||||
panic(assertErr())
|
||||
switch eLock {
|
||||
case _SHARED_LOCK:
|
||||
if rc := vfsOS.DowngradeLock(file, cLock); rc != _OK {
|
||||
return uint32(rc)
|
||||
}
|
||||
if eLock == _SHARED_LOCK {
|
||||
if rc := fLock.Downgrade(); rc != _OK {
|
||||
return uint32(rc)
|
||||
}
|
||||
ptr.SetLock(_SHARED_LOCK)
|
||||
fLock.state = _SHARED_LOCK
|
||||
return _OK
|
||||
}
|
||||
}
|
||||
ptr.SetLock(_SHARED_LOCK)
|
||||
return _OK
|
||||
|
||||
// If we get here, make sure we're dropping all locks.
|
||||
if eLock != _NO_LOCK {
|
||||
panic(assertErr())
|
||||
}
|
||||
|
||||
// Release the connection lock and decrement the shared lock counter.
|
||||
// Release the file lock only when all connections have released the lock.
|
||||
ptr.SetLock(_NO_LOCK)
|
||||
if fLock.shared--; fLock.shared == 0 {
|
||||
rc := fLock.Release()
|
||||
fLock.state = _NO_LOCK
|
||||
case _NO_LOCK:
|
||||
rc := vfsOS.ReleaseLock(file, cLock)
|
||||
ptr.SetLock(_NO_LOCK)
|
||||
return uint32(rc)
|
||||
|
||||
default:
|
||||
panic(assertErr())
|
||||
}
|
||||
return _OK
|
||||
}
|
||||
|
||||
func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut uint32) uint32 {
|
||||
@@ -257,16 +178,9 @@ func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut ui
|
||||
panic(assertErr())
|
||||
}
|
||||
|
||||
fLock := ptr.Locker()
|
||||
fLock.Lock()
|
||||
defer fLock.Unlock()
|
||||
file := ptr.OSFile()
|
||||
|
||||
if fLock.state >= _RESERVED_LOCK {
|
||||
memory{mod}.writeUint32(pResOut, 1)
|
||||
return _OK
|
||||
}
|
||||
|
||||
locked, rc := fLock.CheckReserved()
|
||||
locked, rc := vfsOS.CheckReservedLock(file)
|
||||
var res uint32
|
||||
if locked {
|
||||
res = 1
|
||||
@@ -274,3 +188,28 @@ func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut ui
|
||||
memory{mod}.writeUint32(pResOut, res)
|
||||
return uint32(rc)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) GetSharedLock(file *os.File) xErrorCode {
|
||||
// Acquire the SHARED lock.
|
||||
return vfsOS.readLock(file, _SHARED_FIRST, _SHARED_SIZE)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) GetReservedLock(file *os.File) xErrorCode {
|
||||
// Acquire the RESERVED lock.
|
||||
return vfsOS.writeLock(file, _RESERVED_BYTE, 1)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) GetPendingLock(file *os.File) xErrorCode {
|
||||
// Acquire the PENDING lock.
|
||||
return vfsOS.writeLock(file, _PENDING_BYTE, 1)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) CheckReservedLock(file *os.File) (bool, xErrorCode) {
|
||||
// Test the RESERVED lock.
|
||||
return vfsOS.checkLock(file, _RESERVED_BYTE, 1)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) CheckPendingLock(file *os.File) (bool, xErrorCode) {
|
||||
// Test the PENDING lock.
|
||||
return vfsOS.checkLock(file, _PENDING_BYTE, 1)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ func Test_vfsLock(t *testing.T) {
|
||||
// Other OSes lack open file descriptors locks.
|
||||
switch runtime.GOOS {
|
||||
case "linux", "darwin", "illumos", "windows":
|
||||
//
|
||||
break
|
||||
default:
|
||||
t.Skip()
|
||||
}
|
||||
@@ -33,26 +33,14 @@ func Test_vfsLock(t *testing.T) {
|
||||
}
|
||||
defer file2.Close()
|
||||
|
||||
// Bypass open file reuse.
|
||||
vfsOpenFiles = append(vfsOpenFiles, &vfsOpenFile{
|
||||
file: file1,
|
||||
nref: 1,
|
||||
locker: vfsFileLocker{file: file1},
|
||||
}, &vfsOpenFile{
|
||||
file: file2,
|
||||
nref: 1,
|
||||
locker: vfsFileLocker{file: file2},
|
||||
})
|
||||
|
||||
mem := newMemory(128)
|
||||
mem.writeUint32(4+4, 0)
|
||||
mem.writeUint32(16+4, 1)
|
||||
|
||||
const (
|
||||
pFile1 = 4
|
||||
pFile2 = 16
|
||||
pOutput = 32
|
||||
)
|
||||
mem := newMemory(128)
|
||||
vfsFilePtr{mem.mod, pFile1}.SetID(vfsGetFileID(file1)).SetLock(_NO_LOCK)
|
||||
vfsFilePtr{mem.mod, pFile2}.SetID(vfsGetFileID(file2)).SetLock(_NO_LOCK)
|
||||
|
||||
rc := vfsCheckReservedLock(context.TODO(), mem.mod, pFile1, pOutput)
|
||||
if rc != _OK {
|
||||
@@ -110,11 +98,27 @@ func Test_vfsLock(t *testing.T) {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
|
||||
rc = vfsCheckReservedLock(context.TODO(), mem.mod, pFile1, pOutput)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := mem.readUint32(pOutput); got == 0 {
|
||||
t.Error("file wasn't locked")
|
||||
}
|
||||
|
||||
rc = vfsUnlock(context.TODO(), mem.mod, pFile2, _SHARED_LOCK)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
|
||||
rc = vfsCheckReservedLock(context.TODO(), mem.mod, pFile1, pOutput)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := mem.readUint32(pOutput); got != 0 {
|
||||
t.Error("file was locked")
|
||||
}
|
||||
|
||||
rc = vfsLock(context.TODO(), mem.mod, pFile1, _SHARED_LOCK)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
|
||||
91
vfs_unix.go
91
vfs_unix.go
@@ -8,34 +8,19 @@ import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func deleteOnClose(f *os.File) {
|
||||
_ = os.Remove(f.Name())
|
||||
func (vfsOSMethods) DeleteOnClose(file *os.File) {
|
||||
_ = os.Remove(file.Name())
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) GetShared() xErrorCode {
|
||||
// Acquire the SHARED lock.
|
||||
return l.readLock(_SHARED_FIRST, _SHARED_SIZE)
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) GetReserved() xErrorCode {
|
||||
// Acquire the RESERVED lock.
|
||||
return l.writeLock(_RESERVED_BYTE, 1)
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) GetPending() xErrorCode {
|
||||
// Acquire the PENDING lock.
|
||||
return l.writeLock(_PENDING_BYTE, 1)
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) GetExclusive() xErrorCode {
|
||||
func (vfsOSMethods) GetExclusiveLock(file *os.File) xErrorCode {
|
||||
// Acquire the EXCLUSIVE lock.
|
||||
return l.writeLock(_SHARED_FIRST, _SHARED_SIZE)
|
||||
return vfsOS.writeLock(file, _SHARED_FIRST, _SHARED_SIZE)
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) Downgrade() xErrorCode {
|
||||
if l.state >= _EXCLUSIVE_LOCK {
|
||||
func (vfsOSMethods) DowngradeLock(file *os.File, state vfsLockState) xErrorCode {
|
||||
if state >= _EXCLUSIVE_LOCK {
|
||||
// Downgrade to a SHARED lock.
|
||||
if rc := l.readLock(_SHARED_FIRST, _SHARED_SIZE); rc != _OK {
|
||||
if rc := vfsOS.readLock(file, _SHARED_FIRST, _SHARED_SIZE); rc != _OK {
|
||||
// In theory, the downgrade to a SHARED cannot fail because another
|
||||
// process is holding an incompatible lock. If it does, this
|
||||
// indicates that the other process is not following the locking
|
||||
@@ -45,26 +30,16 @@ func (l *vfsFileLocker) Downgrade() xErrorCode {
|
||||
}
|
||||
}
|
||||
// Release the PENDING and RESERVED locks.
|
||||
return l.unlock(_PENDING_BYTE, 2)
|
||||
return vfsOS.unlock(file, _PENDING_BYTE, 2)
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) Release() xErrorCode {
|
||||
func (vfsOSMethods) ReleaseLock(file *os.File, _ vfsLockState) xErrorCode {
|
||||
// Release all locks.
|
||||
return l.unlock(0, 0)
|
||||
return vfsOS.unlock(file, 0, 0)
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) CheckReserved() (bool, xErrorCode) {
|
||||
// Test the RESERVED lock.
|
||||
return l.checkLock(_RESERVED_BYTE, 1)
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) CheckPending() (bool, xErrorCode) {
|
||||
// Test the PENDING lock.
|
||||
return l.checkLock(_PENDING_BYTE, 1)
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) unlock(start, len int64) xErrorCode {
|
||||
err := l.fcntlSetLock(&syscall.Flock_t{
|
||||
func (vfsOSMethods) unlock(file *os.File, start, len int64) xErrorCode {
|
||||
err := vfsOS.fcntlSetLock(file, &syscall.Flock_t{
|
||||
Type: syscall.F_UNLCK,
|
||||
Start: start,
|
||||
Len: len,
|
||||
@@ -75,67 +50,71 @@ func (l *vfsFileLocker) unlock(start, len int64) xErrorCode {
|
||||
return _OK
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) readLock(start, len int64) xErrorCode {
|
||||
return l.errorCode(l.fcntlSetLock(&syscall.Flock_t{
|
||||
func (vfsOSMethods) readLock(file *os.File, start, len int64) xErrorCode {
|
||||
return vfsOS.lockErrorCode(vfsOS.fcntlSetLock(file, &syscall.Flock_t{
|
||||
Type: syscall.F_RDLCK,
|
||||
Start: start,
|
||||
Len: len,
|
||||
}), IOERR_RDLOCK)
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) writeLock(start, len int64) xErrorCode {
|
||||
return l.errorCode(l.fcntlSetLock(&syscall.Flock_t{
|
||||
func (vfsOSMethods) writeLock(file *os.File, start, len int64) xErrorCode {
|
||||
return vfsOS.lockErrorCode(vfsOS.fcntlSetLock(file, &syscall.Flock_t{
|
||||
Type: syscall.F_WRLCK,
|
||||
Start: start,
|
||||
Len: len,
|
||||
}), IOERR_LOCK)
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) checkLock(start, len int64) (bool, xErrorCode) {
|
||||
func (vfsOSMethods) checkLock(file *os.File, start, len int64) (bool, xErrorCode) {
|
||||
lock := syscall.Flock_t{
|
||||
Type: syscall.F_RDLCK,
|
||||
Start: start,
|
||||
Len: len,
|
||||
}
|
||||
if l.fcntlGetLock(&lock) != nil {
|
||||
if vfsOS.fcntlGetLock(file, &lock) != nil {
|
||||
return false, IOERR_CHECKRESERVEDLOCK
|
||||
}
|
||||
return lock.Type != syscall.F_UNLCK, _OK
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) fcntlGetLock(lock *syscall.Flock_t) error {
|
||||
F_GETLK := syscall.F_GETLK
|
||||
func (vfsOSMethods) fcntlGetLock(file *os.File, lock *syscall.Flock_t) error {
|
||||
var F_OFD_GETLK int
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/fcntl.h
|
||||
F_GETLK = 36 // F_OFD_GETLK
|
||||
F_OFD_GETLK = 36 // F_OFD_GETLK
|
||||
case "darwin":
|
||||
// https://github.com/apple/darwin-xnu/blob/main/bsd/sys/fcntl.h
|
||||
F_GETLK = 92 // F_OFD_GETLK
|
||||
F_OFD_GETLK = 92 // F_OFD_GETLK
|
||||
case "illumos":
|
||||
// https://github.com/illumos/illumos-gate/blob/master/usr/src/uts/common/sys/fcntl.h
|
||||
F_GETLK = 47 // F_OFD_GETLK
|
||||
F_OFD_GETLK = 47 // F_OFD_GETLK
|
||||
default:
|
||||
return notImplErr
|
||||
}
|
||||
return syscall.FcntlFlock(l.file.Fd(), F_GETLK, lock)
|
||||
return syscall.FcntlFlock(file.Fd(), F_OFD_GETLK, lock)
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) fcntlSetLock(lock *syscall.Flock_t) error {
|
||||
F_SETLK := syscall.F_SETLK
|
||||
func (vfsOSMethods) fcntlSetLock(file *os.File, lock *syscall.Flock_t) error {
|
||||
var F_OFD_SETLK int
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/fcntl.h
|
||||
F_SETLK = 37 // F_OFD_SETLK
|
||||
F_OFD_SETLK = 37 // F_OFD_SETLK
|
||||
case "darwin":
|
||||
// https://github.com/apple/darwin-xnu/blob/main/bsd/sys/fcntl.h
|
||||
F_SETLK = 90 // F_OFD_SETLK
|
||||
F_OFD_SETLK = 90 // F_OFD_SETLK
|
||||
case "illumos":
|
||||
// https://github.com/illumos/illumos-gate/blob/master/usr/src/uts/common/sys/fcntl.h
|
||||
F_SETLK = 48 // F_OFD_SETLK
|
||||
F_OFD_SETLK = 48 // F_OFD_SETLK
|
||||
default:
|
||||
return notImplErr
|
||||
}
|
||||
return syscall.FcntlFlock(l.file.Fd(), F_SETLK, lock)
|
||||
return syscall.FcntlFlock(file.Fd(), F_OFD_SETLK, lock)
|
||||
}
|
||||
|
||||
func (*vfsFileLocker) errorCode(err error, def xErrorCode) xErrorCode {
|
||||
func (vfsOSMethods) lockErrorCode(err error, def xErrorCode) xErrorCode {
|
||||
if err == nil {
|
||||
return _OK
|
||||
}
|
||||
|
||||
@@ -7,44 +7,29 @@ import (
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func deleteOnClose(f *os.File) {}
|
||||
func (vfsOSMethods) DeleteOnClose(file *os.File) {}
|
||||
|
||||
func (l *vfsFileLocker) GetShared() xErrorCode {
|
||||
// Acquire the SHARED lock.
|
||||
return l.readLock(_SHARED_FIRST, _SHARED_SIZE)
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) GetReserved() xErrorCode {
|
||||
// Acquire the RESERVED lock.
|
||||
return l.writeLock(_RESERVED_BYTE, 1)
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) GetPending() xErrorCode {
|
||||
// Acquire the PENDING lock.
|
||||
return l.writeLock(_PENDING_BYTE, 1)
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) GetExclusive() xErrorCode {
|
||||
func (vfsOSMethods) GetExclusiveLock(file *os.File) xErrorCode {
|
||||
// Release the SHARED lock.
|
||||
l.unlock(_SHARED_FIRST, _SHARED_SIZE)
|
||||
vfsOS.unlock(file, _SHARED_FIRST, _SHARED_SIZE)
|
||||
|
||||
// Acquire the EXCLUSIVE lock.
|
||||
rc := l.writeLock(_SHARED_FIRST, _SHARED_SIZE)
|
||||
rc := vfsOS.writeLock(file, _SHARED_FIRST, _SHARED_SIZE)
|
||||
|
||||
// Reacquire the SHARED lock.
|
||||
if rc != _OK {
|
||||
l.readLock(_SHARED_FIRST, _SHARED_SIZE)
|
||||
vfsOS.readLock(file, _SHARED_FIRST, _SHARED_SIZE)
|
||||
}
|
||||
return rc
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) Downgrade() xErrorCode {
|
||||
if l.state >= _EXCLUSIVE_LOCK {
|
||||
func (vfsOSMethods) DowngradeLock(file *os.File, state vfsLockState) xErrorCode {
|
||||
if state >= _EXCLUSIVE_LOCK {
|
||||
// Release the SHARED lock.
|
||||
l.unlock(_SHARED_FIRST, _SHARED_SIZE)
|
||||
vfsOS.unlock(file, _SHARED_FIRST, _SHARED_SIZE)
|
||||
|
||||
// Reacquire the SHARED lock.
|
||||
if rc := l.readLock(_SHARED_FIRST, _SHARED_SIZE); rc != _OK {
|
||||
if rc := vfsOS.readLock(file, _SHARED_FIRST, _SHARED_SIZE); rc != _OK {
|
||||
// This should never happen.
|
||||
// We should always be able to reacquire the read lock.
|
||||
return IOERR_RDLOCK
|
||||
@@ -52,49 +37,31 @@ func (l *vfsFileLocker) Downgrade() xErrorCode {
|
||||
}
|
||||
|
||||
// Release the PENDING and RESERVED locks.
|
||||
if l.state >= _RESERVED_LOCK {
|
||||
l.unlock(_RESERVED_BYTE, 1)
|
||||
if state >= _RESERVED_LOCK {
|
||||
vfsOS.unlock(file, _RESERVED_BYTE, 1)
|
||||
}
|
||||
if l.state >= _PENDING_LOCK {
|
||||
l.unlock(_PENDING_BYTE, 1)
|
||||
if state >= _PENDING_LOCK {
|
||||
vfsOS.unlock(file, _PENDING_BYTE, 1)
|
||||
}
|
||||
return _OK
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) Release() xErrorCode {
|
||||
func (vfsOSMethods) ReleaseLock(file *os.File, state vfsLockState) xErrorCode {
|
||||
// Release all locks.
|
||||
if l.state >= _RESERVED_LOCK {
|
||||
l.unlock(_RESERVED_BYTE, 1)
|
||||
if state >= _RESERVED_LOCK {
|
||||
vfsOS.unlock(file, _RESERVED_BYTE, 1)
|
||||
}
|
||||
if l.state >= _SHARED_LOCK {
|
||||
l.unlock(_SHARED_FIRST, _SHARED_SIZE)
|
||||
if state >= _SHARED_LOCK {
|
||||
vfsOS.unlock(file, _SHARED_FIRST, _SHARED_SIZE)
|
||||
}
|
||||
if l.state >= _PENDING_LOCK {
|
||||
l.unlock(_PENDING_BYTE, 1)
|
||||
if state >= _PENDING_LOCK {
|
||||
vfsOS.unlock(file, _PENDING_BYTE, 1)
|
||||
}
|
||||
return _OK
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) CheckReserved() (bool, xErrorCode) {
|
||||
// Test the RESERVED lock.
|
||||
rc := l.readLock(_RESERVED_BYTE, 1)
|
||||
if rc == _OK {
|
||||
l.unlock(_RESERVED_BYTE, 1)
|
||||
}
|
||||
return rc != _OK, _OK
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) CheckPending() (bool, xErrorCode) {
|
||||
// Test the PENDING lock.
|
||||
rc := l.readLock(_PENDING_BYTE, 1)
|
||||
if rc == _OK {
|
||||
l.unlock(_PENDING_BYTE, 1)
|
||||
}
|
||||
return rc != _OK, _OK
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) unlock(start, len uint32) xErrorCode {
|
||||
err := windows.UnlockFileEx(windows.Handle(l.file.Fd()),
|
||||
func (vfsOSMethods) unlock(file *os.File, start, len uint32) xErrorCode {
|
||||
err := windows.UnlockFileEx(windows.Handle(file.Fd()),
|
||||
0, len, 0, &windows.Overlapped{Offset: start})
|
||||
if err != nil {
|
||||
return IOERR_UNLOCK
|
||||
@@ -102,21 +69,29 @@ func (l *vfsFileLocker) unlock(start, len uint32) xErrorCode {
|
||||
return _OK
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) readLock(start, len uint32) xErrorCode {
|
||||
return l.errorCode(windows.LockFileEx(windows.Handle(l.file.Fd()),
|
||||
func (vfsOSMethods) readLock(file *os.File, start, len uint32) xErrorCode {
|
||||
return vfsOS.lockErrorCode(windows.LockFileEx(windows.Handle(file.Fd()),
|
||||
windows.LOCKFILE_FAIL_IMMEDIATELY,
|
||||
0, len, 0, &windows.Overlapped{Offset: start}),
|
||||
IOERR_RDLOCK)
|
||||
}
|
||||
|
||||
func (l *vfsFileLocker) writeLock(start, len uint32) xErrorCode {
|
||||
return l.errorCode(windows.LockFileEx(windows.Handle(l.file.Fd()),
|
||||
func (vfsOSMethods) writeLock(file *os.File, start, len uint32) xErrorCode {
|
||||
return vfsOS.lockErrorCode(windows.LockFileEx(windows.Handle(file.Fd()),
|
||||
windows.LOCKFILE_FAIL_IMMEDIATELY|windows.LOCKFILE_EXCLUSIVE_LOCK,
|
||||
0, len, 0, &windows.Overlapped{Offset: start}),
|
||||
IOERR_LOCK)
|
||||
}
|
||||
|
||||
func (*vfsFileLocker) errorCode(err error, def xErrorCode) xErrorCode {
|
||||
func (vfsOSMethods) checkLock(file *os.File, start, len uint32) (bool, xErrorCode) {
|
||||
rc := vfsOS.readLock(file, start, len)
|
||||
if rc == _OK {
|
||||
vfsOS.unlock(file, start, len)
|
||||
}
|
||||
return rc != _OK, _OK
|
||||
}
|
||||
|
||||
func (vfsOSMethods) lockErrorCode(err error, def xErrorCode) xErrorCode {
|
||||
if err == nil {
|
||||
return _OK
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user