/ Check-in [5a28c7c7]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Clean up test_journal.c to make it easier to follow. (CVS 6140)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 5a28c7c7b45655dcfd0da4898e77d0240b511c5f
User & Date: danielk1977 2009-01-08 12:05:56
Context
2009-01-08
14:36
Allow database files to be created in the root directory on unix. Ticket #3570. (CVS 6141) check-in: 81014334 user: drh tags: trunk
12:05
Clean up test_journal.c to make it easier to follow. (CVS 6140) check-in: 5a28c7c7 user: danielk1977 tags: trunk
03:11
Increase test coverage of where.c. Make sure OR-optimization works on UPDATE and DELETE in addition to SELECT. (Bug found by coverage tests.) (CVS 6139) check-in: 4b2c08e8 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/test_journal.c.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
















































































28
29
30
31
32
33
34
..
44
45
46
47
48
49
50

51
52
53
54
55
56
57
..
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
..
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
...
129
130
131
132
133
134
135
136
137
138
139
140





141
142
143
144
145

146
147
148
149
150
151
152
...
171
172
173
174
175
176
177














178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193





194
195
196
197
198
199





200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255



256
257
258
259
260
261
262
263
264
265
266
267
268
269




















































270
271
272
273
274
275
276
277
...
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318



319
320
321
322
323
324
325
...
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366


367
368
369
370
371
372
373

374
375
376
377
378
379
380
381
382
383
384
385
386
387


388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
...
435
436
437
438
439
440
441

442
443
444
445
446
447
448
...
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
...
643
644
645
646
647
648
649











650
651
652
653
654
655
656
657
658
659



660
661
662
663
664
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
******************************************************************************
**
** This file contains code for a VFS layer that acts as a wrapper around
** an existing VFS. The code in this file attempts to detect a specific
** bug in SQLite - writing data to a database file page when:
**
**   a) the original page data is not stored in a synced portion of the
**      journal file, and
**   b) the page was not a free-list leaf page when the transaction was
**      first opened.
**
** $Id: test_journal.c,v 1.7 2009/01/07 18:08:49 danielk1977 Exp $
*/
#if SQLITE_TEST          /* This file is used for testing only */

#include "sqlite3.h"
#include "sqliteInt.h"

















































































/*
** Maximum pathname length supported by the jt backend.
*/
#define JT_MAX_PATHNAME 512

/*
................................................................................

  /* The following are only used by database file file handles */
  int eLock;               /* Current lock held on the file */
  u32 nPage;               /* Size of file in pages when transaction started */
  u32 nPagesize;           /* Page size when transaction started */
  Bitvec *pWritable;       /* Bitvec of pages that may be written to the file */
  u32 *aCksum;             /* Checksum for first nPage pages */


  /* Only used by journal file-handles */
  sqlite3_int64 iMaxOff;   /* Maximum offset written to this transaction */

  jt_file *pNext;          /* All files are stored in a linked list */
  sqlite3_file *pReal;     /* The file handle for the underlying vfs */
};
................................................................................
/*
** Method declarations for jt_vfs.
*/
static int jtOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
static int jtDelete(sqlite3_vfs*, const char *zName, int syncDir);
static int jtAccess(sqlite3_vfs*, const char *zName, int flags, int *);
static int jtFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
#ifndef SQLITE_OMIT_LOAD_EXTENSION
static void *jtDlOpen(sqlite3_vfs*, const char *zFilename);
static void jtDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
static void (*jtDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void);
static void jtDlClose(sqlite3_vfs*, void*);
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
static int jtRandomness(sqlite3_vfs*, int nByte, char *zOut);
static int jtSleep(sqlite3_vfs*, int microseconds);
static int jtCurrentTime(sqlite3_vfs*, double*);

static sqlite3_vfs jt_vfs = {
  1,                             /* iVersion */
  sizeof(jt_file),               /* szOsFile */
................................................................................
  0,                             /* pNext */
  JT_VFS_NAME,                   /* zName */
  0,                             /* pAppData */
  jtOpen,                        /* xOpen */
  jtDelete,                      /* xDelete */
  jtAccess,                      /* xAccess */
  jtFullPathname,                /* xFullPathname */
#ifndef SQLITE_OMIT_LOAD_EXTENSION
  jtDlOpen,                      /* xDlOpen */
  jtDlError,                     /* xDlError */
  jtDlSym,                       /* xDlSym */
  jtDlClose,                     /* xDlClose */
#else
  0,                             /* xDlOpen */
  0,                             /* xDlError */
  0,                             /* xDlSym */
  0,                             /* xDlClose */
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
  jtRandomness,                  /* xRandomness */
  jtSleep,                       /* xSleep */
  jtCurrentTime                  /* xCurrentTime */
};

