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 if( op==SQLITE_FCNTL_PRAGMA ){ 484 char **argv = (char**)pArg; 485 if( sqlite3_stricmp(argv[1],"error")==0 ){ 486 int rc = SQLITE_ERROR; 487 if( argv[2] ){ 488 const char *z = argv[2]; 489 int x = atoi(z); 490 if( x ){ 491 rc = x; 492 while( sqlite3Isdigit(z[0]) ){ z++; } 493 while( sqlite3Isspace(z[0]) ){ z++; } 494 } 495 if( z[0] ) argv[0] = sqlite3_mprintf("%s", z); 496 } 497 return rc; 498 } 499 if( sqlite3_stricmp(argv[1], "filename")==0 ){ 500 argv[0] = sqlite3_mprintf("%s", p->zFilename); 501 return SQLITE_OK; 502 } 503 } 504 return sqlite3OsFileControl(p->pReal, op, pArg); 505 } 506 507 /* 508 ** Return the sector-size in bytes for an tvfs-file. 509 */ 510 static int tvfsSectorSize(sqlite3_file *pFile){ 511 TestvfsFd *pFd = tvfsGetFd(pFile); 512 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; 513 if( p->iSectorsize>=0 ){ 514 return p->iSectorsize; 515 } 516 return sqlite3OsSectorSize(pFd->pReal); 517 } 518 519 /* 520 ** Return the device characteristic flags supported by an tvfs-file. 521 */ 522 static int tvfsDeviceCharacteristics(sqlite3_file *pFile){ 523 TestvfsFd *pFd = tvfsGetFd(pFile); 524 Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; 525 if( p->iDevchar>=0 ){ 526 return p->iDevchar; 527 } 528 return sqlite3OsDeviceCharacteristics(pFd->pReal); 529 } 530 531 /* 532 ** Open an tvfs file handle. 533 */ 534 static int tvfsOpen( 535 sqlite3_vfs *pVfs, 536 const char *zName, 537 sqlite3_file *pFile, 538 int flags, 539 int *pOutFlags 540 ){ 541 int rc; 542 TestvfsFile *pTestfile = (TestvfsFile *)pFile; 543 TestvfsFd *pFd; 544 Tcl_Obj *pId = 0; 545 Testvfs *p = (Testvfs *)pVfs->pAppData; 546 547 pFd = (TestvfsFd *)ckalloc(sizeof(TestvfsFd) + PARENTVFS(pVfs)->szOsFile); 548 memset(pFd, 0, sizeof(TestvfsFd) + PARENTVFS(pVfs)->szOsFile); 549 pFd->pShm = 0; 550 pFd->pShmId = 0; 551 pFd->zFilename = zName; 552 pFd->pVfs = pVfs; 553 pFd->pReal = (sqlite3_file *)&pFd[1]; 554 memset(pTestfile, 0, sizeof(TestvfsFile)); 555 pTestfile->pFd = pFd; 556 557 /* Evaluate the Tcl script: 558 ** 559 ** SCRIPT xOpen FILENAME KEY-VALUE-ARGS 560 ** 561 ** If the script returns an SQLite error code other than SQLITE_OK, an 562 ** error is returned to the caller. If it returns SQLITE_OK, the new 563 ** connection is named "anon". Otherwise, the value returned by the 564 ** script is used as the connection name. 565 */ 566 Tcl_ResetResult(p->interp); 567 if( p->pScript && p->mask&TESTVFS_OPEN_MASK ){ 568 Tcl_Obj *pArg = Tcl_NewObj(); 569 Tcl_IncrRefCount(pArg); 570 if( flags&SQLITE_OPEN_MAIN_DB ){ 571 const char *z = &zName[strlen(zName)+1]; 572 while( *z ){ 573 Tcl_ListObjAppendElement(0, pArg, Tcl_NewStringObj(z, -1)); 574 z += strlen(z) + 1; 575 Tcl_ListObjAppendElement(0, pArg, Tcl_NewStringObj(z, -1)); 576 z += strlen(z) + 1; 577 } 578 } 579 tvfsExecTcl(p, "xOpen", Tcl_NewStringObj(pFd->zFilename, -1), pArg, 0); 580 Tcl_DecrRefCount(pArg); 581 if( tvfsResultCode(p, &rc) ){ 582 if( rc!=SQLITE_OK ) return rc; 583 }else{ 584 pId = Tcl_GetObjResult(p->interp); 585 } 586 } 587 588 if( (p->mask&TESTVFS_OPEN_MASK) && tvfsInjectIoerr(p) ) return SQLITE_IOERR; 589 if( tvfsInjectCantopenerr(p) ) return SQLITE_CANTOPEN; 590 if( tvfsInjectFullerr(p) ) return SQLITE_FULL; 591 592 if( !pId ){ 593 pId = Tcl_NewStringObj("anon", -1); 594 } 595 Tcl_IncrRefCount(pId); 596 pFd->pShmId = pId; 597 Tcl_ResetResult(p->interp); 598 599 rc = sqlite3OsOpen(PARENTVFS(pVfs), zName, pFd->pReal, flags, pOutFlags); 600 if( pFd->pReal->pMethods ){ 601 sqlite3_io_methods *pMethods; 602 int nByte; 603 604 if( pVfs->iVersion>1 ){ 605 nByte = sizeof(sqlite3_io_methods); 606 }else{ 607 nByte = offsetof(sqlite3_io_methods, xShmMap); 608 } 609 610 pMethods = (sqlite3_io_methods *)ckalloc(nByte); 611 memcpy(pMethods, &tvfs_io_methods, nByte); 612 pMethods->iVersion = pVfs->iVersion; 613 if( pVfs->iVersion>1 && ((Testvfs *)pVfs->pAppData)->isNoshm ){ 614 pMethods->xShmUnmap = 0; 615 pMethods->xShmLock = 0; 616 pMethods->xShmBarrier = 0; 617 pMethods->xShmMap = 0; 618 } 619 pFile->pMethods = pMethods; 620 } 621 622 return rc; 623 } 624 625 /* 626 ** Delete the file located at zPath. If the dirSync argument is true, 627 ** ensure the file-system modifications are synced to disk before 628 ** returning. 629 */ 630 static int tvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ 631 int rc = SQLITE_OK; 632 Testvfs *p = (Testvfs *)pVfs->pAppData; 633 634 if( p->pScript && p->mask&TESTVFS_DELETE_MASK ){ 635 tvfsExecTcl(p, "xDelete", 636 Tcl_NewStringObj(zPath, -1), Tcl_NewIntObj(dirSync), 0 637 ); 638 tvfsResultCode(p, &rc); 639 } 640 if( rc==SQLITE_OK ){ 641 rc = sqlite3OsDelete(PARENTVFS(pVfs), zPath, dirSync); 642 } 643 return rc; 644 } 645 646 /* 647 ** Test for access permissions. Return true if the requested permission 648 ** is available, or false otherwise. 649 */ 650 static int tvfsAccess( 651 sqlite3_vfs *pVfs, 652 const char *zPath, 653 int flags, 654 int *pResOut 655 ){ 656 Testvfs *p = (Testvfs *)pVfs->pAppData; 657 if( p->pScript && p->mask&TESTVFS_ACCESS_MASK ){ 658 int rc; 659 char *zArg = 0; 660 if( flags==SQLITE_ACCESS_EXISTS ) zArg = "SQLITE_ACCESS_EXISTS"; 661 if( flags==SQLITE_ACCESS_READWRITE ) zArg = "SQLITE_ACCESS_READWRITE"; 662 if( flags==SQLITE_ACCESS_READ ) zArg = "SQLITE_ACCESS_READ"; 663 tvfsExecTcl(p, "xAccess", 664 Tcl_NewStringObj(zPath, -1), Tcl_NewStringObj(zArg, -1), 0 665 ); 666 if( tvfsResultCode(p, &rc) ){ 667 if( rc!=SQLITE_OK ) return rc; 668 }else{ 669 Tcl_Interp *interp = p->interp; 670 if( TCL_OK==Tcl_GetBooleanFromObj(0, Tcl_GetObjResult(interp), pResOut) ){ 671 return SQLITE_OK; 672 } 673 } 674 } 675 return sqlite3OsAccess(PARENTVFS(pVfs), zPath, flags, pResOut); 676 } 677 678 /* 679 ** Populate buffer zOut with the full canonical pathname corresponding 680 ** to the pathname in zPath. zOut is guaranteed to point to a buffer 681 ** of at least (DEVSYM_MAX_PATHNAME+1) bytes. 682 */ 683 static int tvfsFullPathname( 684 sqlite3_vfs *pVfs, 685 const char *zPath, 686 int nOut, 687 char *zOut 688 ){ 689 Testvfs *p = (Testvfs *)pVfs->pAppData; 690 if( p->pScript && p->mask&TESTVFS_FULLPATHNAME_MASK ){ 691 int rc; 692 tvfsExecTcl(p, "xFullPathname", Tcl_NewStringObj(zPath, -1), 0, 0); 693 if( tvfsResultCode(p, &rc) ){ 694 if( rc!=SQLITE_OK ) return rc; 695 } 696 } 697 return sqlite3OsFullPathname(PARENTVFS(pVfs), zPath, nOut, zOut); 698 } 699 700 #ifndef SQLITE_OMIT_LOAD_EXTENSION 701 /* 702 ** Open the dynamic library located at zPath and return a handle. 703 */ 704 static void *tvfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){ 705 return sqlite3OsDlOpen(PARENTVFS(pVfs), zPath); 706 } 707 708 /* 709 ** Populate the buffer zErrMsg (size nByte bytes) with a human readable 710 ** utf-8 string describing the most recent error encountered associated 711 ** with dynamic libraries. 712 */ 713 static void tvfsDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ 714 sqlite3OsDlError(PARENTVFS(pVfs), nByte, zErrMsg); 715 } 716 717 /* 718 ** Return a pointer to the symbol zSymbol in the dynamic library pHandle. 719 */ 720 static void (*tvfsDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){ 721 return sqlite3OsDlSym(PARENTVFS(pVfs), p, zSym); 722 } 723 724 /* 725 ** Close the dynamic library handle pHandle. 726 */ 727 static void tvfsDlClose(sqlite3_vfs *pVfs, void *pHandle){ 728 sqlite3OsDlClose(PARENTVFS(pVfs), pHandle); 729 } 730 #endif /* SQLITE_OMIT_LOAD_EXTENSION */ 731 732 /* 733 ** Populate the buffer pointed to by zBufOut with nByte bytes of 734 ** random data. 735 */ 736 static int tvfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ 737 return sqlite3OsRandomness(PARENTVFS(pVfs), nByte, zBufOut); 738 } 739 740 /* 741 ** Sleep for nMicro microseconds. Return the number of microseconds 742 ** actually slept. 743 */ 744 static int tvfsSleep(sqlite3_vfs *pVfs, int nMicro){ 745 return sqlite3OsSleep(PARENTVFS(pVfs), nMicro); 746 } 747 748 /* 749 ** Return the current time as a Julian Day number in *pTimeOut. 750 */ 751 static int tvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ 752 return PARENTVFS(pVfs)->xCurrentTime(PARENTVFS(pVfs), pTimeOut); 753 } 754 755 static int tvfsShmOpen(sqlite3_file *pFile){ 756 Testvfs *p; 757 int rc = SQLITE_OK; /* Return code */ 758 TestvfsBuffer *pBuffer; /* Buffer to open connection to */ 759 TestvfsFd *pFd; /* The testvfs file structure */ 760 761 pFd = tvfsGetFd(pFile); 762 p = (Testvfs *)pFd->pVfs->pAppData; 763 assert( pFd->pShmId && pFd->pShm==0 && pFd->pNext==0 ); 764 765 /* Evaluate the Tcl script: 766 ** 767 ** SCRIPT xShmOpen FILENAME 768 */ 769 Tcl_ResetResult(p->interp); 770 if( p->pScript && p->mask&TESTVFS_SHMOPEN_MASK ){ 771 tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0); 772 if( tvfsResultCode(p, &rc) ){ 773 if( rc!=SQLITE_OK ) return rc; 774 } 775 } 776 777 assert( rc==SQLITE_OK ); 778 if( p->mask&TESTVFS_SHMOPEN_MASK && tvfsInjectIoerr(p) ){ 779 return SQLITE_IOERR; 780 } 781 782 /* Search for a TestvfsBuffer. Create a new one if required. */ 783 for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){ 784 if( 0==strcmp(pFd->zFilename, pBuffer->zFile) ) break; 785 } 786 if( !pBuffer ){ 787 int nByte = sizeof(TestvfsBuffer) + (int)strlen(pFd->zFilename) + 1; 788 pBuffer = (TestvfsBuffer *)ckalloc(nByte); 789 memset(pBuffer, 0, nByte); 790 pBuffer->zFile = (char *)&pBuffer[1]; 791 strcpy(pBuffer->zFile, pFd->zFilename); 792 pBuffer->pNext = p->pBuffer; 793 p->pBuffer = pBuffer; 794 } 795 796 /* Connect the TestvfsBuffer to the new TestvfsShm handle and return. */ 797 pFd->pNext = pBuffer->pFile; 798 pBuffer->pFile = pFd; 799 pFd->pShm = pBuffer; 800 return SQLITE_OK; 801 } 802 803 static void tvfsAllocPage(TestvfsBuffer *p, int iPage, int pgsz){ 804 assert( iPage<TESTVFS_MAX_PAGES ); 805 if( p->aPage[iPage]==0 ){ 806 p->aPage[iPage] = (u8 *)ckalloc(pgsz); 807 memset(p->aPage[iPage], 0, pgsz); 808 p->pgsz = pgsz; 809 } 810 } 811 812 static int tvfsShmMap( 813 sqlite3_file *pFile, /* Handle open on database file */ 814 int iPage, /* Page to retrieve */ 815 int pgsz, /* Size of pages */ 816 int isWrite, /* True to extend file if necessary */ 817 void volatile **pp /* OUT: Mapped memory */ 818 ){ 819 int rc = SQLITE_OK; 820 TestvfsFd *pFd = tvfsGetFd(pFile); 821 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); 822 823 if( 0==pFd->pShm ){ 824 rc = tvfsShmOpen(pFile); 825 if( rc!=SQLITE_OK ){ 826 return rc; 827 } 828 } 829 830 if( p->pScript && p->mask&TESTVFS_SHMMAP_MASK ){ 831 Tcl_Obj *pArg = Tcl_NewObj(); 832 Tcl_IncrRefCount(pArg); 833 Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(iPage)); 834 Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(pgsz)); 835 Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(isWrite)); 836 tvfsExecTcl(p, "xShmMap", 837 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, pArg 838 ); 839 tvfsResultCode(p, &rc); 840 Tcl_DecrRefCount(pArg); 841 } 842 if( rc==SQLITE_OK && p->mask&TESTVFS_SHMMAP_MASK && tvfsInjectIoerr(p) ){ 843 rc = SQLITE_IOERR; 844 } 845 846 if( rc==SQLITE_OK && isWrite && !pFd->pShm->aPage[iPage] ){ 847 tvfsAllocPage(pFd->pShm, iPage, pgsz); 848 } 849 *pp = (void volatile *)pFd->pShm->aPage[iPage]; 850 851 return rc; 852 } 853 854 855 static int tvfsShmLock( 856 sqlite3_file *pFile, 857 int ofst, 858 int n, 859 int flags 860 ){ 861 int rc = SQLITE_OK; 862 TestvfsFd *pFd = tvfsGetFd(pFile); 863 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); 864 int nLock; 865 char zLock[80]; 866 867 if( p->pScript && p->mask&TESTVFS_SHMLOCK_MASK ){ 868 sqlite3_snprintf(sizeof(zLock), zLock, "%d %d", ofst, n); 869 nLock = (int)strlen(zLock); 870 if( flags & SQLITE_SHM_LOCK ){ 871 strcpy(&zLock[nLock], " lock"); 872 }else{ 873 strcpy(&zLock[nLock], " unlock"); 874 } 875 nLock += (int)strlen(&zLock[nLock]); 876 if( flags & SQLITE_SHM_SHARED ){ 877 strcpy(&zLock[nLock], " shared"); 878 }else{ 879 strcpy(&zLock[nLock], " exclusive"); 880 } 881 tvfsExecTcl(p, "xShmLock", 882 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 883 Tcl_NewStringObj(zLock, -1) 884 ); 885 tvfsResultCode(p, &rc); 886 } 887 888 if( rc==SQLITE_OK && p->mask&TESTVFS_SHMLOCK_MASK && tvfsInjectIoerr(p) ){ 889 rc = SQLITE_IOERR; 890 } 891 892 if( rc==SQLITE_OK ){ 893 int isLock = (flags & SQLITE_SHM_LOCK); 894 int isExcl = (flags & SQLITE_SHM_EXCLUSIVE); 895 u32 mask = (((1<<n)-1) << ofst); 896 if( isLock ){ 897 TestvfsFd *p2; 898 for(p2=pFd->pShm->pFile; p2; p2=p2->pNext){ 899 if( p2==pFd ) continue; 900 if( (p2->excllock&mask) || (isExcl && p2->sharedlock&mask) ){ 901 rc = SQLITE_BUSY; 902 break; 903 } 904 } 905 if( rc==SQLITE_OK ){ 906 if( isExcl ) pFd->excllock |= mask; 907 if( !isExcl ) pFd->sharedlock |= mask; 908 } 909 }else{ 910 if( isExcl ) pFd->excllock &= (~mask); 911 if( !isExcl ) pFd->sharedlock &= (~mask); 912 } 913 } 914 915 return rc; 916 } 917 918 static void tvfsShmBarrier(sqlite3_file *pFile){ 919 TestvfsFd *pFd = tvfsGetFd(pFile); 920 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); 921 922 if( p->pScript && p->mask&TESTVFS_SHMBARRIER_MASK ){ 923 tvfsExecTcl(p, "xShmBarrier", 924 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0 925 ); 926 } 927 } 928 929 static int tvfsShmUnmap( 930 sqlite3_file *pFile, 931 int deleteFlag 932 ){ 933 int rc = SQLITE_OK; 934 TestvfsFd *pFd = tvfsGetFd(pFile); 935 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); 936 TestvfsBuffer *pBuffer = pFd->pShm; 937 TestvfsFd **ppFd; 938 939 if( !pBuffer ) return SQLITE_OK; 940 assert( pFd->pShmId && pFd->pShm ); 941 942 if( p->pScript && p->mask&TESTVFS_SHMCLOSE_MASK ){ 943 tvfsExecTcl(p, "xShmUnmap", 944 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0 945 ); 946 tvfsResultCode(p, &rc); 947 } 948 949 for(ppFd=&pBuffer->pFile; *ppFd!=pFd; ppFd=&((*ppFd)->pNext)); 950 assert( (*ppFd)==pFd ); 951 *ppFd = pFd->pNext; 952 pFd->pNext = 0; 953 954 if( pBuffer->pFile==0 ){ 955 int i; 956 TestvfsBuffer **pp; 957 for(pp=&p->pBuffer; *pp!=pBuffer; pp=&((*pp)->pNext)); 958 *pp = (*pp)->pNext; 959 for(i=0; pBuffer->aPage[i]; i++){ 960 ckfree((char *)pBuffer->aPage[i]); 961 } 962 ckfree((char *)pBuffer); 963 } 964 pFd->pShm = 0; 965 966 return rc; 967 } 968 969 static int testvfs_obj_cmd( 970 ClientData cd, 971 Tcl_Interp *interp, 972 int objc, 973 Tcl_Obj *CONST objv[] 974 ){ 975 Testvfs *p = (Testvfs *)cd; 976 977 enum DB_enum { 978 CMD_SHM, CMD_DELETE, CMD_FILTER, CMD_IOERR, CMD_SCRIPT, 979 CMD_DEVCHAR, CMD_SECTORSIZE, CMD_FULLERR, CMD_CANTOPENERR 980 }; 981 struct TestvfsSubcmd { 982 char *zName; 983 enum DB_enum eCmd; 984 } aSubcmd[] = { 985 { "shm", CMD_SHM }, 986 { "delete", CMD_DELETE }, 987 { "filter", CMD_FILTER }, 988 { "ioerr", CMD_IOERR }, 989 { "fullerr", CMD_FULLERR }, 990 { "cantopenerr", CMD_CANTOPENERR }, 991 { "script", CMD_SCRIPT }, 992 { "devchar", CMD_DEVCHAR }, 993 { "sectorsize", CMD_SECTORSIZE }, 994 { 0, 0 } 995 }; 996 int i; 997 998 if( objc<2 ){ 999 Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ..."); 1000 return TCL_ERROR; 1001 } 1002 if( Tcl_GetIndexFromObjStruct( 1003 interp, objv[1], aSubcmd, sizeof(aSubcmd[0]), "subcommand", 0, &i) 1004 ){ 1005 return TCL_ERROR; 1006 } 1007 Tcl_ResetResult(interp); 1008 1009 switch( aSubcmd[i].eCmd ){ 1010 case CMD_SHM: { 1011 Tcl_Obj *pObj; 1012 int i, rc; 1013 TestvfsBuffer *pBuffer; 1014 char *zName; 1015 if( objc!=3 && objc!=4 ){ 1016 Tcl_WrongNumArgs(interp, 2, objv, "FILE ?VALUE?"); 1017 return TCL_ERROR; 1018 } 1019 zName = ckalloc(p->pParent->mxPathname); 1020 rc = p->pParent->xFullPathname( 1021 p->pParent, Tcl_GetString(objv[2]), 1022 p->pParent->mxPathname, zName 1023 ); 1024 if( rc!=SQLITE_OK ){ 1025 Tcl_AppendResult(interp, "failed to get full path: ", 1026 Tcl_GetString(objv[2]), 0); 1027 ckfree(zName); 1028 return TCL_ERROR; 1029 } 1030 for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){ 1031 if( 0==strcmp(pBuffer->zFile, zName) ) break; 1032 } 1033 ckfree(zName); 1034 if( !pBuffer ){ 1035 Tcl_AppendResult(interp, "no such file: ", Tcl_GetString(objv[2]), 0); 1036 return TCL_ERROR; 1037 } 1038 if( objc==4 ){ 1039 int n; 1040 u8 *a = Tcl_GetByteArrayFromObj(objv[3], &n); 1041 int pgsz = pBuffer->pgsz; 1042 if( pgsz==0 ) pgsz = 65536; 1043 for(i=0; i*pgsz<n; i++){ 1044 int nByte = pgsz; 1045 tvfsAllocPage(pBuffer, i, pgsz); 1046 if( n-i*pgsz<pgsz ){ 1047 nByte = n; 1048 } 1049 memcpy(pBuffer->aPage[i], &a[i*pgsz], nByte); 1050 } 1051 } 1052 1053 pObj = Tcl_NewObj(); 1054 for(i=0; pBuffer->aPage[i]; i++){ 1055 int pgsz = pBuffer->pgsz; 1056 if( pgsz==0 ) pgsz = 65536; 1057 Tcl_AppendObjToObj(pObj, Tcl_NewByteArrayObj(pBuffer->aPage[i], pgsz)); 1058 } 1059 Tcl_SetObjResult(interp, pObj); 1060 break; 1061 } 1062 1063 case CMD_FILTER: { 1064 static struct VfsMethod { 1065 char *zName; 1066 int mask; 1067 } vfsmethod [] = { 1068 { "xShmOpen", TESTVFS_SHMOPEN_MASK }, 1069 { "xShmLock", TESTVFS_SHMLOCK_MASK }, 1070 { "xShmBarrier", TESTVFS_SHMBARRIER_MASK }, 1071 { "xShmUnmap", TESTVFS_SHMCLOSE_MASK }, 1072 { "xShmMap", TESTVFS_SHMMAP_MASK }, 1073 { "xSync", TESTVFS_SYNC_MASK }, 1074 { "xDelete", TESTVFS_DELETE_MASK }, 1075 { "xWrite", TESTVFS_WRITE_MASK }, 1076 { "xRead", TESTVFS_READ_MASK }, 1077 { "xTruncate", TESTVFS_TRUNCATE_MASK }, 1078 { "xOpen", TESTVFS_OPEN_MASK }, 1079 { "xClose", TESTVFS_CLOSE_MASK }, 1080 { "xAccess", TESTVFS_ACCESS_MASK }, 1081 { "xFullPathname", TESTVFS_FULLPATHNAME_MASK }, 1082 }; 1083 Tcl_Obj **apElem = 0; 1084 int nElem = 0; 1085 int i; 1086 int mask = 0; 1087 if( objc!=3 ){ 1088 Tcl_WrongNumArgs(interp, 2, objv, "LIST"); 1089 return TCL_ERROR; 1090 } 1091 if( Tcl_ListObjGetElements(interp, objv[2], &nElem, &apElem) ){ 1092 return TCL_ERROR; 1093 } 1094 Tcl_ResetResult(interp); 1095 for(i=0; i<nElem; i++){ 1096 int iMethod; 1097 char *zElem = Tcl_GetString(apElem[i]); 1098 for(iMethod=0; iMethod<ArraySize(vfsmethod); iMethod++){ 1099 if( strcmp(zElem, vfsmethod[iMethod].zName)==0 ){ 1100 mask |= vfsmethod[iMethod].mask; 1101 break; 1102 } 1103 } 1104 if( iMethod==ArraySize(vfsmethod) ){ 1105 Tcl_AppendResult(interp, "unknown method: ", zElem, 0); 1106 return TCL_ERROR; 1107 } 1108 } 1109 p->mask = mask; 1110 break; 1111 } 1112 1113 case CMD_SCRIPT: { 1114 if( objc==3 ){ 1115 int nByte; 1116 if( p->pScript ){ 1117 Tcl_DecrRefCount(p->pScript); 1118 p->pScript = 0; 1119 } 1120 Tcl_GetStringFromObj(objv[2], &nByte); 1121 if( nByte>0 ){ 1122 p->pScript = Tcl_DuplicateObj(objv[2]); 1123 Tcl_IncrRefCount(p->pScript); 1124 } 1125 }else if( objc!=2 ){ 1126 Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?"); 1127 return TCL_ERROR; 1128 } 1129 1130 Tcl_ResetResult(interp); 1131 if( p->pScript ) Tcl_SetObjResult(interp, p->pScript); 1132 1133 break; 1134 } 1135 1136 /* 1137 ** TESTVFS ioerr ?IFAIL PERSIST? 1138 ** 1139 ** Where IFAIL is an integer and PERSIST is boolean. 1140 */ 1141 case CMD_CANTOPENERR: 1142 case CMD_IOERR: 1143 case CMD_FULLERR: { 1144 TestFaultInject *pTest; 1145 int iRet; 1146 1147 switch( aSubcmd[i].eCmd ){ 1148 case CMD_IOERR: pTest = &p->ioerr_err; break; 1149 case CMD_FULLERR: pTest = &p->full_err; break; 1150 case CMD_CANTOPENERR: pTest = &p->cantopen_err; break; 1151 default: assert(0); 1152 } 1153 iRet = pTest->nFail; 1154 pTest->nFail = 0; 1155 pTest->eFault = 0; 1156 pTest->iCnt = 0; 1157 1158 if( objc==4 ){ 1159 int iCnt, iPersist; 1160 if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iCnt) 1161 || TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[3], &iPersist) 1162 ){ 1163 return TCL_ERROR; 1164 } 1165 pTest->eFault = iPersist?FAULT_INJECT_PERSISTENT:FAULT_INJECT_TRANSIENT; 1166 pTest->iCnt = iCnt; 1167 }else if( objc!=2 ){ 1168 Tcl_WrongNumArgs(interp, 2, objv, "?CNT PERSIST?"); 1169 return TCL_ERROR; 1170 } 1171 Tcl_SetObjResult(interp, Tcl_NewIntObj(iRet)); 1172 break; 1173 } 1174 1175 case CMD_DELETE: { 1176 Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); 1177 break; 1178 } 1179 1180 case CMD_DEVCHAR: { 1181 struct DeviceFlag { 1182 char *zName; 1183 int iValue; 1184 } aFlag[] = { 1185 { "default", -1 }, 1186 { "atomic", SQLITE_IOCAP_ATOMIC }, 1187 { "atomic512", SQLITE_IOCAP_ATOMIC512 }, 1188 { "atomic1k", SQLITE_IOCAP_ATOMIC1K }, 1189 { "atomic2k", SQLITE_IOCAP_ATOMIC2K }, 1190 { "atomic4k", SQLITE_IOCAP_ATOMIC4K }, 1191 { "atomic8k", SQLITE_IOCAP_ATOMIC8K }, 1192 { "atomic16k", SQLITE_IOCAP_ATOMIC16K }, 1193 { "atomic32k", SQLITE_IOCAP_ATOMIC32K }, 1194 { "atomic64k", SQLITE_IOCAP_ATOMIC64K }, 1195 { "sequential", SQLITE_IOCAP_SEQUENTIAL }, 1196 { "safe_append", SQLITE_IOCAP_SAFE_APPEND }, 1197 { "undeletable_when_open", SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN }, 1198 { "powersafe_overwrite", SQLITE_IOCAP_POWERSAFE_OVERWRITE }, 1199 { 0, 0 } 1200 }; 1201 Tcl_Obj *pRet; 1202 int iFlag; 1203 1204 if( objc>3 ){ 1205 Tcl_WrongNumArgs(interp, 2, objv, "?ATTR-LIST?"); 1206 return TCL_ERROR; 1207 } 1208 if( objc==3 ){ 1209 int j; 1210 int iNew = 0; 1211 Tcl_Obj **flags = 0; 1212 int nFlags = 0; 1213 1214 if( Tcl_ListObjGetElements(interp, objv[2], &nFlags, &flags) ){ 1215 return TCL_ERROR; 1216 } 1217 1218 for(j=0; j<nFlags; j++){ 1219 int idx = 0; 1220 if( Tcl_GetIndexFromObjStruct(interp, flags[j], aFlag, 1221 sizeof(aFlag[0]), "flag", 0, &idx) 1222 ){ 1223 return TCL_ERROR; 1224 } 1225 if( aFlag[idx].iValue<0 && nFlags>1 ){ 1226 Tcl_AppendResult(interp, "bad flags: ", Tcl_GetString(objv[2]), 0); 1227 return TCL_ERROR; 1228 } 1229 iNew |= aFlag[idx].iValue; 1230 } 1231 1232 p->iDevchar = iNew| 0x10000000; 1233 } 1234 1235 pRet = Tcl_NewObj(); 1236 for(iFlag=0; iFlag<sizeof(aFlag)/sizeof(aFlag[0]); iFlag++){ 1237 if( p->iDevchar & aFlag[iFlag].iValue ){ 1238 Tcl_ListObjAppendElement( 1239 interp, pRet, Tcl_NewStringObj(aFlag[iFlag].zName, -1) 1240 ); 1241 } 1242 } 1243 Tcl_SetObjResult(interp, pRet); 1244 1245 break; 1246 } 1247 1248 case CMD_SECTORSIZE: { 1249 if( objc>3 ){ 1250 Tcl_WrongNumArgs(interp, 2, objv, "?VALUE?"); 1251 return TCL_ERROR; 1252 } 1253 if( objc==3 ){ 1254 int iNew = 0; 1255 if( Tcl_GetIntFromObj(interp, objv[2], &iNew) ){ 1256 return TCL_ERROR; 1257 } 1258 p->iSectorsize = iNew; 1259 } 1260 Tcl_SetObjResult(interp, Tcl_NewIntObj(p->iSectorsize)); 1261 break; 1262 } 1263 } 1264 1265 return TCL_OK; 1266 } 1267 1268 static void testvfs_obj_del(ClientData cd){ 1269 Testvfs *p = (Testvfs *)cd; 1270 if( p->pScript ) Tcl_DecrRefCount(p->pScript); 1271 sqlite3_vfs_unregister(p->pVfs); 1272 ckfree((char *)p->pVfs); 1273 ckfree((char *)p); 1274 } 1275 1276 /* 1277 ** Usage: testvfs VFSNAME ?SWITCHES? 1278 ** 1279 ** Switches are: 1280 ** 1281 ** -noshm BOOLEAN (True to omit shm methods. Default false) 1282 ** -default BOOLEAN (True to make the vfs default. Default false) 1283 ** 1284 ** This command creates two things when it is invoked: an SQLite VFS, and 1285 ** a Tcl command. Both are named VFSNAME. The VFS is installed. It is not 1286 ** installed as the default VFS. 1287 ** 1288 ** The VFS passes all file I/O calls through to the underlying VFS. 1289 ** 1290 ** Whenever the xShmMap method of the VFS 1291 ** is invoked, the SCRIPT is executed as follows: 1292 ** 1293 ** SCRIPT xShmMap FILENAME ID 1294 ** 1295 ** The value returned by the invocation of SCRIPT above is interpreted as 1296 ** an SQLite error code and returned to SQLite. Either a symbolic 1297 ** "SQLITE_OK" or numeric "0" value may be returned. 1298 ** 1299 ** The contents of the shared-memory buffer associated with a given file 1300 ** may be read and set using the following command: 1301 ** 1302 ** VFSNAME shm FILENAME ?NEWVALUE? 1303 ** 1304 ** When the xShmLock method is invoked by SQLite, the following script is 1305 ** run: 1306 ** 1307 ** SCRIPT xShmLock FILENAME ID LOCK 1308 ** 1309 ** where LOCK is of the form "OFFSET NBYTE lock/unlock shared/exclusive" 1310 */ 1311 static int testvfs_cmd( 1312 ClientData cd, 1313 Tcl_Interp *interp, 1314 int objc, 1315 Tcl_Obj *CONST objv[] 1316 ){ 1317 static sqlite3_vfs tvfs_vfs = { 1318 2, /* iVersion */ 1319 0, /* szOsFile */ 1320 0, /* mxPathname */ 1321 0, /* pNext */ 1322 0, /* zName */ 1323 0, /* pAppData */ 1324 tvfsOpen, /* xOpen */ 1325 tvfsDelete, /* xDelete */ 1326 tvfsAccess, /* xAccess */ 1327 tvfsFullPathname, /* xFullPathname */ 1328 #ifndef SQLITE_OMIT_LOAD_EXTENSION 1329 tvfsDlOpen, /* xDlOpen */ 1330 tvfsDlError, /* xDlError */ 1331 tvfsDlSym, /* xDlSym */ 1332 tvfsDlClose, /* xDlClose */ 1333 #else 1334 0, /* xDlOpen */ 1335 0, /* xDlError */ 1336 0, /* xDlSym */ 1337 0, /* xDlClose */ 1338 #endif /* SQLITE_OMIT_LOAD_EXTENSION */ 1339 tvfsRandomness, /* xRandomness */ 1340 tvfsSleep, /* xSleep */ 1341 tvfsCurrentTime, /* xCurrentTime */ 1342 0, /* xGetLastError */ 1343 0, /* xCurrentTimeInt64 */ 1344 }; 1345 1346 Testvfs *p; /* New object */ 1347 sqlite3_vfs *pVfs; /* New VFS */ 1348 char *zVfs; 1349 int nByte; /* Bytes of space to allocate at p */ 1350 1351 int i; 1352 int isNoshm = 0; /* True if -noshm is passed */ 1353 int isDefault = 0; /* True if -default is passed */ 1354 int szOsFile = 0; /* Value passed to -szosfile */ 1355 int mxPathname = -1; /* Value passed to -mxpathname */ 1356 int iVersion = 2; /* Value passed to -iversion */ 1357 1358 if( objc<2 || 0!=(objc%2) ) goto bad_args; 1359 for(i=2; i<objc; i += 2){ 1360 int nSwitch; 1361 char *zSwitch; 1362 zSwitch = Tcl_GetStringFromObj(objv[i], &nSwitch); 1363 1364 if( nSwitch>2 && 0==strncmp("-noshm", zSwitch, nSwitch) ){ 1365 if( Tcl_GetBooleanFromObj(interp, objv[i+1], &isNoshm) ){ 1366 return TCL_ERROR; 1367 } 1368 } 1369 else if( nSwitch>2 && 0==strncmp("-default", zSwitch, nSwitch) ){ 1370 if( Tcl_GetBooleanFromObj(interp, objv[i+1], &isDefault) ){ 1371 return TCL_ERROR; 1372 } 1373 } 1374 else if( nSwitch>2 && 0==strncmp("-szosfile", zSwitch, nSwitch) ){ 1375 if( Tcl_GetIntFromObj(interp, objv[i+1], &szOsFile) ){ 1376 return TCL_ERROR; 1377 } 1378 } 1379 else if( nSwitch>2 && 0==strncmp("-mxpathname", zSwitch, nSwitch) ){ 1380 if( Tcl_GetIntFromObj(interp, objv[i+1], &mxPathname) ){ 1381 return TCL_ERROR; 1382 } 1383 } 1384 else if( nSwitch>2 && 0==strncmp("-iversion", zSwitch, nSwitch) ){ 1385 if( Tcl_GetIntFromObj(interp, objv[i+1], &iVersion) ){ 1386 return TCL_ERROR; 1387 } 1388 } 1389 else{ 1390 goto bad_args; 1391 } 1392 } 1393 1394 if( szOsFile<sizeof(TestvfsFile) ){ 1395 szOsFile = sizeof(TestvfsFile); 1396 } 1397 1398 zVfs = Tcl_GetString(objv[1]); 1399 nByte = sizeof(Testvfs) + (int)strlen(zVfs)+1; 1400 p = (Testvfs *)ckalloc(nByte); 1401 memset(p, 0, nByte); 1402 p->iDevchar = -1; 1403 p->iSectorsize = -1; 1404 1405 /* Create the new object command before querying SQLite for a default VFS 1406 ** to use for 'real' IO operations. This is because creating the new VFS 1407 ** may delete an existing [testvfs] VFS of the same name. If such a VFS 1408 ** is currently the default, the new [testvfs] may end up calling the 1409 ** methods of a deleted object. 1410 */ 1411 Tcl_CreateObjCommand(interp, zVfs, testvfs_obj_cmd, p, testvfs_obj_del); 1412 p->pParent = sqlite3_vfs_find(0); 1413 p->interp = interp; 1414 1415 p->zName = (char *)&p[1]; 1416 memcpy(p->zName, zVfs, strlen(zVfs)+1); 1417 1418 pVfs = (sqlite3_vfs *)ckalloc(sizeof(sqlite3_vfs)); 1419 memcpy(pVfs, &tvfs_vfs, sizeof(sqlite3_vfs)); 1420 pVfs->pAppData = (void *)p; 1421 pVfs->iVersion = iVersion; 1422 pVfs->zName = p->zName; 1423 pVfs->mxPathname = p->pParent->mxPathname; 1424 if( mxPathname>=0 && mxPathname<pVfs->mxPathname ){ 1425 pVfs->mxPathname = mxPathname; 1426 } 1427 pVfs->szOsFile = szOsFile; 1428 p->pVfs = pVfs; 1429 p->isNoshm = isNoshm; 1430 p->mask = TESTVFS_ALL_MASK; 1431 1432 sqlite3_vfs_register(pVfs, isDefault); 1433 1434 return TCL_OK; 1435 1436 bad_args: 1437 Tcl_WrongNumArgs(interp, 1, objv, "VFSNAME ?-noshm BOOL? ?-default BOOL? ?-mxpathname INT? ?-szosfile INT? ?-iversion INT?"); 1438 return TCL_ERROR; 1439 } 1440 1441 int Sqlitetestvfs_Init(Tcl_Interp *interp){ 1442 Tcl_CreateObjCommand(interp, "testvfs", testvfs_cmd, 0, 0); 1443 return TCL_OK; 1444 } 1445 1446 #endif 1447