/ Check-in [8a53f12c]
Login

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

Overview
Comment:Change the checksum used in WAL files so that each frames checksum depends on the content of the WAL header and all frame headers and content up to and including the frame to which the checksum is attached.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 8a53f12c83a107684b99f4a9de371b5ea3ca810a
User & Date: dan 2010-05-24 13:57:43
Context
2010-05-24
17:00
Fix the wal2.test script so that it works in auto_vacuum mode. check-in: 6a818afb user: drh tags: trunk
13:57
Change the checksum used in WAL files so that each frames checksum depends on the content of the WAL header and all frame headers and content up to and including the frame to which the checksum is attached. check-in: 8a53f12c user: dan tags: trunk
13:28
Make sure a WAL frame of all zeros is detected as an invalid frame. check-in: 02d99ad4 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/pager.c.

   217    217   typedef struct PagerSavepoint PagerSavepoint;
   218    218   struct PagerSavepoint {
   219    219     i64 iOffset;                 /* Starting offset in main journal */
   220    220     i64 iHdrOffset;              /* See above */
   221    221     Bitvec *pInSavepoint;        /* Set of pages in this savepoint */
   222    222     Pgno nOrig;                  /* Original number of pages in file */
   223    223     Pgno iSubRec;                /* Index of first record in sub-journal */
   224         -  u32 iFrame;                  /* Last frame in WAL when savepoint opened */
          224  +  u32 aWalData[WAL_SAVEPOINT_NDATA];        /* WAL savepoint context */
   225    225   };
   226    226   
   227    227   /*
   228    228   ** A open page cache is an instance of the following structure.
   229    229   **
   230    230   ** errCode
   231    231   **
................................................................................
  2563   2563     ** will be skipped.  Out-of-range pages are also skipped.
  2564   2564     */
  2565   2565     if( pSavepoint ){
  2566   2566       u32 ii;            /* Loop counter */
  2567   2567       i64 offset = pSavepoint->iSubRec*(4+pPager->pageSize);
  2568   2568   
  2569   2569       if( pagerUseWal(pPager) ){
  2570         -      rc = sqlite3WalSavepointUndo(pPager->pWal, pSavepoint->iFrame);
         2570  +      rc = sqlite3WalSavepointUndo(pPager->pWal, pSavepoint->aWalData);
  2571   2571       }
  2572   2572       for(ii=pSavepoint->iSubRec; rc==SQLITE_OK && ii<pPager->nSubRec; ii++){
  2573   2573         assert( offset==ii*(4+pPager->pageSize) );
  2574   2574         rc = pager_playback_one_page(pPager, &offset, pDone, 0, 1);
  2575   2575       }
  2576   2576       assert( rc!=SQLITE_DONE );
  2577   2577     }
................................................................................
  5444   5444         }
  5445   5445         aNew[ii].iSubRec = pPager->nSubRec;
  5446   5446         aNew[ii].pInSavepoint = sqlite3BitvecCreate(nPage);
  5447   5447         if( !aNew[ii].pInSavepoint ){
  5448   5448           return SQLITE_NOMEM;
  5449   5449         }
  5450   5450         if( pagerUseWal(pPager) ){
  5451         -        aNew[ii].iFrame = sqlite3WalSavepoint(pPager->pWal);
         5451  +        sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData);
  5452   5452         }
  5453   5453       }
  5454   5454   
  5455   5455       /* Open the sub-journal, if it is not already opened. */
  5456   5456       rc = openSubJournal(pPager);
  5457   5457       assertTruncateConstraint(pPager);
  5458   5458     }

