github.com/ncruces/go-sqlite3@v0.15.1-0.20240520133447-53eef1510ff0/vfs/vfs.go (about) 1 package vfs 2 3 import ( 4 "context" 5 "crypto/rand" 6 "io" 7 "reflect" 8 "sync" 9 "time" 10 11 "github.com/ncruces/go-sqlite3/internal/util" 12 "github.com/ncruces/julianday" 13 "github.com/tetratelabs/wazero" 14 "github.com/tetratelabs/wazero/api" 15 ) 16 17 // ExportHostFunctions is an internal API users need not call directly. 18 // 19 // ExportHostFunctions registers the required VFS host functions 20 // with the provided env module. 21 func ExportHostFunctions(env wazero.HostModuleBuilder) wazero.HostModuleBuilder { 22 util.ExportFuncII(env, "go_vfs_find", vfsFind) 23 util.ExportFuncIIJ(env, "go_localtime", vfsLocaltime) 24 util.ExportFuncIIII(env, "go_randomness", vfsRandomness) 25 util.ExportFuncIII(env, "go_sleep", vfsSleep) 26 util.ExportFuncIII(env, "go_current_time_64", vfsCurrentTime64) 27 util.ExportFuncIIIII(env, "go_full_pathname", vfsFullPathname) 28 util.ExportFuncIIII(env, "go_delete", vfsDelete) 29 util.ExportFuncIIIII(env, "go_access", vfsAccess) 30 util.ExportFuncIIIIIII(env, "go_open", vfsOpen) 31 util.ExportFuncII(env, "go_close", vfsClose) 32 util.ExportFuncIIIIJ(env, "go_read", vfsRead) 33 util.ExportFuncIIIIJ(env, "go_write", vfsWrite) 34 util.ExportFuncIIJ(env, "go_truncate", vfsTruncate) 35 util.ExportFuncIII(env, "go_sync", vfsSync) 36 util.ExportFuncIII(env, "go_file_size", vfsFileSize) 37 util.ExportFuncIIII(env, "go_file_control", vfsFileControl) 38 util.ExportFuncII(env, "go_sector_size", vfsSectorSize) 39 util.ExportFuncII(env, "go_device_characteristics", vfsDeviceCharacteristics) 40 util.ExportFuncIII(env, "go_lock", vfsLock) 41 util.ExportFuncIII(env, "go_unlock", vfsUnlock) 42 util.ExportFuncIII(env, "go_check_reserved_lock", vfsCheckReservedLock) 43 util.ExportFuncIIIIII(env, "go_shm_map", vfsShmMap) 44 util.ExportFuncIIIII(env, "go_shm_lock", vfsShmLock) 45 util.ExportFuncIII(env, "go_shm_unmap", vfsShmUnmap) 46 util.ExportFuncVI(env, "go_shm_barrier", vfsShmBarrier) 47 return env 48 } 49 50 func vfsFind(ctx context.Context, mod api.Module, zVfsName uint32) uint32 { 51 name := util.ReadString(mod, zVfsName, _MAX_NAME) 52 if vfs := Find(name); vfs != nil && vfs != (vfsOS{}) { 53 return 1 54 } 55 return 0 56 } 57 58 func vfsLocaltime(ctx context.Context, mod api.Module, pTm uint32, t int64) _ErrorCode { 59 tm := time.Unix(t, 0) 60 var isdst int 61 if tm.IsDST() { 62 isdst = 1 63 } 64 65 const size = 32 / 8 66 // https://pubs.opengroup.org/onlinepubs/7908799/xsh/time.h.html 67 util.WriteUint32(mod, pTm+0*size, uint32(tm.Second())) 68 util.WriteUint32(mod, pTm+1*size, uint32(tm.Minute())) 69 util.WriteUint32(mod, pTm+2*size, uint32(tm.Hour())) 70 util.WriteUint32(mod, pTm+3*size, uint32(tm.Day())) 71 util.WriteUint32(mod, pTm+4*size, uint32(tm.Month()-time.January)) 72 util.WriteUint32(mod, pTm+5*size, uint32(tm.Year()-1900)) 73 util.WriteUint32(mod, pTm+6*size, uint32(tm.Weekday()-time.Sunday)) 74 util.WriteUint32(mod, pTm+7*size, uint32(tm.YearDay()-1)) 75 util.WriteUint32(mod, pTm+8*size, uint32(isdst)) 76 return _OK 77 } 78 79 func vfsRandomness(ctx context.Context, mod api.Module, pVfs uint32, nByte int32, zByte uint32) uint32 { 80 mem := util.View(mod, zByte, uint64(nByte)) 81 n, _ := rand.Reader.Read(mem) 82 return uint32(n) 83 } 84 85 func vfsSleep(ctx context.Context, mod api.Module, pVfs uint32, nMicro int32) _ErrorCode { 86 osSleep(time.Duration(nMicro) * time.Microsecond) 87 return _OK 88 } 89 90 func vfsCurrentTime64(ctx context.Context, mod api.Module, pVfs, piNow uint32) _ErrorCode { 91 day, nsec := julianday.Date(time.Now()) 92 msec := day*86_400_000 + nsec/1_000_000 93 util.WriteUint64(mod, piNow, uint64(msec)) 94 return _OK 95 } 96 97 func vfsFullPathname(ctx context.Context, mod api.Module, pVfs, zRelative uint32, nFull int32, zFull uint32) _ErrorCode { 98 vfs := vfsGet(mod, pVfs) 99 path := util.ReadString(mod, zRelative, _MAX_PATHNAME) 100 101 path, err := vfs.FullPathname(path) 102 103 if len(path) >= int(nFull) { 104 return _CANTOPEN_FULLPATH 105 } 106 util.WriteString(mod, zFull, path) 107 108 return vfsErrorCode(err, _CANTOPEN_FULLPATH) 109 } 110 111 func vfsDelete(ctx context.Context, mod api.Module, pVfs, zPath, syncDir uint32) _ErrorCode { 112 vfs := vfsGet(mod, pVfs) 113 path := util.ReadString(mod, zPath, _MAX_PATHNAME) 114 115 err := vfs.Delete(path, syncDir != 0) 116 return vfsErrorCode(err, _IOERR_DELETE) 117 } 118 119 func vfsAccess(ctx context.Context, mod api.Module, pVfs, zPath uint32, flags AccessFlag, pResOut uint32) _ErrorCode { 120 vfs := vfsGet(mod, pVfs) 121 path := util.ReadString(mod, zPath, _MAX_PATHNAME) 122 123 ok, err := vfs.Access(path, flags) 124 var res uint32 125 if ok { 126 res = 1 127 } 128 129 util.WriteUint32(mod, pResOut, res) 130 return vfsErrorCode(err, _IOERR_ACCESS) 131 } 132 133 func vfsOpen(ctx context.Context, mod api.Module, pVfs, zPath, pFile uint32, flags OpenFlag, pOutFlags, pOutVFS uint32) _ErrorCode { 134 vfs := vfsGet(mod, pVfs) 135 136 var path string 137 if zPath != 0 { 138 path = util.ReadString(mod, zPath, _MAX_PATHNAME) 139 } 140 141 var file File 142 var err error 143 if ffs, ok := vfs.(VFSFilename); ok { 144 name := OpenFilename(ctx, mod, zPath, flags) 145 file, flags, err = ffs.OpenFilename(name, flags) 146 } else { 147 file, flags, err = vfs.Open(path, flags) 148 } 149 if err != nil { 150 return vfsErrorCode(err, _CANTOPEN) 151 } 152 153 if file, ok := file.(FilePowersafeOverwrite); ok { 154 name := OpenFilename(ctx, mod, zPath, flags) 155 if b, ok := util.ParseBool(name.URIParameter("psow")); ok { 156 file.SetPowersafeOverwrite(b) 157 } 158 } 159 if file, ok := file.(FileSharedMemory); ok && 160 pOutVFS != 0 && file.SharedMemory() != nil { 161 util.WriteUint32(mod, pOutVFS, 1) 162 } 163 if pOutFlags != 0 { 164 util.WriteUint32(mod, pOutFlags, uint32(flags)) 165 } 166 vfsFileRegister(ctx, mod, pFile, file) 167 return _OK 168 } 169 170 func vfsClose(ctx context.Context, mod api.Module, pFile uint32) _ErrorCode { 171 err := vfsFileClose(ctx, mod, pFile) 172 if err != nil { 173 return vfsErrorCode(err, _IOERR_CLOSE) 174 } 175 return _OK 176 } 177 178 func vfsRead(ctx context.Context, mod api.Module, pFile, zBuf uint32, iAmt int32, iOfst int64) _ErrorCode { 179 file := vfsFileGet(ctx, mod, pFile).(File) 180 buf := util.View(mod, zBuf, uint64(iAmt)) 181 182 n, err := file.ReadAt(buf, iOfst) 183 if n == int(iAmt) { 184 return _OK 185 } 186 if err != io.EOF { 187 return vfsErrorCode(err, _IOERR_READ) 188 } 189 clear(buf[n:]) 190 return _IOERR_SHORT_READ 191 } 192 193 func vfsWrite(ctx context.Context, mod api.Module, pFile, zBuf uint32, iAmt int32, iOfst int64) _ErrorCode { 194 file := vfsFileGet(ctx, mod, pFile).(File) 195 buf := util.View(mod, zBuf, uint64(iAmt)) 196 197 _, err := file.WriteAt(buf, iOfst) 198 if err != nil { 199 return vfsErrorCode(err, _IOERR_WRITE) 200 } 201 return _OK 202 } 203 204 func vfsTruncate(ctx context.Context, mod api.Module, pFile uint32, nByte int64) _ErrorCode { 205 file := vfsFileGet(ctx, mod, pFile).(File) 206 err := file.Truncate(nByte) 207 return vfsErrorCode(err, _IOERR_TRUNCATE) 208 } 209 210 func vfsSync(ctx context.Context, mod api.Module, pFile uint32, flags SyncFlag) _ErrorCode { 211 file := vfsFileGet(ctx, mod, pFile).(File) 212 err := file.Sync(flags) 213 return vfsErrorCode(err, _IOERR_FSYNC) 214 } 215 216 func vfsFileSize(ctx context.Context, mod api.Module, pFile, pSize uint32) _ErrorCode { 217 file := vfsFileGet(ctx, mod, pFile).(File) 218 size, err := file.Size() 219 util.WriteUint64(mod, pSize, uint64(size)) 220 return vfsErrorCode(err, _IOERR_SEEK) 221 } 222 223 func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock LockLevel) _ErrorCode { 224 file := vfsFileGet(ctx, mod, pFile).(File) 225 err := file.Lock(eLock) 226 return vfsErrorCode(err, _IOERR_LOCK) 227 } 228 229 func vfsUnlock(ctx context.Context, mod api.Module, pFile uint32, eLock LockLevel) _ErrorCode { 230 file := vfsFileGet(ctx, mod, pFile).(File) 231 err := file.Unlock(eLock) 232 return vfsErrorCode(err, _IOERR_UNLOCK) 233 } 234 235 func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut uint32) _ErrorCode { 236 file := vfsFileGet(ctx, mod, pFile).(File) 237 locked, err := file.CheckReservedLock() 238 239 var res uint32 240 if locked { 241 res = 1 242 } 243 244 util.WriteUint32(mod, pResOut, res) 245 return vfsErrorCode(err, _IOERR_CHECKRESERVEDLOCK) 246 } 247 248 func vfsFileControl(ctx context.Context, mod api.Module, pFile uint32, op _FcntlOpcode, pArg uint32) _ErrorCode { 249 file := vfsFileGet(ctx, mod, pFile).(File) 250 251 switch op { 252 case _FCNTL_LOCKSTATE: 253 if file, ok := file.(FileLockState); ok { 254 util.WriteUint32(mod, pArg, uint32(file.LockState())) 255 return _OK 256 } 257 258 case _FCNTL_PERSIST_WAL: 259 if file, ok := file.(FilePersistentWAL); ok { 260 if i := util.ReadUint32(mod, pArg); int32(i) >= 0 { 261 file.SetPersistentWAL(i != 0) 262 } else if file.PersistentWAL() { 263 util.WriteUint32(mod, pArg, 1) 264 } else { 265 util.WriteUint32(mod, pArg, 0) 266 } 267 return _OK 268 } 269 270 case _FCNTL_POWERSAFE_OVERWRITE: 271 if file, ok := file.(FilePowersafeOverwrite); ok { 272 if i := util.ReadUint32(mod, pArg); int32(i) >= 0 { 273 file.SetPowersafeOverwrite(i != 0) 274 } else if file.PowersafeOverwrite() { 275 util.WriteUint32(mod, pArg, 1) 276 } else { 277 util.WriteUint32(mod, pArg, 0) 278 } 279 return _OK 280 } 281 282 case _FCNTL_CHUNK_SIZE: 283 if file, ok := file.(FileChunkSize); ok { 284 size := util.ReadUint32(mod, pArg) 285 file.ChunkSize(int(size)) 286 return _OK 287 } 288 289 case _FCNTL_SIZE_HINT: 290 if file, ok := file.(FileSizeHint); ok { 291 size := util.ReadUint64(mod, pArg) 292 err := file.SizeHint(int64(size)) 293 return vfsErrorCode(err, _IOERR_TRUNCATE) 294 } 295 296 case _FCNTL_HAS_MOVED: 297 if file, ok := file.(FileHasMoved); ok { 298 moved, err := file.HasMoved() 299 var res uint32 300 if moved { 301 res = 1 302 } 303 util.WriteUint32(mod, pArg, res) 304 return vfsErrorCode(err, _IOERR_FSTAT) 305 } 306 307 case _FCNTL_OVERWRITE: 308 if file, ok := file.(FileOverwrite); ok { 309 err := file.Overwrite() 310 return vfsErrorCode(err, _IOERR) 311 } 312 313 case _FCNTL_COMMIT_PHASETWO: 314 if file, ok := file.(FileCommitPhaseTwo); ok { 315 err := file.CommitPhaseTwo() 316 return vfsErrorCode(err, _IOERR) 317 } 318 319 case _FCNTL_BEGIN_ATOMIC_WRITE: 320 if file, ok := file.(FileBatchAtomicWrite); ok { 321 err := file.BeginAtomicWrite() 322 return vfsErrorCode(err, _IOERR_BEGIN_ATOMIC) 323 } 324 case _FCNTL_COMMIT_ATOMIC_WRITE: 325 if file, ok := file.(FileBatchAtomicWrite); ok { 326 err := file.CommitAtomicWrite() 327 return vfsErrorCode(err, _IOERR_COMMIT_ATOMIC) 328 } 329 case _FCNTL_ROLLBACK_ATOMIC_WRITE: 330 if file, ok := file.(FileBatchAtomicWrite); ok { 331 err := file.RollbackAtomicWrite() 332 return vfsErrorCode(err, _IOERR_ROLLBACK_ATOMIC) 333 } 334 335 case _FCNTL_CKPT_DONE: 336 if file, ok := file.(FileCheckpoint); ok { 337 err := file.CheckpointDone() 338 return vfsErrorCode(err, _IOERR) 339 } 340 case _FCNTL_CKPT_START: 341 if file, ok := file.(FileCheckpoint); ok { 342 err := file.CheckpointStart() 343 return vfsErrorCode(err, _IOERR) 344 } 345 346 case _FCNTL_PRAGMA: 347 if file, ok := file.(FilePragma); ok { 348 ptr := util.ReadUint32(mod, pArg+1*ptrlen) 349 name := util.ReadString(mod, ptr, _MAX_SQL_LENGTH) 350 var value string 351 if ptr := util.ReadUint32(mod, pArg+2*ptrlen); ptr != 0 { 352 value = util.ReadString(mod, ptr, _MAX_SQL_LENGTH) 353 } 354 355 out, err := file.Pragma(name, value) 356 357 ret := vfsErrorCode(err, _ERROR) 358 if ret == _ERROR { 359 out = err.Error() 360 } 361 if out != "" { 362 fn := mod.ExportedFunction("malloc") 363 stack := [...]uint64{uint64(len(out) + 1)} 364 if err := fn.CallWithStack(ctx, stack[:]); err != nil { 365 panic(err) 366 } 367 util.WriteUint32(mod, pArg, uint32(stack[0])) 368 util.WriteString(mod, uint32(stack[0]), out) 369 } 370 return ret 371 } 372 } 373 374 // Consider also implementing these opcodes (in use by SQLite): 375 // _FCNTL_BUSYHANDLER 376 // _FCNTL_LAST_ERRNO 377 // _FCNTL_SYNC 378 return _NOTFOUND 379 } 380 381 func vfsSectorSize(ctx context.Context, mod api.Module, pFile uint32) uint32 { 382 file := vfsFileGet(ctx, mod, pFile).(File) 383 return uint32(file.SectorSize()) 384 } 385 386 func vfsDeviceCharacteristics(ctx context.Context, mod api.Module, pFile uint32) DeviceCharacteristic { 387 file := vfsFileGet(ctx, mod, pFile).(File) 388 return file.DeviceCharacteristics() 389 } 390 391 var shmBarrier sync.Mutex 392 393 func vfsShmBarrier(ctx context.Context, mod api.Module, pFile uint32) { 394 shmBarrier.Lock() 395 defer shmBarrier.Unlock() 396 } 397 398 func vfsShmMap(ctx context.Context, mod api.Module, pFile uint32, iRegion, szRegion int32, bExtend, pp uint32) _ErrorCode { 399 shm := vfsFileGet(ctx, mod, pFile).(FileSharedMemory).SharedMemory() 400 p, err := shm.shmMap(ctx, mod, iRegion, szRegion, bExtend != 0) 401 if err != nil { 402 return vfsErrorCode(err, _IOERR_SHMMAP) 403 } 404 util.WriteUint32(mod, pp, p) 405 return _OK 406 } 407 408 func vfsShmLock(ctx context.Context, mod api.Module, pFile uint32, offset, n int32, flags _ShmFlag) _ErrorCode { 409 shm := vfsFileGet(ctx, mod, pFile).(FileSharedMemory).SharedMemory() 410 err := shm.shmLock(offset, n, flags) 411 return vfsErrorCode(err, _IOERR_SHMLOCK) 412 } 413 414 func vfsShmUnmap(ctx context.Context, mod api.Module, pFile, bDelete uint32) _ErrorCode { 415 shm := vfsFileGet(ctx, mod, pFile).(FileSharedMemory).SharedMemory() 416 shm.shmUnmap(bDelete != 0) 417 return _OK 418 } 419 420 func vfsGet(mod api.Module, pVfs uint32) VFS { 421 var name string 422 if pVfs != 0 { 423 const zNameOffset = 16 424 name = util.ReadString(mod, util.ReadUint32(mod, pVfs+zNameOffset), _MAX_NAME) 425 } 426 if vfs := Find(name); vfs != nil { 427 return vfs 428 } 429 panic(util.NoVFSErr + util.ErrorString(name)) 430 } 431 432 func vfsFileRegister(ctx context.Context, mod api.Module, pFile uint32, file File) { 433 const fileHandleOffset = 4 434 id := util.AddHandle(ctx, file) 435 util.WriteUint32(mod, pFile+fileHandleOffset, id) 436 } 437 438 func vfsFileGet(ctx context.Context, mod api.Module, pFile uint32) any { 439 const fileHandleOffset = 4 440 id := util.ReadUint32(mod, pFile+fileHandleOffset) 441 return util.GetHandle(ctx, id) 442 } 443 444 func vfsFileClose(ctx context.Context, mod api.Module, pFile uint32) error { 445 const fileHandleOffset = 4 446 id := util.ReadUint32(mod, pFile+fileHandleOffset) 447 return util.DelHandle(ctx, id) 448 } 449 450 func vfsErrorCode(err error, def _ErrorCode) _ErrorCode { 451 if err == nil { 452 return _OK 453 } 454 switch v := reflect.ValueOf(err); v.Kind() { 455 case reflect.Uint8, reflect.Uint16, reflect.Uint32: 456 return _ErrorCode(v.Uint()) 457 } 458 return def 459 }