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  }