blob: d8f510ecc68b81e2ab97120e490c63d48248b037 [file] [log] [blame]
drh9c06c952005-11-26 00:25:00 +00001/*
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/*
drh054889e2005-11-30 03:20:31 +000023** A copy of the original sqlite3Os structure
drh9c06c952005-11-26 00:25:00 +000024*/
drh054889e2005-11-30 03:20:31 +000025static struct sqlite3OsVtbl origOs;
drh9c06c952005-11-26 00:25:00 +000026
27/*
drh054889e2005-11-30 03:20:31 +000028** crashFile is a subclass of OsFile that is taylored for the
29** crash test module.
drh9c06c952005-11-26 00:25:00 +000030*/
drh054889e2005-11-30 03:20:31 +000031typedef struct crashFile crashFile;
32struct crashFile {
33 IoMethod const *pMethod; /* Must be first */
34 u8 **apBlk; /* Array of blocks that have been written to. */
35 int nBlk; /* Size of apBlock. */
36 i64 offset; /* Next character to be read from the file */
37 int nMaxWrite; /* Largest offset written to. */
38 char *zName; /* File name */
39 OsFile *pBase; /* The real file */
40 crashFile *pNext; /* Next in a list of them all */
drh9c06c952005-11-26 00:25:00 +000041};
42
43/*
44** Size of a simulated disk block
45*/
46#define BLOCKSIZE 512
47#define BLOCK_OFFSET(x) ((x) * BLOCKSIZE)
48
49
50/*
51** The following variables control when a simulated crash occurs.
52**
53** If iCrashDelay is non-zero, then zCrashFile contains (full path) name of
54** a file that SQLite will call sqlite3OsSync() on. Each time this happens
55** iCrashDelay is decremented. If iCrashDelay is zero after being
56** decremented, a "crash" occurs during the sync() operation.
57**
58** In other words, a crash occurs the iCrashDelay'th time zCrashFile is
59** synced.
60*/
61static int iCrashDelay = 0;
62static char zCrashFile[500];
63
64/*
65** Set the value of the two crash parameters.
66*/
67static void setCrashParams(int iDelay, char const *zFile){
drh054889e2005-11-30 03:20:31 +000068 sqlite3Os.xEnterMutex();
drh9c06c952005-11-26 00:25:00 +000069 assert( strlen(zFile)<sizeof(zCrashFile) );
70 strcpy(zCrashFile, zFile);
71 iCrashDelay = iDelay;
drh054889e2005-11-30 03:20:31 +000072 sqlite3Os.xLeaveMutex();
drh9c06c952005-11-26 00:25:00 +000073}
74
75/*
76** File zPath is being sync()ed. Return non-zero if this should
77** cause a crash.
78*/
79static int crashRequired(char const *zPath){
80 int r;
81 int n;
drh054889e2005-11-30 03:20:31 +000082 sqlite3Os.xEnterMutex();
drh9c06c952005-11-26 00:25:00 +000083 n = strlen(zCrashFile);
84 if( zCrashFile[n-1]=='*' ){
85 n--;
86 }else if( strlen(zPath)>n ){
87 n = strlen(zPath);
88 }
89 r = 0;
90 if( iCrashDelay>0 && strncmp(zPath, zCrashFile, n)==0 ){
91 iCrashDelay--;
92 if( iCrashDelay<=0 ){
93 r = 1;
94 }
95 }
drh054889e2005-11-30 03:20:31 +000096 sqlite3Os.xLeaveMutex();
drh9c06c952005-11-26 00:25:00 +000097 return r;
98}
99
100/*
101** A list of all open files.
102*/
drh054889e2005-11-30 03:20:31 +0000103static crashFile *pAllFiles = 0;
drh9c06c952005-11-26 00:25:00 +0000104
drh054889e2005-11-30 03:20:31 +0000105/* Forward reference */
106static void initFile(OsFile **pId, char const *zName, OsFile *pBase);
drh9c06c952005-11-26 00:25:00 +0000107
108/*
drh1a235932005-11-29 18:37:15 +0000109** Undo the work done by initFile. Delete the OsFile structure
drh9c06c952005-11-26 00:25:00 +0000110** and unlink the structure from the pAllFiles list.
111*/
drh054889e2005-11-30 03:20:31 +0000112static void closeFile(crashFile **pId){
113 crashFile *pFile = *pId;
drh9c06c952005-11-26 00:25:00 +0000114 if( pFile==pAllFiles ){
115 pAllFiles = pFile->pNext;
116 }else{
drh054889e2005-11-30 03:20:31 +0000117 crashFile *p;
drh9c06c952005-11-26 00:25:00 +0000118 for(p=pAllFiles; p->pNext!=pFile; p=p->pNext ){
119 assert( p );
120 }
121 p->pNext = pFile->pNext;
122 }
drh1a235932005-11-29 18:37:15 +0000123 sqliteFree(*pId);
124 *pId = 0;
drh9c06c952005-11-26 00:25:00 +0000125}
126
127/*
drh1a235932005-11-29 18:37:15 +0000128** Read block 'blk' off of the real disk file and into the cache of pFile.
drh9c06c952005-11-26 00:25:00 +0000129*/
drh054889e2005-11-30 03:20:31 +0000130static int readBlockIntoCache(crashFile *pFile, int blk){
drh9c06c952005-11-26 00:25:00 +0000131 if( blk>=pFile->nBlk ){
132 int n = ((pFile->nBlk * 2) + 100 + blk);
133 /* if( pFile->nBlk==0 ){ printf("DIRTY %s\n", pFile->zName); } */
134 pFile->apBlk = (u8 **)sqliteRealloc(pFile->apBlk, n * sizeof(u8*));
135 if( !pFile->apBlk ) return SQLITE_NOMEM;
136 memset(&pFile->apBlk[pFile->nBlk], 0, (n - pFile->nBlk)*sizeof(u8*));
137 pFile->nBlk = n;
138 }
139
140 if( !pFile->apBlk[blk] ){
141 i64 filesize;
142 int rc;
143
144 u8 *p = sqliteMalloc(BLOCKSIZE);
145 if( !p ) return SQLITE_NOMEM;
146 pFile->apBlk[blk] = p;
147
drh054889e2005-11-30 03:20:31 +0000148 rc = sqlite3OsFileSize(pFile->pBase, &filesize);
drh9c06c952005-11-26 00:25:00 +0000149 if( rc!=SQLITE_OK ) return rc;
150
151 if( BLOCK_OFFSET(blk)<filesize ){
152 int len = BLOCKSIZE;
drh054889e2005-11-30 03:20:31 +0000153 rc = sqlite3OsSeek(pFile->pBase, blk*BLOCKSIZE);
drh9c06c952005-11-26 00:25:00 +0000154 if( BLOCK_OFFSET(blk+1)>filesize ){
155 len = filesize - BLOCK_OFFSET(blk);
156 }
157 if( rc!=SQLITE_OK ) return rc;
drh054889e2005-11-30 03:20:31 +0000158 rc = sqlite3OsRead(pFile->pBase, p, len);
drh9c06c952005-11-26 00:25:00 +0000159 if( rc!=SQLITE_OK ) return rc;
160 }
161 }
162
163 return SQLITE_OK;
164}
165
166/*
167** Write the cache of pFile to disk. If crash is non-zero, randomly
168** skip blocks when writing. The cache is deleted before returning.
169*/
drh054889e2005-11-30 03:20:31 +0000170static int writeCache2(crashFile *pFile, int crash){
drh9c06c952005-11-26 00:25:00 +0000171 int i;
172 int nMax = pFile->nMaxWrite;
drh9c06c952005-11-26 00:25:00 +0000173 int rc = SQLITE_OK;
174
drh9c06c952005-11-26 00:25:00 +0000175 for(i=0; i<pFile->nBlk; i++){
176 u8 *p = pFile->apBlk[i];
177 if( p ){
178 int skip = 0;
179 int trash = 0;
180 if( crash ){
181 char random;
182 sqlite3Randomness(1, &random);
183 if( random & 0x01 ){
184 if( random & 0x02 ){
185 trash = 1;
186#ifdef TRACE_WRITECACHE
187printf("Trashing block %d of %s\n", i, pFile->zName);
188#endif
189 }else{
190 skip = 1;
191#ifdef TRACE_WRITECACHE
192printf("Skiping block %d of %s\n", i, pFile->zName);
193#endif
194 }
195 }else{
196#ifdef TRACE_WRITECACHE
197printf("Writing block %d of %s\n", i, pFile->zName);
198#endif
199 }
200 }
201 if( rc==SQLITE_OK ){
drh054889e2005-11-30 03:20:31 +0000202 rc = sqlite3OsSeek(pFile->pBase, BLOCK_OFFSET(i));
drh9c06c952005-11-26 00:25:00 +0000203 }
204 if( rc==SQLITE_OK && !skip ){
205 int len = BLOCKSIZE;
206 if( BLOCK_OFFSET(i+1)>nMax ){
207 len = nMax-BLOCK_OFFSET(i);
208 }
209 if( len>0 ){
210 if( trash ){
211 sqlite3Randomness(len, p);
212 }
drh054889e2005-11-30 03:20:31 +0000213 rc = sqlite3OsWrite(pFile->pBase, p, len);
drh9c06c952005-11-26 00:25:00 +0000214 }
215 }
216 sqliteFree(p);
217 }
218 }
219 sqliteFree(pFile->apBlk);
220 pFile->nBlk = 0;
221 pFile->apBlk = 0;
222 pFile->nMaxWrite = 0;
drh9c06c952005-11-26 00:25:00 +0000223 return rc;
224}
225
226/*
227** Write the cache to disk.
228*/
drh054889e2005-11-30 03:20:31 +0000229static int writeCache(crashFile *pFile){
drh9c06c952005-11-26 00:25:00 +0000230 if( pFile->apBlk ){
231 int c = crashRequired(pFile->zName);
232 if( c ){
drh054889e2005-11-30 03:20:31 +0000233 crashFile *p;
drh9c06c952005-11-26 00:25:00 +0000234#ifdef TRACE_WRITECACHE
235 printf("\nCrash during sync of %s\n", pFile->zName);
236#endif
237 for(p=pAllFiles; p; p=p->pNext){
238 writeCache2(p, 1);
239 }
240 exit(-1);
241 }else{
242 return writeCache2(pFile, 0);
243 }
244 }
245 return SQLITE_OK;
246}
247
248/*
249** Close the file.
250*/
drh1a235932005-11-29 18:37:15 +0000251static int crashClose(OsFile **pId){
drh054889e2005-11-30 03:20:31 +0000252 crashFile *pFile = (crashFile*)*pId;
drh1a235932005-11-29 18:37:15 +0000253 if( pFile ){
254 /* printf("CLOSE %s (%d blocks)\n", pFile->zName, pFile->nBlk); */
255 writeCache(pFile);
drh054889e2005-11-30 03:20:31 +0000256 sqlite3OsClose(&pFile->pBase);
drh9c06c952005-11-26 00:25:00 +0000257 }
drh054889e2005-11-30 03:20:31 +0000258 closeFile(&pFile);
259 *pId = 0;
drh1a235932005-11-29 18:37:15 +0000260 return SQLITE_OK;
261}
262
263static int crashSeek(OsFile *id, i64 offset){
drh054889e2005-11-30 03:20:31 +0000264 ((crashFile*)id)->offset = offset;
drh9c06c952005-11-26 00:25:00 +0000265 return SQLITE_OK;
266}
267
268static int crashRead(OsFile *id, void *pBuf, int amt){
269 i64 offset; /* The current offset from the start of the file */
270 i64 end; /* The byte just past the last byte read */
271 int blk; /* Block number the read starts on */
272 int i;
273 u8 *zCsr;
274 int rc = SQLITE_OK;
drh054889e2005-11-30 03:20:31 +0000275 crashFile *pFile = (crashFile*)id;
drh9c06c952005-11-26 00:25:00 +0000276
drh1a235932005-11-29 18:37:15 +0000277 offset = pFile->offset;
drh9c06c952005-11-26 00:25:00 +0000278 end = offset+amt;
279 blk = (offset/BLOCKSIZE);
280
281 zCsr = (u8 *)pBuf;
282 for(i=blk; i*BLOCKSIZE<end; i++){
283 int off = 0;
284 int len = 0;
285
286
287 if( BLOCK_OFFSET(i) < offset ){
288 off = offset-BLOCK_OFFSET(i);
289 }
290 len = BLOCKSIZE - off;
291 if( BLOCK_OFFSET(i+1) > end ){
292 len = len - (BLOCK_OFFSET(i+1)-end);
293 }
294
295 if( i<pFile->nBlk && pFile->apBlk[i]){
296 u8 *pBlk = pFile->apBlk[i];
297 memcpy(zCsr, &pBlk[off], len);
298 }else{
drh054889e2005-11-30 03:20:31 +0000299 rc = sqlite3OsSeek(pFile->pBase, BLOCK_OFFSET(i) + off);
drh9c06c952005-11-26 00:25:00 +0000300 if( rc!=SQLITE_OK ) return rc;
drh054889e2005-11-30 03:20:31 +0000301 rc = sqlite3OsRead(pFile->pBase, zCsr, len);
drh9c06c952005-11-26 00:25:00 +0000302 if( rc!=SQLITE_OK ) return rc;
303 }
304
305 zCsr += len;
306 }
307 assert( zCsr==&((u8 *)pBuf)[amt] );
308
drh054889e2005-11-30 03:20:31 +0000309 pFile->offset = end;
drh9c06c952005-11-26 00:25:00 +0000310 return rc;
311}
312
313static int crashWrite(OsFile *id, const void *pBuf, int amt){
314 i64 offset; /* The current offset from the start of the file */
315 i64 end; /* The byte just past the last byte written */
316 int blk; /* Block number the write starts on */
317 int i;
318 const u8 *zCsr;
319 int rc = SQLITE_OK;
drh054889e2005-11-30 03:20:31 +0000320 crashFile *pFile = (crashFile*)id;
drh9c06c952005-11-26 00:25:00 +0000321
drh054889e2005-11-30 03:20:31 +0000322 offset = pFile->offset;
drh9c06c952005-11-26 00:25:00 +0000323 end = offset+amt;
324 blk = (offset/BLOCKSIZE);
325
326 zCsr = (u8 *)pBuf;
327 for(i=blk; i*BLOCKSIZE<end; i++){
328 u8 *pBlk;
329 int off = 0;
330 int len = 0;
331
332 /* Make sure the block is in the cache */
drh054889e2005-11-30 03:20:31 +0000333 rc = readBlockIntoCache(pFile, i);
drh9c06c952005-11-26 00:25:00 +0000334 if( rc!=SQLITE_OK ) return rc;
335
336 /* Write into the cache */
drh054889e2005-11-30 03:20:31 +0000337 pBlk = pFile->apBlk[i];
drh9c06c952005-11-26 00:25:00 +0000338 assert( pBlk );
339
340 if( BLOCK_OFFSET(i) < offset ){
341 off = offset-BLOCK_OFFSET(i);
342 }
343 len = BLOCKSIZE - off;
344 if( BLOCK_OFFSET(i+1) > end ){
345 len = len - (BLOCK_OFFSET(i+1)-end);
346 }
347 memcpy(&pBlk[off], zCsr, len);
348 zCsr += len;
349 }
drh054889e2005-11-30 03:20:31 +0000350 if( pFile->nMaxWrite<end ){
351 pFile->nMaxWrite = end;
drh9c06c952005-11-26 00:25:00 +0000352 }
353 assert( zCsr==&((u8 *)pBuf)[amt] );
drh054889e2005-11-30 03:20:31 +0000354 pFile->offset = end;
drh9c06c952005-11-26 00:25:00 +0000355 return rc;
356}
357
358/*
359** Sync the file. First flush the write-cache to disk, then call the
360** real sync() function.
361*/
362static int crashSync(OsFile *id, int dataOnly){
drh054889e2005-11-30 03:20:31 +0000363 return writeCache((crashFile*)id);
drh9c06c952005-11-26 00:25:00 +0000364}
365
366/*
367** Truncate the file. Set the internal OsFile.nMaxWrite variable to the new
368** file size to ensure that nothing in the write-cache past this point
369** is written to disk.
370*/
371static int crashTruncate(OsFile *id, i64 nByte){
drh054889e2005-11-30 03:20:31 +0000372 crashFile *pFile = (crashFile*)id;
373 pFile->nMaxWrite = nByte;
374 return sqlite3OsTruncate(pFile->pBase, nByte);
drh9c06c952005-11-26 00:25:00 +0000375}
376
377/*
378** Return the size of the file. If the cache contains a write that extended
379** the file, then return this size instead of the on-disk size.
380*/
381static int crashFileSize(OsFile *id, i64 *pSize){
drh054889e2005-11-30 03:20:31 +0000382 crashFile *pFile = (crashFile*)id;
383 int rc = sqlite3OsFileSize(pFile->pBase, pSize);
384 if( rc==SQLITE_OK && pSize && *pSize<pFile->nMaxWrite ){
385 *pSize = pFile->nMaxWrite;
drh9c06c952005-11-26 00:25:00 +0000386 }
387 return rc;
388}
389
390/*
391** The three functions used to open files. All that is required is to
392** initialise the os_test.c specific fields and then call the corresponding
393** os_unix.c function to really open the file.
394*/
drh1a235932005-11-29 18:37:15 +0000395static int crashOpenReadWrite(const char *zFilename, OsFile **pId,int *pRdonly){
396 OsFile *pBase = 0;
drh054889e2005-11-30 03:20:31 +0000397 int rc = origOs.xOpenReadWrite(zFilename, &pBase, pRdonly);
drh1a235932005-11-29 18:37:15 +0000398 if( !rc ){
399 initFile(pId, zFilename, pBase);
400 }
401 return rc;
drh9c06c952005-11-26 00:25:00 +0000402}
drh1a235932005-11-29 18:37:15 +0000403static int crashOpenExclusive(const char *zFilename, OsFile **pId, int delFlag){
404 OsFile *pBase = 0;
drh054889e2005-11-30 03:20:31 +0000405 int rc = origOs.xOpenExclusive(zFilename, &pBase, delFlag);
drh1a235932005-11-29 18:37:15 +0000406 if( !rc ){
407 initFile(pId, zFilename, pBase);
408 }
409 return rc;
drh9c06c952005-11-26 00:25:00 +0000410}
drh1a235932005-11-29 18:37:15 +0000411static int crashOpenReadOnly(const char *zFilename, OsFile **pId){
412 OsFile *pBase = 0;
drh054889e2005-11-30 03:20:31 +0000413 int rc = origOs.xOpenReadOnly(zFilename, &pBase);
drh1a235932005-11-29 18:37:15 +0000414 if( !rc ){
415 initFile(pId, zFilename, pBase);
416 }
417 return rc;
drh9c06c952005-11-26 00:25:00 +0000418}
419
420/*
drh054889e2005-11-30 03:20:31 +0000421** OpenDirectory is a no-op
drh18839212005-11-26 03:43:23 +0000422*/
drh054889e2005-11-30 03:20:31 +0000423static int crashOpenDir(OsFile *id, const char *zName){
drh1a235932005-11-29 18:37:15 +0000424 return SQLITE_OK;
425}
426
427/*
428** Locking primitives are passed through into the underlying
429** file descriptor.
430*/
431int crashLock(OsFile *id, int lockType){
drh054889e2005-11-30 03:20:31 +0000432 return sqlite3OsLock(((crashFile*)id)->pBase, lockType);
drh1a235932005-11-29 18:37:15 +0000433}
434int crashUnlock(OsFile *id, int lockType){
drh054889e2005-11-30 03:20:31 +0000435 return sqlite3OsUnlock(((crashFile*)id)->pBase, lockType);
drh1a235932005-11-29 18:37:15 +0000436}
437int crashCheckReservedLock(OsFile *id){
drh054889e2005-11-30 03:20:31 +0000438 return sqlite3OsCheckReservedLock(((crashFile*)id)->pBase);
drh1a235932005-11-29 18:37:15 +0000439}
440void crashSetFullSync(OsFile *id, int setting){
441 return; /* This is a no-op */
442}
443int crashLockState(OsFile *id){
drh054889e2005-11-30 03:20:31 +0000444 return sqlite3OsLockState(((crashFile*)id)->pBase);
drh1a235932005-11-29 18:37:15 +0000445}
446
447/*
448** Return the underlying file handle.
449*/
450int crashFileHandle(OsFile *id){
drh054889e2005-11-30 03:20:31 +0000451 return sqlite3OsFileHandle(((crashFile*)id)->pBase);
drh18839212005-11-26 03:43:23 +0000452}
453
454/*
drh054889e2005-11-30 03:20:31 +0000455** This vector defines all the methods that can operate on an OsFile
456** for the crash tester.
457*/
458static const IoMethod crashIoMethod = {
459 crashClose,
460 crashOpenDir,
461 crashRead,
462 crashWrite,
463 crashSeek,
464 crashTruncate,
465 crashSync,
466 crashSetFullSync,
467 crashFileHandle,
468 crashFileSize,
469 crashLock,
470 crashUnlock,
471 crashLockState,
472 crashCheckReservedLock,
473};
474
475
476/*
477** Initialise the os_test.c specific fields of pFile.
478*/
479static void initFile(OsFile **pId, char const *zName, OsFile *pBase){
480 crashFile *pFile = sqliteMalloc(sizeof(crashFile) + strlen(zName)+1);
481 pFile->pMethod = &crashIoMethod;
482 pFile->nMaxWrite = 0;
483 pFile->offset = 0;
484 pFile->nBlk = 0;
485 pFile->apBlk = 0;
486 pFile->zName = (char *)(&pFile[1]);
487 strcpy(pFile->zName, zName);
488 pFile->pBase = pBase;
489 pFile->pNext = pAllFiles;
490 pAllFiles = pFile;
491 *pId = (OsFile*)pFile;
492}
493
494
495/*
drh9c06c952005-11-26 00:25:00 +0000496** tclcmd: sqlite_crashparams DELAY CRASHFILE
497**
498** This procedure implements a TCL command that enables crash testing
499** in testfixture. Once enabled, crash testing cannot be disabled.
500*/
501static int crashParamsObjCmd(
502 void * clientData,
503 Tcl_Interp *interp,
504 int objc,
505 Tcl_Obj *CONST objv[]
506){
507 int delay;
508 const char *zFile;
509 int nFile;
510 if( objc!=3 ){
511 Tcl_WrongNumArgs(interp, 1, objv, "DELAY CRASHFILE");
512 return TCL_ERROR;
513 }
514 if( Tcl_GetIntFromObj(interp, objv[1], &delay) ) return TCL_ERROR;
515 zFile = Tcl_GetStringFromObj(objv[2], &nFile);
516 if( nFile>=sizeof(zCrashFile)-1 ){
517 Tcl_AppendResult(interp, "crash file name too big", 0);
518 return TCL_ERROR;
519 }
520 setCrashParams(delay, zFile);
drh054889e2005-11-30 03:20:31 +0000521 if( origOs.xOpenReadWrite==0 ){
522 origOs = sqlite3Os;
523 sqlite3Os.xOpenReadWrite = crashOpenReadWrite;
524 sqlite3Os.xOpenExclusive = crashOpenExclusive;
525 sqlite3Os.xOpenReadOnly = crashOpenReadOnly;
526 }
drh9c06c952005-11-26 00:25:00 +0000527 return TCL_OK;
528}
529
530/*
531** This procedure registers the TCL procedures defined in this file.
532*/
533int Sqlitetest6_Init(Tcl_Interp *interp){
534 Tcl_CreateObjCommand(interp, "sqlite3_crashparams", crashParamsObjCmd, 0, 0);
drh9c06c952005-11-26 00:25:00 +0000535 return TCL_OK;
536}
537
538#endif /* SQLITE_TEST */