Files
sqlite3/vfs/shm_bsd.go

257 lines
5.2 KiB
Go
Raw Normal View History

2025-11-10 11:26:28 +00:00
//go:build ((freebsd || openbsd || netbsd || dragonfly || illumos) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le || loong64) && !sqlite3_dotlk) || sqlite3_flock
package vfs
import (
2025-10-17 16:40:15 +01:00
"cmp"
"context"
2024-12-16 13:15:00 +00:00
"errors"
"io"
2024-12-16 13:15:00 +00:00
"io/fs"
"os"
"sync"
"github.com/tetratelabs/wazero/api"
"golang.org/x/sys/unix"
2024-10-18 12:20:32 +01:00
"github.com/ncruces/go-sqlite3/internal/util"
)
2024-11-07 12:18:42 +00:00
type vfsShmParent struct {
*os.File
info os.FileInfo
2024-11-07 12:18:42 +00:00
refs int // +checklocks:vfsShmListMtx
2024-12-18 16:21:24 +00:00
lock [_SHM_NLOCK]int8 // +checklocks:Mutex
2024-10-31 15:21:15 +00:00
sync.Mutex
}
var (
2024-11-07 12:18:42 +00:00
// +checklocks:vfsShmListMtx
vfsShmList []*vfsShmParent
vfsShmListMtx sync.Mutex
)
type vfsShm struct {
2024-11-07 12:18:42 +00:00
*vfsShmParent
2024-11-05 17:30:10 +00:00
path string
lock [_SHM_NLOCK]bool
regions []*util.MappedRegion
}
func (s *vfsShm) Close() error {
2024-11-07 12:18:42 +00:00
if s.vfsShmParent == nil {
return nil
}
2024-11-07 12:18:42 +00:00
vfsShmListMtx.Lock()
defer vfsShmListMtx.Unlock()
2024-08-11 17:35:27 +01:00
// Unlock everything.
s.shmLock(0, _SHM_NLOCK, _SHM_UNLOCK)
// Decrease reference count.
2024-11-07 12:18:42 +00:00
if s.vfsShmParent.refs > 0 {
s.vfsShmParent.refs--
s.vfsShmParent = nil
return nil
}
2024-08-11 17:35:27 +01:00
err := s.File.Close()
2024-11-07 12:18:42 +00:00
for i, g := range vfsShmList {
if g == s.vfsShmParent {
vfsShmList[i] = nil
s.vfsShmParent = nil
2024-08-11 17:35:27 +01:00
return err
}
}
2024-08-11 17:35:27 +01:00
panic(util.AssertErr())
}
2025-10-17 16:40:15 +01:00
func (s *vfsShm) shmOpen() (err error) {
2024-11-07 12:18:42 +00:00
if s.vfsShmParent != nil {
2025-10-17 16:40:15 +01:00
return nil
}
2024-12-16 13:15:00 +00:00
vfsShmListMtx.Lock()
defer vfsShmListMtx.Unlock()
2024-12-16 13:15:00 +00:00
// 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) {
2025-10-17 16:40:15 +01:00
return sysError{err, _IOERR_FSTAT}
}
2024-06-05 11:01:39 +01:00
// Find a shared file, increase the reference count.
2024-11-07 12:18:42 +00:00
for _, g := range vfsShmList {
if g != nil && os.SameFile(fi, g.info) {
2024-11-07 12:18:42 +00:00
s.vfsShmParent = g
2024-08-30 01:27:57 +01:00
g.refs++
2025-10-17 16:40:15 +01:00
return nil
}
}
2024-06-05 11:01:39 +01:00
2024-12-16 13:15:00 +00:00
// Always open file read-write, as it will be shared.
2025-03-11 20:15:53 +00:00
f, err := os.OpenFile(s.path,
2024-12-16 13:15:00 +00:00
os.O_RDWR|os.O_CREATE|_O_NOFOLLOW, 0666)
if err != nil {
2025-10-17 16:40:15 +01:00
return sysError{err, _CANTOPEN}
2024-12-16 13:15:00 +00:00
}
2025-03-11 20:15:53 +00:00
defer func() {
2025-10-17 16:40:15 +01:00
if err != nil {
2025-03-11 20:15:53 +00:00
f.Close()
}
}()
2024-12-16 13:15:00 +00:00
// Dead man's switch.
2025-10-17 16:40:15 +01:00
if lock, err := osTestLock(f, _SHM_DMS, 1, _IOERR_LOCK); err != nil {
return err
2024-12-16 13:15:00 +00:00
} else if lock == unix.F_WRLCK {
return _BUSY
} else if lock == unix.F_UNLCK {
2025-10-17 16:40:15 +01:00
if err := osWriteLock(f, _SHM_DMS, 1); err != nil {
return err
2024-12-16 13:15:00 +00:00
}
if err := f.Truncate(0); err != nil {
2025-10-17 16:40:15 +01:00
return sysError{err, _IOERR_SHMOPEN}
2024-12-16 13:15:00 +00:00
}
}
2025-10-17 16:40:15 +01:00
if err := osReadLock(f, _SHM_DMS, 1); err != nil {
return err
2024-11-05 17:30:10 +00:00
}
2024-12-16 13:15:00 +00:00
fi, err = f.Stat()
if err != nil {
2025-10-17 16:40:15 +01:00
return sysError{err, _IOERR_FSTAT}
2024-06-05 11:01:39 +01:00
}
// Add the new shared file.
2024-11-07 12:18:42 +00:00
s.vfsShmParent = &vfsShmParent{
File: f,
info: fi,
}
2024-11-07 12:18:42 +00:00
for i, g := range vfsShmList {
if g == nil {
2024-11-07 12:18:42 +00:00
vfsShmList[i] = s.vfsShmParent
2025-10-17 16:40:15 +01:00
return nil
}
}
2024-11-07 12:18:42 +00:00
vfsShmList = append(vfsShmList, s.vfsShmParent)
2025-10-17 16:40:15 +01:00
return nil
}
2025-10-15 16:22:36 +01:00
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (ptr_t, error) {
// Ensure size is a multiple of the OS page size.
if int(size)&(unix.Getpagesize()-1) != 0 {
return 0, _IOERR_SHMMAP
}
2025-10-17 16:40:15 +01:00
if err := s.shmOpen(); err != nil {
return 0, err
}
// Check if file is big enough.
o, err := s.Seek(0, io.SeekEnd)
if err != nil {
2025-10-15 16:22:36 +01:00
return 0, sysError{err, _IOERR_SHMSIZE}
}
if n := (int64(id) + 1) * int64(size); n > o {
if !extend {
2025-10-15 16:22:36 +01:00
return 0, nil
}
2025-10-15 16:22:36 +01:00
if err := osAllocate(s.File, n); err != nil {
return 0, sysError{err, _IOERR_SHMSIZE}
}
}
2024-11-05 17:30:10 +00:00
r, err := util.MapRegion(ctx, mod, s.File, int64(id)*int64(size), size, false)
if err != nil {
2025-10-15 16:22:36 +01:00
return 0, err
}
s.regions = append(s.regions, r)
2025-10-15 16:22:36 +01:00
return r.Ptr, nil
}
2025-10-15 16:22:36 +01:00
func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) error {
if s.vfsShmParent == nil {
return _IOERR_SHMLOCK
}
2024-10-31 15:21:15 +00:00
s.Lock()
defer s.Unlock()
2024-12-16 13:15:00 +00:00
2024-12-21 09:40:37 +00:00
// Check if we can obtain/release locks locally.
2025-10-17 16:40:15 +01:00
err := s.shmMemLock(offset, n, flags)
if err != nil {
return err
2024-12-16 13:15:00 +00:00
}
2024-12-18 16:21:24 +00:00
// Obtain/release the appropriate file locks.
2024-12-16 13:15:00 +00:00
switch {
case flags&_SHM_UNLOCK != 0:
2024-12-21 09:40:37 +00:00
// Relasing a shared lock decrements the counter,
// but may leave parts of the range still locked.
2024-12-18 16:21:24 +00:00
begin, end := offset, offset+n
for i := begin; i < end; i++ {
if s.vfsShmParent.lock[i] != 0 {
if i > begin {
2025-10-17 16:40:15 +01:00
err = cmp.Or(err,
osUnlock(s.File, _SHM_BASE+int64(begin), int64(i-begin)))
2024-12-18 16:21:24 +00:00
}
begin = i + 1
}
}
if end > begin {
2025-10-17 16:40:15 +01:00
err = cmp.Or(err,
osUnlock(s.File, _SHM_BASE+int64(begin), int64(end-begin)))
2024-12-18 16:21:24 +00:00
}
2025-10-17 16:40:15 +01:00
return err
2024-12-16 13:15:00 +00:00
case flags&_SHM_SHARED != 0:
2024-12-21 09:40:37 +00:00
// Acquiring a new shared lock on the file is only necessary
// if there was a new shared lock in the range.
for i := offset; i < offset+n; i++ {
if s.vfsShmParent.lock[i] == 1 {
2025-10-17 16:40:15 +01:00
err = osReadLock(s.File, _SHM_BASE+int64(offset), int64(n))
2024-12-21 09:40:37 +00:00
break
}
}
2024-12-16 13:15:00 +00:00
case flags&_SHM_EXCLUSIVE != 0:
2024-12-21 09:40:37 +00:00
// Acquiring an exclusive lock on the file is always necessary.
2025-10-17 16:40:15 +01:00
err = osWriteLock(s.File, _SHM_BASE+int64(offset), int64(n))
2024-12-16 13:15:00 +00:00
default:
panic(util.AssertErr())
}
2025-10-17 16:40:15 +01:00
if err != nil {
// Release the local locks we had acquired.
2024-12-16 13:15:00 +00:00
s.shmMemLock(offset, n, flags^(_SHM_UNLOCK|_SHM_LOCK))
}
2025-10-17 16:40:15 +01:00
return err
}
func (s *vfsShm) shmUnmap(delete bool) {
2024-11-07 12:18:42 +00:00
if s.vfsShmParent == nil {
return
}
// Unmap regions.
for _, r := range s.regions {
r.Unmap()
}
2024-10-31 15:21:15 +00:00
s.regions = nil
// Close the file.
if delete {
os.Remove(s.path)
}
s.Close()
}
2024-10-17 15:39:01 +01:00
func (s *vfsShm) shmBarrier() {
2024-10-31 15:21:15 +00:00
s.Lock()
2024-10-22 23:32:57 +01:00
//lint:ignore SA2001 memory barrier.
2024-10-31 15:21:15 +00:00
s.Unlock()
2024-10-17 15:39:01 +01:00
}