static sqlite3_io_methods jt_io_methods = {
  1,                             /* iVersion */
................................................................................
  jtCheckReservedLock,           /* xCheckReservedLock */
  jtFileControl,                 /* xFileControl */
  jtSectorSize,                  /* xSectorSize */
  jtDeviceCharacteristics        /* xDeviceCharacteristics */
};

struct JtGlobal {
  sqlite3_vfs *pVfs;
  jt_file *pList;
};
static struct JtGlobal g = {0, 0};






static void closeTransaction(jt_file *p){
  sqlite3BitvecDestroy(p->pWritable);
  sqlite3_free(p->aCksum);
  p->pWritable = 0;
  p->aCksum = 0;

}

/*
** Close an jt-file.
*/
static int jtClose(sqlite3_file *pFile){
  jt_file **pp;
................................................................................
  sqlite_int64 iOfst
){
  jt_file *p = (jt_file *)pFile;
  return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
}
















static jt_file *locateDatabaseHandle(const char *zJournal){
  jt_file *pMain;
  for(pMain=g.pList; pMain; pMain=pMain->pNext){
    int nName = strlen(zJournal) - strlen("-journal");
    if( (pMain->flags&SQLITE_OPEN_MAIN_DB)
     && (strlen(pMain->zName)==nName)
     && 0==memcmp(pMain->zName, zJournal, nName)
     && (pMain->eLock>=SQLITE_LOCK_RESERVED)
    ){
      break;
    }
  }
  return pMain;
}







static u32 decodeUint32(const unsigned char *z){
  return (z[0]<<24) + (z[1]<<16) + (z[2]<<8) + z[3];
}

static int calculateCksums(jt_file *pMain){
  int rc = SQLITE_OK;





  int ii, jj;
  unsigned char *aData = sqlite3_malloc(pMain->nPagesize);
  if( !aData ){
    return SQLITE_IOERR_NOMEM;
  }

  for(ii=0; rc==SQLITE_OK && ii<pMain->nPage; ii++){
    u32 cksum = 0;
    sqlite_int64 iOff = (sqlite3_int64)(pMain->nPagesize) * (sqlite3_int64)ii;
    rc = sqlite3OsRead(pMain->pReal, aData, pMain->nPagesize, iOff);
    for(jj=0; jj<pMain->nPagesize; jj++){
      cksum = cksum + aData[jj] + (cksum<<3);
    }
    pMain->aCksum[ii] = cksum;
  }
  sqlite3_free(aData);

  return rc;
}

static int readFreelist(jt_file *pMain){
  int rc;
  sqlite3_file *p = pMain->pReal;
  sqlite3_int64 iSize;

  rc = sqlite3OsFileSize(p, &iSize);
  if( rc==SQLITE_OK && iSize>=pMain->nPagesize ){
    unsigned char *zBuf = (unsigned char *)malloc(pMain->nPagesize);
    u32 iTrunk;

    rc = sqlite3OsRead(p, zBuf, pMain->nPagesize, 0);
    iTrunk = decodeUint32(&zBuf[32]);
    while( rc==SQLITE_OK && iTrunk>0 ){
      u32 nLeaf;
      u32 iLeaf;
      sqlite3_int64 iOff = (iTrunk-1)*pMain->nPagesize;
      rc = sqlite3OsRead(p, zBuf, pMain->nPagesize, iOff);
      nLeaf = decodeUint32(&zBuf[4]);
      for(iLeaf=0; rc==SQLITE_OK && iLeaf<nLeaf; iLeaf++){
        u32 pgno = decodeUint32(&zBuf[8+4*iLeaf]);
        sqlite3BitvecSet(pMain->pWritable, pgno);
      }
      iTrunk = decodeUint32(zBuf);
    }

    free(zBuf);
  }

  return rc;
}

