• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1From 90cffaced08c1214ad71a19837d04d70bea71e5d Mon Sep 17 00:00:00 2001
2From: MartinChoo <214582617@qq.com>
3Date: Sat, 26 Jul 2025 17:27:24 +0800
4Subject: [PATCH 1/4] Allow enable checksum through PRAGMA
5
6---
7 ext/misc/cksumvfs.c | 190 ++++++++++++++++++++++++++++++++++++--------
8 src/sqlite3.c       | 175 ++++++++++++++++++++++++++++++++--------
9 2 files changed, 296 insertions(+), 69 deletions(-)
10
11diff --git a/ext/misc/cksumvfs.c b/ext/misc/cksumvfs.c
12index 6f4c55c..8df1a5e 100644
13--- a/ext/misc/cksumvfs.c
14+++ b/ext/misc/cksumvfs.c
15@@ -73,12 +73,12 @@
16 ** encountered that contains an invalid checksum.
17 **
18 ** Checksumming only works on databases that have a reserve-bytes
19-** value of exactly 8.  The default value for reserve-bytes is 0.
20+** value of exactly 10.  The default value for reserve-bytes is 0.
21 ** Hence, newly created database files will omit the checksum by
22 ** default.  To create a database that includes a checksum, change
23-** the reserve-bytes value to 8 by runing:
24+** the reserve-bytes value to 10 by runing:
25 **
26-**    int n = 8;
27+**    int n = 10;
28 **    sqlite3_file_control(db, 0, SQLITE_FCNTL_RESERVE_BYTES, &n);
29 **
30 ** If you do this immediately after creating a new database file,
31@@ -92,12 +92,12 @@
32 ** If the database is in WAL mode, you should shutdown and
33 ** reopen all database connections before continuing.
34 **
35-** From the CLI, use the ".filectrl reserve_bytes 8" command,
36+** From the CLI, use the ".filectrl reserve_bytes 10" command,
37 ** followed by "VACUUM;".
38 **
39 ** Note that SQLite allows the number of reserve-bytes to be
40 ** increased but not decreased.  So if a database file already
41-** has a reserve-bytes value greater than 8, there is no way to
42+** has a reserve-bytes value greater than 10, there is no way to
43 ** activate checksumming on that database, other than to dump
44 ** and restore the database file.  Note also that other extensions
45 ** might also make use of the reserve-bytes.  Checksumming will
46@@ -140,26 +140,29 @@
47 ** "Verification" in this context means the feature that causes
48 ** SQLITE_IOERR_DATA errors if a checksum mismatch is detected while
49 ** reading.  Checksums are always kept up-to-date as long as the
50-** reserve-bytes value of the database is 8, regardless of the setting
51+** reserve-bytes value of the database is 10, regardless of the setting
52 ** of this pragma.  Checksum verification can be disabled (for example)
53 ** to do forensic analysis of a database that has previously reported
54 ** a checksum error.
55 **
56 ** The "checksum_verification" pragma will always respond with "0" if
57-** the database file does not have a reserve-bytes value of 8.  The
58+** the database file does not have a reserve-bytes value of 10.  The
59 ** pragma will return no rows at all if the cksumvfs extension is
60 ** not loaded.
61 **
62 ** IMPLEMENTATION NOTES
63 **
64-** The checksum is stored in the last 8 bytes of each page.  This
65+** The checksum is stored in the last 10 bytes of each page.  This
66 ** module only operates if the "bytes of reserved space on each page"
67-** value at offset 20 the SQLite database header is exactly 8.  If
68-** the reserved-space value is not 8, this module is a no-op.
69+** value at offset 20 the SQLite database header is exactly 10.  If
70+** the reserved-space value is not 10, this module is a no-op.
71 */
72 #if defined(SQLITE_AMALGAMATION) && !defined(SQLITE_CKSUMVFS_STATIC)
73 # define SQLITE_CKSUMVFS_STATIC
74 #endif
75+#ifndef SQLITE_EXPORT
76+# define SQLITE_EXPORT
77+#endif
78 #ifdef SQLITE_CKSUMVFS_STATIC
79 # include "sqlite3.h"
80 #else
81@@ -193,6 +196,21 @@ typedef struct CksmFile CksmFile;
82   typedef unsigned char u8;
83   typedef unsigned int u32;
84 #endif
85+typedef sqlite3_uint64 u64;
86+
87+/*
88+** Cksumvfs reserved area, file format:
89+** -----------------------------------------------
90+** |--  MagicNum  --|-- CksumType --|--Checksum--|
91+** |--1 byte, 0xff--|--1 byte, 0/1--|-- 8 bytes--|
92+** -----------------------------------------------
93+** MagicNum: 0xff, CksumType: 0 means without checksum, 1 means with checksum
94+*/
95+#define CKSUMVFS_MAGIC_NUM 0xff
96+#define CKSUMVFS_WITHOUT_CHECKSUM 0
97+#define CKSUMVFS_CALC_CHECKSUM 1
98+#define CKSUMVFS_CHECKSUM_SIZE 8
99+#define CKSUMVFS_RESERVED_SIZE (1+1+CKSUMVFS_CHECKSUM_SIZE)
100
101 /* Access to a lower-level VFS that (might) implement dynamic loading,
102 ** access to randomness, etc.
103@@ -205,8 +223,9 @@ struct CksmFile {
104   sqlite3_file base;    /* IO methods */
105   const char *zFName;   /* Original name of the file */
106   char computeCksm;     /* True to compute checksums.
107-                        ** Always true if reserve size is 8. */
108+                        ** Always true if reserve size is 10. */
109   char verifyCksm;      /* True to verify checksums */
110+  char disableVerify;   /* True if checksum verification is disabled */
111   char isWal;           /* True if processing a WAL file */
112   char inCkpt;          /* Currently doing a checkpoint */
113   CksmFile *pPartner;   /* Ptr from WAL to main-db, or from main-db to WAL */
114@@ -307,6 +326,79 @@ static const sqlite3_io_methods cksm_io_methods = {
115   + (((x)&0x00FF0000)>>8)  + (((x)&0xFF000000)>>24) \
116 )
117
118+static u64 cksmComputeDeepFastLE(void *src, int srcLen){
119+  u32 s1 = 0, s2 = 0;
120+  u32 s3 = 0, s4 = 0;
121+  u32 s5 = 0, s6 = 0;
122+  u32 s7 = 0, s8 = 0;
123+  u32 *aData = (u32*)src;
124+  u32 *aEnd = aData + srcLen/4;
125+  u64 aOut;
126+  u8 *u8p_aOut = (u8*)&aOut;
127+  while( aData + 32 <= aEnd ){
128+    s1 += *aData++ + s2;
129+    s2 += *aData++ + s1;
130+    s1 += *aData++ + s2;
131+    s2 += *aData++ + s1;
132+    s1 += *aData++ + s2;
133+    s2 += *aData++ + s1;
134+    s1 += *aData++ + s2;
135+    s2 += *aData++ + s1;
136+
137+    s3 += *aData++ + s4;
138+    s4 += *aData++ + s3;
139+    s3 += *aData++ + s4;
140+    s4 += *aData++ + s3;
141+    s3 += *aData++ + s4;
142+    s4 += *aData++ + s3;
143+    s3 += *aData++ + s4;
144+    s4 += *aData++ + s3;
145+
146+    s5 += *aData++ + s6;
147+    s6 += *aData++ + s5;
148+    s5 += *aData++ + s6;
149+    s6 += *aData++ + s5;
150+    s5 += *aData++ + s6;
151+    s6 += *aData++ + s5;
152+    s5 += *aData++ + s6;
153+    s6 += *aData++ + s5;
154+
155+    s7 += *aData++ + s8;
156+    s8 += *aData++ + s7;
157+    s7 += *aData++ + s8;
158+    s8 += *aData++ + s7;
159+    s7 += *aData++ + s8;
160+    s8 += *aData++ + s7;
161+    s7 += *aData++ + s8;
162+    s8 += *aData++ + s7;
163+
164+    s3 += 13*s1 + 21*s2;
165+    s4 += 21*s1 + 34*s2;
166+
167+    s5 += 13*s3 + 21*s4;
168+    s6 += 21*s3 + 34*s4;
169+
170+    s7 += 13*s5 + 21*s6;
171+    s8 += 21*s5 + 34*s6;
172+
173+    s1 = s7;
174+    s2 = s8;
175+    s3 = 0;
176+    s4 = 0;
177+    s5 = 0;
178+    s6 = 0;
179+    s7 = 0;
180+    s8 = 0;
181+  }
182+  while( aData < aEnd ){
183+    s1 = *aData++ + s1 + s2;
184+    s2 = *aData++ + s1 + s2;
185+  }
186+  memcpy(u8p_aOut, &s1, 4);
187+  memcpy(u8p_aOut + 4, &s2, 4);
188+  return aOut;
189+}
190+
191 /* Compute a checksum on a buffer */
192 static void cksmCompute(
193   u8 *a,           /* Content to be checksummed */
194@@ -324,10 +416,8 @@ static void cksmCompute(
195
196   if( 1 == *(u8*)&x ){
197     /* Little-endian */
198-    do {
199-      s1 += *aData++ + s2;
200-      s2 += *aData++ + s1;
201-    }while( aData<aEnd );
202+    *(u64*)aOut = cksmComputeDeepFastLE(a, nByte);
203+    return;
204   }else{
205     /* Big-endian */
206     do {
207@@ -356,14 +446,16 @@ static void cksmVerifyFunc(
208 ){
209   int nByte;
210   u8 *data;
211-  u8 cksum[8];
212+  u8 cksum[CKSUMVFS_CHECKSUM_SIZE];
213   data = (u8*)sqlite3_value_blob(argv[0]);
214   if( data==0 ) return;
215   if( sqlite3_value_type(argv[0])!=SQLITE_BLOB ) return;
216   nByte = sqlite3_value_bytes(argv[0]);
217   if( nByte<512 || nByte>65536 || (nByte & (nByte-1))!=0 ) return;
218-  cksmCompute(data, nByte-8, cksum);
219-  sqlite3_result_int(context, memcmp(data+nByte-8,cksum,8)==0);
220+  if( data[nByte-CKSUMVFS_RESERVED_SIZE]!=CKSUMVFS_MAGIC_NUM
221+    || data[nByte-CKSUMVFS_RESERVED_SIZE+1]!=CKSUMVFS_CALC_CHECKSUM ) return;
222+  cksmCompute(data, nByte-CKSUMVFS_CHECKSUM_SIZE, cksum);
223+  sqlite3_result_int(context, memcmp(data+nByte-CKSUMVFS_CHECKSUM_SIZE,cksum,CKSUMVFS_CHECKSUM_SIZE)==0);
224 }
225
226 #ifdef SQLITE_CKSUMVFS_INIT_FUNCNAME
227@@ -375,7 +467,7 @@ static void cksmVerifyFunc(
228 **
229 **   sqlite3_file_control(db, SCHEMANAME, SQLITE_FCNTL_RESERVE_BYTE, &n);
230 **
231-** In order to set the reserve bytes value to 8, so that cksumvfs will
232+** In order to set the reserve bytes value to 10, so that cksumvfs will
233 ** operation.  This feature is provided (if and only if the
234 ** SQLITE_CKSUMVFS_INIT_FUNCNAME compile-time option is set to a string
235 ** which is the name of the SQL function) so as to provide the ability
236@@ -393,7 +485,7 @@ static void cksmInitFunc(
237   int argc,
238   sqlite3_value **argv
239 ){
240-  int nByte = 8;
241+  int nByte = CKSUMVFS_RESERVED_SIZE;
242   const char *zSchemaName = (const char*)sqlite3_value_text(argv[0]);
243   sqlite3 *db = sqlite3_context_db_handle(context);
244   sqlite3_file_control(db, zSchemaName, SQLITE_FCNTL_RESERVE_BYTES, &nByte);
245@@ -422,9 +514,11 @@ static int cksmClose(sqlite3_file *pFile){
246 static void cksmSetFlags(CksmFile *p, int hasCorrectReserveSize){
247   if( hasCorrectReserveSize!=p->computeCksm ){
248     p->computeCksm = p->verifyCksm = hasCorrectReserveSize;
249+    if( p->disableVerify ) p->verifyCksm = 0;
250     if( p->pPartner ){
251       p->pPartner->verifyCksm = hasCorrectReserveSize;
252       p->pPartner->computeCksm = hasCorrectReserveSize;
253+      if( p->pPartner->disableVerify ) p->pPartner->verifyCksm = 0;
254     }
255   }
256 }
257@@ -435,9 +529,11 @@ static void EncodeReservedBytesIntoBase16(const u8 *reserved, int len, char *enc
258     *encodeStr++ = baseCode[(reserved[i] >> 4) & 0x0F];
259     *encodeStr++ = baseCode[reserved[i] & 0x0F];
260   }
261-  *encodeStr = '0';
262+  *encodeStr = '\0';
263 }
264
265+#define CKSUM_HEX_LEN (CKSUMVFS_CHECKSUM_SIZE+1)*2
266+
267 /*
268 ** Read data from a cksm-file.
269 */
270@@ -452,11 +548,11 @@ static int cksmRead(
271   pFile = ORIGFILE(pFile);
272   rc = pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst);
273   if( rc==SQLITE_OK ){
274-    if( iOfst==0 && iAmt>=100 && (
275+    if( (iOfst==0 || p->isWal) && iAmt>=100 && (
276           memcmp(zBuf,"SQLite format 3",16)==0 || memcmp(zBuf,"ZV-",3)==0
277     )){
278       u8 *d = (u8*)zBuf;
279-      char hasCorrectReserveSize = (d[20]==8);
280+      char hasCorrectReserveSize = (d[20]==CKSUMVFS_RESERVED_SIZE);
281       cksmSetFlags(p, hasCorrectReserveSize);
282     }
283     /* Verify the checksum if
284@@ -464,18 +560,28 @@ static int cksmRead(
285     **        database page, only support pageSize:4K
286     **    (2) checksum verification is enabled
287     **    (3) we are not in the middle of checkpoint
288+    **    (4) magic number should be 0xff
289+    **    (5) checksum type should be 1, 0 means without checksum
290     */
291     if( iAmt==4096           /* (1) */
292      && p->verifyCksm       /* (2) */
293      && !p->inCkpt          /* (3) */
294     ){
295-      u8 cksum[8];
296-      cksmCompute((u8*)zBuf, iAmt-8, cksum);
297-      if( memcmp((u8*)zBuf+iAmt-8, cksum, 8)!=0 ){
298-        char expect[18] = {0};
299-        char actual[18] = {0};
300-        EncodeReservedBytesIntoBase16((u8 *)zBuf+iAmt-8, 8, expect, 18);
301-        EncodeReservedBytesIntoBase16(cksum, 8, actual, 18);
302+      if( ((u8*)zBuf)[iAmt-CKSUMVFS_RESERVED_SIZE]!=CKSUMVFS_MAGIC_NUM ){ /* (4) */
303+        sqlite3_log(SQLITE_IOERR_DATA, "unrecognized format, offset %lld of \"%s\", amt:%d", iOfst, p->zFName, iAmt);
304+        return SQLITE_IOERR_DATA;
305+      }
306+      if( ((u8*)zBuf)[iAmt-CKSUMVFS_RESERVED_SIZE+1]==CKSUMVFS_WITHOUT_CHECKSUM ){ /* (5) */
307+        return rc;
308+      }
309+      u8 cksum[CKSUMVFS_CHECKSUM_SIZE];
310+      cksmCompute((u8*)zBuf, iAmt-CKSUMVFS_CHECKSUM_SIZE, cksum);
311+      if( memcmp((u8*)zBuf+iAmt-CKSUMVFS_CHECKSUM_SIZE, cksum, CKSUMVFS_CHECKSUM_SIZE)!=0 ){
312+        char expect[CKSUM_HEX_LEN] = {0};
313+        char actual[CKSUM_HEX_LEN] = {0};
314+        EncodeReservedBytesIntoBase16((u8 *)zBuf+iAmt-CKSUMVFS_CHECKSUM_SIZE, CKSUMVFS_CHECKSUM_SIZE, expect,
315+          CKSUM_HEX_LEN);
316+        EncodeReservedBytesIntoBase16(cksum, CKSUMVFS_CHECKSUM_SIZE, actual, CKSUM_HEX_LEN);
317         sqlite3_log(SQLITE_IOERR_DATA, "checksum fault offset %lld of \"%s\", amt:%d, expect:%s, actual:%s",
318            iOfst, p->zFName, iAmt, expect, actual);
319         rc = SQLITE_IOERR_DATA;
320@@ -496,11 +602,11 @@ static int cksmWrite(
321 ){
322   CksmFile *p = (CksmFile *)pFile;
323   pFile = ORIGFILE(pFile);
324-  if( iOfst==0 && iAmt>=100 && (
325+  if( (iOfst==0 || p->isWal) && iAmt>=100 && (
326         memcmp(zBuf,"SQLite format 3",16)==0 || memcmp(zBuf,"ZV-",3)==0
327   )){
328     u8 *d = (u8*)zBuf;
329-    char hasCorrectReserveSize = (d[20]==8);
330+    char hasCorrectReserveSize = (d[20]==CKSUMVFS_RESERVED_SIZE);
331     cksmSetFlags(p, hasCorrectReserveSize);
332   }
333   /* If the write size is appropriate for a database page and if
334@@ -513,7 +619,11 @@ static int cksmWrite(
335    && p->computeCksm
336    && !p->inCkpt
337   ){
338-    cksmCompute((u8*)zBuf, iAmt-8, ((u8*)zBuf)+iAmt-8);
339+    ((u8*)zBuf)[iAmt-CKSUMVFS_RESERVED_SIZE]=CKSUMVFS_MAGIC_NUM;
340+    ((u8*)zBuf)[iAmt-CKSUMVFS_RESERVED_SIZE+1]=p->verifyCksm ? CKSUMVFS_CALC_CHECKSUM : CKSUMVFS_WITHOUT_CHECKSUM;
341+    if( p->verifyCksm ){ /* do not compute checksum if verifyCksm is off */
342+      cksmCompute((u8*)zBuf, iAmt-CKSUMVFS_CHECKSUM_SIZE, ((u8*)zBuf)+iAmt-CKSUMVFS_CHECKSUM_SIZE);
343+    }
344   }
345   return pFile->pMethods->xWrite(pFile, zBuf, iAmt, iOfst);
346 }
347@@ -585,11 +695,16 @@ static int cksmFileControl(sqlite3_file *pFile, int op, void *pArg){
348          || sqlite3_stricmp("yes",zArg)==0
349          || sqlite3_stricmp("on",zArg)==0
350         ){
351+          p->disableVerify = 0;
352           p->verifyCksm = p->computeCksm;
353         }else{
354+          p->disableVerify = 1;
355           p->verifyCksm = 0;
356         }
357-        if( p->pPartner ) p->pPartner->verifyCksm = p->verifyCksm;
358+        if( p->pPartner ){
359+          p->pPartner->disableVerify = p->disableVerify;
360+          p->pPartner->verifyCksm = p->verifyCksm;
361+        }
362       }
363       azArg[0] = sqlite3_mprintf("%d",p->verifyCksm);
364       return SQLITE_OK;
365@@ -729,9 +844,12 @@ static int cksmOpen(
366     p->pPartner->pPartner = p;
367     p->isWal = 1;
368     p->computeCksm = p->pPartner->computeCksm;
369+    p->verifyCksm = p->pPartner->verifyCksm;
370+    p->disableVerify = p->pPartner->disableVerify;
371   }else{
372     p->isWal = 0;
373     p->computeCksm = 0;
374+    p->verifyCksm = 0;
375   }
376   p->zFName = zName;
377 cksm_open_done:
378@@ -902,6 +1020,10 @@ int sqlite3_cksumvfs_init(
379 #endif /* !defined(SQLITE_CKSUMVFS_STATIC) */
380
381 #ifdef SQLITE_CKSUMVFS_STATIC
382+sqlite3_file *sqlite3_get_orig_file(sqlite3_file *file) {
383+  return ORIGFILE(file);
384+}
385+
386 struct sqlite3_api_routines_cksumvfs {
387   int (*register_cksumvfs)(const char *);
388   int (*unregister_cksumvfs)();
389diff --git a/src/sqlite3.c b/src/sqlite3.c
390index 4c538a1..c84348b 100644
391--- a/src/sqlite3.c
392+++ b/src/sqlite3.c
393@@ -139875,6 +139875,10 @@ static int integrityCheckResultRow(Vdbe *v){
394   return addr;
395 }
396
397+#ifdef SQLITE_CKSUMVFS_STATIC
398+static int PragmaCksumPersistEnable(sqlite3 *db, int iDb, Parse *parse, const char *zLeft, const char *zRight);
399+#endif
400+
401 /*
402 ** Process a pragma statement.
403 **
404@@ -139997,7 +140001,15 @@ SQLITE_PRIVATE void sqlite3Pragma(
405     /* PragmaMetaDoubleWrie executes internal */
406     goto pragma_out;
407   }
408-#endif
409+#endif /* SQLITE_META_DWR */
410+
411+#ifdef SQLITE_CKSUMVFS_STATIC
412+  if( PragmaCksumPersistEnable(db, iDb, pParse, zLeft, zRight) ){
413+    /* PragmaCksumPersistEnable executes internal */
414+    goto pragma_out;
415+  }
416+#endif /* SQLITE_CKSUMVFS_STATIC */
417+
418   /* Locate the pragma in the lookup table */
419   pPragma = pragmaLocate(zLeft);
420   if( pPragma==0 ){
421@@ -184259,16 +184271,12 @@ SQLITE_API int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, vo
422       *(unsigned int*)pArg = sqlite3PagerDataVersion(pPager);
423       rc = SQLITE_OK;
424     }else if( op==SQLITE_FCNTL_RESERVE_BYTES ){
425-#ifndef SQLITE_CKSUMVFS_STATIC
426-      rc = SQLITE_OK;
427-#else
428       int iNew = *(int*)pArg;
429       *(int*)pArg = sqlite3BtreeGetRequestedReserve(pBtree);
430       if( iNew>=0 && iNew<=255 ){
431         sqlite3BtreeSetPageSize(pBtree, 0, iNew, 0);
432       }
433       rc = SQLITE_OK;
434-#endif
435     }else if( op==SQLITE_FCNTL_RESET_CACHE ){
436       sqlite3BtreeClearCache(pBtree);
437       rc = SQLITE_OK;
438@@ -256094,7 +256102,71 @@ export_finish:
439   return;
440 }
441 /************** End file hw_codec.c *****************************************/
442-#endif
443+#endif /* SQLITE_HAS_CODEC */
444+
445+#ifdef SQLITE_CKSUMVFS_STATIC
446+#define SQLITE_CKSUMVFS_RESERVED_SIZE 10
447+static int PragmaCksumPersistEnable(sqlite3 *db, int iDb, Parse *parse, const char *zLeft, const char *zRight){
448+  Btree *pBt = db->aDb[iDb].pBt;
449+  if( pBt==NULL || zLeft==NULL || sqlite3StrICmp(zLeft, "checksum_persist_enable")!=0 ){
450+    return 0;
451+  }
452+  sqlite3_mutex_enter(db->mutex);
453+  int reserved = sqlite3BtreeGetRequestedReserve(pBt);
454+  sqlite3_mutex_leave(db->mutex);
455+  if( zRight==NULL ){
456+    // Try to get the state of cksumvfs enable
457+    Vdbe *v = sqlite3GetVdbe(parse);
458+    sqlite3VdbeSetNumCols(v, 1);
459+    sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "checksum_persist_enable", SQLITE_STATIC);
460+    sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, (reserved==SQLITE_CKSUMVFS_RESERVED_SIZE ? "ON" : "OFF"), 0);
461+    sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
462+  }else if( sqlite3StrICmp(zRight, "on")==0 ){
463+    // Enabled
464+    if( reserved==0 ){
465+      Pager *pPager = pBt->pBt->pPager;
466+      if( (pPager->memDb!=0 && pPager->dbSize!=0) || sqlite3PcacheRefCount(pPager->pPCache)!=0 ){
467+        parse->nErr++;
468+        parse->rc = SQLITE_MISUSE;
469+        sqlite3_log(SQLITE_MISUSE, "checksum_persist_enable can only be enabled before any other ops.");
470+        return 1;
471+      }
472+      int reservedTarget = SQLITE_CKSUMVFS_RESERVED_SIZE;
473+      int res = sqlite3_file_control(db, 0, SQLITE_FCNTL_RESERVE_BYTES, &reservedTarget);
474+      if( res!=SQLITE_OK ){
475+        parse->nErr++;
476+        parse->rc = res;
477+        sqlite3_log(res, "try set reserved region to turn on checksum_persist_enable");
478+      }
479+    }else if( reserved!=SQLITE_CKSUMVFS_RESERVED_SIZE ){
480+      // undefined reserved bytes
481+      parse->nErr++;
482+      parse->rc = SQLITE_MISUSE;
483+      sqlite3_log(SQLITE_MISUSE, "unrecognized reserved region led to cksumvfs cannot enable");
484+    }
485+  }
486+  return 1;
487+}
488+extern sqlite3_file *sqlite3_get_orig_file(sqlite3_file *file);
489+#else
490+static sqlite3_file *sqlite3_get_orig_file(sqlite3_file *file) {
491+  return file;
492+}
493+#endif /* SQLITE_CKSUMVFS_STATIC */
494+
495+#if SQLITE_OS_UNIX
496+#define SQLITE_CHECK_FILE_ID_UNIX 1
497+#define SQLITE_CHECK_FILE_ID_CKSM 2
498+
499+// checkFileId should not be 0, it must be SQLITE_CHECK_FILE_ID_UNIX(1) or SQLITE_CHECK_FILE_ID_CKSM(2)
500+static unixFile *Sqlite3GetUnixFile(sqlite3_file *file, u8 checkFileId) {
501+  if (checkFileId == SQLITE_CHECK_FILE_ID_UNIX) {
502+    return (unixFile*)file;
503+  }
504+  return (unixFile*)sqlite3_get_orig_file(file);
505+}
506+#endif /* SQLITE_OS_UNIX */
507+
508 #ifdef SQLITE_META_DWR
509 #define META_DWR_MAX_PAGES 500
510 #define META_DWR_MAGIC 0x234A86D9
511@@ -256126,7 +256198,7 @@ typedef struct MetaDwrHdr {
512   Pgno *pages;
513   u32 pageBufSize;
514   u8 hdrValid;
515-  u8 checkFileId;
516+  u8 checkFileId; /* 0, means no check, 1 means vfs:unix, 2 means vfs:cksm */
517   u16 needSync;
518   i64 lastSyncTime;
519 } MetaDwrHdr;
520@@ -256140,7 +256212,7 @@ typedef struct MetaDwrHdr {
521 static int MetaDwrHeaderSimpleCheck(Pager *pPager, MetaDwrHdr *hdr) {
522 #if SQLITE_OS_UNIX
523   if (hdr->checkFileId) {
524-    unixFile *fd = (unixFile *)pPager->fd;
525+    unixFile *fd = Sqlite3GetUnixFile(pPager->fd, hdr->checkFileId);
526     if (fd == NULL || fd->pInode == NULL || pPager->pVfs == NULL) {
527       return SQLITE_INTERNAL;
528     }
529@@ -256150,7 +256222,7 @@ static int MetaDwrHeaderSimpleCheck(Pager *pPager, MetaDwrHdr *hdr) {
530       return SQLITE_IOERR_DATA;
531     }
532   }
533-#endif
534+#endif /* SQLITE_OS_UNIX */
535   if (hdr->pageCnt > META_DWR_MAX_PAGES || hdr->version != META_DWR_VERSION ||
536       hdr->magic != META_DWR_MAGIC || hdr->checkSum != META_DWR_MAGIC) {
537     sqlite3_log(SQLITE_IOERR_DATA, "Meta dwr file check wrong pageCnt %u, version %u, magic %u, checkSum %u",
538@@ -256382,7 +256454,15 @@ static MetaDwrHdr *AllocInitMetaHeaderDwr(Pager *pPager) {
539     MetaDwrReleaseHdr(hdr);
540     return NULL;
541   }
542-  hdr->checkFileId = (pPager->pVfs != NULL && sqlite3_stricmp(pPager->pVfs->zName, "unix") == 0);
543+  if (pPager->pVfs==NULL) {
544+    hdr->checkFileId = 0;
545+  } else if (sqlite3_stricmp(pPager->pVfs->zName, "unix") == 0) {
546+    hdr->checkFileId = SQLITE_CHECK_FILE_ID_UNIX;
547+  } else if (sqlite3_stricmp(pPager->pVfs->zName, "cksmvfs") == 0) {
548+    hdr->checkFileId = SQLITE_CHECK_FILE_ID_CKSM;
549+  } else {
550+    hdr->checkFileId = 0;
551+  }
552   return hdr;
553 }
554
555@@ -256395,7 +256475,7 @@ static void MetaDwrCloseFile(Pager *pPager) {
556     osMunmap(pPager->metaMapPage, META_DWR_HEADER_PAGE_SIZE);
557     pPager->metaMapPage = NULL;
558   }
559-#endif
560+#endif /* SQLITE_OS_UNIX */
561   if (pPager->metaHdr && pPager->metaHdr->needSync > 0) {
562     (void)sqlite3OsSync(pPager->metaFd, SQLITE_SYNC_NORMAL);
563   }
564@@ -256490,10 +256570,10 @@ static void MetaDwrUpdateHeaderDbInfo(BtShared *pBt) {
565   } else {
566     hdr->mxFrameInWal = 0;
567   }
568-#endif
569+#endif /* SQLITE_OMIT_WAL */
570 #if SQLITE_OS_UNIX
571   if (hdr->checkFileId) {
572-    unixFile *fd = (unixFile *)pBt->pPager->fd;
573+    unixFile *fd = Sqlite3GetUnixFile(pBt->pPager->fd, hdr->checkFileId);
574     if (fd == NULL || fd->pInode == NULL) {
575       sqlite3_log(SQLITE_WARNING_DUMP, "update meta header invalid fd");
576       hdr->hdrValid = 0;
577@@ -256501,7 +256581,7 @@ static void MetaDwrUpdateHeaderDbInfo(BtShared *pBt) {
578     }
579     hdr->dbFileInode = fd->pInode->fileId.ino;
580   }
581-#endif
582+#endif /* SQLITE_OS_UNIX */
583   hdr->freeListPageNo = sqlite3Get4byte(dbHdrInfo + 4);
584   hdr->freeListPageCnt = sqlite3Get4byte(dbHdrInfo + 8);
585   hdr->schemaCookie = sqlite3Get4byte(dbHdrInfo + 12);
586@@ -256597,7 +256677,7 @@ static int MetaDwrWriteOnePage(Btree *pBt, PgHdr *pPage, MetaDwrHdr *hdr, u8 cur
587     return SQLITE_NOMEM;
588 #else
589   pData = pPage->pData;
590-#endif
591+#endif /* SQLITE_HAS_CODEC */
592   rc = sqlite3OsWrite(pPager->metaFd, pData, pageSz, ofs);
593   if (rc != SQLITE_OK) {
594     return rc;
595@@ -256659,7 +256739,7 @@ static int MetaDwrCheckPerm(sqlite3_vfs *pVfs, u8 openCreate, char *metaPath) {
596   if (openCreate) {
597     sqlite3_log(SQLITE_WARNING_DUMP, "Meta double write disabled, sysno %d", errno);
598   }
599-#endif
600+#endif /* OS_FEATURE */
601   return rc;
602 }
603
604@@ -256702,7 +256782,7 @@ static int MetaDwrOpenFile(Pager *pPager, u8 openCreate) {
605       pPager->metaMapPage = page;
606     }
607   }
608-#endif
609+#endif /* SQLITE_OS_UNIX */
610   pPager->metaFd = metaFd;
611 INIT_META_OUT:
612   sqlite3EndBenignMalloc();
613@@ -256826,7 +256906,7 @@ static int MetaDwrRecoverHeadPage(
614       goto RELEASE_OUT;
615     }
616   }
617-#endif
618+#endif /* SQLITE_OMIT_WAL */
619   rc = SQLITE_NOTADB;
620   for (u32 i = 0; i < hdr->pageCnt; i++) {
621     if (hdr->pages[i] != pgno) {
622@@ -257076,15 +257156,24 @@ CHK_RESTORE_OUT:
623 static inline u8 IsConnectionValidForCheck(Pager *pPager)
624 {
625 #if SQLITE_OS_UNIX
626-    unixFile *fd = (unixFile *)pPager->fd;
627-    // unix and only one connection exist
628-    if (fd == NULL || fd->pInode == NULL || pPager->pVfs == NULL ||
629-      sqlite3_stricmp(pPager->pVfs->zName, "unix") != 0 || fd->pInode->nRef != 1) {
630-      return 0;
631-    }
632-    return 1;
633-#else
634+  if (pPager->pVfs == NULL) {
635     return 0;
636+  }
637+  if (sqlite3_stricmp(pPager->pVfs->zName, "unix") != 0 && sqlite3_stricmp(pPager->pVfs->zName, "cksmvfs") != 0) {
638+    return 0;
639+  }
640+  u8 checkFileId = SQLITE_CHECK_FILE_ID_UNIX;
641+  if (sqlite3_stricmp(pPager->pVfs->zName, "cksmvfs") == 0) {
642+    checkFileId = SQLITE_CHECK_FILE_ID_CKSM;
643+  }
644+  unixFile *fd = Sqlite3GetUnixFile(pPager->fd, checkFileId);
645+  // unix and only one connection exist
646+  if (fd == NULL || fd->pInode == NULL || fd->pInode->nRef != 1) {
647+    return 0;
648+  }
649+  return 1;
650+#else
651+  return 0;
652 #endif
653 }
654
655@@ -257099,7 +257188,7 @@ static int MetaDwrOpenAndCheck(Btree *pBt)
656   if (pPager->xCodec) {
657     return SQLITE_OK;
658   }
659-#endif
660+#endif /* SQLITE_HAS_CODEC */
661   sqlite3BtreeEnter(pBt);
662   int rc = SQLITE_OK;
663   int openedTransaction = 0;
664@@ -257144,7 +257233,7 @@ static void MetaDwrDisable(Btree *pBt)
665   if (pPager->xCodec) {
666     return;
667   }
668-#endif
669+#endif /* SQLITE_HAS_CODEC */
670   sqlite3BtreeEnter(pBt);
671   MetaDwrCloseFile(pPager);
672   MetaDwrReleaseHdr(pPager->metaHdr);
673@@ -257159,7 +257248,8 @@ static void MetaDwrDisable(Btree *pBt)
674   }
675   sqlite3BtreeLeave(pBt);
676 }
677-#endif
678+#endif /* SQLITE_META_DWR */
679+
680 #if SQLITE_OS_UNIX
681 #include <unistd.h>
682 #include <sys/syscall.h>
683@@ -257368,10 +257458,17 @@ static void DumpLocksByWal(Wal *pWal)
684     sqlite3_log(SQLITE_ERROR, "Wal ptr is NULL!");
685     return;
686   }
687-  if (pWal->pVfs == NULL || sqlite3_stricmp(pWal->pVfs->zName, "unix") != 0) {
688+  u8 checkFileId = 0;
689+  if (pWal->pVfs==NULL) {
690+    return;
691+  } else if (sqlite3_stricmp(pWal->pVfs->zName, "unix") == 0) {
692+    checkFileId = SQLITE_CHECK_FILE_ID_UNIX;
693+  } else if (sqlite3_stricmp(pWal->pVfs->zName, "cksmvfs") == 0) {
694+    checkFileId = SQLITE_CHECK_FILE_ID_CKSM;
695+  } else {
696     return;
697   }
698-  DumpLocksInfo((unixFile *)(pWal->pDbFd), 1);
699+  DumpLocksInfo(Sqlite3GetUnixFile(pWal->pDbFd, checkFileId), 1);
700 }
701 #endif /* #ifndef SQLITE_OMIT_WAL */
702
703@@ -257381,13 +257478,20 @@ static void DumpLocksByPager(Pager *pPager)
704     sqlite3_log(SQLITE_ERROR, "Pager ptr is NULL!");
705     return;
706   }
707-  if (pPager->pVfs == NULL || sqlite3_stricmp(pPager->pVfs->zName, "unix") != 0) {
708+  u8 checkFileId = 0;
709+  if (pPager->pVfs==NULL) {
710+    return;
711+  } else if (sqlite3_stricmp(pPager->pVfs->zName, "unix") == 0) {
712+    checkFileId = SQLITE_CHECK_FILE_ID_UNIX;
713+  } else if (sqlite3_stricmp(pPager->pVfs->zName, "cksmvfs") == 0) {
714+    checkFileId = SQLITE_CHECK_FILE_ID_CKSM;
715+  } else {
716     return;
717   }
718 #ifndef SQLITE_OMIT_WAL
719-  DumpLocksInfo((unixFile *)(pPager->fd), pPager->pWal != NULL);
720+  DumpLocksInfo(Sqlite3GetUnixFile(pPager->fd, checkFileId), pPager->pWal != NULL);
721 #else /* #ifndef SQLITE_OMIT_WAL */
722-  DumpLocksInfo((unixFile *)(pPager->fd), 0);
723+  DumpLocksInfo(Sqlite3GetUnixFile(pPager->fd, checkFileId), 0);
724 #endif /* #ifndef SQLITE_OMIT_WAL */
725 }
726 #endif /* SQLITE_OS_UNIX */
727@@ -258499,7 +258603,8 @@ static const sqlite3_api_routines_cksumvfs sqlite3CksumvfsApis = {
728 };
729
730 EXPORT_SYMBOLS const sqlite3_api_routines_cksumvfs *sqlite3_export_cksumvfs_symbols = &sqlite3CksumvfsApis;
731-#endif
732+#endif /* SQLITE_CKSUMVFS_STATIC */
733+
734 struct sqlite3_api_routines_extra {
735   int (*initialize)();
736   int (*config)(int,...);
737--
7382.47.0.windows.2
739
740