xref: /sqlite-3.40.0/src/test6.c (revision 74e4352a)
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 the testing only */
18 #include "sqliteInt.h"
19 #include "os.h"
20 #include "tcl.h"
21 
22 #ifndef SQLITE_OMIT_DISKIO  /* This file is a no-op if disk I/O is disabled */
23 
24 /*
25 ** crashFile is a subclass of OsFile that is taylored for the
26 ** crash test module.
27 */
28 typedef struct crashFile crashFile;
29 struct crashFile {
30   IoMethod const *pMethod; /* Must be first */
31   u8 **apBlk;              /* Array of blocks that have been written to. */
32   int nBlk;                /* Size of apBlock. */
33   i64 offset;              /* Next character to be read from the file */
34   int nMaxWrite;           /* Largest offset written to. */
35   char *zName;             /* File name */
36   OsFile *pBase;           /* The real file */
37   crashFile *pNext;        /* Next in a list of them all */
38 };
39 
40 /*
41 ** Size of a simulated disk block
42 */
43 #define BLOCKSIZE 512
44 #define BLOCK_OFFSET(x) ((x) * BLOCKSIZE)
45 
46 
47 /*
48 ** The following variables control when a simulated crash occurs.
49 **
50 ** If iCrashDelay is non-zero, then zCrashFile contains (full path) name of
51 ** a file that SQLite will call sqlite3OsSync() on. Each time this happens
52 ** iCrashDelay is decremented. If iCrashDelay is zero after being
53 ** decremented, a "crash" occurs during the sync() operation.
54 **
55 ** In other words, a crash occurs the iCrashDelay'th time zCrashFile is
56 ** synced.
57 */
58 static int iCrashDelay = 0;
59 static char zCrashFile[500];
60 
61 /*
62 ** Set the value of the two crash parameters.
63 */
64 static void setCrashParams(int iDelay, char const *zFile){
65   sqlite3OsEnterMutex();
66   assert( strlen(zFile)<sizeof(zCrashFile) );
67   strcpy(zCrashFile, zFile);
68   iCrashDelay = iDelay;
69   sqlite3OsLeaveMutex();
70 }
71 
72 /*
73 ** File zPath is being sync()ed. Return non-zero if this should
74 ** cause a crash.
75 */
76 static int crashRequired(char const *zPath){
77   int r;
78   int n;
79   sqlite3OsEnterMutex();
80   n = strlen(zCrashFile);
81   if( zCrashFile[n-1]=='*' ){
82     n--;
83   }else if( strlen(zPath)>n ){
84     n = strlen(zPath);
85   }
86   r = 0;
87   if( iCrashDelay>0 && strncmp(zPath, zCrashFile, n)==0 ){
88     iCrashDelay--;
89     if( iCrashDelay<=0 ){
90       r = 1;
91     }
92   }
93   sqlite3OsLeaveMutex();
94   return r;
95 }
96 
97 /*
98 ** A list of all open files.
99 */
100 static crashFile *pAllFiles = 0;
101 
102 /* Forward reference */
103 static void initFile(OsFile **pId, char const *zName, OsFile *pBase);
104 
105 /*
106 ** Undo the work done by initFile. Delete the OsFile structure
107 ** and unlink the structure from the pAllFiles list.
108 */
109 static void closeFile(crashFile **pId){
110   crashFile *pFile = *pId;
111   if( pFile==pAllFiles ){
112     pAllFiles = pFile->pNext;
113   }else{
114     crashFile *p;
115     for(p=pAllFiles; p->pNext!=pFile; p=p->pNext ){
116       assert( p );
117     }
118     p->pNext = pFile->pNext;
119   }
120   sqliteFree(*pId);
121   *pId = 0;
122 }
123 
124 /*
125 ** Read block 'blk' off of the real disk file and into the cache of pFile.
126 */
127 static int readBlockIntoCache(crashFile *pFile, int blk){
128   if( blk>=pFile->nBlk ){
129     int n = ((pFile->nBlk * 2) + 100 + blk);
130     /* if( pFile->nBlk==0 ){ printf("DIRTY %s\n", pFile->zName); } */
131     pFile->apBlk = (u8 **)sqliteRealloc(pFile->apBlk, n * sizeof(u8*));
132     if( !pFile->apBlk ) return SQLITE_NOMEM;
133     memset(&pFile->apBlk[pFile->nBlk], 0, (n - pFile->nBlk)*sizeof(u8*));
134     pFile->nBlk = n;
135   }
136 
137   if( !pFile->apBlk[blk] ){
138     i64 filesize;
139     int rc;
140 
141     u8 *p = sqliteMalloc(BLOCKSIZE);
142     if( !p ) return SQLITE_NOMEM;
143     pFile->apBlk[blk] = p;
144 
145     rc = sqlite3OsFileSize(pFile->pBase, &filesize);
146     if( rc!=SQLITE_OK ) return rc;
147 
148     if( BLOCK_OFFSET(blk)<filesize ){
149       int len = BLOCKSIZE;
150       rc = sqlite3OsSeek(pFile->pBase, blk*BLOCKSIZE);
151       if( BLOCK_OFFSET(blk+1)>filesize ){
152         len = filesize - BLOCK_OFFSET(blk);
153       }
154       if( rc!=SQLITE_OK ) return rc;
155       rc = sqlite3OsRead(pFile->pBase, p, len);
156       if( rc!=SQLITE_OK ) return rc;
157     }
158   }
159 
160   return SQLITE_OK;
161 }
162 
163 /*
164 ** Write the cache of pFile to disk. If crash is non-zero, randomly
165 ** skip blocks when writing. The cache is deleted before returning.
166 */
167 static int writeCache2(crashFile *pFile, int crash){
168   int i;
169   int nMax = pFile->nMaxWrite;
170   int rc = SQLITE_OK;
171 
172   for(i=0; i<pFile->nBlk; i++){
173     u8 *p = pFile->apBlk[i];
174     if( p ){
175       int skip = 0;
176       int trash = 0;
177       if( crash ){
178         char random;
179         sqlite3Randomness(1, &random);
180         if( random & 0x01 ){
181           if( random & 0x02 ){
182             trash = 1;
183 #ifdef TRACE_WRITECACHE
184 printf("Trashing block %d of %s\n", i, pFile->zName);
185 #endif
186           }else{
187             skip = 1;
188 #ifdef TRACE_WRITECACHE
189 printf("Skiping block %d of %s\n", i, pFile->zName);
190 #endif
191           }
192         }else{
193 #ifdef TRACE_WRITECACHE
194 printf("Writing block %d of %s\n", i, pFile->zName);
195 #endif
196         }
197       }
198       if( rc==SQLITE_OK ){
199         rc = sqlite3OsSeek(pFile->pBase, BLOCK_OFFSET(i));
200       }
201       if( rc==SQLITE_OK && !skip ){
202         int len = BLOCKSIZE;
203         if( BLOCK_OFFSET(i+1)>nMax ){
204           len = nMax-BLOCK_OFFSET(i);
205         }
206         if( len>0 ){
207           if( trash ){
208             sqlite3Randomness(len, p);
209           }
210           rc = sqlite3OsWrite(pFile->pBase, p, len);
211         }
212       }
213       sqliteFree(p);
214     }
215   }
216   sqliteFree(pFile->apBlk);
217   pFile->nBlk = 0;
218   pFile->apBlk = 0;
219   pFile->nMaxWrite = 0;
220   return rc;
221 }
222 
223 /*
224 ** Write the cache to disk.
225 */
226 static int writeCache(crashFile *pFile){
227   if( pFile->apBlk ){
228     int c = crashRequired(pFile->zName);
229     if( c ){
230       crashFile *p;
231 #ifdef TRACE_WRITECACHE
232       printf("\nCrash during sync of %s\n", pFile->zName);
233 #endif
234       for(p=pAllFiles; p; p=p->pNext){
235         writeCache2(p, 1);
236       }
237       exit(-1);
238     }else{
239       return writeCache2(pFile, 0);
240     }
241   }
242   return SQLITE_OK;
243 }
244 
245 /*
246 ** Close the file.
247 */
248 static int crashClose(OsFile **pId){
249   crashFile *pFile = (crashFile*)*pId;
250   if( pFile ){
251     /* printf("CLOSE %s (%d blocks)\n", pFile->zName, pFile->nBlk); */
252     writeCache(pFile);
253     sqlite3OsClose(&pFile->pBase);
254   }
255   closeFile(&pFile);
256   *pId = 0;
257   return SQLITE_OK;
258 }
259 
260 static int crashSeek(OsFile *id, i64 offset){
261   ((crashFile*)id)->offset = offset;
262   return SQLITE_OK;
263 }
264 
265 static int crashRead(OsFile *id, void *pBuf, int amt){
266   i64 offset;       /* The current offset from the start of the file */
267   i64 end;          /* The byte just past the last byte read */
268   int blk;            /* Block number the read starts on */
269   int i;
270   u8 *zCsr;
271   int rc = SQLITE_OK;
272   crashFile *pFile = (crashFile*)id;
273 
274   offset = pFile->offset;
275   end = offset+amt;
276   blk = (offset/BLOCKSIZE);
277 
278   zCsr = (u8 *)pBuf;
279   for(i=blk; i*BLOCKSIZE<end; i++){
280     int off = 0;
281     int len = 0;
282 
283 
284     if( BLOCK_OFFSET(i) < offset ){
285       off = offset-BLOCK_OFFSET(i);
286     }
287     len = BLOCKSIZE - off;
288     if( BLOCK_OFFSET(i+1) > end ){
289       len = len - (BLOCK_OFFSET(i+1)-end);
290     }
291 
292     if( i<pFile->nBlk && pFile->apBlk[i]){
293       u8 *pBlk = pFile->apBlk[i];
294       memcpy(zCsr, &pBlk[off], len);
295     }else{
296       rc = sqlite3OsSeek(pFile->pBase, BLOCK_OFFSET(i) + off);
297       if( rc!=SQLITE_OK ) return rc;
298       rc = sqlite3OsRead(pFile->pBase, zCsr, len);
299       if( rc!=SQLITE_OK ) return rc;
300     }
301 
302     zCsr += len;
303   }
304   assert( zCsr==&((u8 *)pBuf)[amt] );
305 
306   pFile->offset = end;
307   return rc;
308 }
309 
310 static int crashWrite(OsFile *id, const void *pBuf, int amt){
311   i64 offset;       /* The current offset from the start of the file */
312   i64 end;          /* The byte just past the last byte written */
313   int blk;            /* Block number the write starts on */
314   int i;
315   const u8 *zCsr;
316   int rc = SQLITE_OK;
317   crashFile *pFile = (crashFile*)id;
318 
319   offset = pFile->offset;
320   end = offset+amt;
321   blk = (offset/BLOCKSIZE);
322 
323   zCsr = (u8 *)pBuf;
324   for(i=blk; i*BLOCKSIZE<end; i++){
325     u8 *pBlk;
326     int off = 0;
327     int len = 0;
328 
329     /* Make sure the block is in the cache */
330     rc = readBlockIntoCache(pFile, i);
331     if( rc!=SQLITE_OK ) return rc;
332 
333     /* Write into the cache */
334     pBlk = pFile->apBlk[i];
335     assert( pBlk );
336 
337     if( BLOCK_OFFSET(i) < offset ){
338       off = offset-BLOCK_OFFSET(i);
339     }
340     len = BLOCKSIZE - off;
341     if( BLOCK_OFFSET(i+1) > end ){
342       len = len - (BLOCK_OFFSET(i+1)-end);
343     }
344     memcpy(&pBlk[off], zCsr, len);
345     zCsr += len;
346   }
347   if( pFile->nMaxWrite<end ){
348     pFile->nMaxWrite = end;
349   }
350   assert( zCsr==&((u8 *)pBuf)[amt] );
351   pFile->offset = end;
352   return rc;
353 }
354 
355 /*
356 ** Sync the file. First flush the write-cache to disk, then call the
357 ** real sync() function.
358 */
359 static int crashSync(OsFile *id, int dataOnly){
360   return writeCache((crashFile*)id);
361 }
362 
363 /*
364 ** Truncate the file. Set the internal OsFile.nMaxWrite variable to the new
365 ** file size to ensure that nothing in the write-cache past this point
366 ** is written to disk.
367 */
368 static int crashTruncate(OsFile *id, i64 nByte){
369   crashFile *pFile = (crashFile*)id;
370   pFile->nMaxWrite = nByte;
371   return sqlite3OsTruncate(pFile->pBase, nByte);
372 }
373 
374 /*
375 ** Return the size of the file. If the cache contains a write that extended
376 ** the file, then return this size instead of the on-disk size.
377 */
378 static int crashFileSize(OsFile *id, i64 *pSize){
379   crashFile *pFile = (crashFile*)id;
380   int rc = sqlite3OsFileSize(pFile->pBase, pSize);
381   if( rc==SQLITE_OK && pSize && *pSize<pFile->nMaxWrite ){
382     *pSize = pFile->nMaxWrite;
383   }
384   return rc;
385 }
386 
387 /*
388 ** Set this global variable to 1 to enable crash testing.
389 */
390 int sqlite3CrashTestEnable = 0;
391 
392 /*
393 ** The three functions used to open files. All that is required is to
394 ** initialise the os_test.c specific fields and then call the corresponding
395 ** os_unix.c function to really open the file.
396 */
397 int sqlite3CrashOpenReadWrite(const char *zFilename, OsFile **pId,int *pRdonly){
398   OsFile *pBase = 0;
399   int rc;
400 
401   sqlite3CrashTestEnable = 0;
402   rc = sqlite3OsOpenReadWrite(zFilename, &pBase, pRdonly);
403   sqlite3CrashTestEnable = 1;
404   if( !rc ){
405     initFile(pId, zFilename, pBase);
406   }
407   return rc;
408 }
409 int sqlite3CrashOpenExclusive(const char *zFilename, OsFile **pId, int delFlag){
410   OsFile *pBase = 0;
411   int rc;
412 
413   sqlite3CrashTestEnable = 0;
414   rc = sqlite3OsOpenExclusive(zFilename, &pBase, delFlag);
415   sqlite3CrashTestEnable = 1;
416   if( !rc ){
417     initFile(pId, zFilename, pBase);
418   }
419   return rc;
420 }
421 int sqlite3CrashOpenReadOnly(const char *zFilename, OsFile **pId, int NotUsed){
422   OsFile *pBase = 0;
423   int rc;
424 
425   sqlite3CrashTestEnable = 0;
426   rc = sqlite3OsOpenReadOnly(zFilename, &pBase);
427   sqlite3CrashTestEnable = 1;
428   if( !rc ){
429     initFile(pId, zFilename, pBase);
430   }
431   return rc;
432 }
433 
434 /*
435 ** OpenDirectory is a no-op
436 */
437 static int crashOpenDir(OsFile *id, const char *zName){
438   return SQLITE_OK;
439 }
440 
441 /*
442 ** Locking primitives are passed through into the underlying
443 ** file descriptor.
444 */
445 int crashLock(OsFile *id, int lockType){
446   return sqlite3OsLock(((crashFile*)id)->pBase, lockType);
447 }
448 int crashUnlock(OsFile *id, int lockType){
449   return sqlite3OsUnlock(((crashFile*)id)->pBase, lockType);
450 }
451 int crashCheckReservedLock(OsFile *id){
452   return sqlite3OsCheckReservedLock(((crashFile*)id)->pBase);
453 }
454 void crashSetFullSync(OsFile *id, int setting){
455   return;  /* This is a no-op */
456 }
457 int crashLockState(OsFile *id){
458   return sqlite3OsLockState(((crashFile*)id)->pBase);
459 }
460 
461 /*
462 ** Return the underlying file handle.
463 */
464 int crashFileHandle(OsFile *id){
465 #if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
466   return sqlite3OsFileHandle(((crashFile*)id)->pBase);
467 #endif
468   return 0;
469 }
470 
471 /*
472 ** This vector defines all the methods that can operate on an OsFile
473 ** for the crash tester.
474 */
475 static const IoMethod crashIoMethod = {
476   crashClose,
477   crashOpenDir,
478   crashRead,
479   crashWrite,
480   crashSeek,
481   crashTruncate,
482   crashSync,
483   crashSetFullSync,
484   crashFileHandle,
485   crashFileSize,
486   crashLock,
487   crashUnlock,
488   crashLockState,
489   crashCheckReservedLock,
490 };
491 
492 
493 /*
494 ** Initialise the os_test.c specific fields of pFile.
495 */
496 static void initFile(OsFile **pId, char const *zName, OsFile *pBase){
497   crashFile *pFile = sqliteMalloc(sizeof(crashFile) + strlen(zName)+1);
498   pFile->pMethod = &crashIoMethod;
499   pFile->nMaxWrite = 0;
500   pFile->offset = 0;
501   pFile->nBlk = 0;
502   pFile->apBlk = 0;
503   pFile->zName = (char *)(&pFile[1]);
504   strcpy(pFile->zName, zName);
505   pFile->pBase = pBase;
506   pFile->pNext = pAllFiles;
507   pAllFiles = pFile;
508   *pId = (OsFile*)pFile;
509 }
510 
511 
512 /*
513 ** tclcmd:   sqlite_crashparams DELAY CRASHFILE
514 **
515 ** This procedure implements a TCL command that enables crash testing
516 ** in testfixture.  Once enabled, crash testing cannot be disabled.
517 */
518 static int crashParamsObjCmd(
519   void * clientData,
520   Tcl_Interp *interp,
521   int objc,
522   Tcl_Obj *CONST objv[]
523 ){
524   int delay;
525   const char *zFile;
526   int nFile;
527   if( objc!=3 ){
528     Tcl_WrongNumArgs(interp, 1, objv, "DELAY CRASHFILE");
529     return TCL_ERROR;
530   }
531   if( Tcl_GetIntFromObj(interp, objv[1], &delay) ) return TCL_ERROR;
532   zFile = Tcl_GetStringFromObj(objv[2], &nFile);
533   if( nFile>=sizeof(zCrashFile)-1 ){
534     Tcl_AppendResult(interp, "crash file name too big", 0);
535     return TCL_ERROR;
536   }
537   setCrashParams(delay, zFile);
538   sqlite3CrashTestEnable = 1;
539   return TCL_OK;
540 }
541 
542 #endif /* SQLITE_OMIT_DISKIO */
543 
544 /*
545 ** This procedure registers the TCL procedures defined in this file.
546 */
547 int Sqlitetest6_Init(Tcl_Interp *interp){
548 #ifndef SQLITE_OMIT_DISKIO
549   Tcl_CreateObjCommand(interp, "sqlite3_crashparams", crashParamsObjCmd, 0, 0);
550 #endif
551   return TCL_OK;
552 }
553 
554 #endif /* SQLITE_TEST */
555