/*
** The first argument, zBuf, points to a buffer containing a 28 byte
** serialized journal header. This function deserializes four of the
** integer fields contained in the journal header and writes their
** values to the output variables.



*/
static int decodeJournalHdr(
  const unsigned char *zBuf,         /* Input: 28 byte journal header */
  u32 *pnRec,                        /* Out: Number of journalled records */
  u32 *pnPage,                       /* Out: Original database page count */
  u32 *pnSector,                     /* Out: Sector size in bytes */
  u32 *pnPagesize                    /* Out: Page size in bytes */
){
  unsigned char aMagic[] = { 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd7 };
  if( memcmp(aMagic, zBuf, 8) ) return 1;
  if( pnRec ) *pnRec = decodeUint32(&zBuf[8]);
  if( pnPage ) *pnPage = decodeUint32(&zBuf[16]);
  if( pnSector ) *pnSector = decodeUint32(&zBuf[20]);
  if( pnPagesize ) *pnPagesize = decodeUint32(&zBuf[24]);




















































  return 0;
}

/*
** Write data to an jt-file.
*/
static int jtWrite(
  sqlite3_file *pFile, 
................................................................................
        /* Zeroing the first journal-file header. This is the end of a
        ** transaction. */
        closeTransaction(pMain);
      }else{
        /* Writing the first journal header to a journal file. This happens
        ** when a transaction is first started.  */
        int rc;
        pMain->pWritable = sqlite3BitvecCreate(pMain->nPage);
        pMain->aCksum = sqlite3_malloc(sizeof(u32) * (pMain->nPage + 1));
        p->iMaxOff = 0;
        if( !pMain->pWritable || !pMain->aCksum ){
  	return SQLITE_IOERR_NOMEM;
        }
        rc = readFreelist(pMain);
        if( rc!=SQLITE_OK ){
          return rc;
        }
        rc = calculateCksums(pMain);
        if( rc!=SQLITE_OK ){
          return rc;
        }
      }
    }
    if( p->iMaxOff<(iOfst + iAmt) ){
      p->iMaxOff = iOfst + iAmt;
    }
  }

  if( p->flags&SQLITE_OPEN_MAIN_DB && p->pWritable && iAmt==p->nPagesize ){
    u32 pgno = iOfst/p->nPagesize + 1;



    assert( pgno>p->nPage || sqlite3BitvecTest(p->pWritable, pgno) );
  }

  return sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
}

/*
................................................................................
** page in the journal to the Bitvec object passed as the second argument.
*/
static int readJournalFile(jt_file *p, jt_file *pMain){
  int rc = SQLITE_OK;
  unsigned char zBuf[28];
  sqlite3_file *pReal = p->pReal;
  sqlite3_int64 iOff = 0;
  sqlite3_int64 iSize = 0;
  unsigned char *aPage;

  aPage = sqlite3_malloc(pMain->nPagesize);
  if( !aPage ){
    return SQLITE_IOERR_NOMEM;
  }
  /* rc = sqlite3OsFileSize(p->pReal, &iSize); */
  iSize = p->iMaxOff;
  while( rc==SQLITE_OK && iOff<iSize ){
    u32 nRec, nPage, nSector, nPagesize;
    u32 ii;


    rc = sqlite3OsRead(pReal, zBuf, 28, iOff);
    if( rc!=SQLITE_OK 
     || decodeJournalHdr(zBuf, &nRec, &nPage, &nSector, &nPagesize) 
    ){
      goto finish_rjf;
    }
    iOff += nSector;

    if( nRec==0 ){
      /* A trick. There might be another journal-header immediately 
      ** following this one. In this case, 0 records means 0 records, 
      ** not "read until the end of the file". See also ticket #2565.
      */
      if( iSize>=(iOff+nSector) ){
        rc = sqlite3OsRead(pReal, zBuf, 28, iOff);
        if( rc!=SQLITE_OK || 0==decodeJournalHdr(zBuf, 0, 0, 0, 0) ){
assert(rc!=SQLITE_OK);
          continue;
        }
      }
      nRec = (iSize-iOff) / (pMain->nPagesize+8);
    }


    for(ii=0; rc==SQLITE_OK && ii<nRec && iOff<iSize; ii++){
      u32 pgno;
      rc = sqlite3OsRead(pReal, zBuf, 4, iOff);
      if( rc==SQLITE_OK ){
        pgno = decodeUint32(zBuf);
        if( pgno>0 && pgno<=pMain->nPage ){
          if( 0==sqlite3BitvecTest(pMain->pWritable, pgno) ){
            rc = sqlite3OsRead(pReal, aPage, pMain->nPagesize, iOff+4);
            if( rc==SQLITE_OK ){
              int jj;
              u32 cksum = 0;
              for(jj=0; jj<pMain->nPagesize; jj++){
                cksum = cksum + aPage[jj] + (cksum<<3);
              }
              assert( cksum==pMain->aCksum[pgno-1] );
            }
          }
          sqlite3BitvecSet(pMain->pWritable, pgno);
        }
        iOff += (8 + pMain->nPagesize);
      }
................................................................................
    ** this journal file.
    */
    pMain = locateDatabaseHandle(p->zName);
    assert(pMain);

    /* Set the bitvec values */
    if( pMain->pWritable ){

      rc = readJournalFile(p, pMain);
      if( rc!=SQLITE_OK ){
        return rc;
      }
    }
  }

................................................................................
  const char *zPath, 
  int nOut, 
  char *zOut
){
  return sqlite3OsFullPathname(g.pVfs, zPath, nOut, zOut);
}

#ifndef SQLITE_OMIT_LOAD_EXTENSION
/*
** Open the dynamic library located at zPath and return a handle.
*/
static void *jtDlOpen(sqlite3_vfs *pVfs, const char *zPath){
  return sqlite3OsDlOpen(g.pVfs, zPath);
}

/*
** Populate the buffer zErrMsg (size nByte bytes) with a human readable
** utf-8 string describing the most recent error encountered associated 
** with dynamic libraries.
*/
static void jtDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
  sqlite3OsDlError(g.pVfs, nByte, zErrMsg);
}

