Remove the untested and undocumented "scrub" utility program. It was written
in 2016, but made obsolete by the [VACUUM INTO] command in 2019. FossilOrigin-Name: 27d67fb175e839f6e7450f54166b52e0a6137c075758511da32fd82c57c310c4
This commit is contained in:
@@ -1897,9 +1897,6 @@ RSYNC_OPT = \
|
|||||||
sqlite3_rsync.exe: $(RSYNC_SRC) $(LIBRESOBJS)
|
sqlite3_rsync.exe: $(RSYNC_SRC) $(LIBRESOBJS)
|
||||||
$(LTLINK) $(RSYNC_OPT) $(NO_WARN) $(RSYNC_SRC) /link $(LDFLAGS) $(LTLINKOPTS) $(LIBRESOBJS)
|
$(LTLINK) $(RSYNC_OPT) $(NO_WARN) $(RSYNC_SRC) /link $(LDFLAGS) $(LTLINKOPTS) $(LIBRESOBJS)
|
||||||
|
|
||||||
scrub.exe: $(TOP)\ext\misc\scrub.c $(SQLITE3C) $(SQLITE3H)
|
|
||||||
$(LTLINK) $(NO_WARN) -DSCRUB_STANDALONE=1 $(TOP)\ext\misc\scrub.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
|
||||||
|
|
||||||
srcck1.exe: $(TOP)\tool\srcck1.c
|
srcck1.exe: $(TOP)\tool\srcck1.c
|
||||||
$(BCC) $(NO_WARN) -Fe$@ $(TOP)\tool\srcck1.c
|
$(BCC) $(NO_WARN) -Fe$@ $(TOP)\tool\srcck1.c
|
||||||
|
|
||||||
|
|||||||
@@ -1,610 +0,0 @@
|
|||||||
/*
|
|
||||||
** 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 <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
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 {
|
|
||||||
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 */
|
|
||||||
u32 szPage; /* Page size */
|
|
||||||
u32 szUsable; /* Usable bytes on each page */
|
|
||||||
u32 nPage; /* Number of pages */
|
|
||||||
u32 iLastPage; /* Page number of last page written so far*/
|
|
||||||
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_malloc64( 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, const 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;
|
|
||||||
}
|
|
||||||
if( (u32)pgno>p->iLastPage ) p->iLastPage = pgno;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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_READWRITE |
|
|
||||||
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, "SELECT 1 FROM sqlite_schema; 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);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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( n<mx ){
|
|
||||||
memset(&a[n*4+8], 0, 4*(mx-n));
|
|
||||||
}
|
|
||||||
scrubBackupWrite(p, pgno, a);
|
|
||||||
pgno = scrubBackupInt32(a);
|
|
||||||
#if 0
|
|
||||||
/* There is really no point in copying the freelist leaf pages.
|
|
||||||
** Simply leave them uninitialized in the destination database. The
|
|
||||||
** OS filesystem should zero those pages for us automatically.
|
|
||||||
*/
|
|
||||||
for(i=0; i<n && nFree; i++){
|
|
||||||
u32 iLeaf = scrubBackupInt32(&a[i*4+8]);
|
|
||||||
if( aZero==0 ){
|
|
||||||
aZero = scrubBackupAllocPage(p);
|
|
||||||
if( aZero==0 ){ pgno = 0; break; }
|
|
||||||
memset(aZero, 0, p->szPage);
|
|
||||||
}
|
|
||||||
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==0 ) 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;
|
|
||||||
int ln = 0;
|
|
||||||
|
|
||||||
|
|
||||||
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 ){ ln=__LINE__; goto btree_corrupt; }
|
|
||||||
y = szHdr + nPrefix + nCell*2;
|
|
||||||
if( y>x ){ ln=__LINE__; goto btree_corrupt; }
|
|
||||||
if( y<x ) memset(a+y, 0, x-y); /* Zero the gap */
|
|
||||||
|
|
||||||
/* Zero out all the free blocks */
|
|
||||||
pc = scrubBackupInt16(&aTop[1]);
|
|
||||||
if( pc>0 && pc<x ){ ln=__LINE__; goto btree_corrupt; }
|
|
||||||
while( pc ){
|
|
||||||
if( pc>(p->szUsable)-4 ){ ln=__LINE__; goto btree_corrupt; }
|
|
||||||
n = scrubBackupInt16(&a[pc+2]);
|
|
||||||
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( x<pc+4 && x>0 ){ ln=__LINE__; goto btree_corrupt; }
|
|
||||||
pc = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write this one page */
|
|
||||||
scrubBackupWrite(p, pgno, a);
|
|
||||||
|
|
||||||
/* Walk the tree and process child pages */
|
|
||||||
for(i=0; i<nCell; i++){
|
|
||||||
u32 X, M, K, nLocal;
|
|
||||||
sqlite3_int64 P;
|
|
||||||
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 ){ 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 ){ ln=__LINE__; 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) ){ ln=__LINE__; goto btree_corrupt; }
|
|
||||||
}
|
|
||||||
nLocal = K<=X ? K : M;
|
|
||||||
if( pc+nLocal > p->szUsable-4 ){ ln=__LINE__; goto btree_corrupt; }
|
|
||||||
iChild = scrubBackupInt32(&a[pc+nLocal]);
|
|
||||||
scrubBackupOverflow(p, iChild, (u32)(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 (errid=%d)",
|
|
||||||
pgno, ln);
|
|
||||||
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;
|
|
||||||
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);
|
|
||||||
|
|
||||||
/* 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_schema WHERE coalesce(rootpage,0)>0");
|
|
||||||
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);
|
|
||||||
|
|
||||||
/* If the last page of the input db file is a free-list leaf, then the
|
|
||||||
** backup file on disk is still smaller than the size indicated within
|
|
||||||
** the database header. In this case, write a page of zeroes to the
|
|
||||||
** last page of the backup database so that SQLite does not mistakenly
|
|
||||||
** think the db is corrupt. */
|
|
||||||
if( s.iLastPage<s.nPage ){
|
|
||||||
u8 *aZero = scrubBackupAllocPage(&s);
|
|
||||||
if( aZero ){
|
|
||||||
memset(aZero, 0, s.szPage);
|
|
||||||
scrubBackupWrite(&s, s.nPage, aZero);
|
|
||||||
sqlite3_free(aZero);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ){
|
|
||||||
*pzErr = s.zErr;
|
|
||||||
}else{
|
|
||||||
sqlite3_free(s.zErr);
|
|
||||||
}
|
|
||||||
return s.rcErr;
|
|
||||||
}
|
|
||||||
|
|
||||||
#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;
|
|
||||||
int rc;
|
|
||||||
if( argc!=3 ){
|
|
||||||
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]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if( zErr ){
|
|
||||||
fprintf(stderr, "%s: %s\n", argv[0], zErr);
|
|
||||||
sqlite3_free(zErr);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -2236,11 +2236,6 @@ install: install-man1
|
|||||||
install-pc: sqlite3.pc $(install-dir.pkgconfig)
|
install-pc: sqlite3.pc $(install-dir.pkgconfig)
|
||||||
$(INSTALL.noexec) sqlite3.pc "$(install-dir.pkgconfig)"
|
$(INSTALL.noexec) sqlite3.pc "$(install-dir.pkgconfig)"
|
||||||
|
|
||||||
scrub$(T.exe): $(TOP)/ext/misc/scrub.c sqlite3.o
|
|
||||||
$(T.link) -o $@ -I. -DSCRUB_STANDALONE \
|
|
||||||
$(TOP)/ext/misc/scrub.c sqlite3.o $(LDFLAGS.libsqlite3)
|
|
||||||
xbin: scrub$(T.exe)
|
|
||||||
|
|
||||||
srcck1$(B.exe): $(TOP)/tool/srcck1.c
|
srcck1$(B.exe): $(TOP)/tool/srcck1.c
|
||||||
$(B.cc) -o srcck1$(B.exe) $(TOP)/tool/srcck1.c
|
$(B.cc) -o srcck1$(B.exe) $(TOP)/tool/srcck1.c
|
||||||
xbin: srcck1$(B.exe)
|
xbin: srcck1$(B.exe)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
C Be\ssure\sto\sdo\sfloating-point\sto\stext\sconversions\sin\sJSON\sto\sthe\sfull\n17-digits.
|
C Remove\sthe\suntested\sand\sundocumented\s"scrub"\sutility\sprogram.\s\sIt\swas\swritten\nin\s2016,\sbut\smade\sobsolete\sby\sthe\s[VACUUM\sINTO]\scommand\sin\s2019.
|
||||||
D 2026-05-29T23:14:22.751
|
D 2026-05-30T10:09:33.845
|
||||||
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
|
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
|
||||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||||
@@ -7,7 +7,7 @@ F AGENTS.md 44ddfb38ef23e277888bd70ae378e6cbf5f5cab0e2a4420bd21d89b6caff7a4a
|
|||||||
F LICENSE.md 6bc480fc673fb4acbc4094e77edb326267dd460162d7723c7f30bee2d3d9e97d
|
F LICENSE.md 6bc480fc673fb4acbc4094e77edb326267dd460162d7723c7f30bee2d3d9e97d
|
||||||
F Makefile.in 5fda086f33b144da08119255da1d2557f983d0764a13707f05acf0159fd89ba5
|
F Makefile.in 5fda086f33b144da08119255da1d2557f983d0764a13707f05acf0159fd89ba5
|
||||||
F Makefile.linux-generic bd3e3cacd369821a6241d4ea1967395c962dfe3057e38cb0a435cee0e8b789d0
|
F Makefile.linux-generic bd3e3cacd369821a6241d4ea1967395c962dfe3057e38cb0a435cee0e8b789d0
|
||||||
F Makefile.msc 3b2129c90e15ea734682c8769b567012200ac30d7ff1e73c4717e23edbe1bb4b
|
F Makefile.msc 46ff6560f0046d92017ccafc91a3cd6cb2b54fbd25c56b2b6890cd84ed268159
|
||||||
F README.md f00091ffb5c6379b783afb57ca5f443a5742e119082f37c407f64a5e16c56346
|
F README.md f00091ffb5c6379b783afb57ca5f443a5742e119082f37c407f64a5e16c56346
|
||||||
F VERSION 99cf3be5f13d091183e4314b7fc2e0c0e69accfbe64608b45a313338bbdd7b62
|
F VERSION 99cf3be5f13d091183e4314b7fc2e0c0e69accfbe64608b45a313338bbdd7b62
|
||||||
F art/icon-243x273.gif 9750b734f82fdb3dc43127753d5e6fbf3b62c9f4e136c2fbf573b2f57ea87af5
|
F art/icon-243x273.gif 9750b734f82fdb3dc43127753d5e6fbf3b62c9f4e136c2fbf573b2f57ea87af5
|
||||||
@@ -395,7 +395,6 @@ F ext/misc/randomjson.c ef835fc64289e76ac4873b85fe12f9463a036168d7683cf2b773e36e
|
|||||||
F ext/misc/regexp.c 378e6e84516952a4b9f3f4df88927f20e6538e2609f55b773ed78899dab8206e
|
F ext/misc/regexp.c 378e6e84516952a4b9f3f4df88927f20e6538e2609f55b773ed78899dab8206e
|
||||||
F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c
|
F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c
|
||||||
F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d38556c
|
F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d38556c
|
||||||
F ext/misc/scrub.c df54e202887e480bf9cd73168c9ac829e3b0211381b68b9809e0cb5bc1bdc2cf
|
|
||||||
F ext/misc/series.c 496f43bac9bad2ee2cea63fb5212036f30ad3003b4cd317d5c2d6f3ad7c7d264
|
F ext/misc/series.c 496f43bac9bad2ee2cea63fb5212036f30ad3003b4cd317d5c2d6f3ad7c7d264
|
||||||
F ext/misc/sha1.c 9a11826db885e8afd997c0a1b28bb799a43e462ef770ac33f19e744887c9c6fa
|
F ext/misc/sha1.c 9a11826db885e8afd997c0a1b28bb799a43e462ef770ac33f19e744887c9c6fa
|
||||||
F ext/misc/shathree.c fd22d70620f86a0467acfdd3acd8435d5cb54eb1e2d9ff36ae44e389826993df
|
F ext/misc/shathree.c fd22d70620f86a0467acfdd3acd8435d5cb54eb1e2d9ff36ae44e389826993df
|
||||||
@@ -660,7 +659,7 @@ F ext/wasm/tests/opfs/sahpool/index.html be736567fd92d3ecb9754c145755037cbbd2bca
|
|||||||
F ext/wasm/tests/opfs/sahpool/sahpool-pausing.js f264925cfc82155de38cecb3d204c36e0f6991460fff0cb7c15079454679a4e2
|
F ext/wasm/tests/opfs/sahpool/sahpool-pausing.js f264925cfc82155de38cecb3d204c36e0f6991460fff0cb7c15079454679a4e2
|
||||||
F ext/wasm/tests/opfs/sahpool/sahpool-worker.js bd25a43fc2ab2d1bafd8f2854ad3943ef673f7c3be03e95ecf1612ff6e8e2a61
|
F ext/wasm/tests/opfs/sahpool/sahpool-worker.js bd25a43fc2ab2d1bafd8f2854ad3943ef673f7c3be03e95ecf1612ff6e8e2a61
|
||||||
F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0
|
F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0
|
||||||
F main.mk 0bbfd0c3d2d3ac908b2a6ab4017bd650be0e694291b7f97490877b7c28a147e4
|
F main.mk 77e33a16a795abd4a07a55f7eb2e11136326998db597b67c8655d5d2d9b98054
|
||||||
F make.bat a136fd0b1c93e89854a86d5f4edcf0386d211e5d5ec2434480f6eea436c7420c
|
F make.bat a136fd0b1c93e89854a86d5f4edcf0386d211e5d5ec2434480f6eea436c7420c
|
||||||
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
|
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
|
||||||
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
|
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
|
||||||
@@ -2208,8 +2207,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
|
|||||||
F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
|
F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
|
||||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||||
F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
|
F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
|
||||||
P c7839f7a1749b38f8a36c06d93a7b59095e91ce753e8dd020de273ca8f73a239
|
P 61a776650cdc3fe632bdb1dc39f50f542b00a9e5b468180eb54b69750d0cd70c
|
||||||
R 98e56ce237be73746fac763491f7390f
|
R 9c2fc9974fe8017282e58f1c910cfd7a
|
||||||
U drh
|
U drh
|
||||||
Z 387ede1c1d94fbfd8f5814bf5cc8fe24
|
Z 51c91ccc61b1424ccb83e3d868932c40
|
||||||
# Remove this line to create a well-formed Fossil manifest.
|
# Remove this line to create a well-formed Fossil manifest.
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
61a776650cdc3fe632bdb1dc39f50f542b00a9e5b468180eb54b69750d0cd70c
|
27d67fb175e839f6e7450f54166b52e0a6137c075758511da32fd82c57c310c4
|
||||||
|
|||||||
Reference in New Issue
Block a user