• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
10 **
11 ******************************************************************************
12 **
13 */
14 #if SQLITE_TEST          /* This file is used for testing only */
15 
16 /*
17 ** This file contains the implementation of the Tcl [testvfs] command,
18 ** used to create SQLite VFS implementations with various properties and
19 ** instrumentation to support testing SQLite.
20 **
21 **   testvfs VFSNAME ?OPTIONS?
22 **
23 ** Available options are:
24 **
25 **   -noshm      BOOLEAN        (True to omit shm methods. Default false)
26 **   -default    BOOLEAN        (True to make the vfs default. Default false)
27 **   -szosfile   INTEGER        (Value for sqlite3_vfs.szOsFile)
28 **   -mxpathname INTEGER        (Value for sqlite3_vfs.mxPathname)
29 **   -iversion   INTEGER        (Value for sqlite3_vfs.iVersion)
30 */
31 
32 #include "sqlite3.h"
33 #include "sqliteInt.h"
34 
35 typedef struct Testvfs Testvfs;
36 typedef struct TestvfsShm TestvfsShm;
37 typedef struct TestvfsBuffer TestvfsBuffer;
38 typedef struct TestvfsFile TestvfsFile;
39 typedef struct TestvfsFd TestvfsFd;
40 
41 /*
42 ** An open file handle.
43 */
44 struct TestvfsFile {
45   sqlite3_file base;              /* Base class.  Must be first */
46   TestvfsFd *pFd;                 /* File data */
47 };
48 #define tvfsGetFd(pFile) (((TestvfsFile *)pFile)->pFd)
49 
50 struct TestvfsFd {
51   sqlite3_vfs *pVfs;              /* The VFS */
52   const char *zFilename;          /* Filename as passed to xOpen() */
53   sqlite3_file *pReal;            /* The real, underlying file descriptor */
54   Tcl_Obj *pShmId;                /* Shared memory id for Tcl callbacks */
55 
56   TestvfsBuffer *pShm;            /* Shared memory buffer */
57   u32 excllock;                   /* Mask of exclusive locks */
58   u32 sharedlock;                 /* Mask of shared locks */
59   TestvfsFd *pNext;               /* Next handle opened on the same file */
60 };
61 
62 
63 #define FAULT_INJECT_NONE       0
64 #define FAULT_INJECT_TRANSIENT  1
65 #define FAULT_INJECT_PERSISTENT 2
66 
67 typedef struct TestFaultInject TestFaultInject;
68 struct TestFaultInject {
69   int iCnt;                       /* Remaining calls before fault injection */
70   int eFault;                     /* A FAULT_INJECT_* value */
71   int nFail;                      /* Number of faults injected */
72 };
73 
74 /*
75 ** An instance of this structure is allocated for each VFS created. The
76 ** sqlite3_vfs.pAppData field of the VFS structure registered with SQLite
77 ** is set to point to it.
78 */
79 struct Testvfs {
80   char *zName;                    /* Name of this VFS */
81   sqlite3_vfs *pParent;           /* The VFS to use for file IO */
82   sqlite3_vfs *pVfs;              /* The testvfs registered with SQLite */
83   Tcl_Interp *interp;             /* Interpreter to run script in */
84   Tcl_Obj *pScript;               /* Script to execute */
85   int nScript;                    /* Number of elements in array apScript */
86   Tcl_Obj **apScript;             /* Array version of pScript */
87   TestvfsBuffer *pBuffer;         /* List of shared buffers */
88   int isNoshm;
89 
90   int mask;                       /* Mask controlling [script] and [ioerr] */
91 
92   TestFaultInject ioerr_err;
93   TestFaultInject full_err;
94   TestFaultInject cantopen_err;
95 
96 #if 0
97   int iIoerrCnt;
98   int ioerr;
99   int nIoerrFail;
100   int iFullCnt;
101   int fullerr;
102   int nFullFail;
103 #endif
104 
105   int iDevchar;
106   int iSectorsize;
107 };
108 
109 /*
110 ** The Testvfs.mask variable is set to a combination of the following.
111 ** If a bit is clear in Testvfs.mask, then calls made by SQLite to the
112 ** corresponding VFS method is ignored for purposes of:
113 **
114 **   + Simulating IO errors, and
115 **   + Invoking the Tcl callback script.
116 */
117 #define TESTVFS_SHMOPEN_MASK    0x00000001
118 #define TESTVFS_SHMLOCK_MASK    0x00000010
119 #define TESTVFS_SHMMAP_MASK     0x00000020
120 #define TESTVFS_SHMBARRIER_MASK 0x00000040
121 #define TESTVFS_SHMCLOSE_MASK   0x00000080
122 
123 #define TESTVFS_OPEN_MASK       0x00000100
124 #define TESTVFS_SYNC_MASK       0x00000200
125 #define TESTVFS_DELETE_MASK     0x00000400
126 #define TESTVFS_CLOSE_MASK      0x00000800
127 #define TESTVFS_WRITE_MASK      0x00001000
128 #define TESTVFS_TRUNCATE_MASK   0x00002000
129 #define TESTVFS_ACCESS_MASK     0x00004000
130 #define TESTVFS_ALL_MASK        0x00007FFF
131 
132 
133 #define TESTVFS_MAX_PAGES 1024
134 
135 /*
136 ** A shared-memory buffer. There is one of these objects for each shared
137 ** memory region opened by clients. If two clients open the same file,
138 ** there are two TestvfsFile structures but only one TestvfsBuffer structure.
139 */
140 struct TestvfsBuffer {
141   char *zFile;                    /* Associated file name */
142   int pgsz;                       /* Page size */
143   u8 *aPage[TESTVFS_MAX_PAGES];   /* Array of ckalloc'd pages */
144   TestvfsFd *pFile;               /* List of open handles */
145   TestvfsBuffer *pNext;           /* Next in linked list of all buffers */
146 };
147 
148 
149 #define PARENTVFS(x) (((Testvfs *)((x)->pAppData))->pParent)
150 
151 #define TESTVFS_MAX_ARGS 12
152 
153 
154 /*
155 ** Method declarations for TestvfsFile.
156 */
157 static int tvfsClose(sqlite3_file*);
158 static int tvfsRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
159 static int tvfsWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
160 static int tvfsTruncate(sqlite3_file*, sqlite3_int64 size);
161 static int tvfsSync(sqlite3_file*, int flags);
162 static int tvfsFileSize(sqlite3_file*, sqlite3_int64 *pSize);
163 static int tvfsLock(sqlite3_file*, int);
164 static int tvfsUnlock(sqlite3_file*, int);
165 static int tvfsCheckReservedLock(sqlite3_file*, int *);
166 static int tvfsFileControl(sqlite3_file*, int op, void *pArg);
167 static int tvfsSectorSize(sqlite3_file*);
168 static int tvfsDeviceCharacteristics(sqlite3_file*);
169 
170 /*
171 ** Method declarations for tvfs_vfs.
172 */
173 static int tvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
174 static int tvfsDelete(sqlite3_vfs*, const char *zName, int syncDir);
175 static int tvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *);
176 static int tvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
177 #ifndef SQLITE_OMIT_LOAD_EXTENSION
178 static void *tvfsDlOpen(sqlite3_vfs*, const char *zFilename);
179 static void tvfsDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
180 static void (*tvfsDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void);
181 static void tvfsDlClose(sqlite3_vfs*, void*);
182 #endif /* SQLITE_OMIT_LOAD_EXTENSION */
183 static int tvfsRandomness(sqlite3_vfs*, int nByte, char *zOut);
184 static int tvfsSleep(sqlite3_vfs*, int microseconds);
185 static int tvfsCurrentTime(sqlite3_vfs*, double*);
186 
187 static int tvfsShmOpen(sqlite3_file*);
188 static int tvfsShmLock(sqlite3_file*, int , int, int);
189 static int tvfsShmMap(sqlite3_file*,int,int,int, void volatile **);
190 static void tvfsShmBarrier(sqlite3_file*);
191 static int tvfsShmUnmap(sqlite3_file*, int);
192 
193 static sqlite3_io_methods tvfs_io_methods = {
194   2,                              /* iVersion */
195   tvfsClose,                      /* xClose */
196   tvfsRead,                       /* xRead */
197   tvfsWrite,                      /* xWrite */
198   tvfsTruncate,                   /* xTruncate */
199   tvfsSync,                       /* xSync */
200   tvfsFileSize,                   /* xFileSize */
201   tvfsLock,                       /* xLock */
202   tvfsUnlock,                     /* xUnlock */
203   tvfsCheckReservedLock,          /* xCheckReservedLock */
204   tvfsFileControl,                /* xFileControl */
205   tvfsSectorSize,                 /* xSectorSize */
206   tvfsDeviceCharacteristics,      /* xDeviceCharacteristics */
207   tvfsShmMap,                     /* xShmMap */
208   tvfsShmLock,                    /* xShmLock */
209   tvfsShmBarrier,                 /* xShmBarrier */
210   tvfsShmUnmap                    /* xShmUnmap */
211 };
212 
tvfsResultCode(Testvfs * p,int * pRc)213 static int tvfsResultCode(Testvfs *p, int *pRc){
214   struct errcode {
215     int eCode;
216     const char *zCode;
217   } aCode[] = {
218     { SQLITE_OK,     "SQLITE_OK"     },
219     { SQLITE_ERROR,  "SQLITE_ERROR"  },
220     { SQLITE_IOERR,  "SQLITE_IOERR"  },
221     { SQLITE_LOCKED, "SQLITE_LOCKED" },
222     { SQLITE_BUSY,   "SQLITE_BUSY"   },
223   };
224 
225   const char *z;
226   int i;
227 
228   z = Tcl_GetStringResult(p->interp);
229   for(i=0; i<ArraySize(aCode); i++){
230     if( 0==strcmp(z, aCode[i].zCode) ){
231       *pRc = aCode[i].eCode;
232       return 1;
233     }
234   }
235 
236   return 0;
237 }
238 
tvfsInjectFault(TestFaultInject * p)239 static int tvfsInjectFault(TestFaultInject *p){
240   int ret = 0;
241   if( p->eFault ){
242     p->iCnt--;
243     if( p->iCnt==0 || (p->iCnt<0 && p->eFault==FAULT_INJECT_PERSISTENT ) ){
244       ret = 1;
245       p->nFail++;
246     }
247   }
248   return ret;
249 }
250 
251 
tvfsInjectIoerr(Testvfs * p)252 static int tvfsInjectIoerr(Testvfs *p){
253   return tvfsInjectFault(&p->ioerr_err);
254 }
255 
tvfsInjectFullerr(Testvfs * p)256 static int tvfsInjectFullerr(Testvfs *p){
257   return tvfsInjectFault(&p->full_err);
258 }
tvfsInjectCantopenerr(Testvfs * p)259 static int tvfsInjectCantopenerr(Testvfs *p){
260   return tvfsInjectFault(&p->cantopen_err);
261 }
262 
263 
tvfsExecTcl(Testvfs * p,const char * zMethod,Tcl_Obj * arg1,Tcl_Obj * arg2,Tcl_Obj * arg3)264 static void tvfsExecTcl(
265   Testvfs *p,
266   const char *zMethod,
267   Tcl_Obj *arg1,
268   Tcl_Obj *arg2,
269   Tcl_Obj *arg3
270 ){
271   int rc;                         /* Return code from Tcl_EvalObj() */
272   int nArg;                       /* Elements in eval'd list */
273   int nScript;
274   Tcl_Obj ** ap;
275 
276   assert( p->pScript );
277 
278   if( !p->apScript ){
279     int nByte;
280     int i;
281     if( TCL_OK!=Tcl_ListObjGetElements(p->interp, p->pScript, &nScript, &ap) ){
282       Tcl_BackgroundError(p->interp);
283       Tcl_ResetResult(p->interp);
284       return;
285     }
286     p->nScript = nScript;
287     nByte = (nScript+TESTVFS_MAX_ARGS)*sizeof(Tcl_Obj *);
288     p->apScript = (Tcl_Obj **)ckalloc(nByte);
289     memset(p->apScript, 0, nByte);
290     for(i=0; i<nScript; i++){
291       p->apScript[i] = ap[i];
292     }
293   }
294 
295   p->apScript[p->nScript] = Tcl_NewStringObj(zMethod, -1);
296   p->apScript[p->nScript+1] = arg1;
297   p->apScript[p->nScript+2] = arg2;
298   p->apScript[p->nScript+3] = arg3;
299 
300   for(nArg=p->nScript; p->apScript[nArg]; nArg++){
301     Tcl_IncrRefCount(p->apScript[nArg]);
302   }
303 
304   rc = Tcl_EvalObjv(p->interp, nArg, p->apScript, TCL_EVAL_GLOBAL);
305   if( rc!=TCL_OK ){
306     Tcl_BackgroundError(p->interp);
307     Tcl_ResetResult(p->interp);
308   }
309 
310   for(nArg=p->nScript; p->apScript[nArg]; nArg++){
311     Tcl_DecrRefCount(p->apScript[nArg]);
312     p->apScript[nArg] = 0;
313   }
314 }
315 
316 
317 /*
318 ** Close an tvfs-file.
319 */
tvfsClose(sqlite3_file * pFile)320 static int tvfsClose(sqlite3_file *pFile){
321   int rc;
322   TestvfsFile *pTestfile = (TestvfsFile *)pFile;
323   TestvfsFd *pFd = pTestfile->pFd;
324   Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
325 
326   if( p->pScript && p->mask&TESTVFS_CLOSE_MASK ){
327     tvfsExecTcl(p, "xClose",
328         Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
329     );
330   }
331 
332   if( pFd->pShmId ){
333     Tcl_DecrRefCount(pFd->pShmId);
334     pFd->pShmId = 0;
335   }
336   if( pFile->pMethods ){
337     ckfree((char *)pFile->pMethods);
338   }
339   rc = sqlite3OsClose(pFd->pReal);
340   ckfree((char *)pFd);
341   pTestfile->pFd = 0;
342   return rc;
343 }
344 
345 /*
346 ** Read data from an tvfs-file.
347 */
tvfsRead(sqlite3_file * pFile,void * zBuf,int iAmt,sqlite_int64 iOfst)348 static int tvfsRead(
349   sqlite3_file *pFile,
350   void *zBuf,
351   int iAmt,
352   sqlite_int64 iOfst
353 ){
354   TestvfsFd *p = tvfsGetFd(pFile);
355   return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
356 }
357 
358 /*
359 ** Write data to an tvfs-file.
360 */
tvfsWrite(sqlite3_file * pFile,const void * zBuf,int iAmt,sqlite_int64 iOfst)361 static int tvfsWrite(
362   sqlite3_file *pFile,
363   const void *zBuf,
364   int iAmt,
365   sqlite_int64 iOfst
366 ){
367   int rc = SQLITE_OK;
368   TestvfsFd *pFd = tvfsGetFd(pFile);
369   Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
370 
371   if( p->pScript && p->mask&TESTVFS_WRITE_MASK ){
372     tvfsExecTcl(p, "xWrite",
373         Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
374     );
375     tvfsResultCode(p, &rc);
376   }
377 
378   if( rc==SQLITE_OK && tvfsInjectFullerr(p) ){
379     rc = SQLITE_FULL;
380   }
381   if( rc==SQLITE_OK && p->mask&TESTVFS_WRITE_MASK && tvfsInjectIoerr(p) ){
382     rc = SQLITE_IOERR;
383   }
384 
385   if( rc==SQLITE_OK ){
386     rc = sqlite3OsWrite(pFd->pReal, zBuf, iAmt, iOfst);
387   }
388   return rc;
389 }
390 
391 /*
392 ** Truncate an tvfs-file.
393 */
tvfsTruncate(sqlite3_file * pFile,sqlite_int64 size)394 static int tvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){
395   int rc = SQLITE_OK;
396   TestvfsFd *pFd = tvfsGetFd(pFile);
397   Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
398 
399   if( p->pScript && p->mask&TESTVFS_TRUNCATE_MASK ){
400     tvfsExecTcl(p, "xTruncate",
401         Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
402     );
403     tvfsResultCode(p, &rc);
404   }
405 
406   if( rc==SQLITE_OK ){
407     rc = sqlite3OsTruncate(pFd->pReal, size);
408   }
409   return rc;
410 }
411 
412 /*
413 ** Sync an tvfs-file.
414 */
tvfsSync(sqlite3_file * pFile,int flags)415 static int tvfsSync(sqlite3_file *pFile, int flags){
416   int rc = SQLITE_OK;
417   TestvfsFd *pFd = tvfsGetFd(pFile);
418   Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
419 
420   if( p->pScript && p->mask&TESTVFS_SYNC_MASK ){
421     char *zFlags;
422 
423     switch( flags ){
424       case SQLITE_SYNC_NORMAL:
425         zFlags = "normal";
426         break;
427       case SQLITE_SYNC_FULL:
428         zFlags = "full";
429         break;
430       case SQLITE_SYNC_NORMAL|SQLITE_SYNC_DATAONLY:
431         zFlags = "normal|dataonly";
432         break;
433       case SQLITE_SYNC_FULL|SQLITE_SYNC_DATAONLY:
434         zFlags = "full|dataonly";
435         break;
436       default:
437         assert(0);
438     }
439 
440     tvfsExecTcl(p, "xSync",
441         Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId,
442         Tcl_NewStringObj(zFlags, -1)
443     );
444     tvfsResultCode(p, &rc);
445   }
446 
447   if( rc==SQLITE_OK && tvfsInjectFullerr(p) ) rc = SQLITE_FULL;
448 
449   if( rc==SQLITE_OK ){
450     rc = sqlite3OsSync(pFd->pReal, flags);
451   }
452 
453   return rc;
454 }
455 
456 /*
457 ** Return the current file-size of an tvfs-file.
458 */
tvfsFileSize(sqlite3_file * pFile,sqlite_int64 * pSize)459 static int tvfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
460   TestvfsFd *p = tvfsGetFd(pFile);
461   return sqlite3OsFileSize(p->pReal, pSize);
462 }
463 
464 /*
465 ** Lock an tvfs-file.
466 */
tvfsLock(sqlite3_file * pFile,int eLock)467 static int tvfsLock(sqlite3_file *pFile, int eLock){
468   TestvfsFd *p = tvfsGetFd(pFile);
469   return sqlite3OsLock(p->pReal, eLock);
470 }
471 
472 /*
473 ** Unlock an tvfs-file.
474 */
tvfsUnlock(sqlite3_file * pFile,int eLock)475 static int tvfsUnlock(sqlite3_file *pFile, int eLock){
476   TestvfsFd *p = tvfsGetFd(pFile);
477   return sqlite3OsUnlock(p->pReal, eLock);
478 }
479 
480 /*
481 ** Check if another file-handle holds a RESERVED lock on an tvfs-file.
482 */
tvfsCheckReservedLock(sqlite3_file * pFile,int * pResOut)483 static int tvfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){
484   TestvfsFd *p = tvfsGetFd(pFile);
485   return sqlite3OsCheckReservedLock(p->pReal, pResOut);
486 }
487 
488 /*
489 ** File control method. For custom operations on an tvfs-file.
490 */
tvfsFileControl(sqlite3_file * pFile,int op,void * pArg)491 static int tvfsFileControl(sqlite3_file *pFile, int op, void *pArg){
492   TestvfsFd *p = tvfsGetFd(pFile);
493   return sqlite3OsFileControl(p->pReal, op, pArg);
494 }
495 
496 /*
497 ** Return the sector-size in bytes for an tvfs-file.
498 */
tvfsSectorSize(sqlite3_file * pFile)499 static int tvfsSectorSize(sqlite3_file *pFile){
500   TestvfsFd *pFd = tvfsGetFd(pFile);
501   Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
502   if( p->iSectorsize>=0 ){
503     return p->iSectorsize;
504   }
505   return sqlite3OsSectorSize(pFd->pReal);
506 }
507 
508 /*
509 ** Return the device characteristic flags supported by an tvfs-file.
510 */
tvfsDeviceCharacteristics(sqlite3_file * pFile)511 static int tvfsDeviceCharacteristics(sqlite3_file *pFile){
512   TestvfsFd *pFd = tvfsGetFd(pFile);
513   Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
514   if( p->iDevchar>=0 ){
515     return p->iDevchar;
516   }
517   return sqlite3OsDeviceCharacteristics(pFd->pReal);
518 }
519 
520 /*
521 ** Open an tvfs file handle.
522 */
tvfsOpen(sqlite3_vfs * pVfs,const char * zName,sqlite3_file * pFile,int flags,int * pOutFlags)523 static int tvfsOpen(
524   sqlite3_vfs *pVfs,
525   const char *zName,
526   sqlite3_file *pFile,
527   int flags,
528   int *pOutFlags
529 ){
530   int rc;
531   TestvfsFile *pTestfile = (TestvfsFile *)pFile;
532   TestvfsFd *pFd;
533   Tcl_Obj *pId = 0;
534   Testvfs *p = (Testvfs *)pVfs->pAppData;
535 
536   pFd = (TestvfsFd *)ckalloc(sizeof(TestvfsFd) + PARENTVFS(pVfs)->szOsFile);
537   memset(pFd, 0, sizeof(TestvfsFd) + PARENTVFS(pVfs)->szOsFile);
538   pFd->pShm = 0;
539   pFd->pShmId = 0;
540   pFd->zFilename = zName;
541   pFd->pVfs = pVfs;
542   pFd->pReal = (sqlite3_file *)&pFd[1];
543   memset(pTestfile, 0, sizeof(TestvfsFile));
544   pTestfile->pFd = pFd;
545 
546   /* Evaluate the Tcl script:
547   **
548   **   SCRIPT xOpen FILENAME
549   **
550   ** If the script returns an SQLite error code other than SQLITE_OK, an
551   ** error is returned to the caller. If it returns SQLITE_OK, the new
552   ** connection is named "anon". Otherwise, the value returned by the
553   ** script is used as the connection name.
554   */
555   Tcl_ResetResult(p->interp);
556   if( p->pScript && p->mask&TESTVFS_OPEN_MASK ){
557     tvfsExecTcl(p, "xOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0);
558     if( tvfsResultCode(p, &rc) ){
559       if( rc!=SQLITE_OK ) return rc;
560     }else{
561       pId = Tcl_GetObjResult(p->interp);
562     }
563   }
564 
565   if( (p->mask&TESTVFS_OPEN_MASK) &&  tvfsInjectIoerr(p) ) return SQLITE_IOERR;
566   if( tvfsInjectCantopenerr(p) ) return SQLITE_CANTOPEN;
567   if( tvfsInjectFullerr(p) ) return SQLITE_FULL;
568 
569   if( !pId ){
570     pId = Tcl_NewStringObj("anon", -1);
571   }
572   Tcl_IncrRefCount(pId);
573   pFd->pShmId = pId;
574   Tcl_ResetResult(p->interp);
575 
576   rc = sqlite3OsOpen(PARENTVFS(pVfs), zName, pFd->pReal, flags, pOutFlags);
577   if( pFd->pReal->pMethods ){
578     sqlite3_io_methods *pMethods;
579     int nByte;
580 
581     if( pVfs->iVersion>1 ){
582       nByte = sizeof(sqlite3_io_methods);
583     }else{
584       nByte = offsetof(sqlite3_io_methods, xShmMap);
585     }
586 
587     pMethods = (sqlite3_io_methods *)ckalloc(nByte);
588     memcpy(pMethods, &tvfs_io_methods, nByte);
589     pMethods->iVersion = pVfs->iVersion;
590     if( pVfs->iVersion>1 && ((Testvfs *)pVfs->pAppData)->isNoshm ){
591       pMethods->xShmUnmap = 0;
592       pMethods->xShmLock = 0;
593       pMethods->xShmBarrier = 0;
594       pMethods->xShmMap = 0;
595     }
596     pFile->pMethods = pMethods;
597   }
598 
599   return rc;
600 }
601 
602 /*
603 ** Delete the file located at zPath. If the dirSync argument is true,
604 ** ensure the file-system modifications are synced to disk before
605 ** returning.
606 */
tvfsDelete(sqlite3_vfs * pVfs,const char * zPath,int dirSync)607 static int tvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
608   int rc = SQLITE_OK;
609   Testvfs *p = (Testvfs *)pVfs->pAppData;
610 
611   if( p->pScript && p->mask&TESTVFS_DELETE_MASK ){
612     tvfsExecTcl(p, "xDelete",
613         Tcl_NewStringObj(zPath, -1), Tcl_NewIntObj(dirSync), 0
614     );
615     tvfsResultCode(p, &rc);
616   }
617   if( rc==SQLITE_OK ){
618     rc = sqlite3OsDelete(PARENTVFS(pVfs), zPath, dirSync);
619   }
620   return rc;
621 }
622 
623 /*
624 ** Test for access permissions. Return true if the requested permission
625 ** is available, or false otherwise.
626 */
tvfsAccess(sqlite3_vfs * pVfs,const char * zPath,int flags,int * pResOut)627 static int tvfsAccess(
628   sqlite3_vfs *pVfs,
629   const char *zPath,
630   int flags,
631   int *pResOut
632 ){
633   Testvfs *p = (Testvfs *)pVfs->pAppData;
634   if( p->pScript && p->mask&TESTVFS_ACCESS_MASK ){
635     int rc;
636     char *zArg = 0;
637     if( flags==SQLITE_ACCESS_EXISTS ) zArg = "SQLITE_ACCESS_EXISTS";
638     if( flags==SQLITE_ACCESS_READWRITE ) zArg = "SQLITE_ACCESS_READWRITE";
639     if( flags==SQLITE_ACCESS_READ ) zArg = "SQLITE_ACCESS_READ";
640     tvfsExecTcl(p, "xAccess",
641         Tcl_NewStringObj(zPath, -1), Tcl_NewStringObj(zArg, -1), 0
642     );
643     if( tvfsResultCode(p, &rc) ){
644       if( rc!=SQLITE_OK ) return rc;
645     }else{
646       Tcl_Interp *interp = p->interp;
647       if( TCL_OK==Tcl_GetBooleanFromObj(0, Tcl_GetObjResult(interp), pResOut) ){
648         return SQLITE_OK;
649       }
650     }
651   }
652   return sqlite3OsAccess(PARENTVFS(pVfs), zPath, flags, pResOut);
653 }
654 
655 /*
656 ** Populate buffer zOut with the full canonical pathname corresponding
657 ** to the pathname in zPath. zOut is guaranteed to point to a buffer
658 ** of at least (DEVSYM_MAX_PATHNAME+1) bytes.
659 */
tvfsFullPathname(sqlite3_vfs * pVfs,const char * zPath,int nOut,char * zOut)660 static int tvfsFullPathname(
661   sqlite3_vfs *pVfs,
662   const char *zPath,
663   int nOut,
664   char *zOut
665 ){
666   return sqlite3OsFullPathname(PARENTVFS(pVfs), zPath, nOut, zOut);
667 }
668 
669 #ifndef SQLITE_OMIT_LOAD_EXTENSION
670 /*
671 ** Open the dynamic library located at zPath and return a handle.
672 */
tvfsDlOpen(sqlite3_vfs * pVfs,const char * zPath)673 static void *tvfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
674   return sqlite3OsDlOpen(PARENTVFS(pVfs), zPath);
675 }
676 
677 /*
678 ** Populate the buffer zErrMsg (size nByte bytes) with a human readable
679 ** utf-8 string describing the most recent error encountered associated
680 ** with dynamic libraries.
681 */
tvfsDlError(sqlite3_vfs * pVfs,int nByte,char * zErrMsg)682 static void tvfsDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
683   sqlite3OsDlError(PARENTVFS(pVfs), nByte, zErrMsg);
684 }
685 
686 /*
687 ** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
688 */
tvfsDlSym(sqlite3_vfs * pVfs,void * p,const char * zSym)689 static void (*tvfsDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
690   return sqlite3OsDlSym(PARENTVFS(pVfs), p, zSym);
691 }
692 
693 /*
694 ** Close the dynamic library handle pHandle.
695 */
tvfsDlClose(sqlite3_vfs * pVfs,void * pHandle)696 static void tvfsDlClose(sqlite3_vfs *pVfs, void *pHandle){
697   sqlite3OsDlClose(PARENTVFS(pVfs), pHandle);
698 }
699 #endif /* SQLITE_OMIT_LOAD_EXTENSION */
700 
701 /*
702 ** Populate the buffer pointed to by zBufOut with nByte bytes of
703 ** random data.
704 */
tvfsRandomness(sqlite3_vfs * pVfs,int nByte,char * zBufOut)705 static int tvfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
706   return sqlite3OsRandomness(PARENTVFS(pVfs), nByte, zBufOut);
707 }
708 
709 /*
710 ** Sleep for nMicro microseconds. Return the number of microseconds
711 ** actually slept.
712 */
tvfsSleep(sqlite3_vfs * pVfs,int nMicro)713 static int tvfsSleep(sqlite3_vfs *pVfs, int nMicro){
714   return sqlite3OsSleep(PARENTVFS(pVfs), nMicro);
715 }
716 
717 /*
718 ** Return the current time as a Julian Day number in *pTimeOut.
719 */
tvfsCurrentTime(sqlite3_vfs * pVfs,double * pTimeOut)720 static int tvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
721   return PARENTVFS(pVfs)->xCurrentTime(PARENTVFS(pVfs), pTimeOut);
722 }
723 
tvfsShmOpen(sqlite3_file * pFile)724 static int tvfsShmOpen(sqlite3_file *pFile){
725   Testvfs *p;
726   int rc = SQLITE_OK;             /* Return code */
727   TestvfsBuffer *pBuffer;         /* Buffer to open connection to */
728   TestvfsFd *pFd;                 /* The testvfs file structure */
729 
730   pFd = tvfsGetFd(pFile);
731   p = (Testvfs *)pFd->pVfs->pAppData;
732   assert( pFd->pShmId && pFd->pShm==0 && pFd->pNext==0 );
733 
734   /* Evaluate the Tcl script:
735   **
736   **   SCRIPT xShmOpen FILENAME
737   */
738   Tcl_ResetResult(p->interp);
739   if( p->pScript && p->mask&TESTVFS_SHMOPEN_MASK ){
740     tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0);
741     if( tvfsResultCode(p, &rc) ){
742       if( rc!=SQLITE_OK ) return rc;
743     }
744   }
745 
746   assert( rc==SQLITE_OK );
747   if( p->mask&TESTVFS_SHMOPEN_MASK && tvfsInjectIoerr(p) ){
748     return SQLITE_IOERR;
749   }
750 
751   /* Search for a TestvfsBuffer. Create a new one if required. */
752   for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){
753     if( 0==strcmp(pFd->zFilename, pBuffer->zFile) ) break;
754   }
755   if( !pBuffer ){
756     int nByte = sizeof(TestvfsBuffer) + strlen(pFd->zFilename) + 1;
757     pBuffer = (TestvfsBuffer *)ckalloc(nByte);
758     memset(pBuffer, 0, nByte);
759     pBuffer->zFile = (char *)&pBuffer[1];
760     strcpy(pBuffer->zFile, pFd->zFilename);
761     pBuffer->pNext = p->pBuffer;
762     p->pBuffer = pBuffer;
763   }
764 
765   /* Connect the TestvfsBuffer to the new TestvfsShm handle and return. */
766   pFd->pNext = pBuffer->pFile;
767   pBuffer->pFile = pFd;
768   pFd->pShm = pBuffer;
769   return SQLITE_OK;
770 }
771 
tvfsAllocPage(TestvfsBuffer * p,int iPage,int pgsz)772 static void tvfsAllocPage(TestvfsBuffer *p, int iPage, int pgsz){
773   assert( iPage<TESTVFS_MAX_PAGES );
774   if( p->aPage[iPage]==0 ){
775     p->aPage[iPage] = (u8 *)ckalloc(pgsz);
776     memset(p->aPage[iPage], 0, pgsz);
777     p->pgsz = pgsz;
778   }
779 }
780 
tvfsShmMap(sqlite3_file * pFile,int iPage,int pgsz,int isWrite,void volatile ** pp)781 static int tvfsShmMap(
782   sqlite3_file *pFile,            /* Handle open on database file */
783   int iPage,                      /* Page to retrieve */
784   int pgsz,                       /* Size of pages */
785   int isWrite,                    /* True to extend file if necessary */
786   void volatile **pp              /* OUT: Mapped memory */
787 ){
788   int rc = SQLITE_OK;
789   TestvfsFd *pFd = tvfsGetFd(pFile);
790   Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
791 
792   if( 0==pFd->pShm ){
793     rc = tvfsShmOpen(pFile);
794     if( rc!=SQLITE_OK ){
795       return rc;
796     }
797   }
798 
799   if( p->pScript && p->mask&TESTVFS_SHMMAP_MASK ){
800     Tcl_Obj *pArg = Tcl_NewObj();
801     Tcl_IncrRefCount(pArg);
802     Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(iPage));
803     Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(pgsz));
804     Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(isWrite));
805     tvfsExecTcl(p, "xShmMap",
806         Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, pArg
807     );
808     tvfsResultCode(p, &rc);
809     Tcl_DecrRefCount(pArg);
810   }
811   if( rc==SQLITE_OK && p->mask&TESTVFS_SHMMAP_MASK && tvfsInjectIoerr(p) ){
812     rc = SQLITE_IOERR;
813   }
814 
815   if( rc==SQLITE_OK && isWrite && !pFd->pShm->aPage[iPage] ){
816     tvfsAllocPage(pFd->pShm, iPage, pgsz);
817   }
818   *pp = (void volatile *)pFd->pShm->aPage[iPage];
819 
820   return rc;
821 }
822 
823 
tvfsShmLock(sqlite3_file * pFile,int ofst,int n,int flags)824 static int tvfsShmLock(
825   sqlite3_file *pFile,
826   int ofst,
827   int n,
828   int flags
829 ){
830   int rc = SQLITE_OK;
831   TestvfsFd *pFd = tvfsGetFd(pFile);
832   Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
833   int nLock;
834   char zLock[80];
835 
836   if( p->pScript && p->mask&TESTVFS_SHMLOCK_MASK ){
837     sqlite3_snprintf(sizeof(zLock), zLock, "%d %d", ofst, n);
838     nLock = strlen(zLock);
839     if( flags & SQLITE_SHM_LOCK ){
840       strcpy(&zLock[nLock], " lock");
841     }else{
842       strcpy(&zLock[nLock], " unlock");
843     }
844     nLock += strlen(&zLock[nLock]);
845     if( flags & SQLITE_SHM_SHARED ){
846       strcpy(&zLock[nLock], " shared");
847     }else{
848       strcpy(&zLock[nLock], " exclusive");
849     }
850     tvfsExecTcl(p, "xShmLock",
851         Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId,
852         Tcl_NewStringObj(zLock, -1)
853     );
854     tvfsResultCode(p, &rc);
855   }
856 
857   if( rc==SQLITE_OK && p->mask&TESTVFS_SHMLOCK_MASK && tvfsInjectIoerr(p) ){
858     rc = SQLITE_IOERR;
859   }
860 
861   if( rc==SQLITE_OK ){
862     int isLock = (flags & SQLITE_SHM_LOCK);
863     int isExcl = (flags & SQLITE_SHM_EXCLUSIVE);
864     u32 mask = (((1<<n)-1) << ofst);
865     if( isLock ){
866       TestvfsFd *p2;
867       for(p2=pFd->pShm->pFile; p2; p2=p2->pNext){
868         if( p2==pFd ) continue;
869         if( (p2->excllock&mask) || (isExcl && p2->sharedlock&mask) ){
870           rc = SQLITE_BUSY;
871           break;
872         }
873       }
874       if( rc==SQLITE_OK ){
875         if( isExcl )  pFd->excllock |= mask;
876         if( !isExcl ) pFd->sharedlock |= mask;
877       }
878     }else{
879       if( isExcl )  pFd->excllock &= (~mask);
880       if( !isExcl ) pFd->sharedlock &= (~mask);
881     }
882   }
883 
884   return rc;
885 }
886 
tvfsShmBarrier(sqlite3_file * pFile)887 static void tvfsShmBarrier(sqlite3_file *pFile){
888   TestvfsFd *pFd = tvfsGetFd(pFile);
889   Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
890 
891   if( p->pScript && p->mask&TESTVFS_SHMBARRIER_MASK ){
892     tvfsExecTcl(p, "xShmBarrier",
893         Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0
894     );
895   }
896 }
897 
tvfsShmUnmap(sqlite3_file * pFile,int deleteFlag)898 static int tvfsShmUnmap(
899   sqlite3_file *pFile,
900   int deleteFlag
901 ){
902   int rc = SQLITE_OK;
903   TestvfsFd *pFd = tvfsGetFd(pFile);
904   Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
905   TestvfsBuffer *pBuffer = pFd->pShm;
906   TestvfsFd **ppFd;
907 
908   if( !pBuffer ) return SQLITE_OK;
909   assert( pFd->pShmId && pFd->pShm );
910 
911   if( p->pScript && p->mask&TESTVFS_SHMCLOSE_MASK ){
912     tvfsExecTcl(p, "xShmUnmap",
913         Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0
914     );
915     tvfsResultCode(p, &rc);
916   }
917 
918   for(ppFd=&pBuffer->pFile; *ppFd!=pFd; ppFd=&((*ppFd)->pNext));
919   assert( (*ppFd)==pFd );
920   *ppFd = pFd->pNext;
921   pFd->pNext = 0;
922 
923   if( pBuffer->pFile==0 ){
924     int i;
925     TestvfsBuffer **pp;
926     for(pp=&p->pBuffer; *pp!=pBuffer; pp=&((*pp)->pNext));
927     *pp = (*pp)->pNext;
928     for(i=0; pBuffer->aPage[i]; i++){
929       ckfree((char *)pBuffer->aPage[i]);
930     }
931     ckfree((char *)pBuffer);
932   }
933   pFd->pShm = 0;
934 
935   return rc;
936 }
937 
testvfs_obj_cmd(ClientData cd,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])938 static int testvfs_obj_cmd(
939   ClientData cd,
940   Tcl_Interp *interp,
941   int objc,
942   Tcl_Obj *CONST objv[]
943 ){
944   Testvfs *p = (Testvfs *)cd;
945 
946   enum DB_enum {
947     CMD_SHM, CMD_DELETE, CMD_FILTER, CMD_IOERR, CMD_SCRIPT,
948     CMD_DEVCHAR, CMD_SECTORSIZE, CMD_FULLERR, CMD_CANTOPENERR
949   };
950   struct TestvfsSubcmd {
951     char *zName;
952     enum DB_enum eCmd;
953   } aSubcmd[] = {
954     { "shm",         CMD_SHM         },
955     { "delete",      CMD_DELETE      },
956     { "filter",      CMD_FILTER      },
957     { "ioerr",       CMD_IOERR       },
958     { "fullerr",     CMD_FULLERR     },
959     { "cantopenerr", CMD_CANTOPENERR },
960     { "script",      CMD_SCRIPT      },
961     { "devchar",     CMD_DEVCHAR     },
962     { "sectorsize",  CMD_SECTORSIZE  },
963     { 0, 0 }
964   };
965   int i;
966 
967   if( objc<2 ){
968     Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
969     return TCL_ERROR;
970   }
971   if( Tcl_GetIndexFromObjStruct(
972         interp, objv[1], aSubcmd, sizeof(aSubcmd[0]), "subcommand", 0, &i)
973   ){
974     return TCL_ERROR;
975   }
976   Tcl_ResetResult(interp);
977 
978   switch( aSubcmd[i].eCmd ){
979     case CMD_SHM: {
980       Tcl_Obj *pObj;
981       int i;
982       TestvfsBuffer *pBuffer;
983       char *zName;
984       if( objc!=3 && objc!=4 ){
985         Tcl_WrongNumArgs(interp, 2, objv, "FILE ?VALUE?");
986         return TCL_ERROR;
987       }
988       zName = ckalloc(p->pParent->mxPathname);
989       p->pParent->xFullPathname(
990           p->pParent, Tcl_GetString(objv[2]),
991           p->pParent->mxPathname, zName
992       );
993       for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){
994         if( 0==strcmp(pBuffer->zFile, zName) ) break;
995       }
996       ckfree(zName);
997       if( !pBuffer ){
998         Tcl_AppendResult(interp, "no such file: ", Tcl_GetString(objv[2]), 0);
999         return TCL_ERROR;
1000       }
1001       if( objc==4 ){
1002         int n;
1003         u8 *a = Tcl_GetByteArrayFromObj(objv[3], &n);
1004         int pgsz = pBuffer->pgsz;
1005         if( pgsz==0 ) pgsz = 65536;
1006         for(i=0; i*pgsz<n; i++){
1007           int nByte = pgsz;
1008           tvfsAllocPage(pBuffer, i, pgsz);
1009           if( n-i*pgsz<pgsz ){
1010             nByte = n;
1011           }
1012           memcpy(pBuffer->aPage[i], &a[i*pgsz], nByte);
1013         }
1014       }
1015 
1016       pObj = Tcl_NewObj();
1017       for(i=0; pBuffer->aPage[i]; i++){
1018         int pgsz = pBuffer->pgsz;
1019         if( pgsz==0 ) pgsz = 65536;
1020         Tcl_AppendObjToObj(pObj, Tcl_NewByteArrayObj(pBuffer->aPage[i], pgsz));
1021       }
1022       Tcl_SetObjResult(interp, pObj);
1023       break;
1024     }
1025 
1026     case CMD_FILTER: {
1027       static struct VfsMethod {
1028         char *zName;
1029         int mask;
1030       } vfsmethod [] = {
1031         { "xShmOpen",    TESTVFS_SHMOPEN_MASK },
1032         { "xShmLock",    TESTVFS_SHMLOCK_MASK },
1033         { "xShmBarrier", TESTVFS_SHMBARRIER_MASK },
1034         { "xShmUnmap",   TESTVFS_SHMCLOSE_MASK },
1035         { "xShmMap",     TESTVFS_SHMMAP_MASK },
1036         { "xSync",       TESTVFS_SYNC_MASK },
1037         { "xDelete",     TESTVFS_DELETE_MASK },
1038         { "xWrite",      TESTVFS_WRITE_MASK },
1039         { "xTruncate",   TESTVFS_TRUNCATE_MASK },
1040         { "xOpen",       TESTVFS_OPEN_MASK },
1041         { "xClose",      TESTVFS_CLOSE_MASK },
1042         { "xAccess",     TESTVFS_ACCESS_MASK },
1043       };
1044       Tcl_Obj **apElem = 0;
1045       int nElem = 0;
1046       int i;
1047       int mask = 0;
1048       if( objc!=3 ){
1049         Tcl_WrongNumArgs(interp, 2, objv, "LIST");
1050         return TCL_ERROR;
1051       }
1052       if( Tcl_ListObjGetElements(interp, objv[2], &nElem, &apElem) ){
1053         return TCL_ERROR;
1054       }
1055       Tcl_ResetResult(interp);
1056       for(i=0; i<nElem; i++){
1057         int iMethod;
1058         char *zElem = Tcl_GetString(apElem[i]);
1059         for(iMethod=0; iMethod<ArraySize(vfsmethod); iMethod++){
1060           if( strcmp(zElem, vfsmethod[iMethod].zName)==0 ){
1061             mask |= vfsmethod[iMethod].mask;
1062             break;
1063           }
1064         }
1065         if( iMethod==ArraySize(vfsmethod) ){
1066           Tcl_AppendResult(interp, "unknown method: ", zElem, 0);
1067           return TCL_ERROR;
1068         }
1069       }
1070       p->mask = mask;
1071       break;
1072     }
1073 
1074     case CMD_SCRIPT: {
1075       if( objc==3 ){
1076         int nByte;
1077         if( p->pScript ){
1078           Tcl_DecrRefCount(p->pScript);
1079           ckfree((char *)p->apScript);
1080           p->apScript = 0;
1081           p->nScript = 0;
1082           p->pScript = 0;
1083         }
1084         Tcl_GetStringFromObj(objv[2], &nByte);
1085         if( nByte>0 ){
1086           p->pScript = Tcl_DuplicateObj(objv[2]);
1087           Tcl_IncrRefCount(p->pScript);
1088         }
1089       }else if( objc!=2 ){
1090         Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?");
1091         return TCL_ERROR;
1092       }
1093 
1094       Tcl_ResetResult(interp);
1095       if( p->pScript ) Tcl_SetObjResult(interp, p->pScript);
1096 
1097       break;
1098     }
1099 
1100     /*
1101     ** TESTVFS ioerr ?IFAIL PERSIST?
1102     **
1103     **   Where IFAIL is an integer and PERSIST is boolean.
1104     */
1105     case CMD_CANTOPENERR:
1106     case CMD_IOERR:
1107     case CMD_FULLERR: {
1108       TestFaultInject *pTest;
1109       int iRet;
1110 
1111       switch( aSubcmd[i].eCmd ){
1112         case CMD_IOERR: pTest = &p->ioerr_err; break;
1113         case CMD_FULLERR: pTest = &p->full_err; break;
1114         case CMD_CANTOPENERR: pTest = &p->cantopen_err; break;
1115         default: assert(0);
1116       }
1117       iRet = pTest->nFail;
1118       pTest->nFail = 0;
1119       pTest->eFault = 0;
1120       pTest->iCnt = 0;
1121 
1122       if( objc==4 ){
1123         int iCnt, iPersist;
1124         if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iCnt)
1125          || TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[3], &iPersist)
1126         ){
1127           return TCL_ERROR;
1128         }
1129         pTest->eFault = iPersist?FAULT_INJECT_PERSISTENT:FAULT_INJECT_TRANSIENT;
1130         pTest->iCnt = iCnt;
1131       }else if( objc!=2 ){
1132         Tcl_WrongNumArgs(interp, 2, objv, "?CNT PERSIST?");
1133         return TCL_ERROR;
1134       }
1135       Tcl_SetObjResult(interp, Tcl_NewIntObj(iRet));
1136       break;
1137     }
1138 
1139     case CMD_DELETE: {
1140       Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
1141       break;
1142     }
1143 
1144     case CMD_DEVCHAR: {
1145       struct DeviceFlag {
1146         char *zName;
1147         int iValue;
1148       } aFlag[] = {
1149         { "default",               -1 },
1150         { "atomic",                SQLITE_IOCAP_ATOMIC      },
1151         { "atomic512",             SQLITE_IOCAP_ATOMIC512   },
1152         { "atomic1k",              SQLITE_IOCAP_ATOMIC1K    },
1153         { "atomic2k",              SQLITE_IOCAP_ATOMIC2K    },
1154         { "atomic4k",              SQLITE_IOCAP_ATOMIC4K    },
1155         { "atomic8k",              SQLITE_IOCAP_ATOMIC8K    },
1156         { "atomic16k",             SQLITE_IOCAP_ATOMIC16K   },
1157         { "atomic32k",             SQLITE_IOCAP_ATOMIC32K   },
1158         { "atomic64k",             SQLITE_IOCAP_ATOMIC64K   },
1159         { "sequential",            SQLITE_IOCAP_SEQUENTIAL  },
1160         { "safe_append",           SQLITE_IOCAP_SAFE_APPEND },
1161         { "undeletable_when_open", SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN },
1162         { 0, 0 }
1163       };
1164       Tcl_Obj *pRet;
1165       int iFlag;
1166 
1167       if( objc>3 ){
1168         Tcl_WrongNumArgs(interp, 2, objv, "?ATTR-LIST?");
1169         return TCL_ERROR;
1170       }
1171       if( objc==3 ){
1172         int j;
1173         int iNew = 0;
1174         Tcl_Obj **flags = 0;
1175         int nFlags = 0;
1176 
1177         if( Tcl_ListObjGetElements(interp, objv[2], &nFlags, &flags) ){
1178           return TCL_ERROR;
1179         }
1180 
1181         for(j=0; j<nFlags; j++){
1182           int idx = 0;
1183           if( Tcl_GetIndexFromObjStruct(interp, flags[j], aFlag,
1184                 sizeof(aFlag[0]), "flag", 0, &idx)
1185           ){
1186             return TCL_ERROR;
1187           }
1188           if( aFlag[idx].iValue<0 && nFlags>1 ){
1189             Tcl_AppendResult(interp, "bad flags: ", Tcl_GetString(objv[2]), 0);
1190             return TCL_ERROR;
1191           }
1192           iNew |= aFlag[idx].iValue;
1193         }
1194 
1195         p->iDevchar = iNew;
1196       }
1197 
1198       pRet = Tcl_NewObj();
1199       for(iFlag=0; iFlag<sizeof(aFlag)/sizeof(aFlag[0]); iFlag++){
1200         if( p->iDevchar & aFlag[iFlag].iValue ){
1201           Tcl_ListObjAppendElement(
1202               interp, pRet, Tcl_NewStringObj(aFlag[iFlag].zName, -1)
1203           );
1204         }
1205       }
1206       Tcl_SetObjResult(interp, pRet);
1207 
1208       break;
1209     }
1210 
1211     case CMD_SECTORSIZE: {
1212       if( objc>3 ){
1213         Tcl_WrongNumArgs(interp, 2, objv, "?VALUE?");
1214         return TCL_ERROR;
1215       }
1216       if( objc==3 ){
1217         int iNew = 0;
1218         if( Tcl_GetIntFromObj(interp, objv[2], &iNew) ){
1219           return TCL_ERROR;
1220         }
1221         p->iSectorsize = iNew;
1222       }
1223       Tcl_SetObjResult(interp, Tcl_NewIntObj(p->iSectorsize));
1224       break;
1225     }
1226   }
1227 
1228   return TCL_OK;
1229 }
1230 
testvfs_obj_del(ClientData cd)1231 static void testvfs_obj_del(ClientData cd){
1232   Testvfs *p = (Testvfs *)cd;
1233   if( p->pScript ) Tcl_DecrRefCount(p->pScript);
1234   sqlite3_vfs_unregister(p->pVfs);
1235   ckfree((char *)p->apScript);
1236   ckfree((char *)p->pVfs);
1237   ckfree((char *)p);
1238 }
1239 
1240 /*
1241 ** Usage:  testvfs VFSNAME ?SWITCHES?
1242 **
1243 ** Switches are:
1244 **
1245 **   -noshm   BOOLEAN             (True to omit shm methods. Default false)
1246 **   -default BOOLEAN             (True to make the vfs default. Default false)
1247 **
1248 ** This command creates two things when it is invoked: an SQLite VFS, and
1249 ** a Tcl command. Both are named VFSNAME. The VFS is installed. It is not
1250 ** installed as the default VFS.
1251 **
1252 ** The VFS passes all file I/O calls through to the underlying VFS.
1253 **
1254 ** Whenever the xShmMap method of the VFS
1255 ** is invoked, the SCRIPT is executed as follows:
1256 **
1257 **   SCRIPT xShmMap    FILENAME ID
1258 **
1259 ** The value returned by the invocation of SCRIPT above is interpreted as
1260 ** an SQLite error code and returned to SQLite. Either a symbolic
1261 ** "SQLITE_OK" or numeric "0" value may be returned.
1262 **
1263 ** The contents of the shared-memory buffer associated with a given file
1264 ** may be read and set using the following command:
1265 **
1266 **   VFSNAME shm FILENAME ?NEWVALUE?
1267 **
1268 ** When the xShmLock method is invoked by SQLite, the following script is
1269 ** run:
1270 **
1271 **   SCRIPT xShmLock    FILENAME ID LOCK
1272 **
1273 ** where LOCK is of the form "OFFSET NBYTE lock/unlock shared/exclusive"
1274 */
testvfs_cmd(ClientData cd,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])1275 static int testvfs_cmd(
1276   ClientData cd,
1277   Tcl_Interp *interp,
1278   int objc,
1279   Tcl_Obj *CONST objv[]
1280 ){
1281   static sqlite3_vfs tvfs_vfs = {
1282     2,                            /* iVersion */
1283     0,                            /* szOsFile */
1284     0,                            /* mxPathname */
1285     0,                            /* pNext */
1286     0,                            /* zName */
1287     0,                            /* pAppData */
1288     tvfsOpen,                     /* xOpen */
1289     tvfsDelete,                   /* xDelete */
1290     tvfsAccess,                   /* xAccess */
1291     tvfsFullPathname,             /* xFullPathname */
1292 #ifndef SQLITE_OMIT_LOAD_EXTENSION
1293     tvfsDlOpen,                   /* xDlOpen */
1294     tvfsDlError,                  /* xDlError */
1295     tvfsDlSym,                    /* xDlSym */
1296     tvfsDlClose,                  /* xDlClose */
1297 #else
1298     0,                            /* xDlOpen */
1299     0,                            /* xDlError */
1300     0,                            /* xDlSym */
1301     0,                            /* xDlClose */
1302 #endif /* SQLITE_OMIT_LOAD_EXTENSION */
1303     tvfsRandomness,               /* xRandomness */
1304     tvfsSleep,                    /* xSleep */
1305     tvfsCurrentTime,              /* xCurrentTime */
1306     0,                            /* xGetLastError */
1307     0,                            /* xCurrentTimeInt64 */
1308   };
1309 
1310   Testvfs *p;                     /* New object */
1311   sqlite3_vfs *pVfs;              /* New VFS */
1312   char *zVfs;
1313   int nByte;                      /* Bytes of space to allocate at p */
1314 
1315   int i;
1316   int isNoshm = 0;                /* True if -noshm is passed */
1317   int isDefault = 0;              /* True if -default is passed */
1318   int szOsFile = 0;               /* Value passed to -szosfile */
1319   int mxPathname = -1;            /* Value passed to -mxpathname */
1320   int iVersion = 2;               /* Value passed to -iversion */
1321 
1322   if( objc<2 || 0!=(objc%2) ) goto bad_args;
1323   for(i=2; i<objc; i += 2){
1324     int nSwitch;
1325     char *zSwitch;
1326     zSwitch = Tcl_GetStringFromObj(objv[i], &nSwitch);
1327 
1328     if( nSwitch>2 && 0==strncmp("-noshm", zSwitch, nSwitch) ){
1329       if( Tcl_GetBooleanFromObj(interp, objv[i+1], &isNoshm) ){
1330         return TCL_ERROR;
1331       }
1332     }
1333     else if( nSwitch>2 && 0==strncmp("-default", zSwitch, nSwitch) ){
1334       if( Tcl_GetBooleanFromObj(interp, objv[i+1], &isDefault) ){
1335         return TCL_ERROR;
1336       }
1337     }
1338     else if( nSwitch>2 && 0==strncmp("-szosfile", zSwitch, nSwitch) ){
1339       if( Tcl_GetIntFromObj(interp, objv[i+1], &szOsFile) ){
1340         return TCL_ERROR;
1341       }
1342     }
1343     else if( nSwitch>2 && 0==strncmp("-mxpathname", zSwitch, nSwitch) ){
1344       if( Tcl_GetIntFromObj(interp, objv[i+1], &mxPathname) ){
1345         return TCL_ERROR;
1346       }
1347     }
1348     else if( nSwitch>2 && 0==strncmp("-iversion", zSwitch, nSwitch) ){
1349       if( Tcl_GetIntFromObj(interp, objv[i+1], &iVersion) ){
1350         return TCL_ERROR;
1351       }
1352     }
1353     else{
1354       goto bad_args;
1355     }
1356   }
1357 
1358   if( szOsFile<sizeof(TestvfsFile) ){
1359     szOsFile = sizeof(TestvfsFile);
1360   }
1361 
1362   zVfs = Tcl_GetString(objv[1]);
1363   nByte = sizeof(Testvfs) + strlen(zVfs)+1;
1364   p = (Testvfs *)ckalloc(nByte);
1365   memset(p, 0, nByte);
1366   p->iDevchar = -1;
1367   p->iSectorsize = -1;
1368 
1369   /* Create the new object command before querying SQLite for a default VFS
1370   ** to use for 'real' IO operations. This is because creating the new VFS
1371   ** may delete an existing [testvfs] VFS of the same name. If such a VFS
1372   ** is currently the default, the new [testvfs] may end up calling the
1373   ** methods of a deleted object.
1374   */
1375   Tcl_CreateObjCommand(interp, zVfs, testvfs_obj_cmd, p, testvfs_obj_del);
1376   p->pParent = sqlite3_vfs_find(0);
1377   p->interp = interp;
1378 
1379   p->zName = (char *)&p[1];
1380   memcpy(p->zName, zVfs, strlen(zVfs)+1);
1381 
1382   pVfs = (sqlite3_vfs *)ckalloc(sizeof(sqlite3_vfs));
1383   memcpy(pVfs, &tvfs_vfs, sizeof(sqlite3_vfs));
1384   pVfs->pAppData = (void *)p;
1385   pVfs->iVersion = iVersion;
1386   pVfs->zName = p->zName;
1387   pVfs->mxPathname = p->pParent->mxPathname;
1388   if( mxPathname>=0 && mxPathname<pVfs->mxPathname ){
1389     pVfs->mxPathname = mxPathname;
1390   }
1391   pVfs->szOsFile = szOsFile;
1392   p->pVfs = pVfs;
1393   p->isNoshm = isNoshm;
1394   p->mask = TESTVFS_ALL_MASK;
1395 
1396   sqlite3_vfs_register(pVfs, isDefault);
1397 
1398   return TCL_OK;
1399 
1400  bad_args:
1401   Tcl_WrongNumArgs(interp, 1, objv, "VFSNAME ?-noshm BOOL? ?-default BOOL? ?-mxpathname INT? ?-szosfile INT? ?-iversion INT?");
1402   return TCL_ERROR;
1403 }
1404 
Sqlitetestvfs_Init(Tcl_Interp * interp)1405 int Sqlitetestvfs_Init(Tcl_Interp *interp){
1406   Tcl_CreateObjCommand(interp, "testvfs", testvfs_cmd, 0, 0);
1407   return TCL_OK;
1408 }
1409 
1410 #endif
1411