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