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