restructure to allow non-amalgamated builds again
[sqlcipher.git] / src / test_vfs.c
blobd1c34a38e4cede5be7078a8a25dff4b2f74b42e8
1 /*
2 ** 2010 May 05
3 **
4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
6 **
7 ** May you do good and not evil.
8 ** May you find forgiveness for yourself and forgive others.
9 ** May you share freely, never taking more than you give.
11 ******************************************************************************
13 ** This file contains the implementation of the Tcl [testvfs] command,
14 ** used to create SQLite VFS implementations with various properties and
15 ** instrumentation to support testing SQLite.
17 ** testvfs VFSNAME ?OPTIONS?
19 ** Available options are:
21 ** -noshm BOOLEAN (True to omit shm methods. Default false)
22 ** -default BOOLEAN (True to make the vfs default. Default false)
23 ** -szosfile INTEGER (Value for sqlite3_vfs.szOsFile)
24 ** -mxpathname INTEGER (Value for sqlite3_vfs.mxPathname)
25 ** -iversion INTEGER (Value for sqlite3_vfs.iVersion)
27 #if SQLITE_TEST /* This file is used for testing only */
29 #include "sqlite3.h"
30 #include "sqliteInt.h"
32 typedef struct Testvfs Testvfs;
33 typedef struct TestvfsShm TestvfsShm;
34 typedef struct TestvfsBuffer TestvfsBuffer;
35 typedef struct TestvfsFile TestvfsFile;
36 typedef struct TestvfsFd TestvfsFd;
39 ** An open file handle.
41 struct TestvfsFile {
42 sqlite3_file base; /* Base class. Must be first */
43 TestvfsFd *pFd; /* File data */
45 #define tvfsGetFd(pFile) (((TestvfsFile *)pFile)->pFd)
47 struct TestvfsFd {
48 sqlite3_vfs *pVfs; /* The VFS */
49 const char *zFilename; /* Filename as passed to xOpen() */
50 sqlite3_file *pReal; /* The real, underlying file descriptor */
51 Tcl_Obj *pShmId; /* Shared memory id for Tcl callbacks */
53 TestvfsBuffer *pShm; /* Shared memory buffer */
54 u32 excllock; /* Mask of exclusive locks */
55 u32 sharedlock; /* Mask of shared locks */
56 TestvfsFd *pNext; /* Next handle opened on the same file */
60 #define FAULT_INJECT_NONE 0
61 #define FAULT_INJECT_TRANSIENT 1
62 #define FAULT_INJECT_PERSISTENT 2
64 typedef struct TestFaultInject TestFaultInject;
65 struct TestFaultInject {
66 int iCnt; /* Remaining calls before fault injection */
67 int eFault; /* A FAULT_INJECT_* value */
68 int nFail; /* Number of faults injected */
72 ** An instance of this structure is allocated for each VFS created. The
73 ** sqlite3_vfs.pAppData field of the VFS structure registered with SQLite
74 ** is set to point to it.
76 struct Testvfs {
77 char *zName; /* Name of this VFS */
78 sqlite3_vfs *pParent; /* The VFS to use for file IO */
79 sqlite3_vfs *pVfs; /* The testvfs registered with SQLite */
80 Tcl_Interp *interp; /* Interpreter to run script in */
81 Tcl_Obj *pScript; /* Script to execute */
82 TestvfsBuffer *pBuffer; /* List of shared buffers */
83 int isNoshm;
85 int mask; /* Mask controlling [script] and [ioerr] */
87 TestFaultInject ioerr_err;
88 TestFaultInject full_err;
89 TestFaultInject cantopen_err;
91 #if 0
92 int iIoerrCnt;
93 int ioerr;
94 int nIoerrFail;
95 int iFullCnt;
96 int fullerr;
97 int nFullFail;
98 #endif
100 int iDevchar;
101 int iSectorsize;
105 ** The Testvfs.mask variable is set to a combination of the following.
106 ** If a bit is clear in Testvfs.mask, then calls made by SQLite to the
107 ** corresponding VFS method is ignored for purposes of:
109 ** + Simulating IO errors, and
110 ** + Invoking the Tcl callback script.
112 #define TESTVFS_SHMOPEN_MASK 0x00000001
113 #define TESTVFS_SHMLOCK_MASK 0x00000010
114 #define TESTVFS_SHMMAP_MASK 0x00000020
115 #define TESTVFS_SHMBARRIER_MASK 0x00000040
116 #define TESTVFS_SHMCLOSE_MASK 0x00000080
118 #define TESTVFS_OPEN_MASK 0x00000100
119 #define TESTVFS_SYNC_MASK 0x00000200
120 #define TESTVFS_DELETE_MASK 0x00000400
121 #define TESTVFS_CLOSE_MASK 0x00000800
122 #define TESTVFS_WRITE_MASK 0x00001000
123 #define TESTVFS_TRUNCATE_MASK 0x00002000
124 #define TESTVFS_ACCESS_MASK 0x00004000
125 #define TESTVFS_FULLPATHNAME_MASK 0x00008000
126 #define TESTVFS_READ_MASK 0x00010000
128 #define TESTVFS_ALL_MASK 0x0001FFFF
131 #define TESTVFS_MAX_PAGES 1024
134 ** A shared-memory buffer. There is one of these objects for each shared
135 ** memory region opened by clients. If two clients open the same file,
136 ** there are two TestvfsFile structures but only one TestvfsBuffer structure.
138 struct TestvfsBuffer {
139 char *zFile; /* Associated file name */
140 int pgsz; /* Page size */
141 u8 *aPage[TESTVFS_MAX_PAGES]; /* Array of ckalloc'd pages */
142 TestvfsFd *pFile; /* List of open handles */
143 TestvfsBuffer *pNext; /* Next in linked list of all buffers */
147 #define PARENTVFS(x) (((Testvfs *)((x)->pAppData))->pParent)
149 #define TESTVFS_MAX_ARGS 12
153 ** Method declarations for TestvfsFile.
155 static int tvfsClose(sqlite3_file*);
156 static int tvfsRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
157 static int tvfsWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
158 static int tvfsTruncate(sqlite3_file*, sqlite3_int64 size);
159 static int tvfsSync(sqlite3_file*, int flags);
160 static int tvfsFileSize(sqlite3_file*, sqlite3_int64 *pSize);
161 static int tvfsLock(sqlite3_file*, int);
162 static int tvfsUnlock(sqlite3_file*, int);
163 static int tvfsCheckReservedLock(sqlite3_file*, int *);
164 static int tvfsFileControl(sqlite3_file*, int op, void *pArg);
165 static int tvfsSectorSize(sqlite3_file*);
166 static int tvfsDeviceCharacteristics(sqlite3_file*);
169 ** Method declarations for tvfs_vfs.
171 static int tvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
172 static int tvfsDelete(sqlite3_vfs*, const char *zName, int syncDir);
173 static int tvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *);
174 static int tvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
175 #ifndef SQLITE_OMIT_LOAD_EXTENSION
176 static void *tvfsDlOpen(sqlite3_vfs*, const char *zFilename);
177 static void tvfsDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
178 static void (*tvfsDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void);
179 static void tvfsDlClose(sqlite3_vfs*, void*);
180 #endif /* SQLITE_OMIT_LOAD_EXTENSION */
181 static int tvfsRandomness(sqlite3_vfs*, int nByte, char *zOut);
182 static int tvfsSleep(sqlite3_vfs*, int microseconds);
183 static int tvfsCurrentTime(sqlite3_vfs*, double*);
185 static int tvfsShmOpen(sqlite3_file*);
186 static int tvfsShmLock(sqlite3_file*, int , int, int);
187 static int tvfsShmMap(sqlite3_file*,int,int,int, void volatile **);
188 static void tvfsShmBarrier(sqlite3_file*);
189 static int tvfsShmUnmap(sqlite3_file*, int);
191 static sqlite3_io_methods tvfs_io_methods = {
192 2, /* iVersion */
193 tvfsClose, /* xClose */
194 tvfsRead, /* xRead */
195 tvfsWrite, /* xWrite */
196 tvfsTruncate, /* xTruncate */
197 tvfsSync, /* xSync */
198 tvfsFileSize, /* xFileSize */
199 tvfsLock, /* xLock */
200 tvfsUnlock, /* xUnlock */
201 tvfsCheckReservedLock, /* xCheckReservedLock */
202 tvfsFileControl, /* xFileControl */
203 tvfsSectorSize, /* xSectorSize */
204 tvfsDeviceCharacteristics, /* xDeviceCharacteristics */
205 tvfsShmMap, /* xShmMap */
206 tvfsShmLock, /* xShmLock */
207 tvfsShmBarrier, /* xShmBarrier */
208 tvfsShmUnmap /* xShmUnmap */
211 static int tvfsResultCode(Testvfs *p, int *pRc){
212 struct errcode {
213 int eCode;
214 const char *zCode;
215 } aCode[] = {
216 { SQLITE_OK, "SQLITE_OK" },
217 { SQLITE_ERROR, "SQLITE_ERROR" },
218 { SQLITE_IOERR, "SQLITE_IOERR" },
219 { SQLITE_LOCKED, "SQLITE_LOCKED" },
220 { SQLITE_BUSY, "SQLITE_BUSY" },
223 const char *z;
224 int i;
226 z = Tcl_GetStringResult(p->interp);
227 for(i=0; i<ArraySize(aCode); i++){
228 if( 0==strcmp(z, aCode[i].zCode) ){
229 *pRc = aCode[i].eCode;
230 return 1;
234 return 0;
237 static int tvfsInjectFault(TestFaultInject *p){
238 int ret = 0;
239 if( p->eFault ){
240 p->iCnt--;
241 if( p->iCnt==0 || (p->iCnt<0 && p->eFault==FAULT_INJECT_PERSISTENT ) ){
242 ret = 1;
243 p->nFail++;
246 return ret;
250 static int tvfsInjectIoerr(Testvfs *p){
251 return tvfsInjectFault(&p->ioerr_err);
254 static int tvfsInjectFullerr(Testvfs *p){
255 return tvfsInjectFault(&p->full_err);
257 static int tvfsInjectCantopenerr(Testvfs *p){
258 return tvfsInjectFault(&p->cantopen_err);
262 static void tvfsExecTcl(
263 Testvfs *p,
264 const char *zMethod,
265 Tcl_Obj *arg1,
266 Tcl_Obj *arg2,
267 Tcl_Obj *arg3
269 int rc; /* Return code from Tcl_EvalObj() */
270 Tcl_Obj *pEval;
271 assert( p->pScript );
273 assert( zMethod );
274 assert( p );
275 assert( arg2==0 || arg1!=0 );
276 assert( arg3==0 || arg2!=0 );
278 pEval = Tcl_DuplicateObj(p->pScript);
279 Tcl_IncrRefCount(p->pScript);
280 Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zMethod, -1));
281 if( arg1 ) Tcl_ListObjAppendElement(p->interp, pEval, arg1);
282 if( arg2 ) Tcl_ListObjAppendElement(p->interp, pEval, arg2);
283 if( arg3 ) Tcl_ListObjAppendElement(p->interp, pEval, arg3);
285 rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
286 if( rc!=TCL_OK ){
287 Tcl_BackgroundError(p->interp);
288 Tcl_ResetResult(p->interp);
294 ** Close an tvfs-file.
296 static int tvfsClose(sqlite3_file *pFile){
297 int rc;
298 TestvfsFile *pTestfile = (TestvfsFile *)pFile;
299 TestvfsFd *pFd = pTestfile->pFd;
300 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
302 if( p->pScript && p->mask&TESTVFS_CLOSE_MASK ){
303 tvfsExecTcl(p, "xClose",
304 Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
308 if( pFd->pShmId ){
309 Tcl_DecrRefCount(pFd->pShmId);
310 pFd->pShmId = 0;
312 if( pFile->pMethods ){
313 ckfree((char *)pFile->pMethods);
315 rc = sqlite3OsClose(pFd->pReal);
316 ckfree((char *)pFd);
317 pTestfile->pFd = 0;
318 return rc;
322 ** Read data from an tvfs-file.
324 static int tvfsRead(
325 sqlite3_file *pFile,
326 void *zBuf,
327 int iAmt,
328 sqlite_int64 iOfst
330 int rc = SQLITE_OK;
331 TestvfsFd *pFd = tvfsGetFd(pFile);
332 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
333 if( p->pScript && p->mask&TESTVFS_READ_MASK ){
334 tvfsExecTcl(p, "xRead",
335 Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
337 tvfsResultCode(p, &rc);
339 if( rc==SQLITE_OK && p->mask&TESTVFS_READ_MASK && tvfsInjectIoerr(p) ){
340 rc = SQLITE_IOERR;
342 if( rc==SQLITE_OK ){
343 rc = sqlite3OsRead(pFd->pReal, zBuf, iAmt, iOfst);
345 return rc;
349 ** Write data to an tvfs-file.
351 static int tvfsWrite(
352 sqlite3_file *pFile,
353 const void *zBuf,
354 int iAmt,
355 sqlite_int64 iOfst
357 int rc = SQLITE_OK;
358 TestvfsFd *pFd = tvfsGetFd(pFile);
359 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
361 if( p->pScript && p->mask&TESTVFS_WRITE_MASK ){
362 tvfsExecTcl(p, "xWrite",
363 Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
365 tvfsResultCode(p, &rc);
368 if( rc==SQLITE_OK && tvfsInjectFullerr(p) ){
369 rc = SQLITE_FULL;
371 if( rc==SQLITE_OK && p->mask&TESTVFS_WRITE_MASK && tvfsInjectIoerr(p) ){
372 rc = SQLITE_IOERR;
375 if( rc==SQLITE_OK ){
376 rc = sqlite3OsWrite(pFd->pReal, zBuf, iAmt, iOfst);
378 return rc;
382 ** Truncate an tvfs-file.
384 static int tvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){
385 int rc = SQLITE_OK;
386 TestvfsFd *pFd = tvfsGetFd(pFile);
387 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
389 if( p->pScript && p->mask&TESTVFS_TRUNCATE_MASK ){
390 tvfsExecTcl(p, "xTruncate",
391 Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
393 tvfsResultCode(p, &rc);
396 if( rc==SQLITE_OK ){
397 rc = sqlite3OsTruncate(pFd->pReal, size);
399 return rc;
403 ** Sync an tvfs-file.
405 static int tvfsSync(sqlite3_file *pFile, int flags){
406 int rc = SQLITE_OK;
407 TestvfsFd *pFd = tvfsGetFd(pFile);
408 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
410 if( p->pScript && p->mask&TESTVFS_SYNC_MASK ){
411 char *zFlags;
413 switch( flags ){
414 case SQLITE_SYNC_NORMAL:
415 zFlags = "normal";
416 break;
417 case SQLITE_SYNC_FULL:
418 zFlags = "full";
419 break;
420 case SQLITE_SYNC_NORMAL|SQLITE_SYNC_DATAONLY:
421 zFlags = "normal|dataonly";
422 break;
423 case SQLITE_SYNC_FULL|SQLITE_SYNC_DATAONLY:
424 zFlags = "full|dataonly";
425 break;
426 default:
427 assert(0);
430 tvfsExecTcl(p, "xSync",
431 Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId,
432 Tcl_NewStringObj(zFlags, -1)
434 tvfsResultCode(p, &rc);
437 if( rc==SQLITE_OK && tvfsInjectFullerr(p) ) rc = SQLITE_FULL;
439 if( rc==SQLITE_OK ){
440 rc = sqlite3OsSync(pFd->pReal, flags);
443 return rc;
447 ** Return the current file-size of an tvfs-file.
449 static int tvfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
450 TestvfsFd *p = tvfsGetFd(pFile);
451 return sqlite3OsFileSize(p->pReal, pSize);
455 ** Lock an tvfs-file.
457 static int tvfsLock(sqlite3_file *pFile, int eLock){
458 TestvfsFd *p = tvfsGetFd(pFile);
459 return sqlite3OsLock(p->pReal, eLock);
463 ** Unlock an tvfs-file.
465 static int tvfsUnlock(sqlite3_file *pFile, int eLock){
466 TestvfsFd *p = tvfsGetFd(pFile);
467 return sqlite3OsUnlock(p->pReal, eLock);
471 ** Check if another file-handle holds a RESERVED lock on an tvfs-file.
473 static int tvfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){
474 TestvfsFd *p = tvfsGetFd(pFile);
475 return sqlite3OsCheckReservedLock(p->pReal, pResOut);
479 ** File control method. For custom operations on an tvfs-file.
481 static int tvfsFileControl(sqlite3_file *pFile, int op, void *pArg){
482 TestvfsFd *p = tvfsGetFd(pFile);
483 if( op==SQLITE_FCNTL_PRAGMA ){
484 char **argv = (char**)pArg;
485 if( sqlite3_stricmp(argv[1],"error")==0 ){
486 int rc = SQLITE_ERROR;
487 if( argv[2] ){
488 const char *z = argv[2];
489 int x = atoi(z);
490 if( x ){
491 rc = x;
492 while( sqlite3Isdigit(z[0]) ){ z++; }
493 while( sqlite3Isspace(z[0]) ){ z++; }
495 if( z[0] ) argv[0] = sqlite3_mprintf("%s", z);
497 return rc;
499 if( sqlite3_stricmp(argv[1], "filename")==0 ){
500 argv[0] = sqlite3_mprintf("%s", p->zFilename);
501 return SQLITE_OK;
504 return sqlite3OsFileControl(p->pReal, op, pArg);
508 ** Return the sector-size in bytes for an tvfs-file.
510 static int tvfsSectorSize(sqlite3_file *pFile){
511 TestvfsFd *pFd = tvfsGetFd(pFile);
512 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
513 if( p->iSectorsize>=0 ){
514 return p->iSectorsize;
516 return sqlite3OsSectorSize(pFd->pReal);
520 ** Return the device characteristic flags supported by an tvfs-file.
522 static int tvfsDeviceCharacteristics(sqlite3_file *pFile){
523 TestvfsFd *pFd = tvfsGetFd(pFile);
524 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
525 if( p->iDevchar>=0 ){
526 return p->iDevchar;
528 return sqlite3OsDeviceCharacteristics(pFd->pReal);
532 ** Open an tvfs file handle.
534 static int tvfsOpen(
535 sqlite3_vfs *pVfs,
536 const char *zName,
537 sqlite3_file *pFile,
538 int flags,
539 int *pOutFlags
541 int rc;
542 TestvfsFile *pTestfile = (TestvfsFile *)pFile;
543 TestvfsFd *pFd;
544 Tcl_Obj *pId = 0;
545 Testvfs *p = (Testvfs *)pVfs->pAppData;
547 pFd = (TestvfsFd *)ckalloc(sizeof(TestvfsFd) + PARENTVFS(pVfs)->szOsFile);
548 memset(pFd, 0, sizeof(TestvfsFd) + PARENTVFS(pVfs)->szOsFile);
549 pFd->pShm = 0;
550 pFd->pShmId = 0;
551 pFd->zFilename = zName;
552 pFd->pVfs = pVfs;
553 pFd->pReal = (sqlite3_file *)&pFd[1];
554 memset(pTestfile, 0, sizeof(TestvfsFile));
555 pTestfile->pFd = pFd;
557 /* Evaluate the Tcl script:
559 ** SCRIPT xOpen FILENAME KEY-VALUE-ARGS
561 ** If the script returns an SQLite error code other than SQLITE_OK, an
562 ** error is returned to the caller. If it returns SQLITE_OK, the new
563 ** connection is named "anon". Otherwise, the value returned by the
564 ** script is used as the connection name.
566 Tcl_ResetResult(p->interp);
567 if( p->pScript && p->mask&TESTVFS_OPEN_MASK ){
568 Tcl_Obj *pArg = Tcl_NewObj();
569 Tcl_IncrRefCount(pArg);
570 if( flags&SQLITE_OPEN_MAIN_DB ){
571 const char *z = &zName[strlen(zName)+1];
572 while( *z ){
573 Tcl_ListObjAppendElement(0, pArg, Tcl_NewStringObj(z, -1));
574 z += strlen(z) + 1;
575 Tcl_ListObjAppendElement(0, pArg, Tcl_NewStringObj(z, -1));
576 z += strlen(z) + 1;
579 tvfsExecTcl(p, "xOpen", Tcl_NewStringObj(pFd->zFilename, -1), pArg, 0);
580 Tcl_DecrRefCount(pArg);
581 if( tvfsResultCode(p, &rc) ){
582 if( rc!=SQLITE_OK ) return rc;
583 }else{
584 pId = Tcl_GetObjResult(p->interp);
588 if( (p->mask&TESTVFS_OPEN_MASK) && tvfsInjectIoerr(p) ) return SQLITE_IOERR;
589 if( tvfsInjectCantopenerr(p) ) return SQLITE_CANTOPEN;
590 if( tvfsInjectFullerr(p) ) return SQLITE_FULL;
592 if( !pId ){
593 pId = Tcl_NewStringObj("anon", -1);
595 Tcl_IncrRefCount(pId);
596 pFd->pShmId = pId;
597 Tcl_ResetResult(p->interp);
599 rc = sqlite3OsOpen(PARENTVFS(pVfs), zName, pFd->pReal, flags, pOutFlags);
600 if( pFd->pReal->pMethods ){
601 sqlite3_io_methods *pMethods;
602 int nByte;
604 if( pVfs->iVersion>1 ){
605 nByte = sizeof(sqlite3_io_methods);
606 }else{
607 nByte = offsetof(sqlite3_io_methods, xShmMap);
610 pMethods = (sqlite3_io_methods *)ckalloc(nByte);
611 memcpy(pMethods, &tvfs_io_methods, nByte);
612 pMethods->iVersion = pVfs->iVersion;
613 if( pVfs->iVersion>1 && ((Testvfs *)pVfs->pAppData)->isNoshm ){
614 pMethods->xShmUnmap = 0;
615 pMethods->xShmLock = 0;
616 pMethods->xShmBarrier = 0;
617 pMethods->xShmMap = 0;
619 pFile->pMethods = pMethods;
622 return rc;
626 ** Delete the file located at zPath. If the dirSync argument is true,
627 ** ensure the file-system modifications are synced to disk before
628 ** returning.
630 static int tvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
631 int rc = SQLITE_OK;
632 Testvfs *p = (Testvfs *)pVfs->pAppData;
634 if( p->pScript && p->mask&TESTVFS_DELETE_MASK ){
635 tvfsExecTcl(p, "xDelete",
636 Tcl_NewStringObj(zPath, -1), Tcl_NewIntObj(dirSync), 0
638 tvfsResultCode(p, &rc);
640 if( rc==SQLITE_OK ){
641 rc = sqlite3OsDelete(PARENTVFS(pVfs), zPath, dirSync);
643 return rc;
647 ** Test for access permissions. Return true if the requested permission
648 ** is available, or false otherwise.
650 static int tvfsAccess(
651 sqlite3_vfs *pVfs,
652 const char *zPath,
653 int flags,
654 int *pResOut
656 Testvfs *p = (Testvfs *)pVfs->pAppData;
657 if( p->pScript && p->mask&TESTVFS_ACCESS_MASK ){
658 int rc;
659 char *zArg = 0;
660 if( flags==SQLITE_ACCESS_EXISTS ) zArg = "SQLITE_ACCESS_EXISTS";
661 if( flags==SQLITE_ACCESS_READWRITE ) zArg = "SQLITE_ACCESS_READWRITE";
662 if( flags==SQLITE_ACCESS_READ ) zArg = "SQLITE_ACCESS_READ";
663 tvfsExecTcl(p, "xAccess",
664 Tcl_NewStringObj(zPath, -1), Tcl_NewStringObj(zArg, -1), 0
666 if( tvfsResultCode(p, &rc) ){
667 if( rc!=SQLITE_OK ) return rc;
668 }else{
669 Tcl_Interp *interp = p->interp;
670 if( TCL_OK==Tcl_GetBooleanFromObj(0, Tcl_GetObjResult(interp), pResOut) ){
671 return SQLITE_OK;
675 return sqlite3OsAccess(PARENTVFS(pVfs), zPath, flags, pResOut);
679 ** Populate buffer zOut with the full canonical pathname corresponding
680 ** to the pathname in zPath. zOut is guaranteed to point to a buffer
681 ** of at least (DEVSYM_MAX_PATHNAME+1) bytes.
683 static int tvfsFullPathname(
684 sqlite3_vfs *pVfs,
685 const char *zPath,
686 int nOut,
687 char *zOut
689 Testvfs *p = (Testvfs *)pVfs->pAppData;
690 if( p->pScript && p->mask&TESTVFS_FULLPATHNAME_MASK ){
691 int rc;
692 tvfsExecTcl(p, "xFullPathname", Tcl_NewStringObj(zPath, -1), 0, 0);
693 if( tvfsResultCode(p, &rc) ){
694 if( rc!=SQLITE_OK ) return rc;
697 return sqlite3OsFullPathname(PARENTVFS(pVfs), zPath, nOut, zOut);
700 #ifndef SQLITE_OMIT_LOAD_EXTENSION
702 ** Open the dynamic library located at zPath and return a handle.
704 static void *tvfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
705 return sqlite3OsDlOpen(PARENTVFS(pVfs), zPath);
709 ** Populate the buffer zErrMsg (size nByte bytes) with a human readable
710 ** utf-8 string describing the most recent error encountered associated
711 ** with dynamic libraries.
713 static void tvfsDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
714 sqlite3OsDlError(PARENTVFS(pVfs), nByte, zErrMsg);
718 ** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
720 static void (*tvfsDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
721 return sqlite3OsDlSym(PARENTVFS(pVfs), p, zSym);
725 ** Close the dynamic library handle pHandle.
727 static void tvfsDlClose(sqlite3_vfs *pVfs, void *pHandle){
728 sqlite3OsDlClose(PARENTVFS(pVfs), pHandle);
730 #endif /* SQLITE_OMIT_LOAD_EXTENSION */
733 ** Populate the buffer pointed to by zBufOut with nByte bytes of
734 ** random data.
736 static int tvfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
737 return sqlite3OsRandomness(PARENTVFS(pVfs), nByte, zBufOut);
741 ** Sleep for nMicro microseconds. Return the number of microseconds
742 ** actually slept.
744 static int tvfsSleep(sqlite3_vfs *pVfs, int nMicro){
745 return sqlite3OsSleep(PARENTVFS(pVfs), nMicro);
749 ** Return the current time as a Julian Day number in *pTimeOut.
751 static int tvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
752 return PARENTVFS(pVfs)->xCurrentTime(PARENTVFS(pVfs), pTimeOut);
755 static int tvfsShmOpen(sqlite3_file *pFile){
756 Testvfs *p;
757 int rc = SQLITE_OK; /* Return code */
758 TestvfsBuffer *pBuffer; /* Buffer to open connection to */
759 TestvfsFd *pFd; /* The testvfs file structure */
761 pFd = tvfsGetFd(pFile);
762 p = (Testvfs *)pFd->pVfs->pAppData;
763 assert( pFd->pShmId && pFd->pShm==0 && pFd->pNext==0 );
765 /* Evaluate the Tcl script:
767 ** SCRIPT xShmOpen FILENAME
769 Tcl_ResetResult(p->interp);
770 if( p->pScript && p->mask&TESTVFS_SHMOPEN_MASK ){
771 tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0);
772 if( tvfsResultCode(p, &rc) ){
773 if( rc!=SQLITE_OK ) return rc;
777 assert( rc==SQLITE_OK );
778 if( p->mask&TESTVFS_SHMOPEN_MASK && tvfsInjectIoerr(p) ){
779 return SQLITE_IOERR;
782 /* Search for a TestvfsBuffer. Create a new one if required. */
783 for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){
784 if( 0==strcmp(pFd->zFilename, pBuffer->zFile) ) break;
786 if( !pBuffer ){
787 int nByte = sizeof(TestvfsBuffer) + (int)strlen(pFd->zFilename) + 1;
788 pBuffer = (TestvfsBuffer *)ckalloc(nByte);
789 memset(pBuffer, 0, nByte);
790 pBuffer->zFile = (char *)&pBuffer[1];
791 strcpy(pBuffer->zFile, pFd->zFilename);
792 pBuffer->pNext = p->pBuffer;
793 p->pBuffer = pBuffer;
796 /* Connect the TestvfsBuffer to the new TestvfsShm handle and return. */
797 pFd->pNext = pBuffer->pFile;
798 pBuffer->pFile = pFd;
799 pFd->pShm = pBuffer;
800 return SQLITE_OK;
803 static void tvfsAllocPage(TestvfsBuffer *p, int iPage, int pgsz){
804 assert( iPage<TESTVFS_MAX_PAGES );
805 if( p->aPage[iPage]==0 ){
806 p->aPage[iPage] = (u8 *)ckalloc(pgsz);
807 memset(p->aPage[iPage], 0, pgsz);
808 p->pgsz = pgsz;
812 static int tvfsShmMap(
813 sqlite3_file *pFile, /* Handle open on database file */
814 int iPage, /* Page to retrieve */
815 int pgsz, /* Size of pages */
816 int isWrite, /* True to extend file if necessary */
817 void volatile **pp /* OUT: Mapped memory */
819 int rc = SQLITE_OK;
820 TestvfsFd *pFd = tvfsGetFd(pFile);
821 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
823 if( 0==pFd->pShm ){
824 rc = tvfsShmOpen(pFile);
825 if( rc!=SQLITE_OK ){
826 return rc;
830 if( p->pScript && p->mask&TESTVFS_SHMMAP_MASK ){
831 Tcl_Obj *pArg = Tcl_NewObj();
832 Tcl_IncrRefCount(pArg);
833 Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(iPage));
834 Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(pgsz));
835 Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(isWrite));
836 tvfsExecTcl(p, "xShmMap",
837 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, pArg
839 tvfsResultCode(p, &rc);
840 Tcl_DecrRefCount(pArg);
842 if( rc==SQLITE_OK && p->mask&TESTVFS_SHMMAP_MASK && tvfsInjectIoerr(p) ){
843 rc = SQLITE_IOERR;
846 if( rc==SQLITE_OK && isWrite && !pFd->pShm->aPage[iPage] ){
847 tvfsAllocPage(pFd->pShm, iPage, pgsz);
849 *pp = (void volatile *)pFd->pShm->aPage[iPage];
851 return rc;
855 static int tvfsShmLock(
856 sqlite3_file *pFile,
857 int ofst,
858 int n,
859 int flags
861 int rc = SQLITE_OK;
862 TestvfsFd *pFd = tvfsGetFd(pFile);
863 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
864 int nLock;
865 char zLock[80];
867 if( p->pScript && p->mask&TESTVFS_SHMLOCK_MASK ){
868 sqlite3_snprintf(sizeof(zLock), zLock, "%d %d", ofst, n);
869 nLock = (int)strlen(zLock);
870 if( flags & SQLITE_SHM_LOCK ){
871 strcpy(&zLock[nLock], " lock");
872 }else{
873 strcpy(&zLock[nLock], " unlock");
875 nLock += (int)strlen(&zLock[nLock]);
876 if( flags & SQLITE_SHM_SHARED ){
877 strcpy(&zLock[nLock], " shared");
878 }else{
879 strcpy(&zLock[nLock], " exclusive");
881 tvfsExecTcl(p, "xShmLock",
882 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId,
883 Tcl_NewStringObj(zLock, -1)
885 tvfsResultCode(p, &rc);
888 if( rc==SQLITE_OK && p->mask&TESTVFS_SHMLOCK_MASK && tvfsInjectIoerr(p) ){
889 rc = SQLITE_IOERR;
892 if( rc==SQLITE_OK ){
893 int isLock = (flags & SQLITE_SHM_LOCK);
894 int isExcl = (flags & SQLITE_SHM_EXCLUSIVE);
895 u32 mask = (((1<<n)-1) << ofst);
896 if( isLock ){
897 TestvfsFd *p2;
898 for(p2=pFd->pShm->pFile; p2; p2=p2->pNext){
899 if( p2==pFd ) continue;
900 if( (p2->excllock&mask) || (isExcl && p2->sharedlock&mask) ){
901 rc = SQLITE_BUSY;
902 break;
905 if( rc==SQLITE_OK ){
906 if( isExcl ) pFd->excllock |= mask;
907 if( !isExcl ) pFd->sharedlock |= mask;
909 }else{
910 if( isExcl ) pFd->excllock &= (~mask);
911 if( !isExcl ) pFd->sharedlock &= (~mask);
915 return rc;
918 static void tvfsShmBarrier(sqlite3_file *pFile){
919 TestvfsFd *pFd = tvfsGetFd(pFile);
920 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
922 if( p->pScript && p->mask&TESTVFS_SHMBARRIER_MASK ){
923 tvfsExecTcl(p, "xShmBarrier",
924 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0
929 static int tvfsShmUnmap(
930 sqlite3_file *pFile,
931 int deleteFlag
933 int rc = SQLITE_OK;
934 TestvfsFd *pFd = tvfsGetFd(pFile);
935 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
936 TestvfsBuffer *pBuffer = pFd->pShm;
937 TestvfsFd **ppFd;
939 if( !pBuffer ) return SQLITE_OK;
940 assert( pFd->pShmId && pFd->pShm );
942 if( p->pScript && p->mask&TESTVFS_SHMCLOSE_MASK ){
943 tvfsExecTcl(p, "xShmUnmap",
944 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0
946 tvfsResultCode(p, &rc);
949 for(ppFd=&pBuffer->pFile; *ppFd!=pFd; ppFd=&((*ppFd)->pNext));
950 assert( (*ppFd)==pFd );
951 *ppFd = pFd->pNext;
952 pFd->pNext = 0;
954 if( pBuffer->pFile==0 ){
955 int i;
956 TestvfsBuffer **pp;
957 for(pp=&p->pBuffer; *pp!=pBuffer; pp=&((*pp)->pNext));
958 *pp = (*pp)->pNext;
959 for(i=0; pBuffer->aPage[i]; i++){
960 ckfree((char *)pBuffer->aPage[i]);
962 ckfree((char *)pBuffer);
964 pFd->pShm = 0;
966 return rc;
969 static int testvfs_obj_cmd(
970 ClientData cd,
971 Tcl_Interp *interp,
972 int objc,
973 Tcl_Obj *CONST objv[]
975 Testvfs *p = (Testvfs *)cd;
977 enum DB_enum {
978 CMD_SHM, CMD_DELETE, CMD_FILTER, CMD_IOERR, CMD_SCRIPT,
979 CMD_DEVCHAR, CMD_SECTORSIZE, CMD_FULLERR, CMD_CANTOPENERR
981 struct TestvfsSubcmd {
982 char *zName;
983 enum DB_enum eCmd;
984 } aSubcmd[] = {
985 { "shm", CMD_SHM },
986 { "delete", CMD_DELETE },
987 { "filter", CMD_FILTER },
988 { "ioerr", CMD_IOERR },
989 { "fullerr", CMD_FULLERR },
990 { "cantopenerr", CMD_CANTOPENERR },
991 { "script", CMD_SCRIPT },
992 { "devchar", CMD_DEVCHAR },
993 { "sectorsize", CMD_SECTORSIZE },
994 { 0, 0 }
996 int i;
998 if( objc<2 ){
999 Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
1000 return TCL_ERROR;
1002 if( Tcl_GetIndexFromObjStruct(
1003 interp, objv[1], aSubcmd, sizeof(aSubcmd[0]), "subcommand", 0, &i)
1005 return TCL_ERROR;
1007 Tcl_ResetResult(interp);
1009 switch( aSubcmd[i].eCmd ){
1010 case CMD_SHM: {
1011 Tcl_Obj *pObj;
1012 int i, rc;
1013 TestvfsBuffer *pBuffer;
1014 char *zName;
1015 if( objc!=3 && objc!=4 ){
1016 Tcl_WrongNumArgs(interp, 2, objv, "FILE ?VALUE?");
1017 return TCL_ERROR;
1019 zName = ckalloc(p->pParent->mxPathname);
1020 rc = p->pParent->xFullPathname(
1021 p->pParent, Tcl_GetString(objv[2]),
1022 p->pParent->mxPathname, zName
1024 if( rc!=SQLITE_OK ){
1025 Tcl_AppendResult(interp, "failed to get full path: ",
1026 Tcl_GetString(objv[2]), 0);
1027 ckfree(zName);
1028 return TCL_ERROR;
1030 for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){
1031 if( 0==strcmp(pBuffer->zFile, zName) ) break;
1033 ckfree(zName);
1034 if( !pBuffer ){
1035 Tcl_AppendResult(interp, "no such file: ", Tcl_GetString(objv[2]), 0);
1036 return TCL_ERROR;
1038 if( objc==4 ){
1039 int n;
1040 u8 *a = Tcl_GetByteArrayFromObj(objv[3], &n);
1041 int pgsz = pBuffer->pgsz;
1042 if( pgsz==0 ) pgsz = 65536;
1043 for(i=0; i*pgsz<n; i++){
1044 int nByte = pgsz;
1045 tvfsAllocPage(pBuffer, i, pgsz);
1046 if( n-i*pgsz<pgsz ){
1047 nByte = n;
1049 memcpy(pBuffer->aPage[i], &a[i*pgsz], nByte);
1053 pObj = Tcl_NewObj();
1054 for(i=0; pBuffer->aPage[i]; i++){
1055 int pgsz = pBuffer->pgsz;
1056 if( pgsz==0 ) pgsz = 65536;
1057 Tcl_AppendObjToObj(pObj, Tcl_NewByteArrayObj(pBuffer->aPage[i], pgsz));
1059 Tcl_SetObjResult(interp, pObj);
1060 break;
1063 case CMD_FILTER: {
1064 static struct VfsMethod {
1065 char *zName;
1066 int mask;
1067 } vfsmethod [] = {
1068 { "xShmOpen", TESTVFS_SHMOPEN_MASK },
1069 { "xShmLock", TESTVFS_SHMLOCK_MASK },
1070 { "xShmBarrier", TESTVFS_SHMBARRIER_MASK },
1071 { "xShmUnmap", TESTVFS_SHMCLOSE_MASK },
1072 { "xShmMap", TESTVFS_SHMMAP_MASK },
1073 { "xSync", TESTVFS_SYNC_MASK },
1074 { "xDelete", TESTVFS_DELETE_MASK },
1075 { "xWrite", TESTVFS_WRITE_MASK },
1076 { "xRead", TESTVFS_READ_MASK },
1077 { "xTruncate", TESTVFS_TRUNCATE_MASK },
1078 { "xOpen", TESTVFS_OPEN_MASK },
1079 { "xClose", TESTVFS_CLOSE_MASK },
1080 { "xAccess", TESTVFS_ACCESS_MASK },
1081 { "xFullPathname", TESTVFS_FULLPATHNAME_MASK },
1083 Tcl_Obj **apElem = 0;
1084 int nElem = 0;
1085 int i;
1086 int mask = 0;
1087 if( objc!=3 ){
1088 Tcl_WrongNumArgs(interp, 2, objv, "LIST");
1089 return TCL_ERROR;
1091 if( Tcl_ListObjGetElements(interp, objv[2], &nElem, &apElem) ){
1092 return TCL_ERROR;
1094 Tcl_ResetResult(interp);
1095 for(i=0; i<nElem; i++){
1096 int iMethod;
1097 char *zElem = Tcl_GetString(apElem[i]);
1098 for(iMethod=0; iMethod<ArraySize(vfsmethod); iMethod++){
1099 if( strcmp(zElem, vfsmethod[iMethod].zName)==0 ){
1100 mask |= vfsmethod[iMethod].mask;
1101 break;
1104 if( iMethod==ArraySize(vfsmethod) ){
1105 Tcl_AppendResult(interp, "unknown method: ", zElem, 0);
1106 return TCL_ERROR;
1109 p->mask = mask;
1110 break;
1113 case CMD_SCRIPT: {
1114 if( objc==3 ){
1115 int nByte;
1116 if( p->pScript ){
1117 Tcl_DecrRefCount(p->pScript);
1118 p->pScript = 0;
1120 Tcl_GetStringFromObj(objv[2], &nByte);
1121 if( nByte>0 ){
1122 p->pScript = Tcl_DuplicateObj(objv[2]);
1123 Tcl_IncrRefCount(p->pScript);
1125 }else if( objc!=2 ){
1126 Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?");
1127 return TCL_ERROR;
1130 Tcl_ResetResult(interp);
1131 if( p->pScript ) Tcl_SetObjResult(interp, p->pScript);
1133 break;
1137 ** TESTVFS ioerr ?IFAIL PERSIST?
1139 ** Where IFAIL is an integer and PERSIST is boolean.
1141 case CMD_CANTOPENERR:
1142 case CMD_IOERR:
1143 case CMD_FULLERR: {
1144 TestFaultInject *pTest;
1145 int iRet;
1147 switch( aSubcmd[i].eCmd ){
1148 case CMD_IOERR: pTest = &p->ioerr_err; break;
1149 case CMD_FULLERR: pTest = &p->full_err; break;
1150 case CMD_CANTOPENERR: pTest = &p->cantopen_err; break;
1151 default: assert(0);
1153 iRet = pTest->nFail;
1154 pTest->nFail = 0;
1155 pTest->eFault = 0;
1156 pTest->iCnt = 0;
1158 if( objc==4 ){
1159 int iCnt, iPersist;
1160 if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iCnt)
1161 || TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[3], &iPersist)
1163 return TCL_ERROR;
1165 pTest->eFault = iPersist?FAULT_INJECT_PERSISTENT:FAULT_INJECT_TRANSIENT;
1166 pTest->iCnt = iCnt;
1167 }else if( objc!=2 ){
1168 Tcl_WrongNumArgs(interp, 2, objv, "?CNT PERSIST?");
1169 return TCL_ERROR;
1171 Tcl_SetObjResult(interp, Tcl_NewIntObj(iRet));
1172 break;
1175 case CMD_DELETE: {
1176 Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
1177 break;
1180 case CMD_DEVCHAR: {
1181 struct DeviceFlag {
1182 char *zName;
1183 int iValue;
1184 } aFlag[] = {
1185 { "default", -1 },
1186 { "atomic", SQLITE_IOCAP_ATOMIC },
1187 { "atomic512", SQLITE_IOCAP_ATOMIC512 },
1188 { "atomic1k", SQLITE_IOCAP_ATOMIC1K },
1189 { "atomic2k", SQLITE_IOCAP_ATOMIC2K },
1190 { "atomic4k", SQLITE_IOCAP_ATOMIC4K },
1191 { "atomic8k", SQLITE_IOCAP_ATOMIC8K },
1192 { "atomic16k", SQLITE_IOCAP_ATOMIC16K },
1193 { "atomic32k", SQLITE_IOCAP_ATOMIC32K },
1194 { "atomic64k", SQLITE_IOCAP_ATOMIC64K },
1195 { "sequential", SQLITE_IOCAP_SEQUENTIAL },
1196 { "safe_append", SQLITE_IOCAP_SAFE_APPEND },
1197 { "undeletable_when_open", SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN },
1198 { "powersafe_overwrite", SQLITE_IOCAP_POWERSAFE_OVERWRITE },
1199 { 0, 0 }
1201 Tcl_Obj *pRet;
1202 int iFlag;
1204 if( objc>3 ){
1205 Tcl_WrongNumArgs(interp, 2, objv, "?ATTR-LIST?");
1206 return TCL_ERROR;
1208 if( objc==3 ){
1209 int j;
1210 int iNew = 0;
1211 Tcl_Obj **flags = 0;
1212 int nFlags = 0;
1214 if( Tcl_ListObjGetElements(interp, objv[2], &nFlags, &flags) ){
1215 return TCL_ERROR;
1218 for(j=0; j<nFlags; j++){
1219 int idx = 0;
1220 if( Tcl_GetIndexFromObjStruct(interp, flags[j], aFlag,
1221 sizeof(aFlag[0]), "flag", 0, &idx)
1223 return TCL_ERROR;
1225 if( aFlag[idx].iValue<0 && nFlags>1 ){
1226 Tcl_AppendResult(interp, "bad flags: ", Tcl_GetString(objv[2]), 0);
1227 return TCL_ERROR;
1229 iNew |= aFlag[idx].iValue;
1232 p->iDevchar = iNew| 0x10000000;
1235 pRet = Tcl_NewObj();
1236 for(iFlag=0; iFlag<sizeof(aFlag)/sizeof(aFlag[0]); iFlag++){
1237 if( p->iDevchar & aFlag[iFlag].iValue ){
1238 Tcl_ListObjAppendElement(
1239 interp, pRet, Tcl_NewStringObj(aFlag[iFlag].zName, -1)
1243 Tcl_SetObjResult(interp, pRet);
1245 break;
1248 case CMD_SECTORSIZE: {
1249 if( objc>3 ){
1250 Tcl_WrongNumArgs(interp, 2, objv, "?VALUE?");
1251 return TCL_ERROR;
1253 if( objc==3 ){
1254 int iNew = 0;
1255 if( Tcl_GetIntFromObj(interp, objv[2], &iNew) ){
1256 return TCL_ERROR;
1258 p->iSectorsize = iNew;
1260 Tcl_SetObjResult(interp, Tcl_NewIntObj(p->iSectorsize));
1261 break;
1265 return TCL_OK;
1268 static void testvfs_obj_del(ClientData cd){
1269 Testvfs *p = (Testvfs *)cd;
1270 if( p->pScript ) Tcl_DecrRefCount(p->pScript);
1271 sqlite3_vfs_unregister(p->pVfs);
1272 ckfree((char *)p->pVfs);
1273 ckfree((char *)p);
1277 ** Usage: testvfs VFSNAME ?SWITCHES?
1279 ** Switches are:
1281 ** -noshm BOOLEAN (True to omit shm methods. Default false)
1282 ** -default BOOLEAN (True to make the vfs default. Default false)
1284 ** This command creates two things when it is invoked: an SQLite VFS, and
1285 ** a Tcl command. Both are named VFSNAME. The VFS is installed. It is not
1286 ** installed as the default VFS.
1288 ** The VFS passes all file I/O calls through to the underlying VFS.
1290 ** Whenever the xShmMap method of the VFS
1291 ** is invoked, the SCRIPT is executed as follows:
1293 ** SCRIPT xShmMap FILENAME ID
1295 ** The value returned by the invocation of SCRIPT above is interpreted as
1296 ** an SQLite error code and returned to SQLite. Either a symbolic
1297 ** "SQLITE_OK" or numeric "0" value may be returned.
1299 ** The contents of the shared-memory buffer associated with a given file
1300 ** may be read and set using the following command:
1302 ** VFSNAME shm FILENAME ?NEWVALUE?
1304 ** When the xShmLock method is invoked by SQLite, the following script is
1305 ** run:
1307 ** SCRIPT xShmLock FILENAME ID LOCK
1309 ** where LOCK is of the form "OFFSET NBYTE lock/unlock shared/exclusive"
1311 static int testvfs_cmd(
1312 ClientData cd,
1313 Tcl_Interp *interp,
1314 int objc,
1315 Tcl_Obj *CONST objv[]
1317 static sqlite3_vfs tvfs_vfs = {
1318 2, /* iVersion */
1319 0, /* szOsFile */
1320 0, /* mxPathname */
1321 0, /* pNext */
1322 0, /* zName */
1323 0, /* pAppData */
1324 tvfsOpen, /* xOpen */
1325 tvfsDelete, /* xDelete */
1326 tvfsAccess, /* xAccess */
1327 tvfsFullPathname, /* xFullPathname */
1328 #ifndef SQLITE_OMIT_LOAD_EXTENSION
1329 tvfsDlOpen, /* xDlOpen */
1330 tvfsDlError, /* xDlError */
1331 tvfsDlSym, /* xDlSym */
1332 tvfsDlClose, /* xDlClose */
1333 #else
1334 0, /* xDlOpen */
1335 0, /* xDlError */
1336 0, /* xDlSym */
1337 0, /* xDlClose */
1338 #endif /* SQLITE_OMIT_LOAD_EXTENSION */
1339 tvfsRandomness, /* xRandomness */
1340 tvfsSleep, /* xSleep */
1341 tvfsCurrentTime, /* xCurrentTime */
1342 0, /* xGetLastError */
1343 0, /* xCurrentTimeInt64 */
1346 Testvfs *p; /* New object */
1347 sqlite3_vfs *pVfs; /* New VFS */
1348 char *zVfs;
1349 int nByte; /* Bytes of space to allocate at p */
1351 int i;
1352 int isNoshm = 0; /* True if -noshm is passed */
1353 int isDefault = 0; /* True if -default is passed */
1354 int szOsFile = 0; /* Value passed to -szosfile */
1355 int mxPathname = -1; /* Value passed to -mxpathname */
1356 int iVersion = 2; /* Value passed to -iversion */
1358 if( objc<2 || 0!=(objc%2) ) goto bad_args;
1359 for(i=2; i<objc; i += 2){
1360 int nSwitch;
1361 char *zSwitch;
1362 zSwitch = Tcl_GetStringFromObj(objv[i], &nSwitch);
1364 if( nSwitch>2 && 0==strncmp("-noshm", zSwitch, nSwitch) ){
1365 if( Tcl_GetBooleanFromObj(interp, objv[i+1], &isNoshm) ){
1366 return TCL_ERROR;
1369 else if( nSwitch>2 && 0==strncmp("-default", zSwitch, nSwitch) ){
1370 if( Tcl_GetBooleanFromObj(interp, objv[i+1], &isDefault) ){
1371 return TCL_ERROR;
1374 else if( nSwitch>2 && 0==strncmp("-szosfile", zSwitch, nSwitch) ){
1375 if( Tcl_GetIntFromObj(interp, objv[i+1], &szOsFile) ){
1376 return TCL_ERROR;
1379 else if( nSwitch>2 && 0==strncmp("-mxpathname", zSwitch, nSwitch) ){
1380 if( Tcl_GetIntFromObj(interp, objv[i+1], &mxPathname) ){
1381 return TCL_ERROR;
1384 else if( nSwitch>2 && 0==strncmp("-iversion", zSwitch, nSwitch) ){
1385 if( Tcl_GetIntFromObj(interp, objv[i+1], &iVersion) ){
1386 return TCL_ERROR;
1389 else{
1390 goto bad_args;
1394 if( szOsFile<sizeof(TestvfsFile) ){
1395 szOsFile = sizeof(TestvfsFile);
1398 zVfs = Tcl_GetString(objv[1]);
1399 nByte = sizeof(Testvfs) + (int)strlen(zVfs)+1;
1400 p = (Testvfs *)ckalloc(nByte);
1401 memset(p, 0, nByte);
1402 p->iDevchar = -1;
1403 p->iSectorsize = -1;
1405 /* Create the new object command before querying SQLite for a default VFS
1406 ** to use for 'real' IO operations. This is because creating the new VFS
1407 ** may delete an existing [testvfs] VFS of the same name. If such a VFS
1408 ** is currently the default, the new [testvfs] may end up calling the
1409 ** methods of a deleted object.
1411 Tcl_CreateObjCommand(interp, zVfs, testvfs_obj_cmd, p, testvfs_obj_del);
1412 p->pParent = sqlite3_vfs_find(0);
1413 p->interp = interp;
1415 p->zName = (char *)&p[1];
1416 memcpy(p->zName, zVfs, strlen(zVfs)+1);
1418 pVfs = (sqlite3_vfs *)ckalloc(sizeof(sqlite3_vfs));
1419 memcpy(pVfs, &tvfs_vfs, sizeof(sqlite3_vfs));
1420 pVfs->pAppData = (void *)p;
1421 pVfs->iVersion = iVersion;
1422 pVfs->zName = p->zName;
1423 pVfs->mxPathname = p->pParent->mxPathname;
1424 if( mxPathname>=0 && mxPathname<pVfs->mxPathname ){
1425 pVfs->mxPathname = mxPathname;
1427 pVfs->szOsFile = szOsFile;
1428 p->pVfs = pVfs;
1429 p->isNoshm = isNoshm;
1430 p->mask = TESTVFS_ALL_MASK;
1432 sqlite3_vfs_register(pVfs, isDefault);
1434 return TCL_OK;
1436 bad_args:
1437 Tcl_WrongNumArgs(interp, 1, objv, "VFSNAME ?-noshm BOOL? ?-default BOOL? ?-mxpathname INT? ?-szosfile INT? ?-iversion INT?");
1438 return TCL_ERROR;
1441 int Sqlitetestvfs_Init(Tcl_Interp *interp){
1442 Tcl_CreateObjCommand(interp, "testvfs", testvfs_cmd, 0, 0);
1443 return TCL_OK;
1446 #endif