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