Annotation of embedaddon/sqlite3/src/test6.c, revision 1.1
1.1 ! misho 1: /*
! 2: ** 2004 May 22
! 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 code that modified the OS layer in order to simulate
! 14: ** the effect on the database file of an OS crash or power failure. This
! 15: ** is used to test the ability of SQLite to recover from those situations.
! 16: */
! 17: #if SQLITE_TEST /* This file is used for testing only */
! 18: #include "sqliteInt.h"
! 19: #include "tcl.h"
! 20:
! 21: #ifndef SQLITE_OMIT_DISKIO /* This file is a no-op if disk I/O is disabled */
! 22:
! 23: /* #define TRACE_CRASHTEST */
! 24:
! 25: typedef struct CrashFile CrashFile;
! 26: typedef struct CrashGlobal CrashGlobal;
! 27: typedef struct WriteBuffer WriteBuffer;
! 28:
! 29: /*
! 30: ** Method:
! 31: **
! 32: ** This layer is implemented as a wrapper around the "real"
! 33: ** sqlite3_file object for the host system. Each time data is
! 34: ** written to the file object, instead of being written to the
! 35: ** underlying file, the write operation is stored in an in-memory
! 36: ** structure (type WriteBuffer). This structure is placed at the
! 37: ** end of a global ordered list (the write-list).
! 38: **
! 39: ** When data is read from a file object, the requested region is
! 40: ** first retrieved from the real file. The write-list is then
! 41: ** traversed and data copied from any overlapping WriteBuffer
! 42: ** structures to the output buffer. i.e. a read() operation following
! 43: ** one or more write() operations works as expected, even if no
! 44: ** data has actually been written out to the real file.
! 45: **
! 46: ** When a fsync() operation is performed, an operating system crash
! 47: ** may be simulated, in which case exit(-1) is called (the call to
! 48: ** xSync() never returns). Whether or not a crash is simulated,
! 49: ** the data associated with a subset of the WriteBuffer structures
! 50: ** stored in the write-list is written to the real underlying files
! 51: ** and the entries removed from the write-list. If a crash is simulated,
! 52: ** a subset of the buffers may be corrupted before the data is written.
! 53: **
! 54: ** The exact subset of the write-list written and/or corrupted is
! 55: ** determined by the simulated device characteristics and sector-size.
! 56: **
! 57: ** "Normal" mode:
! 58: **
! 59: ** Normal mode is used when the simulated device has none of the
! 60: ** SQLITE_IOCAP_XXX flags set.
! 61: **
! 62: ** In normal mode, if the fsync() is not a simulated crash, the
! 63: ** write-list is traversed from beginning to end. Each WriteBuffer
! 64: ** structure associated with the file handle used to call xSync()
! 65: ** is written to the real file and removed from the write-list.
! 66: **
! 67: ** If a crash is simulated, one of the following takes place for
! 68: ** each WriteBuffer in the write-list, regardless of which
! 69: ** file-handle it is associated with:
! 70: **
! 71: ** 1. The buffer is correctly written to the file, just as if
! 72: ** a crash were not being simulated.
! 73: **
! 74: ** 2. Nothing is done.
! 75: **
! 76: ** 3. Garbage data is written to all sectors of the file that
! 77: ** overlap the region specified by the WriteBuffer. Or garbage
! 78: ** data is written to some contiguous section within the
! 79: ** overlapped sectors.
! 80: **
! 81: ** Device Characteristic flag handling:
! 82: **
! 83: ** If the IOCAP_ATOMIC flag is set, then option (3) above is
! 84: ** never selected.
! 85: **
! 86: ** If the IOCAP_ATOMIC512 flag is set, and the WriteBuffer represents
! 87: ** an aligned write() of an integer number of 512 byte regions, then
! 88: ** option (3) above is never selected. Instead, each 512 byte region
! 89: ** is either correctly written or left completely untouched. Similar
! 90: ** logic governs the behaviour if any of the other ATOMICXXX flags
! 91: ** is set.
! 92: **
! 93: ** If either the IOCAP_SAFEAPPEND or IOCAP_SEQUENTIAL flags are set
! 94: ** and a crash is being simulated, then an entry of the write-list is
! 95: ** selected at random. Everything in the list after the selected entry
! 96: ** is discarded before processing begins.
! 97: **
! 98: ** If IOCAP_SEQUENTIAL is set and a crash is being simulated, option
! 99: ** (1) is selected for all write-list entries except the last. If a
! 100: ** crash is not being simulated, then all entries in the write-list
! 101: ** that occur before at least one write() on the file-handle specified
! 102: ** as part of the xSync() are written to their associated real files.
! 103: **
! 104: ** If IOCAP_SAFEAPPEND is set and the first byte written by the write()
! 105: ** operation is one byte past the current end of the file, then option
! 106: ** (1) is always selected.
! 107: */
! 108:
! 109: /*
! 110: ** Each write operation in the write-list is represented by an instance
! 111: ** of the following structure.
! 112: **
! 113: ** If zBuf is 0, then this structure represents a call to xTruncate(),
! 114: ** not xWrite(). In that case, iOffset is the size that the file is
! 115: ** truncated to.
! 116: */
! 117: struct WriteBuffer {
! 118: i64 iOffset; /* Byte offset of the start of this write() */
! 119: int nBuf; /* Number of bytes written */
! 120: u8 *zBuf; /* Pointer to copy of written data */
! 121: CrashFile *pFile; /* File this write() applies to */
! 122:
! 123: WriteBuffer *pNext; /* Next in CrashGlobal.pWriteList */
! 124: };
! 125:
! 126: struct CrashFile {
! 127: const sqlite3_io_methods *pMethod; /* Must be first */
! 128: sqlite3_file *pRealFile; /* Underlying "real" file handle */
! 129: char *zName;
! 130: int flags; /* Flags the file was opened with */
! 131:
! 132: /* Cache of the entire file. This is used to speed up OsRead() and
! 133: ** OsFileSize() calls. Although both could be done by traversing the
! 134: ** write-list, in practice this is impractically slow.
! 135: */
! 136: int iSize; /* Size of file in bytes */
! 137: int nData; /* Size of buffer allocated at zData */
! 138: u8 *zData; /* Buffer containing file contents */
! 139: };
! 140:
! 141: struct CrashGlobal {
! 142: WriteBuffer *pWriteList; /* Head of write-list */
! 143: WriteBuffer *pWriteListEnd; /* End of write-list */
! 144:
! 145: int iSectorSize; /* Value of simulated sector size */
! 146: int iDeviceCharacteristics; /* Value of simulated device characteristics */
! 147:
! 148: int iCrash; /* Crash on the iCrash'th call to xSync() */
! 149: char zCrashFile[500]; /* Crash during an xSync() on this file */
! 150: };
! 151:
! 152: static CrashGlobal g = {0, 0, SQLITE_DEFAULT_SECTOR_SIZE, 0, 0};
! 153:
! 154: /*
! 155: ** Set this global variable to 1 to enable crash testing.
! 156: */
! 157: static int sqlite3CrashTestEnable = 0;
! 158:
! 159: static void *crash_malloc(int nByte){
! 160: return (void *)Tcl_Alloc((size_t)nByte);
! 161: }
! 162: static void crash_free(void *p){
! 163: Tcl_Free(p);
! 164: }
! 165: static void *crash_realloc(void *p, int n){
! 166: return (void *)Tcl_Realloc(p, (size_t)n);
! 167: }
! 168:
! 169: /*
! 170: ** Wrapper around the sqlite3OsWrite() function that avoids writing to the
! 171: ** 512 byte block begining at offset PENDING_BYTE.
! 172: */
! 173: static int writeDbFile(CrashFile *p, u8 *z, i64 iAmt, i64 iOff){
! 174: int rc = SQLITE_OK;
! 175: int iSkip = 0;
! 176: if( iOff==PENDING_BYTE && (p->flags&SQLITE_OPEN_MAIN_DB) ){
! 177: iSkip = 512;
! 178: }
! 179: if( (iAmt-iSkip)>0 ){
! 180: rc = sqlite3OsWrite(p->pRealFile, &z[iSkip], iAmt-iSkip, iOff+iSkip);
! 181: }
! 182: return rc;
! 183: }
! 184:
! 185: /*
! 186: ** Flush the write-list as if xSync() had been called on file handle
! 187: ** pFile. If isCrash is true, simulate a crash.
! 188: */
! 189: static int writeListSync(CrashFile *pFile, int isCrash){
! 190: int rc = SQLITE_OK;
! 191: int iDc = g.iDeviceCharacteristics;
! 192:
! 193: WriteBuffer *pWrite;
! 194: WriteBuffer **ppPtr;
! 195:
! 196: /* If this is not a crash simulation, set pFinal to point to the
! 197: ** last element of the write-list that is associated with file handle
! 198: ** pFile.
! 199: **
! 200: ** If this is a crash simulation, set pFinal to an arbitrarily selected
! 201: ** element of the write-list.
! 202: */
! 203: WriteBuffer *pFinal = 0;
! 204: if( !isCrash ){
! 205: for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext){
! 206: if( pWrite->pFile==pFile ){
! 207: pFinal = pWrite;
! 208: }
! 209: }
! 210: }else if( iDc&(SQLITE_IOCAP_SEQUENTIAL|SQLITE_IOCAP_SAFE_APPEND) ){
! 211: int nWrite = 0;
! 212: int iFinal;
! 213: for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext) nWrite++;
! 214: sqlite3_randomness(sizeof(int), &iFinal);
! 215: iFinal = ((iFinal<0)?-1*iFinal:iFinal)%nWrite;
! 216: for(pWrite=g.pWriteList; iFinal>0; pWrite=pWrite->pNext) iFinal--;
! 217: pFinal = pWrite;
! 218: }
! 219:
! 220: #ifdef TRACE_CRASHTEST
! 221: printf("Sync %s (is %s crash)\n", pFile->zName, (isCrash?"a":"not a"));
! 222: #endif
! 223:
! 224: ppPtr = &g.pWriteList;
! 225: for(pWrite=*ppPtr; rc==SQLITE_OK && pWrite; pWrite=*ppPtr){
! 226: sqlite3_file *pRealFile = pWrite->pFile->pRealFile;
! 227:
! 228: /* (eAction==1) -> write block out normally,
! 229: ** (eAction==2) -> do nothing,
! 230: ** (eAction==3) -> trash sectors.
! 231: */
! 232: int eAction = 0;
! 233: if( !isCrash ){
! 234: eAction = 2;
! 235: if( (pWrite->pFile==pFile || iDc&SQLITE_IOCAP_SEQUENTIAL) ){
! 236: eAction = 1;
! 237: }
! 238: }else{
! 239: char random;
! 240: sqlite3_randomness(1, &random);
! 241:
! 242: /* Do not select option 3 (sector trashing) if the IOCAP_ATOMIC flag
! 243: ** is set or this is an OsTruncate(), not an Oswrite().
! 244: */
! 245: if( (iDc&SQLITE_IOCAP_ATOMIC) || (pWrite->zBuf==0) ){
! 246: random &= 0x01;
! 247: }
! 248:
! 249: /* If IOCAP_SEQUENTIAL is set and this is not the final entry
! 250: ** in the truncated write-list, always select option 1 (write
! 251: ** out correctly).
! 252: */
! 253: if( (iDc&SQLITE_IOCAP_SEQUENTIAL && pWrite!=pFinal) ){
! 254: random = 0;
! 255: }
! 256:
! 257: /* If IOCAP_SAFE_APPEND is set and this OsWrite() operation is
! 258: ** an append (first byte of the written region is 1 byte past the
! 259: ** current EOF), always select option 1 (write out correctly).
! 260: */
! 261: if( iDc&SQLITE_IOCAP_SAFE_APPEND && pWrite->zBuf ){
! 262: i64 iSize;
! 263: sqlite3OsFileSize(pRealFile, &iSize);
! 264: if( iSize==pWrite->iOffset ){
! 265: random = 0;
! 266: }
! 267: }
! 268:
! 269: if( (random&0x06)==0x06 ){
! 270: eAction = 3;
! 271: }else{
! 272: eAction = ((random&0x01)?2:1);
! 273: }
! 274: }
! 275:
! 276: switch( eAction ){
! 277: case 1: { /* Write out correctly */
! 278: if( pWrite->zBuf ){
! 279: rc = writeDbFile(
! 280: pWrite->pFile, pWrite->zBuf, pWrite->nBuf, pWrite->iOffset
! 281: );
! 282: }else{
! 283: rc = sqlite3OsTruncate(pRealFile, pWrite->iOffset);
! 284: }
! 285: *ppPtr = pWrite->pNext;
! 286: #ifdef TRACE_CRASHTEST
! 287: if( isCrash ){
! 288: printf("Writing %d bytes @ %d (%s)\n",
! 289: pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName
! 290: );
! 291: }
! 292: #endif
! 293: crash_free(pWrite);
! 294: break;
! 295: }
! 296: case 2: { /* Do nothing */
! 297: ppPtr = &pWrite->pNext;
! 298: #ifdef TRACE_CRASHTEST
! 299: if( isCrash ){
! 300: printf("Omiting %d bytes @ %d (%s)\n",
! 301: pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName
! 302: );
! 303: }
! 304: #endif
! 305: break;
! 306: }
! 307: case 3: { /* Trash sectors */
! 308: u8 *zGarbage;
! 309: int iFirst = (pWrite->iOffset/g.iSectorSize);
! 310: int iLast = (pWrite->iOffset+pWrite->nBuf-1)/g.iSectorSize;
! 311:
! 312: assert(pWrite->zBuf);
! 313:
! 314: #ifdef TRACE_CRASHTEST
! 315: printf("Trashing %d sectors @ sector %d (%s)\n",
! 316: 1+iLast-iFirst, iFirst, pWrite->pFile->zName
! 317: );
! 318: #endif
! 319:
! 320: zGarbage = crash_malloc(g.iSectorSize);
! 321: if( zGarbage ){
! 322: sqlite3_int64 i;
! 323: for(i=iFirst; rc==SQLITE_OK && i<=iLast; i++){
! 324: sqlite3_randomness(g.iSectorSize, zGarbage);
! 325: rc = writeDbFile(
! 326: pWrite->pFile, zGarbage, g.iSectorSize, i*g.iSectorSize
! 327: );
! 328: }
! 329: crash_free(zGarbage);
! 330: }else{
! 331: rc = SQLITE_NOMEM;
! 332: }
! 333:
! 334: ppPtr = &pWrite->pNext;
! 335: break;
! 336: }
! 337:
! 338: default:
! 339: assert(!"Cannot happen");
! 340: }
! 341:
! 342: if( pWrite==pFinal ) break;
! 343: }
! 344:
! 345: if( rc==SQLITE_OK && isCrash ){
! 346: exit(-1);
! 347: }
! 348:
! 349: for(pWrite=g.pWriteList; pWrite && pWrite->pNext; pWrite=pWrite->pNext);
! 350: g.pWriteListEnd = pWrite;
! 351:
! 352: return rc;
! 353: }
! 354:
! 355: /*
! 356: ** Add an entry to the end of the write-list.
! 357: */
! 358: static int writeListAppend(
! 359: sqlite3_file *pFile,
! 360: sqlite3_int64 iOffset,
! 361: const u8 *zBuf,
! 362: int nBuf
! 363: ){
! 364: WriteBuffer *pNew;
! 365:
! 366: assert((zBuf && nBuf) || (!nBuf && !zBuf));
! 367:
! 368: pNew = (WriteBuffer *)crash_malloc(sizeof(WriteBuffer) + nBuf);
! 369: if( pNew==0 ){
! 370: fprintf(stderr, "out of memory in the crash simulator\n");
! 371: }
! 372: memset(pNew, 0, sizeof(WriteBuffer)+nBuf);
! 373: pNew->iOffset = iOffset;
! 374: pNew->nBuf = nBuf;
! 375: pNew->pFile = (CrashFile *)pFile;
! 376: if( zBuf ){
! 377: pNew->zBuf = (u8 *)&pNew[1];
! 378: memcpy(pNew->zBuf, zBuf, nBuf);
! 379: }
! 380:
! 381: if( g.pWriteList ){
! 382: assert(g.pWriteListEnd);
! 383: g.pWriteListEnd->pNext = pNew;
! 384: }else{
! 385: g.pWriteList = pNew;
! 386: }
! 387: g.pWriteListEnd = pNew;
! 388:
! 389: return SQLITE_OK;
! 390: }
! 391:
! 392: /*
! 393: ** Close a crash-file.
! 394: */
! 395: static int cfClose(sqlite3_file *pFile){
! 396: CrashFile *pCrash = (CrashFile *)pFile;
! 397: writeListSync(pCrash, 0);
! 398: sqlite3OsClose(pCrash->pRealFile);
! 399: return SQLITE_OK;
! 400: }
! 401:
! 402: /*
! 403: ** Read data from a crash-file.
! 404: */
! 405: static int cfRead(
! 406: sqlite3_file *pFile,
! 407: void *zBuf,
! 408: int iAmt,
! 409: sqlite_int64 iOfst
! 410: ){
! 411: CrashFile *pCrash = (CrashFile *)pFile;
! 412:
! 413: /* Check the file-size to see if this is a short-read */
! 414: if( pCrash->iSize<(iOfst+iAmt) ){
! 415: return SQLITE_IOERR_SHORT_READ;
! 416: }
! 417:
! 418: memcpy(zBuf, &pCrash->zData[iOfst], iAmt);
! 419: return SQLITE_OK;
! 420: }
! 421:
! 422: /*
! 423: ** Write data to a crash-file.
! 424: */
! 425: static int cfWrite(
! 426: sqlite3_file *pFile,
! 427: const void *zBuf,
! 428: int iAmt,
! 429: sqlite_int64 iOfst
! 430: ){
! 431: CrashFile *pCrash = (CrashFile *)pFile;
! 432: if( iAmt+iOfst>pCrash->iSize ){
! 433: pCrash->iSize = iAmt+iOfst;
! 434: }
! 435: while( pCrash->iSize>pCrash->nData ){
! 436: u8 *zNew;
! 437: int nNew = (pCrash->nData*2) + 4096;
! 438: zNew = crash_realloc(pCrash->zData, nNew);
! 439: if( !zNew ){
! 440: return SQLITE_NOMEM;
! 441: }
! 442: memset(&zNew[pCrash->nData], 0, nNew-pCrash->nData);
! 443: pCrash->nData = nNew;
! 444: pCrash->zData = zNew;
! 445: }
! 446: memcpy(&pCrash->zData[iOfst], zBuf, iAmt);
! 447: return writeListAppend(pFile, iOfst, zBuf, iAmt);
! 448: }
! 449:
! 450: /*
! 451: ** Truncate a crash-file.
! 452: */
! 453: static int cfTruncate(sqlite3_file *pFile, sqlite_int64 size){
! 454: CrashFile *pCrash = (CrashFile *)pFile;
! 455: assert(size>=0);
! 456: if( pCrash->iSize>size ){
! 457: pCrash->iSize = size;
! 458: }
! 459: return writeListAppend(pFile, size, 0, 0);
! 460: }
! 461:
! 462: /*
! 463: ** Sync a crash-file.
! 464: */
! 465: static int cfSync(sqlite3_file *pFile, int flags){
! 466: CrashFile *pCrash = (CrashFile *)pFile;
! 467: int isCrash = 0;
! 468:
! 469: const char *zName = pCrash->zName;
! 470: const char *zCrashFile = g.zCrashFile;
! 471: int nName = strlen(zName);
! 472: int nCrashFile = strlen(zCrashFile);
! 473:
! 474: if( nCrashFile>0 && zCrashFile[nCrashFile-1]=='*' ){
! 475: nCrashFile--;
! 476: if( nName>nCrashFile ) nName = nCrashFile;
! 477: }
! 478:
! 479: if( nName==nCrashFile && 0==memcmp(zName, zCrashFile, nName) ){
! 480: if( (--g.iCrash)==0 ) isCrash = 1;
! 481: }
! 482:
! 483: return writeListSync(pCrash, isCrash);
! 484: }
! 485:
! 486: /*
! 487: ** Return the current file-size of the crash-file.
! 488: */
! 489: static int cfFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
! 490: CrashFile *pCrash = (CrashFile *)pFile;
! 491: *pSize = (i64)pCrash->iSize;
! 492: return SQLITE_OK;
! 493: }
! 494:
! 495: /*
! 496: ** Calls related to file-locks are passed on to the real file handle.
! 497: */
! 498: static int cfLock(sqlite3_file *pFile, int eLock){
! 499: return sqlite3OsLock(((CrashFile *)pFile)->pRealFile, eLock);
! 500: }
! 501: static int cfUnlock(sqlite3_file *pFile, int eLock){
! 502: return sqlite3OsUnlock(((CrashFile *)pFile)->pRealFile, eLock);
! 503: }
! 504: static int cfCheckReservedLock(sqlite3_file *pFile, int *pResOut){
! 505: return sqlite3OsCheckReservedLock(((CrashFile *)pFile)->pRealFile, pResOut);
! 506: }
! 507: static int cfFileControl(sqlite3_file *pFile, int op, void *pArg){
! 508: if( op==SQLITE_FCNTL_SIZE_HINT ){
! 509: CrashFile *pCrash = (CrashFile *)pFile;
! 510: i64 nByte = *(i64 *)pArg;
! 511: if( nByte>pCrash->iSize ){
! 512: if( SQLITE_OK==writeListAppend(pFile, nByte, 0, 0) ){
! 513: pCrash->iSize = nByte;
! 514: }
! 515: }
! 516: return SQLITE_OK;
! 517: }
! 518: return sqlite3OsFileControl(((CrashFile *)pFile)->pRealFile, op, pArg);
! 519: }
! 520:
! 521: /*
! 522: ** The xSectorSize() and xDeviceCharacteristics() functions return
! 523: ** the global values configured by the [sqlite_crashparams] tcl
! 524: * interface.
! 525: */
! 526: static int cfSectorSize(sqlite3_file *pFile){
! 527: return g.iSectorSize;
! 528: }
! 529: static int cfDeviceCharacteristics(sqlite3_file *pFile){
! 530: return g.iDeviceCharacteristics;
! 531: }
! 532:
! 533: /*
! 534: ** Pass-throughs for WAL support.
! 535: */
! 536: static int cfShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
! 537: return sqlite3OsShmLock(((CrashFile*)pFile)->pRealFile, ofst, n, flags);
! 538: }
! 539: static void cfShmBarrier(sqlite3_file *pFile){
! 540: sqlite3OsShmBarrier(((CrashFile*)pFile)->pRealFile);
! 541: }
! 542: static int cfShmUnmap(sqlite3_file *pFile, int delFlag){
! 543: return sqlite3OsShmUnmap(((CrashFile*)pFile)->pRealFile, delFlag);
! 544: }
! 545: static int cfShmMap(
! 546: sqlite3_file *pFile, /* Handle open on database file */
! 547: int iRegion, /* Region to retrieve */
! 548: int sz, /* Size of regions */
! 549: int w, /* True to extend file if necessary */
! 550: void volatile **pp /* OUT: Mapped memory */
! 551: ){
! 552: return sqlite3OsShmMap(((CrashFile*)pFile)->pRealFile, iRegion, sz, w, pp);
! 553: }
! 554:
! 555: static const sqlite3_io_methods CrashFileVtab = {
! 556: 2, /* iVersion */
! 557: cfClose, /* xClose */
! 558: cfRead, /* xRead */
! 559: cfWrite, /* xWrite */
! 560: cfTruncate, /* xTruncate */
! 561: cfSync, /* xSync */
! 562: cfFileSize, /* xFileSize */
! 563: cfLock, /* xLock */
! 564: cfUnlock, /* xUnlock */
! 565: cfCheckReservedLock, /* xCheckReservedLock */
! 566: cfFileControl, /* xFileControl */
! 567: cfSectorSize, /* xSectorSize */
! 568: cfDeviceCharacteristics, /* xDeviceCharacteristics */
! 569: cfShmMap, /* xShmMap */
! 570: cfShmLock, /* xShmLock */
! 571: cfShmBarrier, /* xShmBarrier */
! 572: cfShmUnmap /* xShmUnmap */
! 573: };
! 574:
! 575: /*
! 576: ** Application data for the crash VFS
! 577: */
! 578: struct crashAppData {
! 579: sqlite3_vfs *pOrig; /* Wrapped vfs structure */
! 580: };
! 581:
! 582: /*
! 583: ** Open a crash-file file handle.
! 584: **
! 585: ** The caller will have allocated pVfs->szOsFile bytes of space
! 586: ** at pFile. This file uses this space for the CrashFile structure
! 587: ** and allocates space for the "real" file structure using
! 588: ** sqlite3_malloc(). The assumption here is (pVfs->szOsFile) is
! 589: ** equal or greater than sizeof(CrashFile).
! 590: */
! 591: static int cfOpen(
! 592: sqlite3_vfs *pCfVfs,
! 593: const char *zName,
! 594: sqlite3_file *pFile,
! 595: int flags,
! 596: int *pOutFlags
! 597: ){
! 598: sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
! 599: int rc;
! 600: CrashFile *pWrapper = (CrashFile *)pFile;
! 601: sqlite3_file *pReal = (sqlite3_file*)&pWrapper[1];
! 602:
! 603: memset(pWrapper, 0, sizeof(CrashFile));
! 604: rc = sqlite3OsOpen(pVfs, zName, pReal, flags, pOutFlags);
! 605:
! 606: if( rc==SQLITE_OK ){
! 607: i64 iSize;
! 608: pWrapper->pMethod = &CrashFileVtab;
! 609: pWrapper->zName = (char *)zName;
! 610: pWrapper->pRealFile = pReal;
! 611: rc = sqlite3OsFileSize(pReal, &iSize);
! 612: pWrapper->iSize = (int)iSize;
! 613: pWrapper->flags = flags;
! 614: }
! 615: if( rc==SQLITE_OK ){
! 616: pWrapper->nData = (4096 + pWrapper->iSize);
! 617: pWrapper->zData = crash_malloc(pWrapper->nData);
! 618: if( pWrapper->zData ){
! 619: /* os_unix.c contains an assert() that fails if the caller attempts
! 620: ** to read data from the 512-byte locking region of a file opened
! 621: ** with the SQLITE_OPEN_MAIN_DB flag. This region of a database file
! 622: ** never contains valid data anyhow. So avoid doing such a read here.
! 623: */
! 624: const int isDb = (flags&SQLITE_OPEN_MAIN_DB);
! 625: i64 iChunk = pWrapper->iSize;
! 626: if( iChunk>PENDING_BYTE && isDb ){
! 627: iChunk = PENDING_BYTE;
! 628: }
! 629: memset(pWrapper->zData, 0, pWrapper->nData);
! 630: rc = sqlite3OsRead(pReal, pWrapper->zData, iChunk, 0);
! 631: if( SQLITE_OK==rc && pWrapper->iSize>(PENDING_BYTE+512) && isDb ){
! 632: i64 iOff = PENDING_BYTE+512;
! 633: iChunk = pWrapper->iSize - iOff;
! 634: rc = sqlite3OsRead(pReal, &pWrapper->zData[iOff], iChunk, iOff);
! 635: }
! 636: }else{
! 637: rc = SQLITE_NOMEM;
! 638: }
! 639: }
! 640: if( rc!=SQLITE_OK && pWrapper->pMethod ){
! 641: sqlite3OsClose(pFile);
! 642: }
! 643: return rc;
! 644: }
! 645:
! 646: static int cfDelete(sqlite3_vfs *pCfVfs, const char *zPath, int dirSync){
! 647: sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
! 648: return pVfs->xDelete(pVfs, zPath, dirSync);
! 649: }
! 650: static int cfAccess(
! 651: sqlite3_vfs *pCfVfs,
! 652: const char *zPath,
! 653: int flags,
! 654: int *pResOut
! 655: ){
! 656: sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
! 657: return pVfs->xAccess(pVfs, zPath, flags, pResOut);
! 658: }
! 659: static int cfFullPathname(
! 660: sqlite3_vfs *pCfVfs,
! 661: const char *zPath,
! 662: int nPathOut,
! 663: char *zPathOut
! 664: ){
! 665: sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
! 666: return pVfs->xFullPathname(pVfs, zPath, nPathOut, zPathOut);
! 667: }
! 668: static void *cfDlOpen(sqlite3_vfs *pCfVfs, const char *zPath){
! 669: sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
! 670: return pVfs->xDlOpen(pVfs, zPath);
! 671: }
! 672: static void cfDlError(sqlite3_vfs *pCfVfs, int nByte, char *zErrMsg){
! 673: sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
! 674: pVfs->xDlError(pVfs, nByte, zErrMsg);
! 675: }
! 676: static void (*cfDlSym(sqlite3_vfs *pCfVfs, void *pH, const char *zSym))(void){
! 677: sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
! 678: return pVfs->xDlSym(pVfs, pH, zSym);
! 679: }
! 680: static void cfDlClose(sqlite3_vfs *pCfVfs, void *pHandle){
! 681: sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
! 682: pVfs->xDlClose(pVfs, pHandle);
! 683: }
! 684: static int cfRandomness(sqlite3_vfs *pCfVfs, int nByte, char *zBufOut){
! 685: sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
! 686: return pVfs->xRandomness(pVfs, nByte, zBufOut);
! 687: }
! 688: static int cfSleep(sqlite3_vfs *pCfVfs, int nMicro){
! 689: sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
! 690: return pVfs->xSleep(pVfs, nMicro);
! 691: }
! 692: static int cfCurrentTime(sqlite3_vfs *pCfVfs, double *pTimeOut){
! 693: sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
! 694: return pVfs->xCurrentTime(pVfs, pTimeOut);
! 695: }
! 696:
! 697: static int processDevSymArgs(
! 698: Tcl_Interp *interp,
! 699: int objc,
! 700: Tcl_Obj *CONST objv[],
! 701: int *piDeviceChar,
! 702: int *piSectorSize
! 703: ){
! 704: struct DeviceFlag {
! 705: char *zName;
! 706: int iValue;
! 707: } aFlag[] = {
! 708: { "atomic", SQLITE_IOCAP_ATOMIC },
! 709: { "atomic512", SQLITE_IOCAP_ATOMIC512 },
! 710: { "atomic1k", SQLITE_IOCAP_ATOMIC1K },
! 711: { "atomic2k", SQLITE_IOCAP_ATOMIC2K },
! 712: { "atomic4k", SQLITE_IOCAP_ATOMIC4K },
! 713: { "atomic8k", SQLITE_IOCAP_ATOMIC8K },
! 714: { "atomic16k", SQLITE_IOCAP_ATOMIC16K },
! 715: { "atomic32k", SQLITE_IOCAP_ATOMIC32K },
! 716: { "atomic64k", SQLITE_IOCAP_ATOMIC64K },
! 717: { "sequential", SQLITE_IOCAP_SEQUENTIAL },
! 718: { "safe_append", SQLITE_IOCAP_SAFE_APPEND },
! 719: { "powersafe_overwrite", SQLITE_IOCAP_POWERSAFE_OVERWRITE },
! 720: { 0, 0 }
! 721: };
! 722:
! 723: int i;
! 724: int iDc = 0;
! 725: int iSectorSize = 0;
! 726: int setSectorsize = 0;
! 727: int setDeviceChar = 0;
! 728:
! 729: for(i=0; i<objc; i+=2){
! 730: int nOpt;
! 731: char *zOpt = Tcl_GetStringFromObj(objv[i], &nOpt);
! 732:
! 733: if( (nOpt>11 || nOpt<2 || strncmp("-sectorsize", zOpt, nOpt))
! 734: && (nOpt>16 || nOpt<2 || strncmp("-characteristics", zOpt, nOpt))
! 735: ){
! 736: Tcl_AppendResult(interp,
! 737: "Bad option: \"", zOpt,
! 738: "\" - must be \"-characteristics\" or \"-sectorsize\"", 0
! 739: );
! 740: return TCL_ERROR;
! 741: }
! 742: if( i==objc-1 ){
! 743: Tcl_AppendResult(interp, "Option requires an argument: \"", zOpt, "\"",0);
! 744: return TCL_ERROR;
! 745: }
! 746:
! 747: if( zOpt[1]=='s' ){
! 748: if( Tcl_GetIntFromObj(interp, objv[i+1], &iSectorSize) ){
! 749: return TCL_ERROR;
! 750: }
! 751: setSectorsize = 1;
! 752: }else{
! 753: int j;
! 754: Tcl_Obj **apObj;
! 755: int nObj;
! 756: if( Tcl_ListObjGetElements(interp, objv[i+1], &nObj, &apObj) ){
! 757: return TCL_ERROR;
! 758: }
! 759: for(j=0; j<nObj; j++){
! 760: int rc;
! 761: int iChoice;
! 762: Tcl_Obj *pFlag = Tcl_DuplicateObj(apObj[j]);
! 763: Tcl_IncrRefCount(pFlag);
! 764: Tcl_UtfToLower(Tcl_GetString(pFlag));
! 765:
! 766: rc = Tcl_GetIndexFromObjStruct(
! 767: interp, pFlag, aFlag, sizeof(aFlag[0]), "no such flag", 0, &iChoice
! 768: );
! 769: Tcl_DecrRefCount(pFlag);
! 770: if( rc ){
! 771: return TCL_ERROR;
! 772: }
! 773:
! 774: iDc |= aFlag[iChoice].iValue;
! 775: }
! 776: setDeviceChar = 1;
! 777: }
! 778: }
! 779:
! 780: if( setDeviceChar ){
! 781: *piDeviceChar = iDc;
! 782: }
! 783: if( setSectorsize ){
! 784: *piSectorSize = iSectorSize;
! 785: }
! 786:
! 787: return TCL_OK;
! 788: }
! 789:
! 790: /*
! 791: ** tclcmd: sqlite_crash_enable ENABLE
! 792: **
! 793: ** Parameter ENABLE must be a boolean value. If true, then the "crash"
! 794: ** vfs is added to the system. If false, it is removed.
! 795: */
! 796: static int crashEnableCmd(
! 797: void * clientData,
! 798: Tcl_Interp *interp,
! 799: int objc,
! 800: Tcl_Obj *CONST objv[]
! 801: ){
! 802: int isEnable;
! 803: static sqlite3_vfs crashVfs = {
! 804: 2, /* iVersion */
! 805: 0, /* szOsFile */
! 806: 0, /* mxPathname */
! 807: 0, /* pNext */
! 808: "crash", /* zName */
! 809: 0, /* pAppData */
! 810:
! 811: cfOpen, /* xOpen */
! 812: cfDelete, /* xDelete */
! 813: cfAccess, /* xAccess */
! 814: cfFullPathname, /* xFullPathname */
! 815: cfDlOpen, /* xDlOpen */
! 816: cfDlError, /* xDlError */
! 817: cfDlSym, /* xDlSym */
! 818: cfDlClose, /* xDlClose */
! 819: cfRandomness, /* xRandomness */
! 820: cfSleep, /* xSleep */
! 821: cfCurrentTime, /* xCurrentTime */
! 822: 0, /* xGetlastError */
! 823: 0, /* xCurrentTimeInt64 */
! 824: };
! 825:
! 826: if( objc!=2 ){
! 827: Tcl_WrongNumArgs(interp, 1, objv, "ENABLE");
! 828: return TCL_ERROR;
! 829: }
! 830:
! 831: if( Tcl_GetBooleanFromObj(interp, objv[1], &isEnable) ){
! 832: return TCL_ERROR;
! 833: }
! 834:
! 835: if( (isEnable && crashVfs.pAppData) || (!isEnable && !crashVfs.pAppData) ){
! 836: return TCL_OK;
! 837: }
! 838:
! 839: if( crashVfs.pAppData==0 ){
! 840: sqlite3_vfs *pOriginalVfs = sqlite3_vfs_find(0);
! 841: crashVfs.mxPathname = pOriginalVfs->mxPathname;
! 842: crashVfs.pAppData = (void *)pOriginalVfs;
! 843: crashVfs.szOsFile = sizeof(CrashFile) + pOriginalVfs->szOsFile;
! 844: sqlite3_vfs_register(&crashVfs, 0);
! 845: }else{
! 846: crashVfs.pAppData = 0;
! 847: sqlite3_vfs_unregister(&crashVfs);
! 848: }
! 849:
! 850: return TCL_OK;
! 851: }
! 852:
! 853: /*
! 854: ** tclcmd: sqlite_crashparams ?OPTIONS? DELAY CRASHFILE
! 855: **
! 856: ** This procedure implements a TCL command that enables crash testing
! 857: ** in testfixture. Once enabled, crash testing cannot be disabled.
! 858: **
! 859: ** Available options are "-characteristics" and "-sectorsize". Both require
! 860: ** an argument. For -sectorsize, this is the simulated sector size in
! 861: ** bytes. For -characteristics, the argument must be a list of io-capability
! 862: ** flags to simulate. Valid flags are "atomic", "atomic512", "atomic1K",
! 863: ** "atomic2K", "atomic4K", "atomic8K", "atomic16K", "atomic32K",
! 864: ** "atomic64K", "sequential" and "safe_append".
! 865: **
! 866: ** Example:
! 867: **
! 868: ** sqlite_crashparams -sect 1024 -char {atomic sequential} ./test.db 1
! 869: **
! 870: */
! 871: static int crashParamsObjCmd(
! 872: void * clientData,
! 873: Tcl_Interp *interp,
! 874: int objc,
! 875: Tcl_Obj *CONST objv[]
! 876: ){
! 877: int iDelay;
! 878: const char *zCrashFile;
! 879: int nCrashFile, iDc, iSectorSize;
! 880:
! 881: iDc = -1;
! 882: iSectorSize = -1;
! 883:
! 884: if( objc<3 ){
! 885: Tcl_WrongNumArgs(interp, 1, objv, "?OPTIONS? DELAY CRASHFILE");
! 886: goto error;
! 887: }
! 888:
! 889: zCrashFile = Tcl_GetStringFromObj(objv[objc-1], &nCrashFile);
! 890: if( nCrashFile>=sizeof(g.zCrashFile) ){
! 891: Tcl_AppendResult(interp, "Filename is too long: \"", zCrashFile, "\"", 0);
! 892: goto error;
! 893: }
! 894: if( Tcl_GetIntFromObj(interp, objv[objc-2], &iDelay) ){
! 895: goto error;
! 896: }
! 897:
! 898: if( processDevSymArgs(interp, objc-3, &objv[1], &iDc, &iSectorSize) ){
! 899: return TCL_ERROR;
! 900: }
! 901:
! 902: if( iDc>=0 ){
! 903: g.iDeviceCharacteristics = iDc;
! 904: }
! 905: if( iSectorSize>=0 ){
! 906: g.iSectorSize = iSectorSize;
! 907: }
! 908:
! 909: g.iCrash = iDelay;
! 910: memcpy(g.zCrashFile, zCrashFile, nCrashFile+1);
! 911: sqlite3CrashTestEnable = 1;
! 912: return TCL_OK;
! 913:
! 914: error:
! 915: return TCL_ERROR;
! 916: }
! 917:
! 918: static int devSymObjCmd(
! 919: void * clientData,
! 920: Tcl_Interp *interp,
! 921: int objc,
! 922: Tcl_Obj *CONST objv[]
! 923: ){
! 924: void devsym_register(int iDeviceChar, int iSectorSize);
! 925:
! 926: int iDc = -1;
! 927: int iSectorSize = -1;
! 928:
! 929: if( processDevSymArgs(interp, objc-1, &objv[1], &iDc, &iSectorSize) ){
! 930: return TCL_ERROR;
! 931: }
! 932: devsym_register(iDc, iSectorSize);
! 933:
! 934: return TCL_OK;
! 935: }
! 936:
! 937: /*
! 938: ** tclcmd: register_jt_vfs ?-default? PARENT-VFS
! 939: */
! 940: static int jtObjCmd(
! 941: void * clientData,
! 942: Tcl_Interp *interp,
! 943: int objc,
! 944: Tcl_Obj *CONST objv[]
! 945: ){
! 946: int jt_register(char *, int);
! 947: char *zParent = 0;
! 948:
! 949: if( objc!=2 && objc!=3 ){
! 950: Tcl_WrongNumArgs(interp, 1, objv, "?-default? PARENT-VFS");
! 951: return TCL_ERROR;
! 952: }
! 953: zParent = Tcl_GetString(objv[1]);
! 954: if( objc==3 ){
! 955: if( strcmp(zParent, "-default") ){
! 956: Tcl_AppendResult(interp,
! 957: "bad option \"", zParent, "\": must be -default", 0
! 958: );
! 959: return TCL_ERROR;
! 960: }
! 961: zParent = Tcl_GetString(objv[2]);
! 962: }
! 963:
! 964: if( !(*zParent) ){
! 965: zParent = 0;
! 966: }
! 967: if( jt_register(zParent, objc==3) ){
! 968: Tcl_AppendResult(interp, "Error in jt_register", 0);
! 969: return TCL_ERROR;
! 970: }
! 971:
! 972: return TCL_OK;
! 973: }
! 974:
! 975: /*
! 976: ** tclcmd: unregister_jt_vfs
! 977: */
! 978: static int jtUnregisterObjCmd(
! 979: void * clientData,
! 980: Tcl_Interp *interp,
! 981: int objc,
! 982: Tcl_Obj *CONST objv[]
! 983: ){
! 984: void jt_unregister(void);
! 985:
! 986: if( objc!=1 ){
! 987: Tcl_WrongNumArgs(interp, 1, objv, "");
! 988: return TCL_ERROR;
! 989: }
! 990:
! 991: jt_unregister();
! 992: return TCL_OK;
! 993: }
! 994:
! 995: #endif /* SQLITE_OMIT_DISKIO */
! 996:
! 997: /*
! 998: ** This procedure registers the TCL procedures defined in this file.
! 999: */
! 1000: int Sqlitetest6_Init(Tcl_Interp *interp){
! 1001: #ifndef SQLITE_OMIT_DISKIO
! 1002: Tcl_CreateObjCommand(interp, "sqlite3_crash_enable", crashEnableCmd, 0, 0);
! 1003: Tcl_CreateObjCommand(interp, "sqlite3_crashparams", crashParamsObjCmd, 0, 0);
! 1004: Tcl_CreateObjCommand(interp, "sqlite3_simulate_device", devSymObjCmd, 0, 0);
! 1005: Tcl_CreateObjCommand(interp, "register_jt_vfs", jtObjCmd, 0, 0);
! 1006: Tcl_CreateObjCommand(interp, "unregister_jt_vfs", jtUnregisterObjCmd, 0, 0);
! 1007: #endif
! 1008: return TCL_OK;
! 1009: }
! 1010:
! 1011: #endif /* SQLITE_TEST */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>