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