diff --git a/conn.go b/conn.go index d61e0c1..56ed013 100644 --- a/conn.go +++ b/conn.go @@ -40,7 +40,7 @@ func OpenFlags(filename string, flags OpenFlag) (conn *Conn, err error) { c := newConn(module) c.ctx = context.Background() namePtr := c.newString(filename) - connPtr := c.new(wordSize) + connPtr := c.new(ptrlen) defer c.free(namePtr) defer c.free(connPtr) @@ -86,8 +86,8 @@ func (c *Conn) Prepare(sql string) (stmt *Stmt, tail string, err error) { func (c *Conn) PrepareFlags(sql string, flags PrepareFlag) (stmt *Stmt, tail string, err error) { sqlPtr := c.newString(sql) - stmtPtr := c.new(wordSize) - tailPtr := c.new(wordSize) + stmtPtr := c.new(ptrlen) + tailPtr := c.new(ptrlen) defer c.free(sqlPtr) defer c.free(stmtPtr) defer c.free(tailPtr) @@ -221,5 +221,3 @@ func getString(memory api.Memory, ptr, maxlen uint32) string { return string(mem[:i]) } } - -const wordSize = 4 diff --git a/const.go b/const.go index e16fbfe..e45d76a 100644 --- a/const.go +++ b/const.go @@ -8,6 +8,9 @@ const ( _UTF8 = 1 _MAX_PATHNAME = 512 + + assert = true + ptrlen = 4 ) type ErrorCode uint8 diff --git a/vfs.go b/vfs.go index 3de1d10..86aac8a 100644 --- a/vfs.go +++ b/vfs.go @@ -67,15 +67,15 @@ func vfsLocaltime(ctx context.Context, mod api.Module, t uint64, pTm uint32) uin } // https://pubs.opengroup.org/onlinepubs/7908799/xsh/time.h.html if mem := mod.Memory(); true && - mem.WriteUint32Le(pTm+0*wordSize, uint32(tm.Second())) && - mem.WriteUint32Le(pTm+1*wordSize, uint32(tm.Minute())) && - mem.WriteUint32Le(pTm+2*wordSize, uint32(tm.Hour())) && - mem.WriteUint32Le(pTm+3*wordSize, uint32(tm.Day())) && - mem.WriteUint32Le(pTm+4*wordSize, uint32(tm.Month()-time.January)) && - mem.WriteUint32Le(pTm+5*wordSize, uint32(tm.Year()-1900)) && - mem.WriteUint32Le(pTm+6*wordSize, uint32(tm.Weekday()-time.Sunday)) && - mem.WriteUint32Le(pTm+7*wordSize, uint32(tm.YearDay()-1)) && - mem.WriteUint32Le(pTm+8*wordSize, uint32(isdst)) { + mem.WriteUint32Le(pTm+0*ptrlen, uint32(tm.Second())) && + mem.WriteUint32Le(pTm+1*ptrlen, uint32(tm.Minute())) && + mem.WriteUint32Le(pTm+2*ptrlen, uint32(tm.Hour())) && + mem.WriteUint32Le(pTm+3*ptrlen, uint32(tm.Day())) && + mem.WriteUint32Le(pTm+4*ptrlen, uint32(tm.Month()-time.January)) && + mem.WriteUint32Le(pTm+5*ptrlen, uint32(tm.Year()-1900)) && + mem.WriteUint32Le(pTm+6*ptrlen, uint32(tm.Weekday()-time.Sunday)) && + mem.WriteUint32Le(pTm+7*ptrlen, uint32(tm.YearDay()-1)) && + mem.WriteUint32Le(pTm+8*ptrlen, uint32(isdst)) { return _OK } panic(rangeErr) diff --git a/vfs_files.go b/vfs_files.go index 3d3b748..6c1fb09 100644 --- a/vfs_files.go +++ b/vfs_files.go @@ -42,7 +42,7 @@ func vfsGetOpenFileID(file *os.File, info os.FileInfo) uint32 { info: info, nref: 1, - vfsLocker: &vfsNoopLocker{}, + vfsLocker: &vfsFileLocker{file, _NO_LOCK}, } // Find an empty slot. @@ -88,7 +88,7 @@ func (p vfsFilePtr) ID() uint32 { if p.ptr == 0 { panic(nilErr) } - id, ok := p.Memory().ReadUint32Le(p.ptr + wordSize) + id, ok := p.Memory().ReadUint32Le(p.ptr + ptrlen) if !ok { panic(rangeErr) } @@ -99,7 +99,7 @@ func (p vfsFilePtr) Lock() vfsLockState { if p.ptr == 0 { panic(nilErr) } - lk, ok := p.Memory().ReadUint32Le(p.ptr + 2*wordSize) + lk, ok := p.Memory().ReadUint32Le(p.ptr + 2*ptrlen) if !ok { panic(rangeErr) } @@ -110,7 +110,7 @@ func (p vfsFilePtr) SetID(id uint32) vfsFilePtr { if p.ptr == 0 { panic(nilErr) } - if ok := p.Memory().WriteUint32Le(p.ptr+wordSize, id); !ok { + if ok := p.Memory().WriteUint32Le(p.ptr+ptrlen, id); !ok { panic(rangeErr) } return p @@ -120,7 +120,7 @@ func (p vfsFilePtr) SetLock(lock vfsLockState) vfsFilePtr { if p.ptr == 0 { panic(nilErr) } - if ok := p.Memory().WriteUint32Le(p.ptr+2*wordSize, uint32(lock)); !ok { + if ok := p.Memory().WriteUint32Le(p.ptr+2*ptrlen, uint32(lock)); !ok { panic(rangeErr) } return p diff --git a/vfs_lock.go b/vfs_lock.go index 3fb7641..06a9fd1 100644 --- a/vfs_lock.go +++ b/vfs_lock.go @@ -52,19 +52,22 @@ const ( _SHARED_SIZE = 510 ) -type vfsLockState uint32 +type ( + vfsLockState uint32 + xErrorCode = ExtendedErrorCode +) type vfsLocker interface { LockState() vfsLockState - LockShared() uint32 // UNLOCKED -> SHARED - LockReserved() uint32 // SHARED -> RESERVED - LockPending() uint32 // SHARED|RESERVED -> PENDING - LockExclusive() uint32 // PENDING -> EXCLUSIVE - DowngradeLock() uint32 // SHARED <- EXCLUSIVE|PENDING|RESERVED - Unlock() uint32 // UNLOCKED <- EXCLUSIVE|PENDING|RESERVED|SHARED + LockShared() xErrorCode // UNLOCKED -> SHARED + LockReserved() xErrorCode // SHARED -> RESERVED + LockPending() xErrorCode // SHARED|RESERVED -> PENDING + LockExclusive() xErrorCode // PENDING -> EXCLUSIVE + DowngradeLock() xErrorCode // SHARED <- EXCLUSIVE|PENDING|RESERVED + Unlock() xErrorCode // UNLOCKED <- EXCLUSIVE|PENDING|RESERVED|SHARED - CheckReservedLock() (bool, uint32) + CheckReservedLock() (bool, xErrorCode) } func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockState) uint32 { diff --git a/vfs_lock_noop.go b/vfs_lock_noop.go index 22f04e3..cae644b 100644 --- a/vfs_lock_noop.go +++ b/vfs_lock_noop.go @@ -1,7 +1,5 @@ package sqlite3 -const assert = true - type vfsNoopLocker struct { state vfsLockState } @@ -12,7 +10,7 @@ func (l *vfsNoopLocker) LockState() vfsLockState { return l.state } -func (l *vfsNoopLocker) LockShared() uint32 { +func (l *vfsNoopLocker) LockShared() xErrorCode { if assert && !(l.state == _NO_LOCK) { panic(assertErr + " [wz9dcw]") } @@ -20,7 +18,7 @@ func (l *vfsNoopLocker) LockShared() uint32 { return _OK } -func (l *vfsNoopLocker) LockReserved() uint32 { +func (l *vfsNoopLocker) LockReserved() xErrorCode { if assert && !(l.state == _SHARED_LOCK) { panic(assertErr + " [m9hcil]") } @@ -28,7 +26,7 @@ func (l *vfsNoopLocker) LockReserved() uint32 { return _OK } -func (l *vfsNoopLocker) LockPending() uint32 { +func (l *vfsNoopLocker) LockPending() xErrorCode { if assert && !(l.state == _SHARED_LOCK || l.state == _RESERVED_LOCK) { panic(assertErr + " [wx8nk2]") } @@ -36,7 +34,7 @@ func (l *vfsNoopLocker) LockPending() uint32 { return _OK } -func (l *vfsNoopLocker) LockExclusive() uint32 { +func (l *vfsNoopLocker) LockExclusive() xErrorCode { if assert && !(l.state == _PENDING_LOCK) { panic(assertErr + " [84nbax]") } @@ -44,7 +42,7 @@ func (l *vfsNoopLocker) LockExclusive() uint32 { return _OK } -func (l *vfsNoopLocker) DowngradeLock() uint32 { +func (l *vfsNoopLocker) DowngradeLock() xErrorCode { if assert && !(l.state > _SHARED_LOCK) { panic(assertErr + " [je31i3]") } @@ -52,7 +50,7 @@ func (l *vfsNoopLocker) DowngradeLock() uint32 { return _OK } -func (l *vfsNoopLocker) Unlock() uint32 { +func (l *vfsNoopLocker) Unlock() xErrorCode { if assert && !(l.state > _NO_LOCK) { panic(assertErr + " [m6e9w5]") } @@ -60,7 +58,7 @@ func (l *vfsNoopLocker) Unlock() uint32 { return _OK } -func (l *vfsNoopLocker) CheckReservedLock() (bool, uint32) { +func (l *vfsNoopLocker) CheckReservedLock() (bool, xErrorCode) { if l.state >= _RESERVED_LOCK { return true, _OK } diff --git a/vfs_unix.go b/vfs_unix.go index 7cff772..ae0d1d0 100644 --- a/vfs_unix.go +++ b/vfs_unix.go @@ -2,8 +2,146 @@ package sqlite3 -import "os" +import ( + "os" + "syscall" +) func deleteOnClose(f *os.File) { _ = os.Remove(f.Name()) } + +type vfsFileLocker struct { + *os.File + state vfsLockState +} + +func (l *vfsFileLocker) LockState() vfsLockState { + return l.state +} + +func (l *vfsFileLocker) LockShared() xErrorCode { + // A PENDING lock is needed before acquiring a SHARED lock. + if !l.fcntlSetLock(&syscall.Flock_t{ + Type: syscall.F_RDLCK, + Start: _PENDING_BYTE, + Len: 1, + }) { + return IOERR_LOCK + } + + // Acquire the SHARED lock. + if !l.fcntlSetLock(&syscall.Flock_t{ + Type: syscall.F_RDLCK, + Start: _SHARED_FIRST, + Len: _SHARED_SIZE, + }) { + return IOERR_LOCK + } + l.state = _SHARED_LOCK + + // Relese the PENDING lock. + if !l.fcntlSetLock(&syscall.Flock_t{ + Type: syscall.F_UNLCK, + Start: _PENDING_BYTE, + Len: 1, + }) { + return IOERR_UNLOCK + } + + return _OK +} + +func (l *vfsFileLocker) LockReserved() xErrorCode { + // Acquire the RESERVED lock. + if !l.fcntlSetLock(&syscall.Flock_t{ + Type: syscall.F_WRLCK, + Start: _RESERVED_BYTE, + Len: 1, + }) { + return IOERR_LOCK + } + l.state = _RESERVED_LOCK + return _OK +} + +func (l *vfsFileLocker) LockPending() xErrorCode { + // Acquire the PENDING lock. + if !l.fcntlSetLock(&syscall.Flock_t{ + Type: syscall.F_WRLCK, + Start: _PENDING_BYTE, + Len: 1, + }) { + return IOERR_LOCK + } + l.state = _PENDING_LOCK + return _OK +} + +func (l *vfsFileLocker) LockExclusive() xErrorCode { + // Acquire the EXCLUSIVE lock. + if !l.fcntlSetLock(&syscall.Flock_t{ + Type: syscall.F_WRLCK, + Start: _SHARED_FIRST, + Len: _SHARED_SIZE, + }) { + return IOERR_LOCK + } + l.state = _EXCLUSIVE_LOCK + return _OK +} + +func (l *vfsFileLocker) DowngradeLock() xErrorCode { + // Downgrade to a SHARED lock. + if !l.fcntlSetLock(&syscall.Flock_t{ + Type: syscall.F_RDLCK, + Start: _SHARED_FIRST, + Len: _SHARED_SIZE, + }) { + return IOERR_RDLOCK + } + l.state = _SHARED_LOCK + + // Release the PENDING and RESERVED locks. + if !l.fcntlSetLock(&syscall.Flock_t{ + Type: syscall.F_UNLCK, + Start: _PENDING_BYTE, + Len: 2, + }) { + return IOERR_UNLOCK + } + return _OK +} + +func (l *vfsFileLocker) Unlock() xErrorCode { + // Release all locks. + if !l.fcntlSetLock(&syscall.Flock_t{ + Type: syscall.F_UNLCK, + }) { + return IOERR_UNLOCK + } + l.state = _NO_LOCK + return _OK +} + +func (l *vfsFileLocker) CheckReservedLock() (bool, xErrorCode) { + if l.state >= _RESERVED_LOCK { + return true, _OK + } + // Test all write locks. + lock := syscall.Flock_t{ + Type: syscall.F_RDLCK, + } + if !l.fcntlGetLock(&lock) { + return false, IOERR_CHECKRESERVEDLOCK + } + return lock.Type == syscall.F_UNLCK, _OK +} + +func (l *vfsFileLocker) fcntlGetLock(lock *syscall.Flock_t) bool { + return syscall.FcntlFlock(l.Fd(), syscall.F_GETLK, lock) == nil +} + +func (l *vfsFileLocker) fcntlSetLock(lock *syscall.Flock_t) bool { + return syscall.FcntlFlock(l.Fd(), syscall.F_SETLK, lock) == nil +} diff --git a/vfs_windows.go b/vfs_windows.go index ca5d3f4..e01350a 100644 --- a/vfs_windows.go +++ b/vfs_windows.go @@ -1,3 +1,5 @@ package sqlite3 func deleteOnClose(f *os.File) {} + +type vfsFileLocker = vfsNoopLocker