1 /*
2 ** 2020-04-20
3 **
4 ** The author disclaims copyright to this source code.  In place of
5 ** a legal notice, here is a blessing:
6 **
7 **    May you do good and not evil.
8 **    May you find forgiveness for yourself and forgive others.
9 **    May you share freely, never taking more than you give.
10 **
11 ******************************************************************************
12 **
13 ** This file implements a VFS shim that writes a checksum on each page
14 ** of an SQLite database file.  When reading pages, the checksum is verified
15 ** and an error is raised if the checksum is incorrect.
16 **
17 ** COMPILING
18 **
19 ** This extension requires SQLite 3.32.0 or later.  It uses the
20 ** sqlite3_database_file_object() interface which was added in
21 ** version 3.32.0, so it will not link with an earlier version of
22 ** SQLite.
23 **
24 ** To build this extension as a separately loaded shared library or
25 ** DLL, use compiler command-lines similar to the following:
26 **
27 **   (linux)    gcc -fPIC -shared cksumvfs.c -o cksumvfs.so
28 **   (mac)      clang -fPIC -dynamiclib cksumvfs.c -o cksumvfs.dylib
29 **   (windows)  cl cksumvfs.c -link -dll -out:cksumvfs.dll
30 **
31 ** You may want to add additional compiler options, of course,
32 ** according to the needs of your project.
33 **
34 ** If you want to statically link this extension with your product,
35 ** then compile it like any other C-language module but add the
36 ** "-DSQLITE_CKSUMVFS_STATIC" option so that this module knows that
37 ** it is being statically linked rather than dynamically linked
38 **
39 ** LOADING
40 **
41 ** To load this extension as a shared library, you first have to
42 ** bring up a dummy SQLite database connection to use as the argument
43 ** to the sqlite3_load_extension() API call.  Then you invoke the
44 ** sqlite3_load_extension() API and shutdown the dummy database
45 ** connection.  All subsequent database connections that are opened
46 ** will include this extension.  For example:
47 **
48 **     sqlite3 *db;
49 **     sqlite3_open(":memory:", &db);
50 **     sqlite3_load_extention(db, "./cksumvfs");
51 **     sqlite3_close(db);
52 **
53 ** If this extension is compiled with -DSQLITE_CKSUMVFS_STATIC and
54 ** statically linked against the application, initialize it using
55 ** a single API call as follows:
56 **
57 **     sqlite3_register_cksumvfs();
58 **
59 ** Cksumvfs is a VFS Shim. When loaded, "cksmvfs" becomes the new
60 ** default VFS and it uses the prior default VFS as the next VFS
61 ** down in the stack.  This is normally what you want.  However, in
62 ** complex situations where multiple VFS shims are being loaded,
63 ** it might be important to ensure that cksumvfs is loaded in the
64 ** correct order so that it sequences itself into the default VFS
65 ** Shim stack in the right order.
66 **
67 ** USING
68 **
69 ** Open database connections using the sqlite3_open() or
70 ** sqlite3_open_v2() interfaces, as normal.  Ordinary database files
71 ** (without a checksum) will operate normally.  Databases with
72 ** checksums will return an SQLITE_IOERR_DATA error if a page is
73 ** encountered that contains an invalid checksum.
74 **
75 ** Checksumming only works on databases that have a reserve-bytes
76 ** value of exactly 8.  The default value for reserve-bytes is 0.
77 ** Hence, newly created database files will omit the checksum by
78 ** default.  To create a database that includes a checksum, change
79 ** the reserve-bytes value to 8 by runing:
80 **
81 **    int n = 8;
82 **    sqlite3_file_control(db, 0, SQLITE_FCNTL_RESERVE_BYTES, &n);
83 **
84 ** If you do this immediately after creating a new database file,
85 ** before anything else has been written into the file, then that
86 ** might be all that you need to do.  Otherwise, the API call
87 ** above should be followed by:
88 **
89 **    sqlite3_exec(db, "VACUUM", 0, 0, 0);
90 **
91 ** It never hurts to run the VACUUM, even if you don't need it.
92 ** If the database is in WAL mode, you should shutdown and
93 ** reopen all database connections before continuing.
94 **
95 ** From the CLI, use the ".filectrl reserve_bytes 8" command,
96 ** followed by "VACUUM;".
97 **
98 ** Note that SQLite allows the number of reserve-bytes to be
99 ** increased but not decreased.  So if a database file already
100 ** has a reserve-bytes value greater than 8, there is no way to
101 ** activate checksumming on that database, other than to dump
102 ** and restore the database file.  Note also that other extensions
103 ** might also make use of the reserve-bytes.  Checksumming will
104 ** be incompatible with those other extensions.
105 **
106 ** VERIFICATION OF CHECKSUMS
107 **
108 ** If any checksum is incorrect, the "PRAGMA quick_check" command
109 ** will find it.  To verify that checksums are actually enabled
110 ** and running, use the following query:
111 **
112 **   SELECT count(*), verify_checksum(data)
113 **     FROM sqlite_dbpage
114 **    GROUP BY 2;
115 **
116 ** There are three possible outputs form the verify_checksum()
117 ** function: 1, 0, and NULL.  1 is returned if the checksum is
118 ** correct.  0 is returned if the checksum is incorrect.  NULL
119 ** is returned if the page is unreadable.  If checksumming is
120 ** enabled, the read will fail if the checksum is wrong, so the
121 ** usual result from verify_checksum() on a bad checksum is NULL.
122 **
123 ** If everything is OK, the query above should return a single
124 ** row where the second column is 1.  Any other result indicates
125 ** either that there is a checksum error, or checksum validation
126 ** is disabled.
127 **
128 ** CONTROLLING CHECKSUM VERIFICATION
129 **
130 ** The cksumvfs extension implements a new PRAGMA statement that can
131 ** be used to disable, re-enable, or query the status of checksum
132 ** verification:
133 **
134 **    PRAGMA checksum_verification;          -- query status
135 **    PRAGMA checksum_verification=OFF;      -- disable verification
136 **    PRAGMA checksum_verification=ON;       -- re-enable verification
137 **
138 ** The "checksum_verification" pragma will return "1" (true) or "0"
139 ** (false) if checksum verification is enabled or disabled, respectively.
140 ** "Verification" in this context means the feature that causes
141 ** SQLITE_IOERR_DATA errors if a checksum mismatch is detected while
142 ** reading.  Checksums are always kept up-to-date as long as the
143 ** reserve-bytes value of the database is 8, regardless of the setting
144 ** of this pragma.  Checksum verification can be disabled (for example)
145 ** to do forensic analysis of a database that has previously reported
146 ** a checksum error.
147 **
148 ** The "checksum_verification" pragma will always respond with "0" if
149 ** the database file does not have a reserve-bytes value of 8.  The
150 ** pragma will return no rows at all if the cksumvfs extension is
151 ** not loaded.
152 **
153 ** IMPLEMENTATION NOTES
154 **
155 ** The checksum is stored in the last 8 bytes of each page.  This
156 ** module only operates if the "bytes of reserved space on each page"
157 ** value at offset 20 the SQLite database header is exactly 8.  If
158 ** the reserved-space value is not 8, this module is a no-op.
159 */
160 #if defined(SQLITE_AMALGAMATION) && !defined(SQLITE_CKSUMVFS_STATIC)
161 # define SQLITE_CKSUMVFS_STATIC
162 #endif
163 #ifdef SQLITE_CKSUMVFS_STATIC
164 # include "sqlite3.h"
165 #else
166 # include "sqlite3ext.h"
167   SQLITE_EXTENSION_INIT1
168 #endif
169 #include <string.h>
170 #include <assert.h>
171 
172 
173 /*
174 ** Forward declaration of objects used by this utility
175 */
176 typedef struct sqlite3_vfs CksmVfs;
177 typedef struct CksmFile CksmFile;
178 
179 /*
180 ** Useful datatype abbreviations
181 */
182 #if !defined(SQLITE_AMALGAMATION)
183   typedef unsigned char u8;
184   typedef unsigned int u32;
185 #endif
186 
187 /* Access to a lower-level VFS that (might) implement dynamic loading,
188 ** access to randomness, etc.
189 */
190 #define ORIGVFS(p)  ((sqlite3_vfs*)((p)->pAppData))
191 #define ORIGFILE(p) ((sqlite3_file*)(((CksmFile*)(p))+1))
192 
193 /* An open file */
194 struct CksmFile {
195   sqlite3_file base;    /* IO methods */
196   const char *zFName;   /* Original name of the file */
197   char computeCksm;     /* True to compute checksums.
198                         ** Always true if reserve size is 8. */
199   char verifyCksm;      /* True to verify checksums */
200   char isWal;           /* True if processing a WAL file */
201   char inCkpt;          /* Currently doing a checkpoint */
202   CksmFile *pPartner;   /* Ptr from WAL to main-db, or from main-db to WAL */
203 };
204 
205 /*
206 ** Methods for CksmFile
207 */
208 static int cksmClose(sqlite3_file*);
209 static int cksmRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
210 static int cksmWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
211 static int cksmTruncate(sqlite3_file*, sqlite3_int64 size);
212 static int cksmSync(sqlite3_file*, int flags);
213 static int cksmFileSize(sqlite3_file*, sqlite3_int64 *pSize);
214 static int cksmLock(sqlite3_file*, int);
215 static int cksmUnlock(sqlite3_file*, int);
216 static int cksmCheckReservedLock(sqlite3_file*, int *pResOut);
217 static int cksmFileControl(sqlite3_file*, int op, void *pArg);
218 static int cksmSectorSize(sqlite3_file*);
219 static int cksmDeviceCharacteristics(sqlite3_file*);
220 static int cksmShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
221 static int cksmShmLock(sqlite3_file*, int offset, int n, int flags);
222 static void cksmShmBarrier(sqlite3_file*);
223 static int cksmShmUnmap(sqlite3_file*, int deleteFlag);
224 static int cksmFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
225 static int cksmUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
226 
227 /*
228 ** Methods for CksmVfs
229 */
230 static int cksmOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
231 static int cksmDelete(sqlite3_vfs*, const char *zName, int syncDir);
232 static int cksmAccess(sqlite3_vfs*, const char *zName, int flags, int *);
233 static int cksmFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
234 static void *cksmDlOpen(sqlite3_vfs*, const char *zFilename);
235 static void cksmDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
236 static void (*cksmDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
237 static void cksmDlClose(sqlite3_vfs*, void*);
238 static int cksmRandomness(sqlite3_vfs*, int nByte, char *zOut);
239 static int cksmSleep(sqlite3_vfs*, int microseconds);
240 static int cksmCurrentTime(sqlite3_vfs*, double*);
241 static int cksmGetLastError(sqlite3_vfs*, int, char *);
242 static int cksmCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
243 static int cksmSetSystemCall(sqlite3_vfs*, const char*,sqlite3_syscall_ptr);
244 static sqlite3_syscall_ptr cksmGetSystemCall(sqlite3_vfs*, const char *z);
245 static const char *cksmNextSystemCall(sqlite3_vfs*, const char *zName);
246 
247 static sqlite3_vfs cksm_vfs = {
248   3,                            /* iVersion (set when registered) */
249   0,                            /* szOsFile (set when registered) */
250   1024,                         /* mxPathname */
251   0,                            /* pNext */
252   "cksmvfs",                    /* zName */
253   0,                            /* pAppData (set when registered) */
254   cksmOpen,                     /* xOpen */
255   cksmDelete,                   /* xDelete */
256   cksmAccess,                   /* xAccess */
257   cksmFullPathname,             /* xFullPathname */
258   cksmDlOpen,                   /* xDlOpen */
259   cksmDlError,                  /* xDlError */
260   cksmDlSym,                    /* xDlSym */
261   cksmDlClose,                  /* xDlClose */
262   cksmRandomness,               /* xRandomness */
263   cksmSleep,                    /* xSleep */
264   cksmCurrentTime,              /* xCurrentTime */
265   cksmGetLastError,             /* xGetLastError */
266   cksmCurrentTimeInt64,         /* xCurrentTimeInt64 */
267   cksmSetSystemCall,            /* xSetSystemCall */
268   cksmGetSystemCall,            /* xGetSystemCall */
269   cksmNextSystemCall            /* xNextSystemCall */
270 };
271 
272 static const sqlite3_io_methods cksm_io_methods = {
273   3,                              /* iVersion */
274   cksmClose,                      /* xClose */
275   cksmRead,                       /* xRead */
276   cksmWrite,                      /* xWrite */
277   cksmTruncate,                   /* xTruncate */
278   cksmSync,                       /* xSync */
279   cksmFileSize,                   /* xFileSize */
280   cksmLock,                       /* xLock */
281   cksmUnlock,                     /* xUnlock */
282   cksmCheckReservedLock,          /* xCheckReservedLock */
283   cksmFileControl,                /* xFileControl */
284   cksmSectorSize,                 /* xSectorSize */
285   cksmDeviceCharacteristics,      /* xDeviceCharacteristics */
286   cksmShmMap,                     /* xShmMap */
287   cksmShmLock,                    /* xShmLock */
288   cksmShmBarrier,                 /* xShmBarrier */
289   cksmShmUnmap,                   /* xShmUnmap */
290   cksmFetch,                      /* xFetch */
291   cksmUnfetch                     /* xUnfetch */
292 };
293 
294 /* Do byte swapping on a unsigned 32-bit integer */
295 #define BYTESWAP32(x) ( \
296     (((x)&0x000000FF)<<24) + (((x)&0x0000FF00)<<8)  \
297   + (((x)&0x00FF0000)>>8)  + (((x)&0xFF000000)>>24) \
298 )
299 
300 /* Compute a checksum on a buffer */
cksmCompute(u8 * a,int nByte,u8 * aOut)301 static void cksmCompute(
302   u8 *a,           /* Content to be checksummed */
303   int nByte,       /* Bytes of content in a[].  Must be a multiple of 8. */
304   u8 *aOut         /* OUT: Final 8-byte checksum value output */
305 ){
306   u32 s1 = 0, s2 = 0;
307   u32 *aData = (u32*)a;
308   u32 *aEnd = (u32*)&a[nByte];
309   u32 x = 1;
310 
311   assert( nByte>=8 );
312   assert( (nByte&0x00000007)==0 );
313   assert( nByte<=65536 );
314 
315   if( 1 == *(u8*)&x ){
316     /* Little-endian */
317     do {
318       s1 += *aData++ + s2;
319       s2 += *aData++ + s1;
320     }while( aData<aEnd );
321   }else{
322     /* Big-endian */
323     do {
324       s1 += BYTESWAP32(aData[0]) + s2;
325       s2 += BYTESWAP32(aData[1]) + s1;
326       aData += 2;
327     }while( aData<aEnd );
328     s1 = BYTESWAP32(s1);
329     s2 = BYTESWAP32(s2);
330   }
331   memcpy(aOut, &s1, 4);
332   memcpy(aOut+4, &s2, 4);
333 }
334 
335 /*
336 ** SQL function:    verify_checksum(BLOB)
337 **
338 ** Return 0 or 1 if the checksum is invalid or valid.  Or return
339 ** NULL if the input is not a BLOB that is the right size for a
340 ** database page.
341 */
cksmVerifyFunc(sqlite3_context * context,int argc,sqlite3_value ** argv)342 static void cksmVerifyFunc(
343   sqlite3_context *context,
344   int argc,
345   sqlite3_value **argv
346 ){
347   int nByte;
348   u8 *data;
349   u8 cksum[8];
350   data = (u8*)sqlite3_value_blob(argv[0]);
351   if( data==0 ) return;
352   if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ) return;
353   nByte = sqlite3_value_bytes(argv[0]);
354   if( nByte<512 || nByte>65536 || (nByte & (nByte-1))!=0 ) return;
355   cksmCompute(data, nByte-8, cksum);
356   sqlite3_result_int(context, memcmp(data+nByte-8,cksum,8)==0);
357 }
358 
359 #ifdef SQLITE_CKSUMVFS_INIT_FUNCNAME
360 /*
361 ** SQL function:    initialize_cksumvfs(SCHEMANAME)
362 **
363 ** This SQL functions (whose name is actually determined at compile-time
364 ** by the value of the SQLITE_CKSUMVFS_INIT_FUNCNAME macro) invokes:
365 **
366 **   sqlite3_file_control(db, SCHEMANAME, SQLITE_FCNTL_RESERVE_BYTE, &n);
367 **
368 ** In order to set the reserve bytes value to 8, so that cksumvfs will
369 ** operation.  This feature is provided (if and only if the
370 ** SQLITE_CKSUMVFS_INIT_FUNCNAME compile-time option is set to a string
371 ** which is the name of the SQL function) so as to provide the ability
372 ** to invoke the file-control in programming languages that lack
373 ** direct access to the sqlite3_file_control() interface (ex: Java).
374 **
375 ** This interface is undocumented, apart from this comment.  Usage
376 ** example:
377 **
378 **    1.  Compile with -DSQLITE_CKSUMVFS_INIT_FUNCNAME="ckvfs_init"
379 **    2.  Run:  "SELECT cksum_init('main'); VACUUM;"
380 */
cksmInitFunc(sqlite3_context * context,int argc,sqlite3_value ** argv)381 static void cksmInitFunc(
382   sqlite3_context *context,
383   int argc,
384   sqlite3_value **argv
385 ){
386   int nByte = 8;
387   const char *zSchemaName = (const char*)sqlite3_value_text(argv[0]);
388   sqlite3 *db = sqlite3_context_db_handle(context);
389   sqlite3_file_control(db, zSchemaName, SQLITE_FCNTL_RESERVE_BYTES, &nByte);
390   /* Return NULL */
391 }
392 #endif /* SQLITE_CKSUMBFS_INIT_FUNCNAME */
393 
394 /*
395 ** Close a cksm-file.
396 */
cksmClose(sqlite3_file * pFile)397 static int cksmClose(sqlite3_file *pFile){
398   CksmFile *p = (CksmFile *)pFile;
399   if( p->pPartner ){
400     assert( p->pPartner->pPartner==p );
401     p->pPartner->pPartner = 0;
402     p->pPartner = 0;
403   }
404   pFile = ORIGFILE(pFile);
405   return pFile->pMethods->xClose(pFile);
406 }
407 
408 /*
409 ** Set the computeCkSm and verifyCksm flags, if they need to be
410 ** changed.
411 */
cksmSetFlags(CksmFile * p,int hasCorrectReserveSize)412 static void cksmSetFlags(CksmFile *p, int hasCorrectReserveSize){
413   if( hasCorrectReserveSize!=p->computeCksm ){
414     p->computeCksm = p->verifyCksm = hasCorrectReserveSize;
415     if( p->pPartner ){
416       p->pPartner->verifyCksm = hasCorrectReserveSize;
417       p->pPartner->computeCksm = hasCorrectReserveSize;
418     }
419   }
420 }
421 
422 /*
423 ** Read data from a cksm-file.
424 */
cksmRead(sqlite3_file * pFile,void * zBuf,int iAmt,sqlite_int64 iOfst)425 static int cksmRead(
426   sqlite3_file *pFile,
427   void *zBuf,
428   int iAmt,
429   sqlite_int64 iOfst
430 ){
431   int rc;
432   CksmFile *p = (CksmFile *)pFile;
433   pFile = ORIGFILE(pFile);
434   rc = pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst);
435   if( rc==SQLITE_OK ){
436     if( iOfst==0 && iAmt>=100 && (
437           memcmp(zBuf,"SQLite format 3",16)==0 || memcmp(zBuf,"ZV-",3)==0
438     )){
439       u8 *d = (u8*)zBuf;
440       char hasCorrectReserveSize = (d[20]==8);
441       cksmSetFlags(p, hasCorrectReserveSize);
442     }
443     /* Verify the checksum if
444     **    (1) the size indicates that we are dealing with a complete
445     **        database page
446     **    (2) checksum verification is enabled
447     **    (3) we are not in the middle of checkpoint
448     */
449     if( iAmt>=512           /* (1) */
450      && p->verifyCksm       /* (2) */
451      && !p->inCkpt          /* (3) */
452     ){
453       u8 cksum[8];
454       cksmCompute((u8*)zBuf, iAmt-8, cksum);
455       if( memcmp((u8*)zBuf+iAmt-8, cksum, 8)!=0 ){
456         sqlite3_log(SQLITE_IOERR_DATA,
457            "checksum fault offset %lld of \"%s\"",
458            iOfst, p->zFName);
459         rc = SQLITE_IOERR_DATA;
460       }
461     }
462   }
463   return rc;
464 }
465 
466 /*
467 ** Write data to a cksm-file.
468 */
cksmWrite(sqlite3_file * pFile,const void * zBuf,int iAmt,sqlite_int64 iOfst)469 static int cksmWrite(
470   sqlite3_file *pFile,
471   const void *zBuf,
472   int iAmt,
473   sqlite_int64 iOfst
474 ){
475   CksmFile *p = (CksmFile *)pFile;
476   pFile = ORIGFILE(pFile);
477   if( iOfst==0 && iAmt>=100 && (
478         memcmp(zBuf,"SQLite format 3",16)==0 || memcmp(zBuf,"ZV-",3)==0
479   )){
480     u8 *d = (u8*)zBuf;
481     char hasCorrectReserveSize = (d[20]==8);
482     cksmSetFlags(p, hasCorrectReserveSize);
483   }
484   /* If the write size is appropriate for a database page and if
485   ** checksums where ever enabled, then it will be safe to compute
486   ** the checksums.  The reserve byte size might have increased, but
487   ** it will never decrease.  And because it cannot decrease, the
488   ** checksum will not overwrite anything.
489   */
490   if( iAmt>=512
491    && p->computeCksm
492    && !p->inCkpt
493   ){
494     cksmCompute((u8*)zBuf, iAmt-8, ((u8*)zBuf)+iAmt-8);
495   }
496   return pFile->pMethods->xWrite(pFile, zBuf, iAmt, iOfst);
497 }
498 
499 /*
500 ** Truncate a cksm-file.
501 */
cksmTruncate(sqlite3_file * pFile,sqlite_int64 size)502 static int cksmTruncate(sqlite3_file *pFile, sqlite_int64 size){
503   pFile = ORIGFILE(pFile);
504   return pFile->pMethods->xTruncate(pFile, size);
505 }
506 
507 /*
508 ** Sync a cksm-file.
509 */
cksmSync(sqlite3_file * pFile,int flags)510 static int cksmSync(sqlite3_file *pFile, int flags){
511   pFile = ORIGFILE(pFile);
512   return pFile->pMethods->xSync(pFile, flags);
513 }
514 
515 /*
516 ** Return the current file-size of a cksm-file.
517 */
cksmFileSize(sqlite3_file * pFile,sqlite_int64 * pSize)518 static int cksmFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
519   CksmFile *p = (CksmFile *)pFile;
520   pFile = ORIGFILE(p);
521   return pFile->pMethods->xFileSize(pFile, pSize);
522 }
523 
524 /*
525 ** Lock a cksm-file.
526 */
cksmLock(sqlite3_file * pFile,int eLock)527 static int cksmLock(sqlite3_file *pFile, int eLock){
528   pFile = ORIGFILE(pFile);
529   return pFile->pMethods->xLock(pFile, eLock);
530 }
531 
532 /*
533 ** Unlock a cksm-file.
534 */
cksmUnlock(sqlite3_file * pFile,int eLock)535 static int cksmUnlock(sqlite3_file *pFile, int eLock){
536   pFile = ORIGFILE(pFile);
537   return pFile->pMethods->xUnlock(pFile, eLock);
538 }
539 
540 /*
541 ** Check if another file-handle holds a RESERVED lock on a cksm-file.
542 */
cksmCheckReservedLock(sqlite3_file * pFile,int * pResOut)543 static int cksmCheckReservedLock(sqlite3_file *pFile, int *pResOut){
544   pFile = ORIGFILE(pFile);
545   return pFile->pMethods->xCheckReservedLock(pFile, pResOut);
546 }
547 
548 /*
549 ** File control method. For custom operations on a cksm-file.
550 */
cksmFileControl(sqlite3_file * pFile,int op,void * pArg)551 static int cksmFileControl(sqlite3_file *pFile, int op, void *pArg){
552   int rc;
553   CksmFile *p = (CksmFile*)pFile;
554   pFile = ORIGFILE(pFile);
555   if( op==SQLITE_FCNTL_PRAGMA ){
556     char **azArg = (char**)pArg;
557     assert( azArg[1]!=0 );
558     if( sqlite3_stricmp(azArg[1],"checksum_verification")==0 ){
559       char *zArg = azArg[2];
560       if( zArg!=0 ){
561         if( (zArg[0]>='1' && zArg[0]<='9')
562          || sqlite3_strlike("enable%",zArg,0)==0
563          || sqlite3_stricmp("yes",zArg)==0
564          || sqlite3_stricmp("on",zArg)==0
565         ){
566           p->verifyCksm = p->computeCksm;
567         }else{
568           p->verifyCksm = 0;
569         }
570         if( p->pPartner ) p->pPartner->verifyCksm = p->verifyCksm;
571       }
572       azArg[0] = sqlite3_mprintf("%d",p->verifyCksm);
573       return SQLITE_OK;
574     }else if( p->computeCksm && azArg[2]!=0
575            && sqlite3_stricmp(azArg[1], "page_size")==0 ){
576       /* Do not allow page size changes on a checksum database */
577       return SQLITE_OK;
578     }
579   }else if( op==SQLITE_FCNTL_CKPT_START || op==SQLITE_FCNTL_CKPT_DONE ){
580     p->inCkpt = op==SQLITE_FCNTL_CKPT_START;
581     if( p->pPartner ) p->pPartner->inCkpt = p->inCkpt;
582   }
583   rc = pFile->pMethods->xFileControl(pFile, op, pArg);
584   if( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){
585     *(char**)pArg = sqlite3_mprintf("cksm/%z", *(char**)pArg);
586   }
587   return rc;
588 }
589 
590 /*
591 ** Return the sector-size in bytes for a cksm-file.
592 */
cksmSectorSize(sqlite3_file * pFile)593 static int cksmSectorSize(sqlite3_file *pFile){
594   pFile = ORIGFILE(pFile);
595   return pFile->pMethods->xSectorSize(pFile);
596 }
597 
598 /*
599 ** Return the device characteristic flags supported by a cksm-file.
600 */
cksmDeviceCharacteristics(sqlite3_file * pFile)601 static int cksmDeviceCharacteristics(sqlite3_file *pFile){
602   pFile = ORIGFILE(pFile);
603   return pFile->pMethods->xDeviceCharacteristics(pFile);
604 }
605 
606 /* Create a shared memory file mapping */
cksmShmMap(sqlite3_file * pFile,int iPg,int pgsz,int bExtend,void volatile ** pp)607 static int cksmShmMap(
608   sqlite3_file *pFile,
609   int iPg,
610   int pgsz,
611   int bExtend,
612   void volatile **pp
613 ){
614   pFile = ORIGFILE(pFile);
615   return pFile->pMethods->xShmMap(pFile,iPg,pgsz,bExtend,pp);
616 }
617 
618 /* Perform locking on a shared-memory segment */
cksmShmLock(sqlite3_file * pFile,int offset,int n,int flags)619 static int cksmShmLock(sqlite3_file *pFile, int offset, int n, int flags){
620   pFile = ORIGFILE(pFile);
621   return pFile->pMethods->xShmLock(pFile,offset,n,flags);
622 }
623 
624 /* Memory barrier operation on shared memory */
cksmShmBarrier(sqlite3_file * pFile)625 static void cksmShmBarrier(sqlite3_file *pFile){
626   pFile = ORIGFILE(pFile);
627   pFile->pMethods->xShmBarrier(pFile);
628 }
629 
630 /* Unmap a shared memory segment */
cksmShmUnmap(sqlite3_file * pFile,int deleteFlag)631 static int cksmShmUnmap(sqlite3_file *pFile, int deleteFlag){
632   pFile = ORIGFILE(pFile);
633   return pFile->pMethods->xShmUnmap(pFile,deleteFlag);
634 }
635 
636 /* Fetch a page of a memory-mapped file */
cksmFetch(sqlite3_file * pFile,sqlite3_int64 iOfst,int iAmt,void ** pp)637 static int cksmFetch(
638   sqlite3_file *pFile,
639   sqlite3_int64 iOfst,
640   int iAmt,
641   void **pp
642 ){
643   CksmFile *p = (CksmFile *)pFile;
644   if( p->computeCksm ){
645     *pp = 0;
646     return SQLITE_OK;
647   }
648   pFile = ORIGFILE(pFile);
649   if( pFile->pMethods->iVersion>2 && pFile->pMethods->xFetch ){
650     return pFile->pMethods->xFetch(pFile, iOfst, iAmt, pp);
651   }
652   *pp = 0;
653   return SQLITE_OK;
654 }
655 
656 /* Release a memory-mapped page */
cksmUnfetch(sqlite3_file * pFile,sqlite3_int64 iOfst,void * pPage)657 static int cksmUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
658   pFile = ORIGFILE(pFile);
659   if( pFile->pMethods->iVersion>2 && pFile->pMethods->xUnfetch ){
660     return pFile->pMethods->xUnfetch(pFile, iOfst, pPage);
661   }
662   return SQLITE_OK;
663 }
664 
665 /*
666 ** Open a cksm file handle.
667 */
cksmOpen(sqlite3_vfs * pVfs,const char * zName,sqlite3_file * pFile,int flags,int * pOutFlags)668 static int cksmOpen(
669   sqlite3_vfs *pVfs,
670   const char *zName,
671   sqlite3_file *pFile,
672   int flags,
673   int *pOutFlags
674 ){
675   CksmFile *p;
676   sqlite3_file *pSubFile;
677   sqlite3_vfs *pSubVfs;
678   int rc;
679   pSubVfs = ORIGVFS(pVfs);
680   if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_WAL))==0 ){
681     return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags);
682   }
683   p = (CksmFile*)pFile;
684   memset(p, 0, sizeof(*p));
685   pSubFile = ORIGFILE(pFile);
686   pFile->pMethods = &cksm_io_methods;
687   rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags);
688   if( rc ) goto cksm_open_done;
689   if( flags & SQLITE_OPEN_WAL ){
690     sqlite3_file *pDb = sqlite3_database_file_object(zName);
691     p->pPartner = (CksmFile*)pDb;
692     assert( p->pPartner->pPartner==0 );
693     p->pPartner->pPartner = p;
694     p->isWal = 1;
695     p->computeCksm = p->pPartner->computeCksm;
696   }else{
697     p->isWal = 0;
698     p->computeCksm = 0;
699   }
700   p->zFName = zName;
701 cksm_open_done:
702   if( rc ) pFile->pMethods = 0;
703   return rc;
704 }
705 
706 /*
707 ** All other VFS methods are pass-thrus.
708 */
cksmDelete(sqlite3_vfs * pVfs,const char * zPath,int dirSync)709 static int cksmDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
710   return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, dirSync);
711 }
cksmAccess(sqlite3_vfs * pVfs,const char * zPath,int flags,int * pResOut)712 static int cksmAccess(
713   sqlite3_vfs *pVfs,
714   const char *zPath,
715   int flags,
716   int *pResOut
717 ){
718   return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zPath, flags, pResOut);
719 }
cksmFullPathname(sqlite3_vfs * pVfs,const char * zPath,int nOut,char * zOut)720 static int cksmFullPathname(
721   sqlite3_vfs *pVfs,
722   const char *zPath,
723   int nOut,
724   char *zOut
725 ){
726   return ORIGVFS(pVfs)->xFullPathname(ORIGVFS(pVfs),zPath,nOut,zOut);
727 }
cksmDlOpen(sqlite3_vfs * pVfs,const char * zPath)728 static void *cksmDlOpen(sqlite3_vfs *pVfs, const char *zPath){
729   return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath);
730 }
cksmDlError(sqlite3_vfs * pVfs,int nByte,char * zErrMsg)731 static void cksmDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
732   ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
733 }
cksmDlSym(sqlite3_vfs * pVfs,void * p,const char * zSym)734 static void (*cksmDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
735   return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
736 }
cksmDlClose(sqlite3_vfs * pVfs,void * pHandle)737 static void cksmDlClose(sqlite3_vfs *pVfs, void *pHandle){
738   ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
739 }
cksmRandomness(sqlite3_vfs * pVfs,int nByte,char * zBufOut)740 static int cksmRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
741   return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
742 }
cksmSleep(sqlite3_vfs * pVfs,int nMicro)743 static int cksmSleep(sqlite3_vfs *pVfs, int nMicro){
744   return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro);
745 }
cksmCurrentTime(sqlite3_vfs * pVfs,double * pTimeOut)746 static int cksmCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
747   return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
748 }
cksmGetLastError(sqlite3_vfs * pVfs,int a,char * b)749 static int cksmGetLastError(sqlite3_vfs *pVfs, int a, char *b){
750   return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
751 }
cksmCurrentTimeInt64(sqlite3_vfs * pVfs,sqlite3_int64 * p)752 static int cksmCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
753   sqlite3_vfs *pOrig = ORIGVFS(pVfs);
754   int rc;
755   assert( pOrig->iVersion>=2 );
756   if( pOrig->xCurrentTimeInt64 ){
757     rc = pOrig->xCurrentTimeInt64(pOrig, p);
758   }else{
759     double r;
760     rc = pOrig->xCurrentTime(pOrig, &r);
761     *p = (sqlite3_int64)(r*86400000.0);
762   }
763   return rc;
764 }
cksmSetSystemCall(sqlite3_vfs * pVfs,const char * zName,sqlite3_syscall_ptr pCall)765 static int cksmSetSystemCall(
766   sqlite3_vfs *pVfs,
767   const char *zName,
768   sqlite3_syscall_ptr pCall
769 ){
770   return ORIGVFS(pVfs)->xSetSystemCall(ORIGVFS(pVfs),zName,pCall);
771 }
cksmGetSystemCall(sqlite3_vfs * pVfs,const char * zName)772 static sqlite3_syscall_ptr cksmGetSystemCall(
773   sqlite3_vfs *pVfs,
774   const char *zName
775 ){
776   return ORIGVFS(pVfs)->xGetSystemCall(ORIGVFS(pVfs),zName);
777 }
cksmNextSystemCall(sqlite3_vfs * pVfs,const char * zName)778 static const char *cksmNextSystemCall(sqlite3_vfs *pVfs, const char *zName){
779   return ORIGVFS(pVfs)->xNextSystemCall(ORIGVFS(pVfs), zName);
780 }
781 
782 /* Register the verify_checksum() SQL function.
783 */
cksmRegisterFunc(sqlite3 * db,char ** pzErrMsg,const sqlite3_api_routines * pApi)784 static int cksmRegisterFunc(
785   sqlite3 *db,
786   char **pzErrMsg,
787   const sqlite3_api_routines *pApi
788 ){
789   int rc;
790   if( db==0 ) return SQLITE_OK;
791   rc = sqlite3_create_function(db, "verify_checksum", 1,
792                    SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
793                    0, cksmVerifyFunc, 0, 0);
794 #ifdef SQLITE_CKSUMVFS_INIT_FUNCNAME
795   (void)sqlite3_create_function(db, SQLITE_CKSUMVFS_INIT_FUNCNAME, 1,
796                    SQLITE_UTF8|SQLITE_DIRECTONLY,
797                    0, cksmInitFunc, 0, 0);
798 #endif
799   return rc;
800 }
801 
802 /*
803 ** Register the cksum VFS as the default VFS for the system.
804 ** Also make arrangements to automatically register the "verify_checksum()"
805 ** SQL function on each new database connection.
806 */
cksmRegisterVfs(void)807 static int cksmRegisterVfs(void){
808   int rc = SQLITE_OK;
809   sqlite3_vfs *pOrig;
810   if( sqlite3_vfs_find("cksmvfs")!=0 ) return SQLITE_OK;
811   pOrig = sqlite3_vfs_find(0);
812   cksm_vfs.iVersion = pOrig->iVersion;
813   cksm_vfs.pAppData = pOrig;
814   cksm_vfs.szOsFile = pOrig->szOsFile + sizeof(CksmFile);
815   rc = sqlite3_vfs_register(&cksm_vfs, 1);
816   if( rc==SQLITE_OK ){
817     rc = sqlite3_auto_extension((void(*)(void))cksmRegisterFunc);
818   }
819   return rc;
820 }
821 
822 #if defined(SQLITE_CKSUMVFS_STATIC)
823 /* This variant of the initializer runs when the extension is
824 ** statically linked.
825 */
sqlite3_register_cksumvfs(const char * NotUsed)826 int sqlite3_register_cksumvfs(const char *NotUsed){
827   (void)NotUsed;
828   return cksmRegisterVfs();
829 }
sqlite3_unregister_cksumvfs(void)830 int sqlite3_unregister_cksumvfs(void){
831   if( sqlite3_vfs_find("cksmvfs") ){
832     sqlite3_vfs_unregister(&cksm_vfs);
833     sqlite3_cancel_auto_extension((void(*)(void))cksmRegisterFunc);
834   }
835   return SQLITE_OK;
836 }
837 #endif /* defined(SQLITE_CKSUMVFS_STATIC */
838 
839 #if !defined(SQLITE_CKSUMVFS_STATIC)
840 /* This variant of the initializer function is used when the
841 ** extension is shared library to be loaded at run-time.
842 */
843 #ifdef _WIN32
844 __declspec(dllexport)
845 #endif
846 /*
847 ** This routine is called by sqlite3_load_extension() when the
848 ** extension is first loaded.
849 ***/
sqlite3_cksumvfs_init(sqlite3 * db,char ** pzErrMsg,const sqlite3_api_routines * pApi)850 int sqlite3_cksumvfs_init(
851   sqlite3 *db,
852   char **pzErrMsg,
853   const sqlite3_api_routines *pApi
854 ){
855   int rc;
856   SQLITE_EXTENSION_INIT2(pApi);
857   (void)pzErrMsg; /* not used */
858   rc = cksmRegisterFunc(db, 0, 0);
859   if( rc==SQLITE_OK ){
860 
861   }
862   if( rc==SQLITE_OK ){
863     rc = cksmRegisterVfs();
864   }
865   if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY;
866   return rc;
867 }
868 #endif /* !defined(SQLITE_CKSUMVFS_STATIC) */
869