File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sqlite3 / src / test_vfs.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 17:04:17 2012 UTC (12 years, 7 months ago) by misho
Branches: sqlite3, MAIN
CVS tags: v3_7_10, HEAD
sqlite3

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>