Changes to src/wal.c.

   210    210   /*
   211    211   ** The following object holds a copy of the wal-index header content.
   212    212   **
   213    213   ** The actual header in the wal-index consists of two copies of this
   214    214   ** object.
   215    215   */
   216    216   struct WalIndexHdr {
   217         -  u32 iChange;      /* Counter incremented each transaction */
   218         -  u16 bigEndCksum;  /* True if checksums in WAL are big-endian */
   219         -  u16 szPage;       /* Database page size in bytes */
   220         -  u32 mxFrame;      /* Index of last valid frame in the WAL */
   221         -  u32 nPage;        /* Size of database in pages */
   222         -  u32 aSalt[2];     /* Salt-1 and salt-2 values copied from WAL header */
   223         -  u32 aCksum[2];    /* Checksum over all prior fields */
          217  +  u32 iChange;                    /* Counter incremented each transaction */
          218  +  u16 bigEndCksum;                /* True if checksums in WAL are big-endian */
          219  +  u16 szPage;                     /* Database page size in bytes */
          220  +  u32 mxFrame;                    /* Index of last valid frame in the WAL */
          221  +  u32 nPage;                      /* Size of database in pages */
          222  +  u32 aFrameCksum[2];             /* Checksum of last frame in log */
          223  +  u32 aSalt[2];                   /* Two salt values copied from WAL header */
          224  +  u32 aCksum[2];                  /* Checksum over all prior fields */
   224    225   };
   225    226   
   226    227   /* A block of WALINDEX_LOCK_RESERVED bytes beginning at
   227    228   ** WALINDEX_LOCK_OFFSET is reserved for locks. Since some systems
   228    229   ** only support mandatory file-locks, we do not read or write data
   229    230   ** from the region of the file on which locks are applied.
   230    231   */