/*
** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
*/
static void (*jtDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
  return sqlite3OsDlSym(g.pVfs, p, zSym);
}

/*
** Close the dynamic library handle pHandle.
*/
static void jtDlClose(sqlite3_vfs *pVfs, void *pHandle){
  sqlite3OsDlClose(g.pVfs, pHandle);
}
#endif /* SQLITE_OMIT_LOAD_EXTENSION */

/*
** Populate the buffer pointed to by zBufOut with nByte bytes of 
** random data.
*/
static int jtRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
  return sqlite3OsRandomness(g.pVfs, nByte, zBufOut);
................................................................................
/*
** Return the current time as a Julian Day number in *pTimeOut.
*/
static int jtCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
  return sqlite3OsCurrentTime(g.pVfs, pTimeOut);
}












int jt_register(char *zWrap, int isDefault){
  g.pVfs = sqlite3_vfs_find(zWrap);
  if( g.pVfs==0 ){
    return SQLITE_ERROR;
  }
  jt_vfs.szOsFile += g.pVfs->szOsFile;
  sqlite3_vfs_register(&jt_vfs, isDefault);
  return SQLITE_OK;
}




void jt_unregister(){
  sqlite3_vfs_unregister(&jt_vfs);
}

#endif







|
|
|
<
<
<
<

|





>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>







 







<




<







 







<




<
<
<
<
<
<







 







|
|



>
>
>
>
>





>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>

|













<
>
>
>
>
>




<
<
>
>
>
>
>
|
<
<
<
<
<
<
|
<
|
<
|
|
<
<
<
<
|


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<





>
>
>









|




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|







 







|
<
<
<
<
<
<
<
<
<
<
<









|

>
>
>







 







|






|
<



>
>







>








<





>
>









<
|
<
<
<







 







>







 







<




|








|






|






|

<







 







>
>
>
>
>
>
>
>
>
>
>





|




>
>
>





7
8
9
10
11
12
13
14
15
16




17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
...
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
...
152
153
154
155
156
157
158

159
160
161
162

163
164
165
166
167
168
169
...
171
172
173
174
175
176
177

178
179
180
181






182
183
184
185
186
187
188
...
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
...
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280

281
282
283
284
285
286
287
288
289


290
291
292
293
294
295






296

297

298
299




300
301
302































303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
...
396
397
398
399
400
401
402
403











404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
...
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461

462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482

483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498

499



500
501
502
503
504
505
506
...
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
...
687
688
689
690
691
692
693

694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722

723
724
725
726
727
728
729
...
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
******************************************************************************
**
** This file contains code for a VFS layer that acts as a wrapper around
** an existing VFS. The code in this file attempts to verify that SQLite
** correctly populates and syncs a journal file before writing to a
** corresponding database file.




