Files
sqlite3/sqlite3vfs/vfs.go

349 lines
9.5 KiB
Go
Raw Normal View History

2023-05-19 03:04:07 +01:00
package sqlite3vfs
2023-03-29 15:01:25 +01:00
import (
"context"
"crypto/rand"
"io"
2023-05-19 03:04:07 +01:00
"reflect"
2023-03-29 15:01:25 +01:00
"time"
"github.com/ncruces/go-sqlite3/internal/util"
"github.com/ncruces/julianday"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"
)
2023-05-19 03:04:07 +01:00
func ExportHostFunctions(env wazero.HostModuleBuilder) wazero.HostModuleBuilder {
2023-05-17 01:00:08 +01:00
util.RegisterFuncII(env, "go_vfs_find", vfsFind)
2023-05-16 17:25:36 +01:00
util.RegisterFuncIIJ(env, "go_localtime", vfsLocaltime)
util.RegisterFuncIIII(env, "go_randomness", vfsRandomness)
util.RegisterFuncIII(env, "go_sleep", vfsSleep)
util.RegisterFuncIII(env, "go_current_time", vfsCurrentTime)
util.RegisterFuncIII(env, "go_current_time_64", vfsCurrentTime64)
util.RegisterFuncIIIII(env, "go_full_pathname", vfsFullPathname)
util.RegisterFuncIIII(env, "go_delete", vfsDelete)
util.RegisterFuncIIIII(env, "go_access", vfsAccess)
util.RegisterFuncIIIIII(env, "go_open", vfsOpen)
util.RegisterFuncII(env, "go_close", vfsClose)
util.RegisterFuncIIIIJ(env, "go_read", vfsRead)
util.RegisterFuncIIIIJ(env, "go_write", vfsWrite)
util.RegisterFuncIIJ(env, "go_truncate", vfsTruncate)
util.RegisterFuncIII(env, "go_sync", vfsSync)
util.RegisterFuncIII(env, "go_file_size", vfsFileSize)
util.RegisterFuncIIII(env, "go_file_control", vfsFileControl)
util.RegisterFuncII(env, "go_sector_size", vfsSectorSize)
util.RegisterFuncII(env, "go_device_characteristics", vfsDeviceCharacteristics)
util.RegisterFuncIII(env, "go_lock", vfsLock)
util.RegisterFuncIII(env, "go_unlock", vfsUnlock)
util.RegisterFuncIII(env, "go_check_reserved_lock", vfsCheckReservedLock)
2023-03-29 15:01:25 +01:00
return env
}
type vfsKey struct{}
type vfsState struct {
2023-05-19 03:04:07 +01:00
files []File
2023-03-29 15:01:25 +01:00
}
2023-05-19 03:04:07 +01:00
func NewContext(ctx context.Context) (context.Context, io.Closer) {
2023-03-29 15:01:25 +01:00
vfs := &vfsState{}
return context.WithValue(ctx, vfsKey{}, vfs), vfs
}
func (vfs *vfsState) Close() error {
for _, f := range vfs.files {
2023-05-18 16:00:34 +01:00
if f != nil {
2023-03-29 15:01:25 +01:00
f.Close()
}
}
vfs.files = nil
return nil
}
2023-05-17 01:00:08 +01:00
func vfsFind(ctx context.Context, mod api.Module, zVfsName uint32) uint32 {
2023-05-17 14:04:00 +01:00
name := util.ReadString(mod, zVfsName, _MAX_STRING)
2023-05-19 03:04:07 +01:00
if Find(name) != nil {
2023-05-17 14:04:00 +01:00
return 1
}
2023-05-17 01:00:08 +01:00
return 0
}
2023-03-29 15:01:25 +01:00
func vfsLocaltime(ctx context.Context, mod api.Module, pTm uint32, t int64) _ErrorCode {
tm := time.Unix(t, 0)
var isdst int
if tm.IsDST() {
isdst = 1
}
2023-03-31 13:48:19 +01:00
const size = 32 / 8
2023-03-29 15:01:25 +01:00
// https://pubs.opengroup.org/onlinepubs/7908799/xsh/time.h.html
2023-03-31 13:48:19 +01:00
util.WriteUint32(mod, pTm+0*size, uint32(tm.Second()))
util.WriteUint32(mod, pTm+1*size, uint32(tm.Minute()))
util.WriteUint32(mod, pTm+2*size, uint32(tm.Hour()))
util.WriteUint32(mod, pTm+3*size, uint32(tm.Day()))
util.WriteUint32(mod, pTm+4*size, uint32(tm.Month()-time.January))
util.WriteUint32(mod, pTm+5*size, uint32(tm.Year()-1900))
util.WriteUint32(mod, pTm+6*size, uint32(tm.Weekday()-time.Sunday))
util.WriteUint32(mod, pTm+7*size, uint32(tm.YearDay()-1))
util.WriteUint32(mod, pTm+8*size, uint32(isdst))
2023-03-29 15:01:25 +01:00
return _OK
}
func vfsRandomness(ctx context.Context, mod api.Module, pVfs, nByte, zByte uint32) uint32 {
mem := util.View(mod, zByte, uint64(nByte))
n, _ := rand.Reader.Read(mem)
return uint32(n)
}
func vfsSleep(ctx context.Context, mod api.Module, pVfs, nMicro uint32) _ErrorCode {
time.Sleep(time.Duration(nMicro) * time.Microsecond)
return _OK
}
func vfsCurrentTime(ctx context.Context, mod api.Module, pVfs, prNow uint32) _ErrorCode {
day := julianday.Float(time.Now())
util.WriteFloat64(mod, prNow, day)
return _OK
}
func vfsCurrentTime64(ctx context.Context, mod api.Module, pVfs, piNow uint32) _ErrorCode {
day, nsec := julianday.Date(time.Now())
msec := day*86_400_000 + nsec/1_000_000
util.WriteUint64(mod, piNow, uint64(msec))
return _OK
}
func vfsFullPathname(ctx context.Context, mod api.Module, pVfs, zRelative, nFull, zFull uint32) _ErrorCode {
2023-05-18 01:34:54 +01:00
vfs := vfsAPIGet(mod, pVfs)
2023-05-18 16:00:34 +01:00
path := util.ReadString(mod, zRelative, _MAX_PATHNAME)
path, err := vfs.FullPathname(path)
2023-03-29 15:01:25 +01:00
2023-05-18 16:00:34 +01:00
size := uint64(len(path) + 1)
2023-03-29 15:01:25 +01:00
if size > uint64(nFull) {
return _CANTOPEN_FULLPATH
}
mem := util.View(mod, zFull, size)
2023-05-18 16:00:34 +01:00
mem[len(path)] = 0
copy(mem, path)
2023-03-29 15:01:25 +01:00
2023-05-18 16:00:34 +01:00
return vfsAPIErrorCode(err, _CANTOPEN_FULLPATH)
2023-03-29 15:01:25 +01:00
}
func vfsDelete(ctx context.Context, mod api.Module, pVfs, zPath, syncDir uint32) _ErrorCode {
2023-05-18 01:34:54 +01:00
vfs := vfsAPIGet(mod, pVfs)
2023-03-29 15:01:25 +01:00
path := util.ReadString(mod, zPath, _MAX_PATHNAME)
2023-05-18 01:34:54 +01:00
2023-05-18 16:00:34 +01:00
err := vfs.Delete(path, syncDir != 0)
return vfsAPIErrorCode(err, _IOERR_DELETE)
2023-03-29 15:01:25 +01:00
}
2023-05-19 03:04:07 +01:00
func vfsAccess(ctx context.Context, mod api.Module, pVfs, zPath uint32, flags AccessFlag, pResOut uint32) _ErrorCode {
2023-05-18 01:34:54 +01:00
vfs := vfsAPIGet(mod, pVfs)
2023-03-29 15:01:25 +01:00
path := util.ReadString(mod, zPath, _MAX_PATHNAME)
2023-05-18 16:00:34 +01:00
ok, err := vfs.Access(path, flags)
2023-03-29 15:01:25 +01:00
var res uint32
2023-05-18 16:00:34 +01:00
if ok {
res = 1
2023-03-29 15:01:25 +01:00
}
util.WriteUint32(mod, pResOut, res)
2023-05-18 16:00:34 +01:00
return vfsAPIErrorCode(err, _IOERR_ACCESS)
2023-03-29 15:01:25 +01:00
}
2023-05-19 03:04:07 +01:00
func vfsOpen(ctx context.Context, mod api.Module, pVfs, zPath, pFile uint32, flags OpenFlag, pOutFlags uint32) _ErrorCode {
2023-05-18 01:34:54 +01:00
vfs := vfsAPIGet(mod, pVfs)
2023-05-17 14:04:00 +01:00
2023-05-18 16:00:34 +01:00
var path string
if zPath != 0 {
path = util.ReadString(mod, zPath, _MAX_PATHNAME)
2023-03-29 15:01:25 +01:00
}
2023-05-18 16:00:34 +01:00
file, flags, err := vfs.Open(path, flags)
2023-03-29 15:01:25 +01:00
if err != nil {
2023-05-18 16:00:34 +01:00
return vfsAPIErrorCode(err, _CANTOPEN)
2023-03-29 15:01:25 +01:00
}
2023-05-18 16:00:34 +01:00
vfsFileRegister(ctx, mod, pFile, file)
2023-03-29 15:01:25 +01:00
if pOutFlags != 0 {
util.WriteUint32(mod, pOutFlags, uint32(flags))
}
return _OK
}
func vfsClose(ctx context.Context, mod api.Module, pFile uint32) _ErrorCode {
2023-05-18 01:34:54 +01:00
err := vfsFileClose(ctx, mod, pFile)
2023-03-29 15:01:25 +01:00
if err != nil {
return _IOERR_CLOSE
}
return _OK
}
func vfsRead(ctx context.Context, mod api.Module, pFile, zBuf, iAmt uint32, iOfst int64) _ErrorCode {
2023-05-18 16:00:34 +01:00
file := vfsFileGet(ctx, mod, pFile)
2023-03-29 15:01:25 +01:00
buf := util.View(mod, zBuf, uint64(iAmt))
n, err := file.ReadAt(buf, iOfst)
if n == int(iAmt) {
return _OK
}
if n == 0 && err != io.EOF {
return _IOERR_READ
}
for i := range buf[n:] {
buf[n+i] = 0
}
return _IOERR_SHORT_READ
}
func vfsWrite(ctx context.Context, mod api.Module, pFile, zBuf, iAmt uint32, iOfst int64) _ErrorCode {
2023-05-18 16:00:34 +01:00
file := vfsFileGet(ctx, mod, pFile)
2023-03-29 15:01:25 +01:00
buf := util.View(mod, zBuf, uint64(iAmt))
_, err := file.WriteAt(buf, iOfst)
if err != nil {
return _IOERR_WRITE
}
return _OK
}
func vfsTruncate(ctx context.Context, mod api.Module, pFile uint32, nByte int64) _ErrorCode {
2023-05-18 01:34:54 +01:00
file := vfsFileGet(ctx, mod, pFile)
2023-03-29 15:01:25 +01:00
err := file.Truncate(nByte)
2023-05-18 16:00:34 +01:00
return vfsAPIErrorCode(err, _IOERR_TRUNCATE)
2023-03-29 15:01:25 +01:00
}
2023-05-19 03:04:07 +01:00
func vfsSync(ctx context.Context, mod api.Module, pFile uint32, flags SyncFlag) _ErrorCode {
2023-05-18 01:34:54 +01:00
file := vfsFileGet(ctx, mod, pFile)
2023-05-18 16:00:34 +01:00
err := file.Sync(flags)
return vfsAPIErrorCode(err, _IOERR_FSYNC)
2023-03-29 15:01:25 +01:00
}
func vfsFileSize(ctx context.Context, mod api.Module, pFile, pSize uint32) _ErrorCode {
2023-05-18 01:34:54 +01:00
file := vfsFileGet(ctx, mod, pFile)
2023-05-18 16:00:34 +01:00
size, err := file.FileSize()
util.WriteUint64(mod, pSize, uint64(size))
return vfsAPIErrorCode(err, _IOERR_SEEK)
}
2023-05-19 03:04:07 +01:00
func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock LockLevel) _ErrorCode {
2023-05-18 16:00:34 +01:00
file := vfsFileGet(ctx, mod, pFile)
err := file.Lock(eLock)
return vfsAPIErrorCode(err, _IOERR_LOCK)
}
2023-05-19 03:04:07 +01:00
func vfsUnlock(ctx context.Context, mod api.Module, pFile uint32, eLock LockLevel) _ErrorCode {
2023-05-18 16:00:34 +01:00
file := vfsFileGet(ctx, mod, pFile)
err := file.Unlock(eLock)
return vfsAPIErrorCode(err, _IOERR_UNLOCK)
}
func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut uint32) _ErrorCode {
file := vfsFileGet(ctx, mod, pFile)
locked, err := file.CheckReservedLock()
var res uint32
if locked {
res = 1
2023-03-29 15:01:25 +01:00
}
2023-05-18 16:00:34 +01:00
util.WriteUint32(mod, pResOut, res)
return vfsAPIErrorCode(err, _IOERR_CHECKRESERVEDLOCK)
2023-03-29 15:01:25 +01:00
}
func vfsFileControl(ctx context.Context, mod api.Module, pFile uint32, op _FcntlOpcode, pArg uint32) _ErrorCode {
2023-05-19 02:00:16 +01:00
file := vfsFileGet(ctx, mod, pFile)
2023-03-29 15:01:25 +01:00
switch op {
2023-04-10 19:55:44 +01:00
case _FCNTL_LOCKSTATE:
2023-05-19 03:04:07 +01:00
if file, ok := file.(FileLockState); ok {
2023-05-19 02:00:16 +01:00
util.WriteUint32(mod, pArg, uint32(file.LockState()))
return _OK
2023-05-18 16:00:34 +01:00
}
2023-05-19 02:00:16 +01:00
2023-04-10 19:55:44 +01:00
case _FCNTL_LOCK_TIMEOUT:
2023-05-19 02:00:16 +01:00
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
2023-05-18 16:00:34 +01:00
}
2023-05-19 02:00:16 +01:00
2023-04-10 19:55:44 +01:00
case _FCNTL_POWERSAFE_OVERWRITE:
2023-05-19 03:04:07 +01:00
if file, ok := file.(FilePowersafeOverwrite); ok {
2023-05-19 02:00:16 +01:00
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)
}
2023-04-10 19:55:44 +01:00
}
2023-05-19 02:00:16 +01:00
return _OK
2023-04-10 19:55:44 +01:00
}
2023-05-19 02:00:16 +01:00
2023-03-29 15:01:25 +01:00
case _FCNTL_SIZE_HINT:
2023-05-19 03:04:07 +01:00
if file, ok := file.(FileSizeHint); ok {
2023-05-19 02:00:16 +01:00
size := util.ReadUint64(mod, pArg)
err := file.SizeHint(int64(size))
return vfsAPIErrorCode(err, _IOERR_TRUNCATE)
}
2023-03-29 15:01:25 +01:00
case _FCNTL_HAS_MOVED:
2023-05-19 03:04:07 +01:00
if file, ok := file.(FileHasMoved); ok {
2023-05-19 02:00:16 +01:00
moved, err := file.HasMoved()
var res uint32
if moved {
res = 1
}
util.WriteUint32(mod, pArg, res)
return vfsAPIErrorCode(err, _IOERR_FSTAT)
}
2023-03-29 15:01:25 +01:00
}
2023-05-19 02:00:16 +01:00
2023-04-10 19:55:44 +01:00
// Consider also implementing these opcodes (in use by SQLite):
// _FCNTL_BUSYHANDLER
// _FCNTL_COMMIT_PHASETWO
// _FCNTL_PDB
// _FCNTL_PRAGMA
// _FCNTL_SYNC
2023-03-29 15:01:25 +01:00
return _NOTFOUND
}
2023-04-10 19:55:44 +01:00
func vfsSectorSize(ctx context.Context, mod api.Module, pFile uint32) uint32 {
2023-05-18 16:00:34 +01:00
file := vfsFileGet(ctx, mod, pFile)
return uint32(file.SectorSize())
2023-04-10 19:55:44 +01:00
}
2023-05-19 03:04:07 +01:00
func vfsDeviceCharacteristics(ctx context.Context, mod api.Module, pFile uint32) DeviceCharacteristic {
2023-05-19 02:00:16 +01:00
file := vfsFileGet(ctx, mod, pFile)
return file.DeviceCharacteristics()
2023-03-29 15:01:25 +01:00
}
2023-05-19 03:04:07 +01:00
func vfsAPIGet(mod api.Module, pVfs uint32) VFS {
if pVfs != 0 {
name := util.ReadString(mod, util.ReadUint32(mod, pVfs+16), _MAX_STRING)
if vfs := Find(name); vfs != nil {
return vfs
}
}
return vfsOS{}
}
func vfsAPIErrorCode(err error, def _ErrorCode) _ErrorCode {
if err == nil {
return _OK
}
switch v := reflect.ValueOf(err); v.Kind() {
case reflect.Uint8, reflect.Uint16, reflect.Uint32:
return _ErrorCode(v.Uint())
}
return def
}