#if defined(SQLITE_AMALGAMATION) && !defined(SQLITE_CKSUMVFS_STATIC)
# define SQLITE_CKSUMVFS_STATIC
#endif
#ifdef SQLITE_CKSUMVFS_STATIC
# include "sqlite3.h"
#else
# include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#endif
#include <string.h>
#include <assert.h>
typedef struct sqlite3_vfs CksmVfs;
typedef struct CksmFile CksmFile;
#if !defined(SQLITE_AMALGAMATION)
typedef unsigned char u8;
typedef unsigned int u32;
#endif
#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
#define ORIGFILE(p) ((sqlite3_file*)(((CksmFile*)(p))+1))
struct CksmFile {
sqlite3_file base;
const char *zFName;
char computeCksm;
char verifyCksm;
char isWal;
char inCkpt;
CksmFile *pPartner;
};
static int cksmClose(sqlite3_file*);
static int cksmRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
static int cksmWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
static int cksmTruncate(sqlite3_file*, sqlite3_int64 size);
static int cksmSync(sqlite3_file*, int flags);
static int cksmFileSize(sqlite3_file*, sqlite3_int64 *pSize);
static int cksmLock(sqlite3_file*, int);
static int cksmUnlock(sqlite3_file*, int);
static int cksmCheckReservedLock(sqlite3_file*, int *pResOut);
static int cksmFileControl(sqlite3_file*, int op, void *pArg);
static int cksmSectorSize(sqlite3_file*);
static int cksmDeviceCharacteristics(sqlite3_file*);
static int cksmShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
static int cksmShmLock(sqlite3_file*, int offset, int n, int flags);
static void cksmShmBarrier(sqlite3_file*);
static int cksmShmUnmap(sqlite3_file*, int deleteFlag);
static int cksmFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
static int cksmUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
static int cksmOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
static int cksmDelete(sqlite3_vfs*, const char *zName, int syncDir);
static int cksmAccess(sqlite3_vfs*, const char *zName, int flags, int *);
static int cksmFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
static void *cksmDlOpen(sqlite3_vfs*, const char *zFilename);
static void cksmDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
static void (*cksmDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
static void cksmDlClose(sqlite3_vfs*, void*);
static int cksmRandomness(sqlite3_vfs*, int nByte, char *zOut);
static int cksmSleep(sqlite3_vfs*, int microseconds);
static int cksmCurrentTime(sqlite3_vfs*, double*);
static int cksmGetLastError(sqlite3_vfs*, int, char *);
static int cksmCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
static int cksmSetSystemCall(sqlite3_vfs*, const char*,sqlite3_syscall_ptr);
static sqlite3_syscall_ptr cksmGetSystemCall(sqlite3_vfs*, const char *z);
static const char *cksmNextSystemCall(sqlite3_vfs*, const char *zName);
static sqlite3_vfs cksm_vfs = {
3,
0,
1024,
0,
"cksmvfs",
0,
cksmOpen,
cksmDelete,
cksmAccess,
cksmFullPathname,
cksmDlOpen,
cksmDlError,
cksmDlSym,
cksmDlClose,
cksmRandomness,
cksmSleep,
cksmCurrentTime,
cksmGetLastError,
cksmCurrentTimeInt64,
cksmSetSystemCall,
cksmGetSystemCall,
cksmNextSystemCall
};
static const sqlite3_io_methods cksm_io_methods = {
3,
cksmClose,
cksmRead,
cksmWrite,
cksmTruncate,
cksmSync,
cksmFileSize,
cksmLock,
cksmUnlock,
cksmCheckReservedLock,
cksmFileControl,
cksmSectorSize,
cksmDeviceCharacteristics,
cksmShmMap,
cksmShmLock,
cksmShmBarrier,
cksmShmUnmap,
cksmFetch,
cksmUnfetch
};
#define BYTESWAP32(x) ( \
(((x)&0x000000FF)<<24) + (((x)&0x0000FF00)<<8) \
+ (((x)&0x00FF0000)>>8) + (((x)&0xFF000000)>>24) \
)
static void cksmCompute(
u8 *a,
int nByte,
u8 *aOut
){
u32 s1 = 0, s2 = 0;
u32 *aData = (u32*)a;
u32 *aEnd = (u32*)&a[nByte];
u32 x = 1;
assert( nByte>=8 );
assert( (nByte&0x00000007)==0 );
assert( nByte<=65536 );
if( 1 == *(u8*)&x ){
do {
s1 += *aData++ + s2;
s2 += *aData++ + s1;
}while( aData<aEnd );
}else{
do {
s1 += BYTESWAP32(aData[0]) + s2;
s2 += BYTESWAP32(aData[1]) + s1;
aData += 2;
}while( aData<aEnd );
s1 = BYTESWAP32(s1);
s2 = BYTESWAP32(s2);
}
memcpy(aOut, &s1, 4);
memcpy(aOut+4, &s2, 4);
}
static void cksmVerifyFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
int nByte;
u8 *data;
u8 cksum[8];
data = (u8*)sqlite3_value_blob(argv[0]);
if( data==0 ) return;
if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ) return;
nByte = sqlite3_value_bytes(argv[0]);
if( nByte<512 || nByte>65536 || (nByte & (nByte-1))!=0 ) return;
cksmCompute(data, nByte-8, cksum);
sqlite3_result_int(context, memcmp(data+nByte-8,cksum,8)==0);
}
#ifdef SQLITE_CKSUMVFS_INIT_FUNCNAME
static void cksmInitFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
int nByte = 8;
const char *zSchemaName = (const char*)sqlite3_value_text(argv[0]);
sqlite3 *db = sqlite3_context_db_handle(context);
sqlite3_file_control(db, zSchemaName, SQLITE_FCNTL_RESERVE_BYTES, &nByte);
}
#endif
static int cksmClose(sqlite3_file *pFile){
CksmFile *p = (CksmFile *)pFile;
if( p->pPartner ){
assert( p->pPartner->pPartner==p );
p->pPartner->pPartner = 0;
p->pPartner = 0;
}
pFile = ORIGFILE(pFile);
return pFile->pMethods->xClose(pFile);
}
static void cksmSetFlags(CksmFile *p, int hasCorrectReserveSize){
if( hasCorrectReserveSize!=p->computeCksm ){
p->computeCksm = p->verifyCksm = hasCorrectReserveSize;
if( p->pPartner ){
p->pPartner->verifyCksm = hasCorrectReserveSize;
p->pPartner->computeCksm = hasCorrectReserveSize;
}
}
}
static int cksmRead(
sqlite3_file *pFile,
void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
int rc;
CksmFile *p = (CksmFile *)pFile;
pFile = ORIGFILE(pFile);
rc = pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst);
if( rc==SQLITE_OK ){
if( iOfst==0 && iAmt>=100 && (
memcmp(zBuf,"SQLite format 3",16)==0 || memcmp(zBuf,"ZV-",3)==0
)){
u8 *d = (u8*)zBuf;
char hasCorrectReserveSize = (d[20]==8);
cksmSetFlags(p, hasCorrectReserveSize);
}
if( iAmt>=512
&& p->verifyCksm
&& !p->inCkpt
){
u8 cksum[8];
cksmCompute((u8*)zBuf, iAmt-8, cksum);
if( memcmp((u8*)zBuf+iAmt-8, cksum, 8)!=0 ){
sqlite3_log(SQLITE_IOERR_DATA,
"checksum fault offset %lld of \"%s\"",
iOfst, p->zFName);
rc = SQLITE_IOERR_DATA;
}
}
}
return rc;
}
static int cksmWrite(
sqlite3_file *pFile,
const void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
CksmFile *p = (CksmFile *)pFile;
pFile = ORIGFILE(pFile);
if( iOfst==0 && iAmt>=100 && (
memcmp(zBuf,"SQLite format 3",16)==0 || memcmp(zBuf,"ZV-",3)==0
)){
u8 *d = (u8*)zBuf;
char hasCorrectReserveSize = (d[20]==8);
cksmSetFlags(p, hasCorrectReserveSize);
}
if( iAmt>=512
&& p->computeCksm
&& !p->inCkpt
){
cksmCompute((u8*)zBuf, iAmt-8, ((u8*)zBuf)+iAmt-8);
}
return pFile->pMethods->xWrite(pFile, zBuf, iAmt, iOfst);
}
static int cksmTruncate(sqlite3_file *pFile, sqlite_int64 size){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xTruncate(pFile, size);
}
static int cksmSync(sqlite3_file *pFile, int flags){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xSync(pFile, flags);
}
static int cksmFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
CksmFile *p = (CksmFile *)pFile;
pFile = ORIGFILE(p);
return pFile->pMethods->xFileSize(pFile, pSize);
}
static int cksmLock(sqlite3_file *pFile, int eLock){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xLock(pFile, eLock);
}
static int cksmUnlock(sqlite3_file *pFile, int eLock){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xUnlock(pFile, eLock);
}
static int cksmCheckReservedLock(sqlite3_file *pFile, int *pResOut){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xCheckReservedLock(pFile, pResOut);
}
static int cksmFileControl(sqlite3_file *pFile, int op, void *pArg){
int rc;
CksmFile *p = (CksmFile*)pFile;
pFile = ORIGFILE(pFile);
if( op==SQLITE_FCNTL_PRAGMA ){
char **azArg = (char**)pArg;
assert( azArg[1]!=0 );
if( sqlite3_stricmp(azArg[1],"checksum_verification")==0 ){
char *zArg = azArg[2];
if( zArg!=0 ){
if( (zArg[0]>='1' && zArg[0]<='9')
|| sqlite3_strlike("enable%",zArg,0)==0
|| sqlite3_stricmp("yes",zArg)==0
|| sqlite3_stricmp("on",zArg)==0
){
p->verifyCksm = p->computeCksm;
}else{
p->verifyCksm = 0;
}
if( p->pPartner ) p->pPartner->verifyCksm = p->verifyCksm;
}
azArg[0] = sqlite3_mprintf("%d",p->verifyCksm);
return SQLITE_OK;
}else if( p->computeCksm && azArg[2]!=0
&& sqlite3_stricmp(azArg[1], "page_size")==0 ){
return SQLITE_OK;
}
}else if( op==SQLITE_FCNTL_CKPT_START || op==SQLITE_FCNTL_CKPT_DONE ){
p->inCkpt = op==SQLITE_FCNTL_CKPT_START;
if( p->pPartner ) p->pPartner->inCkpt = p->inCkpt;
}else if( op==SQLITE_FCNTL_CKSM_FILE ){
sqlite3_file **ppFile = (sqlite3_file**)pArg;
*ppFile = (sqlite3_file*)p;
return SQLITE_OK;
}
rc = pFile->pMethods->xFileControl(pFile, op, pArg);
if( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){
*(char**)pArg = sqlite3_mprintf("cksm/%z", *(char**)pArg);
}
return rc;
}
static int cksmSectorSize(sqlite3_file *pFile){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xSectorSize(pFile);
}
static int cksmDeviceCharacteristics(sqlite3_file *pFile){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xDeviceCharacteristics(pFile);
}
static int cksmShmMap(
sqlite3_file *pFile,
int iPg,
int pgsz,
int bExtend,
void volatile **pp
){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xShmMap(pFile,iPg,pgsz,bExtend,pp);
}
static int cksmShmLock(sqlite3_file *pFile, int offset, int n, int flags){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xShmLock(pFile,offset,n,flags);
}
static void cksmShmBarrier(sqlite3_file *pFile){
pFile = ORIGFILE(pFile);
pFile->pMethods->xShmBarrier(pFile);
}
static int cksmShmUnmap(sqlite3_file *pFile, int deleteFlag){
pFile = ORIGFILE(pFile);
return pFile->pMethods->xShmUnmap(pFile,deleteFlag);
}
static int cksmFetch(
sqlite3_file *pFile,
sqlite3_int64 iOfst,
int iAmt,
void **pp
){
CksmFile *p = (CksmFile *)pFile;
if( p->computeCksm ){
*pp = 0;
return SQLITE_OK;
}
pFile = ORIGFILE(pFile);
if( pFile->pMethods->iVersion>2 && pFile->pMethods->xFetch ){
return pFile->pMethods->xFetch(pFile, iOfst, iAmt, pp);
}
*pp = 0;
return SQLITE_OK;
}
static int cksmUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
pFile = ORIGFILE(pFile);
if( pFile->pMethods->iVersion>2 && pFile->pMethods->xUnfetch ){
return pFile->pMethods->xUnfetch(pFile, iOfst, pPage);
}
return SQLITE_OK;
}
static int cksmOpen(
sqlite3_vfs *pVfs,
const char *zName,
sqlite3_file *pFile,
int flags,
int *pOutFlags
){
CksmFile *p;
sqlite3_file *pSubFile;
sqlite3_vfs *pSubVfs;
int rc;
pSubVfs = ORIGVFS(pVfs);
if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_WAL))==0 ){
return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
}
p = (CksmFile*)pFile;
memset(p, 0, sizeof(*p));
pSubFile = ORIGFILE(pFile);
pFile->pMethods = &cksm_io_methods;
rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags);
if( rc ) goto cksm_open_done;
if( flags & SQLITE_OPEN_WAL ){
sqlite3_file *pDb = sqlite3_database_file_object(zName);
rc = pDb->pMethods->xFileControl(pDb, SQLITE_FCNTL_CKSM_FILE, (void*)&pDb);
assert( rc==SQLITE_OK );
p->pPartner = (CksmFile*)pDb;
assert( p->pPartner->pPartner==0 );
p->pPartner->pPartner = p;
p->isWal = 1;
p->computeCksm = p->pPartner->computeCksm;
}else{
p->isWal = 0;
p->computeCksm = 0;
}
p->zFName = zName;
cksm_open_done:
if( rc ) pFile->pMethods = 0;
return rc;
}
static int cksmDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, dirSync);
}
static int cksmAccess(
sqlite3_vfs *pVfs,
const char *zPath,
int flags,
int *pResOut
){
return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zPath, flags, pResOut);
}
static int cksmFullPathname(
sqlite3_vfs *pVfs,
const char *zPath,
int nOut,
char *zOut
){
return ORIGVFS(pVfs)->xFullPathname(ORIGVFS(pVfs),zPath,nOut,zOut);
}
static void *cksmDlOpen(sqlite3_vfs *pVfs, const char *zPath){
return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath);
}
static void cksmDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
}
static void (*cksmDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
}
static void cksmDlClose(sqlite3_vfs *pVfs, void *pHandle){
ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
}
static int cksmRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
}
static int cksmSleep(sqlite3_vfs *pVfs, int nMicro){
return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro);
}
static int cksmCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
}
static int cksmGetLastError(sqlite3_vfs *pVfs, int a, char *b){
return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
}
static int cksmCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
sqlite3_vfs *pOrig = ORIGVFS(pVfs);
int rc;
assert( pOrig->iVersion>=2 );
if( pOrig->xCurrentTimeInt64 ){
rc = pOrig->xCurrentTimeInt64(pOrig, p);
}else{
double r;
rc = pOrig->xCurrentTime(pOrig, &r);
*p = (sqlite3_int64)(r*86400000.0);
}
return rc;
}
static int cksmSetSystemCall(
sqlite3_vfs *pVfs,
const char *zName,
sqlite3_syscall_ptr pCall
){
return ORIGVFS(pVfs)->xSetSystemCall(ORIGVFS(pVfs),zName,pCall);
}
static sqlite3_syscall_ptr cksmGetSystemCall(
sqlite3_vfs *pVfs,
const char *zName
){
return ORIGVFS(pVfs)->xGetSystemCall(ORIGVFS(pVfs),zName);
}
static const char *cksmNextSystemCall(sqlite3_vfs *pVfs, const char *zName){
return ORIGVFS(pVfs)->xNextSystemCall(ORIGVFS(pVfs), zName);
}
static int cksmRegisterFunc(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc;
if( db==0 ) return SQLITE_OK;
rc = sqlite3_create_function(db, "verify_checksum", 1,
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
0, cksmVerifyFunc, 0, 0);
#ifdef SQLITE_CKSUMVFS_INIT_FUNCNAME
(void)sqlite3_create_function(db, SQLITE_CKSUMVFS_INIT_FUNCNAME, 1,
SQLITE_UTF8|SQLITE_DIRECTONLY,
0, cksmInitFunc, 0, 0);
#endif
return rc;
}
static int cksmRegisterVfs(void){
int rc = SQLITE_OK;
sqlite3_vfs *pOrig;
if( sqlite3_vfs_find("cksmvfs")!=0 ) return SQLITE_OK;
pOrig = sqlite3_vfs_find(0);
if( pOrig==0 ) return SQLITE_ERROR;
cksm_vfs.iVersion = pOrig->iVersion;
cksm_vfs.pAppData = pOrig;
cksm_vfs.szOsFile = pOrig->szOsFile + sizeof(CksmFile);
rc = sqlite3_vfs_register(&cksm_vfs, 1);
if( rc==SQLITE_OK ){
rc = sqlite3_auto_extension((void(*)(void))cksmRegisterFunc);
}
return rc;
}
#if defined(SQLITE_CKSUMVFS_STATIC)
int sqlite3_register_cksumvfs(const char *NotUsed){
(void)NotUsed;
return cksmRegisterVfs();
}
int sqlite3_unregister_cksumvfs(void){
if( sqlite3_vfs_find("cksmvfs") ){
sqlite3_vfs_unregister(&cksm_vfs);
sqlite3_cancel_auto_extension((void(*)(void))cksmRegisterFunc);
}
return SQLITE_OK;
}
#endif
#if !defined(SQLITE_CKSUMVFS_STATIC)
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_cksumvfs_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc;
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErrMsg;
rc = cksmRegisterFunc(db, 0, 0);
if( rc==SQLITE_OK ){
rc = cksmRegisterVfs();
}
if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY;
return rc;
}
#endif