OLD | NEW |
| (Empty) |
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 behavior if any of the other ATOMICXXX flags | |
91 ** is set. | |
92 ** | |
93 ** If either the IOCAP_SAFEAPPEND or IOCAP_SEQUENTIAL flags are set | |
94 ** and a crash is being simulated, then an entry of the write-list is | |
95 ** selected at random. Everything in the list after the selected entry | |
96 ** is discarded before processing begins. | |
97 ** | |
98 ** If IOCAP_SEQUENTIAL is set and a crash is being simulated, option | |
99 ** (1) is selected for all write-list entries except the last. If a | |
100 ** crash is not being simulated, then all entries in the write-list | |
101 ** that occur before at least one write() on the file-handle specified | |
102 ** as part of the xSync() are written to their associated real files. | |
103 ** | |
104 ** If IOCAP_SAFEAPPEND is set and the first byte written by the write() | |
105 ** operation is one byte past the current end of the file, then option | |
106 ** (1) is always selected. | |
107 */ | |
108 | |
109 /* | |
110 ** Each write operation in the write-list is represented by an instance | |
111 ** of the following structure. | |
112 ** | |
113 ** If zBuf is 0, then this structure represents a call to xTruncate(), | |
114 ** not xWrite(). In that case, iOffset is the size that the file is | |
115 ** truncated to. | |
116 */ | |
117 struct WriteBuffer { | |
118 i64 iOffset; /* Byte offset of the start of this write() */ | |
119 int nBuf; /* Number of bytes written */ | |
120 u8 *zBuf; /* Pointer to copy of written data */ | |
121 CrashFile *pFile; /* File this write() applies to */ | |
122 | |
123 WriteBuffer *pNext; /* Next in CrashGlobal.pWriteList */ | |
124 }; | |
125 | |
126 struct CrashFile { | |
127 const sqlite3_io_methods *pMethod; /* Must be first */ | |
128 sqlite3_file *pRealFile; /* Underlying "real" file handle */ | |
129 char *zName; | |
130 int flags; /* Flags the file was opened with */ | |
131 | |
132 /* Cache of the entire file. This is used to speed up OsRead() and | |
133 ** OsFileSize() calls. Although both could be done by traversing the | |
134 ** write-list, in practice this is impractically slow. | |
135 */ | |
136 u8 *zData; /* Buffer containing file contents */ | |
137 int nData; /* Size of buffer allocated at zData */ | |
138 i64 iSize; /* Size of file in bytes */ | |
139 }; | |
140 | |
141 struct CrashGlobal { | |
142 WriteBuffer *pWriteList; /* Head of write-list */ | |
143 WriteBuffer *pWriteListEnd; /* End of write-list */ | |
144 | |
145 int iSectorSize; /* Value of simulated sector size */ | |
146 int iDeviceCharacteristics; /* Value of simulated device characteristics */ | |
147 | |
148 int iCrash; /* Crash on the iCrash'th call to xSync() */ | |
149 char zCrashFile[500]; /* Crash during an xSync() on this file */ | |
150 }; | |
151 | |
152 static CrashGlobal g = {0, 0, SQLITE_DEFAULT_SECTOR_SIZE, 0, 0}; | |
153 | |
154 /* | |
155 ** Set this global variable to 1 to enable crash testing. | |
156 */ | |
157 static int sqlite3CrashTestEnable = 0; | |
158 | |
159 static void *crash_malloc(int nByte){ | |
160 return (void *)Tcl_Alloc((size_t)nByte); | |
161 } | |
162 static void crash_free(void *p){ | |
163 Tcl_Free(p); | |
164 } | |
165 static void *crash_realloc(void *p, int n){ | |
166 return (void *)Tcl_Realloc(p, (size_t)n); | |
167 } | |
168 | |
169 /* | |
170 ** Wrapper around the sqlite3OsWrite() function that avoids writing to the | |
171 ** 512 byte block begining at offset PENDING_BYTE. | |
172 */ | |
173 static int writeDbFile(CrashFile *p, u8 *z, i64 iAmt, i64 iOff){ | |
174 int rc = SQLITE_OK; | |
175 int iSkip = 0; | |
176 if( (iAmt-iSkip)>0 ){ | |
177 rc = sqlite3OsWrite(p->pRealFile, &z[iSkip], (int)(iAmt-iSkip), iOff+iSkip); | |
178 } | |
179 return rc; | |
180 } | |
181 | |
182 /* | |
183 ** Flush the write-list as if xSync() had been called on file handle | |
184 ** pFile. If isCrash is true, simulate a crash. | |
185 */ | |
186 static int writeListSync(CrashFile *pFile, int isCrash){ | |
187 int rc = SQLITE_OK; | |
188 int iDc = g.iDeviceCharacteristics; | |
189 | |
190 WriteBuffer *pWrite; | |
191 WriteBuffer **ppPtr; | |
192 | |
193 /* If this is not a crash simulation, set pFinal to point to the | |
194 ** last element of the write-list that is associated with file handle | |
195 ** pFile. | |
196 ** | |
197 ** If this is a crash simulation, set pFinal to an arbitrarily selected | |
198 ** element of the write-list. | |
199 */ | |
200 WriteBuffer *pFinal = 0; | |
201 if( !isCrash ){ | |
202 for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext){ | |
203 if( pWrite->pFile==pFile ){ | |
204 pFinal = pWrite; | |
205 } | |
206 } | |
207 }else if( iDc&(SQLITE_IOCAP_SEQUENTIAL|SQLITE_IOCAP_SAFE_APPEND) ){ | |
208 int nWrite = 0; | |
209 int iFinal; | |
210 for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext) nWrite++; | |
211 sqlite3_randomness(sizeof(int), &iFinal); | |
212 iFinal = ((iFinal<0)?-1*iFinal:iFinal)%nWrite; | |
213 for(pWrite=g.pWriteList; iFinal>0; pWrite=pWrite->pNext) iFinal--; | |
214 pFinal = pWrite; | |
215 } | |
216 | |
217 #ifdef TRACE_CRASHTEST | |
218 printf("Sync %s (is %s crash)\n", pFile->zName, (isCrash?"a":"not a")); | |
219 #endif | |
220 | |
221 ppPtr = &g.pWriteList; | |
222 for(pWrite=*ppPtr; rc==SQLITE_OK && pWrite; pWrite=*ppPtr){ | |
223 sqlite3_file *pRealFile = pWrite->pFile->pRealFile; | |
224 | |
225 /* (eAction==1) -> write block out normally, | |
226 ** (eAction==2) -> do nothing, | |
227 ** (eAction==3) -> trash sectors. | |
228 */ | |
229 int eAction = 0; | |
230 if( !isCrash ){ | |
231 eAction = 2; | |
232 if( (pWrite->pFile==pFile || iDc&SQLITE_IOCAP_SEQUENTIAL) ){ | |
233 eAction = 1; | |
234 } | |
235 }else{ | |
236 char random; | |
237 sqlite3_randomness(1, &random); | |
238 | |
239 /* Do not select option 3 (sector trashing) if the IOCAP_ATOMIC flag | |
240 ** is set or this is an OsTruncate(), not an Oswrite(). | |
241 */ | |
242 if( (iDc&SQLITE_IOCAP_ATOMIC) || (pWrite->zBuf==0) ){ | |
243 random &= 0x01; | |
244 } | |
245 | |
246 /* If IOCAP_SEQUENTIAL is set and this is not the final entry | |
247 ** in the truncated write-list, always select option 1 (write | |
248 ** out correctly). | |
249 */ | |
250 if( (iDc&SQLITE_IOCAP_SEQUENTIAL && pWrite!=pFinal) ){ | |
251 random = 0; | |
252 } | |
253 | |
254 /* If IOCAP_SAFE_APPEND is set and this OsWrite() operation is | |
255 ** an append (first byte of the written region is 1 byte past the | |
256 ** current EOF), always select option 1 (write out correctly). | |
257 */ | |
258 if( iDc&SQLITE_IOCAP_SAFE_APPEND && pWrite->zBuf ){ | |
259 i64 iSize; | |
260 sqlite3OsFileSize(pRealFile, &iSize); | |
261 if( iSize==pWrite->iOffset ){ | |
262 random = 0; | |
263 } | |
264 } | |
265 | |
266 if( (random&0x06)==0x06 ){ | |
267 eAction = 3; | |
268 }else{ | |
269 eAction = ((random&0x01)?2:1); | |
270 } | |
271 } | |
272 | |
273 switch( eAction ){ | |
274 case 1: { /* Write out correctly */ | |
275 if( pWrite->zBuf ){ | |
276 rc = writeDbFile( | |
277 pWrite->pFile, pWrite->zBuf, pWrite->nBuf, pWrite->iOffset | |
278 ); | |
279 }else{ | |
280 rc = sqlite3OsTruncate(pRealFile, pWrite->iOffset); | |
281 } | |
282 *ppPtr = pWrite->pNext; | |
283 #ifdef TRACE_CRASHTEST | |
284 if( isCrash ){ | |
285 printf("Writing %d bytes @ %d (%s)\n", | |
286 pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName | |
287 ); | |
288 } | |
289 #endif | |
290 crash_free(pWrite); | |
291 break; | |
292 } | |
293 case 2: { /* Do nothing */ | |
294 ppPtr = &pWrite->pNext; | |
295 #ifdef TRACE_CRASHTEST | |
296 if( isCrash ){ | |
297 printf("Omiting %d bytes @ %d (%s)\n", | |
298 pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName | |
299 ); | |
300 } | |
301 #endif | |
302 break; | |
303 } | |
304 case 3: { /* Trash sectors */ | |
305 u8 *zGarbage; | |
306 int iFirst = (int)(pWrite->iOffset/g.iSectorSize); | |
307 int iLast = (int)((pWrite->iOffset+pWrite->nBuf-1)/g.iSectorSize); | |
308 | |
309 assert(pWrite->zBuf); | |
310 | |
311 #ifdef TRACE_CRASHTEST | |
312 printf("Trashing %d sectors @ %lld (sector %d) (%s)\n", | |
313 1+iLast-iFirst, pWrite->iOffset, iFirst, pWrite->pFile->zName | |
314 ); | |
315 #endif | |
316 | |
317 zGarbage = crash_malloc(g.iSectorSize); | |
318 if( zGarbage ){ | |
319 sqlite3_int64 i; | |
320 for(i=iFirst; rc==SQLITE_OK && i<=iLast; i++){ | |
321 sqlite3_randomness(g.iSectorSize, zGarbage); | |
322 rc = writeDbFile( | |
323 pWrite->pFile, zGarbage, g.iSectorSize, i*g.iSectorSize | |
324 ); | |
325 } | |
326 crash_free(zGarbage); | |
327 }else{ | |
328 rc = SQLITE_NOMEM; | |
329 } | |
330 | |
331 ppPtr = &pWrite->pNext; | |
332 break; | |
333 } | |
334 | |
335 default: | |
336 assert(!"Cannot happen"); | |
337 } | |
338 | |
339 if( pWrite==pFinal ) break; | |
340 } | |
341 | |
342 if( rc==SQLITE_OK && isCrash ){ | |
343 exit(-1); | |
344 } | |
345 | |
346 for(pWrite=g.pWriteList; pWrite && pWrite->pNext; pWrite=pWrite->pNext); | |
347 g.pWriteListEnd = pWrite; | |
348 | |
349 return rc; | |
350 } | |
351 | |
352 /* | |
353 ** Add an entry to the end of the write-list. | |
354 */ | |
355 static int writeListAppend( | |
356 sqlite3_file *pFile, | |
357 sqlite3_int64 iOffset, | |
358 const u8 *zBuf, | |
359 int nBuf | |
360 ){ | |
361 WriteBuffer *pNew; | |
362 | |
363 assert((zBuf && nBuf) || (!nBuf && !zBuf)); | |
364 | |
365 pNew = (WriteBuffer *)crash_malloc(sizeof(WriteBuffer) + nBuf); | |
366 if( pNew==0 ){ | |
367 fprintf(stderr, "out of memory in the crash simulator\n"); | |
368 } | |
369 memset(pNew, 0, sizeof(WriteBuffer)+nBuf); | |
370 pNew->iOffset = iOffset; | |
371 pNew->nBuf = nBuf; | |
372 pNew->pFile = (CrashFile *)pFile; | |
373 if( zBuf ){ | |
374 pNew->zBuf = (u8 *)&pNew[1]; | |
375 memcpy(pNew->zBuf, zBuf, nBuf); | |
376 } | |
377 | |
378 if( g.pWriteList ){ | |
379 assert(g.pWriteListEnd); | |
380 g.pWriteListEnd->pNext = pNew; | |
381 }else{ | |
382 g.pWriteList = pNew; | |
383 } | |
384 g.pWriteListEnd = pNew; | |
385 | |
386 return SQLITE_OK; | |
387 } | |
388 | |
389 /* | |
390 ** Close a crash-file. | |
391 */ | |
392 static int cfClose(sqlite3_file *pFile){ | |
393 CrashFile *pCrash = (CrashFile *)pFile; | |
394 writeListSync(pCrash, 0); | |
395 sqlite3OsClose(pCrash->pRealFile); | |
396 return SQLITE_OK; | |
397 } | |
398 | |
399 /* | |
400 ** Read data from a crash-file. | |
401 */ | |
402 static int cfRead( | |
403 sqlite3_file *pFile, | |
404 void *zBuf, | |
405 int iAmt, | |
406 sqlite_int64 iOfst | |
407 ){ | |
408 CrashFile *pCrash = (CrashFile *)pFile; | |
409 int nCopy = (int)MIN((i64)iAmt, (pCrash->iSize - iOfst)); | |
410 | |
411 if( nCopy>0 ){ | |
412 memcpy(zBuf, &pCrash->zData[iOfst], nCopy); | |
413 } | |
414 | |
415 /* Check the file-size to see if this is a short-read */ | |
416 if( nCopy<iAmt ){ | |
417 return SQLITE_IOERR_SHORT_READ; | |
418 } | |
419 | |
420 return SQLITE_OK; | |
421 } | |
422 | |
423 /* | |
424 ** Write data to a crash-file. | |
425 */ | |
426 static int cfWrite( | |
427 sqlite3_file *pFile, | |
428 const void *zBuf, | |
429 int iAmt, | |
430 sqlite_int64 iOfst | |
431 ){ | |
432 CrashFile *pCrash = (CrashFile *)pFile; | |
433 if( iAmt+iOfst>pCrash->iSize ){ | |
434 pCrash->iSize = (int)(iAmt+iOfst); | |
435 } | |
436 while( pCrash->iSize>pCrash->nData ){ | |
437 u8 *zNew; | |
438 int nNew = (pCrash->nData*2) + 4096; | |
439 zNew = crash_realloc(pCrash->zData, nNew); | |
440 if( !zNew ){ | |
441 return SQLITE_NOMEM; | |
442 } | |
443 memset(&zNew[pCrash->nData], 0, nNew-pCrash->nData); | |
444 pCrash->nData = nNew; | |
445 pCrash->zData = zNew; | |
446 } | |
447 memcpy(&pCrash->zData[iOfst], zBuf, iAmt); | |
448 return writeListAppend(pFile, iOfst, zBuf, iAmt); | |
449 } | |
450 | |
451 /* | |
452 ** Truncate a crash-file. | |
453 */ | |
454 static int cfTruncate(sqlite3_file *pFile, sqlite_int64 size){ | |
455 CrashFile *pCrash = (CrashFile *)pFile; | |
456 assert(size>=0); | |
457 if( pCrash->iSize>size ){ | |
458 pCrash->iSize = (int)size; | |
459 } | |
460 return writeListAppend(pFile, size, 0, 0); | |
461 } | |
462 | |
463 /* | |
464 ** Sync a crash-file. | |
465 */ | |
466 static int cfSync(sqlite3_file *pFile, int flags){ | |
467 CrashFile *pCrash = (CrashFile *)pFile; | |
468 int isCrash = 0; | |
469 | |
470 const char *zName = pCrash->zName; | |
471 const char *zCrashFile = g.zCrashFile; | |
472 int nName = (int)strlen(zName); | |
473 int nCrashFile = (int)strlen(zCrashFile); | |
474 | |
475 if( nCrashFile>0 && zCrashFile[nCrashFile-1]=='*' ){ | |
476 nCrashFile--; | |
477 if( nName>nCrashFile ) nName = nCrashFile; | |
478 } | |
479 | |
480 #ifdef TRACE_CRASHTEST | |
481 printf("cfSync(): nName = %d, nCrashFile = %d, zName = %s, zCrashFile = %s\n", | |
482 nName, nCrashFile, zName, zCrashFile); | |
483 #endif | |
484 | |
485 if( nName==nCrashFile && 0==memcmp(zName, zCrashFile, nName) ){ | |
486 #ifdef TRACE_CRASHTEST | |
487 printf("cfSync(): name matched, g.iCrash = %d\n", g.iCrash); | |
488 #endif | |
489 if( (--g.iCrash)==0 ) isCrash = 1; | |
490 } | |
491 | |
492 return writeListSync(pCrash, isCrash); | |
493 } | |
494 | |
495 /* | |
496 ** Return the current file-size of the crash-file. | |
497 */ | |
498 static int cfFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ | |
499 CrashFile *pCrash = (CrashFile *)pFile; | |
500 *pSize = (i64)pCrash->iSize; | |
501 return SQLITE_OK; | |
502 } | |
503 | |
504 /* | |
505 ** Calls related to file-locks are passed on to the real file handle. | |
506 */ | |
507 static int cfLock(sqlite3_file *pFile, int eLock){ | |
508 return sqlite3OsLock(((CrashFile *)pFile)->pRealFile, eLock); | |
509 } | |
510 static int cfUnlock(sqlite3_file *pFile, int eLock){ | |
511 return sqlite3OsUnlock(((CrashFile *)pFile)->pRealFile, eLock); | |
512 } | |
513 static int cfCheckReservedLock(sqlite3_file *pFile, int *pResOut){ | |
514 return sqlite3OsCheckReservedLock(((CrashFile *)pFile)->pRealFile, pResOut); | |
515 } | |
516 static int cfFileControl(sqlite3_file *pFile, int op, void *pArg){ | |
517 if( op==SQLITE_FCNTL_SIZE_HINT ){ | |
518 CrashFile *pCrash = (CrashFile *)pFile; | |
519 i64 nByte = *(i64 *)pArg; | |
520 if( nByte>pCrash->iSize ){ | |
521 if( SQLITE_OK==writeListAppend(pFile, nByte, 0, 0) ){ | |
522 pCrash->iSize = (int)nByte; | |
523 } | |
524 } | |
525 return SQLITE_OK; | |
526 } | |
527 return sqlite3OsFileControl(((CrashFile *)pFile)->pRealFile, op, pArg); | |
528 } | |
529 | |
530 /* | |
531 ** The xSectorSize() and xDeviceCharacteristics() functions return | |
532 ** the global values configured by the [sqlite_crashparams] tcl | |
533 * interface. | |
534 */ | |
535 static int cfSectorSize(sqlite3_file *pFile){ | |
536 return g.iSectorSize; | |
537 } | |
538 static int cfDeviceCharacteristics(sqlite3_file *pFile){ | |
539 return g.iDeviceCharacteristics; | |
540 } | |
541 | |
542 /* | |
543 ** Pass-throughs for WAL support. | |
544 */ | |
545 static int cfShmLock(sqlite3_file *pFile, int ofst, int n, int flags){ | |
546 return sqlite3OsShmLock(((CrashFile*)pFile)->pRealFile, ofst, n, flags); | |
547 } | |
548 static void cfShmBarrier(sqlite3_file *pFile){ | |
549 sqlite3OsShmBarrier(((CrashFile*)pFile)->pRealFile); | |
550 } | |
551 static int cfShmUnmap(sqlite3_file *pFile, int delFlag){ | |
552 return sqlite3OsShmUnmap(((CrashFile*)pFile)->pRealFile, delFlag); | |
553 } | |
554 static int cfShmMap( | |
555 sqlite3_file *pFile, /* Handle open on database file */ | |
556 int iRegion, /* Region to retrieve */ | |
557 int sz, /* Size of regions */ | |
558 int w, /* True to extend file if necessary */ | |
559 void volatile **pp /* OUT: Mapped memory */ | |
560 ){ | |
561 return sqlite3OsShmMap(((CrashFile*)pFile)->pRealFile, iRegion, sz, w, pp); | |
562 } | |
563 | |
564 static const sqlite3_io_methods CrashFileVtab = { | |
565 2, /* iVersion */ | |
566 cfClose, /* xClose */ | |
567 cfRead, /* xRead */ | |
568 cfWrite, /* xWrite */ | |
569 cfTruncate, /* xTruncate */ | |
570 cfSync, /* xSync */ | |
571 cfFileSize, /* xFileSize */ | |
572 cfLock, /* xLock */ | |
573 cfUnlock, /* xUnlock */ | |
574 cfCheckReservedLock, /* xCheckReservedLock */ | |
575 cfFileControl, /* xFileControl */ | |
576 cfSectorSize, /* xSectorSize */ | |
577 cfDeviceCharacteristics, /* xDeviceCharacteristics */ | |
578 cfShmMap, /* xShmMap */ | |
579 cfShmLock, /* xShmLock */ | |
580 cfShmBarrier, /* xShmBarrier */ | |
581 cfShmUnmap /* xShmUnmap */ | |
582 }; | |
583 | |
584 /* | |
585 ** Application data for the crash VFS | |
586 */ | |
587 struct crashAppData { | |
588 sqlite3_vfs *pOrig; /* Wrapped vfs structure */ | |
589 }; | |
590 | |
591 /* | |
592 ** Open a crash-file file handle. | |
593 ** | |
594 ** The caller will have allocated pVfs->szOsFile bytes of space | |
595 ** at pFile. This file uses this space for the CrashFile structure | |
596 ** and allocates space for the "real" file structure using | |
597 ** sqlite3_malloc(). The assumption here is (pVfs->szOsFile) is | |
598 ** equal or greater than sizeof(CrashFile). | |
599 */ | |
600 static int cfOpen( | |
601 sqlite3_vfs *pCfVfs, | |
602 const char *zName, | |
603 sqlite3_file *pFile, | |
604 int flags, | |
605 int *pOutFlags | |
606 ){ | |
607 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; | |
608 int rc; | |
609 CrashFile *pWrapper = (CrashFile *)pFile; | |
610 sqlite3_file *pReal = (sqlite3_file*)&pWrapper[1]; | |
611 | |
612 memset(pWrapper, 0, sizeof(CrashFile)); | |
613 rc = sqlite3OsOpen(pVfs, zName, pReal, flags, pOutFlags); | |
614 | |
615 if( rc==SQLITE_OK ){ | |
616 i64 iSize; | |
617 pWrapper->pMethod = &CrashFileVtab; | |
618 pWrapper->zName = (char *)zName; | |
619 pWrapper->pRealFile = pReal; | |
620 rc = sqlite3OsFileSize(pReal, &iSize); | |
621 pWrapper->iSize = (int)iSize; | |
622 pWrapper->flags = flags; | |
623 } | |
624 if( rc==SQLITE_OK ){ | |
625 pWrapper->nData = (int)(4096 + pWrapper->iSize); | |
626 pWrapper->zData = crash_malloc(pWrapper->nData); | |
627 if( pWrapper->zData ){ | |
628 /* os_unix.c contains an assert() that fails if the caller attempts | |
629 ** to read data from the 512-byte locking region of a file opened | |
630 ** with the SQLITE_OPEN_MAIN_DB flag. This region of a database file | |
631 ** never contains valid data anyhow. So avoid doing such a read here. | |
632 ** | |
633 ** UPDATE: It also contains an assert() verifying that each call | |
634 ** to the xRead() method reads less than 128KB of data. | |
635 */ | |
636 i64 iOff; | |
637 | |
638 memset(pWrapper->zData, 0, pWrapper->nData); | |
639 for(iOff=0; iOff<pWrapper->iSize; iOff += 512){ | |
640 int nRead = (int)(pWrapper->iSize - iOff); | |
641 if( nRead>512 ) nRead = 512; | |
642 rc = sqlite3OsRead(pReal, &pWrapper->zData[iOff], nRead, iOff); | |
643 } | |
644 }else{ | |
645 rc = SQLITE_NOMEM; | |
646 } | |
647 } | |
648 if( rc!=SQLITE_OK && pWrapper->pMethod ){ | |
649 sqlite3OsClose(pFile); | |
650 } | |
651 return rc; | |
652 } | |
653 | |
654 static int cfDelete(sqlite3_vfs *pCfVfs, const char *zPath, int dirSync){ | |
655 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; | |
656 return pVfs->xDelete(pVfs, zPath, dirSync); | |
657 } | |
658 static int cfAccess( | |
659 sqlite3_vfs *pCfVfs, | |
660 const char *zPath, | |
661 int flags, | |
662 int *pResOut | |
663 ){ | |
664 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; | |
665 return pVfs->xAccess(pVfs, zPath, flags, pResOut); | |
666 } | |
667 static int cfFullPathname( | |
668 sqlite3_vfs *pCfVfs, | |
669 const char *zPath, | |
670 int nPathOut, | |
671 char *zPathOut | |
672 ){ | |
673 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; | |
674 return pVfs->xFullPathname(pVfs, zPath, nPathOut, zPathOut); | |
675 } | |
676 static void *cfDlOpen(sqlite3_vfs *pCfVfs, const char *zPath){ | |
677 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; | |
678 return pVfs->xDlOpen(pVfs, zPath); | |
679 } | |
680 static void cfDlError(sqlite3_vfs *pCfVfs, int nByte, char *zErrMsg){ | |
681 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; | |
682 pVfs->xDlError(pVfs, nByte, zErrMsg); | |
683 } | |
684 static void (*cfDlSym(sqlite3_vfs *pCfVfs, void *pH, const char *zSym))(void){ | |
685 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; | |
686 return pVfs->xDlSym(pVfs, pH, zSym); | |
687 } | |
688 static void cfDlClose(sqlite3_vfs *pCfVfs, void *pHandle){ | |
689 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; | |
690 pVfs->xDlClose(pVfs, pHandle); | |
691 } | |
692 static int cfRandomness(sqlite3_vfs *pCfVfs, int nByte, char *zBufOut){ | |
693 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; | |
694 return pVfs->xRandomness(pVfs, nByte, zBufOut); | |
695 } | |
696 static int cfSleep(sqlite3_vfs *pCfVfs, int nMicro){ | |
697 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; | |
698 return pVfs->xSleep(pVfs, nMicro); | |
699 } | |
700 static int cfCurrentTime(sqlite3_vfs *pCfVfs, double *pTimeOut){ | |
701 sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; | |
702 return pVfs->xCurrentTime(pVfs, pTimeOut); | |
703 } | |
704 | |
705 static int processDevSymArgs( | |
706 Tcl_Interp *interp, | |
707 int objc, | |
708 Tcl_Obj *CONST objv[], | |
709 int *piDeviceChar, | |
710 int *piSectorSize | |
711 ){ | |
712 struct DeviceFlag { | |
713 char *zName; | |
714 int iValue; | |
715 } aFlag[] = { | |
716 { "atomic", SQLITE_IOCAP_ATOMIC }, | |
717 { "atomic512", SQLITE_IOCAP_ATOMIC512 }, | |
718 { "atomic1k", SQLITE_IOCAP_ATOMIC1K }, | |
719 { "atomic2k", SQLITE_IOCAP_ATOMIC2K }, | |
720 { "atomic4k", SQLITE_IOCAP_ATOMIC4K }, | |
721 { "atomic8k", SQLITE_IOCAP_ATOMIC8K }, | |
722 { "atomic16k", SQLITE_IOCAP_ATOMIC16K }, | |
723 { "atomic32k", SQLITE_IOCAP_ATOMIC32K }, | |
724 { "atomic64k", SQLITE_IOCAP_ATOMIC64K }, | |
725 { "sequential", SQLITE_IOCAP_SEQUENTIAL }, | |
726 { "safe_append", SQLITE_IOCAP_SAFE_APPEND }, | |
727 { "powersafe_overwrite", SQLITE_IOCAP_POWERSAFE_OVERWRITE }, | |
728 { 0, 0 } | |
729 }; | |
730 | |
731 int i; | |
732 int iDc = 0; | |
733 int iSectorSize = 0; | |
734 int setSectorsize = 0; | |
735 int setDeviceChar = 0; | |
736 | |
737 for(i=0; i<objc; i+=2){ | |
738 int nOpt; | |
739 char *zOpt = Tcl_GetStringFromObj(objv[i], &nOpt); | |
740 | |
741 if( (nOpt>11 || nOpt<2 || strncmp("-sectorsize", zOpt, nOpt)) | |
742 && (nOpt>16 || nOpt<2 || strncmp("-characteristics", zOpt, nOpt)) | |
743 ){ | |
744 Tcl_AppendResult(interp, | |
745 "Bad option: \"", zOpt, | |
746 "\" - must be \"-characteristics\" or \"-sectorsize\"", 0 | |
747 ); | |
748 return TCL_ERROR; | |
749 } | |
750 if( i==objc-1 ){ | |
751 Tcl_AppendResult(interp, "Option requires an argument: \"", zOpt, "\"",0); | |
752 return TCL_ERROR; | |
753 } | |
754 | |
755 if( zOpt[1]=='s' ){ | |
756 if( Tcl_GetIntFromObj(interp, objv[i+1], &iSectorSize) ){ | |
757 return TCL_ERROR; | |
758 } | |
759 setSectorsize = 1; | |
760 }else{ | |
761 int j; | |
762 Tcl_Obj **apObj; | |
763 int nObj; | |
764 if( Tcl_ListObjGetElements(interp, objv[i+1], &nObj, &apObj) ){ | |
765 return TCL_ERROR; | |
766 } | |
767 for(j=0; j<nObj; j++){ | |
768 int rc; | |
769 int iChoice; | |
770 Tcl_Obj *pFlag = Tcl_DuplicateObj(apObj[j]); | |
771 Tcl_IncrRefCount(pFlag); | |
772 Tcl_UtfToLower(Tcl_GetString(pFlag)); | |
773 | |
774 rc = Tcl_GetIndexFromObjStruct( | |
775 interp, pFlag, aFlag, sizeof(aFlag[0]), "no such flag", 0, &iChoice | |
776 ); | |
777 Tcl_DecrRefCount(pFlag); | |
778 if( rc ){ | |
779 return TCL_ERROR; | |
780 } | |
781 | |
782 iDc |= aFlag[iChoice].iValue; | |
783 } | |
784 setDeviceChar = 1; | |
785 } | |
786 } | |
787 | |
788 if( setDeviceChar ){ | |
789 *piDeviceChar = iDc; | |
790 } | |
791 if( setSectorsize ){ | |
792 *piSectorSize = iSectorSize; | |
793 } | |
794 | |
795 return TCL_OK; | |
796 } | |
797 | |
798 /* | |
799 ** tclcmd: sqlite_crash_enable ENABLE | |
800 ** | |
801 ** Parameter ENABLE must be a boolean value. If true, then the "crash" | |
802 ** vfs is added to the system. If false, it is removed. | |
803 */ | |
804 static int crashEnableCmd( | |
805 void * clientData, | |
806 Tcl_Interp *interp, | |
807 int objc, | |
808 Tcl_Obj *CONST objv[] | |
809 ){ | |
810 int isEnable; | |
811 static sqlite3_vfs crashVfs = { | |
812 2, /* iVersion */ | |
813 0, /* szOsFile */ | |
814 0, /* mxPathname */ | |
815 0, /* pNext */ | |
816 "crash", /* zName */ | |
817 0, /* pAppData */ | |
818 | |
819 cfOpen, /* xOpen */ | |
820 cfDelete, /* xDelete */ | |
821 cfAccess, /* xAccess */ | |
822 cfFullPathname, /* xFullPathname */ | |
823 cfDlOpen, /* xDlOpen */ | |
824 cfDlError, /* xDlError */ | |
825 cfDlSym, /* xDlSym */ | |
826 cfDlClose, /* xDlClose */ | |
827 cfRandomness, /* xRandomness */ | |
828 cfSleep, /* xSleep */ | |
829 cfCurrentTime, /* xCurrentTime */ | |
830 0, /* xGetlastError */ | |
831 0, /* xCurrentTimeInt64 */ | |
832 }; | |
833 | |
834 if( objc!=2 ){ | |
835 Tcl_WrongNumArgs(interp, 1, objv, "ENABLE"); | |
836 return TCL_ERROR; | |
837 } | |
838 | |
839 if( Tcl_GetBooleanFromObj(interp, objv[1], &isEnable) ){ | |
840 return TCL_ERROR; | |
841 } | |
842 | |
843 if( (isEnable && crashVfs.pAppData) || (!isEnable && !crashVfs.pAppData) ){ | |
844 return TCL_OK; | |
845 } | |
846 | |
847 if( crashVfs.pAppData==0 ){ | |
848 sqlite3_vfs *pOriginalVfs = sqlite3_vfs_find(0); | |
849 crashVfs.mxPathname = pOriginalVfs->mxPathname; | |
850 crashVfs.pAppData = (void *)pOriginalVfs; | |
851 crashVfs.szOsFile = sizeof(CrashFile) + pOriginalVfs->szOsFile; | |
852 sqlite3_vfs_register(&crashVfs, 0); | |
853 }else{ | |
854 crashVfs.pAppData = 0; | |
855 sqlite3_vfs_unregister(&crashVfs); | |
856 } | |
857 | |
858 return TCL_OK; | |
859 } | |
860 | |
861 /* | |
862 ** tclcmd: sqlite_crashparams ?OPTIONS? DELAY CRASHFILE | |
863 ** | |
864 ** This procedure implements a TCL command that enables crash testing | |
865 ** in testfixture. Once enabled, crash testing cannot be disabled. | |
866 ** | |
867 ** Available options are "-characteristics" and "-sectorsize". Both require | |
868 ** an argument. For -sectorsize, this is the simulated sector size in | |
869 ** bytes. For -characteristics, the argument must be a list of io-capability | |
870 ** flags to simulate. Valid flags are "atomic", "atomic512", "atomic1K", | |
871 ** "atomic2K", "atomic4K", "atomic8K", "atomic16K", "atomic32K", | |
872 ** "atomic64K", "sequential" and "safe_append". | |
873 ** | |
874 ** Example: | |
875 ** | |
876 ** sqlite_crashparams -sect 1024 -char {atomic sequential} ./test.db 1 | |
877 ** | |
878 */ | |
879 static int crashParamsObjCmd( | |
880 void * clientData, | |
881 Tcl_Interp *interp, | |
882 int objc, | |
883 Tcl_Obj *CONST objv[] | |
884 ){ | |
885 int iDelay; | |
886 const char *zCrashFile; | |
887 int nCrashFile, iDc, iSectorSize; | |
888 | |
889 iDc = -1; | |
890 iSectorSize = -1; | |
891 | |
892 if( objc<3 ){ | |
893 Tcl_WrongNumArgs(interp, 1, objv, "?OPTIONS? DELAY CRASHFILE"); | |
894 goto error; | |
895 } | |
896 | |
897 zCrashFile = Tcl_GetStringFromObj(objv[objc-1], &nCrashFile); | |
898 if( nCrashFile>=sizeof(g.zCrashFile) ){ | |
899 Tcl_AppendResult(interp, "Filename is too long: \"", zCrashFile, "\"", 0); | |
900 goto error; | |
901 } | |
902 if( Tcl_GetIntFromObj(interp, objv[objc-2], &iDelay) ){ | |
903 goto error; | |
904 } | |
905 | |
906 if( processDevSymArgs(interp, objc-3, &objv[1], &iDc, &iSectorSize) ){ | |
907 return TCL_ERROR; | |
908 } | |
909 | |
910 if( iDc>=0 ){ | |
911 g.iDeviceCharacteristics = iDc; | |
912 } | |
913 if( iSectorSize>=0 ){ | |
914 g.iSectorSize = iSectorSize; | |
915 } | |
916 | |
917 g.iCrash = iDelay; | |
918 memcpy(g.zCrashFile, zCrashFile, nCrashFile+1); | |
919 sqlite3CrashTestEnable = 1; | |
920 return TCL_OK; | |
921 | |
922 error: | |
923 return TCL_ERROR; | |
924 } | |
925 | |
926 static int devSymObjCmd( | |
927 void * clientData, | |
928 Tcl_Interp *interp, | |
929 int objc, | |
930 Tcl_Obj *CONST objv[] | |
931 ){ | |
932 void devsym_register(int iDeviceChar, int iSectorSize); | |
933 | |
934 int iDc = -1; | |
935 int iSectorSize = -1; | |
936 | |
937 if( processDevSymArgs(interp, objc-1, &objv[1], &iDc, &iSectorSize) ){ | |
938 return TCL_ERROR; | |
939 } | |
940 devsym_register(iDc, iSectorSize); | |
941 | |
942 return TCL_OK; | |
943 } | |
944 | |
945 /* | |
946 ** tclcmd: register_jt_vfs ?-default? PARENT-VFS | |
947 */ | |
948 static int jtObjCmd( | |
949 void * clientData, | |
950 Tcl_Interp *interp, | |
951 int objc, | |
952 Tcl_Obj *CONST objv[] | |
953 ){ | |
954 int jt_register(char *, int); | |
955 char *zParent = 0; | |
956 | |
957 if( objc!=2 && objc!=3 ){ | |
958 Tcl_WrongNumArgs(interp, 1, objv, "?-default? PARENT-VFS"); | |
959 return TCL_ERROR; | |
960 } | |
961 zParent = Tcl_GetString(objv[1]); | |
962 if( objc==3 ){ | |
963 if( strcmp(zParent, "-default") ){ | |
964 Tcl_AppendResult(interp, | |
965 "bad option \"", zParent, "\": must be -default", 0 | |
966 ); | |
967 return TCL_ERROR; | |
968 } | |
969 zParent = Tcl_GetString(objv[2]); | |
970 } | |
971 | |
972 if( !(*zParent) ){ | |
973 zParent = 0; | |
974 } | |
975 if( jt_register(zParent, objc==3) ){ | |
976 Tcl_AppendResult(interp, "Error in jt_register", 0); | |
977 return TCL_ERROR; | |
978 } | |
979 | |
980 return TCL_OK; | |
981 } | |
982 | |
983 /* | |
984 ** tclcmd: unregister_jt_vfs | |
985 */ | |
986 static int jtUnregisterObjCmd( | |
987 void * clientData, | |
988 Tcl_Interp *interp, | |
989 int objc, | |
990 Tcl_Obj *CONST objv[] | |
991 ){ | |
992 void jt_unregister(void); | |
993 | |
994 if( objc!=1 ){ | |
995 Tcl_WrongNumArgs(interp, 1, objv, ""); | |
996 return TCL_ERROR; | |
997 } | |
998 | |
999 jt_unregister(); | |
1000 return TCL_OK; | |
1001 } | |
1002 | |
1003 #endif /* SQLITE_OMIT_DISKIO */ | |
1004 | |
1005 /* | |
1006 ** This procedure registers the TCL procedures defined in this file. | |
1007 */ | |
1008 int Sqlitetest6_Init(Tcl_Interp *interp){ | |
1009 #ifndef SQLITE_OMIT_DISKIO | |
1010 Tcl_CreateObjCommand(interp, "sqlite3_crash_enable", crashEnableCmd, 0, 0); | |
1011 Tcl_CreateObjCommand(interp, "sqlite3_crashparams", crashParamsObjCmd, 0, 0); | |
1012 Tcl_CreateObjCommand(interp, "sqlite3_simulate_device", devSymObjCmd, 0, 0); | |
1013 Tcl_CreateObjCommand(interp, "register_jt_vfs", jtObjCmd, 0, 0); | |
1014 Tcl_CreateObjCommand(interp, "unregister_jt_vfs", jtUnregisterObjCmd, 0, 0); | |
1015 #endif | |
1016 return TCL_OK; | |
1017 } | |
1018 | |
1019 #endif /* SQLITE_TEST */ | |
OLD | NEW |