More VFS API.

This commit is contained in:
Nuno Cruces
2023-05-19 02:00:16 +01:00
parent df953b31c2
commit e1cce83f71
7 changed files with 158 additions and 105 deletions

View File

@@ -137,7 +137,7 @@ const (
AUTH_USER ExtendedErrorCode = xErrorCode(AUTH) | (1 << 8)
)
// OpenFlag is a flag for a file open operation.
// OpenFlag is a flag for the [OpenFlags] function.
//
// https://www.sqlite.org/c3ref/c_open_autoproxy.html
type OpenFlag uint32

View File

@@ -101,6 +101,27 @@ const (
_LOCK_EXCLUSIVE = sqlite3vfs.LOCK_EXCLUSIVE
)
// https://www.sqlite.org/c3ref/c_iocap_atomic.html
type _DeviceCharacteristic = sqlite3vfs.DeviceCharacteristic
const (
_IOCAP_ATOMIC = sqlite3vfs.IOCAP_ATOMIC
_IOCAP_ATOMIC512 = sqlite3vfs.IOCAP_ATOMIC512
_IOCAP_ATOMIC1K = sqlite3vfs.IOCAP_ATOMIC1K
_IOCAP_ATOMIC2K = sqlite3vfs.IOCAP_ATOMIC2K
_IOCAP_ATOMIC4K = sqlite3vfs.IOCAP_ATOMIC4K
_IOCAP_ATOMIC8K = sqlite3vfs.IOCAP_ATOMIC8K
_IOCAP_ATOMIC16K = sqlite3vfs.IOCAP_ATOMIC16K
_IOCAP_ATOMIC32K = sqlite3vfs.IOCAP_ATOMIC32K
_IOCAP_ATOMIC64K = sqlite3vfs.IOCAP_ATOMIC64K
_IOCAP_SAFE_APPEND = sqlite3vfs.IOCAP_SAFE_APPEND
_IOCAP_SEQUENTIAL = sqlite3vfs.IOCAP_SEQUENTIAL
_IOCAP_UNDELETABLE_WHEN_OPEN = sqlite3vfs.IOCAP_UNDELETABLE_WHEN_OPEN
_IOCAP_POWERSAFE_OVERWRITE = sqlite3vfs.IOCAP_POWERSAFE_OVERWRITE
_IOCAP_IMMUTABLE = sqlite3vfs.IOCAP_IMMUTABLE
_IOCAP_BATCH_ATOMIC = sqlite3vfs.IOCAP_BATCH_ATOMIC
)
// https://www.sqlite.org/c3ref/c_fcntl_begin_atomic_write.html
type _FcntlOpcode uint32
@@ -147,24 +168,3 @@ const (
_FCNTL_CKSM_FILE _FcntlOpcode = 41
_FCNTL_RESET_CACHE _FcntlOpcode = 42
)
// https://www.sqlite.org/c3ref/c_iocap_atomic.html
type _DeviceCharacteristic uint32
const (
_IOCAP_ATOMIC _DeviceCharacteristic = 0x00000001
_IOCAP_ATOMIC512 _DeviceCharacteristic = 0x00000002
_IOCAP_ATOMIC1K _DeviceCharacteristic = 0x00000004
_IOCAP_ATOMIC2K _DeviceCharacteristic = 0x00000008
_IOCAP_ATOMIC4K _DeviceCharacteristic = 0x00000010
_IOCAP_ATOMIC8K _DeviceCharacteristic = 0x00000020
_IOCAP_ATOMIC16K _DeviceCharacteristic = 0x00000040
_IOCAP_ATOMIC32K _DeviceCharacteristic = 0x00000080
_IOCAP_ATOMIC64K _DeviceCharacteristic = 0x00000100
_IOCAP_SAFE_APPEND _DeviceCharacteristic = 0x00000200
_IOCAP_SEQUENTIAL _DeviceCharacteristic = 0x00000400
_IOCAP_UNDELETABLE_WHEN_OPEN _DeviceCharacteristic = 0x00000800
_IOCAP_POWERSAFE_OVERWRITE _DeviceCharacteristic = 0x00001000
_IOCAP_IMMUTABLE _DeviceCharacteristic = 0x00002000
_IOCAP_BATCH_ATOMIC _DeviceCharacteristic = 0x00004000
)

