123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- /*
- ** 2017 October 11
- **
- ** 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 module exports a single C function:
- **
- ** int sqlite3_check_freelist(sqlite3 *db, const char *zDb);
- **
- ** This function checks the free-list in database zDb (one of "main",
- ** "temp", etc.) and reports any errors by invoking the sqlite3_log()
- ** function. It returns SQLITE_OK if successful, or an SQLite error
- ** code otherwise. It is not an error if the free-list is corrupted but
- ** no IO or OOM errors occur.
- **
- ** If this file is compiled and loaded as an SQLite loadable extension,
- ** it adds an SQL function "checkfreelist" to the database handle, to
- ** be invoked as follows:
- **
- ** SELECT checkfreelist(<database-name>);
- **
- ** This function performs the same checks as sqlite3_check_freelist(),
- ** except that it returns all error messages as a single text value,
- ** separated by newline characters. If the freelist is not corrupted
- ** in any way, an empty string is returned.
- **
- ** To compile this module for use as an SQLite loadable extension:
- **
- ** gcc -Os -fPIC -shared checkfreelist.c -o checkfreelist.so
- */
- #include "sqlite3ext.h"
- SQLITE_EXTENSION_INIT1
- #ifndef SQLITE_AMALGAMATION
- # include <string.h>
- # include <stdio.h>
- # include <stdlib.h>
- # include <assert.h>
- # if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
- # define SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS 1
- # endif
- # if defined(SQLITE_OMIT_AUXILIARY_SAFETY_CHECKS)
- # define ALWAYS(X) (1)
- # define NEVER(X) (0)
- # elif !defined(NDEBUG)
- # define ALWAYS(X) ((X)?1:(assert(0),0))
- # define NEVER(X) ((X)?(assert(0),1):0)
- # else
- # define ALWAYS(X) (X)
- # define NEVER(X) (X)
- # endif
- typedef unsigned char u8;
- typedef unsigned short u16;
- typedef unsigned int u32;
- #define get4byte(x) ( \
- ((u32)((x)[0])<<24) + \
- ((u32)((x)[1])<<16) + \
- ((u32)((x)[2])<<8) + \
- ((u32)((x)[3])) \
- )
- #endif
- /*
- ** Execute a single PRAGMA statement and return the integer value returned
- ** via output parameter (*pnOut).
- **
- ** The SQL statement passed as the third argument should be a printf-style
- ** format string containing a single "%s" which will be replace by the
- ** value passed as the second argument. e.g.
- **
- ** sqlGetInteger(db, "main", "PRAGMA %s.page_count", pnOut)
- **
- ** executes "PRAGMA main.page_count" and stores the results in (*pnOut).
- */
- static int sqlGetInteger(
- sqlite3 *db, /* Database handle */
- const char *zDb, /* Database name ("main", "temp" etc.) */
- const char *zFmt, /* SQL statement format */
- u32 *pnOut /* OUT: Integer value */
- ){
- int rc, rc2;
- char *zSql;
- sqlite3_stmt *pStmt = 0;
- int bOk = 0;
- zSql = sqlite3_mprintf(zFmt, zDb);
- if( zSql==0 ){
- rc = SQLITE_NOMEM;
- }else{
- rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
- sqlite3_free(zSql);
- }
- if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
- *pnOut = (u32)sqlite3_column_int(pStmt, 0);
- bOk = 1;
- }
- rc2 = sqlite3_finalize(pStmt);
- if( rc==SQLITE_OK ) rc = rc2;
- if( rc==SQLITE_OK && bOk==0 ) rc = SQLITE_ERROR;
- return rc;
- }
- /*
- ** Argument zFmt must be a printf-style format string and must be
- ** followed by its required arguments. If argument pzOut is NULL,
- ** then the results of printf()ing the format string are passed to
- ** sqlite3_log(). Otherwise, they are appended to the string
- ** at (*pzOut).
- */
- static int checkFreelistError(char **pzOut, const char *zFmt, ...){
- int rc = SQLITE_OK;
- char *zErr = 0;
- va_list ap;
- va_start(ap, zFmt);
- zErr = sqlite3_vmprintf(zFmt, ap);
- if( zErr==0 ){
- rc = SQLITE_NOMEM;
- }else{
- if( pzOut ){
- *pzOut = sqlite3_mprintf("%s%z%s", *pzOut?"\n":"", *pzOut, zErr);
- if( *pzOut==0 ) rc = SQLITE_NOMEM;
- }else{
- sqlite3_log(SQLITE_ERROR, "checkfreelist: %s", zErr);
- }
- sqlite3_free(zErr);
- }
- va_end(ap);
- return rc;
- }
- static int checkFreelist(
- sqlite3 *db,
- const char *zDb,
- char **pzOut
- ){
- /* This query returns one row for each page on the free list. Each row has
- ** two columns - the page number and page content. */
- const char *zTrunk =
- "WITH freelist_trunk(i, d, n) AS ("
- "SELECT 1, NULL, sqlite_readint32(data, 32) "
- "FROM sqlite_dbpage(:1) WHERE pgno=1 "
- "UNION ALL "
- "SELECT n, data, sqlite_readint32(data) "
- "FROM freelist_trunk, sqlite_dbpage(:1) WHERE pgno=n "
- ")"
- "SELECT i, d FROM freelist_trunk WHERE i!=1;";
- int rc, rc2; /* Return code */
- sqlite3_stmt *pTrunk = 0; /* Compilation of zTrunk */
- u32 nPage = 0; /* Number of pages in db */
- u32 nExpected = 0; /* Expected number of free pages */
- u32 nFree = 0; /* Number of pages on free list */
- if( zDb==0 ) zDb = "main";
- if( (rc = sqlGetInteger(db, zDb, "PRAGMA %s.page_count", &nPage))
- || (rc = sqlGetInteger(db, zDb, "PRAGMA %s.freelist_count", &nExpected))
- ){
- return rc;
- }
- rc = sqlite3_prepare_v2(db, zTrunk, -1, &pTrunk, 0);
- if( rc!=SQLITE_OK ) return rc;
- sqlite3_bind_text(pTrunk, 1, zDb, -1, SQLITE_STATIC);
- while( rc==SQLITE_OK && sqlite3_step(pTrunk)==SQLITE_ROW ){
- u32 i;
- u32 iTrunk = (u32)sqlite3_column_int(pTrunk, 0);
- const u8 *aData = (const u8*)sqlite3_column_blob(pTrunk, 1);
- u32 nData = (u32)sqlite3_column_bytes(pTrunk, 1);
- u32 iNext = get4byte(&aData[0]);
- u32 nLeaf = get4byte(&aData[4]);
- if( nLeaf>((nData/4)-2-6) ){
- rc = checkFreelistError(pzOut,
- "leaf count out of range (%d) on trunk page %d",
- (int)nLeaf, (int)iTrunk
- );
- nLeaf = (nData/4) - 2 - 6;
- }
- nFree += 1+nLeaf;
- if( iNext>nPage ){
- rc = checkFreelistError(pzOut,
- "trunk page %d is out of range", (int)iNext
- );
- }
- for(i=0; rc==SQLITE_OK && i<nLeaf; i++){
- u32 iLeaf = get4byte(&aData[8 + 4*i]);
- if( iLeaf==0 || iLeaf>nPage ){
- rc = checkFreelistError(pzOut,
- "leaf page %d is out of range (child %d of trunk page %d)",
- (int)iLeaf, (int)i, (int)iTrunk
- );
- }
- }
- }
- if( rc==SQLITE_OK && nFree!=nExpected ){
- rc = checkFreelistError(pzOut,
- "free-list count mismatch: actual=%d header=%d",
- (int)nFree, (int)nExpected
- );
- }
- rc2 = sqlite3_finalize(pTrunk);
- if( rc==SQLITE_OK ) rc = rc2;
- return rc;
- }
- int sqlite3_check_freelist(sqlite3 *db, const char *zDb){
- return checkFreelist(db, zDb, 0);
- }
- static void checkfreelist_function(
- sqlite3_context *pCtx,
- int nArg,
- sqlite3_value **apArg
- ){
- const char *zDb;
- int rc;
- char *zOut = 0;
- sqlite3 *db = sqlite3_context_db_handle(pCtx);
- assert( nArg==1 );
- zDb = (const char*)sqlite3_value_text(apArg[0]);
- rc = checkFreelist(db, zDb, &zOut);
- if( rc==SQLITE_OK ){
- sqlite3_result_text(pCtx, zOut?zOut:"ok", -1, SQLITE_TRANSIENT);
- }else{
- sqlite3_result_error_code(pCtx, rc);
- }
- sqlite3_free(zOut);
- }
- /*
- ** An SQL function invoked as follows:
- **
- ** sqlite_readint32(BLOB) -- Decode 32-bit integer from start of blob
- */
- static void readint_function(
- sqlite3_context *pCtx,
- int nArg,
- sqlite3_value **apArg
- ){
- const u8 *zBlob;
- int nBlob;
- int iOff = 0;
- u32 iRet = 0;
- if( nArg!=1 && nArg!=2 ){
- sqlite3_result_error(
- pCtx, "wrong number of arguments to function sqlite_readint32()", -1
- );
- return;
- }
- if( nArg==2 ){
- iOff = sqlite3_value_int(apArg[1]);
- }
- zBlob = sqlite3_value_blob(apArg[0]);
- nBlob = sqlite3_value_bytes(apArg[0]);
- if( nBlob>=(iOff+4) ){
- iRet = get4byte(&zBlob[iOff]);
- }
- sqlite3_result_int64(pCtx, (sqlite3_int64)iRet);
- }
- /*
- ** Register the SQL functions.
- */
- static int cflRegister(sqlite3 *db){
- int rc = sqlite3_create_function(
- db, "sqlite_readint32", -1, SQLITE_UTF8, 0, readint_function, 0, 0
- );
- if( rc!=SQLITE_OK ) return rc;
- rc = sqlite3_create_function(
- db, "checkfreelist", 1, SQLITE_UTF8, 0, checkfreelist_function, 0, 0
- );
- return rc;
- }
- /*
- ** Extension load function.
- */
- #ifdef _WIN32
- __declspec(dllexport)
- #endif
- int sqlite3_checkfreelist_init(
- sqlite3 *db,
- char **pzErrMsg,
- const sqlite3_api_routines *pApi
- ){
- SQLITE_EXTENSION_INIT2(pApi);
- return cflRegister(db);
- }
|