gitlab.com/CoiaPrant/sqlite3@v1.19.1/vfs/patches.go (about)

     1  // Copyright 2022 The Sqlite Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package vfs
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  	"io/fs"
    11  	"os"
    12  	"sync"
    13  	"sync/atomic"
    14  	"unsafe"
    15  
    16  	"modernc.org/libc"
    17  	sqlite3 "gitlab.com/CoiaPrant/sqlite3/lib"
    18  )
    19  
    20  var (
    21  	fToken uintptr
    22  
    23  	// New, FS.Close
    24  	mu sync.Mutex
    25  
    26  	objectMu sync.Mutex
    27  	objects  = map[uintptr]interface{}{}
    28  )
    29  
    30  func token() uintptr { return atomic.AddUintptr(&fToken, 1) }
    31  
    32  func addObject(o interface{}) uintptr {
    33  	t := token()
    34  	objectMu.Lock()
    35  	objects[t] = o
    36  	objectMu.Unlock()
    37  	return t
    38  }
    39  
    40  func getObject(t uintptr) interface{} {
    41  	objectMu.Lock()
    42  	o := objects[t]
    43  	if o == nil {
    44  		panic("internal error")
    45  	}
    46  
    47  	objectMu.Unlock()
    48  	return o
    49  }
    50  
    51  func removeObject(t uintptr) {
    52  	objectMu.Lock()
    53  	if _, ok := objects[t]; !ok {
    54  		panic("internal error")
    55  	}
    56  
    57  	delete(objects, t)
    58  	objectMu.Unlock()
    59  }
    60  
    61  var vfsio = sqlite3_io_methods{
    62  	iVersion: 1, // iVersion
    63  }
    64  
    65  func vfsOpen(tls *libc.TLS, pVfs uintptr, zName uintptr, pFile uintptr, flags int32, pOutFlags uintptr) int32 {
    66  	if zName == 0 {
    67  		return sqlite3.SQLITE_IOERR
    68  	}
    69  
    70  	if flags&sqlite3.SQLITE_OPEN_MAIN_JOURNAL != 0 {
    71  		return sqlite3.SQLITE_NOMEM
    72  	}
    73  
    74  	p := pFile
    75  	*(*VFSFile)(unsafe.Pointer(p)) = VFSFile{}
    76  	fsys := getObject((*sqlite3_vfs)(unsafe.Pointer(pVfs)).pAppData).(fs.FS)
    77  	f, err := fsys.Open(libc.GoString(zName))
    78  	if err != nil {
    79  		return sqlite3.SQLITE_CANTOPEN
    80  	}
    81  
    82  	h := addObject(f)
    83  	(*VFSFile)(unsafe.Pointer(p)).fsFile = h
    84  	if pOutFlags != 0 {
    85  		*(*int32)(unsafe.Pointer(pOutFlags)) = int32(os.O_RDONLY)
    86  	}
    87  	(*VFSFile)(unsafe.Pointer(p)).base.pMethods = uintptr(unsafe.Pointer(&vfsio))
    88  	return sqlite3.SQLITE_OK
    89  }
    90  
    91  func vfsRead(tls *libc.TLS, pFile uintptr, zBuf uintptr, iAmt int32, iOfst sqlite_int64) int32 {
    92  	p := pFile
    93  	f := getObject((*VFSFile)(unsafe.Pointer(p)).fsFile).(fs.File)
    94  	seeker, ok := f.(io.Seeker)
    95  	if !ok {
    96  		return sqlite3.SQLITE_IOERR_READ
    97  	}
    98  
    99  	if n, err := seeker.Seek(iOfst, io.SeekStart); err != nil || n != iOfst {
   100  		return sqlite3.SQLITE_IOERR_READ
   101  	}
   102  
   103  	b := (*libc.RawMem)(unsafe.Pointer(zBuf))[:iAmt]
   104  	n, err := f.Read(b)
   105  	if n == int(iAmt) {
   106  		return sqlite3.SQLITE_OK
   107  	}
   108  
   109  	if n < int(iAmt) && err == nil {
   110  		b := b[n:]
   111  		for i := range b {
   112  			b[i] = 0
   113  		}
   114  		return sqlite3.SQLITE_IOERR_SHORT_READ
   115  	}
   116  
   117  	return sqlite3.SQLITE_IOERR_READ
   118  }
   119  
   120  func vfsAccess(tls *libc.TLS, pVfs uintptr, zPath uintptr, flags int32, pResOut uintptr) int32 {
   121  	if flags == sqlite3.SQLITE_ACCESS_READWRITE {
   122  		*(*int32)(unsafe.Pointer(pResOut)) = 0
   123  		return sqlite3.SQLITE_OK
   124  	}
   125  
   126  	fn := libc.GoString(zPath)
   127  	fsys := getObject((*sqlite3_vfs)(unsafe.Pointer(pVfs)).pAppData).(fs.FS)
   128  	if _, err := fs.Stat(fsys, fn); err != nil {
   129  		*(*int32)(unsafe.Pointer(pResOut)) = 0
   130  		return sqlite3.SQLITE_OK
   131  	}
   132  
   133  	*(*int32)(unsafe.Pointer(pResOut)) = 1
   134  	return sqlite3.SQLITE_OK
   135  }
   136  
   137  func vfsFileSize(tls *libc.TLS, pFile uintptr, pSize uintptr) int32 {
   138  	p := pFile
   139  	f := getObject((*VFSFile)(unsafe.Pointer(p)).fsFile).(fs.File)
   140  	fi, err := f.Stat()
   141  	if err != nil {
   142  		return sqlite3.SQLITE_IOERR_FSTAT
   143  	}
   144  
   145  	*(*sqlite_int64)(unsafe.Pointer(pSize)) = fi.Size()
   146  	return sqlite3.SQLITE_OK
   147  }
   148  
   149  func vfsClose(tls *libc.TLS, pFile uintptr) int32 {
   150  	p := pFile
   151  	h := (*VFSFile)(unsafe.Pointer(p)).fsFile
   152  	f := getObject(h).(fs.File)
   153  	f.Close()
   154  	removeObject(h)
   155  	return sqlite3.SQLITE_OK
   156  }
   157  
   158  // FS represents a SQLite read only file system backed by Go's fs.FS.
   159  type FS struct {
   160  	cname    uintptr
   161  	cvfs     uintptr
   162  	fsHandle uintptr
   163  	name     string
   164  	tls      *libc.TLS
   165  
   166  	closed int32
   167  }
   168  
   169  // New creates a new sqlite VFS and registers it. If successful, the
   170  // file system can be used with the URI parameter `?vfs=<returned name>`.
   171  func New(fs fs.FS) (name string, _ *FS, _ error) {
   172  	if fs == nil {
   173  		return "", nil, fmt.Errorf("fs argument cannot be nil")
   174  	}
   175  
   176  	mu.Lock()
   177  
   178  	defer mu.Unlock()
   179  
   180  	tls := libc.NewTLS()
   181  	h := addObject(fs)
   182  
   183  	name = fmt.Sprintf("vfs%x", h)
   184  	cname, err := libc.CString(name)
   185  	if err != nil {
   186  		return "", nil, err
   187  	}
   188  
   189  	vfs := Xsqlite3_fsFS(tls, cname, h)
   190  	if vfs == 0 {
   191  		removeObject(h)
   192  		libc.Xfree(tls, cname)
   193  		tls.Close()
   194  		return "", nil, fmt.Errorf("out of memory")
   195  	}
   196  
   197  	if rc := sqlite3.Xsqlite3_vfs_register(tls, vfs, libc.Bool32(false)); rc != sqlite3.SQLITE_OK {
   198  		removeObject(h)
   199  		libc.Xfree(tls, cname)
   200  		libc.Xfree(tls, vfs)
   201  		tls.Close()
   202  		return "", nil, fmt.Errorf("registering VFS %s: %d", name, rc)
   203  	}
   204  
   205  	return name, &FS{name: name, cname: cname, cvfs: vfs, fsHandle: h, tls: tls}, nil
   206  }
   207  
   208  // Close unregisters f and releases its resources.
   209  func (f *FS) Close() error {
   210  	if atomic.SwapInt32(&f.closed, 1) != 0 {
   211  		return nil
   212  	}
   213  
   214  	mu.Lock()
   215  
   216  	defer mu.Unlock()
   217  
   218  	rc := sqlite3.Xsqlite3_vfs_unregister(f.tls, f.cvfs)
   219  	libc.Xfree(f.tls, f.cname)
   220  	libc.Xfree(f.tls, f.cvfs)
   221  	f.tls.Close()
   222  	removeObject(f.fsHandle)
   223  	if rc != 0 {
   224  		return fmt.Errorf("unregistering VFS %s: %d", f.name, rc)
   225  	}
   226  
   227  	return nil
   228  }