Files
sqlite3/vfs.go

349 lines
9.0 KiB
Go
Raw Normal View History

2023-01-18 01:30:11 +00:00
package sqlite3
import (
"context"
2023-02-06 01:01:17 +00:00
"crypto/rand"
2023-01-19 00:18:39 +00:00
"errors"
2023-01-18 23:45:22 +00:00
"io"
2023-01-19 00:18:39 +00:00
"io/fs"
2023-01-18 18:58:49 +00:00
"os"
2023-01-18 11:40:08 +00:00
"path/filepath"
2023-01-26 18:20:54 +00:00
"runtime"
2023-01-19 13:27:32 +00:00
"syscall"
2023-01-18 01:30:11 +00:00
"time"
"github.com/ncruces/julianday"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/sys"
)
2023-02-16 13:52:05 +00:00
func vfsInstantiate(ctx context.Context, r wazero.Runtime) {
2023-01-18 01:30:11 +00:00
wasi := r.NewHostModuleBuilder("wasi_snapshot_preview1")
wasi.NewFunctionBuilder().WithFunc(vfsExit).Export("proc_exit")
2023-02-16 13:52:05 +00:00
_, err := wasi.Instantiate(ctx)
2023-01-18 01:30:11 +00:00
if err != nil {
2023-02-16 13:52:05 +00:00
panic(err)
2023-01-18 01:30:11 +00:00
}
2023-03-05 11:36:02 +00:00
env := vfsNewEnvModuleBuilder(r)
_, err = env.Instantiate(ctx)
if err != nil {
panic(err)
}
}
func vfsNewEnvModuleBuilder(r wazero.Runtime) wazero.HostModuleBuilder {
2023-01-18 01:30:11 +00:00
env := r.NewHostModuleBuilder("env")
2023-03-01 12:16:36 +00:00
env.NewFunctionBuilder().WithFunc(vfsLocaltime).Export("os_localtime")
env.NewFunctionBuilder().WithFunc(vfsRandomness).Export("os_randomness")
env.NewFunctionBuilder().WithFunc(vfsSleep).Export("os_sleep")
env.NewFunctionBuilder().WithFunc(vfsCurrentTime).Export("os_current_time")
env.NewFunctionBuilder().WithFunc(vfsCurrentTime64).Export("os_current_time_64")
env.NewFunctionBuilder().WithFunc(vfsFullPathname).Export("os_full_pathname")
env.NewFunctionBuilder().WithFunc(vfsDelete).Export("os_delete")
env.NewFunctionBuilder().WithFunc(vfsAccess).Export("os_access")
env.NewFunctionBuilder().WithFunc(vfsOpen).Export("os_open")
env.NewFunctionBuilder().WithFunc(vfsClose).Export("os_close")
env.NewFunctionBuilder().WithFunc(vfsRead).Export("os_read")
env.NewFunctionBuilder().WithFunc(vfsWrite).Export("os_write")
env.NewFunctionBuilder().WithFunc(vfsTruncate).Export("os_truncate")
env.NewFunctionBuilder().WithFunc(vfsSync).Export("os_sync")
env.NewFunctionBuilder().WithFunc(vfsFileSize).Export("os_file_size")
env.NewFunctionBuilder().WithFunc(vfsLock).Export("os_lock")
env.NewFunctionBuilder().WithFunc(vfsUnlock).Export("os_unlock")
env.NewFunctionBuilder().WithFunc(vfsCheckReservedLock).Export("os_check_reserved_lock")
env.NewFunctionBuilder().WithFunc(vfsFileControl).Export("os_file_control")
2023-03-05 11:36:02 +00:00
return env
2023-01-18 01:30:11 +00:00
}
2023-03-07 10:45:11 +00:00
// Poor man's namespaces.
const (
vfsOS vfsOSMethods = false
vfsFile vfsFileMethods = false
)
type (
vfsOSMethods bool
vfsFileMethods bool
)
2023-02-23 13:29:51 +00:00
2023-03-07 10:45:11 +00:00
type vfsKey struct{}
type vfsState struct {
files []*os.File
}
func vfsContext(ctx context.Context) (context.Context, io.Closer) {
vfs := &vfsState{}
return context.WithValue(ctx, vfsKey{}, vfs), vfs
}
func (vfs *vfsState) Close() error {
for _, f := range vfs.files {
if f != nil {
f.Close()
}
}
vfs.files = nil
return nil
}
2023-02-23 13:29:51 +00:00
2023-01-18 01:30:11 +00:00
func vfsExit(ctx context.Context, mod api.Module, exitCode uint32) {
// Ensure other callers see the exit code.
_ = mod.CloseWithExitCode(ctx, exitCode)
// Prevent any code from executing after this function.
panic(sys.NewExitError(mod.Name(), exitCode))
}
2023-01-24 11:17:30 -08:00
func vfsLocaltime(ctx context.Context, mod api.Module, t uint64, pTm uint32) uint32 {
tm := time.Unix(int64(t), 0)
var isdst int
if tm.IsDST() {
isdst = 1
}
// https://pubs.opengroup.org/onlinepubs/7908799/xsh/time.h.html
2023-01-26 14:52:38 +00:00
mem := memory{mod}
mem.writeUint32(pTm+0*ptrlen, uint32(tm.Second()))
mem.writeUint32(pTm+1*ptrlen, uint32(tm.Minute()))
mem.writeUint32(pTm+2*ptrlen, uint32(tm.Hour()))
mem.writeUint32(pTm+3*ptrlen, uint32(tm.Day()))
mem.writeUint32(pTm+4*ptrlen, uint32(tm.Month()-time.January))
mem.writeUint32(pTm+5*ptrlen, uint32(tm.Year()-1900))
mem.writeUint32(pTm+6*ptrlen, uint32(tm.Weekday()-time.Sunday))
mem.writeUint32(pTm+7*ptrlen, uint32(tm.YearDay()-1))
mem.writeUint32(pTm+8*ptrlen, uint32(isdst))
return _OK
2023-01-24 11:17:30 -08:00
}
2023-01-22 15:44:39 +00:00
func vfsRandomness(ctx context.Context, mod api.Module, pVfs, nByte, zByte uint32) uint32 {
2023-02-27 03:20:23 +00:00
mem := memory{mod}.view(zByte, uint64(nByte))
2023-02-06 01:01:17 +00:00
n, _ := rand.Reader.Read(mem)
2023-01-18 01:30:11 +00:00
return uint32(n)
}
2023-01-22 15:44:39 +00:00
func vfsSleep(ctx context.Context, pVfs, nMicro uint32) uint32 {
time.Sleep(time.Duration(nMicro) * time.Microsecond)
2023-01-18 01:30:11 +00:00
return _OK
}
2023-01-22 15:44:39 +00:00
func vfsCurrentTime(ctx context.Context, mod api.Module, pVfs, prNow uint32) uint32 {
2023-01-18 01:30:11 +00:00
day := julianday.Float(time.Now())
2023-01-26 14:52:38 +00:00
memory{mod}.writeFloat64(prNow, day)
2023-01-18 01:30:11 +00:00
return _OK
}
2023-01-22 15:44:39 +00:00
func vfsCurrentTime64(ctx context.Context, mod api.Module, pVfs, piNow uint32) uint32 {
2023-01-18 01:30:11 +00:00
day, nsec := julianday.Date(time.Now())
msec := day*86_400_000 + nsec/1_000_000
2023-01-26 14:52:38 +00:00
memory{mod}.writeUint64(piNow, uint64(msec))
2023-01-18 01:30:11 +00:00
return _OK
}
2023-01-18 11:40:08 +00:00
2023-01-22 15:44:39 +00:00
func vfsFullPathname(ctx context.Context, mod api.Module, pVfs, zRelative, nFull, zFull uint32) uint32 {
2023-01-26 14:52:38 +00:00
rel := memory{mod}.readString(zRelative, _MAX_PATHNAME)
2023-01-22 15:44:39 +00:00
abs, err := filepath.Abs(rel)
2023-01-18 11:40:08 +00:00
if err != nil {
return uint32(IOERR)
}
2023-01-22 17:33:21 +00:00
// Consider either using [filepath.EvalSymlinks] to canonicalize the path (as the Unix VFS does).
// Or using [os.Readlink] to resolve a symbolic link (as the Unix VFS did).
// This might be buggy on Windows (the Windows VFS doesn't try).
2023-02-27 03:20:23 +00:00
size := uint64(len(abs) + 1)
if size > uint64(nFull) {
2023-01-25 14:59:02 +00:00
return uint32(CANTOPEN_FULLPATH)
}
2023-02-14 11:34:24 +00:00
mem := memory{mod}.view(zFull, size)
2023-01-18 11:40:08 +00:00
2023-01-22 15:44:39 +00:00
mem[len(abs)] = 0
copy(mem, abs)
2023-01-18 11:40:08 +00:00
return _OK
}
2023-01-18 18:58:49 +00:00
2023-01-22 15:44:39 +00:00
func vfsDelete(ctx context.Context, mod api.Module, pVfs, zPath, syncDir uint32) uint32 {
2023-01-26 14:52:38 +00:00
path := memory{mod}.readString(zPath, _MAX_PATHNAME)
2023-01-22 15:44:39 +00:00
err := os.Remove(path)
2023-01-19 01:12:09 +00:00
if errors.Is(err, fs.ErrNotExist) {
return _OK
}
if err != nil {
return uint32(IOERR_DELETE)
}
2023-01-26 18:20:54 +00:00
if runtime.GOOS != "windows" && syncDir != 0 {
2023-01-22 15:44:39 +00:00
f, err := os.Open(filepath.Dir(path))
2023-01-19 01:12:09 +00:00
if err == nil {
err = f.Sync()
f.Close()
}
if err != nil {
return uint32(IOERR_DELETE)
}
}
return _OK
}
2023-01-18 18:58:49 +00:00
2023-02-18 03:43:17 +00:00
func vfsAccess(ctx context.Context, mod api.Module, pVfs, zPath uint32, flags _AccessFlag, pResOut uint32) uint32 {
2023-01-23 14:00:37 +00:00
// Consider using [syscall.Access] for [ACCESS_READWRITE]/[ACCESS_READ]
// (as the Unix VFS does).
2023-01-26 14:52:38 +00:00
path := memory{mod}.readString(zPath, _MAX_PATHNAME)
2023-01-22 15:44:39 +00:00
fi, err := os.Stat(path)
2023-01-19 00:18:39 +00:00
var res uint32
2023-01-25 14:59:02 +00:00
switch {
2023-02-18 03:43:17 +00:00
case flags == _ACCESS_EXISTS:
2023-01-19 00:18:39 +00:00
switch {
case err == nil:
res = 1
case errors.Is(err, fs.ErrNotExist):
res = 0
default:
return uint32(IOERR_ACCESS)
}
2023-01-25 14:59:02 +00:00
case err == nil:
2023-01-19 13:27:32 +00:00
var want fs.FileMode = syscall.S_IRUSR
2023-02-18 03:43:17 +00:00
if flags == _ACCESS_READWRITE {
2023-01-19 13:27:32 +00:00
want |= syscall.S_IWUSR
2023-01-19 00:18:39 +00:00
}
2023-01-19 13:27:32 +00:00
if fi.IsDir() {
want |= syscall.S_IXUSR
}
if fi.Mode()&want == want {
2023-01-19 00:18:39 +00:00
res = 1
} else {
res = 0
}
2023-01-25 14:59:02 +00:00
case errors.Is(err, fs.ErrPermission):
2023-01-19 13:27:32 +00:00
res = 0
2023-01-25 14:59:02 +00:00
default:
2023-01-19 13:27:32 +00:00
return uint32(IOERR_ACCESS)
2023-01-19 00:18:39 +00:00
}
2023-01-26 14:52:38 +00:00
memory{mod}.writeUint32(pResOut, res)
2023-01-19 00:18:39 +00:00
return _OK
}
2023-01-18 18:58:49 +00:00
2023-01-25 14:59:02 +00:00
func vfsOpen(ctx context.Context, mod api.Module, pVfs, zName, pFile uint32, flags OpenFlag, pOutFlags uint32) uint32 {
2023-01-18 18:58:49 +00:00
var oflags int
2023-01-25 14:59:02 +00:00
if flags&OPEN_EXCLUSIVE != 0 {
2023-01-18 18:58:49 +00:00
oflags |= os.O_EXCL
}
2023-01-25 14:59:02 +00:00
if flags&OPEN_CREATE != 0 {
2023-01-18 18:58:49 +00:00
oflags |= os.O_CREATE
}
2023-01-25 14:59:02 +00:00
if flags&OPEN_READONLY != 0 {
2023-01-18 18:58:49 +00:00
oflags |= os.O_RDONLY
}
2023-01-25 14:59:02 +00:00
if flags&OPEN_READWRITE != 0 {
2023-01-18 18:58:49 +00:00
oflags |= os.O_RDWR
}
2023-01-25 14:59:02 +00:00
var err error
var file *os.File
if zName == 0 {
file, err = os.CreateTemp("", "*.db")
} else {
2023-01-26 14:52:38 +00:00
name := memory{mod}.readString(zName, _MAX_PATHNAME)
2023-01-25 14:59:02 +00:00
file, err = os.OpenFile(name, oflags, 0600)
}
2023-01-18 18:58:49 +00:00
if err != nil {
return uint32(CANTOPEN)
}
2023-01-25 14:59:02 +00:00
if flags&OPEN_DELETEONCLOSE != 0 {
2023-02-23 13:29:51 +00:00
vfsOS.DeleteOnClose(file)
2023-01-25 14:59:02 +00:00
}
2023-03-07 10:45:11 +00:00
vfsFile.Open(ctx, mod, pFile, file)
2023-01-22 17:33:21 +00:00
2023-01-26 14:52:38 +00:00
if pOutFlags != 0 {
memory{mod}.writeUint32(pOutFlags, uint32(flags))
2023-01-19 00:25:56 +00:00
}
2023-01-18 18:58:49 +00:00
return _OK
}
2023-01-22 15:44:39 +00:00
func vfsClose(ctx context.Context, mod api.Module, pFile uint32) uint32 {
2023-03-07 10:45:11 +00:00
err := vfsFile.Close(ctx, mod, pFile)
2023-01-18 18:58:49 +00:00
if err != nil {
2023-01-19 00:18:39 +00:00
return uint32(IOERR_CLOSE)
2023-01-18 18:58:49 +00:00
}
return _OK
}
2023-01-22 15:44:39 +00:00
func vfsRead(ctx context.Context, mod api.Module, pFile, zBuf, iAmt uint32, iOfst uint64) uint32 {
2023-02-27 03:20:23 +00:00
buf := memory{mod}.view(zBuf, uint64(iAmt))
2023-01-18 23:45:22 +00:00
2023-03-07 10:45:11 +00:00
file := vfsFile.GetOS(ctx, mod, pFile)
2023-01-25 14:59:02 +00:00
n, err := file.ReadAt(buf, int64(iOfst))
2023-01-18 23:45:22 +00:00
if n == int(iAmt) {
return _OK
}
if n == 0 && err != io.EOF {
return uint32(IOERR_READ)
}
2023-01-25 14:59:02 +00:00
for i := range buf[n:] {
2023-01-29 02:11:41 +00:00
buf[n+i] = 0
2023-01-18 23:45:22 +00:00
}
return uint32(IOERR_SHORT_READ)
2023-01-18 18:58:49 +00:00
}
2023-01-22 15:44:39 +00:00
func vfsWrite(ctx context.Context, mod api.Module, pFile, zBuf, iAmt uint32, iOfst uint64) uint32 {
2023-02-27 03:20:23 +00:00
buf := memory{mod}.view(zBuf, uint64(iAmt))
2023-01-19 00:58:57 +00:00
2023-03-07 10:45:11 +00:00
file := vfsFile.GetOS(ctx, mod, pFile)
2023-01-25 14:59:02 +00:00
_, err := file.WriteAt(buf, int64(iOfst))
2023-01-19 00:58:57 +00:00
if err != nil {
return uint32(IOERR_WRITE)
}
return _OK
}
2023-01-18 18:58:49 +00:00
2023-01-22 15:44:39 +00:00
func vfsTruncate(ctx context.Context, mod api.Module, pFile uint32, nByte uint64) uint32 {
2023-03-07 10:45:11 +00:00
file := vfsFile.GetOS(ctx, mod, pFile)
2023-01-22 15:44:39 +00:00
err := file.Truncate(int64(nByte))
2023-01-19 01:12:09 +00:00
if err != nil {
return uint32(IOERR_TRUNCATE)
}
return _OK
}
2023-01-18 18:58:49 +00:00
2023-01-22 15:44:39 +00:00
func vfsSync(ctx context.Context, mod api.Module, pFile, flags uint32) uint32 {
2023-03-07 10:45:11 +00:00
file := vfsFile.GetOS(ctx, mod, pFile)
2023-01-22 15:44:39 +00:00
err := file.Sync()
2023-01-19 00:58:57 +00:00
if err != nil {
return uint32(IOERR_FSYNC)
}
return _OK
}
2023-01-18 18:58:49 +00:00
2023-01-22 15:44:39 +00:00
func vfsFileSize(ctx context.Context, mod api.Module, pFile, pSize uint32) uint32 {
2023-02-07 03:11:59 +00:00
// This uses [os.File.Seek] because we don't care about the offset for reading/writing.
// But consider using [os.File.Stat] instead (as other VFSes do).
2023-01-25 14:59:02 +00:00
2023-03-07 10:45:11 +00:00
file := vfsFile.GetOS(ctx, mod, pFile)
2023-01-22 15:44:39 +00:00
off, err := file.Seek(0, io.SeekEnd)
2023-01-19 00:25:56 +00:00
if err != nil {
return uint32(IOERR_SEEK)
}
2023-01-26 14:52:38 +00:00
memory{mod}.writeUint64(pSize, uint64(off))
2023-01-19 00:25:56 +00:00
return _OK
}
2023-02-13 13:52:52 +00:00
func vfsFileControl(ctx context.Context, pFile, op, pArg uint32) uint32 {
// SQLite calls vfsFileControl with these opcodes:
// SQLITE_FCNTL_SIZE_HINT
// SQLITE_FCNTL_PRAGMA
// SQLITE_FCNTL_BUSYHANDLER
// SQLITE_FCNTL_HAS_MOVED
// SQLITE_FCNTL_SYNC
// SQLITE_FCNTL_COMMIT_PHASETWO
// SQLITE_FCNTL_PDB
return uint32(NOTFOUND)
}