AryaWu/sqlite
0
1/*2** 2004 May 223**4** The author disclaims copyright to this source code. In place of5** 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 simulate14** the effect on the database file of an OS crash or power failure. This15** 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 "tclsqlite.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 25typedef struct CrashFile CrashFile;26typedef struct CrashGlobal CrashGlobal;27typedef 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 is34** written to the file object, instead of being written to the35** underlying file, the write operation is stored in an in-memory36** structure (type WriteBuffer). This structure is placed at the37** end of a global ordered list (the write-list).38**39** When data is read from a file object, the requested region is40** first retrieved from the real file. The write-list is then41** traversed and data copied from any overlapping WriteBuffer42** structures to the output buffer. i.e. a read() operation following43** one or more write() operations works as expected, even if no44** data has actually been written out to the real file.45**46** When a fsync() operation is performed, an operating system crash47** may be simulated, in which case exit(-1) is called (the call to48** xSync() never returns). Whether or not a crash is simulated,49** the data associated with a subset of the WriteBuffer structures50** stored in the write-list is written to the real underlying files51** 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 is55** 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 the60** SQLITE_IOCAP_XXX flags set.61**62** In normal mode, if the fsync() is not a simulated crash, the63** write-list is traversed from beginning to end. Each WriteBuffer64** 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 for68** each WriteBuffer in the write-list, regardless of which69** file-handle it is associated with:70**71** 1. The buffer is correctly written to the file, just as if72** 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 that77** overlap the region specified by the WriteBuffer. Or garbage78** data is written to some contiguous section within the79** overlapped sectors.80**81** Device Characteristic flag handling:82**83** If the IOCAP_ATOMIC flag is set, then option (3) above is84** never selected.85**86** If the IOCAP_ATOMIC512 flag is set, and the WriteBuffer represents87** an aligned write() of an integer number of 512 byte regions, then88** option (3) above is never selected. Instead, each 512 byte region89** is either correctly written or left completely untouched. Similar90** logic governs the behavior if any of the other ATOMICXXX flags91** is set.92**93** If either the IOCAP_SAFEAPPEND or IOCAP_SEQUENTIAL flags are set94** and a crash is being simulated, then an entry of the write-list is95** selected at random. Everything in the list after the selected entry96** is discarded before processing begins.97**98** If IOCAP_SEQUENTIAL is set and a crash is being simulated, option99** (1) is selected for all write-list entries except the last. If a100** crash is not being simulated, then all entries in the write-list101** that occur before at least one write() on the file-handle specified102** 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 option106** (1) is always selected.107*/108 109/*110** Each write operation in the write-list is represented by an instance111** 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 is115** truncated to.116*/117struct 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 126struct 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() and133 ** OsFileSize() calls. Although both could be done by traversing the134 ** write-list, in practice this is impractically slow.135 */136 u8 *zData; /* Buffer containing file contents */137 int nData; /* Size of buffer allocated at zData */138 i64 iSize; /* Size of file in bytes */139};140 141struct 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 152static CrashGlobal g = {0, 0, SQLITE_DEFAULT_SECTOR_SIZE, 0, 0};153 154/*155** Set this global variable to 1 to enable crash testing.156*/157static int sqlite3CrashTestEnable = 0;158 159static void *crash_malloc(int nByte){160 return (void *)Tcl_AttemptAlloc((size_t)nByte);161}162static void crash_free(void *p){163 Tcl_Free(p);164}165static void *crash_realloc(void *p, int n){166 return (void *)Tcl_AttemptRealloc(p, (size_t)n);167}168 169/*170** Wrapper around the sqlite3OsWrite() function that avoids writing to the171** 512 byte block beginning at offset PENDING_BYTE.172*/173static int writeDbFile(CrashFile *p, u8 *z, i64 iAmt, i64 iOff){174 int rc = SQLITE_OK;175 int iSkip = 0;176 if( (iAmt-iSkip)>0 ){177 rc = sqlite3OsWrite(p->pRealFile, &z[iSkip], (int)(iAmt-iSkip), iOff+iSkip);178 }179 return rc;180}181 182/*183** Flush the write-list as if xSync() had been called on file handle184** pFile. If isCrash is true, simulate a crash.185*/186static int writeListSync(CrashFile *pFile, int isCrash){187 int rc = SQLITE_OK;188 int iDc = g.iDeviceCharacteristics;189 190 WriteBuffer *pWrite;191 WriteBuffer **ppPtr;192 193 /* If this is not a crash simulation, set pFinal to point to the194 ** last element of the write-list that is associated with file handle195 ** pFile.196 **197 ** If this is a crash simulation, set pFinal to an arbitrarily selected198 ** element of the write-list.199 */200 WriteBuffer *pFinal = 0;201 if( !isCrash ){202 for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext){203 if( pWrite->pFile==pFile ){204 pFinal = pWrite;205 }206 }207 }else if( iDc&(SQLITE_IOCAP_SEQUENTIAL|SQLITE_IOCAP_SAFE_APPEND) ){208 int nWrite = 0;209 int iFinal;210 for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext) nWrite++;211 sqlite3_randomness(sizeof(int), &iFinal);212 iFinal = ((iFinal<0)?-1*iFinal:iFinal)%nWrite;213 for(pWrite=g.pWriteList; iFinal>0; pWrite=pWrite->pNext) iFinal--;214 pFinal = pWrite;215 }216 217#ifdef TRACE_CRASHTEST218 if( pFile ){219 printf("Sync %s (is %s crash)\n", pFile->zName, (isCrash?"a":"not a"));220 }221#endif222 223 ppPtr = &g.pWriteList;224 for(pWrite=*ppPtr; rc==SQLITE_OK && pWrite; pWrite=*ppPtr){225 sqlite3_file *pRealFile = pWrite->pFile->pRealFile;226 227 /* (eAction==1) -> write block out normally,228 ** (eAction==2) -> do nothing,229 ** (eAction==3) -> trash sectors.230 */231 int eAction = 0;232 if( !isCrash ){233 eAction = 2;234 if( (pWrite->pFile==pFile || iDc&SQLITE_IOCAP_SEQUENTIAL) ){235 eAction = 1;236 }237 }else{238 char random;239 sqlite3_randomness(1, &random);240 241 /* Do not select option 3 (sector trashing) if the IOCAP_ATOMIC flag242 ** is set or this is an OsTruncate(), not an Oswrite().243 */244 if( (iDc&SQLITE_IOCAP_ATOMIC) || (pWrite->zBuf==0) ){245 random &= 0x01;246 }247 248 /* If IOCAP_SEQUENTIAL is set and this is not the final entry249 ** in the truncated write-list, always select option 1 (write250 ** out correctly).251 */252 if( (iDc&SQLITE_IOCAP_SEQUENTIAL && pWrite!=pFinal) ){253 random = 0;254 }255 256 /* If IOCAP_SAFE_APPEND is set and this OsWrite() operation is257 ** an append (first byte of the written region is 1 byte past the258 ** current EOF), always select option 1 (write out correctly).259 */260 if( iDc&SQLITE_IOCAP_SAFE_APPEND && pWrite->zBuf ){261 i64 iSize;262 sqlite3OsFileSize(pRealFile, &iSize);263 if( iSize==pWrite->iOffset ){264 random = 0;265 }266 }267 268 if( (random&0x06)==0x06 ){269 eAction = 3;270 }else{271 eAction = ((random&0x01)?2:1);272 }273 }274 275 switch( eAction ){276 case 1: { /* Write out correctly */277 if( pWrite->zBuf ){278 rc = writeDbFile(279 pWrite->pFile, pWrite->zBuf, pWrite->nBuf, pWrite->iOffset280 );281 }else{282 rc = sqlite3OsTruncate(pRealFile, pWrite->iOffset);283 }284 *ppPtr = pWrite->pNext;285#ifdef TRACE_CRASHTEST286 if( isCrash ){287 printf("Writing %d bytes @ %d (%s)\n",288 pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName289 );290 }291#endif292 crash_free(pWrite);293 break;294 }295 case 2: { /* Do nothing */296 ppPtr = &pWrite->pNext;297#ifdef TRACE_CRASHTEST298 if( isCrash ){299 printf("Omiting %d bytes @ %d (%s)\n",300 pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName301 );302 }303#endif304 break;305 }306 case 3: { /* Trash sectors */307 u8 *zGarbage;308 int iFirst = (int)(pWrite->iOffset/g.iSectorSize);309 int iLast = (int)((pWrite->iOffset+pWrite->nBuf-1)/g.iSectorSize);310 311 assert(pWrite->zBuf);312 313#ifdef TRACE_CRASHTEST314 printf("Trashing %d sectors (%d bytes) @ %lld (sector %d) (%s)\n",315 1+iLast-iFirst, (1+iLast-iFirst)*g.iSectorSize,316 pWrite->iOffset, iFirst, pWrite->pFile->zName317 );318#endif319 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.iSectorSize327 );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*/358static int writeListAppend(359 sqlite3_file *pFile,360 sqlite3_int64 iOffset,361 const u8 *zBuf,362 int nBuf363){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*/395static 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*/405static int cfRead(406 sqlite3_file *pFile,407 void *zBuf,408 int iAmt,409 sqlite_int64 iOfst410){411 CrashFile *pCrash = (CrashFile *)pFile;412 int nCopy = (int)MIN((i64)iAmt, (pCrash->iSize - iOfst));413 414 if( nCopy>0 ){415 memcpy(zBuf, &pCrash->zData[iOfst], nCopy);416 }417 418 /* Check the file-size to see if this is a short-read */419 if( nCopy<iAmt ){420 return SQLITE_IOERR_SHORT_READ;421 }422 423 return SQLITE_OK;424}425 426/*427** Write data to a crash-file.428*/429static int cfWrite(430 sqlite3_file *pFile,431 const void *zBuf,432 int iAmt,433 sqlite_int64 iOfst434){435 CrashFile *pCrash = (CrashFile *)pFile;436 if( iAmt+iOfst>pCrash->iSize ){437 pCrash->iSize = (int)(iAmt+iOfst);438 }439 while( pCrash->iSize>pCrash->nData ){440 u8 *zNew;441 int nNew = (pCrash->nData*2) + 4096;442 zNew = crash_realloc(pCrash->zData, nNew);443 if( !zNew ){444 return SQLITE_NOMEM;445 }446 memset(&zNew[pCrash->nData], 0, nNew-pCrash->nData);447 pCrash->nData = nNew;448 pCrash->zData = zNew;449 }450 memcpy(&pCrash->zData[iOfst], zBuf, iAmt);451 return writeListAppend(pFile, iOfst, zBuf, iAmt);452}453 454/*455** Truncate a crash-file.456*/457static int cfTruncate(sqlite3_file *pFile, sqlite_int64 size){458 CrashFile *pCrash = (CrashFile *)pFile;459 assert(size>=0);460 if( pCrash->iSize>size ){461 pCrash->iSize = (int)size;462 }463 return writeListAppend(pFile, size, 0, 0);464}465 466/*467** Sync a crash-file.468*/469static int cfSync(sqlite3_file *pFile, int flags){470 CrashFile *pCrash = (CrashFile *)pFile;471 int isCrash = 0;472 473 const char *zName = pCrash->zName;474 const char *zCrashFile = g.zCrashFile;475 int nName = (int)strlen(zName);476 int nCrashFile = (int)strlen(zCrashFile);477 478 if( nCrashFile>0 && zCrashFile[nCrashFile-1]=='*' ){479 nCrashFile--;480 if( nName>nCrashFile ) nName = nCrashFile;481 }482 483#ifdef TRACE_CRASHTEST484 printf("cfSync(): nName = %d, nCrashFile = %d, zName = %s, zCrashFile = %s\n",485 nName, nCrashFile, zName, zCrashFile);486#endif487 488 if( nName==nCrashFile && 0==memcmp(zName, zCrashFile, nName) ){489#ifdef TRACE_CRASHTEST490 printf("cfSync(): name matched, g.iCrash = %d\n", g.iCrash);491#endif492 if( (--g.iCrash)==0 ) isCrash = 1;493 }494 495 return writeListSync(pCrash, isCrash);496}497 498/*499** Return the current file-size of the crash-file.500*/501static int cfFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){502 CrashFile *pCrash = (CrashFile *)pFile;503 *pSize = (i64)pCrash->iSize;504 return SQLITE_OK;505}506 507/*508** Calls related to file-locks are passed on to the real file handle.509*/510static int cfLock(sqlite3_file *pFile, int eLock){511 return sqlite3OsLock(((CrashFile *)pFile)->pRealFile, eLock);512}513static int cfUnlock(sqlite3_file *pFile, int eLock){514 return sqlite3OsUnlock(((CrashFile *)pFile)->pRealFile, eLock);515}516static int cfCheckReservedLock(sqlite3_file *pFile, int *pResOut){517 return sqlite3OsCheckReservedLock(((CrashFile *)pFile)->pRealFile, pResOut);518}519static int cfFileControl(sqlite3_file *pFile, int op, void *pArg){520 if( op==SQLITE_FCNTL_SIZE_HINT ){521 CrashFile *pCrash = (CrashFile *)pFile;522 i64 nByte = *(i64 *)pArg;523 if( nByte>pCrash->iSize ){524 if( SQLITE_OK==writeListAppend(pFile, nByte, 0, 0) ){525 pCrash->iSize = (int)nByte;526 }527 }528 return SQLITE_OK;529 }530 return sqlite3OsFileControl(((CrashFile *)pFile)->pRealFile, op, pArg);531}532 533/*534** The xSectorSize() and xDeviceCharacteristics() functions return535** the global values configured by the [sqlite_crashparams] tcl536* interface.537*/538static int cfSectorSize(sqlite3_file *pFile){539 return g.iSectorSize;540}541static int cfDeviceCharacteristics(sqlite3_file *pFile){542 return g.iDeviceCharacteristics;543}544 545/*546** Pass-throughs for WAL support.547*/548static int cfShmLock(sqlite3_file *pFile, int ofst, int n, int flags){549 sqlite3_file *pReal = ((CrashFile*)pFile)->pRealFile;550 return pReal->pMethods->xShmLock(pReal, ofst, n, flags);551}552static void cfShmBarrier(sqlite3_file *pFile){553 sqlite3_file *pReal = ((CrashFile*)pFile)->pRealFile;554 pReal->pMethods->xShmBarrier(pReal);555}556static int cfShmUnmap(sqlite3_file *pFile, int delFlag){557 sqlite3_file *pReal = ((CrashFile*)pFile)->pRealFile;558 return pReal->pMethods->xShmUnmap(pReal, delFlag);559}560static int cfShmMap(561 sqlite3_file *pFile, /* Handle open on database file */562 int iRegion, /* Region to retrieve */563 int sz, /* Size of regions */564 int w, /* True to extend file if necessary */565 void volatile **pp /* OUT: Mapped memory */566){567 sqlite3_file *pReal = ((CrashFile*)pFile)->pRealFile;568 return pReal->pMethods->xShmMap(pReal, iRegion, sz, w, pp);569}570 571static const sqlite3_io_methods CrashFileVtab = {572 2, /* iVersion */573 cfClose, /* xClose */574 cfRead, /* xRead */575 cfWrite, /* xWrite */576 cfTruncate, /* xTruncate */577 cfSync, /* xSync */578 cfFileSize, /* xFileSize */579 cfLock, /* xLock */580 cfUnlock, /* xUnlock */581 cfCheckReservedLock, /* xCheckReservedLock */582 cfFileControl, /* xFileControl */583 cfSectorSize, /* xSectorSize */584 cfDeviceCharacteristics, /* xDeviceCharacteristics */585 cfShmMap, /* xShmMap */586 cfShmLock, /* xShmLock */587 cfShmBarrier, /* xShmBarrier */588 cfShmUnmap /* xShmUnmap */589};590 591/*592** Application data for the crash VFS593*/594struct crashAppData {595 sqlite3_vfs *pOrig; /* Wrapped vfs structure */596};597 598/*599** Open a crash-file file handle.600**601** The caller will have allocated pVfs->szOsFile bytes of space602** at pFile. This file uses this space for the CrashFile structure603** and allocates space for the "real" file structure using604** sqlite3_malloc(). The assumption here is (pVfs->szOsFile) is605** equal or greater than sizeof(CrashFile).606*/607static int cfOpen(608 sqlite3_vfs *pCfVfs,609 const char *zName,610 sqlite3_file *pFile,611 int flags,612 int *pOutFlags613){614 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;615 int rc;616 CrashFile *pWrapper = (CrashFile *)pFile;617 sqlite3_file *pReal = (sqlite3_file*)&pWrapper[1];618 619 memset(pWrapper, 0, sizeof(CrashFile));620 rc = sqlite3OsOpen(pVfs, zName, pReal, flags, pOutFlags);621 622 if( rc==SQLITE_OK ){623 i64 iSize;624 pWrapper->pMethod = &CrashFileVtab;625 pWrapper->zName = (char *)zName;626 pWrapper->pRealFile = pReal;627 rc = sqlite3OsFileSize(pReal, &iSize);628 pWrapper->iSize = (int)iSize;629 pWrapper->flags = flags;630 }631 if( rc==SQLITE_OK ){632 pWrapper->nData = (int)(4096 + pWrapper->iSize);633 pWrapper->zData = crash_malloc(pWrapper->nData);634 if( pWrapper->zData ){635 /* os_unix.c contains an assert() that fails if the caller attempts636 ** to read data from the 512-byte locking region of a file opened637 ** with the SQLITE_OPEN_MAIN_DB flag. This region of a database file638 ** never contains valid data anyhow. So avoid doing such a read here.639 **640 ** UPDATE: It also contains an assert() verifying that each call641 ** to the xRead() method reads less than 128KB of data.642 */643 i64 iOff;644 645 memset(pWrapper->zData, 0, pWrapper->nData);646 for(iOff=0; iOff<pWrapper->iSize; iOff += 512){647 int nRead = (int)(pWrapper->iSize - iOff);648 if( nRead>512 ) nRead = 512;649 rc = sqlite3OsRead(pReal, &pWrapper->zData[iOff], nRead, iOff);650 }651 }else{652 rc = SQLITE_NOMEM;653 }654 }655 if( rc!=SQLITE_OK && pWrapper->pMethod ){656 sqlite3OsClose(pFile);657 }658 return rc;659}660 661static int cfDelete(sqlite3_vfs *pCfVfs, const char *zPath, int dirSync){662 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;663 return pVfs->xDelete(pVfs, zPath, dirSync);664}665static int cfAccess(666 sqlite3_vfs *pCfVfs,667 const char *zPath,668 int flags,669 int *pResOut670){671 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;672 return pVfs->xAccess(pVfs, zPath, flags, pResOut);673}674static int cfFullPathname(675 sqlite3_vfs *pCfVfs,676 const char *zPath,677 int nPathOut,678 char *zPathOut679){680 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;681 return pVfs->xFullPathname(pVfs, zPath, nPathOut, zPathOut);682}683static void *cfDlOpen(sqlite3_vfs *pCfVfs, const char *zPath){684 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;685 return pVfs->xDlOpen(pVfs, zPath);686}687static void cfDlError(sqlite3_vfs *pCfVfs, int nByte, char *zErrMsg){688 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;689 pVfs->xDlError(pVfs, nByte, zErrMsg);690}691static void (*cfDlSym(sqlite3_vfs *pCfVfs, void *pH, const char *zSym))(void){692 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;693 return pVfs->xDlSym(pVfs, pH, zSym);694}695static void cfDlClose(sqlite3_vfs *pCfVfs, void *pHandle){696 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;697 pVfs->xDlClose(pVfs, pHandle);698}699static int cfRandomness(sqlite3_vfs *pCfVfs, int nByte, char *zBufOut){700 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;701 return pVfs->xRandomness(pVfs, nByte, zBufOut);702}703static int cfSleep(sqlite3_vfs *pCfVfs, int nMicro){704 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;705 return pVfs->xSleep(pVfs, nMicro);706}707static int cfCurrentTime(sqlite3_vfs *pCfVfs, double *pTimeOut){708 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;709 return pVfs->xCurrentTime(pVfs, pTimeOut);710}711static int cfGetLastError(sqlite3_vfs *pCfVfs, int n, char *z){712 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;713 return pVfs->xGetLastError(pVfs, n, z);714}715 716static int processDevSymArgs(717 Tcl_Interp *interp,718 int objc,719 Tcl_Obj *CONST objv[],720 int *piDeviceChar,721 int *piSectorSize722){723 struct DeviceFlag {724 char *zName;725 int iValue;726 } aFlag[] = {727 { "atomic", SQLITE_IOCAP_ATOMIC },728 { "atomic512", SQLITE_IOCAP_ATOMIC512 },729 { "atomic1k", SQLITE_IOCAP_ATOMIC1K },730 { "atomic2k", SQLITE_IOCAP_ATOMIC2K },731 { "atomic4k", SQLITE_IOCAP_ATOMIC4K },732 { "atomic8k", SQLITE_IOCAP_ATOMIC8K },733 { "atomic16k", SQLITE_IOCAP_ATOMIC16K },734 { "atomic32k", SQLITE_IOCAP_ATOMIC32K },735 { "atomic64k", SQLITE_IOCAP_ATOMIC64K },736 { "sequential", SQLITE_IOCAP_SEQUENTIAL },737 { "safe_append", SQLITE_IOCAP_SAFE_APPEND },738 { "powersafe_overwrite", SQLITE_IOCAP_POWERSAFE_OVERWRITE },739 { "batch-atomic", SQLITE_IOCAP_BATCH_ATOMIC },740 { 0, 0 }741 };742 743 int i;744 int iDc = 0;745 int iSectorSize = 0;746 int setSectorsize = 0;747 int setDeviceChar = 0;748 749 for(i=0; i<objc; i+=2){750 Tcl_Size nOpt;751 char *zOpt = Tcl_GetStringFromObj(objv[i], &nOpt);752 753 if( (nOpt>11 || nOpt<2 || strncmp("-sectorsize", zOpt, nOpt))754 && (nOpt>16 || nOpt<2 || strncmp("-characteristics", zOpt, nOpt))755 ){756 Tcl_AppendResult(interp,757 "Bad option: \"", zOpt,758 "\" - must be \"-characteristics\" or \"-sectorsize\"", NULL759 );760 return TCL_ERROR;761 }762 if( i==objc-1 ){763 Tcl_AppendResult(interp, "Option requires an argument: \"", zOpt, "\"", NULL);764 return TCL_ERROR;765 }766 767 if( zOpt[1]=='s' ){768 if( Tcl_GetIntFromObj(interp, objv[i+1], &iSectorSize) ){769 return TCL_ERROR;770 }771 setSectorsize = 1;772 }else{773 int j;774 Tcl_Obj **apObj;775 Tcl_Size nObj;776 if( Tcl_ListObjGetElements(interp, objv[i+1], &nObj, &apObj) ){777 return TCL_ERROR;778 }779 for(j=0; j<(int)nObj; j++){780 int rc;781 int iChoice;782 Tcl_Obj *pFlag = Tcl_DuplicateObj(apObj[j]);783 Tcl_IncrRefCount(pFlag);784 Tcl_UtfToLower(Tcl_GetString(pFlag));785 786 rc = Tcl_GetIndexFromObjStruct(787 interp, pFlag, aFlag, sizeof(aFlag[0]), "no such flag", 0, &iChoice788 );789 Tcl_DecrRefCount(pFlag);790 if( rc ){791 return TCL_ERROR;792 }793 794 iDc |= aFlag[iChoice].iValue;795 }796 setDeviceChar = 1;797 }798 }799 800 if( setDeviceChar ){801 *piDeviceChar = iDc;802 }803 if( setSectorsize ){804 *piSectorSize = iSectorSize;805 }806 807 return TCL_OK;808}809 810/*811** tclcmd: sqlite3_crash_now812**813** Simulate a crash immediately. This function does not return814** (writeListSync() calls exit(-1)).815*/816static int SQLITE_TCLAPI crashNowCmd(817 void * clientData,818 Tcl_Interp *interp,819 int objc,820 Tcl_Obj *CONST objv[]821){822 if( objc!=1 ){823 Tcl_WrongNumArgs(interp, 1, objv, "");824 return TCL_ERROR;825 }826 writeListSync(0, 1);827 assert( 0 );828 return TCL_OK;829}830 831/*832** tclcmd: sqlite_crash_enable ENABLE ?DEFAULT?833**834** Parameter ENABLE must be a boolean value. If true, then the "crash"835** vfs is added to the system. If false, it is removed.836*/837static int SQLITE_TCLAPI crashEnableCmd(838 void * clientData,839 Tcl_Interp *interp,840 int objc,841 Tcl_Obj *CONST objv[]842){843 int isEnable;844 int isDefault = 0;845 static sqlite3_vfs crashVfs = {846 2, /* iVersion */847 0, /* szOsFile */848 0, /* mxPathname */849 0, /* pNext */850 "crash", /* zName */851 0, /* pAppData */852 853 cfOpen, /* xOpen */854 cfDelete, /* xDelete */855 cfAccess, /* xAccess */856 cfFullPathname, /* xFullPathname */857 cfDlOpen, /* xDlOpen */858 cfDlError, /* xDlError */859 cfDlSym, /* xDlSym */860 cfDlClose, /* xDlClose */861 cfRandomness, /* xRandomness */862 cfSleep, /* xSleep */863 cfCurrentTime, /* xCurrentTime */864 cfGetLastError, /* xGetLastError */865 0, /* xCurrentTimeInt64 */866 };867 868 if( objc!=2 && objc!=3 ){869 Tcl_WrongNumArgs(interp, 1, objv, "ENABLE ?DEFAULT?");870 return TCL_ERROR;871 }872 873 if( Tcl_GetBooleanFromObj(interp, objv[1], &isEnable) ){874 return TCL_ERROR;875 }876 if( objc==3 && Tcl_GetBooleanFromObj(interp, objv[2], &isDefault) ){877 return TCL_ERROR;878 }879 880 if( (isEnable && crashVfs.pAppData) || (!isEnable && !crashVfs.pAppData) ){881 return TCL_OK;882 }883 884 if( crashVfs.pAppData==0 ){885 sqlite3_vfs *pOriginalVfs = sqlite3_vfs_find(0);886 crashVfs.mxPathname = pOriginalVfs->mxPathname;887 crashVfs.pAppData = (void *)pOriginalVfs;888 crashVfs.szOsFile = sizeof(CrashFile) + pOriginalVfs->szOsFile;889 sqlite3_vfs_register(&crashVfs, isDefault);890 }else{891 crashVfs.pAppData = 0;892 sqlite3_vfs_unregister(&crashVfs);893 }894 895 return TCL_OK;896}897 898/*899** tclcmd: sqlite_crashparams ?OPTIONS? DELAY CRASHFILE900**901** This procedure implements a TCL command that enables crash testing902** in testfixture. Once enabled, crash testing cannot be disabled.903**904** Available options are "-characteristics" and "-sectorsize". Both require905** an argument. For -sectorsize, this is the simulated sector size in906** bytes. For -characteristics, the argument must be a list of io-capability907** flags to simulate. Valid flags are "atomic", "atomic512", "atomic1K",908** "atomic2K", "atomic4K", "atomic8K", "atomic16K", "atomic32K",909** "atomic64K", "sequential" and "safe_append".910**911** Example:912**913** sqlite_crashparams -sect 1024 -char {atomic sequential} ./test.db 1914**915*/916static int SQLITE_TCLAPI crashParamsObjCmd(917 void * clientData,918 Tcl_Interp *interp,919 int objc,920 Tcl_Obj *CONST objv[]921){922 int iDelay;923 const char *zCrashFile;924 Tcl_Size nCrashFile;925 int iDc, iSectorSize;926 927 iDc = -1;928 iSectorSize = -1;929 930 if( objc<3 ){931 Tcl_WrongNumArgs(interp, 1, objv, "?OPTIONS? DELAY CRASHFILE");932 goto error;933 }934 935 zCrashFile = Tcl_GetStringFromObj(objv[objc-1], &nCrashFile);936 if( nCrashFile>=sizeof(g.zCrashFile) ){937 Tcl_AppendResult(interp, "Filename is too long: \"", zCrashFile, "\"", NULL);938 goto error;939 }940 if( Tcl_GetIntFromObj(interp, objv[objc-2], &iDelay) ){941 goto error;942 }943 944 if( processDevSymArgs(interp, objc-3, &objv[1], &iDc, &iSectorSize) ){945 return TCL_ERROR;946 }947 948 if( iDc>=0 ){949 g.iDeviceCharacteristics = iDc;950 }951 if( iSectorSize>=0 ){952 g.iSectorSize = iSectorSize;953 }954 955 g.iCrash = iDelay;956 memcpy(g.zCrashFile, zCrashFile, nCrashFile+1);957 sqlite3CrashTestEnable = 1;958 return TCL_OK;959 960error:961 return TCL_ERROR;962}963 964static int SQLITE_TCLAPI devSymObjCmd(965 void * clientData,966 Tcl_Interp *interp,967 int objc,968 Tcl_Obj *CONST objv[]969){970 void devsym_register(int iDeviceChar, int iSectorSize);971 972 int iDc = -1;973 int iSectorSize = -1;974 975 if( processDevSymArgs(interp, objc-1, &objv[1], &iDc, &iSectorSize) ){976 return TCL_ERROR;977 }978 devsym_register(iDc, iSectorSize);979 980 return TCL_OK;981}982 983/*984** tclcmd: sqlite3_crash_on_write N985*/986static int SQLITE_TCLAPI writeCrashObjCmd(987 void * clientData,988 Tcl_Interp *interp,989 int objc,990 Tcl_Obj *CONST objv[]991){992 void devsym_crash_on_write(int);993 int nWrite = 0;994 995 if( objc!=2 ){996 Tcl_WrongNumArgs(interp, 1, objv, "NWRITE");997 return TCL_ERROR;998 }999 if( Tcl_GetIntFromObj(interp, objv[1], &nWrite) ){1000 return TCL_ERROR;1001 }1002 1003 devsym_crash_on_write(nWrite);1004 return TCL_OK;1005}1006 1007/*1008** tclcmd: unregister_devsim1009*/1010static int SQLITE_TCLAPI dsUnregisterObjCmd(1011 void * clientData,1012 Tcl_Interp *interp,1013 int objc,1014 Tcl_Obj *CONST objv[]1015){1016 void devsym_unregister(void);1017 1018 if( objc!=1 ){1019 Tcl_WrongNumArgs(interp, 1, objv, "");1020 return TCL_ERROR;1021 }1022 1023 devsym_unregister();1024 return TCL_OK;1025}1026 1027/*1028** tclcmd: register_jt_vfs ?-default? PARENT-VFS1029*/1030static int SQLITE_TCLAPI jtObjCmd(1031 void * clientData,1032 Tcl_Interp *interp,1033 int objc,1034 Tcl_Obj *CONST objv[]1035){1036 int jt_register(char *, int);1037 char *zParent = 0;1038 1039 if( objc!=2 && objc!=3 ){1040 Tcl_WrongNumArgs(interp, 1, objv, "?-default? PARENT-VFS");1041 return TCL_ERROR;1042 }1043 zParent = Tcl_GetString(objv[1]);1044 if( objc==3 ){1045 if( strcmp(zParent, "-default") ){1046 Tcl_AppendResult(interp,1047 "bad option \"", zParent, "\": must be -default", NULL1048 );1049 return TCL_ERROR;1050 }1051 zParent = Tcl_GetString(objv[2]);1052 }1053 1054 if( !(*zParent) ){1055 zParent = 0;1056 }1057 if( jt_register(zParent, objc==3) ){1058 Tcl_AppendResult(interp, "Error in jt_register", NULL);1059 return TCL_ERROR;1060 }1061 1062 return TCL_OK;1063}1064 1065/*1066** tclcmd: unregister_jt_vfs1067*/1068static int SQLITE_TCLAPI jtUnregisterObjCmd(1069 void * clientData,1070 Tcl_Interp *interp,1071 int objc,1072 Tcl_Obj *CONST objv[]1073){1074 void jt_unregister(void);1075 1076 if( objc!=1 ){1077 Tcl_WrongNumArgs(interp, 1, objv, "");1078 return TCL_ERROR;1079 }1080 1081 jt_unregister();1082 return TCL_OK;1083}1084 1085#endif /* SQLITE_OMIT_DISKIO */1086 1087/*1088** This procedure registers the TCL procedures defined in this file.1089*/1090int Sqlitetest6_Init(Tcl_Interp *interp){1091#ifndef SQLITE_OMIT_DISKIO1092 Tcl_CreateObjCommand(interp, "sqlite3_crash_enable", crashEnableCmd, 0, 0);1093 Tcl_CreateObjCommand(interp, "sqlite3_crashparams", crashParamsObjCmd, 0, 0);1094 Tcl_CreateObjCommand(interp, "sqlite3_crash_now", crashNowCmd, 0, 0);1095 Tcl_CreateObjCommand(interp, "sqlite3_simulate_device", devSymObjCmd, 0, 0);1096 Tcl_CreateObjCommand(interp, "sqlite3_crash_on_write", writeCrashObjCmd,0,0);1097 Tcl_CreateObjCommand(interp, "unregister_devsim", dsUnregisterObjCmd, 0, 0);1098 Tcl_CreateObjCommand(interp, "register_jt_vfs", jtObjCmd, 0, 0);1099 Tcl_CreateObjCommand(interp, "unregister_jt_vfs", jtUnregisterObjCmd, 0, 0);1100#endif1101 return TCL_OK;1102}1103 1104#endif /* SQLITE_TEST */1105 