mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-12 05:59:14 +00:00
Improve error reporting. (#327)
This commit is contained in:
2
.github/workflows/libc.yml
vendored
2
.github/workflows/libc.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-24.04, ubuntu-24.04-arm, macos-13, macos-15]
|
os: [ubuntu-24.04, ubuntu-24.04-arm, macos-15, macos-15-intel]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -224,7 +224,7 @@ jobs:
|
|||||||
run: go test -v ./...
|
run: go test -v ./...
|
||||||
|
|
||||||
test-macintel:
|
test-macintel:
|
||||||
runs-on: macos-13
|
runs-on: macos-15-intel
|
||||||
needs: test
|
needs: test
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
4
error.go
4
error.go
@@ -40,6 +40,10 @@ func (e *Error) Error() string {
|
|||||||
b.WriteString(": ")
|
b.WriteString(": ")
|
||||||
b.WriteString(e.msg)
|
b.WriteString(e.msg)
|
||||||
}
|
}
|
||||||
|
if e.sys != nil {
|
||||||
|
b.WriteString(": ")
|
||||||
|
b.WriteString(e.sys.Error())
|
||||||
|
}
|
||||||
|
|
||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ func (sqlt *sqlite) error(rc res_t, handle ptr_t, sql ...string) error {
|
|||||||
msg = strings.TrimPrefix(msg, "sqlite3: ")
|
msg = strings.TrimPrefix(msg, "sqlite3: ")
|
||||||
msg = strings.TrimPrefix(msg, util.ErrorCodeString(rc)[len("sqlite3: "):])
|
msg = strings.TrimPrefix(msg, util.ErrorCodeString(rc)[len("sqlite3: "):])
|
||||||
msg = strings.TrimPrefix(msg, ": ")
|
msg = strings.TrimPrefix(msg, ": ")
|
||||||
if msg == "not an error" {
|
if msg == "" || msg == "not an error" {
|
||||||
msg = ""
|
msg = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import (
|
|||||||
|
|
||||||
// A VFS defines the interface between the SQLite core and the underlying operating system.
|
// A VFS defines the interface between the SQLite core and the underlying operating system.
|
||||||
//
|
//
|
||||||
// Use sqlite3.ErrorCode or sqlite3.ExtendedErrorCode to return specific error codes to SQLite.
|
// Use [SystemError], sqlite3.ErrorCode, or sqlite3.ExtendedErrorCode
|
||||||
|
// to return specific error codes to SQLite.
|
||||||
//
|
//
|
||||||
// https://sqlite.org/c3ref/vfs.html
|
// https://sqlite.org/c3ref/vfs.html
|
||||||
type VFS interface {
|
type VFS interface {
|
||||||
@@ -31,8 +32,9 @@ type VFSFilename interface {
|
|||||||
|
|
||||||
// A File represents an open file in the OS interface layer.
|
// A File represents an open file in the OS interface layer.
|
||||||
//
|
//
|
||||||
// Use sqlite3.ErrorCode or sqlite3.ExtendedErrorCode to return specific error codes to SQLite.
|
// Use [SystemError], sqlite3.ErrorCode, or sqlite3.ExtendedErrorCode
|
||||||
// In particular, sqlite3.BUSY is necessary to correctly implement lock methods.
|
// to return specific error codes to SQLite.
|
||||||
|
// In particular, sqlite3.BUSY is needed to correctly implement lock methods.
|
||||||
//
|
//
|
||||||
// https://sqlite.org/c3ref/io_methods.html
|
// https://sqlite.org/c3ref/io_methods.html
|
||||||
type File interface {
|
type File interface {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func (e _ErrorCode) Error() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_OK _ErrorCode = util.OK
|
_OK = util.OK
|
||||||
_ERROR _ErrorCode = util.ERROR
|
_ERROR _ErrorCode = util.ERROR
|
||||||
_PERM _ErrorCode = util.PERM
|
_PERM _ErrorCode = util.PERM
|
||||||
_BUSY _ErrorCode = util.BUSY
|
_BUSY _ErrorCode = util.BUSY
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ func (vfsOS) Delete(path string, syncDir bool) error {
|
|||||||
if isUnix && syncDir {
|
if isUnix && syncDir {
|
||||||
f, err := os.Open(filepath.Dir(path))
|
f, err := os.Open(filepath.Dir(path))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
err = osSync(f, 0, SYNC_FULL)
|
err = osSync(f, 0, SYNC_FULL)
|
||||||
|
|||||||
20
vfs/lock.go
20
vfs/lock.go
@@ -51,8 +51,8 @@ func (f *vfsFile) Lock(lock LockLevel) error {
|
|||||||
if f.lock != LOCK_NONE {
|
if f.lock != LOCK_NONE {
|
||||||
panic(util.AssertErr())
|
panic(util.AssertErr())
|
||||||
}
|
}
|
||||||
if rc := osGetSharedLock(f.File); rc != _OK {
|
if err := osGetSharedLock(f.File); err != nil {
|
||||||
return rc
|
return err
|
||||||
}
|
}
|
||||||
f.lock = LOCK_SHARED
|
f.lock = LOCK_SHARED
|
||||||
return nil
|
return nil
|
||||||
@@ -62,8 +62,8 @@ func (f *vfsFile) Lock(lock LockLevel) error {
|
|||||||
if f.lock != LOCK_SHARED {
|
if f.lock != LOCK_SHARED {
|
||||||
panic(util.AssertErr())
|
panic(util.AssertErr())
|
||||||
}
|
}
|
||||||
if rc := osGetReservedLock(f.File); rc != _OK {
|
if err := osGetReservedLock(f.File); err != nil {
|
||||||
return rc
|
return err
|
||||||
}
|
}
|
||||||
f.lock = LOCK_RESERVED
|
f.lock = LOCK_RESERVED
|
||||||
return nil
|
return nil
|
||||||
@@ -73,8 +73,8 @@ func (f *vfsFile) Lock(lock LockLevel) error {
|
|||||||
if f.lock <= LOCK_NONE || f.lock >= LOCK_EXCLUSIVE {
|
if f.lock <= LOCK_NONE || f.lock >= LOCK_EXCLUSIVE {
|
||||||
panic(util.AssertErr())
|
panic(util.AssertErr())
|
||||||
}
|
}
|
||||||
if rc := osGetExclusiveLock(f.File, &f.lock); rc != _OK {
|
if err := osGetExclusiveLock(f.File, &f.lock); err != nil {
|
||||||
return rc
|
return err
|
||||||
}
|
}
|
||||||
f.lock = LOCK_EXCLUSIVE
|
f.lock = LOCK_EXCLUSIVE
|
||||||
return nil
|
return nil
|
||||||
@@ -101,14 +101,14 @@ func (f *vfsFile) Unlock(lock LockLevel) error {
|
|||||||
|
|
||||||
switch lock {
|
switch lock {
|
||||||
case LOCK_SHARED:
|
case LOCK_SHARED:
|
||||||
rc := osDowngradeLock(f.File, f.lock)
|
err := osDowngradeLock(f.File, f.lock)
|
||||||
f.lock = LOCK_SHARED
|
f.lock = LOCK_SHARED
|
||||||
return rc
|
return err
|
||||||
|
|
||||||
case LOCK_NONE:
|
case LOCK_NONE:
|
||||||
rc := osReleaseLock(f.File, f.lock)
|
err := osReleaseLock(f.File, f.lock)
|
||||||
f.lock = LOCK_NONE
|
f.lock = LOCK_NONE
|
||||||
return rc
|
return err
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic(util.AssertErr())
|
panic(util.AssertErr())
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ import (
|
|||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
func osGetSharedLock(file *os.File) _ErrorCode {
|
func osGetSharedLock(file *os.File) error {
|
||||||
return osFlock(file, unix.LOCK_SH|unix.LOCK_NB, _IOERR_RDLOCK)
|
return osFlock(file, unix.LOCK_SH|unix.LOCK_NB, _IOERR_RDLOCK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osGetReservedLock(file *os.File) _ErrorCode {
|
func osGetReservedLock(file *os.File) error {
|
||||||
rc := osFlock(file, unix.LOCK_EX|unix.LOCK_NB, _IOERR_LOCK)
|
err := osFlock(file, unix.LOCK_EX|unix.LOCK_NB, _IOERR_LOCK)
|
||||||
if rc == _BUSY {
|
if err == _BUSY {
|
||||||
// The documentation states that a lock is upgraded by
|
// The documentation states that a lock is upgraded by
|
||||||
// releasing the previous lock, then acquiring the new lock.
|
// releasing the previous lock, then acquiring the new lock.
|
||||||
// Going over the source code of various BSDs, though,
|
// Going over the source code of various BSDs, though,
|
||||||
@@ -26,19 +26,19 @@ func osGetReservedLock(file *os.File) _ErrorCode {
|
|||||||
// and invoke the busy handler if appropriate.
|
// and invoke the busy handler if appropriate.
|
||||||
return _BUSY_SNAPSHOT
|
return _BUSY_SNAPSHOT
|
||||||
}
|
}
|
||||||
return rc
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode {
|
func osGetExclusiveLock(file *os.File, state *LockLevel) error {
|
||||||
if *state >= LOCK_RESERVED {
|
if *state >= LOCK_RESERVED {
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
return osGetReservedLock(file)
|
return osGetReservedLock(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osDowngradeLock(file *os.File, _ LockLevel) _ErrorCode {
|
func osDowngradeLock(file *os.File, _ LockLevel) error {
|
||||||
rc := osFlock(file, unix.LOCK_SH|unix.LOCK_NB, _IOERR_RDLOCK)
|
err := osFlock(file, unix.LOCK_SH|unix.LOCK_NB, _IOERR_RDLOCK)
|
||||||
if rc == _BUSY {
|
if err == _BUSY {
|
||||||
// The documentation states that a lock is downgraded by
|
// The documentation states that a lock is downgraded by
|
||||||
// releasing the previous lock then acquiring the new lock.
|
// releasing the previous lock then acquiring the new lock.
|
||||||
// Going over the source code of various BSDs, though,
|
// Going over the source code of various BSDs, though,
|
||||||
@@ -46,44 +46,44 @@ func osDowngradeLock(file *os.File, _ LockLevel) _ErrorCode {
|
|||||||
// Return IOERR_RDLOCK, as BUSY would cause an assert to fail.
|
// Return IOERR_RDLOCK, as BUSY would cause an assert to fail.
|
||||||
return _IOERR_RDLOCK
|
return _IOERR_RDLOCK
|
||||||
}
|
}
|
||||||
return _OK
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func osReleaseLock(file *os.File, _ LockLevel) _ErrorCode {
|
func osReleaseLock(file *os.File, _ LockLevel) error {
|
||||||
for {
|
for {
|
||||||
err := unix.Flock(int(file.Fd()), unix.LOCK_UN)
|
err := unix.Flock(int(file.Fd()), unix.LOCK_UN)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
if err != unix.EINTR {
|
if err != unix.EINTR {
|
||||||
return _IOERR_UNLOCK
|
return sysError{err, _IOERR_UNLOCK}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func osCheckReservedLock(file *os.File) (bool, _ErrorCode) {
|
func osCheckReservedLock(file *os.File) (bool, error) {
|
||||||
// Test the RESERVED lock with fcntl(F_GETLK).
|
// Test the RESERVED lock with fcntl(F_GETLK).
|
||||||
// This only works on systems where fcntl and flock are compatible.
|
// This only works on systems where fcntl and flock are compatible.
|
||||||
// However, SQLite only calls this while holding a shared lock,
|
// However, SQLite only calls this while holding a shared lock,
|
||||||
// so the difference is immaterial.
|
// so the difference is immaterial.
|
||||||
lock, rc := osTestLock(file, _RESERVED_BYTE, 1)
|
lock, err := osTestLock(file, _RESERVED_BYTE, 1, _IOERR_CHECKRESERVEDLOCK)
|
||||||
return lock == unix.F_WRLCK, rc
|
return lock == unix.F_WRLCK, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func osFlock(file *os.File, how int, def _ErrorCode) _ErrorCode {
|
func osFlock(file *os.File, how int, def _ErrorCode) error {
|
||||||
err := unix.Flock(int(file.Fd()), how)
|
err := unix.Flock(int(file.Fd()), how)
|
||||||
return osLockErrorCode(err, def)
|
return osLockErrorCode(err, def)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osReadLock(file *os.File, start, len int64) _ErrorCode {
|
func osReadLock(file *os.File, start, len int64) error {
|
||||||
return osLock(file, unix.F_RDLCK, start, len, _IOERR_RDLOCK)
|
return osLock(file, unix.F_RDLCK, start, len, _IOERR_RDLOCK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osWriteLock(file *os.File, start, len int64) _ErrorCode {
|
func osWriteLock(file *os.File, start, len int64) error {
|
||||||
return osLock(file, unix.F_WRLCK, start, len, _IOERR_LOCK)
|
return osLock(file, unix.F_WRLCK, start, len, _IOERR_LOCK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osLock(file *os.File, typ int16, start, len int64, def _ErrorCode) _ErrorCode {
|
func osLock(file *os.File, typ int16, start, len int64, def _ErrorCode) error {
|
||||||
err := unix.FcntlFlock(file.Fd(), unix.F_SETLK, &unix.Flock_t{
|
err := unix.FcntlFlock(file.Fd(), unix.F_SETLK, &unix.Flock_t{
|
||||||
Type: typ,
|
Type: typ,
|
||||||
Start: start,
|
Start: start,
|
||||||
@@ -92,7 +92,7 @@ func osLock(file *os.File, typ int16, start, len int64, def _ErrorCode) _ErrorCo
|
|||||||
return osLockErrorCode(err, def)
|
return osLockErrorCode(err, def)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osUnlock(file *os.File, start, len int64) _ErrorCode {
|
func osUnlock(file *os.File, start, len int64) error {
|
||||||
lock := unix.Flock_t{
|
lock := unix.Flock_t{
|
||||||
Type: unix.F_UNLCK,
|
Type: unix.F_UNLCK,
|
||||||
Start: start,
|
Start: start,
|
||||||
@@ -101,10 +101,10 @@ func osUnlock(file *os.File, start, len int64) _ErrorCode {
|
|||||||
for {
|
for {
|
||||||
err := unix.FcntlFlock(file.Fd(), unix.F_SETLK, &lock)
|
err := unix.FcntlFlock(file.Fd(), unix.F_SETLK, &lock)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
if err != unix.EINTR {
|
if err != unix.EINTR {
|
||||||
return _IOERR_UNLOCK
|
return sysError{err, _IOERR_UNLOCK}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,15 +75,15 @@ func osAllocate(file *os.File, size int64) error {
|
|||||||
return file.Truncate(size)
|
return file.Truncate(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osReadLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode {
|
func osReadLock(file *os.File, start, len int64, timeout time.Duration) error {
|
||||||
return osLock(file, unix.F_RDLCK, start, len, timeout, _IOERR_RDLOCK)
|
return osLock(file, unix.F_RDLCK, start, len, timeout, _IOERR_RDLOCK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osWriteLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode {
|
func osWriteLock(file *os.File, start, len int64, timeout time.Duration) error {
|
||||||
return osLock(file, unix.F_WRLCK, start, len, timeout, _IOERR_LOCK)
|
return osLock(file, unix.F_WRLCK, start, len, timeout, _IOERR_LOCK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, def _ErrorCode) _ErrorCode {
|
func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, def _ErrorCode) error {
|
||||||
lock := &flocktimeout_t{fl: unix.Flock_t{
|
lock := &flocktimeout_t{fl: unix.Flock_t{
|
||||||
Type: typ,
|
Type: typ,
|
||||||
Start: start,
|
Start: start,
|
||||||
@@ -103,7 +103,7 @@ func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, d
|
|||||||
return osLockErrorCode(err, def)
|
return osLockErrorCode(err, def)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osUnlock(file *os.File, start, len int64) _ErrorCode {
|
func osUnlock(file *os.File, start, len int64) error {
|
||||||
lock := unix.Flock_t{
|
lock := unix.Flock_t{
|
||||||
Type: unix.F_UNLCK,
|
Type: unix.F_UNLCK,
|
||||||
Start: start,
|
Start: start,
|
||||||
@@ -112,10 +112,10 @@ func osUnlock(file *os.File, start, len int64) _ErrorCode {
|
|||||||
for {
|
for {
|
||||||
err := unix.FcntlFlock(file.Fd(), _F_OFD_SETLK, &lock)
|
err := unix.FcntlFlock(file.Fd(), _F_OFD_SETLK, &lock)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
if err != unix.EINTR {
|
if err != unix.EINTR {
|
||||||
return _IOERR_UNLOCK
|
return sysError{err, _IOERR_UNLOCK}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ type vfsDotLocker struct {
|
|||||||
reserved *os.File // +checklocks:vfsDotLocksMtx
|
reserved *os.File // +checklocks:vfsDotLocksMtx
|
||||||
}
|
}
|
||||||
|
|
||||||
func osGetSharedLock(file *os.File) _ErrorCode {
|
func osGetSharedLock(file *os.File) error {
|
||||||
vfsDotLocksMtx.Lock()
|
vfsDotLocksMtx.Lock()
|
||||||
defer vfsDotLocksMtx.Unlock()
|
defer vfsDotLocksMtx.Unlock()
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ func osGetSharedLock(file *os.File) _ErrorCode {
|
|||||||
if errors.Is(err, fs.ErrExist) {
|
if errors.Is(err, fs.ErrExist) {
|
||||||
return _BUSY // Another process has the lock.
|
return _BUSY // Another process has the lock.
|
||||||
}
|
}
|
||||||
return _IOERR_LOCK
|
return sysError{err, _IOERR_LOCK}
|
||||||
}
|
}
|
||||||
locker = &vfsDotLocker{}
|
locker = &vfsDotLocker{}
|
||||||
vfsDotLocks[name] = locker
|
vfsDotLocks[name] = locker
|
||||||
@@ -44,10 +44,10 @@ func osGetSharedLock(file *os.File) _ErrorCode {
|
|||||||
return _BUSY
|
return _BUSY
|
||||||
}
|
}
|
||||||
locker.shared++
|
locker.shared++
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func osGetReservedLock(file *os.File) _ErrorCode {
|
func osGetReservedLock(file *os.File) error {
|
||||||
vfsDotLocksMtx.Lock()
|
vfsDotLocksMtx.Lock()
|
||||||
defer vfsDotLocksMtx.Unlock()
|
defer vfsDotLocksMtx.Unlock()
|
||||||
|
|
||||||
@@ -61,10 +61,10 @@ func osGetReservedLock(file *os.File) _ErrorCode {
|
|||||||
return _BUSY
|
return _BUSY
|
||||||
}
|
}
|
||||||
locker.reserved = file
|
locker.reserved = file
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func osGetExclusiveLock(file *os.File, _ *LockLevel) _ErrorCode {
|
func osGetExclusiveLock(file *os.File, _ *LockLevel) error {
|
||||||
vfsDotLocksMtx.Lock()
|
vfsDotLocksMtx.Lock()
|
||||||
defer vfsDotLocksMtx.Unlock()
|
defer vfsDotLocksMtx.Unlock()
|
||||||
|
|
||||||
@@ -81,10 +81,10 @@ func osGetExclusiveLock(file *os.File, _ *LockLevel) _ErrorCode {
|
|||||||
if locker.shared > 1 {
|
if locker.shared > 1 {
|
||||||
return _BUSY
|
return _BUSY
|
||||||
}
|
}
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func osDowngradeLock(file *os.File, _ LockLevel) _ErrorCode {
|
func osDowngradeLock(file *os.File, _ LockLevel) error {
|
||||||
vfsDotLocksMtx.Lock()
|
vfsDotLocksMtx.Lock()
|
||||||
defer vfsDotLocksMtx.Unlock()
|
defer vfsDotLocksMtx.Unlock()
|
||||||
|
|
||||||
@@ -100,10 +100,10 @@ func osDowngradeLock(file *os.File, _ LockLevel) _ErrorCode {
|
|||||||
if locker.pending == file {
|
if locker.pending == file {
|
||||||
locker.pending = nil
|
locker.pending = nil
|
||||||
}
|
}
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func osReleaseLock(file *os.File, state LockLevel) _ErrorCode {
|
func osReleaseLock(file *os.File, state LockLevel) error {
|
||||||
vfsDotLocksMtx.Lock()
|
vfsDotLocksMtx.Lock()
|
||||||
defer vfsDotLocksMtx.Unlock()
|
defer vfsDotLocksMtx.Unlock()
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ func osReleaseLock(file *os.File, state LockLevel) _ErrorCode {
|
|||||||
|
|
||||||
if locker.shared == 1 {
|
if locker.shared == 1 {
|
||||||
if err := dotlk.Unlock(name + ".lock"); err != nil {
|
if err := dotlk.Unlock(name + ".lock"); err != nil {
|
||||||
return _IOERR_UNLOCK
|
return sysError{err, _IOERR_UNLOCK}
|
||||||
}
|
}
|
||||||
delete(vfsDotLocks, name)
|
delete(vfsDotLocks, name)
|
||||||
}
|
}
|
||||||
@@ -127,17 +127,14 @@ func osReleaseLock(file *os.File, state LockLevel) _ErrorCode {
|
|||||||
locker.pending = nil
|
locker.pending = nil
|
||||||
}
|
}
|
||||||
locker.shared--
|
locker.shared--
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func osCheckReservedLock(file *os.File) (bool, _ErrorCode) {
|
func osCheckReservedLock(file *os.File) (bool, error) {
|
||||||
vfsDotLocksMtx.Lock()
|
vfsDotLocksMtx.Lock()
|
||||||
defer vfsDotLocksMtx.Unlock()
|
defer vfsDotLocksMtx.Unlock()
|
||||||
|
|
||||||
name := file.Name()
|
name := file.Name()
|
||||||
locker := vfsDotLocks[name]
|
locker := vfsDotLocks[name]
|
||||||
if locker == nil {
|
return locker != nil && locker.reserved != nil, nil
|
||||||
return false, _OK
|
|
||||||
}
|
|
||||||
return locker.reserved != nil, _OK
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,15 +44,15 @@ func osAllocate(file *os.File, size int64) error {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func osReadLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode {
|
func osReadLock(file *os.File, start, len int64, timeout time.Duration) error {
|
||||||
return osLock(file, unix.F_RDLCK, start, len, timeout, _IOERR_RDLOCK)
|
return osLock(file, unix.F_RDLCK, start, len, timeout, _IOERR_RDLOCK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osWriteLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode {
|
func osWriteLock(file *os.File, start, len int64, timeout time.Duration) error {
|
||||||
return osLock(file, unix.F_WRLCK, start, len, timeout, _IOERR_LOCK)
|
return osLock(file, unix.F_WRLCK, start, len, timeout, _IOERR_LOCK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, def _ErrorCode) _ErrorCode {
|
func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, def _ErrorCode) error {
|
||||||
lock := unix.Flock_t{
|
lock := unix.Flock_t{
|
||||||
Type: typ,
|
Type: typ,
|
||||||
Start: start,
|
Start: start,
|
||||||
@@ -68,7 +68,7 @@ func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, d
|
|||||||
return osLockErrorCode(err, def)
|
return osLockErrorCode(err, def)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osUnlock(file *os.File, start, len int64) _ErrorCode {
|
func osUnlock(file *os.File, start, len int64) error {
|
||||||
lock := unix.Flock_t{
|
lock := unix.Flock_t{
|
||||||
Type: unix.F_UNLCK,
|
Type: unix.F_UNLCK,
|
||||||
Start: start,
|
Start: start,
|
||||||
@@ -77,10 +77,10 @@ func osUnlock(file *os.File, start, len int64) _ErrorCode {
|
|||||||
for {
|
for {
|
||||||
err := unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &lock)
|
err := unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &lock)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
if err != unix.EINTR {
|
if err != unix.EINTR {
|
||||||
return _IOERR_UNLOCK
|
return sysError{err, _IOERR_UNLOCK}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,25 +9,25 @@ import (
|
|||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
func osGetSharedLock(file *os.File) _ErrorCode {
|
func osGetSharedLock(file *os.File) error {
|
||||||
// Test the PENDING lock before acquiring a new SHARED lock.
|
// Test the PENDING lock before acquiring a new SHARED lock.
|
||||||
if lock, _ := osTestLock(file, _PENDING_BYTE, 1); lock == unix.F_WRLCK {
|
if lock, _ := osTestLock(file, _PENDING_BYTE, 1, _IOERR); lock == unix.F_WRLCK {
|
||||||
return _BUSY
|
return _BUSY
|
||||||
}
|
}
|
||||||
// Acquire the SHARED lock.
|
// Acquire the SHARED lock.
|
||||||
return osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0)
|
return osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osGetReservedLock(file *os.File) _ErrorCode {
|
func osGetReservedLock(file *os.File) error {
|
||||||
// Acquire the RESERVED lock.
|
// Acquire the RESERVED lock.
|
||||||
return osWriteLock(file, _RESERVED_BYTE, 1, 0)
|
return osWriteLock(file, _RESERVED_BYTE, 1, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode {
|
func osGetExclusiveLock(file *os.File, state *LockLevel) error {
|
||||||
if *state == LOCK_RESERVED {
|
if *state == LOCK_RESERVED {
|
||||||
// A PENDING lock is needed before acquiring an EXCLUSIVE lock.
|
// A PENDING lock is needed before acquiring an EXCLUSIVE lock.
|
||||||
if rc := osWriteLock(file, _PENDING_BYTE, 1, -1); rc != _OK {
|
if err := osWriteLock(file, _PENDING_BYTE, 1, -1); err != nil {
|
||||||
return rc
|
return err
|
||||||
}
|
}
|
||||||
*state = LOCK_PENDING
|
*state = LOCK_PENDING
|
||||||
}
|
}
|
||||||
@@ -35,10 +35,10 @@ func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode {
|
|||||||
return osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, time.Millisecond)
|
return osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode {
|
func osDowngradeLock(file *os.File, state LockLevel) error {
|
||||||
if state >= LOCK_EXCLUSIVE {
|
if state >= LOCK_EXCLUSIVE {
|
||||||
// Downgrade to a SHARED lock.
|
// Downgrade to a SHARED lock.
|
||||||
if rc := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); rc != _OK {
|
if err := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); err != nil {
|
||||||
// notest // this should never happen
|
// notest // this should never happen
|
||||||
return _IOERR_RDLOCK
|
return _IOERR_RDLOCK
|
||||||
}
|
}
|
||||||
@@ -47,13 +47,13 @@ func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode {
|
|||||||
return osUnlock(file, _PENDING_BYTE, 2)
|
return osUnlock(file, _PENDING_BYTE, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osReleaseLock(file *os.File, _ LockLevel) _ErrorCode {
|
func osReleaseLock(file *os.File, _ LockLevel) error {
|
||||||
// Release all locks.
|
// Release all locks.
|
||||||
return osUnlock(file, 0, 0)
|
return osUnlock(file, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osCheckReservedLock(file *os.File) (bool, _ErrorCode) {
|
func osCheckReservedLock(file *os.File) (bool, error) {
|
||||||
// Test the RESERVED lock.
|
// Test the RESERVED lock.
|
||||||
lock, rc := osTestLock(file, _RESERVED_BYTE, 1)
|
lock, err := osTestLock(file, _RESERVED_BYTE, 1, _IOERR_CHECKRESERVEDLOCK)
|
||||||
return lock == unix.F_WRLCK, rc
|
return lock == unix.F_WRLCK, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ func osSetMode(file *os.File, modeof string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func osTestLock(file *os.File, start, len int64) (int16, _ErrorCode) {
|
func osTestLock(file *os.File, start, len int64, def _ErrorCode) (int16, error) {
|
||||||
lock := unix.Flock_t{
|
lock := unix.Flock_t{
|
||||||
Type: unix.F_WRLCK,
|
Type: unix.F_WRLCK,
|
||||||
Start: start,
|
Start: start,
|
||||||
@@ -68,17 +68,17 @@ func osTestLock(file *os.File, start, len int64) (int16, _ErrorCode) {
|
|||||||
for {
|
for {
|
||||||
err := unix.FcntlFlock(file.Fd(), unix.F_GETLK, &lock)
|
err := unix.FcntlFlock(file.Fd(), unix.F_GETLK, &lock)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return lock.Type, _OK
|
return lock.Type, nil
|
||||||
}
|
}
|
||||||
if err != unix.EINTR {
|
if err != unix.EINTR {
|
||||||
return 0, _IOERR_CHECKRESERVEDLOCK
|
return 0, sysError{err, def}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func osLockErrorCode(err error, def _ErrorCode) _ErrorCode {
|
func osLockErrorCode(err error, def _ErrorCode) error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
if errno, ok := err.(unix.Errno); ok {
|
if errno, ok := err.(unix.Errno); ok {
|
||||||
switch errno {
|
switch errno {
|
||||||
@@ -92,12 +92,12 @@ func osLockErrorCode(err error, def _ErrorCode) _ErrorCode {
|
|||||||
unix.ETIMEDOUT:
|
unix.ETIMEDOUT:
|
||||||
return _BUSY
|
return _BUSY
|
||||||
case unix.EPERM:
|
case unix.EPERM:
|
||||||
return _PERM
|
return sysError{err, _PERM}
|
||||||
}
|
}
|
||||||
// notest // usually EWOULDBLOCK == EAGAIN
|
// notest // usually EWOULDBLOCK == EAGAIN
|
||||||
if errno == unix.EWOULDBLOCK && unix.EWOULDBLOCK != unix.EAGAIN {
|
if errno == unix.EWOULDBLOCK && unix.EWOULDBLOCK != unix.EAGAIN {
|
||||||
return _BUSY
|
return _BUSY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return def
|
return sysError{err, def}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,25 +26,25 @@ func osWriteAt(file *os.File, p []byte, off int64) (int, error) {
|
|||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func osGetSharedLock(file *os.File) _ErrorCode {
|
func osGetSharedLock(file *os.File) error {
|
||||||
// Acquire the PENDING lock temporarily before acquiring a new SHARED lock.
|
// Acquire the PENDING lock temporarily before acquiring a new SHARED lock.
|
||||||
rc := osReadLock(file, _PENDING_BYTE, 1, 0)
|
err := osReadLock(file, _PENDING_BYTE, 1, 0)
|
||||||
if rc == _OK {
|
if err == nil {
|
||||||
// Acquire the SHARED lock.
|
// Acquire the SHARED lock.
|
||||||
rc = osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0)
|
err = osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0)
|
||||||
|
|
||||||
// Release the PENDING lock.
|
// Release the PENDING lock.
|
||||||
osUnlock(file, _PENDING_BYTE, 1)
|
osUnlock(file, _PENDING_BYTE, 1)
|
||||||
}
|
}
|
||||||
return rc
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func osGetReservedLock(file *os.File) _ErrorCode {
|
func osGetReservedLock(file *os.File) error {
|
||||||
// Acquire the RESERVED lock.
|
// Acquire the RESERVED lock.
|
||||||
return osWriteLock(file, _RESERVED_BYTE, 1, 0)
|
return osWriteLock(file, _RESERVED_BYTE, 1, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode {
|
func osGetExclusiveLock(file *os.File, state *LockLevel) error {
|
||||||
// A PENDING lock is needed before releasing the SHARED lock.
|
// A PENDING lock is needed before releasing the SHARED lock.
|
||||||
if *state < LOCK_PENDING {
|
if *state < LOCK_PENDING {
|
||||||
// If we were RESERVED, we can block indefinitely.
|
// If we were RESERVED, we can block indefinitely.
|
||||||
@@ -52,8 +52,8 @@ func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode {
|
|||||||
if *state == LOCK_RESERVED {
|
if *state == LOCK_RESERVED {
|
||||||
timeout = -1
|
timeout = -1
|
||||||
}
|
}
|
||||||
if rc := osWriteLock(file, _PENDING_BYTE, 1, timeout); rc != _OK {
|
if err := osWriteLock(file, _PENDING_BYTE, 1, timeout); err != nil {
|
||||||
return rc
|
return err
|
||||||
}
|
}
|
||||||
*state = LOCK_PENDING
|
*state = LOCK_PENDING
|
||||||
}
|
}
|
||||||
@@ -63,25 +63,25 @@ func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode {
|
|||||||
|
|
||||||
// Acquire the EXCLUSIVE lock.
|
// Acquire the EXCLUSIVE lock.
|
||||||
// Can't wait here, because the file is not OVERLAPPED.
|
// Can't wait here, because the file is not OVERLAPPED.
|
||||||
rc := osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, 0)
|
err := osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, 0)
|
||||||
|
|
||||||
if rc != _OK {
|
if err != nil {
|
||||||
// Reacquire the SHARED lock.
|
// Reacquire the SHARED lock.
|
||||||
if rc := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); rc != _OK {
|
if err := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); err != nil {
|
||||||
// notest // this should never happen
|
// notest // this should never happen
|
||||||
return _IOERR_RDLOCK
|
return _IOERR_RDLOCK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rc
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode {
|
func osDowngradeLock(file *os.File, state LockLevel) error {
|
||||||
if state >= LOCK_EXCLUSIVE {
|
if state >= LOCK_EXCLUSIVE {
|
||||||
// Release the EXCLUSIVE lock while holding the PENDING lock.
|
// Release the EXCLUSIVE lock while holding the PENDING lock.
|
||||||
osUnlock(file, _SHARED_FIRST, _SHARED_SIZE)
|
osUnlock(file, _SHARED_FIRST, _SHARED_SIZE)
|
||||||
|
|
||||||
// Reacquire the SHARED lock.
|
// Reacquire the SHARED lock.
|
||||||
if rc := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); rc != _OK {
|
if err := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); err != nil {
|
||||||
// notest // this should never happen
|
// notest // this should never happen
|
||||||
return _IOERR_RDLOCK
|
return _IOERR_RDLOCK
|
||||||
}
|
}
|
||||||
@@ -94,10 +94,10 @@ func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode {
|
|||||||
if state >= LOCK_PENDING {
|
if state >= LOCK_PENDING {
|
||||||
osUnlock(file, _PENDING_BYTE, 1)
|
osUnlock(file, _PENDING_BYTE, 1)
|
||||||
}
|
}
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func osReleaseLock(file *os.File, state LockLevel) _ErrorCode {
|
func osReleaseLock(file *os.File, state LockLevel) error {
|
||||||
// Release all locks, PENDING must be last.
|
// Release all locks, PENDING must be last.
|
||||||
if state >= LOCK_RESERVED {
|
if state >= LOCK_RESERVED {
|
||||||
osUnlock(file, _RESERVED_BYTE, 1)
|
osUnlock(file, _RESERVED_BYTE, 1)
|
||||||
@@ -108,31 +108,32 @@ func osReleaseLock(file *os.File, state LockLevel) _ErrorCode {
|
|||||||
if state >= LOCK_PENDING {
|
if state >= LOCK_PENDING {
|
||||||
osUnlock(file, _PENDING_BYTE, 1)
|
osUnlock(file, _PENDING_BYTE, 1)
|
||||||
}
|
}
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func osCheckReservedLock(file *os.File) (bool, _ErrorCode) {
|
func osCheckReservedLock(file *os.File) (bool, error) {
|
||||||
// Test the RESERVED lock.
|
// Test the RESERVED lock.
|
||||||
rc := osLock(file, 0, _RESERVED_BYTE, 1, 0, _IOERR_CHECKRESERVEDLOCK)
|
err := osLock(file, 0, _RESERVED_BYTE, 1, 0, _IOERR_CHECKRESERVEDLOCK)
|
||||||
if rc == _BUSY {
|
if err == _BUSY {
|
||||||
return true, _OK
|
return true, nil
|
||||||
}
|
}
|
||||||
if rc == _OK {
|
if err == nil {
|
||||||
// Release the RESERVED lock.
|
// Release the RESERVED lock.
|
||||||
osUnlock(file, _RESERVED_BYTE, 1)
|
osUnlock(file, _RESERVED_BYTE, 1)
|
||||||
|
return false, nil
|
||||||
}
|
}
|
||||||
return false, rc
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func osReadLock(file *os.File, start, len uint32, timeout time.Duration) _ErrorCode {
|
func osReadLock(file *os.File, start, len uint32, timeout time.Duration) error {
|
||||||
return osLock(file, 0, start, len, timeout, _IOERR_RDLOCK)
|
return osLock(file, 0, start, len, timeout, _IOERR_RDLOCK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osWriteLock(file *os.File, start, len uint32, timeout time.Duration) _ErrorCode {
|
func osWriteLock(file *os.File, start, len uint32, timeout time.Duration) error {
|
||||||
return osLock(file, windows.LOCKFILE_EXCLUSIVE_LOCK, start, len, timeout, _IOERR_LOCK)
|
return osLock(file, windows.LOCKFILE_EXCLUSIVE_LOCK, start, len, timeout, _IOERR_LOCK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osLock(file *os.File, flags, start, len uint32, timeout time.Duration, def _ErrorCode) _ErrorCode {
|
func osLock(file *os.File, flags, start, len uint32, timeout time.Duration, def _ErrorCode) error {
|
||||||
var err error
|
var err error
|
||||||
switch {
|
switch {
|
||||||
default:
|
default:
|
||||||
@@ -143,16 +144,16 @@ func osLock(file *os.File, flags, start, len uint32, timeout time.Duration, def
|
|||||||
return osLockErrorCode(err, def)
|
return osLockErrorCode(err, def)
|
||||||
}
|
}
|
||||||
|
|
||||||
func osUnlock(file *os.File, start, len uint32) _ErrorCode {
|
func osUnlock(file *os.File, start, len uint32) error {
|
||||||
err := windows.UnlockFileEx(windows.Handle(file.Fd()),
|
err := windows.UnlockFileEx(windows.Handle(file.Fd()),
|
||||||
0, len, 0, &windows.Overlapped{Offset: start})
|
0, len, 0, &windows.Overlapped{Offset: start})
|
||||||
if err == windows.ERROR_NOT_LOCKED {
|
if err == windows.ERROR_NOT_LOCKED {
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return _IOERR_UNLOCK
|
return sysError{err, _IOERR_UNLOCK}
|
||||||
}
|
}
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func osLockEx(file *os.File, flags, start, len uint32) error {
|
func osLockEx(file *os.File, flags, start, len uint32) error {
|
||||||
@@ -160,9 +161,9 @@ func osLockEx(file *os.File, flags, start, len uint32) error {
|
|||||||
0, len, 0, &windows.Overlapped{Offset: start})
|
0, len, 0, &windows.Overlapped{Offset: start})
|
||||||
}
|
}
|
||||||
|
|
||||||
func osLockErrorCode(err error, def _ErrorCode) _ErrorCode {
|
func osLockErrorCode(err error, def _ErrorCode) error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
if errno, ok := err.(windows.Errno); ok {
|
if errno, ok := err.(windows.Errno); ok {
|
||||||
// https://devblogs.microsoft.com/oldnewthing/20140905-00/?p=63
|
// https://devblogs.microsoft.com/oldnewthing/20140905-00/?p=63
|
||||||
@@ -175,5 +176,5 @@ func osLockErrorCode(err error, def _ErrorCode) _ErrorCode {
|
|||||||
return _BUSY
|
return _BUSY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return def
|
return sysError{err, def}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
package vfs
|
package vfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"cmp"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
@@ -68,9 +69,9 @@ func (s *vfsShm) Close() error {
|
|||||||
panic(util.AssertErr())
|
panic(util.AssertErr())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *vfsShm) shmOpen() (rc _ErrorCode) {
|
func (s *vfsShm) shmOpen() (err error) {
|
||||||
if s.vfsShmParent != nil {
|
if s.vfsShmParent != nil {
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
vfsShmListMtx.Lock()
|
vfsShmListMtx.Lock()
|
||||||
@@ -80,7 +81,7 @@ func (s *vfsShm) shmOpen() (rc _ErrorCode) {
|
|||||||
// Closing it would release all POSIX locks on it.
|
// Closing it would release all POSIX locks on it.
|
||||||
fi, err := os.Stat(s.path)
|
fi, err := os.Stat(s.path)
|
||||||
if err != nil && !errors.Is(err, fs.ErrNotExist) {
|
if err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||||||
return _IOERR_FSTAT
|
return sysError{err, _IOERR_FSTAT}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find a shared file, increase the reference count.
|
// Find a shared file, increase the reference count.
|
||||||
@@ -88,7 +89,7 @@ func (s *vfsShm) shmOpen() (rc _ErrorCode) {
|
|||||||
if g != nil && os.SameFile(fi, g.info) {
|
if g != nil && os.SameFile(fi, g.info) {
|
||||||
s.vfsShmParent = g
|
s.vfsShmParent = g
|
||||||
g.refs++
|
g.refs++
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,34 +97,34 @@ func (s *vfsShm) shmOpen() (rc _ErrorCode) {
|
|||||||
f, err := os.OpenFile(s.path,
|
f, err := os.OpenFile(s.path,
|
||||||
os.O_RDWR|os.O_CREATE|_O_NOFOLLOW, 0666)
|
os.O_RDWR|os.O_CREATE|_O_NOFOLLOW, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return _CANTOPEN
|
return sysError{err, _CANTOPEN}
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if rc != _OK {
|
if err != nil {
|
||||||
f.Close()
|
f.Close()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Dead man's switch.
|
// Dead man's switch.
|
||||||
if lock, rc := osTestLock(f, _SHM_DMS, 1); rc != _OK {
|
if lock, err := osTestLock(f, _SHM_DMS, 1, _IOERR_LOCK); err != nil {
|
||||||
return _IOERR_LOCK
|
return err
|
||||||
} else if lock == unix.F_WRLCK {
|
} else if lock == unix.F_WRLCK {
|
||||||
return _BUSY
|
return _BUSY
|
||||||
} else if lock == unix.F_UNLCK {
|
} else if lock == unix.F_UNLCK {
|
||||||
if rc := osWriteLock(f, _SHM_DMS, 1); rc != _OK {
|
if err := osWriteLock(f, _SHM_DMS, 1); err != nil {
|
||||||
return rc
|
return err
|
||||||
}
|
}
|
||||||
if err := f.Truncate(0); err != nil {
|
if err := f.Truncate(0); err != nil {
|
||||||
return _IOERR_SHMOPEN
|
return sysError{err, _IOERR_SHMOPEN}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rc := osReadLock(f, _SHM_DMS, 1); rc != _OK {
|
if err := osReadLock(f, _SHM_DMS, 1); err != nil {
|
||||||
return rc
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fi, err = f.Stat()
|
fi, err = f.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return _IOERR_FSTAT
|
return sysError{err, _IOERR_FSTAT}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the new shared file.
|
// Add the new shared file.
|
||||||
@@ -134,11 +135,11 @@ func (s *vfsShm) shmOpen() (rc _ErrorCode) {
|
|||||||
for i, g := range vfsShmList {
|
for i, g := range vfsShmList {
|
||||||
if g == nil {
|
if g == nil {
|
||||||
vfsShmList[i] = s.vfsShmParent
|
vfsShmList[i] = s.vfsShmParent
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vfsShmList = append(vfsShmList, s.vfsShmParent)
|
vfsShmList = append(vfsShmList, s.vfsShmParent)
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (ptr_t, error) {
|
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (ptr_t, error) {
|
||||||
@@ -147,8 +148,8 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext
|
|||||||
return 0, _IOERR_SHMMAP
|
return 0, _IOERR_SHMMAP
|
||||||
}
|
}
|
||||||
|
|
||||||
if rc := s.shmOpen(); rc != _OK {
|
if err := s.shmOpen(); err != nil {
|
||||||
return 0, rc
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if file is big enough.
|
// Check if file is big enough.
|
||||||
@@ -182,9 +183,9 @@ func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) error {
|
|||||||
defer s.Unlock()
|
defer s.Unlock()
|
||||||
|
|
||||||
// Check if we can obtain/release locks locally.
|
// Check if we can obtain/release locks locally.
|
||||||
rc := s.shmMemLock(offset, n, flags)
|
err := s.shmMemLock(offset, n, flags)
|
||||||
if rc != _OK {
|
if err != nil {
|
||||||
return rc
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtain/release the appropriate file locks.
|
// Obtain/release the appropriate file locks.
|
||||||
@@ -196,36 +197,38 @@ func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) error {
|
|||||||
for i := begin; i < end; i++ {
|
for i := begin; i < end; i++ {
|
||||||
if s.vfsShmParent.lock[i] != 0 {
|
if s.vfsShmParent.lock[i] != 0 {
|
||||||
if i > begin {
|
if i > begin {
|
||||||
rc |= osUnlock(s.File, _SHM_BASE+int64(begin), int64(i-begin))
|
err = cmp.Or(err,
|
||||||
|
osUnlock(s.File, _SHM_BASE+int64(begin), int64(i-begin)))
|
||||||
}
|
}
|
||||||
begin = i + 1
|
begin = i + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if end > begin {
|
if end > begin {
|
||||||
rc |= osUnlock(s.File, _SHM_BASE+int64(begin), int64(end-begin))
|
err = cmp.Or(err,
|
||||||
|
osUnlock(s.File, _SHM_BASE+int64(begin), int64(end-begin)))
|
||||||
}
|
}
|
||||||
return rc
|
return err
|
||||||
case flags&_SHM_SHARED != 0:
|
case flags&_SHM_SHARED != 0:
|
||||||
// Acquiring a new shared lock on the file is only necessary
|
// Acquiring a new shared lock on the file is only necessary
|
||||||
// if there was a new shared lock in the range.
|
// if there was a new shared lock in the range.
|
||||||
for i := offset; i < offset+n; i++ {
|
for i := offset; i < offset+n; i++ {
|
||||||
if s.vfsShmParent.lock[i] == 1 {
|
if s.vfsShmParent.lock[i] == 1 {
|
||||||
rc = osReadLock(s.File, _SHM_BASE+int64(offset), int64(n))
|
err = osReadLock(s.File, _SHM_BASE+int64(offset), int64(n))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case flags&_SHM_EXCLUSIVE != 0:
|
case flags&_SHM_EXCLUSIVE != 0:
|
||||||
// Acquiring an exclusive lock on the file is always necessary.
|
// Acquiring an exclusive lock on the file is always necessary.
|
||||||
rc = osWriteLock(s.File, _SHM_BASE+int64(offset), int64(n))
|
err = osWriteLock(s.File, _SHM_BASE+int64(offset), int64(n))
|
||||||
default:
|
default:
|
||||||
panic(util.AssertErr())
|
panic(util.AssertErr())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release the local locks we had acquired.
|
if err != nil {
|
||||||
if rc != _OK {
|
// Release the local locks we had acquired.
|
||||||
s.shmMemLock(offset, n, flags^(_SHM_UNLOCK|_SHM_LOCK))
|
s.shmMemLock(offset, n, flags^(_SHM_UNLOCK|_SHM_LOCK))
|
||||||
}
|
}
|
||||||
return rc
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *vfsShm) shmUnmap(delete bool) {
|
func (s *vfsShm) shmUnmap(delete bool) {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ const (
|
|||||||
// https://sqlite.org/walformat.html#the_wal_index_file_format
|
// https://sqlite.org/walformat.html#the_wal_index_file_format
|
||||||
|
|
||||||
func (s *vfsShm) shmAcquire(errp *error) {
|
func (s *vfsShm) shmAcquire(errp *error) {
|
||||||
if errp != nil && *errp != _OK {
|
if errp != nil && *errp != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(s.ptrs) == 0 || shmEqual(s.shadow[0][:], s.shared[0][:]) {
|
if len(s.ptrs) == 0 || shmEqual(s.shadow[0][:], s.shared[0][:]) {
|
||||||
|
|||||||
@@ -66,9 +66,9 @@ func (s *vfsShm) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *vfsShm) shmOpen() _ErrorCode {
|
func (s *vfsShm) shmOpen() error {
|
||||||
if s.vfsShmParent != nil {
|
if s.vfsShmParent != nil {
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
vfsShmListMtx.Lock()
|
vfsShmListMtx.Lock()
|
||||||
@@ -78,7 +78,7 @@ func (s *vfsShm) shmOpen() _ErrorCode {
|
|||||||
if g, ok := vfsShmList[s.path]; ok {
|
if g, ok := vfsShmList[s.path]; ok {
|
||||||
s.vfsShmParent = g
|
s.vfsShmParent = g
|
||||||
g.refs++
|
g.refs++
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dead man's switch.
|
// Dead man's switch.
|
||||||
@@ -87,13 +87,13 @@ func (s *vfsShm) shmOpen() _ErrorCode {
|
|||||||
return _BUSY
|
return _BUSY
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return _IOERR_LOCK
|
return sysError{err, _IOERR_LOCK}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the new shared buffer.
|
// Add the new shared buffer.
|
||||||
s.vfsShmParent = &vfsShmParent{}
|
s.vfsShmParent = &vfsShmParent{}
|
||||||
vfsShmList[s.path] = s.vfsShmParent
|
vfsShmList[s.path] = s.vfsShmParent
|
||||||
return _OK
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (ptr_t, error) {
|
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (ptr_t, error) {
|
||||||
@@ -105,8 +105,8 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext
|
|||||||
s.free = mod.ExportedFunction("sqlite3_free")
|
s.free = mod.ExportedFunction("sqlite3_free")
|
||||||
s.alloc = mod.ExportedFunction("sqlite3_malloc64")
|
s.alloc = mod.ExportedFunction("sqlite3_malloc64")
|
||||||
}
|
}
|
||||||
if rc := s.shmOpen(); rc != _OK {
|
if err := s.shmOpen(); err != nil {
|
||||||
return 0, rc
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Lock()
|
s.Lock()
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ package vfs
|
|||||||
import "github.com/ncruces/go-sqlite3/internal/util"
|
import "github.com/ncruces/go-sqlite3/internal/util"
|
||||||
|
|
||||||
// +checklocks:s.Mutex
|
// +checklocks:s.Mutex
|
||||||
func (s *vfsShm) shmMemLock(offset, n int32, flags _ShmFlag) _ErrorCode {
|
func (s *vfsShm) shmMemLock(offset, n int32, flags _ShmFlag) error {
|
||||||
switch {
|
switch {
|
||||||
case flags&_SHM_UNLOCK != 0:
|
case flags&_SHM_UNLOCK != 0:
|
||||||
for i := offset; i < offset+n; i++ {
|
for i := offset; i < offset+n; i++ {
|
||||||
@@ -48,6 +48,5 @@ func (s *vfsShm) shmMemLock(offset, n int32, flags _ShmFlag) _ErrorCode {
|
|||||||
default:
|
default:
|
||||||
panic(util.AssertErr())
|
panic(util.AssertErr())
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
return _OK
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,10 @@ type vfsShm struct {
|
|||||||
|
|
||||||
var _ blockingSharedMemory = &vfsShm{}
|
var _ blockingSharedMemory = &vfsShm{}
|
||||||
|
|
||||||
func (s *vfsShm) shmOpen() _ErrorCode {
|
func (s *vfsShm) shmOpen() error {
|
||||||
|
if s.fileLock {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if s.File == nil {
|
if s.File == nil {
|
||||||
f, err := os.OpenFile(s.path,
|
f, err := os.OpenFile(s.path,
|
||||||
os.O_RDWR|os.O_CREATE|_O_NOFOLLOW, 0666)
|
os.O_RDWR|os.O_CREATE|_O_NOFOLLOW, 0666)
|
||||||
@@ -37,17 +40,15 @@ func (s *vfsShm) shmOpen() _ErrorCode {
|
|||||||
s.readOnly = true
|
s.readOnly = true
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return _CANTOPEN
|
return sysError{err, _CANTOPEN}
|
||||||
}
|
}
|
||||||
|
s.fileLock = false
|
||||||
s.File = f
|
s.File = f
|
||||||
}
|
}
|
||||||
if s.fileLock {
|
|
||||||
return _OK
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dead man's switch.
|
// Dead man's switch.
|
||||||
if lock, rc := osTestLock(s.File, _SHM_DMS, 1); rc != _OK {
|
if lock, err := osTestLock(s.File, _SHM_DMS, 1, _IOERR_LOCK); err != nil {
|
||||||
return _IOERR_LOCK
|
return err
|
||||||
} else if lock == unix.F_WRLCK {
|
} else if lock == unix.F_WRLCK {
|
||||||
return _BUSY
|
return _BUSY
|
||||||
} else if lock == unix.F_UNLCK {
|
} else if lock == unix.F_UNLCK {
|
||||||
@@ -61,16 +62,16 @@ func (s *vfsShm) shmOpen() _ErrorCode {
|
|||||||
// but only downgrade it to a shared lock.
|
// but only downgrade it to a shared lock.
|
||||||
// So no point in blocking here.
|
// So no point in blocking here.
|
||||||
// The call below to obtain the shared DMS lock may use a blocking lock.
|
// The call below to obtain the shared DMS lock may use a blocking lock.
|
||||||
if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc != _OK {
|
if err := osWriteLock(s.File, _SHM_DMS, 1, 0); err != nil {
|
||||||
return rc
|
return err
|
||||||
}
|
}
|
||||||
if err := s.Truncate(0); err != nil {
|
if err := s.Truncate(0); err != nil {
|
||||||
return _IOERR_SHMOPEN
|
return sysError{err, _IOERR_SHMOPEN}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rc := osReadLock(s.File, _SHM_DMS, 1, time.Millisecond)
|
err := osReadLock(s.File, _SHM_DMS, 1, time.Millisecond)
|
||||||
s.fileLock = rc == _OK
|
s.fileLock = err == nil
|
||||||
return rc
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (ptr_t, error) {
|
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (ptr_t, error) {
|
||||||
@@ -79,8 +80,8 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext
|
|||||||
return 0, _IOERR_SHMMAP
|
return 0, _IOERR_SHMMAP
|
||||||
}
|
}
|
||||||
|
|
||||||
if rc := s.shmOpen(); rc != _OK {
|
if err := s.shmOpen(); err != nil {
|
||||||
return 0, rc
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if file is big enough.
|
// Check if file is big enough.
|
||||||
@@ -170,6 +171,7 @@ func (s *vfsShm) shmUnmap(delete bool) {
|
|||||||
}
|
}
|
||||||
s.Close()
|
s.Close()
|
||||||
s.File = nil
|
s.File = nil
|
||||||
|
s.fileLock = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *vfsShm) shmBarrier() {
|
func (s *vfsShm) shmBarrier() {
|
||||||
|
|||||||
@@ -40,29 +40,30 @@ func (s *vfsShm) Close() error {
|
|||||||
return s.File.Close()
|
return s.File.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *vfsShm) shmOpen() _ErrorCode {
|
func (s *vfsShm) shmOpen() error {
|
||||||
|
if s.fileLock {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if s.File == nil {
|
if s.File == nil {
|
||||||
f, err := os.OpenFile(s.path, os.O_RDWR|os.O_CREATE, 0666)
|
f, err := os.OpenFile(s.path, os.O_RDWR|os.O_CREATE, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return _CANTOPEN
|
return sysError{err, _CANTOPEN}
|
||||||
}
|
}
|
||||||
|
s.fileLock = false
|
||||||
s.File = f
|
s.File = f
|
||||||
}
|
}
|
||||||
if s.fileLock {
|
|
||||||
return _OK
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dead man's switch.
|
// Dead man's switch.
|
||||||
if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc == _OK {
|
if osWriteLock(s.File, _SHM_DMS, 1, 0) == nil {
|
||||||
err := s.Truncate(0)
|
err := s.Truncate(0)
|
||||||
osUnlock(s.File, _SHM_DMS, 1)
|
osUnlock(s.File, _SHM_DMS, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return _IOERR_SHMOPEN
|
return sysError{err, _IOERR_SHMOPEN}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rc := osReadLock(s.File, _SHM_DMS, 1, 0)
|
err := osReadLock(s.File, _SHM_DMS, 1, 0)
|
||||||
s.fileLock = rc == _OK
|
s.fileLock = err == nil
|
||||||
return rc
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (_ ptr_t, err error) {
|
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (_ ptr_t, err error) {
|
||||||
@@ -75,8 +76,8 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext
|
|||||||
s.free = mod.ExportedFunction("sqlite3_free")
|
s.free = mod.ExportedFunction("sqlite3_free")
|
||||||
s.alloc = mod.ExportedFunction("sqlite3_malloc64")
|
s.alloc = mod.ExportedFunction("sqlite3_malloc64")
|
||||||
}
|
}
|
||||||
if rc := s.shmOpen(); rc != _OK {
|
if err := s.shmOpen(); err != nil {
|
||||||
return 0, rc
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer s.shmAcquire(&err)
|
defer s.shmAcquire(&err)
|
||||||
@@ -172,6 +173,7 @@ func (s *vfsShm) shmUnmap(delete bool) {
|
|||||||
// Close the file.
|
// Close the file.
|
||||||
s.Close()
|
s.Close()
|
||||||
s.File = nil
|
s.File = nil
|
||||||
|
s.fileLock = false
|
||||||
if delete {
|
if delete {
|
||||||
os.Remove(s.path)
|
os.Remove(s.path)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -477,6 +477,8 @@ func vfsErrorCode(ctx context.Context, err error, code _ErrorCode) _ErrorCode {
|
|||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SystemError tags an error with a given
|
||||||
|
// sqlite3.ErrorCode or sqlite3.ExtendedErrorCode.
|
||||||
func SystemError[T interface{ ~uint8 | ~uint16 }](err error, code T) error {
|
func SystemError[T interface{ ~uint8 | ~uint16 }](err error, code T) error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
Reference in New Issue
Block a user