**
** $Id: test_journal.c,v 1.8 2009/01/08 12:05:56 danielk1977 Exp $
*/
#if SQLITE_TEST          /* This file is used for testing only */

#include "sqlite3.h"
#include "sqliteInt.h"

/*
** INTERFACE
**
**   The public interface to this wrapper VFS is two functions:
**
**     jt_register()
**     jt_unregister()
**
**   See header comments associated with those two functions below for 
**   details.
**
** LIMITATIONS
**
**   This wrapper will not work if "PRAGMA synchronous = off" is used.
**
** OPERATION
**
**  Starting a Transaction:
**
**   When a write-transaction is started, the contents of the database is
**   inspected and the following data stored as part of the database file 
**   handle (type struct jt_file):
**
**     a) The page-size of the database file.
**     b) The number of pages that are in the database file.
**     c) The set of page numbers corresponding to free-list leaf pages.
**     d) A check-sum for every page in the database file.
**
**   The start of a write-transaction is deemed to have occured when a 
**   28-byte journal header is written to byte offset 0 of the journal 
**   file.
**
**  Syncing the Journal File:
**
**   Whenever the xSync method is invoked to sync a journal-file, the
**   contents of the journal file are read. For each page written to
**   the journal file, a check-sum is calculated and compared to the  
**   check-sum calculated for the corresponding database page when the
**   write-transaction was initialized. The success of the comparison
**   is assert()ed. So if SQLite has written something other than the
**   original content to the database file, an assert() will fail.
**
**   Additionally, the set of page numbers for which records exist in
**   the journal file is added to (unioned with) the set of page numbers
**   corresponding to free-list leaf pages collected when the 
**   write-transaction was initialized. This set comprises the page-numbers 
**   corresponding to those pages that SQLite may now safely modify.
**
**  Writing to the Database File:
**
**   When a block of data is written to a database file, the following
**   invariants are asserted:
**
**     a) That the block of data is an aligned block of page-size bytes.
**
**     b) That if the page being written did not exist when the 
**        transaction was started (i.e. the database file is growing), then
**        the journal-file must have been synced at least once since
**        the start of the transaction.
**
**     c) That if the page being written did exist when the transaction 
**        was started, then the page must have either been a free-list
**        leaf page at the start of the transaction, or else must have
**        been stored in the journal file prior to the most recent sync.
**
**  Closing a Transaction:
**
**   When a transaction is closed, all data collected at the start of
**   the transaction, or following an xSync of a journal-file, is 
**   discarded. The end of a transaction is recognized when any one 
**   of the following occur:
**
**     a) A block of zeroes (or anything else that is not a valid 
**        journal-header) is written to the start of the journal file.
**
**     b) A journal file is truncated to zero bytes in size using xTruncate.
**
**     c) The journal file is deleted using xDelete.
*/

/*
** Maximum pathname length supported by the jt backend.
*/
#define JT_MAX_PATHNAME 512

/*
................................................................................

  /* The following are only used by database file file handles */
  int eLock;               /* Current lock held on the file */
  u32 nPage;               /* Size of file in pages when transaction started */
  u32 nPagesize;           /* Page size when transaction started */
  Bitvec *pWritable;       /* Bitvec of pages that may be written to the file */
  u32 *aCksum;             /* Checksum for first nPage pages */
  int nSync;               /* Number of times journal file has been synced */

  /* Only used by journal file-handles */
  sqlite3_int64 iMaxOff;   /* Maximum offset written to this transaction */

  jt_file *pNext;          /* All files are stored in a linked list */
  sqlite3_file *pReal;     /* The file handle for the underlying vfs */
};
................................................................................
/*
** Method declarations for jt_vfs.
*/
static int jtOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
static int jtDelete(sqlite3_vfs*, const char *zName, int syncDir);
static int jtAccess(sqlite3_vfs*, const char *zName, int flags, int *);
static int jtFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);

static void *jtDlOpen(sqlite3_vfs*, const char *zFilename);
static void jtDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
static void (*jtDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void);
static void jtDlClose(sqlite3_vfs*, void*);

static int jtRandomness(sqlite3_vfs*, int nByte, char *zOut);
static int jtSleep(sqlite3_vfs*, int microseconds);
static int jtCurrentTime(sqlite3_vfs*, double*);