View File

@@ -3,10 +3,7 @@ package vfs
import (
"context"
"crypto/rand"
"errors"
"io"
"io/fs"
"os"
"time"
"github.com/ncruces/go-sqlite3/internal/util"
@@ -255,45 +252,61 @@ func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut ui
}
func vfsFileControl(ctx context.Context, mod api.Module, pFile uint32, op _FcntlOpcode, pArg uint32) _ErrorCode {
file := vfsFileGet(ctx, mod, pFile)
switch op {
case _FCNTL_LOCKSTATE:
file, ok := vfsFileGet(ctx, mod, pFile).(*vfsFile)
if !ok {
return _NOTFOUND
if file, ok := file.(sqlite3vfs.FileLockState); ok {
util.WriteUint32(mod, pArg, uint32(file.LockState()))
return _OK
}
util.WriteUint32(mod, pArg, uint32(file.lock))
return _OK
case _FCNTL_LOCK_TIMEOUT:
file, ok := vfsFileGet(ctx, mod, pFile).(*vfsFile)
if !ok {
return _NOTFOUND
if file, ok := file.(*vfsFile); ok {
millis := file.lockTimeout.Milliseconds()
file.lockTimeout = time.Duration(util.ReadUint32(mod, pArg)) * time.Millisecond
util.WriteUint32(mod, pArg, uint32(millis))
return _OK
}
millis := file.lockTimeout.Milliseconds()
file.lockTimeout = time.Duration(util.ReadUint32(mod, pArg)) * time.Millisecond
util.WriteUint32(mod, pArg, uint32(millis))
return _OK
case _FCNTL_POWERSAFE_OVERWRITE:
file, ok := vfsFileGet(ctx, mod, pFile).(*vfsFile)
if !ok {
return _NOTFOUND
}
switch util.ReadUint32(mod, pArg) {
case 0:
file.psow = false
case 1:
file.psow = true
default:
if file.psow {
util.WriteUint32(mod, pArg, 1)
} else {
util.WriteUint32(mod, pArg, 0)
if file, ok := file.(sqlite3vfs.FilePowersafeOverwrite); ok {
switch util.ReadUint32(mod, pArg) {
case 0:
file.SetPowersafeOverwrite(false)
case 1:
file.SetPowersafeOverwrite(true)
default:
if file.PowersafeOverwrite() {
util.WriteUint32(mod, pArg, 1)
} else {
util.WriteUint32(mod, pArg, 0)
}
}
return _OK
}
case _FCNTL_SIZE_HINT:
return vfsSizeHint(ctx, mod, pFile, pArg)
if file, ok := file.(sqlite3vfs.FileSizeHint); ok {
size := util.ReadUint64(mod, pArg)
err := file.SizeHint(int64(size))
return vfsAPIErrorCode(err, _IOERR_TRUNCATE)
}
case _FCNTL_HAS_MOVED:
return vfsFileMoved(ctx, mod, pFile, pArg)
if file, ok := file.(sqlite3vfs.FileHasMoved); ok {
moved, err := file.HasMoved()
var res uint32
if moved {
res = 1
}
util.WriteUint32(mod, pArg, res)
return vfsAPIErrorCode(err, _IOERR_FSTAT)
}
}
// Consider also implementing these opcodes (in use by SQLite):
// _FCNTL_BUSYHANDLER
// _FCNTL_COMMIT_PHASETWO
@@ -309,43 +322,6 @@ func vfsSectorSize(ctx context.Context, mod api.Module, pFile uint32) uint32 {
}
func vfsDeviceCharacteristics(ctx context.Context, mod api.Module, pFile uint32) _DeviceCharacteristic {
file, ok := vfsFileGet(ctx, mod, pFile).(*vfsFile)
if ok && file.psow {
return _IOCAP_POWERSAFE_OVERWRITE
}
return 0
}
func vfsSizeHint(ctx context.Context, mod api.Module, pFile, pArg uint32) _ErrorCode {
file, ok := vfsFileGet(ctx, mod, pFile).(*vfsFile)
if !ok {
return _NOTFOUND
}
size := util.ReadUint64(mod, pArg)
err := osAllocate(file.File, int64(size))
if err != nil {
return _IOERR_TRUNCATE
}
return _OK
}
func vfsFileMoved(ctx context.Context, mod api.Module, pFile, pResOut uint32) _ErrorCode {
file, ok := vfsFileGet(ctx, mod, pFile).(*vfsFile)
if !ok {
return _NOTFOUND
}
fi, err := file.Stat()
if err != nil {
return _IOERR_FSTAT
}
pi, err := os.Stat(file.Name())
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return _IOERR_FSTAT
}
var res uint32
if !os.SameFile(fi, pi) {
res = 1
}
util.WriteUint32(mod, pResOut, res)
return _OK
file := vfsFileGet(ctx, mod, pFile)
return file.DeviceCharacteristics()
}

View File

@@ -8,15 +8,14 @@ import (
"github.com/tetratelabs/wazero/api"
)
func vfsAPIGet(mod api.Module, pVfs uint32) (vfs sqlite3vfs.VFS) {
func vfsAPIGet(mod api.Module, pVfs uint32) sqlite3vfs.VFS {
if pVfs != 0 {
name := util.ReadString(mod, util.ReadUint32(mod, pVfs+16), _MAX_STRING)
vfs = sqlite3vfs.Find(name)
if vfs := sqlite3vfs.Find(name); vfs != nil {
return vfs
}
}
if vfs == nil {
vfs = vfsOS
}
return
return vfsOS{}
}
func vfsAPIErrorCode(err error, def _ErrorCode) _ErrorCode {

View File

@@ -15,11 +15,9 @@ import (
"github.com/tetratelabs/wazero/api"
)
const vfsOS vfsOSAPI = false
type vfsOS struct{}
type vfsOSAPI bool
func (vfsOSAPI) FullPathname(path string) (string, error) {
func (vfsOS) FullPathname(path string) (string, error) {
path, err := filepath.Abs(path)
if err != nil {
return "", err
@@ -37,7 +35,7 @@ func (vfsOSAPI) FullPathname(path string) (string, error) {
return path, err
}
func (vfsOSAPI) Delete(path string, syncDir bool) error {
func (vfsOS) Delete(path string, syncDir bool) error {
err := os.Remove(path)
if errors.Is(err, fs.ErrNotExist) {
return _IOERR_DELETE_NOENT
@@ -59,7 +57,7 @@ func (vfsOSAPI) Delete(path string, syncDir bool) error {
return nil
}
func (vfsOSAPI) Access(name string, flags sqlite3vfs.AccessFlag) (bool, error) {
func (vfsOS) Access(name string, flags sqlite3vfs.AccessFlag) (bool, error) {
err := osAccess(name, flags)
if flags == _ACCESS_EXISTS {
if errors.Is(err, fs.ErrNotExist) {
@@ -73,7 +71,7 @@ func (vfsOSAPI) Access(name string, flags sqlite3vfs.AccessFlag) (bool, error) {
return err == nil, err
}
func (vfsOSAPI) Open(name string, flags sqlite3vfs.OpenFlag) (sqlite3vfs.File, sqlite3vfs.OpenFlag, error) {
func (vfsOS) Open(name string, flags sqlite3vfs.OpenFlag) (sqlite3vfs.File, sqlite3vfs.OpenFlag, error) {
var oflags int
if flags&_OPEN_EXCLUSIVE != 0 {
oflags |= os.O_EXCL
@@ -123,6 +121,14 @@ type vfsFile struct {
readOnly bool
}
var (
// Ensure these interfaces are implemented:
_ sqlite3vfs.FileLockState = &vfsFile{}
_ sqlite3vfs.FileHasMoved = &vfsFile{}
_ sqlite3vfs.FileSizeHint = &vfsFile{}
_ sqlite3vfs.FilePowersafeOverwrite = &vfsFile{}
)
func vfsFileNew(vfs *vfsState, file sqlite3vfs.File) uint32 {
// Find an empty slot.
for id, f := range vfs.files {
@@ -186,3 +192,30 @@ func (f *vfsFile) FileSize() (int64, error) {
func (*vfsFile) SectorSize() int {
return _DEFAULT_SECTOR_SIZE
}
func (f *vfsFile) DeviceCharacteristics() sqlite3vfs.DeviceCharacteristic {
if f.psow {
return _IOCAP_POWERSAFE_OVERWRITE
}
return 0
}
func (f *vfsFile) SizeHint(size int64) error {
return osAllocate(f.File, size)
}
func (f *vfsFile) HasMoved() (bool, error) {
fi, err := f.Stat()
if err != nil {
return false, err
}
pi, err := os.Stat(f.Name())
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return false, err
}
return !os.SameFile(fi, pi), nil
}
func (f *vfsFile) LockState() sqlite3vfs.LockLevel { return f.lock }
func (f *vfsFile) PowersafeOverwrite() bool { return f.psow }
func (f *vfsFile) SetPowersafeOverwrite(psow bool) { f.psow = psow }

View File

@@ -96,3 +96,26 @@ const (
// time that EXCLUSIVE locks are held.
LOCK_EXCLUSIVE LockLevel = 4 /* xLock() only */
)
// DeviceCharacteristic is a flag retuned by the [File.DeviceCharacteristic] method.
//
// https://www.sqlite.org/c3ref/c_iocap_atomic.html
type DeviceCharacteristic uint32
const (
IOCAP_ATOMIC DeviceCharacteristic = 0x00000001
IOCAP_ATOMIC512 DeviceCharacteristic = 0x00000002
IOCAP_ATOMIC1K DeviceCharacteristic = 0x00000004
IOCAP_ATOMIC2K DeviceCharacteristic = 0x00000008
IOCAP_ATOMIC4K DeviceCharacteristic = 0x00000010
IOCAP_ATOMIC8K DeviceCharacteristic = 0x00000020
IOCAP_ATOMIC16K DeviceCharacteristic = 0x00000040
IOCAP_ATOMIC32K DeviceCharacteristic = 0x00000080
IOCAP_ATOMIC64K DeviceCharacteristic = 0x00000100
IOCAP_SAFE_APPEND DeviceCharacteristic = 0x00000200
IOCAP_SEQUENTIAL DeviceCharacteristic = 0x00000400
IOCAP_UNDELETABLE_WHEN_OPEN DeviceCharacteristic = 0x00000800
IOCAP_POWERSAFE_OVERWRITE DeviceCharacteristic = 0x00001000
IOCAP_IMMUTABLE DeviceCharacteristic = 0x00002000
IOCAP_BATCH_ATOMIC DeviceCharacteristic = 0x00004000
)

View File

@@ -20,6 +20,28 @@ type File interface {
Unlock(lock LockLevel) error
CheckReservedLock() (bool, error)
SectorSize() int
DeviceCharacteristics() DeviceCharacteristic
}
type FileLockState interface {
File
LockState() LockLevel
}
type FileSizeHint interface {
File
SizeHint(size int64) error
}
type FileHasMoved interface {
File
HasMoved() (bool, error)
}
type FilePowersafeOverwrite interface {
File
PowersafeOverwrite() bool
SetPowersafeOverwrite(bool)
}
var (