Annotation of embedaddon/sqlite3/src/test_vfs.c, revision 1.1
1.1 ! misho 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>