static sqlite3_vfs jt_vfs = {
  1,                             /* iVersion */
  sizeof(jt_file),               /* szOsFile */
................................................................................
  0,                             /* pNext */
  JT_VFS_NAME,                   /* zName */
  0,                             /* pAppData */
  jtOpen,                        /* xOpen */
  jtDelete,                      /* xDelete */
  jtAccess,                      /* xAccess */
  jtFullPathname,                /* xFullPathname */

  jtDlOpen,                      /* xDlOpen */
  jtDlError,                     /* xDlError */
  jtDlSym,                       /* xDlSym */
  jtDlClose,                     /* xDlClose */






  jtRandomness,                  /* xRandomness */
  jtSleep,                       /* xSleep */
  jtCurrentTime                  /* xCurrentTime */
};

static sqlite3_io_methods jt_io_methods = {
  1,                             /* iVersion */
................................................................................
  jtCheckReservedLock,           /* xCheckReservedLock */
  jtFileControl,                 /* xFileControl */
  jtSectorSize,                  /* xSectorSize */
  jtDeviceCharacteristics        /* xDeviceCharacteristics */
};

struct JtGlobal {
  sqlite3_vfs *pVfs;             /* Parent VFS */
  jt_file *pList;                /* List of all open files */
};
static struct JtGlobal g = {0, 0};

/*
** The jt_file pointed to by the argument may or may not be a file-handle
** open on a main database file. If it is, and a transaction is currently
** opened on the file, then discard all transaction related data.
*/
static void closeTransaction(jt_file *p){
  sqlite3BitvecDestroy(p->pWritable);
  sqlite3_free(p->aCksum);
  p->pWritable = 0;
  p->aCksum = 0;
  p->nSync = 0;
}

/*
** Close an jt-file.
*/
static int jtClose(sqlite3_file *pFile){
  jt_file **pp;
................................................................................
  sqlite_int64 iOfst
){
  jt_file *p = (jt_file *)pFile;
  return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
}


/*
** Parameter zJournal is the name of a journal file that is currently 
** open. This function locates and returns the handle opened on the
** corresponding database file by the pager that currently has the
** journal file opened. This file-handle is identified by the 
** following properties:
**
**   a) SQLITE_OPEN_MAIN_DB was specified when the file was opened.
**
**   b) The file-name specified when the file was opened matches
**      all but the final 8 characters of the journal file name.
**
**   c) There is currently a reserved lock on the file.
**/
static jt_file *locateDatabaseHandle(const char *zJournal){
  jt_file *pMain = 0;
  for(pMain=g.pList; pMain; pMain=pMain->pNext){
    int nName = strlen(zJournal) - strlen("-journal");
    if( (pMain->flags&SQLITE_OPEN_MAIN_DB)
     && (strlen(pMain->zName)==nName)
     && 0==memcmp(pMain->zName, zJournal, nName)
     && (pMain->eLock>=SQLITE_LOCK_RESERVED)
    ){
      break;
    }
  }
  return pMain;
}


/*
** Parameter z points to a buffer of 4 bytes in size containing a 
** unsigned 32-bit integer stored in big-endian format. Decode the 
** integer and return its value.
*/
static u32 decodeUint32(const unsigned char *z){
  return (z[0]<<24) + (z[1]<<16) + (z[2]<<8) + z[3];
}



/*
** Calculate a checksum from the buffer of length n bytes pointed to
** by parameter z.
*/
static u32 genCksum(const unsigned char *z, int n){
  int i;






  u32 cksum = 0;

  for(i=0; i<n; i++){

    cksum = cksum + z[i] + (cksum<<3);
  }




  return cksum;
}
































/*
** The first argument, zBuf, points to a buffer containing a 28 byte
** serialized journal header. This function deserializes four of the
** integer fields contained in the journal header and writes their
** values to the output variables.
**
** SQLITE_OK is returned if the journal-header is successfully 
** decoded. Otherwise, SQLITE_ERROR.
*/
static int decodeJournalHdr(
  const unsigned char *zBuf,         /* Input: 28 byte journal header */
  u32 *pnRec,                        /* Out: Number of journalled records */
  u32 *pnPage,                       /* Out: Original database page count */
  u32 *pnSector,                     /* Out: Sector size in bytes */
  u32 *pnPagesize                    /* Out: Page size in bytes */
){
  unsigned char aMagic[] = { 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd7 };
  if( memcmp(aMagic, zBuf, 8) ) return SQLITE_ERROR;
  if( pnRec ) *pnRec = decodeUint32(&zBuf[8]);
  if( pnPage ) *pnPage = decodeUint32(&zBuf[16]);
  if( pnSector ) *pnSector = decodeUint32(&zBuf[20]);
  if( pnPagesize ) *pnPagesize = decodeUint32(&zBuf[24]);
  return SQLITE_OK;
}

