0
0
mirror of https://gitlab.com/cznic/sqlite.git synced 2025-05-17 23:26:41 +00:00
Files
go-sqlite/vfs/patches.go

229 lines
4.8 KiB
Go
Raw Normal View History

2022-07-22 14:45:40 +02:00
// Copyright 2022 The Sqlite Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
2022-07-23 11:24:08 +02:00
package vfs
2022-07-22 14:45:40 +02:00
import (
2022-07-23 12:46:41 +02:00
"fmt"
2022-07-22 14:45:40 +02:00
"io"
"io/fs"
"os"
"sync"
"sync/atomic"
"unsafe"
"modernc.org/libc"
sqlite3 "modernc.org/sqlite/lib"
2022-07-22 14:45:40 +02:00
)
var (
fToken uintptr
2022-07-23 12:46:41 +02:00
// New, FS.Close
mu sync.Mutex
2022-07-22 14:45:40 +02:00
objectMu sync.Mutex
objects = map[uintptr]interface{}{}
)
func token() uintptr { return atomic.AddUintptr(&fToken, 1) }
func addObject(o interface{}) uintptr {
t := token()
objectMu.Lock()
objects[t] = o
objectMu.Unlock()
return t
}
func getObject(t uintptr) interface{} {
objectMu.Lock()
o := objects[t]
if o == nil {
panic("internal error")
}
objectMu.Unlock()
return o
}
func removeObject(t uintptr) {
objectMu.Lock()
if _, ok := objects[t]; !ok {
panic("internal error")
}
delete(objects, t)
objectMu.Unlock()
}
2022-07-23 11:24:08 +02:00
var vfsio = sqlite3_io_methods{
2022-07-22 14:45:40 +02:00
iVersion: 1, // iVersion
}
2022-07-23 11:24:08 +02:00
func vfsOpen(tls *libc.TLS, pVfs uintptr, zName uintptr, pFile uintptr, flags int32, pOutFlags uintptr) int32 {
2022-07-22 14:45:40 +02:00
if zName == 0 {
return sqlite3.SQLITE_IOERR
}
if flags&sqlite3.SQLITE_OPEN_MAIN_JOURNAL != 0 {
return sqlite3.SQLITE_NOMEM
}
p := pFile
2022-07-23 11:24:08 +02:00
*(*VFSFile)(unsafe.Pointer(p)) = VFSFile{}
2022-07-23 12:46:41 +02:00
fsys := getObject((*sqlite3_vfs)(unsafe.Pointer(pVfs)).pAppData).(fs.FS)
f, err := fsys.Open(libc.GoString(zName))
2022-07-22 14:45:40 +02:00
if err != nil {
return sqlite3.SQLITE_CANTOPEN
}
h := addObject(f)
2022-07-23 11:24:08 +02:00
(*VFSFile)(unsafe.Pointer(p)).fsFile = h
2022-07-22 14:45:40 +02:00
if pOutFlags != 0 {
*(*int32)(unsafe.Pointer(pOutFlags)) = int32(os.O_RDONLY)
}
2022-07-23 11:24:08 +02:00
(*VFSFile)(unsafe.Pointer(p)).base.pMethods = uintptr(unsafe.Pointer(&vfsio))
2022-07-22 14:45:40 +02:00
return sqlite3.SQLITE_OK
}
2022-07-23 11:24:08 +02:00
func vfsRead(tls *libc.TLS, pFile uintptr, zBuf uintptr, iAmt int32, iOfst sqlite_int64) int32 {
2022-07-22 14:45:40 +02:00
p := pFile
2022-07-23 11:24:08 +02:00
f := getObject((*VFSFile)(unsafe.Pointer(p)).fsFile).(fs.File)
2022-07-22 14:45:40 +02:00
seeker, ok := f.(io.Seeker)
if !ok {
return sqlite3.SQLITE_IOERR_READ
}
if n, err := seeker.Seek(iOfst, io.SeekStart); err != nil || n != iOfst {
return sqlite3.SQLITE_IOERR_READ
}
2022-07-24 20:05:45 +02:00
b := (*libc.RawMem)(unsafe.Pointer(zBuf))[:iAmt]
2022-07-22 14:45:40 +02:00
n, err := f.Read(b)
if n == int(iAmt) {
return sqlite3.SQLITE_OK
}
if n < int(iAmt) && err == nil {
b := b[n:]
for i := range b {
b[i] = 0
}
return sqlite3.SQLITE_IOERR_SHORT_READ
}
return sqlite3.SQLITE_IOERR_READ
}
2022-07-23 11:24:08 +02:00
func vfsAccess(tls *libc.TLS, pVfs uintptr, zPath uintptr, flags int32, pResOut uintptr) int32 {
2022-07-22 14:45:40 +02:00
if flags == sqlite3.SQLITE_ACCESS_READWRITE {
*(*int32)(unsafe.Pointer(pResOut)) = 0
return sqlite3.SQLITE_OK
}
fn := libc.GoString(zPath)
2022-07-23 12:46:41 +02:00
fsys := getObject((*sqlite3_vfs)(unsafe.Pointer(pVfs)).pAppData).(fs.FS)
if _, err := fs.Stat(fsys, fn); err != nil {
2022-07-22 14:45:40 +02:00
*(*int32)(unsafe.Pointer(pResOut)) = 0
return sqlite3.SQLITE_OK
}
*(*int32)(unsafe.Pointer(pResOut)) = 1
return sqlite3.SQLITE_OK
}
2022-07-23 11:24:08 +02:00
func vfsFileSize(tls *libc.TLS, pFile uintptr, pSize uintptr) int32 {
2022-07-22 14:45:40 +02:00
p := pFile
2022-07-23 11:24:08 +02:00
f := getObject((*VFSFile)(unsafe.Pointer(p)).fsFile).(fs.File)
2022-07-22 14:45:40 +02:00
fi, err := f.Stat()
if err != nil {
return sqlite3.SQLITE_IOERR_FSTAT
}
*(*sqlite_int64)(unsafe.Pointer(pSize)) = fi.Size()
return sqlite3.SQLITE_OK
}
2022-07-23 11:24:08 +02:00
func vfsClose(tls *libc.TLS, pFile uintptr) int32 {
2022-07-22 14:45:40 +02:00
p := pFile
2022-07-23 11:24:08 +02:00
h := (*VFSFile)(unsafe.Pointer(p)).fsFile
2022-07-22 14:45:40 +02:00
f := getObject(h).(fs.File)
f.Close()
removeObject(h)
return sqlite3.SQLITE_OK
}
2022-07-23 12:46:41 +02:00
// FS represents a SQLite read only file system backed by Go's fs.FS.
type FS struct {
cname uintptr
cvfs uintptr
fsHandle uintptr
name string
tls *libc.TLS
closed int32
}
// New creates a new sqlite VFS and registers it. If successful, the
// file system can be used with the URI parameter `?vfs=<returned name>`.
func New(fs fs.FS) (name string, _ *FS, _ error) {
2022-07-23 12:46:41 +02:00
if fs == nil {
return "", nil, fmt.Errorf("fs argument cannot be nil")
2022-07-23 12:46:41 +02:00
}
mu.Lock()
defer mu.Unlock()
2022-07-23 12:46:41 +02:00
tls := libc.NewTLS()
h := addObject(fs)
name = fmt.Sprintf("vfs%x", h)
cname, err := libc.CString(name)
if err != nil {
return "", nil, err
}
2022-07-23 12:46:41 +02:00
vfs := Xsqlite3_fsFS(tls, cname, h)
if vfs == 0 {
removeObject(h)
libc.Xfree(tls, cname)
tls.Close()
return "", nil, fmt.Errorf("out of memory")
2022-07-23 12:46:41 +02:00
}
if rc := sqlite3.Xsqlite3_vfs_register(tls, vfs, libc.Bool32(false)); rc != sqlite3.SQLITE_OK {
2022-07-23 12:46:41 +02:00
removeObject(h)
libc.Xfree(tls, cname)
2022-07-23 12:56:16 +02:00
libc.Xfree(tls, vfs)
2022-07-23 12:46:41 +02:00
tls.Close()
return "", nil, fmt.Errorf("registering VFS %s: %d", name, rc)
2022-07-23 12:46:41 +02:00
}
return name, &FS{name: name, cname: cname, cvfs: vfs, fsHandle: h, tls: tls}, nil
2022-07-23 12:46:41 +02:00
}
// Close unregisters f and releases its resources.
func (f *FS) Close() error {
if atomic.SwapInt32(&f.closed, 1) != 0 {
return nil
}
mu.Lock()
defer mu.Unlock()
rc := sqlite3.Xsqlite3_vfs_unregister(f.tls, f.cvfs)
libc.Xfree(f.tls, f.cname)
libc.Xfree(f.tls, f.cvfs)
f.tls.Close()
removeObject(f.fsHandle)
if rc != 0 {
return fmt.Errorf("unregistering VFS %s: %d", f.name, rc)
}
return nil
}