Home | History | Annotate | Download | only in src
      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 
    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 
    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 
    252 static int tvfsInjectIoerr(Testvfs *p){
    253   return tvfsInjectFault(&p->ioerr_err);
    254 }
    255 
    256 static int tvfsInjectFullerr(Testvfs *p){
    257   return tvfsInjectFault(&p->full_err);
    258 }
    259 static int tvfsInjectCantopenerr(Testvfs *p){
    260   return tvfsInjectFault(&p->cantopen_err);
    261 }
    262 
    263 
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    720 static int tvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
    721   return PARENTVFS(pVfs)->xCurrentTime(PARENTVFS(pVfs), pTimeOut);
    722 }
    723 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
   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 */
   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 
   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