/*
** This function is called when a new transaction is opened, just after
** the first journal-header is written to the journal file.
*/
static int openTransaction(jt_file *pMain, jt_file *pJournal){
  unsigned char *aData;
  sqlite3_file *p = pMain->pReal;
  int rc = SQLITE_OK;

  aData = sqlite3_malloc(pMain->nPagesize);
  pMain->pWritable = sqlite3BitvecCreate(pMain->nPage);
  pMain->aCksum = sqlite3_malloc(sizeof(u32) * (pMain->nPage + 1));
  pJournal->iMaxOff = 0;

  if( !pMain->pWritable || !pMain->aCksum || !aData ){
    rc = SQLITE_IOERR_NOMEM;
  }else if( pMain->nPage>0 ){
    u32 iTrunk;

    /* Read the database free-list. Add the page-number for each free-list
    ** leaf to the jt_file.pWritable bitvec.
    */
    rc = sqlite3OsRead(p, aData, pMain->nPagesize, 0);
    iTrunk = decodeUint32(&aData[32]);
    while( rc==SQLITE_OK && iTrunk>0 ){
      u32 nLeaf;
      u32 iLeaf;
      sqlite3_int64 iOff = (iTrunk-1)*pMain->nPagesize;
      rc = sqlite3OsRead(p, aData, pMain->nPagesize, iOff);
      nLeaf = decodeUint32(&aData[4]);
      for(iLeaf=0; rc==SQLITE_OK && iLeaf<nLeaf; iLeaf++){
        u32 pgno = decodeUint32(&aData[8+4*iLeaf]);
        sqlite3BitvecSet(pMain->pWritable, pgno);
      }
      iTrunk = decodeUint32(aData);
    }

    /* Calculate and store a checksum for each page in the database file. */
    if( rc==SQLITE_OK ){
      int ii;
      for(ii=0; rc==SQLITE_OK && ii<pMain->nPage; ii++){
        i64 iOff = (i64)(pMain->nPagesize) * (i64)ii;
        rc = sqlite3OsRead(pMain->pReal, aData, pMain->nPagesize, iOff);
        pMain->aCksum[ii] = genCksum(aData, pMain->nPagesize);
      }
    }
  }

  sqlite3_free(aData);
  return rc;
}

/*
** Write data to an jt-file.
*/
static int jtWrite(
  sqlite3_file *pFile, 
................................................................................
        /* Zeroing the first journal-file header. This is the end of a
        ** transaction. */
        closeTransaction(pMain);
      }else{
        /* Writing the first journal header to a journal file. This happens
        ** when a transaction is first started.  */
        int rc;
        if( SQLITE_OK!=(rc=openTransaction(pMain, p)) ){











          return rc;
        }
      }
    }
    if( p->iMaxOff<(iOfst + iAmt) ){
      p->iMaxOff = iOfst + iAmt;
    }
  }

  if( p->flags&SQLITE_OPEN_MAIN_DB && p->pWritable ){
    u32 pgno = iOfst/p->nPagesize + 1;

    assert( iAmt==p->nPagesize && (iOfst%p->nPagesize)==0 );
    assert( pgno<=p->nPage || p->nSync>0 );
    assert( pgno>p->nPage || sqlite3BitvecTest(p->pWritable, pgno) );
  }

  return sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
}