................................................................................
   420    421     Wal *pWal,                      /* The write-ahead log */
   421    422     u32 iPage,                      /* Database page number for frame */
   422    423     u32 nTruncate,                  /* New db size (or 0 for non-commit frames) */
   423    424     u8 *aData,                      /* Pointer to page data */
   424    425     u8 *aFrame                      /* OUT: Write encoded frame here */
   425    426   ){
   426    427     int nativeCksum;                /* True for native byte-order checksums */
   427         -  u32 aCksum[2];
          428  +  u32 *aCksum = pWal->hdr.aFrameCksum;
   428    429     assert( WAL_FRAME_HDRSIZE==24 );
   429    430     sqlite3Put4byte(&aFrame[0], iPage);
   430    431     sqlite3Put4byte(&aFrame[4], nTruncate);
   431    432     memcpy(&aFrame[8], pWal->hdr.aSalt, 8);
   432    433   
   433    434     nativeCksum = (pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN);
   434         -  walChecksumBytes(nativeCksum, aFrame, 16, 0, aCksum);
          435  +  walChecksumBytes(nativeCksum, aFrame, 8, aCksum, aCksum);
   435    436     walChecksumBytes(nativeCksum, aData, pWal->szPage, aCksum, aCksum);
   436    437   
   437    438     sqlite3Put4byte(&aFrame[16], aCksum[0]);
   438    439     sqlite3Put4byte(&aFrame[20], aCksum[1]);
   439    440   }
   440    441   
   441    442   /*
................................................................................
   447    448     Wal *pWal,                      /* The write-ahead log */
   448    449     u32 *piPage,                    /* OUT: Database page number for frame */
   449    450     u32 *pnTruncate,                /* OUT: New db size (or 0 if not commit) */
   450    451     u8 *aData,                      /* Pointer to page data (for checksum) */
   451    452     u8 *aFrame                      /* Frame data */
   452    453   ){
   453    454     int nativeCksum;                /* True for native byte-order checksums */
          455  +  u32 *aCksum = pWal->hdr.aFrameCksum;
   454    456     u32 pgno;                       /* Page number of the frame */
   455         -  u32 aCksum[2];
   456    457     assert( WAL_FRAME_HDRSIZE==24 );
   457    458   
   458    459     /* A frame is only valid if the salt values in the frame-header
   459    460     ** match the salt values in the wal-header. 
   460    461     */
   461    462     if( memcmp(&pWal->hdr.aSalt, &aFrame[8], 8)!=0 ){
   462    463       return 0;
................................................................................
   470    471     }
   471    472   
   472    473     /* A frame is only valid if a checksum of the first 16 bytes
   473    474     ** of the frame-header, and the frame-data matches
   474    475     ** the checksum in the last 8 bytes of the frame-header.
   475    476     */
   476    477     nativeCksum = (pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN);
   477         -  walChecksumBytes(nativeCksum, aFrame, 16, 0, aCksum);
          478  +  walChecksumBytes(nativeCksum, aFrame, 8, aCksum, aCksum);
   478    479     walChecksumBytes(nativeCksum, aData, pWal->szPage, aCksum, aCksum);
   479    480     if( aCksum[0]!=sqlite3Get4byte(&aFrame[16]) 
   480    481      || aCksum[1]!=sqlite3Get4byte(&aFrame[20]) 
   481    482     ){
   482    483       /* Checksum failed. */
   483    484       return 0;
   484    485     }
................................................................................
   735    736   /*
   736    737   ** Recover the wal-index by reading the write-ahead log file. 
   737    738   ** The caller must hold RECOVER lock on the wal-index file.
   738    739   */
   739    740   static int walIndexRecover(Wal *pWal){
   740    741     int rc;                         /* Return Code */
   741    742     i64 nSize;                      /* Size of log file */
   742         -  WalIndexHdr hdr;                /* Recovered wal-index header */
          743  +  u32 aFrameCksum[2] = {0, 0};
   743    744   
   744    745     assert( pWal->lockState>SQLITE_SHM_READ );
   745         -  memset(&hdr, 0, sizeof(hdr));
          746  +  memset(&pWal->hdr, 0, sizeof(WalIndexHdr));
   746    747   
   747    748     rc = sqlite3OsFileSize(pWal->pWalFd, &nSize);
   748    749     if( rc!=SQLITE_OK ){
   749    750       return rc;
   750    751     }
   751    752   
   752    753     if( nSize>WAL_HDRSIZE ){
................................................................................
   775    776       if( (magic&0xFFFFFFFE)!=WAL_MAGIC 
   776    777        || szPage&(szPage-1) 
   777    778        || szPage>SQLITE_MAX_PAGE_SIZE 
   778    779        || szPage<512 
   779    780       ){
   780    781         goto finished;
   781    782       }
   782         -    hdr.bigEndCksum = pWal->hdr.bigEndCksum = (magic&0x00000001);
          783  +    pWal->hdr.bigEndCksum = (magic&0x00000001);
   783    784       pWal->szPage = szPage;
   784    785       pWal->nCkpt = sqlite3Get4byte(&aBuf[12]);
   785    786       memcpy(&pWal->hdr.aSalt, &aBuf[16], 8);
          787  +    walChecksumBytes(pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN, 
          788  +        aBuf, WAL_HDRSIZE, 0, pWal->hdr.aFrameCksum
          789  +    );
   786    790   
   787    791       /* Malloc a buffer to read frames into. */
   788    792       szFrame = szPage + WAL_FRAME_HDRSIZE;
   789    793       aFrame = (u8 *)sqlite3_malloc(szFrame);
   790    794       if( !aFrame ){
   791    795         return SQLITE_NOMEM;
   792    796       }
................................................................................
   805    809         isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame);
   806    810         if( !isValid ) break;
   807    811         rc = walIndexAppend(pWal, ++iFrame, pgno);
   808    812         if( rc!=SQLITE_OK ) break;
   809    813   
   810    814         /* If nTruncate is non-zero, this is a commit record. */
   811    815         if( nTruncate ){
   812         -        hdr.mxFrame = iFrame;
   813         -        hdr.nPage = nTruncate;
   814         -        hdr.szPage = szPage;
          816  +        pWal->hdr.mxFrame = iFrame;
          817  +        pWal->hdr.nPage = nTruncate;
          818  +        pWal->hdr.szPage = szPage;
          819  +        aFrameCksum[0] = pWal->hdr.aFrameCksum[0];
          820  +        aFrameCksum[1] = pWal->hdr.aFrameCksum[1];
   815    821         }
   816    822       }
   817    823   
   818    824       sqlite3_free(aFrame);
   819         -  }else{
   820         -    memset(&hdr, 0, sizeof(hdr));
   821    825     }
   822    826   
   823    827   finished:
   824         -  if( rc==SQLITE_OK && hdr.mxFrame==0 ){
          828  +  if( rc==SQLITE_OK && pWal->hdr.mxFrame==0 ){
   825    829       rc = walIndexRemap(pWal, WALINDEX_MMAP_INCREMENT);
   826    830     }
   827    831     if( rc==SQLITE_OK ){
   828         -    memcpy(&pWal->hdr, &hdr, sizeof(hdr));
          832  +    pWal->hdr.aFrameCksum[0] = aFrameCksum[0];
          833  +    pWal->hdr.aFrameCksum[1] = aFrameCksum[1];
   829    834       walIndexWriteHdr(pWal);
   830    835     }
   831    836     return rc;
   832    837   }
   833    838   
   834    839   /*
   835    840   ** Close an open wal-index.
................................................................................
  1622   1627         }
  1623   1628       }
  1624   1629       walIndexUnmap(pWal);
  1625   1630     }
  1626   1631     return rc;
  1627   1632   }
  1628   1633   
  1629         -/* Return an integer that records the current (uncommitted) write
  1630         -** position in the WAL
         1634  +/* 
         1635  +** Argument aWalData must point to an array of WAL_SAVEPOINT_NDATA u32 
         1636  +** values. This function populates the array with values required to 
         1637  +** "rollback" the write position of the WAL handle back to the current 
         1638  +** point in the event of a savepoint rollback (via WalSavepointUndo()).
  1631   1639   */
  1632         -u32 sqlite3WalSavepoint(Wal *pWal){
         1640  +void sqlite3WalSavepoint(Wal *pWal, u32 *aWalData){
  1633   1641     assert( pWal->lockState==SQLITE_SHM_WRITE );
  1634         -  return pWal->hdr.mxFrame;
         1642  +  aWalData[0] = pWal->hdr.mxFrame;
         1643  +  aWalData[1] = pWal->hdr.aFrameCksum[0];
         1644  +  aWalData[2] = pWal->hdr.aFrameCksum[1];
  1635   1645   }
  1636   1646   
  1637         -/* Move the write position of the WAL back to iFrame.  Called in
  1638         -** response to a ROLLBACK TO command.
         1647  +/* 
         1648  +** Move the write position of the WAL back to the point identified by
         1649  +** the values in the aWalData[] array. aWalData must point to an array
         1650  +** of WAL_SAVEPOINT_NDATA u32 values that has been previously populated
         1651  +** by a call to WalSavepoint().
  1639   1652   */
  1640         -int sqlite3WalSavepointUndo(Wal *pWal, u32 iFrame){
         1653  +int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){
  1641   1654     int rc = SQLITE_OK;
  1642   1655     assert( pWal->lockState==SQLITE_SHM_WRITE );
  1643   1656   
  1644         -  assert( iFrame<=pWal->hdr.mxFrame );
  1645         -  if( iFrame<pWal->hdr.mxFrame ){
         1657  +  assert( aWalData[0]<=pWal->hdr.mxFrame );
         1658  +  if( aWalData[0]<pWal->hdr.mxFrame ){
  1646   1659       rc = walIndexMap(pWal, walMappingSize(pWal->hdr.mxFrame));
  1647         -    pWal->hdr.mxFrame = iFrame;
         1660  +    pWal->hdr.mxFrame = aWalData[0];
         1661  +    pWal->hdr.aFrameCksum[0] = aWalData[1];
         1662  +    pWal->hdr.aFrameCksum[1] = aWalData[2];
  1648   1663       if( rc==SQLITE_OK ){
  1649   1664         walCleanupHash(pWal);
  1650   1665         walIndexUnmap(pWal);
  1651   1666       }
  1652   1667     }
  1653   1668     return rc;
  1654   1669   }
................................................................................
  1690   1705       pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN;
  1691   1706       sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt);
  1692   1707       memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8);
  1693   1708       rc = sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0);
  1694   1709       if( rc!=SQLITE_OK ){
  1695   1710         return rc;
  1696   1711       }
         1712  +    walChecksumBytes(1, aWalHdr, sizeof(aWalHdr), 0, pWal->hdr.aFrameCksum);
  1697   1713     }
  1698   1714     assert( pWal->szPage==szPage );
  1699   1715   
  1700   1716       /* Write the log file. */
  1701   1717     for(p=pList; p; p=p->pDirty){
  1702   1718       u32 nDbsize;                  /* Db-size field for frame header */
  1703   1719       i64 iOffset;                  /* Write offset in log file */

Changes to src/wal.h.

    24     24   # define sqlite3WalClose(w,x,y,z)          0
    25     25   # define sqlite3WalOpenSnapshot(y,z)       0
    26     26   # define sqlite3WalCloseSnapshot(z) 
    27     27   # define sqlite3WalRead(v,w,x,y,z)         0
    28     28   # define sqlite3WalDbsize(y,z)
    29     29   # define sqlite3WalWriteLock(y,z)          0
    30     30   # define sqlite3WalUndo(x,y,z)             0
    31         -# define sqlite3WalSavepoint(z)            0
           31  +# define sqlite3WalSavepoint(y,z)
    32     32   # define sqlite3WalSavepointUndo(y,z)      0
    33     33   # define sqlite3WalFrames(u,v,w,x,y,z)     0
    34     34   # define sqlite3WalCheckpoint(u,v,w,x,y,z) 0
    35     35   # define sqlite3WalCallback(z)             0
    36     36   #else
           37  +
           38  +#define WAL_SAVEPOINT_NDATA 3
    37     39   
    38     40   /* Connection to a write-ahead log (WAL) file. 
    39     41   ** There is one object of this type for each pager. 
    40     42   */
    41     43   typedef struct Wal Wal;
    42     44   
    43     45   /* Open and close a connection to a write-ahead log. */
................................................................................
    65     67   int sqlite3WalWriteLock(Wal *pWal, int op);
    66     68   
    67     69   /* Undo any frames written (but not committed) to the log */
    68     70   int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx);
    69     71   
    70     72   /* Return an integer that records the current (uncommitted) write
    71     73   ** position in the WAL */
    72         -u32 sqlite3WalSavepoint(Wal *pWal);
           74  +void sqlite3WalSavepoint(Wal *pWal, u32 *aWalData);
    73     75   
    74     76   /* Move the write position of the WAL back to iFrame.  Called in
    75     77   ** response to a ROLLBACK TO command. */
    76         -int sqlite3WalSavepointUndo(Wal *pWal, u32 iFrame);
           78  +int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData);
    77     79   
    78     80   /* Write a frame or frames to the log. */
    79     81   int sqlite3WalFrames(Wal *pWal, int, PgHdr *, Pgno, int, int);
    80     82   
    81     83   /* Copy pages from the log to the database file */ 
    82     84   int sqlite3WalCheckpoint(
    83     85     Wal *pWal,                      /* Write-ahead log connection */

Changes to test/wal.test.

  1344   1344         # be ignored.
  1345   1345         #
  1346   1346         set walhdr [binary format IIIIII 931071618 3007000 $pgsz 1234 22 23]
  1347   1347         set framebody [randomblob $pgsz]
  1348   1348         set framehdr  [binary format IIII $pg 5 22 23]
  1349   1349         set c1 0
  1350   1350         set c2 0
  1351         -      logcksum c1 c2 $framehdr
         1351  +      logcksum c1 c2 $walhdr
         1352  +      logcksum c1 c2 [string range $framehdr 0 7]
  1352   1353         logcksum c1 c2 $framebody
  1353   1354         set framehdr [binary format IIIIII $pg 5 22 23 $c1 $c2]
  1354   1355   
  1355   1356         set fd [open test.db-wal w]
  1356   1357         fconfigure $fd -encoding binary -translation binary
  1357   1358         puts -nonewline $fd $walhdr
  1358   1359         puts -nonewline $fd $framehdr

Changes to test/wal2.test.

    15     15   
    16     16   set testdir [file dirname $argv0]
    17     17   source $testdir/tester.tcl
    18     18   source $testdir/lock_common.tcl
    19     19   ifcapable !wal {finish_test ; return }
    20     20   
    21     21   proc set_tvfs_hdr {file args} {
           22  +
           23  +  # Set $nHdr to the number of bytes in the wal-index header:
           24  +  set nHdr 80
           25  +  set nInt [expr {$nHdr/4}]
           26  +
    22     27     if {[llength $args]>1} {
    23     28       return -code error {wrong # args: should be "set_tvfs_hdr fileName ?val?"}
    24     29     }
    25     30   
    26     31     set blob [tvfs shm $file]
    27     32     if {[llength $args]} {
    28         -    set blob [binary format i16a* [lindex $args 0] [string range $blob 64 end]]
           33  +    set blob [
           34  +      binary format i${nInt}a* [lindex $args 0] [string range $blob $nHdr end]
           35  +    ]
    29     36       tvfs shm $file $blob
    30     37     }
    31     38   
    32         -  binary scan $blob i16 ints
           39  +  binary scan $blob i${nInt} ints
    33     40     return $ints
    34     41   }
    35     42   
    36     43   proc incr_tvfs_hdr {file idx incrval} {
    37     44     set ints [set_tvfs_hdr $file]
    38     45     set v [lindex $ints $idx]
    39     46     incr v $incrval

Changes to test/walcksum.test.

    58     58   # File $filename must be a WAL file on disk. Check that the checksum of frame
    59     59   # $iFrame in the file is correct when interpreting data as $endian-endian
    60     60   # integers ($endian must be either "big" or "little"). If the checksum looks
    61     61   # correct, return 1. Otherwise 0.
    62     62   #
    63     63   proc log_checksum_verify {filename iFrame endian} {
    64     64     set data [readfile $filename]
    65         -  set c1 0
    66         -  set c2 0
    67         -  
    68         -  binary scan [string range $data 8 11] I pgsz
    69     65   
    70         -  set n [log_file_size [expr $iFrame-1] $pgsz]
    71         -  binary scan [string range $data [expr $n+16] [expr $n+23]] II expect1 expect2
    72         -  log_cksum $endian c1 c2 [string range $data $n [expr $n+15]]
    73         -  log_cksum $endian c1 c2 [string range $data [expr $n+24] [expr $n+24+$pgsz-1]]
           66  +  foreach {offset c1 c2} [log_checksum_calc $data $iFrame $endian] {}
    74     67   
           68  +  binary scan [string range $data $offset [expr $offset+7]] II expect1 expect2
    75     69     set expect1 [expr $expect1&0xFFFFFFFF]
    76     70     set expect2 [expr $expect2&0xFFFFFFFF]
           71  +
    77     72     expr {$c1==$expect1 && $c2==$expect2}
    78     73   }
    79     74   
    80     75   #
    81     76   # File $filename must be a WAL file on disk. Compute the checksum for frame
    82     77   # $iFrame in the file by interpreting data as $endian-endian integers 
    83     78   # ($endian must be either "big" or "little"). Then write the computed 
    84     79   # checksum into the file.
    85     80   #
    86     81   proc log_checksum_write {filename iFrame endian} {
    87     82     set data [readfile $filename]
    88         -  set c1 0
    89         -  set c2 0
    90         -  
    91         -  binary scan [string range $data 8 11] I pgsz
    92     83   
    93         -  set n [log_file_size [expr $iFrame-1] $pgsz]
    94         -  log_cksum $endian c1 c2 [string range $data $n [expr $n+15]]
    95         -  log_cksum $endian c1 c2 [string range $data [expr $n+24] [expr $n+24+$pgsz-1]]
           84  +  foreach {offset c1 c2} [log_checksum_calc $data $iFrame $endian] {}
    96     85   
    97     86     set bin [binary format II $c1 $c2]
    98     87     set fd [open $filename r+]
    99     88     fconfigure $fd -encoding binary
   100     89     fconfigure $fd -translation binary
   101         -  seek $fd [expr $n+16]
           90  +  seek $fd $offset
   102     91     puts -nonewline $fd $bin
   103     92     close $fd
   104     93   }
           94  +
           95  +proc log_checksum_calc {data iFrame endian} {
           96  +  
           97  +  binary scan [string range $data 8 11] I pgsz
           98  +  if {$iFrame > 1} {
           99  +    set n [log_file_size [expr $iFrame-2] $pgsz]
          100  +    binary scan [string range $data [expr $n+16] [expr $n+23]] II c1 c2
          101  +  } else {
          102  +    set c1 0
          103  +    set c2 0
          104  +    log_cksum $endian c1 c2 [string range $data 0 23]
          105  +  }
          106  +
          107  +  set n [log_file_size [expr $iFrame-1] $pgsz]
          108  +  log_cksum $endian c1 c2 [string range $data $n [expr $n+7]]
          109  +  log_cksum $endian c1 c2 [string range $data [expr $n+24] [expr $n+24+$pgsz-1]]
          110  +
          111  +  list [expr $n+16] $c1 $c2
          112  +}
   105    113   
   106    114   #
   107    115   # File $filename must be a WAL file on disk. Set the 'magic' field of the
   108    116   # WAL header to indicate that checksums are $endian-endian ($endian must be
   109    117   # either "big" or "little").
   110    118   #
   111    119   proc log_checksum_writemagic {filename endian} {
................................................................................
   176    184         log_checksum_verify test2.db-wal $f $native
   177    185       } 1
   178    186     }
   179    187   
   180    188     # Replace all checksums in the current WAL file with $endian versions.
   181    189     # Then check that it is still possible to recover and read the database.
   182    190     #
          191  +  log_checksum_writemagic test2.db-wal $endian
   183    192     for {set f 1} {$f <= 6} {incr f} {
   184    193       do_test walcksum-1.$endian.3.$f {
   185    194         log_checksum_write test2.db-wal $f $endian
   186    195         log_checksum_verify test2.db-wal $f $endian
   187    196       } {1}
   188    197     }
   189    198     do_test walcksum-1.$endian.4.1 {
   190         -    log_checksum_writemagic test2.db-wal $endian
   191    199       file copy -force test2.db test.db
   192    200       file copy -force test2.db-wal test.db-wal
   193    201       sqlite3 db test.db
   194    202       execsql { SELECT a FROM t1 }
   195    203     } {1 2 3 5 8 13 21}
   196    204   
   197    205     # Following recovery, any frames written to the log should use the same 
................................................................................
   259    267       log_checksum_verify test.db-wal 1 $native
   260    268     } {1}
   261    269     do_test walcksum-1.$endian.8.2 {
   262    270       log_checksum_verify test.db-wal 2 $native
   263    271     } {1}
   264    272     do_test walcksum-1.$endian.8.3 {
   265    273       log_checksum_verify test.db-wal 3 $native
   266         -  } [expr {$native == $endian}]
          274  +  } {0}
   267    275   
   268    276     do_test walcksum-1.$endian.9 {
   269    277       execsql { 
   270    278         PRAGMA integrity_check;
   271    279         SELECT a FROM t1;
   272    280       } db2
   273    281     } {ok 1 2 3 5 8 13 21 34 55 89}
   274    282   
   275    283     catch { db close }
   276    284     catch { db2 close }
   277    285   }
          286  +
          287  +do_test walcksum-2.1 {
          288  +  file delete -force test.db test.db-wal test.db-journal
          289  +  sqlite3 db test.db
          290  +  execsql {
          291  +    PRAGMA synchronous = NORMAL;
          292  +    PRAGMA page_size = 1024;
          293  +    PRAGMA journal_mode = WAL;
          294  +    PRAGMA cache_size = 10;
          295  +    CREATE TABLE t1(x PRIMARY KEY);
          296  +    PRAGMA wal_checkpoint;
          297  +    INSERT INTO t1 VALUES(randomblob(800));
          298  +    BEGIN;
          299  +      INSERT INTO t1 SELECT randomblob(800) FROM t1;   /*   2 */
          300  +      INSERT INTO t1 SELECT randomblob(800) FROM t1;   /*   4 */
          301  +      INSERT INTO t1 SELECT randomblob(800) FROM t1;   /*   8 */
          302  +      INSERT INTO t1 SELECT randomblob(800) FROM t1;   /*  16 */
          303  +      SAVEPOINT one;
          304  +        INSERT INTO t1 SELECT randomblob(800) FROM t1;   /*  32 */
          305  +        INSERT INTO t1 SELECT randomblob(800) FROM t1;   /*  64 */
          306  +        INSERT INTO t1 SELECT randomblob(800) FROM t1;   /* 128 */
          307  +        INSERT INTO t1 SELECT randomblob(800) FROM t1;   /* 256 */
          308  +      ROLLBACK TO one;
          309  +      INSERT INTO t1 SELECT randomblob(800) FROM t1;   /*  32 */
          310  +      INSERT INTO t1 SELECT randomblob(800) FROM t1;   /*  64 */
          311  +      INSERT INTO t1 SELECT randomblob(800) FROM t1;   /* 128 */
          312  +      INSERT INTO t1 SELECT randomblob(800) FROM t1;   /* 256 */
          313  +    COMMIT;
          314  +  }
          315  +
          316  +  file copy -force test.db test2.db
          317  +  file copy -force test.db-wal test2.db-wal
          318  +
          319  +  sqlite3 db2 test2.db
          320  +  execsql {
          321  +    PRAGMA integrity_check;
          322  +    SELECT count(*) FROM t1;
          323  +  } db2
          324  +} {ok 256}
          325  +catch { db close }
          326  +catch { db2 close }
   278    327   
   279    328   finish_test