From 1ed62d300d25c232ee0bf8df895b1bf84f8924b6 Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Thu, 23 Feb 2023 13:29:51 +0000 Subject: [PATCH] Require OFD locks. --- error.go | 1 + vfs.go | 20 ++---- vfs_files.go | 58 +++------------- vfs_lock.go | 169 +++++++++++++++-------------------------------- vfs_lock_test.go | 36 +++++----- vfs_unix.go | 91 ++++++++++--------------- vfs_windows.go | 95 ++++++++++---------------- 7 files changed, 161 insertions(+), 309 deletions(-) diff --git a/error.go b/error.go index 8372af7..8e45f0b 100644 --- a/error.go +++ b/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 { diff --git a/vfs.go b/vfs.go index 9ea61d7..4251068 100644 --- a/vfs.go +++ b/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) } diff --git a/vfs_files.go b/vfs_files.go index 97a05a0..8d53969 100644 --- a/vfs_files.go +++ b/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 { diff --git a/vfs_lock.go b/vfs_lock.go index a247bbf..f307f2d 100644 --- a/vfs_lock.go +++ b/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) +} diff --git a/vfs_lock_test.go b/vfs_lock_test.go index bd62844..cdf467c 100644 --- a/vfs_lock_test.go +++ b/vfs_lock_test.go @@ -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) diff --git a/vfs_unix.go b/vfs_unix.go index 16a9028..be7607b 100644 --- a/vfs_unix.go +++ b/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 } diff --git a/vfs_windows.go b/vfs_windows.go index 24e9e6b..2c7c260 100644 --- a/vfs_windows.go +++ b/vfs_windows.go @@ -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 }