/*
................................................................................
** page in the journal to the Bitvec object passed as the second argument.
*/
static int readJournalFile(jt_file *p, jt_file *pMain){
  int rc = SQLITE_OK;
  unsigned char zBuf[28];
  sqlite3_file *pReal = p->pReal;
  sqlite3_int64 iOff = 0;
  sqlite3_int64 iSize = p->iMaxOff;
  unsigned char *aPage;

  aPage = sqlite3_malloc(pMain->nPagesize);
  if( !aPage ){
    return SQLITE_IOERR_NOMEM;
  }


  while( rc==SQLITE_OK && iOff<iSize ){
    u32 nRec, nPage, nSector, nPagesize;
    u32 ii;

    /* Read and decode the next journal-header from the journal file. */
    rc = sqlite3OsRead(pReal, zBuf, 28, iOff);
    if( rc!=SQLITE_OK 
     || decodeJournalHdr(zBuf, &nRec, &nPage, &nSector, &nPagesize) 
    ){
      goto finish_rjf;
    }
    iOff += nSector;

    if( nRec==0 ){
      /* A trick. There might be another journal-header immediately 
      ** following this one. In this case, 0 records means 0 records, 
      ** not "read until the end of the file". See also ticket #2565.
      */
      if( iSize>=(iOff+nSector) ){
        rc = sqlite3OsRead(pReal, zBuf, 28, iOff);
        if( rc!=SQLITE_OK || 0==decodeJournalHdr(zBuf, 0, 0, 0, 0) ){

          continue;
        }
      }
      nRec = (iSize-iOff) / (pMain->nPagesize+8);
    }

    /* Read all the records that follow the journal-header just read. */
    for(ii=0; rc==SQLITE_OK && ii<nRec && iOff<iSize; ii++){
      u32 pgno;
      rc = sqlite3OsRead(pReal, zBuf, 4, iOff);
      if( rc==SQLITE_OK ){
        pgno = decodeUint32(zBuf);
        if( pgno>0 && pgno<=pMain->nPage ){
          if( 0==sqlite3BitvecTest(pMain->pWritable, pgno) ){
            rc = sqlite3OsRead(pReal, aPage, pMain->nPagesize, iOff+4);
            if( rc==SQLITE_OK ){

              u32 cksum = genCksum(aPage, pMain->nPagesize);



              assert( cksum==pMain->aCksum[pgno-1] );
            }
          }
          sqlite3BitvecSet(pMain->pWritable, pgno);
        }
        iOff += (8 + pMain->nPagesize);
      }
................................................................................
    ** this journal file.
    */
    pMain = locateDatabaseHandle(p->zName);
    assert(pMain);

    /* Set the bitvec values */
    if( pMain->pWritable ){
      pMain->nSync++;
      rc = readJournalFile(p, pMain);
      if( rc!=SQLITE_OK ){
        return rc;
      }
    }
  }

................................................................................
  const char *zPath, 
  int nOut, 
  char *zOut
){
  return sqlite3OsFullPathname(g.pVfs, zPath, nOut, zOut);
}


/*
** Open the dynamic library located at zPath and return a handle.
*/
static void *jtDlOpen(sqlite3_vfs *pVfs, const char *zPath){
  return g.pVfs->xDlOpen(g.pVfs, zPath);
}

/*
** Populate the buffer zErrMsg (size nByte bytes) with a human readable
** utf-8 string describing the most recent error encountered associated 
** with dynamic libraries.
*/
static void jtDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
  g.pVfs->xDlError(g.pVfs, nByte, zErrMsg);
}

/*
** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
*/
static void (*jtDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
  return g.pVfs->xDlSym(g.pVfs, p, zSym);
}

/*
** Close the dynamic library handle pHandle.
*/
static void jtDlClose(sqlite3_vfs *pVfs, void *pHandle){
  g.pVfs->xDlClose(g.pVfs, pHandle);
}


/*
** Populate the buffer pointed to by zBufOut with nByte bytes of 
** random data.
*/
static int jtRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
  return sqlite3OsRandomness(g.pVfs, nByte, zBufOut);
................................................................................
/*
** Return the current time as a Julian Day number in *pTimeOut.
*/
static int jtCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
  return sqlite3OsCurrentTime(g.pVfs, pTimeOut);
}

/**************************************************************************
** Start of public API.
*/

/*
** Configure the jt VFS as a wrapper around the VFS named by parameter 
** zWrap. If the isDefault parameter is true, then the jt VFS is installed
** as the new default VFS for SQLite connections. If isDefault is not
** true, then the jt VFS is installed as non-default. In this case it
** is available via its name, "jt".
*/
int jt_register(char *zWrap, int isDefault){
  g.pVfs = sqlite3_vfs_find(zWrap);
  if( g.pVfs==0 ){
    return SQLITE_ERROR;
  }
  jt_vfs.szOsFile = sizeof(jt_file) + g.pVfs->szOsFile;
  sqlite3_vfs_register(&jt_vfs, isDefault);
  return SQLITE_OK;
}

/*
** Uninstall the jt VFS, if it is installed.
*/
void jt_unregister(){
  sqlite3_vfs_unregister(&jt_vfs);
}

#endif