blob: 5b49156daf74dc8a3550f26b0177a20b3e96f952 [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
drh198bf392006-01-06 21:52:49 +000022#ifndef SQLITE_OMIT_DISKIO /* This file is a no-op if disk I/O is disabled */
23
drh054889e2005-11-30 03:20:31 +000024typedef struct crashFile crashFile;
danielk19770d24e6b2007-08-14 17:42:05 +000025typedef struct WriteBuffer WriteBuffer;
26
27/*
28** Method:
29**
30** This layer is implemented as a wrapper around the "real"
31** sqlite3_file object for the host system. Each time data is
32** written to the file object, instead of being written to the
33** underlying file, the write operation is stored in an in-memory
34** structure (type WriteBuffer). This structure is placed at the
35** end of a global ordered list (the write-list).
36**
37** When data is read from a file object, the requested region is
38** first retrieved from the real file. The write-list is then
39** traversed and data copied from any overlapping WriteBuffer
40** structures to the output buffer. i.e. a read() operation following
41** one or more write() operations works as expected, even if no
42** data has actually been written out to the real file.
43**
44** When a fsync() operation is performed, an operating system crash
45** may be simulated, in which case exit(-1) is called (the call to
46** xSync() never returns). Whether or not a crash is simulated,
47** the data associated with a subset of the WriteBuffer structures
48** stored in the write-list is written to the real underlying files
49** and the entries removed from the write-list. If a crash is simulated,
50** a subset of the buffers may be corrupted before the data is written.
51**
52** The exact subset of the write-list written and/or corrupted is
53** determined by the simulated device characteristics and sector-size.
54**
55** "Normal" mode:
56**
57** Normal mode is used when the simulated device has none of the
58** SQLITE_IOCAP_XXX flags set.
59**
60** In normal mode, if the fsync() is not a simulated crash, the
61** write-list is traversed from beginning to end. Each WriteBuffer
62** structure associated with the file handle used to call xSync()
63** is written to the real file and removed from the write-list.
64**
65** If a crash is simulated, one of the following takes place for
66** each WriteBuffer in the write-list, regardless of which
67** file-handle it is associated with:
68**
69** 1. The buffer is correctly written to the file, just as if
70** a crash were not being simulated.
71**
72** 2. Nothing is done.
73**
74** 3. Garbage data is written to all sectors of the file that
75** overlap the region specified by the WriteBuffer. Or garbage
76** data is written to some contiguous section within the
77** overlapped sectors.
78**
79** Device Characteristic flag handling:
80**
81** If the IOCAP_ATOMIC flag is set, then option (3) above is
82** never selected.
83**
84** If the IOCAP_ATOMIC512 flag is set, and the WriteBuffer represents
85** an aligned write() of an integer number of 512 byte regions, then
86** option (3) above is never selected. Instead, each 512 byte region
87** is either correctly written or left completely untouched. Similar
88** logic governs the behaviour if any of the other ATOMICXXX flags
89** is set.
90**
91** If either the IOCAP_SAFEAPPEND or IOCAP_SEQUENTIAL flags are set
92** and a crash is being simulated, then an entry of the write-list is
93** selected at random. Everything in the list after the selected entry
94** is discarded before processing begins.
95**
96** If IOCAP_SEQUENTIAL is set and a crash is being simulated, option
97** (1) is selected for all write-list entries except the last. If a
98** crash is not being simulated, then all entries in the write-list
99** that occur before at least one write() on the file-handle specified
100** as part of the xSync() are written to their associated real files.
101**
102** If IOCAP_SAFEAPPEND is set and the first byte written by the write()
103** operation is one byte past the current end of the file, then option
104** (1) is always selected.
105*/
106struct WriteBuffer {
107 i64 iOffset; /* Byte offset of the start of this write() */
108 int nBuf; /* Number of bytes written */
109 u8 *zBuf; /* Pointer to copy of written data */
110 crashFile *pFile; /* File this write() applies to */
111};
112
113/*
114** crashFile is a subclass of OsFile that is taylored for
115** the crash test module.
116*/
drh054889e2005-11-30 03:20:31 +0000117struct crashFile {
118 IoMethod const *pMethod; /* Must be first */
119 u8 **apBlk; /* Array of blocks that have been written to. */
120 int nBlk; /* Size of apBlock. */
121 i64 offset; /* Next character to be read from the file */
122 int nMaxWrite; /* Largest offset written to. */
123 char *zName; /* File name */
124 OsFile *pBase; /* The real file */
125 crashFile *pNext; /* Next in a list of them all */
drh9c06c952005-11-26 00:25:00 +0000126};
127
128/*
danielk197759a33f92007-03-17 10:26:59 +0000129** Size of a simulated disk block. Default is 512 bytes.
drh9c06c952005-11-26 00:25:00 +0000130*/
danielk197759a33f92007-03-17 10:26:59 +0000131static int BLOCKSIZE = 512;
drh9c06c952005-11-26 00:25:00 +0000132#define BLOCK_OFFSET(x) ((x) * BLOCKSIZE)
133
134
135/*
136** The following variables control when a simulated crash occurs.
137**
138** If iCrashDelay is non-zero, then zCrashFile contains (full path) name of
139** a file that SQLite will call sqlite3OsSync() on. Each time this happens
140** iCrashDelay is decremented. If iCrashDelay is zero after being
141** decremented, a "crash" occurs during the sync() operation.
142**
143** In other words, a crash occurs the iCrashDelay'th time zCrashFile is
144** synced.
145*/
146static int iCrashDelay = 0;
147static char zCrashFile[500];
148
149/*
danielk197759a33f92007-03-17 10:26:59 +0000150** A list of all open files.
151*/
152static crashFile *pAllFiles = 0;
153
154/*
drh9c06c952005-11-26 00:25:00 +0000155** Set the value of the two crash parameters.
156*/
157static void setCrashParams(int iDelay, char const *zFile){
drh66560ad2006-01-06 14:32:19 +0000158 sqlite3OsEnterMutex();
drh9c06c952005-11-26 00:25:00 +0000159 assert( strlen(zFile)<sizeof(zCrashFile) );
160 strcpy(zCrashFile, zFile);
161 iCrashDelay = iDelay;
drh66560ad2006-01-06 14:32:19 +0000162 sqlite3OsLeaveMutex();
drh9c06c952005-11-26 00:25:00 +0000163}
164
165/*
danielk197759a33f92007-03-17 10:26:59 +0000166** Set the value of the simulated disk block size.
167*/
168static void setBlocksize(int iBlockSize){
169 sqlite3OsEnterMutex();
170 assert( !pAllFiles );
171 BLOCKSIZE = iBlockSize;
172 sqlite3OsLeaveMutex();
173}
174
175/*
drh9c06c952005-11-26 00:25:00 +0000176** File zPath is being sync()ed. Return non-zero if this should
177** cause a crash.
178*/
179static int crashRequired(char const *zPath){
180 int r;
181 int n;
drh66560ad2006-01-06 14:32:19 +0000182 sqlite3OsEnterMutex();
drh9c06c952005-11-26 00:25:00 +0000183 n = strlen(zCrashFile);
184 if( zCrashFile[n-1]=='*' ){
185 n--;
186 }else if( strlen(zPath)>n ){
187 n = strlen(zPath);
188 }
189 r = 0;
190 if( iCrashDelay>0 && strncmp(zPath, zCrashFile, n)==0 ){
191 iCrashDelay--;
192 if( iCrashDelay<=0 ){
193 r = 1;
194 }
195 }
drh66560ad2006-01-06 14:32:19 +0000196 sqlite3OsLeaveMutex();
drh9c06c952005-11-26 00:25:00 +0000197 return r;
198}
199
drh054889e2005-11-30 03:20:31 +0000200/* Forward reference */
201static void initFile(OsFile **pId, char const *zName, OsFile *pBase);
drh9c06c952005-11-26 00:25:00 +0000202
203/*
drh1a235932005-11-29 18:37:15 +0000204** Undo the work done by initFile. Delete the OsFile structure
drh9c06c952005-11-26 00:25:00 +0000205** and unlink the structure from the pAllFiles list.
206*/
drh054889e2005-11-30 03:20:31 +0000207static void closeFile(crashFile **pId){
208 crashFile *pFile = *pId;
drh9c06c952005-11-26 00:25:00 +0000209 if( pFile==pAllFiles ){
210 pAllFiles = pFile->pNext;
211 }else{
drh054889e2005-11-30 03:20:31 +0000212 crashFile *p;
drh9c06c952005-11-26 00:25:00 +0000213 for(p=pAllFiles; p->pNext!=pFile; p=p->pNext ){
214 assert( p );
215 }
216 p->pNext = pFile->pNext;
217 }
drh1a235932005-11-29 18:37:15 +0000218 sqliteFree(*pId);
219 *pId = 0;
drh9c06c952005-11-26 00:25:00 +0000220}
221
222/*
drh1a235932005-11-29 18:37:15 +0000223** Read block 'blk' off of the real disk file and into the cache of pFile.
drh9c06c952005-11-26 00:25:00 +0000224*/
drh054889e2005-11-30 03:20:31 +0000225static int readBlockIntoCache(crashFile *pFile, int blk){
drh9c06c952005-11-26 00:25:00 +0000226 if( blk>=pFile->nBlk ){
227 int n = ((pFile->nBlk * 2) + 100 + blk);
228 /* if( pFile->nBlk==0 ){ printf("DIRTY %s\n", pFile->zName); } */
229 pFile->apBlk = (u8 **)sqliteRealloc(pFile->apBlk, n * sizeof(u8*));
230 if( !pFile->apBlk ) return SQLITE_NOMEM;
231 memset(&pFile->apBlk[pFile->nBlk], 0, (n - pFile->nBlk)*sizeof(u8*));
232 pFile->nBlk = n;
233 }
234
235 if( !pFile->apBlk[blk] ){
236 i64 filesize;
237 int rc;
238
239 u8 *p = sqliteMalloc(BLOCKSIZE);
240 if( !p ) return SQLITE_NOMEM;
241 pFile->apBlk[blk] = p;
242
drh054889e2005-11-30 03:20:31 +0000243 rc = sqlite3OsFileSize(pFile->pBase, &filesize);
drh9c06c952005-11-26 00:25:00 +0000244 if( rc!=SQLITE_OK ) return rc;
245
246 if( BLOCK_OFFSET(blk)<filesize ){
247 int len = BLOCKSIZE;
drh054889e2005-11-30 03:20:31 +0000248 rc = sqlite3OsSeek(pFile->pBase, blk*BLOCKSIZE);
drh9c06c952005-11-26 00:25:00 +0000249 if( BLOCK_OFFSET(blk+1)>filesize ){
250 len = filesize - BLOCK_OFFSET(blk);
251 }
252 if( rc!=SQLITE_OK ) return rc;
drh054889e2005-11-30 03:20:31 +0000253 rc = sqlite3OsRead(pFile->pBase, p, len);
drh9c06c952005-11-26 00:25:00 +0000254 if( rc!=SQLITE_OK ) return rc;
255 }
256 }
257
258 return SQLITE_OK;
259}
260
261/*
262** Write the cache of pFile to disk. If crash is non-zero, randomly
263** skip blocks when writing. The cache is deleted before returning.
264*/
drh054889e2005-11-30 03:20:31 +0000265static int writeCache2(crashFile *pFile, int crash){
drh9c06c952005-11-26 00:25:00 +0000266 int i;
267 int nMax = pFile->nMaxWrite;
drh9c06c952005-11-26 00:25:00 +0000268 int rc = SQLITE_OK;
269
drh9c06c952005-11-26 00:25:00 +0000270 for(i=0; i<pFile->nBlk; i++){
271 u8 *p = pFile->apBlk[i];
272 if( p ){
273 int skip = 0;
274 int trash = 0;
275 if( crash ){
276 char random;
277 sqlite3Randomness(1, &random);
278 if( random & 0x01 ){
279 if( random & 0x02 ){
280 trash = 1;
281#ifdef TRACE_WRITECACHE
282printf("Trashing block %d of %s\n", i, pFile->zName);
283#endif
284 }else{
285 skip = 1;
286#ifdef TRACE_WRITECACHE
287printf("Skiping block %d of %s\n", i, pFile->zName);
288#endif
289 }
290 }else{
291#ifdef TRACE_WRITECACHE
292printf("Writing block %d of %s\n", i, pFile->zName);
293#endif
294 }
295 }
296 if( rc==SQLITE_OK ){
drh054889e2005-11-30 03:20:31 +0000297 rc = sqlite3OsSeek(pFile->pBase, BLOCK_OFFSET(i));
drh9c06c952005-11-26 00:25:00 +0000298 }
299 if( rc==SQLITE_OK && !skip ){
300 int len = BLOCKSIZE;
301 if( BLOCK_OFFSET(i+1)>nMax ){
302 len = nMax-BLOCK_OFFSET(i);
303 }
304 if( len>0 ){
305 if( trash ){
306 sqlite3Randomness(len, p);
307 }
drh054889e2005-11-30 03:20:31 +0000308 rc = sqlite3OsWrite(pFile->pBase, p, len);
drh9c06c952005-11-26 00:25:00 +0000309 }
310 }
311 sqliteFree(p);
312 }
313 }
314 sqliteFree(pFile->apBlk);
315 pFile->nBlk = 0;
316 pFile->apBlk = 0;
317 pFile->nMaxWrite = 0;
drh9c06c952005-11-26 00:25:00 +0000318 return rc;
319}
320
321/*
322** Write the cache to disk.
323*/
drh054889e2005-11-30 03:20:31 +0000324static int writeCache(crashFile *pFile){
drh9c06c952005-11-26 00:25:00 +0000325 if( pFile->apBlk ){
326 int c = crashRequired(pFile->zName);
327 if( c ){
drh054889e2005-11-30 03:20:31 +0000328 crashFile *p;
drh9c06c952005-11-26 00:25:00 +0000329#ifdef TRACE_WRITECACHE
330 printf("\nCrash during sync of %s\n", pFile->zName);
331#endif
332 for(p=pAllFiles; p; p=p->pNext){
333 writeCache2(p, 1);
334 }
335 exit(-1);
336 }else{
337 return writeCache2(pFile, 0);
338 }
339 }
340 return SQLITE_OK;
341}
342
343/*
344** Close the file.
345*/
drh1a235932005-11-29 18:37:15 +0000346static int crashClose(OsFile **pId){
drh054889e2005-11-30 03:20:31 +0000347 crashFile *pFile = (crashFile*)*pId;
drh1a235932005-11-29 18:37:15 +0000348 if( pFile ){
349 /* printf("CLOSE %s (%d blocks)\n", pFile->zName, pFile->nBlk); */
350 writeCache(pFile);
drh054889e2005-11-30 03:20:31 +0000351 sqlite3OsClose(&pFile->pBase);
drh9c06c952005-11-26 00:25:00 +0000352 }
drh054889e2005-11-30 03:20:31 +0000353 closeFile(&pFile);
354 *pId = 0;
drh1a235932005-11-29 18:37:15 +0000355 return SQLITE_OK;
356}
357
358static int crashSeek(OsFile *id, i64 offset){
drh054889e2005-11-30 03:20:31 +0000359 ((crashFile*)id)->offset = offset;
drh9c06c952005-11-26 00:25:00 +0000360 return SQLITE_OK;
361}
362
363static int crashRead(OsFile *id, void *pBuf, int amt){
364 i64 offset; /* The current offset from the start of the file */
365 i64 end; /* The byte just past the last byte read */
366 int blk; /* Block number the read starts on */
367 int i;
368 u8 *zCsr;
369 int rc = SQLITE_OK;
drh054889e2005-11-30 03:20:31 +0000370 crashFile *pFile = (crashFile*)id;
drh9c06c952005-11-26 00:25:00 +0000371
drh1a235932005-11-29 18:37:15 +0000372 offset = pFile->offset;
drh9c06c952005-11-26 00:25:00 +0000373 end = offset+amt;
374 blk = (offset/BLOCKSIZE);
375
376 zCsr = (u8 *)pBuf;
377 for(i=blk; i*BLOCKSIZE<end; i++){
378 int off = 0;
379 int len = 0;
380
381
382 if( BLOCK_OFFSET(i) < offset ){
383 off = offset-BLOCK_OFFSET(i);
384 }
385 len = BLOCKSIZE - off;
386 if( BLOCK_OFFSET(i+1) > end ){
387 len = len - (BLOCK_OFFSET(i+1)-end);
388 }
389
390 if( i<pFile->nBlk && pFile->apBlk[i]){
391 u8 *pBlk = pFile->apBlk[i];
392 memcpy(zCsr, &pBlk[off], len);
393 }else{
drh054889e2005-11-30 03:20:31 +0000394 rc = sqlite3OsSeek(pFile->pBase, BLOCK_OFFSET(i) + off);
drh9c06c952005-11-26 00:25:00 +0000395 if( rc!=SQLITE_OK ) return rc;
drh054889e2005-11-30 03:20:31 +0000396 rc = sqlite3OsRead(pFile->pBase, zCsr, len);
drh9c06c952005-11-26 00:25:00 +0000397 if( rc!=SQLITE_OK ) return rc;
398 }
399
400 zCsr += len;
401 }
402 assert( zCsr==&((u8 *)pBuf)[amt] );
403
drh054889e2005-11-30 03:20:31 +0000404 pFile->offset = end;
drh9c06c952005-11-26 00:25:00 +0000405 return rc;
406}
407
408static int crashWrite(OsFile *id, const void *pBuf, int amt){
409 i64 offset; /* The current offset from the start of the file */
410 i64 end; /* The byte just past the last byte written */
411 int blk; /* Block number the write starts on */
412 int i;
413 const u8 *zCsr;
414 int rc = SQLITE_OK;
drh054889e2005-11-30 03:20:31 +0000415 crashFile *pFile = (crashFile*)id;
drh9c06c952005-11-26 00:25:00 +0000416
drh054889e2005-11-30 03:20:31 +0000417 offset = pFile->offset;
drh9c06c952005-11-26 00:25:00 +0000418 end = offset+amt;
419 blk = (offset/BLOCKSIZE);
420
421 zCsr = (u8 *)pBuf;
422 for(i=blk; i*BLOCKSIZE<end; i++){
423 u8 *pBlk;
424 int off = 0;
425 int len = 0;
426
427 /* Make sure the block is in the cache */
drh054889e2005-11-30 03:20:31 +0000428 rc = readBlockIntoCache(pFile, i);
drh9c06c952005-11-26 00:25:00 +0000429 if( rc!=SQLITE_OK ) return rc;
430
431 /* Write into the cache */
drh054889e2005-11-30 03:20:31 +0000432 pBlk = pFile->apBlk[i];
drh9c06c952005-11-26 00:25:00 +0000433 assert( pBlk );
434
435 if( BLOCK_OFFSET(i) < offset ){
436 off = offset-BLOCK_OFFSET(i);
437 }
438 len = BLOCKSIZE - off;
439 if( BLOCK_OFFSET(i+1) > end ){
440 len = len - (BLOCK_OFFSET(i+1)-end);
441 }
442 memcpy(&pBlk[off], zCsr, len);
443 zCsr += len;
444 }
drh054889e2005-11-30 03:20:31 +0000445 if( pFile->nMaxWrite<end ){
446 pFile->nMaxWrite = end;
drh9c06c952005-11-26 00:25:00 +0000447 }
448 assert( zCsr==&((u8 *)pBuf)[amt] );
drh054889e2005-11-30 03:20:31 +0000449 pFile->offset = end;
drh9c06c952005-11-26 00:25:00 +0000450 return rc;
451}
452
453/*
454** Sync the file. First flush the write-cache to disk, then call the
455** real sync() function.
456*/
457static int crashSync(OsFile *id, int dataOnly){
drh054889e2005-11-30 03:20:31 +0000458 return writeCache((crashFile*)id);
drh9c06c952005-11-26 00:25:00 +0000459}
460
461/*
462** Truncate the file. Set the internal OsFile.nMaxWrite variable to the new
463** file size to ensure that nothing in the write-cache past this point
464** is written to disk.
465*/
466static int crashTruncate(OsFile *id, i64 nByte){
drh054889e2005-11-30 03:20:31 +0000467 crashFile *pFile = (crashFile*)id;
468 pFile->nMaxWrite = nByte;
469 return sqlite3OsTruncate(pFile->pBase, nByte);
drh9c06c952005-11-26 00:25:00 +0000470}
471
472/*
473** Return the size of the file. If the cache contains a write that extended
474** the file, then return this size instead of the on-disk size.
475*/
476static int crashFileSize(OsFile *id, i64 *pSize){
drh054889e2005-11-30 03:20:31 +0000477 crashFile *pFile = (crashFile*)id;
478 int rc = sqlite3OsFileSize(pFile->pBase, pSize);
479 if( rc==SQLITE_OK && pSize && *pSize<pFile->nMaxWrite ){
480 *pSize = pFile->nMaxWrite;
drh9c06c952005-11-26 00:25:00 +0000481 }
482 return rc;
483}
484
485/*
drh66560ad2006-01-06 14:32:19 +0000486** Set this global variable to 1 to enable crash testing.
487*/
488int sqlite3CrashTestEnable = 0;
489
490/*
drh9c06c952005-11-26 00:25:00 +0000491** The three functions used to open files. All that is required is to
492** initialise the os_test.c specific fields and then call the corresponding
493** os_unix.c function to really open the file.
494*/
drh66560ad2006-01-06 14:32:19 +0000495int sqlite3CrashOpenReadWrite(const char *zFilename, OsFile **pId,int *pRdonly){
drh1a235932005-11-29 18:37:15 +0000496 OsFile *pBase = 0;
drh66560ad2006-01-06 14:32:19 +0000497 int rc;
498
499 sqlite3CrashTestEnable = 0;
500 rc = sqlite3OsOpenReadWrite(zFilename, &pBase, pRdonly);
501 sqlite3CrashTestEnable = 1;
drh1a235932005-11-29 18:37:15 +0000502 if( !rc ){
503 initFile(pId, zFilename, pBase);
504 }
505 return rc;
drh9c06c952005-11-26 00:25:00 +0000506}
drh66560ad2006-01-06 14:32:19 +0000507int sqlite3CrashOpenExclusive(const char *zFilename, OsFile **pId, int delFlag){
drh1a235932005-11-29 18:37:15 +0000508 OsFile *pBase = 0;
drh66560ad2006-01-06 14:32:19 +0000509 int rc;
510
511 sqlite3CrashTestEnable = 0;
512 rc = sqlite3OsOpenExclusive(zFilename, &pBase, delFlag);
513 sqlite3CrashTestEnable = 1;
drh1a235932005-11-29 18:37:15 +0000514 if( !rc ){
515 initFile(pId, zFilename, pBase);
516 }
517 return rc;
drh9c06c952005-11-26 00:25:00 +0000518}
drh66560ad2006-01-06 14:32:19 +0000519int sqlite3CrashOpenReadOnly(const char *zFilename, OsFile **pId, int NotUsed){
drh1a235932005-11-29 18:37:15 +0000520 OsFile *pBase = 0;
drh66560ad2006-01-06 14:32:19 +0000521 int rc;
522
523 sqlite3CrashTestEnable = 0;
524 rc = sqlite3OsOpenReadOnly(zFilename, &pBase);
525 sqlite3CrashTestEnable = 1;
drh1a235932005-11-29 18:37:15 +0000526 if( !rc ){
527 initFile(pId, zFilename, pBase);
528 }
529 return rc;
drh9c06c952005-11-26 00:25:00 +0000530}
531
532/*
drh054889e2005-11-30 03:20:31 +0000533** OpenDirectory is a no-op
drh18839212005-11-26 03:43:23 +0000534*/
drh054889e2005-11-30 03:20:31 +0000535static int crashOpenDir(OsFile *id, const char *zName){
drh1a235932005-11-29 18:37:15 +0000536 return SQLITE_OK;
537}
538
539/*
540** Locking primitives are passed through into the underlying
541** file descriptor.
542*/
543int crashLock(OsFile *id, int lockType){
drh054889e2005-11-30 03:20:31 +0000544 return sqlite3OsLock(((crashFile*)id)->pBase, lockType);
drh1a235932005-11-29 18:37:15 +0000545}
546int crashUnlock(OsFile *id, int lockType){
drh054889e2005-11-30 03:20:31 +0000547 return sqlite3OsUnlock(((crashFile*)id)->pBase, lockType);
drh1a235932005-11-29 18:37:15 +0000548}
549int crashCheckReservedLock(OsFile *id){
drh054889e2005-11-30 03:20:31 +0000550 return sqlite3OsCheckReservedLock(((crashFile*)id)->pBase);
drh1a235932005-11-29 18:37:15 +0000551}
552void crashSetFullSync(OsFile *id, int setting){
553 return; /* This is a no-op */
554}
555int crashLockState(OsFile *id){
drh054889e2005-11-30 03:20:31 +0000556 return sqlite3OsLockState(((crashFile*)id)->pBase);
drh1a235932005-11-29 18:37:15 +0000557}
558
559/*
560** Return the underlying file handle.
561*/
562int crashFileHandle(OsFile *id){
danielk1977161fb792006-01-24 10:58:21 +0000563#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
drh054889e2005-11-30 03:20:31 +0000564 return sqlite3OsFileHandle(((crashFile*)id)->pBase);
danielk1977161fb792006-01-24 10:58:21 +0000565#endif
566 return 0;
drh18839212005-11-26 03:43:23 +0000567}
568
569/*
danielk1977b4721172007-03-19 05:54:48 +0000570** Return the simulated file-system sector size.
571*/
572int crashSectorSize(OsFile *id){
573 return BLOCKSIZE;
574}
575
576/*
drh054889e2005-11-30 03:20:31 +0000577** This vector defines all the methods that can operate on an OsFile
578** for the crash tester.
579*/
580static const IoMethod crashIoMethod = {
581 crashClose,
582 crashOpenDir,
583 crashRead,
584 crashWrite,
585 crashSeek,
586 crashTruncate,
587 crashSync,
588 crashSetFullSync,
589 crashFileHandle,
590 crashFileSize,
591 crashLock,
592 crashUnlock,
593 crashLockState,
594 crashCheckReservedLock,
danielk1977b4721172007-03-19 05:54:48 +0000595 crashSectorSize,
drh054889e2005-11-30 03:20:31 +0000596};
597
598
599/*
600** Initialise the os_test.c specific fields of pFile.
601*/
602static void initFile(OsFile **pId, char const *zName, OsFile *pBase){
603 crashFile *pFile = sqliteMalloc(sizeof(crashFile) + strlen(zName)+1);
604 pFile->pMethod = &crashIoMethod;
605 pFile->nMaxWrite = 0;
606 pFile->offset = 0;
607 pFile->nBlk = 0;
608 pFile->apBlk = 0;
609 pFile->zName = (char *)(&pFile[1]);
610 strcpy(pFile->zName, zName);
611 pFile->pBase = pBase;
612 pFile->pNext = pAllFiles;
613 pAllFiles = pFile;
614 *pId = (OsFile*)pFile;
615}
616
617
618/*
danielk197759a33f92007-03-17 10:26:59 +0000619** tclcmd: sqlite_crashparams DELAY CRASHFILE ?BLOCKSIZE?
drh9c06c952005-11-26 00:25:00 +0000620**
621** This procedure implements a TCL command that enables crash testing
622** in testfixture. Once enabled, crash testing cannot be disabled.
623*/
624static int crashParamsObjCmd(
625 void * clientData,
626 Tcl_Interp *interp,
627 int objc,
628 Tcl_Obj *CONST objv[]
629){
danielk197759a33f92007-03-17 10:26:59 +0000630 int iDelay;
drh9c06c952005-11-26 00:25:00 +0000631 const char *zFile;
632 int nFile;
danielk197759a33f92007-03-17 10:26:59 +0000633
634 if( objc!=3 && objc!=4 ){
635 Tcl_WrongNumArgs(interp, 1, objv, "DELAY CRASHFILE ?BLOCKSIZE?");
drh9c06c952005-11-26 00:25:00 +0000636 return TCL_ERROR;
637 }
danielk197759a33f92007-03-17 10:26:59 +0000638 if( Tcl_GetIntFromObj(interp, objv[1], &iDelay) ) return TCL_ERROR;
drh9c06c952005-11-26 00:25:00 +0000639 zFile = Tcl_GetStringFromObj(objv[2], &nFile);
640 if( nFile>=sizeof(zCrashFile)-1 ){
641 Tcl_AppendResult(interp, "crash file name too big", 0);
642 return TCL_ERROR;
643 }
danielk197759a33f92007-03-17 10:26:59 +0000644 setCrashParams(iDelay, zFile);
645 if( objc==4 ){
646 int iBlockSize = 0;
647 if( Tcl_GetIntFromObj(interp, objv[3], &iBlockSize) ) return TCL_ERROR;
648 if( pAllFiles ){
649 char *zErr = "Cannot modify blocksize after opening files";
650 Tcl_SetResult(interp, zErr, TCL_STATIC);
651 return TCL_ERROR;
652 }
653 setBlocksize(iBlockSize);
654 }
drh66560ad2006-01-06 14:32:19 +0000655 sqlite3CrashTestEnable = 1;
drh9c06c952005-11-26 00:25:00 +0000656 return TCL_OK;
657}
658
drh198bf392006-01-06 21:52:49 +0000659#endif /* SQLITE_OMIT_DISKIO */
660
drh9c06c952005-11-26 00:25:00 +0000661/*
662** This procedure registers the TCL procedures defined in this file.
663*/
664int Sqlitetest6_Init(Tcl_Interp *interp){
drh198bf392006-01-06 21:52:49 +0000665#ifndef SQLITE_OMIT_DISKIO
drh9c06c952005-11-26 00:25:00 +0000666 Tcl_CreateObjCommand(interp, "sqlite3_crashparams", crashParamsObjCmd, 0, 0);
drh198bf392006-01-06 21:52:49 +0000667#endif
drh9c06c952005-11-26 00:25:00 +0000668 return TCL_OK;
669}
670
671#endif /* SQLITE_TEST */