xref: /sqlite-3.40.0/src/test6.c (revision 7093a3be)
19c06c953Sdrh /*
29c06c953Sdrh ** 2004 May 22
39c06c953Sdrh **
49c06c953Sdrh ** The author disclaims copyright to this source code.  In place of
59c06c953Sdrh ** a legal notice, here is a blessing:
69c06c953Sdrh **
79c06c953Sdrh **    May you do good and not evil.
89c06c953Sdrh **    May you find forgiveness for yourself and forgive others.
99c06c953Sdrh **    May you share freely, never taking more than you give.
109c06c953Sdrh **
119c06c953Sdrh ******************************************************************************
129c06c953Sdrh **
139c06c953Sdrh ** This file contains code that modified the OS layer in order to simulate
149c06c953Sdrh ** the effect on the database file of an OS crash or power failure.  This
159c06c953Sdrh ** is used to test the ability of SQLite to recover from those situations.
169c06c953Sdrh */
17f55b8998Sdanielk1977 #if SQLITE_TEST          /* This file is used for testing only */
189c06c953Sdrh #include "sqliteInt.h"
1952b1dbb5Smistachkin #if defined(INCLUDE_SQLITE_TCL_H)
2052b1dbb5Smistachkin #  include "sqlite_tcl.h"
2152b1dbb5Smistachkin #else
229c06c953Sdrh #  include "tcl.h"
2352b1dbb5Smistachkin #endif
249c06c953Sdrh 
25198bf391Sdrh #ifndef SQLITE_OMIT_DISKIO  /* This file is a no-op if disk I/O is disabled */
26198bf391Sdrh 
27f8940aefSdanielk1977 /* #define TRACE_CRASHTEST */
28f8940aefSdanielk1977 
2962079060Sdanielk1977 typedef struct CrashFile CrashFile;
3062079060Sdanielk1977 typedef struct CrashGlobal CrashGlobal;
310d24e6b8Sdanielk1977 typedef struct WriteBuffer WriteBuffer;
320d24e6b8Sdanielk1977 
330d24e6b8Sdanielk1977 /*
340d24e6b8Sdanielk1977 ** Method:
350d24e6b8Sdanielk1977 **
360d24e6b8Sdanielk1977 **   This layer is implemented as a wrapper around the "real"
370d24e6b8Sdanielk1977 **   sqlite3_file object for the host system. Each time data is
380d24e6b8Sdanielk1977 **   written to the file object, instead of being written to the
390d24e6b8Sdanielk1977 **   underlying file, the write operation is stored in an in-memory
400d24e6b8Sdanielk1977 **   structure (type WriteBuffer). This structure is placed at the
410d24e6b8Sdanielk1977 **   end of a global ordered list (the write-list).
420d24e6b8Sdanielk1977 **
430d24e6b8Sdanielk1977 **   When data is read from a file object, the requested region is
440d24e6b8Sdanielk1977 **   first retrieved from the real file. The write-list is then
450d24e6b8Sdanielk1977 **   traversed and data copied from any overlapping WriteBuffer
460d24e6b8Sdanielk1977 **   structures to the output buffer. i.e. a read() operation following
470d24e6b8Sdanielk1977 **   one or more write() operations works as expected, even if no
480d24e6b8Sdanielk1977 **   data has actually been written out to the real file.
490d24e6b8Sdanielk1977 **
500d24e6b8Sdanielk1977 **   When a fsync() operation is performed, an operating system crash
510d24e6b8Sdanielk1977 **   may be simulated, in which case exit(-1) is called (the call to
520d24e6b8Sdanielk1977 **   xSync() never returns). Whether or not a crash is simulated,
530d24e6b8Sdanielk1977 **   the data associated with a subset of the WriteBuffer structures
540d24e6b8Sdanielk1977 **   stored in the write-list is written to the real underlying files
550d24e6b8Sdanielk1977 **   and the entries removed from the write-list. If a crash is simulated,
560d24e6b8Sdanielk1977 **   a subset of the buffers may be corrupted before the data is written.
570d24e6b8Sdanielk1977 **
580d24e6b8Sdanielk1977 **   The exact subset of the write-list written and/or corrupted is
590d24e6b8Sdanielk1977 **   determined by the simulated device characteristics and sector-size.
600d24e6b8Sdanielk1977 **
610d24e6b8Sdanielk1977 ** "Normal" mode:
620d24e6b8Sdanielk1977 **
630d24e6b8Sdanielk1977 **   Normal mode is used when the simulated device has none of the
640d24e6b8Sdanielk1977 **   SQLITE_IOCAP_XXX flags set.
650d24e6b8Sdanielk1977 **
660d24e6b8Sdanielk1977 **   In normal mode, if the fsync() is not a simulated crash, the
670d24e6b8Sdanielk1977 **   write-list is traversed from beginning to end. Each WriteBuffer
680d24e6b8Sdanielk1977 **   structure associated with the file handle used to call xSync()
690d24e6b8Sdanielk1977 **   is written to the real file and removed from the write-list.
700d24e6b8Sdanielk1977 **
710d24e6b8Sdanielk1977 **   If a crash is simulated, one of the following takes place for
720d24e6b8Sdanielk1977 **   each WriteBuffer in the write-list, regardless of which
730d24e6b8Sdanielk1977 **   file-handle it is associated with:
740d24e6b8Sdanielk1977 **
750d24e6b8Sdanielk1977 **     1. The buffer is correctly written to the file, just as if
760d24e6b8Sdanielk1977 **        a crash were not being simulated.
770d24e6b8Sdanielk1977 **
780d24e6b8Sdanielk1977 **     2. Nothing is done.
790d24e6b8Sdanielk1977 **
800d24e6b8Sdanielk1977 **     3. Garbage data is written to all sectors of the file that
810d24e6b8Sdanielk1977 **        overlap the region specified by the WriteBuffer. Or garbage
820d24e6b8Sdanielk1977 **        data is written to some contiguous section within the
830d24e6b8Sdanielk1977 **        overlapped sectors.
840d24e6b8Sdanielk1977 **
850d24e6b8Sdanielk1977 ** Device Characteristic flag handling:
860d24e6b8Sdanielk1977 **
870d24e6b8Sdanielk1977 **   If the IOCAP_ATOMIC flag is set, then option (3) above is
880d24e6b8Sdanielk1977 **   never selected.
890d24e6b8Sdanielk1977 **
900d24e6b8Sdanielk1977 **   If the IOCAP_ATOMIC512 flag is set, and the WriteBuffer represents
910d24e6b8Sdanielk1977 **   an aligned write() of an integer number of 512 byte regions, then
920d24e6b8Sdanielk1977 **   option (3) above is never selected. Instead, each 512 byte region
930d24e6b8Sdanielk1977 **   is either correctly written or left completely untouched. Similar
9448864df9Smistachkin **   logic governs the behavior if any of the other ATOMICXXX flags
950d24e6b8Sdanielk1977 **   is set.
960d24e6b8Sdanielk1977 **
970d24e6b8Sdanielk1977 **   If either the IOCAP_SAFEAPPEND or IOCAP_SEQUENTIAL flags are set
980d24e6b8Sdanielk1977 **   and a crash is being simulated, then an entry of the write-list is
990d24e6b8Sdanielk1977 **   selected at random. Everything in the list after the selected entry
1000d24e6b8Sdanielk1977 **   is discarded before processing begins.
1010d24e6b8Sdanielk1977 **
1020d24e6b8Sdanielk1977 **   If IOCAP_SEQUENTIAL is set and a crash is being simulated, option
1030d24e6b8Sdanielk1977 **   (1) is selected for all write-list entries except the last. If a
1040d24e6b8Sdanielk1977 **   crash is not being simulated, then all entries in the write-list
1050d24e6b8Sdanielk1977 **   that occur before at least one write() on the file-handle specified
1060d24e6b8Sdanielk1977 **   as part of the xSync() are written to their associated real files.
1070d24e6b8Sdanielk1977 **
1080d24e6b8Sdanielk1977 **   If IOCAP_SAFEAPPEND is set and the first byte written by the write()
1090d24e6b8Sdanielk1977 **   operation is one byte past the current end of the file, then option
1100d24e6b8Sdanielk1977 **   (1) is always selected.
1110d24e6b8Sdanielk1977 */
11262079060Sdanielk1977 
11362079060Sdanielk1977 /*
11462079060Sdanielk1977 ** Each write operation in the write-list is represented by an instance
11562079060Sdanielk1977 ** of the following structure.
11662079060Sdanielk1977 **
11762079060Sdanielk1977 ** If zBuf is 0, then this structure represents a call to xTruncate(),
11862079060Sdanielk1977 ** not xWrite(). In that case, iOffset is the size that the file is
11962079060Sdanielk1977 ** truncated to.
12062079060Sdanielk1977 */
1210d24e6b8Sdanielk1977 struct WriteBuffer {
1220d24e6b8Sdanielk1977   i64 iOffset;                 /* Byte offset of the start of this write() */
1230d24e6b8Sdanielk1977   int nBuf;                    /* Number of bytes written */
1240d24e6b8Sdanielk1977   u8 *zBuf;                    /* Pointer to copy of written data */
12562079060Sdanielk1977   CrashFile *pFile;            /* File this write() applies to */
12662079060Sdanielk1977 
12762079060Sdanielk1977   WriteBuffer *pNext;          /* Next in CrashGlobal.pWriteList */
1280d24e6b8Sdanielk1977 };
1290d24e6b8Sdanielk1977 
13062079060Sdanielk1977 struct CrashFile {
13162079060Sdanielk1977   const sqlite3_io_methods *pMethod;   /* Must be first */
13262079060Sdanielk1977   sqlite3_file *pRealFile;             /* Underlying "real" file handle */
1331e536953Sdanielk1977   char *zName;
134d6846d75Sdanielk1977   int flags;                           /* Flags the file was opened with */
135967a4a1cSdanielk1977 
136f1da17a3Sdanielk1977   /* Cache of the entire file. This is used to speed up OsRead() and
137f1da17a3Sdanielk1977   ** OsFileSize() calls. Although both could be done by traversing the
138f1da17a3Sdanielk1977   ** write-list, in practice this is impractically slow.
139f1da17a3Sdanielk1977   */
140967a4a1cSdanielk1977   u8 *zData;                           /* Buffer containing file contents */
141a703f50dSdrh   int nData;                           /* Size of buffer allocated at zData */
142a703f50dSdrh   i64 iSize;                           /* Size of file in bytes */
1439c06c953Sdrh };
1449c06c953Sdrh 
14562079060Sdanielk1977 struct CrashGlobal {
14662079060Sdanielk1977   WriteBuffer *pWriteList;     /* Head of write-list */
147967a4a1cSdanielk1977   WriteBuffer *pWriteListEnd;  /* End of write-list */
1489c06c953Sdrh 
14962079060Sdanielk1977   int iSectorSize;             /* Value of simulated sector size */
15062079060Sdanielk1977   int iDeviceCharacteristics;  /* Value of simulated device characteristics */
1519c06c953Sdrh 
15262079060Sdanielk1977   int iCrash;                  /* Crash on the iCrash'th call to xSync() */
15362079060Sdanielk1977   char zCrashFile[500];        /* Crash during an xSync() on this file */
15462079060Sdanielk1977 };
1559c06c953Sdrh 
156967a4a1cSdanielk1977 static CrashGlobal g = {0, 0, SQLITE_DEFAULT_SECTOR_SIZE, 0, 0};
1579c06c953Sdrh 
1589c06c953Sdrh /*
15966560adaSdrh ** Set this global variable to 1 to enable crash testing.
16066560adaSdrh */
161967a4a1cSdanielk1977 static int sqlite3CrashTestEnable = 0;
16266560adaSdrh 
crash_malloc(int nByte)163a98d7b47Sdanielk1977 static void *crash_malloc(int nByte){
164d742367aSmistachkin   return (void *)Tcl_AttemptAlloc((size_t)nByte);
165a98d7b47Sdanielk1977 }
crash_free(void * p)166a98d7b47Sdanielk1977 static void crash_free(void *p){
167a98d7b47Sdanielk1977   Tcl_Free(p);
168a98d7b47Sdanielk1977 }
crash_realloc(void * p,int n)169a98d7b47Sdanielk1977 static void *crash_realloc(void *p, int n){
170d742367aSmistachkin   return (void *)Tcl_AttemptRealloc(p, (size_t)n);
171a98d7b47Sdanielk1977 }
172a98d7b47Sdanielk1977 
17366560adaSdrh /*
174d6846d75Sdanielk1977 ** Wrapper around the sqlite3OsWrite() function that avoids writing to the
175d6846d75Sdanielk1977 ** 512 byte block begining at offset PENDING_BYTE.
176d6846d75Sdanielk1977 */
writeDbFile(CrashFile * p,u8 * z,i64 iAmt,i64 iOff)177d6846d75Sdanielk1977 static int writeDbFile(CrashFile *p, u8 *z, i64 iAmt, i64 iOff){
178ebffe41eSshaneh   int rc = SQLITE_OK;
179d6846d75Sdanielk1977   int iSkip = 0;
180d6846d75Sdanielk1977   if( (iAmt-iSkip)>0 ){
1817da5fcb0Sdrh     rc = sqlite3OsWrite(p->pRealFile, &z[iSkip], (int)(iAmt-iSkip), iOff+iSkip);
182d6846d75Sdanielk1977   }
183d6846d75Sdanielk1977   return rc;
184d6846d75Sdanielk1977 }
185d6846d75Sdanielk1977 
186d6846d75Sdanielk1977 /*
18762079060Sdanielk1977 ** Flush the write-list as if xSync() had been called on file handle
18862079060Sdanielk1977 ** pFile. If isCrash is true, simulate a crash.
1899c06c953Sdrh */
writeListSync(CrashFile * pFile,int isCrash)19062079060Sdanielk1977 static int writeListSync(CrashFile *pFile, int isCrash){
19162079060Sdanielk1977   int rc = SQLITE_OK;
19262079060Sdanielk1977   int iDc = g.iDeviceCharacteristics;
19366560adaSdrh 
19462079060Sdanielk1977   WriteBuffer *pWrite;
19562079060Sdanielk1977   WriteBuffer **ppPtr;
19666560adaSdrh 
197f55b8998Sdanielk1977   /* If this is not a crash simulation, set pFinal to point to the
198f55b8998Sdanielk1977   ** last element of the write-list that is associated with file handle
199f55b8998Sdanielk1977   ** pFile.
200f55b8998Sdanielk1977   **
201f55b8998Sdanielk1977   ** If this is a crash simulation, set pFinal to an arbitrarily selected
202f55b8998Sdanielk1977   ** element of the write-list.
20362079060Sdanielk1977   */
20462079060Sdanielk1977   WriteBuffer *pFinal = 0;
20562079060Sdanielk1977   if( !isCrash ){
20662079060Sdanielk1977     for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext){
20762079060Sdanielk1977       if( pWrite->pFile==pFile ){
20862079060Sdanielk1977         pFinal = pWrite;
2091a23593dSdrh       }
2101a23593dSdrh     }
211f55b8998Sdanielk1977   }else if( iDc&(SQLITE_IOCAP_SEQUENTIAL|SQLITE_IOCAP_SAFE_APPEND) ){
212f55b8998Sdanielk1977     int nWrite = 0;
213f55b8998Sdanielk1977     int iFinal;
214f55b8998Sdanielk1977     for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext) nWrite++;
2152fa1868fSdrh     sqlite3_randomness(sizeof(int), &iFinal);
216f55b8998Sdanielk1977     iFinal = ((iFinal<0)?-1*iFinal:iFinal)%nWrite;
217f55b8998Sdanielk1977     for(pWrite=g.pWriteList; iFinal>0; pWrite=pWrite->pNext) iFinal--;
218f55b8998Sdanielk1977     pFinal = pWrite;
21962079060Sdanielk1977   }
22066560adaSdrh 
221f8940aefSdanielk1977 #ifdef TRACE_CRASHTEST
222fe912510Sdan   if( pFile ){
223f8940aefSdanielk1977     printf("Sync %s (is %s crash)\n", pFile->zName, (isCrash?"a":"not a"));
224fe912510Sdan   }
225f8940aefSdanielk1977 #endif
226f8940aefSdanielk1977 
22762079060Sdanielk1977   ppPtr = &g.pWriteList;
22862079060Sdanielk1977   for(pWrite=*ppPtr; rc==SQLITE_OK && pWrite; pWrite=*ppPtr){
229967a4a1cSdanielk1977     sqlite3_file *pRealFile = pWrite->pFile->pRealFile;
23062079060Sdanielk1977 
23162079060Sdanielk1977     /* (eAction==1)      -> write block out normally,
23262079060Sdanielk1977     ** (eAction==2)      -> do nothing,
23362079060Sdanielk1977     ** (eAction==3)      -> trash sectors.
23462079060Sdanielk1977     */
23562079060Sdanielk1977     int eAction = 0;
23662079060Sdanielk1977     if( !isCrash ){
23762079060Sdanielk1977       eAction = 2;
23862079060Sdanielk1977       if( (pWrite->pFile==pFile || iDc&SQLITE_IOCAP_SEQUENTIAL) ){
23962079060Sdanielk1977         eAction = 1;
2401a23593dSdrh       }
24162079060Sdanielk1977     }else{
24262079060Sdanielk1977       char random;
2432fa1868fSdrh       sqlite3_randomness(1, &random);
24462079060Sdanielk1977 
245f55b8998Sdanielk1977       /* Do not select option 3 (sector trashing) if the IOCAP_ATOMIC flag
246f55b8998Sdanielk1977       ** is set or this is an OsTruncate(), not an Oswrite().
247f55b8998Sdanielk1977       */
248f55b8998Sdanielk1977       if( (iDc&SQLITE_IOCAP_ATOMIC) || (pWrite->zBuf==0) ){
24962079060Sdanielk1977         random &= 0x01;
25062079060Sdanielk1977       }
25162079060Sdanielk1977 
252f55b8998Sdanielk1977       /* If IOCAP_SEQUENTIAL is set and this is not the final entry
253f55b8998Sdanielk1977       ** in the truncated write-list, always select option 1 (write
254f55b8998Sdanielk1977       ** out correctly).
255f55b8998Sdanielk1977       */
256f55b8998Sdanielk1977       if( (iDc&SQLITE_IOCAP_SEQUENTIAL && pWrite!=pFinal) ){
257f55b8998Sdanielk1977         random = 0;
258f55b8998Sdanielk1977       }
259f55b8998Sdanielk1977 
260f55b8998Sdanielk1977       /* If IOCAP_SAFE_APPEND is set and this OsWrite() operation is
261f55b8998Sdanielk1977       ** an append (first byte of the written region is 1 byte past the
262f55b8998Sdanielk1977       ** current EOF), always select option 1 (write out correctly).
263f55b8998Sdanielk1977       */
264f55b8998Sdanielk1977       if( iDc&SQLITE_IOCAP_SAFE_APPEND && pWrite->zBuf ){
265f55b8998Sdanielk1977         i64 iSize;
266f55b8998Sdanielk1977         sqlite3OsFileSize(pRealFile, &iSize);
267f55b8998Sdanielk1977         if( iSize==pWrite->iOffset ){
268f55b8998Sdanielk1977           random = 0;
269f55b8998Sdanielk1977         }
270f55b8998Sdanielk1977       }
271f55b8998Sdanielk1977 
27262079060Sdanielk1977       if( (random&0x06)==0x06 ){
27362079060Sdanielk1977         eAction = 3;
27462079060Sdanielk1977       }else{
27562079060Sdanielk1977         eAction = ((random&0x01)?2:1);
27662079060Sdanielk1977       }
27762079060Sdanielk1977     }
27862079060Sdanielk1977 
27962079060Sdanielk1977     switch( eAction ){
28062079060Sdanielk1977       case 1: {               /* Write out correctly */
28162079060Sdanielk1977         if( pWrite->zBuf ){
282d6846d75Sdanielk1977           rc = writeDbFile(
283d6846d75Sdanielk1977               pWrite->pFile, pWrite->zBuf, pWrite->nBuf, pWrite->iOffset
28462079060Sdanielk1977           );
28562079060Sdanielk1977         }else{
286967a4a1cSdanielk1977           rc = sqlite3OsTruncate(pRealFile, pWrite->iOffset);
28762079060Sdanielk1977         }
28862079060Sdanielk1977         *ppPtr = pWrite->pNext;
289f8940aefSdanielk1977 #ifdef TRACE_CRASHTEST
290f8940aefSdanielk1977         if( isCrash ){
291f55b8998Sdanielk1977           printf("Writing %d bytes @ %d (%s)\n",
292f55b8998Sdanielk1977             pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName
293f55b8998Sdanielk1977           );
294f8940aefSdanielk1977         }
295f8940aefSdanielk1977 #endif
296a98d7b47Sdanielk1977         crash_free(pWrite);
29762079060Sdanielk1977         break;
29862079060Sdanielk1977       }
29962079060Sdanielk1977       case 2: {               /* Do nothing */
30062079060Sdanielk1977         ppPtr = &pWrite->pNext;
301f8940aefSdanielk1977 #ifdef TRACE_CRASHTEST
302f8940aefSdanielk1977         if( isCrash ){
303f55b8998Sdanielk1977           printf("Omiting %d bytes @ %d (%s)\n",
304f55b8998Sdanielk1977             pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName
305f55b8998Sdanielk1977           );
306f8940aefSdanielk1977         }
307f8940aefSdanielk1977 #endif
30862079060Sdanielk1977         break;
30962079060Sdanielk1977       }
31062079060Sdanielk1977       case 3: {               /* Trash sectors */
31162079060Sdanielk1977         u8 *zGarbage;
3127da5fcb0Sdrh         int iFirst = (int)(pWrite->iOffset/g.iSectorSize);
3137da5fcb0Sdrh         int iLast = (int)((pWrite->iOffset+pWrite->nBuf-1)/g.iSectorSize);
314967a4a1cSdanielk1977 
315967a4a1cSdanielk1977         assert(pWrite->zBuf);
31662079060Sdanielk1977 
317f8940aefSdanielk1977 #ifdef TRACE_CRASHTEST
318cb1b0a69Sdan         printf("Trashing %d sectors (%d bytes) @ %lld (sector %d) (%s)\n",
319cb1b0a69Sdan             1+iLast-iFirst, (1+iLast-iFirst)*g.iSectorSize,
320cb1b0a69Sdan             pWrite->iOffset, iFirst, pWrite->pFile->zName
321f55b8998Sdanielk1977         );
322f8940aefSdanielk1977 #endif
323f8940aefSdanielk1977 
324a98d7b47Sdanielk1977         zGarbage = crash_malloc(g.iSectorSize);
32562079060Sdanielk1977         if( zGarbage ){
32662079060Sdanielk1977           sqlite3_int64 i;
32762079060Sdanielk1977           for(i=iFirst; rc==SQLITE_OK && i<=iLast; i++){
3282fa1868fSdrh             sqlite3_randomness(g.iSectorSize, zGarbage);
329d6846d75Sdanielk1977             rc = writeDbFile(
330d6846d75Sdanielk1977               pWrite->pFile, zGarbage, g.iSectorSize, i*g.iSectorSize
33162079060Sdanielk1977             );
33262079060Sdanielk1977           }
333a98d7b47Sdanielk1977           crash_free(zGarbage);
33462079060Sdanielk1977         }else{
33562079060Sdanielk1977           rc = SQLITE_NOMEM;
33662079060Sdanielk1977         }
33762079060Sdanielk1977 
33862079060Sdanielk1977         ppPtr = &pWrite->pNext;
33962079060Sdanielk1977         break;
34062079060Sdanielk1977       }
34162079060Sdanielk1977 
34262079060Sdanielk1977       default:
34362079060Sdanielk1977         assert(!"Cannot happen");
34462079060Sdanielk1977     }
34562079060Sdanielk1977 
34662079060Sdanielk1977     if( pWrite==pFinal ) break;
34762079060Sdanielk1977   }
34862079060Sdanielk1977 
34962079060Sdanielk1977   if( rc==SQLITE_OK && isCrash ){
35062079060Sdanielk1977     exit(-1);
35162079060Sdanielk1977   }
35262079060Sdanielk1977 
353967a4a1cSdanielk1977   for(pWrite=g.pWriteList; pWrite && pWrite->pNext; pWrite=pWrite->pNext);
354967a4a1cSdanielk1977   g.pWriteListEnd = pWrite;
355967a4a1cSdanielk1977 
3561a23593dSdrh   return rc;
3579c06c953Sdrh }
3589c06c953Sdrh 
3599c06c953Sdrh /*
36062079060Sdanielk1977 ** Add an entry to the end of the write-list.
36118839217Sdrh */
writeListAppend(sqlite3_file * pFile,sqlite3_int64 iOffset,const u8 * zBuf,int nBuf)36262079060Sdanielk1977 static int writeListAppend(
36362079060Sdanielk1977   sqlite3_file *pFile,
36462079060Sdanielk1977   sqlite3_int64 iOffset,
36562079060Sdanielk1977   const u8 *zBuf,
36662079060Sdanielk1977   int nBuf
36762079060Sdanielk1977 ){
36862079060Sdanielk1977   WriteBuffer *pNew;
36962079060Sdanielk1977 
37062079060Sdanielk1977   assert((zBuf && nBuf) || (!nBuf && !zBuf));
37162079060Sdanielk1977 
372a98d7b47Sdanielk1977   pNew = (WriteBuffer *)crash_malloc(sizeof(WriteBuffer) + nBuf);
373a018dc77Sdrh   if( pNew==0 ){
374a018dc77Sdrh     fprintf(stderr, "out of memory in the crash simulator\n");
375a018dc77Sdrh   }
376a98d7b47Sdanielk1977   memset(pNew, 0, sizeof(WriteBuffer)+nBuf);
37762079060Sdanielk1977   pNew->iOffset = iOffset;
37862079060Sdanielk1977   pNew->nBuf = nBuf;
37962079060Sdanielk1977   pNew->pFile = (CrashFile *)pFile;
38062079060Sdanielk1977   if( zBuf ){
38162079060Sdanielk1977     pNew->zBuf = (u8 *)&pNew[1];
38262079060Sdanielk1977     memcpy(pNew->zBuf, zBuf, nBuf);
38362079060Sdanielk1977   }
38462079060Sdanielk1977 
38562079060Sdanielk1977   if( g.pWriteList ){
386967a4a1cSdanielk1977     assert(g.pWriteListEnd);
387967a4a1cSdanielk1977     g.pWriteListEnd->pNext = pNew;
38862079060Sdanielk1977   }else{
38962079060Sdanielk1977     g.pWriteList = pNew;
39062079060Sdanielk1977   }
391967a4a1cSdanielk1977   g.pWriteListEnd = pNew;
39262079060Sdanielk1977 
3931a23593dSdrh   return SQLITE_OK;
3941a23593dSdrh }
3951a23593dSdrh 
3961a23593dSdrh /*
39762079060Sdanielk1977 ** Close a crash-file.
3981a23593dSdrh */
cfClose(sqlite3_file * pFile)3991e536953Sdanielk1977 static int cfClose(sqlite3_file *pFile){
40062079060Sdanielk1977   CrashFile *pCrash = (CrashFile *)pFile;
40162079060Sdanielk1977   writeListSync(pCrash, 0);
402f1da17a3Sdanielk1977   sqlite3OsClose(pCrash->pRealFile);
40362079060Sdanielk1977   return SQLITE_OK;
4041a23593dSdrh }
4051a23593dSdrh 
4061a23593dSdrh /*
40762079060Sdanielk1977 ** Read data from a crash-file.
4081a23593dSdrh */
cfRead(sqlite3_file * pFile,void * zBuf,int iAmt,sqlite_int64 iOfst)4091e536953Sdanielk1977 static int cfRead(
4101e536953Sdanielk1977   sqlite3_file *pFile,
4111e536953Sdanielk1977   void *zBuf,
4121e536953Sdanielk1977   int iAmt,
4131e536953Sdanielk1977   sqlite_int64 iOfst
4141e536953Sdanielk1977 ){
41562079060Sdanielk1977   CrashFile *pCrash = (CrashFile *)pFile;
416999cd08aSdan   int nCopy = (int)MIN((i64)iAmt, (pCrash->iSize - iOfst));
417999cd08aSdan 
418999cd08aSdan   if( nCopy>0 ){
419999cd08aSdan     memcpy(zBuf, &pCrash->zData[iOfst], nCopy);
420999cd08aSdan   }
42162079060Sdanielk1977 
42262079060Sdanielk1977   /* Check the file-size to see if this is a short-read */
423999cd08aSdan   if( nCopy<iAmt ){
42462079060Sdanielk1977     return SQLITE_IOERR_SHORT_READ;
42562079060Sdanielk1977   }
42662079060Sdanielk1977 
427967a4a1cSdanielk1977   return SQLITE_OK;
42818839217Sdrh }
42918839217Sdrh 
43018839217Sdrh /*
43162079060Sdanielk1977 ** Write data to a crash-file.
432b472117cSdanielk1977 */
cfWrite(sqlite3_file * pFile,const void * zBuf,int iAmt,sqlite_int64 iOfst)4331e536953Sdanielk1977 static int cfWrite(
4341e536953Sdanielk1977   sqlite3_file *pFile,
4351e536953Sdanielk1977   const void *zBuf,
4361e536953Sdanielk1977   int iAmt,
4371e536953Sdanielk1977   sqlite_int64 iOfst
4381e536953Sdanielk1977 ){
439967a4a1cSdanielk1977   CrashFile *pCrash = (CrashFile *)pFile;
440967a4a1cSdanielk1977   if( iAmt+iOfst>pCrash->iSize ){
4417da5fcb0Sdrh     pCrash->iSize = (int)(iAmt+iOfst);
442967a4a1cSdanielk1977   }
443967a4a1cSdanielk1977   while( pCrash->iSize>pCrash->nData ){
4444a50aac5Sdrh     u8 *zNew;
445967a4a1cSdanielk1977     int nNew = (pCrash->nData*2) + 4096;
446a98d7b47Sdanielk1977     zNew = crash_realloc(pCrash->zData, nNew);
447967a4a1cSdanielk1977     if( !zNew ){
448967a4a1cSdanielk1977       return SQLITE_NOMEM;
449967a4a1cSdanielk1977     }
450967a4a1cSdanielk1977     memset(&zNew[pCrash->nData], 0, nNew-pCrash->nData);
451967a4a1cSdanielk1977     pCrash->nData = nNew;
452967a4a1cSdanielk1977     pCrash->zData = zNew;
453967a4a1cSdanielk1977   }
454967a4a1cSdanielk1977   memcpy(&pCrash->zData[iOfst], zBuf, iAmt);
45562079060Sdanielk1977   return writeListAppend(pFile, iOfst, zBuf, iAmt);
456b472117cSdanielk1977 }
457b472117cSdanielk1977 
458b472117cSdanielk1977 /*
45962079060Sdanielk1977 ** Truncate a crash-file.
460054889ecSdrh */
cfTruncate(sqlite3_file * pFile,sqlite_int64 size)4611e536953Sdanielk1977 static int cfTruncate(sqlite3_file *pFile, sqlite_int64 size){
462967a4a1cSdanielk1977   CrashFile *pCrash = (CrashFile *)pFile;
463967a4a1cSdanielk1977   assert(size>=0);
464967a4a1cSdanielk1977   if( pCrash->iSize>size ){
4657da5fcb0Sdrh     pCrash->iSize = (int)size;
466967a4a1cSdanielk1977   }
46762079060Sdanielk1977   return writeListAppend(pFile, size, 0, 0);
46862079060Sdanielk1977 }
46962079060Sdanielk1977 
47062079060Sdanielk1977 /*
47162079060Sdanielk1977 ** Sync a crash-file.
47262079060Sdanielk1977 */
cfSync(sqlite3_file * pFile,int flags)4731e536953Sdanielk1977 static int cfSync(sqlite3_file *pFile, int flags){
47462079060Sdanielk1977   CrashFile *pCrash = (CrashFile *)pFile;
47562079060Sdanielk1977   int isCrash = 0;
47662079060Sdanielk1977 
477967a4a1cSdanielk1977   const char *zName = pCrash->zName;
478967a4a1cSdanielk1977   const char *zCrashFile = g.zCrashFile;
47983cc1392Sdrh   int nName = (int)strlen(zName);
48083cc1392Sdrh   int nCrashFile = (int)strlen(zCrashFile);
481967a4a1cSdanielk1977 
482967a4a1cSdanielk1977   if( nCrashFile>0 && zCrashFile[nCrashFile-1]=='*' ){
483967a4a1cSdanielk1977     nCrashFile--;
484967a4a1cSdanielk1977     if( nName>nCrashFile ) nName = nCrashFile;
48562079060Sdanielk1977   }
486967a4a1cSdanielk1977 
487f8a78464Smistachkin #ifdef TRACE_CRASHTEST
488f8a78464Smistachkin   printf("cfSync(): nName = %d, nCrashFile = %d, zName = %s, zCrashFile = %s\n",
489f8a78464Smistachkin          nName, nCrashFile, zName, zCrashFile);
490f8a78464Smistachkin #endif
491f8a78464Smistachkin 
492967a4a1cSdanielk1977   if( nName==nCrashFile && 0==memcmp(zName, zCrashFile, nName) ){
493f8a78464Smistachkin #ifdef TRACE_CRASHTEST
494f8a78464Smistachkin     printf("cfSync(): name matched, g.iCrash = %d\n", g.iCrash);
495f8a78464Smistachkin #endif
496967a4a1cSdanielk1977     if( (--g.iCrash)==0 ) isCrash = 1;
49762079060Sdanielk1977   }
49862079060Sdanielk1977 
49962079060Sdanielk1977   return writeListSync(pCrash, isCrash);
50062079060Sdanielk1977 }
50162079060Sdanielk1977 
50262079060Sdanielk1977 /*
50362079060Sdanielk1977 ** Return the current file-size of the crash-file.
50462079060Sdanielk1977 */
cfFileSize(sqlite3_file * pFile,sqlite_int64 * pSize)5051e536953Sdanielk1977 static int cfFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
50662079060Sdanielk1977   CrashFile *pCrash = (CrashFile *)pFile;
507967a4a1cSdanielk1977   *pSize = (i64)pCrash->iSize;
50862079060Sdanielk1977   return SQLITE_OK;
50962079060Sdanielk1977 }
51062079060Sdanielk1977 
51162079060Sdanielk1977 /*
51262079060Sdanielk1977 ** Calls related to file-locks are passed on to the real file handle.
51362079060Sdanielk1977 */
cfLock(sqlite3_file * pFile,int eLock)5141e536953Sdanielk1977 static int cfLock(sqlite3_file *pFile, int eLock){
51562079060Sdanielk1977   return sqlite3OsLock(((CrashFile *)pFile)->pRealFile, eLock);
51662079060Sdanielk1977 }
cfUnlock(sqlite3_file * pFile,int eLock)5171e536953Sdanielk1977 static int cfUnlock(sqlite3_file *pFile, int eLock){
51862079060Sdanielk1977   return sqlite3OsUnlock(((CrashFile *)pFile)->pRealFile, eLock);
51962079060Sdanielk1977 }
cfCheckReservedLock(sqlite3_file * pFile,int * pResOut)520861f7456Sdanielk1977 static int cfCheckReservedLock(sqlite3_file *pFile, int *pResOut){
521861f7456Sdanielk1977   return sqlite3OsCheckReservedLock(((CrashFile *)pFile)->pRealFile, pResOut);
52262079060Sdanielk1977 }
cfFileControl(sqlite3_file * pFile,int op,void * pArg)523cc6bb3eaSdrh static int cfFileControl(sqlite3_file *pFile, int op, void *pArg){
52467e10d1fSdan   if( op==SQLITE_FCNTL_SIZE_HINT ){
52567e10d1fSdan     CrashFile *pCrash = (CrashFile *)pFile;
52667e10d1fSdan     i64 nByte = *(i64 *)pArg;
52767e10d1fSdan     if( nByte>pCrash->iSize ){
528422faae0Sdan       if( SQLITE_OK==writeListAppend(pFile, nByte, 0, 0) ){
5297da5fcb0Sdrh         pCrash->iSize = (int)nByte;
53067e10d1fSdan       }
53167e10d1fSdan     }
532422faae0Sdan     return SQLITE_OK;
533422faae0Sdan   }
534cc6bb3eaSdrh   return sqlite3OsFileControl(((CrashFile *)pFile)->pRealFile, op, pArg);
53562079060Sdanielk1977 }
53662079060Sdanielk1977 
53762079060Sdanielk1977 /*
53862079060Sdanielk1977 ** The xSectorSize() and xDeviceCharacteristics() functions return
53962079060Sdanielk1977 ** the global values configured by the [sqlite_crashparams] tcl
54062079060Sdanielk1977 *  interface.
54162079060Sdanielk1977 */
cfSectorSize(sqlite3_file * pFile)5421e536953Sdanielk1977 static int cfSectorSize(sqlite3_file *pFile){
54362079060Sdanielk1977   return g.iSectorSize;
54462079060Sdanielk1977 }
cfDeviceCharacteristics(sqlite3_file * pFile)5451e536953Sdanielk1977 static int cfDeviceCharacteristics(sqlite3_file *pFile){
54662079060Sdanielk1977   return g.iDeviceCharacteristics;
54762079060Sdanielk1977 }
54862079060Sdanielk1977 
549d9e5c4f6Sdrh /*
550d9e5c4f6Sdrh ** Pass-throughs for WAL support.
551d9e5c4f6Sdrh */
cfShmLock(sqlite3_file * pFile,int ofst,int n,int flags)55273b64e4dSdrh static int cfShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
553*7093a3beSdan   sqlite3_file *pReal = ((CrashFile*)pFile)->pRealFile;
554*7093a3beSdan   return pReal->pMethods->xShmLock(pReal, ofst, n, flags);
555d9e5c4f6Sdrh }
cfShmBarrier(sqlite3_file * pFile)556286a2884Sdrh static void cfShmBarrier(sqlite3_file *pFile){
557*7093a3beSdan   sqlite3_file *pReal = ((CrashFile*)pFile)->pRealFile;
558*7093a3beSdan   pReal->pMethods->xShmBarrier(pReal);
559286a2884Sdrh }
cfShmUnmap(sqlite3_file * pFile,int delFlag)560e11fedc5Sdrh static int cfShmUnmap(sqlite3_file *pFile, int delFlag){
561*7093a3beSdan   sqlite3_file *pReal = ((CrashFile*)pFile)->pRealFile;
562*7093a3beSdan   return pReal->pMethods->xShmUnmap(pReal, delFlag);
563d9e5c4f6Sdrh }
cfShmMap(sqlite3_file * pFile,int iRegion,int sz,int w,void volatile ** pp)56418801915Sdan static int cfShmMap(
565067f3165Sdan   sqlite3_file *pFile,            /* Handle open on database file */
56618801915Sdan   int iRegion,                    /* Region to retrieve */
56718801915Sdan   int sz,                         /* Size of regions */
568067f3165Sdan   int w,                          /* True to extend file if necessary */
569067f3165Sdan   void volatile **pp              /* OUT: Mapped memory */
570067f3165Sdan ){
571*7093a3beSdan   sqlite3_file *pReal = ((CrashFile*)pFile)->pRealFile;
572*7093a3beSdan   return pReal->pMethods->xShmMap(pReal, iRegion, sz, w, pp);
573067f3165Sdan }
574d9e5c4f6Sdrh 
57562079060Sdanielk1977 static const sqlite3_io_methods CrashFileVtab = {
576d9e5c4f6Sdrh   2,                            /* iVersion */
57762079060Sdanielk1977   cfClose,                      /* xClose */
57862079060Sdanielk1977   cfRead,                       /* xRead */
57962079060Sdanielk1977   cfWrite,                      /* xWrite */
58062079060Sdanielk1977   cfTruncate,                   /* xTruncate */
58162079060Sdanielk1977   cfSync,                       /* xSync */
58262079060Sdanielk1977   cfFileSize,                   /* xFileSize */
58362079060Sdanielk1977   cfLock,                       /* xLock */
58462079060Sdanielk1977   cfUnlock,                     /* xUnlock */
58562079060Sdanielk1977   cfCheckReservedLock,          /* xCheckReservedLock */
586cc6bb3eaSdrh   cfFileControl,                /* xFileControl */
58762079060Sdanielk1977   cfSectorSize,                 /* xSectorSize */
588d9e5c4f6Sdrh   cfDeviceCharacteristics,      /* xDeviceCharacteristics */
5896b017cc6Sdrh   cfShmMap,                     /* xShmMap */
590da9fe0c3Sdan   cfShmLock,                    /* xShmLock */
591286a2884Sdrh   cfShmBarrier,                 /* xShmBarrier */
592e11fedc5Sdrh   cfShmUnmap                    /* xShmUnmap */
593054889ecSdrh };
594054889ecSdrh 
595054889ecSdrh /*
596d677b3d6Sdrh ** Application data for the crash VFS
597d677b3d6Sdrh */
598d677b3d6Sdrh struct crashAppData {
599f1da17a3Sdanielk1977   sqlite3_vfs *pOrig;                   /* Wrapped vfs structure */
600d677b3d6Sdrh };
601d677b3d6Sdrh 
602d677b3d6Sdrh /*
6030e87b701Sdanielk1977 ** Open a crash-file file handle.
604967a4a1cSdanielk1977 **
605967a4a1cSdanielk1977 ** The caller will have allocated pVfs->szOsFile bytes of space
606967a4a1cSdanielk1977 ** at pFile. This file uses this space for the CrashFile structure
607967a4a1cSdanielk1977 ** and allocates space for the "real" file structure using
608967a4a1cSdanielk1977 ** sqlite3_malloc(). The assumption here is (pVfs->szOsFile) is
609967a4a1cSdanielk1977 ** equal or greater than sizeof(CrashFile).
610054889ecSdrh */
cfOpen(sqlite3_vfs * pCfVfs,const char * zName,sqlite3_file * pFile,int flags,int * pOutFlags)611f1da17a3Sdanielk1977 static int cfOpen(
612f55b8998Sdanielk1977   sqlite3_vfs *pCfVfs,
61362079060Sdanielk1977   const char *zName,
61462079060Sdanielk1977   sqlite3_file *pFile,
61562079060Sdanielk1977   int flags,
61662079060Sdanielk1977   int *pOutFlags
61762079060Sdanielk1977 ){
618f55b8998Sdanielk1977   sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
619967a4a1cSdanielk1977   int rc;
62062079060Sdanielk1977   CrashFile *pWrapper = (CrashFile *)pFile;
6214a50aac5Sdrh   sqlite3_file *pReal = (sqlite3_file*)&pWrapper[1];
622f1da17a3Sdanielk1977 
623967a4a1cSdanielk1977   memset(pWrapper, 0, sizeof(CrashFile));
624f1da17a3Sdanielk1977   rc = sqlite3OsOpen(pVfs, zName, pReal, flags, pOutFlags);
625f1da17a3Sdanielk1977 
626967a4a1cSdanielk1977   if( rc==SQLITE_OK ){
627967a4a1cSdanielk1977     i64 iSize;
62862079060Sdanielk1977     pWrapper->pMethod = &CrashFileVtab;
6291e536953Sdanielk1977     pWrapper->zName = (char *)zName;
630967a4a1cSdanielk1977     pWrapper->pRealFile = pReal;
631967a4a1cSdanielk1977     rc = sqlite3OsFileSize(pReal, &iSize);
632967a4a1cSdanielk1977     pWrapper->iSize = (int)iSize;
633d6846d75Sdanielk1977     pWrapper->flags = flags;
63462079060Sdanielk1977   }
635967a4a1cSdanielk1977   if( rc==SQLITE_OK ){
6362744938eSmistachkin     pWrapper->nData = (int)(4096 + pWrapper->iSize);
637a98d7b47Sdanielk1977     pWrapper->zData = crash_malloc(pWrapper->nData);
638967a4a1cSdanielk1977     if( pWrapper->zData ){
6392d42c2b2Sdanielk1977       /* os_unix.c contains an assert() that fails if the caller attempts
6402d42c2b2Sdanielk1977       ** to read data from the 512-byte locking region of a file opened
6412d42c2b2Sdanielk1977       ** with the SQLITE_OPEN_MAIN_DB flag. This region of a database file
6422d42c2b2Sdanielk1977       ** never contains valid data anyhow. So avoid doing such a read here.
643ce5c42beSdan       **
644ce5c42beSdan       ** UPDATE: It also contains an assert() verifying that each call
645ce5c42beSdan       ** to the xRead() method reads less than 128KB of data.
6462d42c2b2Sdanielk1977       */
647ce5c42beSdan       i64 iOff;
648ce5c42beSdan 
649967a4a1cSdanielk1977       memset(pWrapper->zData, 0, pWrapper->nData);
650ce5c42beSdan       for(iOff=0; iOff<pWrapper->iSize; iOff += 512){
6512744938eSmistachkin         int nRead = (int)(pWrapper->iSize - iOff);
652ce5c42beSdan         if( nRead>512 ) nRead = 512;
653ce5c42beSdan         rc = sqlite3OsRead(pReal, &pWrapper->zData[iOff], nRead, iOff);
6542d42c2b2Sdanielk1977       }
655967a4a1cSdanielk1977     }else{
656967a4a1cSdanielk1977       rc = SQLITE_NOMEM;
657967a4a1cSdanielk1977     }
658967a4a1cSdanielk1977   }
659967a4a1cSdanielk1977   if( rc!=SQLITE_OK && pWrapper->pMethod ){
660967a4a1cSdanielk1977     sqlite3OsClose(pFile);
661967a4a1cSdanielk1977   }
66262079060Sdanielk1977   return rc;
66362079060Sdanielk1977 }
66462079060Sdanielk1977 
cfDelete(sqlite3_vfs * pCfVfs,const char * zPath,int dirSync)665f55b8998Sdanielk1977 static int cfDelete(sqlite3_vfs *pCfVfs, const char *zPath, int dirSync){
666f55b8998Sdanielk1977   sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
667f55b8998Sdanielk1977   return pVfs->xDelete(pVfs, zPath, dirSync);
668f1da17a3Sdanielk1977 }
cfAccess(sqlite3_vfs * pCfVfs,const char * zPath,int flags,int * pResOut)669861f7456Sdanielk1977 static int cfAccess(
670861f7456Sdanielk1977   sqlite3_vfs *pCfVfs,
671861f7456Sdanielk1977   const char *zPath,
672861f7456Sdanielk1977   int flags,
673861f7456Sdanielk1977   int *pResOut
674861f7456Sdanielk1977 ){
675f55b8998Sdanielk1977   sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
676861f7456Sdanielk1977   return pVfs->xAccess(pVfs, zPath, flags, pResOut);
677f1da17a3Sdanielk1977 }
cfFullPathname(sqlite3_vfs * pCfVfs,const char * zPath,int nPathOut,char * zPathOut)678adfb9b05Sdanielk1977 static int cfFullPathname(
679adfb9b05Sdanielk1977   sqlite3_vfs *pCfVfs,
680adfb9b05Sdanielk1977   const char *zPath,
681adfb9b05Sdanielk1977   int nPathOut,
682adfb9b05Sdanielk1977   char *zPathOut
683adfb9b05Sdanielk1977 ){
684f55b8998Sdanielk1977   sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
685adfb9b05Sdanielk1977   return pVfs->xFullPathname(pVfs, zPath, nPathOut, zPathOut);
686f1da17a3Sdanielk1977 }
cfDlOpen(sqlite3_vfs * pCfVfs,const char * zPath)687f55b8998Sdanielk1977 static void *cfDlOpen(sqlite3_vfs *pCfVfs, const char *zPath){
688f55b8998Sdanielk1977   sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
689f55b8998Sdanielk1977   return pVfs->xDlOpen(pVfs, zPath);
690f1da17a3Sdanielk1977 }
cfDlError(sqlite3_vfs * pCfVfs,int nByte,char * zErrMsg)6910e87b701Sdanielk1977 static void cfDlError(sqlite3_vfs *pCfVfs, int nByte, char *zErrMsg){
6920e87b701Sdanielk1977   sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
6930e87b701Sdanielk1977   pVfs->xDlError(pVfs, nByte, zErrMsg);
6940e87b701Sdanielk1977 }
cfDlSym(sqlite3_vfs * pCfVfs,void * pH,const char * zSym)695ec1724e8Sdrh static void (*cfDlSym(sqlite3_vfs *pCfVfs, void *pH, const char *zSym))(void){
6960e87b701Sdanielk1977   sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
697ec1724e8Sdrh   return pVfs->xDlSym(pVfs, pH, zSym);
6980e87b701Sdanielk1977 }
cfDlClose(sqlite3_vfs * pCfVfs,void * pHandle)6990e87b701Sdanielk1977 static void cfDlClose(sqlite3_vfs *pCfVfs, void *pHandle){
7000e87b701Sdanielk1977   sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
7010e87b701Sdanielk1977   pVfs->xDlClose(pVfs, pHandle);
7020e87b701Sdanielk1977 }
cfRandomness(sqlite3_vfs * pCfVfs,int nByte,char * zBufOut)703f55b8998Sdanielk1977 static int cfRandomness(sqlite3_vfs *pCfVfs, int nByte, char *zBufOut){
704f55b8998Sdanielk1977   sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
705f55b8998Sdanielk1977   return pVfs->xRandomness(pVfs, nByte, zBufOut);
706f1da17a3Sdanielk1977 }
cfSleep(sqlite3_vfs * pCfVfs,int nMicro)707f55b8998Sdanielk1977 static int cfSleep(sqlite3_vfs *pCfVfs, int nMicro){
708f55b8998Sdanielk1977   sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
709f55b8998Sdanielk1977   return pVfs->xSleep(pVfs, nMicro);
710f1da17a3Sdanielk1977 }
cfCurrentTime(sqlite3_vfs * pCfVfs,double * pTimeOut)711f55b8998Sdanielk1977 static int cfCurrentTime(sqlite3_vfs *pCfVfs, double *pTimeOut){
712f55b8998Sdanielk1977   sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
713f55b8998Sdanielk1977   return pVfs->xCurrentTime(pVfs, pTimeOut);
714f1da17a3Sdanielk1977 }
cfGetLastError(sqlite3_vfs * pCfVfs,int n,char * z)71505accd22Sdan static int cfGetLastError(sqlite3_vfs *pCfVfs, int n, char *z){
71605accd22Sdan   sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
71705accd22Sdan   return pVfs->xGetLastError(pVfs, n, z);
71805accd22Sdan }
719f1da17a3Sdanielk1977 
processDevSymArgs(Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[],int * piDeviceChar,int * piSectorSize)7202ca0f863Sdanielk1977 static int processDevSymArgs(
7212ca0f863Sdanielk1977   Tcl_Interp *interp,
7222ca0f863Sdanielk1977   int objc,
7232ca0f863Sdanielk1977   Tcl_Obj *CONST objv[],
7242ca0f863Sdanielk1977   int *piDeviceChar,
7252ca0f863Sdanielk1977   int *piSectorSize
7262ca0f863Sdanielk1977 ){
7272ca0f863Sdanielk1977   struct DeviceFlag {
7282ca0f863Sdanielk1977     char *zName;
7292ca0f863Sdanielk1977     int iValue;
7302ca0f863Sdanielk1977   } aFlag[] = {
7312ca0f863Sdanielk1977     { "atomic",              SQLITE_IOCAP_ATOMIC                },
7322ca0f863Sdanielk1977     { "atomic512",           SQLITE_IOCAP_ATOMIC512             },
7332ca0f863Sdanielk1977     { "atomic1k",            SQLITE_IOCAP_ATOMIC1K              },
7342ca0f863Sdanielk1977     { "atomic2k",            SQLITE_IOCAP_ATOMIC2K              },
7352ca0f863Sdanielk1977     { "atomic4k",            SQLITE_IOCAP_ATOMIC4K              },
7362ca0f863Sdanielk1977     { "atomic8k",            SQLITE_IOCAP_ATOMIC8K              },
7372ca0f863Sdanielk1977     { "atomic16k",           SQLITE_IOCAP_ATOMIC16K             },
7382ca0f863Sdanielk1977     { "atomic32k",           SQLITE_IOCAP_ATOMIC32K             },
7392ca0f863Sdanielk1977     { "atomic64k",           SQLITE_IOCAP_ATOMIC64K             },
7402ca0f863Sdanielk1977     { "sequential",          SQLITE_IOCAP_SEQUENTIAL            },
7412ca0f863Sdanielk1977     { "safe_append",         SQLITE_IOCAP_SAFE_APPEND           },
742cb15f35fSdrh     { "powersafe_overwrite", SQLITE_IOCAP_POWERSAFE_OVERWRITE   },
74333447e77Sdan     { "batch-atomic",        SQLITE_IOCAP_BATCH_ATOMIC          },
7442ca0f863Sdanielk1977     { 0, 0 }
7452ca0f863Sdanielk1977   };
7462ca0f863Sdanielk1977 
7472ca0f863Sdanielk1977   int i;
7482ca0f863Sdanielk1977   int iDc = 0;
7492ca0f863Sdanielk1977   int iSectorSize = 0;
7502ca0f863Sdanielk1977   int setSectorsize = 0;
7512ca0f863Sdanielk1977   int setDeviceChar = 0;
7522ca0f863Sdanielk1977 
7532ca0f863Sdanielk1977   for(i=0; i<objc; i+=2){
7542ca0f863Sdanielk1977     int nOpt;
7552ca0f863Sdanielk1977     char *zOpt = Tcl_GetStringFromObj(objv[i], &nOpt);
7562ca0f863Sdanielk1977 
7572ca0f863Sdanielk1977     if( (nOpt>11 || nOpt<2 || strncmp("-sectorsize", zOpt, nOpt))
7582ca0f863Sdanielk1977      && (nOpt>16 || nOpt<2 || strncmp("-characteristics", zOpt, nOpt))
7592ca0f863Sdanielk1977     ){
7602ca0f863Sdanielk1977       Tcl_AppendResult(interp,
7612ca0f863Sdanielk1977         "Bad option: \"", zOpt,
7622ca0f863Sdanielk1977         "\" - must be \"-characteristics\" or \"-sectorsize\"", 0
7632ca0f863Sdanielk1977       );
7642ca0f863Sdanielk1977       return TCL_ERROR;
7652ca0f863Sdanielk1977     }
7662ca0f863Sdanielk1977     if( i==objc-1 ){
7672ca0f863Sdanielk1977       Tcl_AppendResult(interp, "Option requires an argument: \"", zOpt, "\"",0);
7682ca0f863Sdanielk1977       return TCL_ERROR;
7692ca0f863Sdanielk1977     }
7702ca0f863Sdanielk1977 
7712ca0f863Sdanielk1977     if( zOpt[1]=='s' ){
7722ca0f863Sdanielk1977       if( Tcl_GetIntFromObj(interp, objv[i+1], &iSectorSize) ){
7732ca0f863Sdanielk1977         return TCL_ERROR;
7742ca0f863Sdanielk1977       }
7752ca0f863Sdanielk1977       setSectorsize = 1;
7762ca0f863Sdanielk1977     }else{
7772ca0f863Sdanielk1977       int j;
7782ca0f863Sdanielk1977       Tcl_Obj **apObj;
7792ca0f863Sdanielk1977       int nObj;
7802ca0f863Sdanielk1977       if( Tcl_ListObjGetElements(interp, objv[i+1], &nObj, &apObj) ){
7812ca0f863Sdanielk1977         return TCL_ERROR;
7822ca0f863Sdanielk1977       }
7832ca0f863Sdanielk1977       for(j=0; j<nObj; j++){
7842ca0f863Sdanielk1977         int rc;
7852ca0f863Sdanielk1977         int iChoice;
7862ca0f863Sdanielk1977         Tcl_Obj *pFlag = Tcl_DuplicateObj(apObj[j]);
7872ca0f863Sdanielk1977         Tcl_IncrRefCount(pFlag);
7882ca0f863Sdanielk1977         Tcl_UtfToLower(Tcl_GetString(pFlag));
7892ca0f863Sdanielk1977 
7902ca0f863Sdanielk1977         rc = Tcl_GetIndexFromObjStruct(
7912ca0f863Sdanielk1977             interp, pFlag, aFlag, sizeof(aFlag[0]), "no such flag", 0, &iChoice
7922ca0f863Sdanielk1977         );
7932ca0f863Sdanielk1977         Tcl_DecrRefCount(pFlag);
7942ca0f863Sdanielk1977         if( rc ){
7952ca0f863Sdanielk1977           return TCL_ERROR;
7962ca0f863Sdanielk1977         }
7972ca0f863Sdanielk1977 
7982ca0f863Sdanielk1977         iDc |= aFlag[iChoice].iValue;
7992ca0f863Sdanielk1977       }
8002ca0f863Sdanielk1977       setDeviceChar = 1;
8012ca0f863Sdanielk1977     }
8022ca0f863Sdanielk1977   }
8032ca0f863Sdanielk1977 
8042ca0f863Sdanielk1977   if( setDeviceChar ){
8052ca0f863Sdanielk1977     *piDeviceChar = iDc;
8062ca0f863Sdanielk1977   }
8072ca0f863Sdanielk1977   if( setSectorsize ){
8082ca0f863Sdanielk1977     *piSectorSize = iSectorSize;
8092ca0f863Sdanielk1977   }
8102ca0f863Sdanielk1977 
8112ca0f863Sdanielk1977   return TCL_OK;
8122ca0f863Sdanielk1977 }
8132ca0f863Sdanielk1977 
814054889ecSdrh /*
815fe912510Sdan ** tclcmd:   sqlite3_crash_now
816fe912510Sdan **
817fe912510Sdan ** Simulate a crash immediately. This function does not return
818fe912510Sdan ** (writeListSync() calls exit(-1)).
819fe912510Sdan */
crashNowCmd(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])8207617e4a8Smistachkin static int SQLITE_TCLAPI crashNowCmd(
821fe912510Sdan   void * clientData,
822fe912510Sdan   Tcl_Interp *interp,
823fe912510Sdan   int objc,
824fe912510Sdan   Tcl_Obj *CONST objv[]
825fe912510Sdan ){
826fe912510Sdan   if( objc!=1 ){
827fe912510Sdan     Tcl_WrongNumArgs(interp, 1, objv, "");
828fe912510Sdan     return TCL_ERROR;
829fe912510Sdan   }
830fe912510Sdan   writeListSync(0, 1);
831fe912510Sdan   assert( 0 );
832fe912510Sdan   return TCL_OK;
833fe912510Sdan }
834fe912510Sdan 
835fe912510Sdan /*
836cb1b0a69Sdan ** tclcmd:   sqlite_crash_enable ENABLE ?DEFAULT?
837ca0c8971Sdanielk1977 **
838ca0c8971Sdanielk1977 ** Parameter ENABLE must be a boolean value. If true, then the "crash"
839ca0c8971Sdanielk1977 ** vfs is added to the system. If false, it is removed.
840ca0c8971Sdanielk1977 */
crashEnableCmd(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])8417617e4a8Smistachkin static int SQLITE_TCLAPI crashEnableCmd(
842ca0c8971Sdanielk1977   void * clientData,
843ca0c8971Sdanielk1977   Tcl_Interp *interp,
844ca0c8971Sdanielk1977   int objc,
845ca0c8971Sdanielk1977   Tcl_Obj *CONST objv[]
846ca0c8971Sdanielk1977 ){
847ca0c8971Sdanielk1977   int isEnable;
848cb1b0a69Sdan   int isDefault = 0;
849ca0c8971Sdanielk1977   static sqlite3_vfs crashVfs = {
8504c846bb1Sdrh     2,                  /* iVersion */
851ca0c8971Sdanielk1977     0,                  /* szOsFile */
852ca0c8971Sdanielk1977     0,                  /* mxPathname */
853ca0c8971Sdanielk1977     0,                  /* pNext */
854ca0c8971Sdanielk1977     "crash",            /* zName */
855ca0c8971Sdanielk1977     0,                  /* pAppData */
856ca0c8971Sdanielk1977 
857ca0c8971Sdanielk1977     cfOpen,               /* xOpen */
858ca0c8971Sdanielk1977     cfDelete,             /* xDelete */
859ca0c8971Sdanielk1977     cfAccess,             /* xAccess */
860ca0c8971Sdanielk1977     cfFullPathname,       /* xFullPathname */
861ca0c8971Sdanielk1977     cfDlOpen,             /* xDlOpen */
862ca0c8971Sdanielk1977     cfDlError,            /* xDlError */
863ca0c8971Sdanielk1977     cfDlSym,              /* xDlSym */
864ca0c8971Sdanielk1977     cfDlClose,            /* xDlClose */
865ca0c8971Sdanielk1977     cfRandomness,         /* xRandomness */
866ca0c8971Sdanielk1977     cfSleep,              /* xSleep */
867f2424c52Sdrh     cfCurrentTime,        /* xCurrentTime */
86805accd22Sdan     cfGetLastError,       /* xGetLastError */
8694c846bb1Sdrh     0,                    /* xCurrentTimeInt64 */
870ca0c8971Sdanielk1977   };
871ca0c8971Sdanielk1977 
872cb1b0a69Sdan   if( objc!=2 && objc!=3 ){
873cb1b0a69Sdan     Tcl_WrongNumArgs(interp, 1, objv, "ENABLE ?DEFAULT?");
874ca0c8971Sdanielk1977     return TCL_ERROR;
875ca0c8971Sdanielk1977   }
876ca0c8971Sdanielk1977 
877ca0c8971Sdanielk1977   if( Tcl_GetBooleanFromObj(interp, objv[1], &isEnable) ){
878ca0c8971Sdanielk1977     return TCL_ERROR;
879ca0c8971Sdanielk1977   }
880cb1b0a69Sdan   if( objc==3 && Tcl_GetBooleanFromObj(interp, objv[2], &isDefault) ){
881cb1b0a69Sdan     return TCL_ERROR;
882cb1b0a69Sdan   }
883ca0c8971Sdanielk1977 
884ca0c8971Sdanielk1977   if( (isEnable && crashVfs.pAppData) || (!isEnable && !crashVfs.pAppData) ){
885ca0c8971Sdanielk1977     return TCL_OK;
886ca0c8971Sdanielk1977   }
887ca0c8971Sdanielk1977 
888ca0c8971Sdanielk1977   if( crashVfs.pAppData==0 ){
889ca0c8971Sdanielk1977     sqlite3_vfs *pOriginalVfs = sqlite3_vfs_find(0);
890ca0c8971Sdanielk1977     crashVfs.mxPathname = pOriginalVfs->mxPathname;
891ca0c8971Sdanielk1977     crashVfs.pAppData = (void *)pOriginalVfs;
892ca0c8971Sdanielk1977     crashVfs.szOsFile = sizeof(CrashFile) + pOriginalVfs->szOsFile;
893cb1b0a69Sdan     sqlite3_vfs_register(&crashVfs, isDefault);
894ca0c8971Sdanielk1977   }else{
895ca0c8971Sdanielk1977     crashVfs.pAppData = 0;
896ca0c8971Sdanielk1977     sqlite3_vfs_unregister(&crashVfs);
897ca0c8971Sdanielk1977   }
898ca0c8971Sdanielk1977 
899ca0c8971Sdanielk1977   return TCL_OK;
900ca0c8971Sdanielk1977 }
901ca0c8971Sdanielk1977 
902ca0c8971Sdanielk1977 /*
90362079060Sdanielk1977 ** tclcmd:   sqlite_crashparams ?OPTIONS? DELAY CRASHFILE
9049c06c953Sdrh **
9059c06c953Sdrh ** This procedure implements a TCL command that enables crash testing
9069c06c953Sdrh ** in testfixture.  Once enabled, crash testing cannot be disabled.
90762079060Sdanielk1977 **
90862079060Sdanielk1977 ** Available options are "-characteristics" and "-sectorsize". Both require
90962079060Sdanielk1977 ** an argument. For -sectorsize, this is the simulated sector size in
91062079060Sdanielk1977 ** bytes. For -characteristics, the argument must be a list of io-capability
91162079060Sdanielk1977 ** flags to simulate. Valid flags are "atomic", "atomic512", "atomic1K",
91262079060Sdanielk1977 ** "atomic2K", "atomic4K", "atomic8K", "atomic16K", "atomic32K",
91362079060Sdanielk1977 ** "atomic64K", "sequential" and "safe_append".
91462079060Sdanielk1977 **
91562079060Sdanielk1977 ** Example:
91662079060Sdanielk1977 **
91762079060Sdanielk1977 **   sqlite_crashparams -sect 1024 -char {atomic sequential} ./test.db 1
91862079060Sdanielk1977 **
9199c06c953Sdrh */
crashParamsObjCmd(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])9207617e4a8Smistachkin static int SQLITE_TCLAPI crashParamsObjCmd(
9219c06c953Sdrh   void * clientData,
9229c06c953Sdrh   Tcl_Interp *interp,
9239c06c953Sdrh   int objc,
9249c06c953Sdrh   Tcl_Obj *CONST objv[]
9259c06c953Sdrh ){
92662079060Sdanielk1977   int iDelay;
92762079060Sdanielk1977   const char *zCrashFile;
928153c62c4Sdrh   int nCrashFile, iDc, iSectorSize;
929d677b3d6Sdrh 
930153c62c4Sdrh   iDc = -1;
931153c62c4Sdrh   iSectorSize = -1;
93262079060Sdanielk1977 
93362079060Sdanielk1977   if( objc<3 ){
93462079060Sdanielk1977     Tcl_WrongNumArgs(interp, 1, objv, "?OPTIONS? DELAY CRASHFILE");
935b4b47411Sdanielk1977     goto error;
9369c06c953Sdrh   }
93762079060Sdanielk1977 
938967a4a1cSdanielk1977   zCrashFile = Tcl_GetStringFromObj(objv[objc-1], &nCrashFile);
93962079060Sdanielk1977   if( nCrashFile>=sizeof(g.zCrashFile) ){
94062079060Sdanielk1977     Tcl_AppendResult(interp, "Filename is too long: \"", zCrashFile, "\"", 0);
941b4b47411Sdanielk1977     goto error;
9429c06c953Sdrh   }
94362079060Sdanielk1977   if( Tcl_GetIntFromObj(interp, objv[objc-2], &iDelay) ){
944b4b47411Sdanielk1977     goto error;
94559a33f98Sdanielk1977   }
94662079060Sdanielk1977 
9472ca0f863Sdanielk1977   if( processDevSymArgs(interp, objc-3, &objv[1], &iDc, &iSectorSize) ){
9482ca0f863Sdanielk1977     return TCL_ERROR;
94962079060Sdanielk1977   }
95062079060Sdanielk1977 
9512ca0f863Sdanielk1977   if( iDc>=0 ){
95262079060Sdanielk1977     g.iDeviceCharacteristics = iDc;
95362079060Sdanielk1977   }
9542ca0f863Sdanielk1977   if( iSectorSize>=0 ){
95562079060Sdanielk1977     g.iSectorSize = iSectorSize;
95662079060Sdanielk1977   }
9572ca0f863Sdanielk1977 
95862079060Sdanielk1977   g.iCrash = iDelay;
95962079060Sdanielk1977   memcpy(g.zCrashFile, zCrashFile, nCrashFile+1);
96066560adaSdrh   sqlite3CrashTestEnable = 1;
9619c06c953Sdrh   return TCL_OK;
962b4b47411Sdanielk1977 
963b4b47411Sdanielk1977 error:
964b4b47411Sdanielk1977   return TCL_ERROR;
9659c06c953Sdrh }
9669c06c953Sdrh 
devSymObjCmd(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])9677617e4a8Smistachkin static int SQLITE_TCLAPI devSymObjCmd(
9682ca0f863Sdanielk1977   void * clientData,
9692ca0f863Sdanielk1977   Tcl_Interp *interp,
9702ca0f863Sdanielk1977   int objc,
9712ca0f863Sdanielk1977   Tcl_Obj *CONST objv[]
9722ca0f863Sdanielk1977 ){
973bf260978Sdanielk1977   void devsym_register(int iDeviceChar, int iSectorSize);
9742ca0f863Sdanielk1977 
9752ca0f863Sdanielk1977   int iDc = -1;
9762ca0f863Sdanielk1977   int iSectorSize = -1;
977bf260978Sdanielk1977 
9782ca0f863Sdanielk1977   if( processDevSymArgs(interp, objc-1, &objv[1], &iDc, &iSectorSize) ){
9792ca0f863Sdanielk1977     return TCL_ERROR;
9802ca0f863Sdanielk1977   }
981bf260978Sdanielk1977   devsym_register(iDc, iSectorSize);
9822ca0f863Sdanielk1977 
9832ca0f863Sdanielk1977   return TCL_OK;
98433447e77Sdan }
98505accd22Sdan 
98633447e77Sdan /*
98733447e77Sdan ** tclcmd: sqlite3_crash_on_write N
98833447e77Sdan */
writeCrashObjCmd(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])98933447e77Sdan static int SQLITE_TCLAPI writeCrashObjCmd(
99033447e77Sdan   void * clientData,
99133447e77Sdan   Tcl_Interp *interp,
99233447e77Sdan   int objc,
99333447e77Sdan   Tcl_Obj *CONST objv[]
99433447e77Sdan ){
99533447e77Sdan   void devsym_crash_on_write(int);
99633447e77Sdan   int nWrite = 0;
99733447e77Sdan 
99833447e77Sdan   if( objc!=2 ){
99933447e77Sdan     Tcl_WrongNumArgs(interp, 1, objv, "NWRITE");
100033447e77Sdan     return TCL_ERROR;
100133447e77Sdan   }
100233447e77Sdan   if( Tcl_GetIntFromObj(interp, objv[1], &nWrite) ){
100333447e77Sdan     return TCL_ERROR;
100433447e77Sdan   }
100533447e77Sdan 
100633447e77Sdan   devsym_crash_on_write(nWrite);
100733447e77Sdan   return TCL_OK;
100805accd22Sdan }
100905accd22Sdan 
101005accd22Sdan /*
101105accd22Sdan ** tclcmd: unregister_devsim
101205accd22Sdan */
dsUnregisterObjCmd(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])10137617e4a8Smistachkin static int SQLITE_TCLAPI dsUnregisterObjCmd(
101405accd22Sdan   void * clientData,
101505accd22Sdan   Tcl_Interp *interp,
101605accd22Sdan   int objc,
101705accd22Sdan   Tcl_Obj *CONST objv[]
101805accd22Sdan ){
101905accd22Sdan   void devsym_unregister(void);
102005accd22Sdan 
102105accd22Sdan   if( objc!=1 ){
102205accd22Sdan     Tcl_WrongNumArgs(interp, 1, objv, "");
102305accd22Sdan     return TCL_ERROR;
102405accd22Sdan   }
102505accd22Sdan 
102605accd22Sdan   devsym_unregister();
102705accd22Sdan   return TCL_OK;
10282ca0f863Sdanielk1977 }
10292ca0f863Sdanielk1977 
1030a0fc7296Sdanielk1977 /*
1031a0fc7296Sdanielk1977 ** tclcmd: register_jt_vfs ?-default? PARENT-VFS
1032a0fc7296Sdanielk1977 */
jtObjCmd(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])10337617e4a8Smistachkin static int SQLITE_TCLAPI jtObjCmd(
1034a0fc7296Sdanielk1977   void * clientData,
1035a0fc7296Sdanielk1977   Tcl_Interp *interp,
1036a0fc7296Sdanielk1977   int objc,
1037a0fc7296Sdanielk1977   Tcl_Obj *CONST objv[]
1038a0fc7296Sdanielk1977 ){
1039a0fc7296Sdanielk1977   int jt_register(char *, int);
1040a0fc7296Sdanielk1977   char *zParent = 0;
1041a0fc7296Sdanielk1977 
1042a0fc7296Sdanielk1977   if( objc!=2 && objc!=3 ){
1043a0fc7296Sdanielk1977     Tcl_WrongNumArgs(interp, 1, objv, "?-default? PARENT-VFS");
1044a0fc7296Sdanielk1977     return TCL_ERROR;
1045a0fc7296Sdanielk1977   }
1046a0fc7296Sdanielk1977   zParent = Tcl_GetString(objv[1]);
1047a0fc7296Sdanielk1977   if( objc==3 ){
1048a0fc7296Sdanielk1977     if( strcmp(zParent, "-default") ){
1049a0fc7296Sdanielk1977       Tcl_AppendResult(interp,
1050a0fc7296Sdanielk1977           "bad option \"", zParent, "\": must be -default", 0
1051a0fc7296Sdanielk1977       );
1052a0fc7296Sdanielk1977       return TCL_ERROR;
1053a0fc7296Sdanielk1977     }
1054a0fc7296Sdanielk1977     zParent = Tcl_GetString(objv[2]);
1055a0fc7296Sdanielk1977   }
1056a0fc7296Sdanielk1977 
1057a0fc7296Sdanielk1977   if( !(*zParent) ){
1058a0fc7296Sdanielk1977     zParent = 0;
1059a0fc7296Sdanielk1977   }
1060a0fc7296Sdanielk1977   if( jt_register(zParent, objc==3) ){
1061a0fc7296Sdanielk1977     Tcl_AppendResult(interp, "Error in jt_register", 0);
1062a0fc7296Sdanielk1977     return TCL_ERROR;
1063a0fc7296Sdanielk1977   }
1064a0fc7296Sdanielk1977 
1065a0fc7296Sdanielk1977   return TCL_OK;
1066a0fc7296Sdanielk1977 }
1067a0fc7296Sdanielk1977 
1068a0fc7296Sdanielk1977 /*
1069a0fc7296Sdanielk1977 ** tclcmd: unregister_jt_vfs
1070a0fc7296Sdanielk1977 */
jtUnregisterObjCmd(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])10717617e4a8Smistachkin static int SQLITE_TCLAPI jtUnregisterObjCmd(
1072a0fc7296Sdanielk1977   void * clientData,
1073a0fc7296Sdanielk1977   Tcl_Interp *interp,
1074a0fc7296Sdanielk1977   int objc,
1075a0fc7296Sdanielk1977   Tcl_Obj *CONST objv[]
1076a0fc7296Sdanielk1977 ){
1077a0fc7296Sdanielk1977   void jt_unregister(void);
1078a0fc7296Sdanielk1977 
1079a0fc7296Sdanielk1977   if( objc!=1 ){
1080a0fc7296Sdanielk1977     Tcl_WrongNumArgs(interp, 1, objv, "");
1081a0fc7296Sdanielk1977     return TCL_ERROR;
1082a0fc7296Sdanielk1977   }
1083a0fc7296Sdanielk1977 
1084a0fc7296Sdanielk1977   jt_unregister();
1085a0fc7296Sdanielk1977   return TCL_OK;
1086a0fc7296Sdanielk1977 }
1087a0fc7296Sdanielk1977 
1088198bf391Sdrh #endif /* SQLITE_OMIT_DISKIO */
1089198bf391Sdrh 
10909c06c953Sdrh /*
10919c06c953Sdrh ** This procedure registers the TCL procedures defined in this file.
10929c06c953Sdrh */
Sqlitetest6_Init(Tcl_Interp * interp)10939c06c953Sdrh int Sqlitetest6_Init(Tcl_Interp *interp){
1094198bf391Sdrh #ifndef SQLITE_OMIT_DISKIO
1095ca0c8971Sdanielk1977   Tcl_CreateObjCommand(interp, "sqlite3_crash_enable", crashEnableCmd, 0, 0);
10969c06c953Sdrh   Tcl_CreateObjCommand(interp, "sqlite3_crashparams", crashParamsObjCmd, 0, 0);
1097fe912510Sdan   Tcl_CreateObjCommand(interp, "sqlite3_crash_now", crashNowCmd, 0, 0);
10982ca0f863Sdanielk1977   Tcl_CreateObjCommand(interp, "sqlite3_simulate_device", devSymObjCmd, 0, 0);
109933447e77Sdan   Tcl_CreateObjCommand(interp, "sqlite3_crash_on_write", writeCrashObjCmd,0,0);
110005accd22Sdan   Tcl_CreateObjCommand(interp, "unregister_devsim", dsUnregisterObjCmd, 0, 0);
1101a0fc7296Sdanielk1977   Tcl_CreateObjCommand(interp, "register_jt_vfs", jtObjCmd, 0, 0);
1102a0fc7296Sdanielk1977   Tcl_CreateObjCommand(interp, "unregister_jt_vfs", jtUnregisterObjCmd, 0, 0);
1103198bf391Sdrh #endif
11049c06c953Sdrh   return TCL_OK;
11059c06c953Sdrh }
11069c06c953Sdrh 
11079c06c953Sdrh #endif /* SQLITE_TEST */
1108