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>