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 }