From da110bfecec0b590ee6321c4f57bf98c950905da Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 5 May 2016 17:15:23 +0000 Subject: [PATCH 1/5] Initial check-in of the "scrub.exe" utility program prototype. Not yet fully functional. In particular, no scrubbing is done. FossilOrigin-Name: bdf2ec77d1542d4e9b68218f558710a3efc15823 --- Makefile.in | 4 + Makefile.msc | 3 + ext/misc/scrub.c | 306 +++++++++++++++++++++++++++++++++++++++++++++++ main.mk | 3 + manifest | 20 ++-- manifest.uuid | 2 +- 6 files changed, 329 insertions(+), 9 deletions(-) create mode 100644 ext/misc/scrub.c diff --git a/Makefile.in b/Makefile.in index 78480de9e0..4e10d4e231 100644 --- a/Makefile.in +++ b/Makefile.in @@ -589,6 +589,10 @@ sqlite3$(TEXE): $(TOP)/src/shell.c sqlite3.c sqldiff$(TEXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h $(LTLINK) -o $@ $(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS) +scrub$(TEXE): $(TOP)/ext/misc/scrub.c sqlite3.o + $(LTLINK) -o $@ -I. -DSCRUB_STANDALONE \ + $(TOP)/ext/misc/scrub.c sqlite3.o $(TLIBS) + srcck1$(BEXE): $(TOP)/tool/srcck1.c $(BCC) -o srcck1$(BEXE) $(TOP)/tool/srcck1.c diff --git a/Makefile.msc b/Makefile.msc index bb9790d312..408a516364 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -1455,6 +1455,9 @@ $(SQLITE3EXE): $(TOP)\src\shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_S sqldiff.exe: $(TOP)\tool\sqldiff.c $(SQLITE3C) $(SQLITE3H) $(LTLINK) $(NO_WARN) $(TOP)\tool\sqldiff.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) +scrub.exe: $(TOP)\ext\misc\scrub.c $(SQLITE3C) $(SQLITE3H) + $(LTLINK) $(NO_WARN) $(TOP)\ext\misc\scrub.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) + srcck1.exe: $(TOP)\tool\srcck1.c $(BCC) $(NO_WARN) -Fe$@ $(TOP)\tool\srcck1.c diff --git a/ext/misc/scrub.c b/ext/misc/scrub.c new file mode 100644 index 0000000000..fbf2f8c2b7 --- /dev/null +++ b/ext/misc/scrub.c @@ -0,0 +1,306 @@ +/* +** 2016-05-05 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** 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 implements a utility function (and a utility program) that +** makes a copy of an SQLite database while simultaneously zeroing out all +** deleted content. +** +** Normally (when PRAGMA secure_delete=OFF, which is the default) when SQLite +** deletes content, it does not overwrite the deleted content but rather marks +** the region of the file that held that content as being reusable. This can +** cause deleted content to recoverable from the database file. This stale +** content is removed by the VACUUM command, but VACUUM can be expensive for +** large databases. When in PRAGMA secure_delete=ON mode, the deleted content +** is zeroed, but secure_delete=ON has overhead as well. +** +** This utility attempts to make a copy of a complete SQLite database where +** all of the deleted content is zeroed out in the copy, and it attempts to +** do so while being faster than running VACUUM. +** +** Usage: +** +** int sqlite3_scrub_backup( +** const char *zSourceFile, // Source database filename +** const char *zDestFile, // Destination database filename +** char **pzErrMsg // Write error message here +** ); +** +** Simply call the API above specifying the filename of the source database +** and the name of the backup copy. The source database must already exist +** and can be in active use. (A read lock is held during the backup.) The +** destination file should not previously exist. If the pzErrMsg parameter +** is non-NULL and if an error occurs, then an error message might be written +** into memory obtained from sqlite3_malloc() and *pzErrMsg made to point to +** that error message. But if the error is an OOM, the error might not be +** reported. The routine always returns non-zero if there is an error. +** +** If compiled with -DSCRUB_STANDALONE then a main() procedure is added and +** this file becomes a standalone program that can be run as follows: +** +** ./sqlite3scrub SOURCE DEST +*/ +#include "sqlite3.h" +#include +#include +#include +#include +#include + +typedef struct ScrubState ScrubState; +typedef unsigned char u8; + +/* State information for a scrub-and-backup operation */ +struct ScrubState { + const char *zSrcFile; /* Name of the source file */ + const char *zDestFile; /* Name of the destination file */ + int rcErr; /* Error code */ + char *zErr; /* Error message text */ + sqlite3 *dbSrc; /* Source database connection */ + sqlite3_file *pSrc; /* Source file handle */ + sqlite3 *dbDest; /* Destination database connection */ + sqlite3_file *pDest; /* Destination file handle */ + unsigned int szPage; /* Page size */ + unsigned int nPage; /* Number of pages */ + u8 *page1; /* Content of page 1 */ +}; + +/* Store an error message */ +static void scrubBackupErr(ScrubState *p, const char *zFormat, ...){ + va_list ap; + sqlite3_free(p->zErr); + va_start(ap, zFormat); + p->zErr = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + if( p->rcErr==0 ) p->rcErr = SQLITE_ERROR; +} + +/* Allocate memory to hold a single page of content */ +static u8 *scrubBackupAllocPage(ScrubState *p){ + u8 *pPage; + if( p->rcErr ) return 0; + pPage = sqlite3_malloc( p->szPage ); + if( pPage==0 ) p->rcErr = SQLITE_NOMEM; + return pPage; +} + +/* Read a page from the source database into memory. Use the memory +** provided by pBuf if not NULL or allocate a new page if pBuf==NULL. +*/ +static u8 *scrubBackupRead(ScrubState *p, int pgno, u8 *pBuf){ + int rc; + sqlite3_int64 iOff; + u8 *pOut = pBuf; + if( p->rcErr ) return 0; + if( pOut==0 ){ + pOut = scrubBackupAllocPage(p); + if( pOut==0 ) return 0; + } + iOff = (pgno-1)*(sqlite3_int64)p->szPage; + rc = p->pSrc->pMethods->xRead(p->pSrc, pOut, p->szPage, iOff); + if( rc!=SQLITE_OK ){ + if( pBuf==0 ) sqlite3_free(pOut); + pOut = 0; + scrubBackupErr(p, "read failed for page %d", pgno); + p->rcErr = SQLITE_IOERR; + } + return pOut; +} + +/* Write a page to the destination database */ +static void scrubBackupWrite(ScrubState *p, int pgno, u8 *pData){ + int rc; + sqlite3_int64 iOff; + if( p->rcErr ) return; + iOff = (pgno-1)*(sqlite3_int64)p->szPage; + rc = p->pDest->pMethods->xWrite(p->pDest, pData, p->szPage, iOff); + if( rc!=SQLITE_OK ){ + scrubBackupErr(p, "write failed for page %d", pgno); + p->rcErr = SQLITE_IOERR; + } +} + +/* Prepare a statement against the "db" database. */ +static sqlite3_stmt *scrubBackupPrepare( + ScrubState *p, /* Backup context */ + sqlite3 *db, /* Database to prepare against */ + const char *zSql /* SQL statement */ +){ + sqlite3_stmt *pStmt; + if( p->rcErr ) return 0; + p->rcErr = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + if( p->rcErr ){ + scrubBackupErr(p, "SQL error \"%s\" on \"%s\"", + sqlite3_errmsg(db), zSql); + sqlite3_finalize(pStmt); + return 0; + } + return pStmt; +} + + +/* Open the source database file */ +static void scrubBackupOpenSrc(ScrubState *p){ + sqlite3_stmt *pStmt; + int rc; + /* Open the source database file */ + p->rcErr = sqlite3_open_v2(p->zSrcFile, &p->dbSrc, + SQLITE_OPEN_READONLY | + SQLITE_OPEN_URI | SQLITE_OPEN_PRIVATECACHE, 0); + if( p->rcErr ){ + scrubBackupErr(p, "cannot open source database: %s", + sqlite3_errmsg(p->dbSrc)); + return; + } + p->rcErr = sqlite3_exec(p->dbSrc, "BEGIN", 0, 0, 0); + if( p->rcErr ){ + scrubBackupErr(p, + "cannot start a read transaction on the source database: %s", + sqlite3_errmsg(p->dbSrc)); + return; + } + pStmt = scrubBackupPrepare(p, p->dbSrc, "PRAGMA page_size"); + if( pStmt==0 ) return; + rc = sqlite3_step(pStmt); + if( rc==SQLITE_ROW ){ + p->szPage = sqlite3_column_int(pStmt, 0); + }else{ + scrubBackupErr(p, "unable to determine the page size"); + } + sqlite3_finalize(pStmt); + if( p->rcErr ) return; + pStmt = scrubBackupPrepare(p, p->dbSrc, "PRAGMA page_count"); + if( pStmt==0 ) return; + rc = sqlite3_step(pStmt); + if( rc==SQLITE_ROW ){ + p->nPage = sqlite3_column_int(pStmt, 0); + }else{ + scrubBackupErr(p, "unable to determine the size of the source database"); + } + sqlite3_finalize(pStmt); + sqlite3_file_control(p->dbSrc, "main", SQLITE_FCNTL_FILE_POINTER, &p->pSrc); + if( p->pSrc==0 || p->pSrc->pMethods==0 ){ + scrubBackupErr(p, "cannot get the source file handle"); + p->rcErr = SQLITE_ERROR; + } +} + +/* Create and open the destination file */ +static void scrubBackupOpenDest(ScrubState *p){ + sqlite3_stmt *pStmt; + int rc; + char *zSql; + if( p->rcErr ) return; + p->rcErr = sqlite3_open_v2(p->zDestFile, &p->dbDest, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | + SQLITE_OPEN_URI | SQLITE_OPEN_PRIVATECACHE, 0); + if( p->rcErr ){ + scrubBackupErr(p, "cannot open destination database: %s", + sqlite3_errmsg(p->dbDest)); + return; + } + zSql = sqlite3_mprintf("PRAGMA page_size(%u);", p->szPage); + if( zSql==0 ){ + p->rcErr = SQLITE_NOMEM; + return; + } + p->rcErr = sqlite3_exec(p->dbDest, zSql, 0, 0, 0); + sqlite3_free(zSql); + if( p->rcErr ){ + scrubBackupErr(p, + "cannot set the page size on the destination database: %s", + sqlite3_errmsg(p->dbDest)); + return; + } + sqlite3_exec(p->dbDest, "PRAGMA journal_mode=OFF;", 0, 0, 0); + p->rcErr = sqlite3_exec(p->dbDest, "BEGIN EXCLUSIVE;", 0, 0, 0); + if( p->rcErr ){ + scrubBackupErr(p, + "cannot start a write transaction on the destination database: %s", + sqlite3_errmsg(p->dbDest)); + return; + } + pStmt = scrubBackupPrepare(p, p->dbDest, "PRAGMA page_count;"); + if( pStmt==0 ) return; + rc = sqlite3_step(pStmt); + if( rc!=SQLITE_ROW ){ + scrubBackupErr(p, "cannot measure the size of the destination"); + }else if( sqlite3_column_int(pStmt, 0)>1 ){ + scrubBackupErr(p, "destination database is not empty - holds %d pages", + sqlite3_column_int(pStmt, 0)); + } + sqlite3_finalize(pStmt); + sqlite3_file_control(p->dbDest, "main", SQLITE_FCNTL_FILE_POINTER, &p->pDest); + if( p->pDest==0 || p->pDest->pMethods==0 ){ + scrubBackupErr(p, "cannot get the destination file handle"); + p->rcErr = SQLITE_ERROR; + } +} + +int sqlite3_scrub_backup( + const char *zSrcFile, /* Source file */ + const char *zDestFile, /* Destination file */ + char **pzErr /* Write error here if non-NULL */ +){ + ScrubState s; + unsigned int i; + u8 *pBuf = 0; + u8 *pData; + + memset(&s, 0, sizeof(s)); + s.zSrcFile = zSrcFile; + s.zDestFile = zDestFile; + + scrubBackupOpenSrc(&s); + scrubBackupOpenDest(&s); + pBuf = scrubBackupAllocPage(&s); + + for(i=1; s.rcErr==0 && i<=s.nPage; i++){ + pData = scrubBackupRead(&s, i, pBuf); + scrubBackupWrite(&s, i, pData); + } + + /* Close the destination database without closing the transaction. If we + ** commit, page zero will be overwritten. */ + sqlite3_close(s.dbDest); + + sqlite3_close(s.dbSrc); + sqlite3_free(s.page1); + if( pzErr ){ + *pzErr = s.zErr; + }else{ + sqlite3_free(s.zErr); + } + return s.rcErr; +} + +#ifdef SCRUB_STANDALONE +/* The main() routine when this utility is run as a stand-alone program */ +int main(int argc, char **argv){ + char *zErr = 0; + int rc; + if( argc!=3 ){ + fprintf(stderr,"Usage: %s SOURCE DESTINATION\n", argv[0]); + exit(1); + } + rc = sqlite3_scrub_backup(argv[1], argv[2], &zErr); + if( rc==SQLITE_NOMEM ){ + fprintf(stderr, "%s: out of memory\n", argv[0]); + exit(1); + } + if( zErr ){ + fprintf(stderr, "%s: %s\n", argv[0], zErr); + sqlite3_free(zErr); + exit(1); + } + return 0; +} +#endif diff --git a/main.mk b/main.mk index 1e791416d8..f200dc2092 100644 --- a/main.mk +++ b/main.mk @@ -488,6 +488,9 @@ sqldiff$(EXE): $(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h $(TCCX) -o sqldiff$(EXE) -DSQLITE_THREADSAFE=0 \ $(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS) $(THREADLIB) +scrub$(EXE): $(TOP)/ext/misc/scrub.c sqlite3.o + $(TCC) -I. -DSCRUB_STANDALONE -o scrub$(EXE) $(TOP)/ext/misc/scrub.c sqlite3.o $(THREADLIB) + srcck1$(EXE): $(TOP)/tool/srcck1.c $(BCC) -o srcck1$(EXE) $(TOP)/tool/srcck1.c diff --git a/manifest b/manifest index 8e5f6ffbdc..919f2e5e0d 100644 --- a/manifest +++ b/manifest @@ -1,8 +1,8 @@ -C Renumber\sinternal\sconstants\sin\sthe\sprintf()\simplemention\sfor\sa\ssmall\nperformance\simprovement. -D 2016-05-05T11:53:12.439 -F Makefile.in 9eda6e1c90d05c199c3ec8a7069b0682ad307657 +C Initial\scheck-in\sof\sthe\s"scrub.exe"\sutility\sprogram\sprototype.\s\sNot\syet\nfully\sfunctional.\s\sIn\sparticular,\sno\sscrubbing\sis\sdone. +D 2016-05-05T17:15:23.301 +F Makefile.in f59e0763ff448719fc1bd25513882b0567286317 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 -F Makefile.msc db82b35aef27f412fef14d8534afc022138bcdfd +F Makefile.msc 306d73e854b1a92ea06e5d1e637faa5c44de53c7 F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7 F VERSION 5d234da9b5dae329fab75ff75884cfe0a9cb3fda F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 @@ -215,6 +215,7 @@ F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342 F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63 F ext/misc/regexp.c a68d25c659bd2d893cd1215667bbf75ecb9dc7d4 F ext/misc/rot13.c 1ac6f95f99b575907b9b09c81a349114cf9be45a +F ext/misc/scrub.c 76a9c795078a65bd021d5b5cd9e4655e7d0306c0 F ext/misc/series.c e11e534ada797d5b816d7e7a93c022306563ca35 F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52 F ext/misc/spellfix.c bf1b922c2750698e9a3d4c50cce6974adb7e93be @@ -302,7 +303,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 -F main.mk a283660f75c3c4b75d8c9d12a40fa38a066eee9d +F main.mk 0fb2b051c9dbcf7f26a8455cf115ae9457448b93 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 @@ -1487,7 +1488,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 71af9ac165ac02272f4886f69bd9ab4770fd7bb6 -R 89655379113728f544ce9a18ad294b0b +P 69d11447f4b1a8c536c3b6573d2a3419da870412 +R 98e582093972832acc05b11fc975c102 +T *branch * scrub-backup +T *sym-scrub-backup * +T -sym-trunk * U drh -Z f65addcdc03169dedb02067b28293d6c +Z e2196386eaeffd3910bcf89f26dcb9bb diff --git a/manifest.uuid b/manifest.uuid index 4a4e62d8ed..6832a682a5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -69d11447f4b1a8c536c3b6573d2a3419da870412 \ No newline at end of file +bdf2ec77d1542d4e9b68218f558710a3efc15823 \ No newline at end of file From 2a9bd02645c6616e877e090d8a2f7c8bfee77809 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 5 May 2016 23:09:57 +0000 Subject: [PATCH 2/5] Finished implementation compiles, but untested. FossilOrigin-Name: aeb88bdf6fe9e5839b1503ab5740b27bc09d4842 --- ext/misc/scrub.c | 286 +++++++++++++++++++++++++++++++++++++++++++++-- manifest | 15 +-- manifest.uuid | 2 +- 3 files changed, 282 insertions(+), 21 deletions(-) diff --git a/ext/misc/scrub.c b/ext/misc/scrub.c index fbf2f8c2b7..424db6baf5 100644 --- a/ext/misc/scrub.c +++ b/ext/misc/scrub.c @@ -57,6 +57,9 @@ typedef struct ScrubState ScrubState; typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; + /* State information for a scrub-and-backup operation */ struct ScrubState { @@ -68,8 +71,9 @@ struct ScrubState { sqlite3_file *pSrc; /* Source file handle */ sqlite3 *dbDest; /* Destination database connection */ sqlite3_file *pDest; /* Destination file handle */ - unsigned int szPage; /* Page size */ - unsigned int nPage; /* Number of pages */ + u32 szPage; /* Page size */ + u32 szUsable; /* Usable bytes on each page */ + u32 nPage; /* Number of pages */ u8 *page1; /* Content of page 1 */ }; @@ -116,7 +120,7 @@ static u8 *scrubBackupRead(ScrubState *p, int pgno, u8 *pBuf){ } /* Write a page to the destination database */ -static void scrubBackupWrite(ScrubState *p, int pgno, u8 *pData){ +static void scrubBackupWrite(ScrubState *p, int pgno, const u8 *pData){ int rc; sqlite3_int64 iOff; if( p->rcErr ) return; @@ -245,33 +249,293 @@ static void scrubBackupOpenDest(ScrubState *p){ } } +/* Read a 32-bit big-endian integer */ +static u32 scrubBackupInt32(const u8 *a){ + u32 v = a[3]; + v += ((u32)a[2])<<8; + v += ((u32)a[1])<<16; + v += ((u32)a[0])<<24; + return v; +} + +/* Read a 16-bit big-endian integer */ +static u32 scrubBackupInt16(const u8 *a){ + return (a[0]<<8) + a[1]; +} + +/* +** Read a varint. Put the value in *pVal and return the number of bytes. +*/ +static int scrubBackupVarint(const u8 *z, sqlite3_int64 *pVal){ + sqlite3_int64 v = 0; + int i; + for(i=0; i<8; i++){ + v = (v<<7) + (z[i]&0x7f); + if( (z[i]&0x80)==0 ){ *pVal = v; return i+1; } + } + v = (v<<8) + (z[i]&0xff); + *pVal = v; + return 9; +} + +/* +** Return the number of bytes in a varint. +*/ +static int scrubBackupVarintSize(const u8 *z){ + int i; + for(i=0; i<8; i++){ + if( (z[i]&0x80)==0 ){ return i+1; } + } + return 9; +} + +/* +** Copy the freelist trunk page given, and all its descendents, +** zeroing out as much as possible in the process. +*/ +static void scrubBackupFreelist(ScrubState *p, int pgno, u32 nFree){ + u8 *a, *aBuf; + u32 n, mx; + + if( p->rcErr ) return; + aBuf = scrubBackupAllocPage(p); + if( aBuf==0 ) return; + + while( pgno && nFree){ + a = scrubBackupRead(p, pgno, aBuf); + if( a==0 ) break; + n = scrubBackupInt32(&a[4]); + mx = p->szUsable/4 - 2; + if( nszPage); + } + scrubBackupWrite(p, iLeaf, aZero); + nFree--; + } +#endif + } + sqlite3_free(aBuf); +} + +/* +** Copy an overflow chain from source to destination. Zero out any +** unused tail at the end of the overflow chain. +*/ +static void scrubBackupOverflow(ScrubState *p, int pgno, u32 nByte){ + u8 *a, *aBuf; + + aBuf = scrubBackupAllocPage(p); + if( aBuf==0 ) return; + while( nByte>0 && pgno!=0 ){ + a = scrubBackupRead(p, pgno, aBuf); + if( a ) break; + if( nByte >= (p->szUsable)-4 ){ + nByte -= (p->szUsable) - 4; + }else{ + u32 x = (p->szUsable - 4) - nByte; + u32 i = p->szUsable - x; + memset(&a[i], 0, x); + nByte = 0; + } + scrubBackupWrite(p, pgno, a); + pgno = scrubBackupInt32(a); + } + sqlite3_free(aBuf); +} + + +/* +** Copy B-Tree page pgno, and all of its children, from source to destination. +** Zero out deleted content during the copy. +*/ +static void scrubBackupBtree(ScrubState *p, int pgno, int iDepth){ + u8 *a; + u32 i, n, pc; + u32 nCell; + u32 nPrefix; + u32 szHdr; + u32 iChild; + u8 *aTop; + u8 *aCell; + u32 x, y; + + + if( p->rcErr ) return; + if( iDepth>50 ){ + scrubBackupErr(p, "corrupt: b-tree too deep at page %d", pgno); + return; + } + if( pgno==1 ){ + a = p->page1; + }else{ + a = scrubBackupRead(p, pgno, 0); + if( a==0 ) return; + } + nPrefix = pgno==1 ? 100 : 0; + aTop = &a[nPrefix]; + szHdr = 8 + 4*(aTop[0]==0x02 || aTop[0]==0x05); + aCell = aTop + szHdr; + nCell = scrubBackupInt16(&aTop[3]); + + /* Zero out the gap between the cell index and the start of the + ** cell content area */ + x = scrubBackupInt16(&aTop[5]); /* First byte of cell content area */ + if( x>p->szUsable ) goto btree_corrupt; + y = szHdr + nPrefix + nCell*2; + if( y>x ) goto btree_corrupt; + if( y(p->szUsable)-4 ) goto btree_corrupt; + n = scrubBackupInt16(&a[pc+2]); + if( pc+n>(p->szUsable) ) goto btree_corrupt; + if( n>4 ) memset(&a[pc+4], 0, n-4); + x = scrubBackupInt16(&a[pc]); + if( x0 ) goto btree_corrupt; + pc = x; + } + + /* Write this one page */ + scrubBackupWrite(p, pgno, a); + + /* Walk the tree and process child pages */ + for(i=0; i p->szUsable-3 ) goto btree_corrupt; + if( aTop[0]==0x05 || aTop[0]==0x02 ){ + if( pc+4 > p->szUsable ) goto btree_corrupt; + iChild = scrubBackupInt32(&a[pc]); + pc += 4; + scrubBackupBtree(p, iChild, iDepth+1); + if( aTop[0]==0x05 ) continue; + } + pc += scrubBackupVarint(&a[pc], &P); + if( pc >= p->szUsable ) goto btree_corrupt; + if( aTop[0]==0x0d ){ + X = p->szUsable - 35; + }else{ + X = ((p->szUsable - 12)*64/255) - 23; + } + if( P<=X ){ + /* All content is local. No overflow */ + continue; + } + M = ((p->szUsable - 12)*32/255)-23; + K = M + ((P-M)%(p->szUsable-4)); + if( aTop[0]==0x0d ){ + pc += scrubBackupVarintSize(&a[pc]); + if( pc > (p->szUsable-4) ) goto btree_corrupt; + } + nLocal = K<=X ? K : M; + if( pc+nLocal > p->szUsable-4 ) goto btree_corrupt; + iChild = scrubBackupInt32(&a[pc+nLocal]); + scrubBackupOverflow(p, iChild, P-nLocal); + } + + /* Walk the right-most tree */ + if( aTop[0]==0x05 || aTop[0]==0x02 ){ + iChild = scrubBackupInt32(&aTop[8]); + scrubBackupBtree(p, iChild, iDepth+1); + } + + /* All done */ + if( pgno>1 ) sqlite3_free(a); + return; + +btree_corrupt: + scrubBackupErr(p, "corruption on page %d of source database", pgno); + if( pgno>1 ) sqlite3_free(a); +} + +/* +** Copy all ptrmap pages from source to destination. +** This routine is only called if the source database is in autovacuum +** or incremental vacuum mode. +*/ +static void scrubBackupPtrmap(ScrubState *p){ + u32 pgno = 2; + u32 J = p->szUsable/5; + u32 iLock = (1073742335/p->szPage)+1; + u8 *a, *pBuf; + if( p->rcErr ) return; + pBuf = scrubBackupAllocPage(p); + if( pBuf==0 ) return; + while( pgno<=p->nPage ){ + a = scrubBackupRead(p, pgno, pBuf); + if( a==0 ) break; + scrubBackupWrite(p, pgno, a); + pgno += J+1; + if( pgno==iLock ) pgno++; + } + sqlite3_free(pBuf); +} + int sqlite3_scrub_backup( const char *zSrcFile, /* Source file */ const char *zDestFile, /* Destination file */ char **pzErr /* Write error here if non-NULL */ ){ ScrubState s; - unsigned int i; - u8 *pBuf = 0; - u8 *pData; + u32 n, i; + sqlite3_stmt *pStmt; memset(&s, 0, sizeof(s)); s.zSrcFile = zSrcFile; s.zDestFile = zDestFile; + /* Open both source and destination databases */ scrubBackupOpenSrc(&s); scrubBackupOpenDest(&s); - pBuf = scrubBackupAllocPage(&s); - for(i=1; s.rcErr==0 && i<=s.nPage; i++){ - pData = scrubBackupRead(&s, i, pBuf); - scrubBackupWrite(&s, i, pData); + /* Read in page 1 */ + s.page1 = scrubBackupRead(&s, 1, 0); + if( s.page1==0 ) goto scrub_abort; + s.szUsable = s.szPage - s.page1[20]; + + /* Copy the freelist */ + n = scrubBackupInt32(&s.page1[36]); + i = scrubBackupInt32(&s.page1[32]); + if( n ) scrubBackupFreelist(&s, i, n); + + /* Copy ptrmap pages */ + n = scrubBackupInt32(&s.page1[52]); + if( n ) scrubBackupPtrmap(&s); + + /* Copy all of the btrees */ + scrubBackupBtree(&s, 1, 0); + pStmt = scrubBackupPrepare(&s, s.dbSrc, + "SELECT rootpage FROM sqlite_master WHERE rootpage IS NOT NULL"); + if( pStmt==0 ) goto scrub_abort; + while( sqlite3_step(pStmt)==SQLITE_ROW ){ + i = (u32)sqlite3_column_int(pStmt, 0); + scrubBackupBtree(&s, i, 0); } + sqlite3_finalize(pStmt); +scrub_abort: /* Close the destination database without closing the transaction. If we ** commit, page zero will be overwritten. */ sqlite3_close(s.dbDest); - sqlite3_close(s.dbSrc); sqlite3_free(s.page1); if( pzErr ){ diff --git a/manifest b/manifest index 919f2e5e0d..fff7842874 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Initial\scheck-in\sof\sthe\s"scrub.exe"\sutility\sprogram\sprototype.\s\sNot\syet\nfully\sfunctional.\s\sIn\sparticular,\sno\sscrubbing\sis\sdone. -D 2016-05-05T17:15:23.301 +C Finished\simplementation\scompiles,\sbut\suntested. +D 2016-05-05T23:09:57.111 F Makefile.in f59e0763ff448719fc1bd25513882b0567286317 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 306d73e854b1a92ea06e5d1e637faa5c44de53c7 @@ -215,7 +215,7 @@ F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342 F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63 F ext/misc/regexp.c a68d25c659bd2d893cd1215667bbf75ecb9dc7d4 F ext/misc/rot13.c 1ac6f95f99b575907b9b09c81a349114cf9be45a -F ext/misc/scrub.c 76a9c795078a65bd021d5b5cd9e4655e7d0306c0 +F ext/misc/scrub.c 831d12a1ba776815eb946ee5a267997842384074 F ext/misc/series.c e11e534ada797d5b816d7e7a93c022306563ca35 F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52 F ext/misc/spellfix.c bf1b922c2750698e9a3d4c50cce6974adb7e93be @@ -1488,10 +1488,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 69d11447f4b1a8c536c3b6573d2a3419da870412 -R 98e582093972832acc05b11fc975c102 -T *branch * scrub-backup -T *sym-scrub-backup * -T -sym-trunk * +P bdf2ec77d1542d4e9b68218f558710a3efc15823 +R 3a64deda8cf94b6b4577af97769133ef U drh -Z e2196386eaeffd3910bcf89f26dcb9bb +Z 668555cf9e940e9598a6c547736364c9 diff --git a/manifest.uuid b/manifest.uuid index 6832a682a5..73eea5130f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bdf2ec77d1542d4e9b68218f558710a3efc15823 \ No newline at end of file +aeb88bdf6fe9e5839b1503ab5740b27bc09d4842 \ No newline at end of file From e04369e29e01d375fa17c206365407e8baaa0109 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 5 May 2016 23:39:30 +0000 Subject: [PATCH 3/5] Working when run against sqlite.fossil. FossilOrigin-Name: b0bd9dd6255be161ea289ba6caa3fbd568128f62 --- ext/misc/scrub.c | 34 ++++++++++++++++++---------------- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/ext/misc/scrub.c b/ext/misc/scrub.c index 424db6baf5..153625abed 100644 --- a/ext/misc/scrub.c +++ b/ext/misc/scrub.c @@ -342,7 +342,7 @@ static void scrubBackupOverflow(ScrubState *p, int pgno, u32 nByte){ if( aBuf==0 ) return; while( nByte>0 && pgno!=0 ){ a = scrubBackupRead(p, pgno, aBuf); - if( a ) break; + if( a==0 ) break; if( nByte >= (p->szUsable)-4 ){ nByte -= (p->szUsable) - 4; }else{ @@ -372,6 +372,7 @@ static void scrubBackupBtree(ScrubState *p, int pgno, int iDepth){ u8 *aTop; u8 *aCell; u32 x, y; + int ln = 0; if( p->rcErr ) return; @@ -394,21 +395,21 @@ static void scrubBackupBtree(ScrubState *p, int pgno, int iDepth){ /* Zero out the gap between the cell index and the start of the ** cell content area */ x = scrubBackupInt16(&aTop[5]); /* First byte of cell content area */ - if( x>p->szUsable ) goto btree_corrupt; + if( x>p->szUsable ){ ln=__LINE__; goto btree_corrupt; } y = szHdr + nPrefix + nCell*2; - if( y>x ) goto btree_corrupt; + if( y>x ){ ln=__LINE__; goto btree_corrupt; } if( y0 && pc(p->szUsable)-4 ) goto btree_corrupt; + if( pc>(p->szUsable)-4 ){ ln=__LINE__; goto btree_corrupt; } n = scrubBackupInt16(&a[pc+2]); - if( pc+n>(p->szUsable) ) goto btree_corrupt; + if( pc+n>(p->szUsable) ){ ln=__LINE__; goto btree_corrupt; } if( n>4 ) memset(&a[pc+4], 0, n-4); x = scrubBackupInt16(&a[pc]); - if( x0 ) goto btree_corrupt; + if( x0 ){ ln=__LINE__; goto btree_corrupt; } pc = x; } @@ -419,18 +420,18 @@ static void scrubBackupBtree(ScrubState *p, int pgno, int iDepth){ for(i=0; i p->szUsable-3 ) goto btree_corrupt; + pc = scrubBackupInt16(&aCell[i*2]); + if( pc <= szHdr ){ ln=__LINE__; goto btree_corrupt; } + if( pc > p->szUsable-3 ){ ln=__LINE__; goto btree_corrupt; } if( aTop[0]==0x05 || aTop[0]==0x02 ){ - if( pc+4 > p->szUsable ) goto btree_corrupt; + if( pc+4 > p->szUsable ){ ln=__LINE__; goto btree_corrupt; } iChild = scrubBackupInt32(&a[pc]); pc += 4; scrubBackupBtree(p, iChild, iDepth+1); if( aTop[0]==0x05 ) continue; } pc += scrubBackupVarint(&a[pc], &P); - if( pc >= p->szUsable ) goto btree_corrupt; + if( pc >= p->szUsable ){ ln=__LINE__; goto btree_corrupt; } if( aTop[0]==0x0d ){ X = p->szUsable - 35; }else{ @@ -444,10 +445,10 @@ static void scrubBackupBtree(ScrubState *p, int pgno, int iDepth){ K = M + ((P-M)%(p->szUsable-4)); if( aTop[0]==0x0d ){ pc += scrubBackupVarintSize(&a[pc]); - if( pc > (p->szUsable-4) ) goto btree_corrupt; + if( pc > (p->szUsable-4) ){ ln=__LINE__; goto btree_corrupt; } } nLocal = K<=X ? K : M; - if( pc+nLocal > p->szUsable-4 ) goto btree_corrupt; + if( pc+nLocal > p->szUsable-4 ){ ln=__LINE__; goto btree_corrupt; } iChild = scrubBackupInt32(&a[pc+nLocal]); scrubBackupOverflow(p, iChild, P-nLocal); } @@ -463,7 +464,8 @@ static void scrubBackupBtree(ScrubState *p, int pgno, int iDepth){ return; btree_corrupt: - scrubBackupErr(p, "corruption on page %d of source database", pgno); + scrubBackupErr(p, "corruption on page %d of source database (errid=%d)", + pgno, ln); if( pgno>1 ) sqlite3_free(a); } @@ -524,7 +526,7 @@ int sqlite3_scrub_backup( /* Copy all of the btrees */ scrubBackupBtree(&s, 1, 0); pStmt = scrubBackupPrepare(&s, s.dbSrc, - "SELECT rootpage FROM sqlite_master WHERE rootpage IS NOT NULL"); + "SELECT rootpage FROM sqlite_master WHERE coalesce(rootpage,0)>0"); if( pStmt==0 ) goto scrub_abort; while( sqlite3_step(pStmt)==SQLITE_ROW ){ i = (u32)sqlite3_column_int(pStmt, 0); diff --git a/manifest b/manifest index fff7842874..3ded627744 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Finished\simplementation\scompiles,\sbut\suntested. -D 2016-05-05T23:09:57.111 +C Working\swhen\srun\sagainst\ssqlite.fossil. +D 2016-05-05T23:39:30.803 F Makefile.in f59e0763ff448719fc1bd25513882b0567286317 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 306d73e854b1a92ea06e5d1e637faa5c44de53c7 @@ -215,7 +215,7 @@ F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342 F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63 F ext/misc/regexp.c a68d25c659bd2d893cd1215667bbf75ecb9dc7d4 F ext/misc/rot13.c 1ac6f95f99b575907b9b09c81a349114cf9be45a -F ext/misc/scrub.c 831d12a1ba776815eb946ee5a267997842384074 +F ext/misc/scrub.c 548a116cb0b4d8705eac392ab13d28fe2aebefe6 F ext/misc/series.c e11e534ada797d5b816d7e7a93c022306563ca35 F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52 F ext/misc/spellfix.c bf1b922c2750698e9a3d4c50cce6974adb7e93be @@ -1488,7 +1488,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P bdf2ec77d1542d4e9b68218f558710a3efc15823 -R 3a64deda8cf94b6b4577af97769133ef +P aeb88bdf6fe9e5839b1503ab5740b27bc09d4842 +R 6bcd08fd9f0774891e37784b87170024 U drh -Z 668555cf9e940e9598a6c547736364c9 +Z cbd8d4738f2e299be1c42951defbac8d diff --git a/manifest.uuid b/manifest.uuid index 73eea5130f..fee828267f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -aeb88bdf6fe9e5839b1503ab5740b27bc09d4842 \ No newline at end of file +b0bd9dd6255be161ea289ba6caa3fbd568128f62 \ No newline at end of file From 8271630e81940729a34dd614d09a23851b078a42 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 5 May 2016 23:59:22 +0000 Subject: [PATCH 4/5] Additional error reporting. Open the source database read/write so that it can delete the WAL file when done. FossilOrigin-Name: d2efd3c176f0eba2d78105f0bb3161db38bd4bab --- ext/misc/scrub.c | 17 ++++++++++++++++- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/ext/misc/scrub.c b/ext/misc/scrub.c index 153625abed..12adcd7bd9 100644 --- a/ext/misc/scrub.c +++ b/ext/misc/scrub.c @@ -157,7 +157,7 @@ static void scrubBackupOpenSrc(ScrubState *p){ int rc; /* Open the source database file */ p->rcErr = sqlite3_open_v2(p->zSrcFile, &p->dbSrc, - SQLITE_OPEN_READONLY | + SQLITE_OPEN_READWRITE | SQLITE_OPEN_URI | SQLITE_OPEN_PRIVATECACHE, 0); if( p->rcErr ){ scrubBackupErr(p, "cannot open source database: %s", @@ -538,6 +538,9 @@ scrub_abort: /* Close the destination database without closing the transaction. If we ** commit, page zero will be overwritten. */ sqlite3_close(s.dbDest); + + /* But do close out the read-transaction on the source database */ + sqlite3_exec(s.dbSrc, "COMMIT;", 0, 0, 0); sqlite3_close(s.dbSrc); sqlite3_free(s.page1); if( pzErr ){ @@ -549,6 +552,17 @@ scrub_abort: } #ifdef SCRUB_STANDALONE +/* Error and warning log */ +static void errorLogCallback(void *pNotUsed, int iErr, const char *zMsg){ + const char *zType; + switch( iErr&0xff ){ + case SQLITE_WARNING: zType = "WARNING"; break; + case SQLITE_NOTICE: zType = "NOTICE"; break; + default: zType = "ERROR"; break; + } + fprintf(stderr, "%s: %s\n", zType, zMsg); +} + /* The main() routine when this utility is run as a stand-alone program */ int main(int argc, char **argv){ char *zErr = 0; @@ -557,6 +571,7 @@ int main(int argc, char **argv){ fprintf(stderr,"Usage: %s SOURCE DESTINATION\n", argv[0]); exit(1); } + sqlite3_config(SQLITE_CONFIG_LOG, errorLogCallback, 0); rc = sqlite3_scrub_backup(argv[1], argv[2], &zErr); if( rc==SQLITE_NOMEM ){ fprintf(stderr, "%s: out of memory\n", argv[0]); diff --git a/manifest b/manifest index 3ded627744..0f9b581c85 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Working\swhen\srun\sagainst\ssqlite.fossil. -D 2016-05-05T23:39:30.803 +C Additional\serror\sreporting.\s\sOpen\sthe\ssource\sdatabase\sread/write\sso\sthat\nit\scan\sdelete\sthe\sWAL\sfile\swhen\sdone. +D 2016-05-05T23:59:22.078 F Makefile.in f59e0763ff448719fc1bd25513882b0567286317 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 306d73e854b1a92ea06e5d1e637faa5c44de53c7 @@ -215,7 +215,7 @@ F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342 F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63 F ext/misc/regexp.c a68d25c659bd2d893cd1215667bbf75ecb9dc7d4 F ext/misc/rot13.c 1ac6f95f99b575907b9b09c81a349114cf9be45a -F ext/misc/scrub.c 548a116cb0b4d8705eac392ab13d28fe2aebefe6 +F ext/misc/scrub.c a3380a5ff0698f88a56a23c999ee5e53768ac3ae F ext/misc/series.c e11e534ada797d5b816d7e7a93c022306563ca35 F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52 F ext/misc/spellfix.c bf1b922c2750698e9a3d4c50cce6974adb7e93be @@ -1488,7 +1488,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P aeb88bdf6fe9e5839b1503ab5740b27bc09d4842 -R 6bcd08fd9f0774891e37784b87170024 +P b0bd9dd6255be161ea289ba6caa3fbd568128f62 +R dbc8e27f8b8809d87c6c9be80d710e34 U drh -Z cbd8d4738f2e299be1c42951defbac8d +Z 950b8c8bc6ebdccefe511e872cba5384 diff --git a/manifest.uuid b/manifest.uuid index fee828267f..46b409542f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b0bd9dd6255be161ea289ba6caa3fbd568128f62 \ No newline at end of file +d2efd3c176f0eba2d78105f0bb3161db38bd4bab \ No newline at end of file From 0d2c3a0e11e47b1251a41c5f297cd336e6ac89a1 Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 17 May 2016 21:17:51 +0000 Subject: [PATCH 5/5] Enhance the scrub utility program so that it does a FULL checkpoint prior to starting the backup, to ensure that the database file content matches what needs to be backed up without having to look at the WAL file. FossilOrigin-Name: ab1c5ce50f139070d7a322f43132a93c8af2ed68 --- ext/misc/scrub.c | 9 ++++++++- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/ext/misc/scrub.c b/ext/misc/scrub.c index 12adcd7bd9..1d21fc5389 100644 --- a/ext/misc/scrub.c +++ b/ext/misc/scrub.c @@ -164,13 +164,20 @@ static void scrubBackupOpenSrc(ScrubState *p){ sqlite3_errmsg(p->dbSrc)); return; } - p->rcErr = sqlite3_exec(p->dbSrc, "BEGIN", 0, 0, 0); + p->rcErr = sqlite3_exec(p->dbSrc, "SELECT 1 FROM sqlite_master; BEGIN;", + 0, 0, 0); if( p->rcErr ){ scrubBackupErr(p, "cannot start a read transaction on the source database: %s", sqlite3_errmsg(p->dbSrc)); return; } + rc = sqlite3_wal_checkpoint_v2(p->dbSrc, "main", SQLITE_CHECKPOINT_FULL, + 0, 0); + if( rc ){ + scrubBackupErr(p, "cannot checkpoint the source database"); + return; + } pStmt = scrubBackupPrepare(p, p->dbSrc, "PRAGMA page_size"); if( pStmt==0 ) return; rc = sqlite3_step(pStmt); diff --git a/manifest b/manifest index 9b5c6f0b2e..8af4221406 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sthe\slatest\schanges\sfrom\strunk. -D 2016-05-17T17:11:21.184 +C Enhance\sthe\sscrub\sutility\sprogram\sso\sthat\sit\sdoes\sa\sFULL\scheckpoint\sprior\sto\nstarting\sthe\sbackup,\sto\sensure\sthat\sthe\sdatabase\sfile\scontent\smatches\swhat\sneeds\nto\sbe\sbacked\sup\swithout\shaving\sto\slook\sat\sthe\sWAL\sfile. +D 2016-05-17T21:17:51.530 F Makefile.in f59e0763ff448719fc1bd25513882b0567286317 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 306d73e854b1a92ea06e5d1e637faa5c44de53c7 @@ -215,7 +215,7 @@ F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342 F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63 F ext/misc/regexp.c a68d25c659bd2d893cd1215667bbf75ecb9dc7d4 F ext/misc/rot13.c 1ac6f95f99b575907b9b09c81a349114cf9be45a -F ext/misc/scrub.c a3380a5ff0698f88a56a23c999ee5e53768ac3ae +F ext/misc/scrub.c ea0903701e3ac02b4466ce9cffd0325c7ddbfcf0 F ext/misc/series.c e11e534ada797d5b816d7e7a93c022306563ca35 F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52 F ext/misc/spellfix.c bf1b922c2750698e9a3d4c50cce6974adb7e93be @@ -1489,7 +1489,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P d2efd3c176f0eba2d78105f0bb3161db38bd4bab 995c084bde44e678facc5f5d95a2335ce61e57b0 -R c8a04e46ce574c9cb7d04e9b64f57621 +P 5021dfe1f3f723a5938d547a0308f1d63103702d +R a2f9971160f157164223781dabeb5e95 U drh -Z 19bb7af983a6a8b12079243584bed878 +Z 2fde01a79f5811045a1dffd48050935f diff --git a/manifest.uuid b/manifest.uuid index ba749ac7fb..30152d3b67 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5021dfe1f3f723a5938d547a0308f1d63103702d \ No newline at end of file +ab1c5ce50f139070d7a322f43132a93c8af2ed68 \ No newline at end of file