summaryrefslogtreecommitdiff
path: root/vendor/github.com/ncruces/go-sqlite3/vfs/shm_bsd.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/ncruces/go-sqlite3/vfs/shm_bsd.go')
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/shm_bsd.go79
1 files changed, 61 insertions, 18 deletions
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/shm_bsd.go b/vendor/github.com/ncruces/go-sqlite3/vfs/shm_bsd.go
index 8e7f27084..10d6dbf61 100644
--- a/vendor/github.com/ncruces/go-sqlite3/vfs/shm_bsd.go
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/shm_bsd.go
@@ -4,7 +4,9 @@ package vfs
import (
"context"
+ "errors"
"io"
+ "io/fs"
"os"
"sync"
@@ -71,23 +73,21 @@ func (s *vfsShm) shmOpen() _ErrorCode {
return _OK
}
- // Always open file read-write, as it will be shared.
- f, err := os.OpenFile(s.path,
- os.O_RDWR|os.O_CREATE|_O_NOFOLLOW, 0666)
- if err != nil {
- return _CANTOPEN
- }
- // Closes file if it's not nil.
+ var f *os.File
+ // Close file on error.
+ // Keep this here to avoid confusing checklocks.
defer func() { f.Close() }()
- fi, err := f.Stat()
- if err != nil {
- return _IOERR_FSTAT
- }
-
vfsShmListMtx.Lock()
defer vfsShmListMtx.Unlock()
+ // Stat file without opening it.
+ // Closing it would release all POSIX locks on it.
+ fi, err := os.Stat(s.path)
+ if err != nil && !errors.Is(err, fs.ErrNotExist) {
+ return _IOERR_FSTAT
+ }
+
// Find a shared file, increase the reference count.
for _, g := range vfsShmList {
if g != nil && os.SameFile(fi, g.info) {
@@ -97,13 +97,33 @@ func (s *vfsShm) shmOpen() _ErrorCode {
}
}
- // Lock and truncate the file.
- // The lock is only released by closing the file.
- if rc := osLock(f, unix.LOCK_EX|unix.LOCK_NB, _IOERR_LOCK); rc != _OK {
+ // Always open file read-write, as it will be shared.
+ f, err = os.OpenFile(s.path,
+ os.O_RDWR|os.O_CREATE|_O_NOFOLLOW, 0666)
+ if err != nil {
+ return _CANTOPEN
+ }
+
+ // Dead man's switch.
+ if lock, rc := osTestLock(f, _SHM_DMS, 1); rc != _OK {
+ return _IOERR_LOCK
+ } else if lock == unix.F_WRLCK {
+ return _BUSY
+ } else if lock == unix.F_UNLCK {
+ if rc := osWriteLock(f, _SHM_DMS, 1); rc != _OK {
+ return rc
+ }
+ if err := f.Truncate(0); err != nil {
+ return _IOERR_SHMOPEN
+ }
+ }
+ if rc := osReadLock(f, _SHM_DMS, 1); rc != _OK {
return rc
}
- if err := f.Truncate(0); err != nil {
- return _IOERR_SHMOPEN
+
+ fi, err = f.Stat()
+ if err != nil {
+ return _IOERR_FSTAT
}
// Add the new shared file.
@@ -157,7 +177,30 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext
func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) _ErrorCode {
s.Lock()
defer s.Unlock()
- return s.shmMemLock(offset, n, flags)
+
+ // Check if we could obtain/release the lock locally.
+ rc := s.shmMemLock(offset, n, flags)
+ if rc != _OK {
+ return rc
+ }
+
+ // Obtain/release the appropriate file lock.
+ switch {
+ case flags&_SHM_UNLOCK != 0:
+ return osUnlock(s.File, _SHM_BASE+int64(offset), int64(n))
+ case flags&_SHM_SHARED != 0:
+ rc = osReadLock(s.File, _SHM_BASE+int64(offset), int64(n))
+ case flags&_SHM_EXCLUSIVE != 0:
+ rc = osWriteLock(s.File, _SHM_BASE+int64(offset), int64(n))
+ default:
+ panic(util.AssertErr())
+ }
+
+ // Release the local lock.
+ if rc != _OK {
+ s.shmMemLock(offset, n, flags^(_SHM_UNLOCK|_SHM_LOCK))
+ }
+ return rc
}
func (s *vfsShm) shmUnmap(delete bool) {