• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1From d6f2f7b1d7d288bc0cb0ff998052bb8efac40f7f Mon Sep 17 00:00:00 2001
2From: MartinChoo <214582617@qq.com>
3Date: Wed, 23 Jul 2025 17:41:00 +0800
4Subject: [PATCH 04/12] Support meta recovery
5
6---
7 src/sqlite3.c | 1156 ++++++++++++++++++++++++++++++++++++++++++++++++-
8 1 file changed, 1151 insertions(+), 5 deletions(-)
9
10diff --git a/src/sqlite3.c b/src/sqlite3.c
11index 6f423a9..dc09c3c 100644
12--- a/src/sqlite3.c
13+++ b/src/sqlite3.c
14@@ -786,6 +786,7 @@ SQLITE_API int sqlite3_exec(
15 #define SQLITE_WARNING     28   /* Warnings from sqlite3_log() */
16 #define SQLITE_ROW         100  /* sqlite3_step() has another row ready */
17 #define SQLITE_DONE        101  /* sqlite3_step() has finished executing */
18+#define SQLITE_META_RECOVERED     66   /* meta page recovered*/
19 /* end-of-error-codes */
20
21 /*
22@@ -57472,6 +57473,26 @@ struct PagerSavepoint {
23 #endif
24 };
25
26+#if !defined(SQLITE_OS_UNIX) && defined(SQLITE_META_DWR)
27+#undef SQLITE_META_DWR
28+#endif
29+
30+#ifdef SQLITE_META_DWR
31+static int PragmaMetaDoubleWrie(sqlite3 *db, int iDb, Parse *parse, const char *zLeft, const char *zRight);
32+typedef struct MetaDwrHdr MetaDwrHdr;
33+static int MetaDwrWriteHeader(Pager *pPager, MetaDwrHdr *hdr);
34+static int MetaDwrUpdateMetaPages(Btree *pBt);
35+static void MetaDwrPagerRelease(Pager *pPager);
36+static int MetaDwrOpenFile(Pager *pPager, u8 openCreate);
37+static void MetaDwrCheckVacuum(BtShared *pBt);
38+static int MetaDwrRecoverAndBeginTran(Btree *pBt, int wrflag, int *pSchemaVersion);
39+static int MetaDwrOpenAndCheck(Btree *pBt);
40+static void MetaDwrDisable(Btree *pBt);
41+#define META_HEADER_CHANGED 1
42+#define META_SCHEMA_CHANGED 2
43+#define META_IN_RECOVERY 1
44+#define META_RECOVER_SUCCESS 2
45+#endif
46 /*
47 ** Bits of the Pager.doNotSpill flag.  See further description below.
48 */
49@@ -57737,6 +57758,13 @@ struct Pager {
50   Wal *pWal;                  /* Write-ahead log used by "journal_mode=wal" */
51   char *zWal;                 /* File name for write-ahead log */
52 #endif
53+#ifdef SQLITE_META_DWR
54+  u8 metaChanged;
55+  sqlite3_file *metaFd;
56+  MetaDwrHdr *metaHdr;
57+  void *metaMapPage;
58+  int (*xGetMethod)(Pager*,Pgno,DbPage**,int); /* Routine to fetch a patch */
59+#endif
60 };
61
62 /*
63@@ -58096,7 +58124,11 @@ static void setGetterMethod(Pager *pPager){
64     pPager->xGet = getPageMMap;
65 #endif /* SQLITE_MAX_MMAP_SIZE>0 */
66   }else{
67+#ifdef SQLITE_META_DWR
68+    pPager->xGet = pPager->xGetMethod ? pPager->xGetMethod : getPageNormal;
69+#else
70     pPager->xGet = getPageNormal;
71+#endif
72   }
73 }
74
75@@ -59170,7 +59202,14 @@ static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){
76     }
77     sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize);
78   }
79-
80+#ifdef SQLITE_META_DWR
81+  if (bCommit && pPager->metaChanged != 0) {
82+    sqlite3BeginBenignMalloc();
83+    (void)MetaDwrWriteHeader(pPager, pPager->metaHdr);
84+    sqlite3EndBenignMalloc();
85+    pPager->metaChanged = 0;
86+  }
87+#endif
88   if( pagerUseWal(pPager) ){
89     /* Drop the WAL write-lock, if any. Also, if the connection was in
90     ** locking_mode=exclusive mode but is no longer, drop the EXCLUSIVE
91@@ -61269,6 +61308,9 @@ SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager, sqlite3 *db){
92     sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize,a);
93     pPager->pWal = 0;
94   }
95+#endif
96+#ifdef SQLITE_META_DWR
97+  MetaDwrPagerRelease(pPager);
98 #endif
99   pager_reset(pPager);
100   if( MEMDB ){
101@@ -62112,7 +62154,11 @@ act_like_temp_file:
102     rc = sqlite3PcacheOpen(szPageDflt, nExtra, !memDb,
103                        !memDb?pagerStress:0, (void *)pPager, pPager->pPCache);
104   }
105-
106+#ifdef SQLITE_META_DWR
107+  if( rc==SQLITE_OK && !memDb && !readOnly){
108+    (void)MetaDwrOpenFile(pPager, 0);
109+  }
110+#endif
111   /* If an error occurred above, free the  Pager structure and close the file.
112   */
113   if( rc!=SQLITE_OK ){
114@@ -70005,6 +70051,10 @@ struct BtShared {
115 #endif
116   u8 *pTmpSpace;        /* Temp space sufficient to hold a single cell */
117   int nPreformatSize;   /* Size of last cell written by TransferRow() */
118+#ifdef SQLITE_META_DWR
119+  u32 maxMetaPage;
120+  u32 metaRecoverStatus;
121+#endif
122 };
123
124 /*
125@@ -74298,7 +74348,12 @@ trans_begun:
126       rc = sqlite3PagerOpenSavepoint(pPager, p->db->nSavepoint);
127     }
128   }
129-
130+#ifdef SQLITE_META_DWR
131+  if (rc == SQLITE_NOTADB || rc == SQLITE_CORRUPT) {
132+    int rc1 = MetaDwrRecoverAndBeginTran(p, wrflag, pSchemaVersion);
133+    rc = (rc1 == SQLITE_OK) ? SQLITE_OK : rc;
134+  }
135+#endif
136   btreeIntegrity(p);
137   sqlite3BtreeLeave(p);
138   return rc;
139@@ -74687,6 +74742,9 @@ SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *p){
140       if( rc==SQLITE_OK ){
141         rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
142         put4byte(&pBt->pPage1->aData[28], pBt->nPage);
143+#ifdef SQLITE_META_DWR
144+        MetaDwrCheckVacuum(pBt);
145+#endif
146       }
147     }else{
148       rc = SQLITE_DONE;
149@@ -74771,6 +74829,9 @@ static int autoVacuumCommit(Btree *p){
150       put4byte(&pBt->pPage1->aData[28], nFin);
151       pBt->bDoTruncate = 1;
152       pBt->nPage = nFin;
153+#ifdef SQLITE_META_DWR
154+      MetaDwrCheckVacuum(pBt);
155+#endif
156     }
157     if( rc!=SQLITE_OK ){
158       sqlite3PagerRollback(pPager);
159@@ -74827,6 +74888,9 @@ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zSuperJrnl){
160     if( pBt->bDoTruncate ){
161       sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage);
162     }
163+#endif
164+#ifdef SQLITE_META_DWR
165+    (void)MetaDwrUpdateMetaPages(p);
166 #endif
167     rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zSuperJrnl, 0);
168     sqlite3BtreeLeave(p);
169@@ -80898,6 +80962,11 @@ SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){
170       assert( iMeta==0 || iMeta==1 );
171       pBt->incrVacuum = (u8)iMeta;
172     }
173+#endif
174+#ifdef SQLITE_META_DWR
175+    if (idx == 1 && pBt->pPager->metaFd) {
176+      pBt->pPager->metaChanged = META_SCHEMA_CHANGED;
177+    }
178 #endif
179   }
180   sqlite3BtreeLeave(p);
181@@ -82544,7 +82613,12 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
182           sqlite3PagerTruncateImage(pDestPager, nDestTruncate);
183           rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0);
184         }
185-
186+      #ifdef SQLITE_META_DWR
187+        if (rc == SQLITE_OK && p->pDest->pBt->pPager->metaFd) {
188+          p->pDest->pBt->pPager->metaChanged = META_SCHEMA_CHANGED;
189+          (void)MetaDwrUpdateMetaPages(p->pDest);
190+        }
191+      #endif
192         /* Finish committing the transaction to the destination database. */
193         if( SQLITE_OK==rc
194          && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0))
195@@ -138957,7 +139031,12 @@ SQLITE_PRIVATE void sqlite3Pragma(
196   }
197 #endif
198   /* END CODEC */
199-
200+#ifdef SQLITE_META_DWR
201+  if(PragmaMetaDoubleWrie(db, iDb, pParse, zLeft, zRight)) {
202+    /* PragmaMetaDoubleWrie executes internal */
203+    goto pragma_out;
204+  }
205+#endif
206   /* Locate the pragma in the lookup table */
207   pPragma = pragmaLocate(zLeft);
208   if( pPragma==0 ){
209@@ -180709,6 +180788,10 @@ SQLITE_PRIVATE const char *sqlite3ErrStr(int rc){
210       zErr = "no more rows available";
211       break;
212     }
213+    case SQLITE_META_RECOVERED: {
214+      zErr = "warning meta recover message";
215+      break;
216+    }
217     default: {
218       rc &= 0xff;
219       if( ALWAYS(rc>=0) && rc<ArraySize(aMsg) && aMsg[rc]!=0 ){
220@@ -254918,7 +255001,1070 @@ export_finish:
221 }
222 /************** End file hw_codec.c *****************************************/
223 #endif
224+#ifdef SQLITE_META_DWR
225+#define META_DWR_MAX_PAGES 500
226+#define META_DWR_MAGIC 0x234A86D9
227+#define META_DWR_VERSION 0x00000001
228+#define META_DWR_INVALID_ZONE 0x55
229+#define META_DWR_HEADER_PAGE_SIZE 4096
230+#define META_DWR_HEADER_DEFAULT_PAGE_CNT 8
231+typedef struct ScanPages {
232+  u32 pageCnt;
233+  u32 pageBufSize;
234+  u32 maxPageNo;
235+  Pgno *pages;
236+} ScanPages;
237+
238+typedef struct MetaDwrHdr {
239+  u32 magic;
240+  u32 version;
241+  u32 dbSize;
242+  u32 mxFrameInWal;
243+  u32 freeListPageNo;
244+  u32 freeListPageCnt;
245+  u32 schemaCookie;
246+  u32 pageSz;
247+  u32 pageCnt;
248+  u64 dbFileInode;
249+  u32 reserved[12];
250+  u32 checkSum;
251+  u8 *zones;
252+  Pgno *pages;
253+  u32 pageBufSize;
254+  u8 hdrValid;
255+  u8 checkFileId;
256+  u16 needSync;
257+  i64 lastSyncTime;
258+} MetaDwrHdr;
259+
260+#define META_VERIFIED_HDR_LEN (offsetof(MetaDwrHdr, zones))
261+#define META_ZONES_LENGTH (META_DWR_MAX_PAGES * sizeof(u8))
262+#define META_PAGE_NO_OFFSET (META_VERIFIED_HDR_LEN + META_ZONES_LENGTH)
263+#define META_FILE_UPDATE_TIMES_PER_SYNC 100 // sync once for every 100 update
264+#define META_FILE_SYNC_TIMEOUT_MS 30000     // 30 seconds
265+
266+static int MetaDwrHeaderSimpleCheck(Pager *pPager, MetaDwrHdr *hdr) {
267+#if SQLITE_OS_UNIX
268+  if (hdr->checkFileId) {
269+    unixFile *fd = (unixFile *)pPager->fd;
270+    if (fd == NULL || fd->pInode == NULL || pPager->pVfs == NULL) {
271+      return SQLITE_INTERNAL;
272+    }
273+    if (fd->pInode->fileId.ino != hdr->dbFileInode) {
274+      sqlite3_log(SQLITE_IOERR_DATA, "Ino mismatch file %llu dwr file %llu",
275+      fd->pInode->fileId.ino, hdr->dbFileInode);
276+      return SQLITE_IOERR_DATA;
277+    }
278+  }
279+#endif
280+  if (hdr->pageCnt > META_DWR_MAX_PAGES || hdr->version != META_DWR_VERSION ||
281+      hdr->magic != META_DWR_MAGIC || hdr->checkSum != META_DWR_MAGIC) {
282+    sqlite3_log(SQLITE_IOERR_DATA, "Meta dwr file check wrong pageCnt %u, version %u, magic %u, checkSum %u",
283+                hdr->pageCnt, hdr->version, hdr->magic, hdr->checkSum);
284+    return SQLITE_IOERR_DATA;
285+  }
286+  if (hdr->pageSz != pPager->pageSize) {
287+    sqlite3_log(SQLITE_IOERR_DATA, "Meta dwr file check wrong pageSz %u-%u", hdr->pageSz, pPager->pageSize);
288+    return SQLITE_IOERR_DATA;
289+  }
290+  return SQLITE_OK;
291+}
292+
293+static int PragmaMetaDoubleWrie(sqlite3 *db, int iDb, Parse *parse, const char *zLeft, const char *zRight) {
294+  Btree *pBt = db->aDb[iDb].pBt;
295+  if (pBt == NULL || zLeft == NULL || sqlite3StrICmp(zLeft, "meta_double_write") != 0) {
296+    return 0;
297+  }
298+  Pager *pPager = pBt->pBt->pPager;
299+  if (pPager == NULL) {
300+    sqlite3_log(SQLITE_WARNING_DUMP, "Invalid pager handle");
301+    return 1;
302+  }
303+  if (zRight == NULL) {
304+    Vdbe *v = sqlite3GetVdbe(parse);
305+    sqlite3VdbeSetNumCols(v, 1);
306+    sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "meta_double_write", SQLITE_STATIC);
307+    sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, pPager->metaFd ? "enabled" : "disabled", 0);
308+    sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
309+  } else if (strncmp(zRight, "enabled", 7) == 0) {
310+    sqlite3_mutex_enter(db->mutex);
311+    // only support enabled meta double write
312+    int rc = MetaDwrOpenAndCheck(pBt);
313+    if (rc != SQLITE_OK && rc != SQLITE_PERM) {
314+      parse->nErr++;
315+      parse->rc = rc;
316+    }
317+    sqlite3_mutex_leave(db->mutex);
318+  } else if (strncmp(zRight, "disabled", 8) == 0) {
319+    sqlite3_mutex_enter(db->mutex);
320+    MetaDwrDisable(pBt);
321+    sqlite3_mutex_leave(db->mutex);
322+  }
323+  return 1;
324+}
325+
326+static int GetBtreePageNo(MemPage *pPage, void *args) {
327+  ScanPages *pInfo = (ScanPages *)args;
328+  // realloc buffer to store pages
329+  u32 pageCnt = pInfo->pageCnt;
330+  if (pageCnt == pInfo->pageBufSize) {
331+    u32 memSz = sizeof(Pgno) * ROUND8(pageCnt + 1);
332+    Pgno *pages = sqlite3Malloc(memSz);
333+    if (pages == NULL) {
334+      sqlite3_log(SQLITE_NOMEM, "GetPages alloc buffer go wrong %u", memSz);
335+      return SQLITE_NOMEM;
336+    }
337+    if (pageCnt != 0) {
338+      memcpy(pages, pInfo->pages, pageCnt * sizeof(Pgno));
339+    }
340+    sqlite3_free(pInfo->pages);
341+    pInfo->pageBufSize = ROUND8(pageCnt + 1);
342+    pInfo->pages = pages;
343+  }
344+  pInfo->pages[pageCnt] = pPage->pgno;
345+  pInfo->pageCnt++;
346+  if (pInfo->maxPageNo < pPage->pgno) {
347+    pInfo->maxPageNo = pPage->pgno;
348+  }
349+  return 0;
350+}
351+
352+typedef int (*ScanFn)(MemPage *pPage, void *args);
353+static SQLITE_NOINLINE int ScanOverflowPages(
354+    MemPage *pPage,       /* The page that contains the Cell */
355+    unsigned char *pCell, /* First byte of the Cell */
356+    CellInfo *pInfo,      /* Size information about the cell */
357+    ScanFn fn,            /* Scan pages function */
358+    void *args) {
359+  BtShared *pBt;
360+  Pgno ovflPgno;
361+  int rc;
362+  int nOvfl;
363+  u32 ovflPageSize;
364+
365+  if (pCell + pInfo->nSize > pPage->aDataEnd) {
366+    /* Cell extends past end of page */
367+    return SQLITE_CORRUPT_BKPT;
368+  }
369+  ovflPgno = get4byte(pCell + pInfo->nSize - 4);
370+  pBt = pPage->pBt;
371+  assert(pBt->usableSize > 4);
372+  ovflPageSize = pBt->usableSize - 4;
373+  nOvfl = (pInfo->nPayload - pInfo->nLocal + ovflPageSize - 1) / ovflPageSize;
374+  assert(nOvfl > 0 ||
375+         (CORRUPT_DB && (pInfo->nPayload + ovflPageSize) < ovflPageSize));
376+  while (nOvfl > 0) {
377+    nOvfl--;
378+    Pgno iNext = 0;
379+    MemPage *pOvfl = 0;
380+    if (ovflPgno < 2 || ovflPgno > btreePagecount(pBt)) {
381+      sqlite3_log(SQLITE_WARNING_DUMP, "Ignore for ovfl page not as expect, pgno %u ovflPgno %u novfl %d payload %u local %u",
382+        pPage->pgno, ovflPgno, nOvfl, pInfo->nPayload, pInfo->nLocal);
383+      return SQLITE_MISUSE;
384+    }
385+    rc = getOverflowPage(pBt, ovflPgno, &pOvfl, &iNext);
386+    if (rc)
387+      return rc;
388+    if (pOvfl) {
389+      rc = fn(pOvfl, args);
390+      if (rc) {
391+        return rc;
392+      }
393+      sqlite3PagerUnref(pOvfl->pDbPage);
394+    }
395+    ovflPgno = iNext;
396+  }
397+  return SQLITE_OK;
398+}
399+
400+static int ScanBtreePage(
401+    BtShared *pBt, /* The BTree that contains the table */
402+    Pgno pgno,     /* Page number to clear */
403+    ScanFn fn,     /* Scan pages function */
404+    void *args) {  /* Scan pages args */
405+  MemPage *pPage;
406+  int rc;
407+  unsigned char *pCell;
408+  int i;
409+  int hdr;
410+  CellInfo info;
411+
412+  assert(sqlite3_mutex_held(pBt->mutex));
413+  if (pgno > btreePagecount(pBt)) {
414+    return SQLITE_OK;
415+  }
416+  rc = getAndInitPage(pBt, pgno, &pPage, 0);
417+  if (rc) {
418+    return rc;
419+  }
420+  rc = fn(pPage, args);
421+  if (rc) {
422+    goto SCAN_PAGE_OUT;
423+  }
424+  hdr = pPage->hdrOffset;
425+  for (i = pPage->nCell - 1; i >= 0; i--) {
426+    pCell = findCell(pPage, i);
427+    if (!pPage->leaf) {
428+      rc = ScanBtreePage(pBt, get4byte(pCell), fn, args);
429+      if (rc) {
430+        goto SCAN_PAGE_OUT;
431+      }
432+    }
433+    pPage->xParseCell(pPage, pCell, &info);
434+    if (info.nLocal < info.nPayload) {
435+      rc = ScanOverflowPages(pPage, pCell, &info, fn, args);
436+      if (rc) {
437+        goto SCAN_PAGE_OUT;
438+      }
439+    }
440+  }
441+  if (!pPage->leaf) {
442+    rc = ScanBtreePage(pBt, get4byte(&pPage->aData[hdr + 8]), fn, args);
443+    if (rc) {
444+      goto SCAN_PAGE_OUT;
445+    }
446+  }
447+SCAN_PAGE_OUT:
448+  releasePage(pPage);
449+  return rc;
450+}
451+
452+static inline int ScanMetaPages(Btree *pBt, ScanPages *pages) {
453+  return ScanBtreePage(pBt->pBt, 1, GetBtreePageNo, pages);
454+}
455+
456+static int ReleaseMetaPages(ScanPages *pages) {
457+  sqlite3_free(pages->pages);
458+  pages->pages = NULL;
459+  return SQLITE_OK;
460+}
461+
462+static void InitMetaHeader(MetaDwrHdr *hdr) {
463+  (void)memset(hdr, 0, META_VERIFIED_HDR_LEN);
464+  hdr->magic = META_DWR_MAGIC;
465+  hdr->version = META_DWR_VERSION;
466+  hdr->checkSum = META_DWR_MAGIC;
467+}
468+
469+static void MetaDwrReleaseHdr(MetaDwrHdr *hdr) {
470+  if (!hdr) {
471+    return;
472+  }
473+  sqlite3_free(hdr->zones);
474+  sqlite3_free(hdr);
475+}
476+
477+static int ExpandMetaPageBuf(MetaDwrHdr *hdr, u32 minimalPageCnt, u32 bufHasData) {
478+  if (minimalPageCnt < hdr->pageBufSize && hdr->zones != NULL) {
479+    return SQLITE_OK;
480+  }
481+  int pageBufSz = ROUND8(MAX(hdr->pageCnt, minimalPageCnt));
482+  u8 *zones = (u8 *)sqlite3Malloc(pageBufSz * (sizeof(u8) + sizeof(Pgno)));
483+  if (zones == NULL) {
484+    return SQLITE_NOMEM_BKPT;
485+  }
486+  Pgno *pgnos = (Pgno *)(zones + pageBufSz);
487+  if (hdr->zones != NULL) {
488+    if (bufHasData && hdr->pageCnt > 0) {
489+      (void)memcpy(zones, hdr->zones, hdr->pageCnt * sizeof(u8));
490+      (void)memcpy(pgnos, hdr->pages, hdr->pageCnt * sizeof(Pgno));
491+    }
492+    sqlite3_free(hdr->zones);
493+  }
494+  hdr->pageBufSize = pageBufSz;
495+  hdr->zones = zones;
496+  hdr->pages = pgnos;
497+  return SQLITE_OK;
498+}
499+
500+static MetaDwrHdr *AllocInitMetaHeaderDwr(Pager *pPager) {
501+  MetaDwrHdr *hdr = sqlite3MallocZero(sizeof(MetaDwrHdr));
502+  if (hdr == NULL) {
503+    return NULL;
504+  }
505+  InitMetaHeader(hdr);
506+  int rc = ExpandMetaPageBuf(hdr, META_DWR_HEADER_DEFAULT_PAGE_CNT, 0);
507+  if (rc != SQLITE_OK) {
508+    MetaDwrReleaseHdr(hdr);
509+    return NULL;
510+  }
511+  hdr->checkFileId = (pPager->pVfs != NULL && sqlite3_stricmp(pPager->pVfs->zName, "unix") == 0);
512+  return hdr;
513+}
514+
515+static void MetaDwrCloseFile(Pager *pPager) {
516+  if (!pPager->metaFd) {
517+    return;
518+  }
519+#if SQLITE_OS_UNIX
520+  if (pPager->metaMapPage) {
521+    osMunmap(pPager->metaMapPage, META_DWR_HEADER_PAGE_SIZE);
522+    pPager->metaMapPage = NULL;
523+  }
524+#endif
525+  if (pPager->metaHdr && pPager->metaHdr->needSync > 0) {
526+    (void)sqlite3OsSync(pPager->metaFd, SQLITE_SYNC_NORMAL);
527+  }
528+  sqlite3OsClose(pPager->metaFd);
529+}
530+
531+static void MetaDwrPagerRelease(Pager *pPager) {
532+  MetaDwrCloseFile(pPager);
533+  MetaDwrReleaseHdr(pPager->metaHdr);
534+  pPager->metaHdr = NULL;
535+  if (pPager->metaFd) {
536+    sqlite3_free(pPager->metaFd);
537+    pPager->metaFd = NULL;
538+  }
539+}
540+
541+static inline int ReadFromHdrPage(Pager *pPager, void *data, int amt, i64 offset) {
542+  if (pPager->metaMapPage) {
543+    (void)memcpy(data, (u8 *)pPager->metaMapPage + offset, amt);
544+    return SQLITE_OK;
545+  }
546+  return sqlite3OsRead(pPager->metaFd, data, amt, offset);
547+}
548+
549+static int MetaDwrReadHeader(Pager *pPager, MetaDwrHdr *hdr) {
550+  i64 sz = 0;
551+  int rc = sqlite3OsFileSize(pPager->metaFd, &sz);
552+  if (rc != SQLITE_OK) {
553+    sqlite3_log(rc, "Meta dwr file size go wrong");
554+    return rc;
555+  }
556+  if (sz <= META_DWR_HEADER_PAGE_SIZE) {
557+    rc = SQLITE_IOERR_DATA;
558+    goto READ_META_OUT;
559+  }
560+  rc = ReadFromHdrPage(pPager, hdr, META_VERIFIED_HDR_LEN, 0);
561+  if (rc != SQLITE_OK) {
562+    sqlite3_log(rc, "Meta dwr file header read wrong");
563+    goto READ_META_OUT;
564+  }
565+  rc = MetaDwrHeaderSimpleCheck(pPager, hdr);
566+  if (rc != SQLITE_OK) {
567+    goto READ_META_OUT;
568+  }
569+  // avoid realloc buffer if buf can't hold all pages
570+  rc = ExpandMetaPageBuf(hdr, hdr->pageCnt, 0);
571+  if (rc != SQLITE_OK) {
572+    goto READ_META_OUT;
573+  }
574+  int zoneSize = hdr->pageCnt * sizeof(u8);
575+  rc = ReadFromHdrPage(pPager, hdr->zones, zoneSize, META_VERIFIED_HDR_LEN);
576+  if (rc != SQLITE_OK) {
577+    goto READ_META_OUT;
578+  }
579+  rc = ReadFromHdrPage(pPager, hdr->pages, hdr->pageCnt * sizeof(Pgno), META_PAGE_NO_OFFSET);
580+  if (rc != SQLITE_OK) {
581+    goto READ_META_OUT;
582+  }
583+  for (u32 i = 0; i < hdr->pageCnt; i++) {
584+    u8 zoneIdx = hdr->zones[i];
585+    if (zoneIdx != 0 && zoneIdx != 1 && zoneIdx != META_DWR_INVALID_ZONE) {
586+      sqlite3_log(SQLITE_IOERR_DATA, "Invalid zoneIdx %d", zoneIdx);
587+      rc = SQLITE_IOERR_DATA;
588+      break;
589+    }
590+  }
591+READ_META_OUT:
592+  if (rc == SQLITE_IOERR_DATA) {
593+    InitMetaHeader(hdr);
594+    rc = SQLITE_OK;
595+  }
596+  return rc;
597+}
598+
599+static inline u64 CaculateMetaDwrWriteOffset(int pageSz, u32 idx, u8 zone) {
600+  // 1 header page, idx correspond 2 zone pages
601+  return META_DWR_HEADER_PAGE_SIZE + pageSz * (idx * 2 + zone);
602+}
603+
604+static void MetaDwrUpdateHeaderDbInfo(BtShared *pBt) {
605+  MetaDwrHdr *hdr = pBt->pPager->metaHdr;
606+  // 28 offset: dbSize, freelist pageNo, freelist pages count, schema cookie
607+  const u8 *dbHdrInfo = &pBt->pPage1->aData[28];
608+  hdr->dbSize = sqlite3Get4byte(dbHdrInfo);
609+#ifndef SQLITE_OMIT_WAL
610+  if (pagerUseWal(pBt->pPager)) {
611+    WalIndexHdr *pWalHdr = &pBt->pPager->pWal->hdr;
612+    if (pWalHdr->isInit) {
613+      hdr->mxFrameInWal = pWalHdr->mxFrame;
614+      hdr->dbSize = pWalHdr->nPage;
615+    }
616+  } else {
617+    hdr->mxFrameInWal = 0;
618+  }
619+#endif
620+#if SQLITE_OS_UNIX
621+  if (hdr->checkFileId) {
622+    unixFile *fd = (unixFile *)pBt->pPager->fd;
623+    if (fd == NULL || fd->pInode == NULL) {
624+      sqlite3_log(SQLITE_WARNING_DUMP, "update meta header invalid fd");
625+      hdr->hdrValid = 0;
626+      return;
627+    }
628+    hdr->dbFileInode = fd->pInode->fileId.ino;
629+  }
630+#endif
631+  hdr->freeListPageNo = sqlite3Get4byte(dbHdrInfo + 4);
632+  hdr->freeListPageCnt = sqlite3Get4byte(dbHdrInfo + 8);
633+  hdr->schemaCookie = sqlite3Get4byte(dbHdrInfo + 12);
634+  hdr->hdrValid = 1;
635+}
636+
637+static inline int WriteToHdrPage(Pager *pPager, const void *data, int amt, i64 offset) {
638+  if (pPager->metaMapPage) {
639+    (void)memcpy((u8 *)pPager->metaMapPage + offset, data, amt);
640+    return SQLITE_OK;
641+  }
642+  return sqlite3OsWrite(pPager->metaFd, data, amt, offset);
643+}
644+
645+static int MetaDwrWriteHeader(Pager *pPager, MetaDwrHdr *hdr) {
646+  if (pPager->metaChanged == 0 || hdr == NULL || hdr->pageCnt == 0 || hdr->hdrValid == 0) {
647+    return SQLITE_OK;
648+  }
649+  hdr->hdrValid = 0;
650+  hdr->pageSz = pPager->pageSize;
651+  hdr->dbSize = pPager->dbSize;
652+  int rc = WriteToHdrPage(pPager, hdr, META_VERIFIED_HDR_LEN, 0);
653+  if (rc != SQLITE_OK) {
654+    sqlite3_log(rc, "update meta header write hdr %u wrong", META_VERIFIED_HDR_LEN);
655+    return rc;
656+  }
657+  if (hdr->zones) {
658+    int zoneSize = hdr->pageCnt * sizeof(u8);
659+    rc = WriteToHdrPage(pPager, hdr->zones, zoneSize, META_VERIFIED_HDR_LEN);
660+    if (rc != SQLITE_OK) {
661+      sqlite3_log(rc, "update meta header write zonebuf %u wrong", zoneSize);
662+      return rc;
663+    }
664+  }
665+  if (hdr->pages) {
666+    int pageBufSz = hdr->pageCnt * sizeof(Pgno);
667+    rc = WriteToHdrPage(pPager, hdr->pages, pageBufSz, META_PAGE_NO_OFFSET);
668+    if (rc != SQLITE_OK) {
669+      sqlite3_log(rc, "update meta header write pagebuf %u wrong", pageBufSz);
670+    }
671+  }
672+  if (rc == SQLITE_OK) {
673+    u64 size = CaculateMetaDwrWriteOffset((int)pPager->pageSize, hdr->pageCnt, 0);
674+    rc = sqlite3OsTruncate(pPager->metaFd, size);
675+    if (rc != SQLITE_OK) {
676+      sqlite3_log(rc, "update meta header truncate filesz %lu wrong", size);
677+    }
678+    i64 timeMs = 0;
679+    sqlite3OsCurrentTimeInt64(pPager->pVfs, &timeMs);
680+    if ((timeMs - hdr->lastSyncTime) > META_FILE_SYNC_TIMEOUT_MS ||
681+        hdr->needSync >= META_FILE_UPDATE_TIMES_PER_SYNC) {
682+      rc = sqlite3OsSync(pPager->metaFd, SQLITE_SYNC_NORMAL);
683+      if (rc != SQLITE_OK) {
684+        sqlite3_log(rc, "update meta header sync filesz %lu wrong", size);
685+      }
686+      hdr->lastSyncTime = timeMs;
687+      hdr->needSync = 0;
688+    } else {
689+      hdr->needSync++;
690+    }
691+  }
692+  return rc;
693+}
694+
695+static int MetaDwrFindPageIdx(MetaDwrHdr *hdr, u32 pgno, u32 *idx) {
696+  for (u32 i = 0; i < hdr->pageCnt && i < META_DWR_MAX_PAGES; i++) {
697+    if (pgno == hdr->pages[i]) {
698+      *idx = i;
699+      return 1;
700+    }
701+  }
702+  return 0;
703+}
704+
705+static int MetaDwrWriteOnePage(Btree *pBt, PgHdr *pPage, MetaDwrHdr *hdr, u8 curZones, u32 idx) {
706+  int rc = SQLITE_OK;
707+  u8 pageExpand = 0;
708+  if (hdr->pageCnt <= idx) {
709+    rc = ExpandMetaPageBuf(hdr, idx + 1, 1);
710+    if (rc != SQLITE_OK) {
711+      return rc;
712+    }
713+    pageExpand = 1;
714+  }
715+  Pager *pPager = pBt->pBt->pPager;
716+  // asume zone 0 or 1
717+  u8 zone = 1 - curZones;
718+  int pageSz = sqlite3BtreeGetPageSize(pBt);
719+  u64 ofs = CaculateMetaDwrWriteOffset(pageSz, idx, zone);
720+  void *pData;
721+#if defined(SQLITE_HAS_CODEC)
722+  if ((pData = sqlite3PagerCodec(pPage)) == 0)
723+    return SQLITE_NOMEM;
724+#else
725+  pData = pPage->pData;
726+#endif
727+  rc = sqlite3OsWrite(pPager->metaFd, pData, pageSz, ofs);
728+  if (rc != SQLITE_OK) {
729+    return rc;
730+  }
731+  hdr->zones[idx] = zone;
732+  hdr->pages[idx] = pPage->pgno;
733+  if (pageExpand) {
734+    hdr->pageCnt++;
735+  }
736+  return SQLITE_OK;
737+}
738+
739+static int MetaDwrRestoreAllPages(Btree *pBt, const ScanPages *metaPages, MetaDwrHdr *hdr) {
740+  u32 i = 0;
741+  PgHdr *p = NULL;
742+  int rc = SQLITE_OK;
743+  for (i = 0; i < metaPages->pageCnt && i < META_DWR_MAX_PAGES; i++) {
744+    Pgno pgno = metaPages->pages[i];
745+    if (pgno > btreePagecount(pBt->pBt)) {
746+      sqlite3_log(SQLITE_WARNING_DUMP, "pageno %d overlimit", pgno);
747+      return SQLITE_CORRUPT_BKPT;
748+    }
749+    rc = sqlite3PagerGet(pBt->pBt->pPager, pgno, &p, 0);
750+    if (rc) {
751+      return rc;
752+    }
753+    rc = MetaDwrWriteOnePage(pBt, p, hdr, 1, i);
754+    sqlite3PagerUnref(p);
755+    if (rc) {
756+      return rc;
757+    }
758+  }
759+  hdr->pageCnt = metaPages->pageCnt;
760+  MetaDwrUpdateHeaderDbInfo(pBt->pBt);
761+  return rc;
762+}
763+
764+static inline const char *GetMetaFilePath(Pager *pPager)
765+{
766+  return pPager->metaFd == NULL ? NULL : ((const char *)pPager->metaFd + ROUND8(pPager->pVfs->szOsFile));
767+}
768+
769+static int MetaDwrCheckPerm(sqlite3_vfs *pVfs, u8 openCreate, char *metaPath) {
770+  int exists = 0;
771+  int rc = sqlite3OsAccess(pVfs, metaPath, SQLITE_ACCESS_EXISTS, &exists);
772+  if (rc != SQLITE_OK) {
773+    return rc;
774+  }
775+  if (!exists && !openCreate) {
776+    return SQLITE_PERM;
777+  }
778+#ifdef OS_FEATURE
779+  // check if the path have enough permission
780+  rc = osAccess(metaPath, W_OK|R_OK);
781+  if (rc == 0 || errno == ENOENT) {
782+    return SQLITE_OK;
783+  }
784+  rc = SQLITE_PERM;
785+  if (openCreate) {
786+    sqlite3_log(SQLITE_WARNING_DUMP, "Meta double write disabled, sysno %d", errno);
787+  }
788+#endif
789+  return rc;
790+}
791+
792+static int MetaDwrOpenFile(Pager *pPager, u8 openCreate) {
793+  if (pPager->metaFd || pPager->zFilename == NULL) {
794+    return SQLITE_OK;
795+  }
796+  sqlite3BeginBenignMalloc();
797+  sqlite3_vfs *pVfs = pPager->pVfs;
798+  int size = strlen(pPager->zFilename) + sizeof("-dwr");
799+  int szOsFile = ROUND8(pVfs->szOsFile);
800+  sqlite3_file *metaFd = (sqlite3_file *)sqlite3MallocZero(szOsFile + size);
801+  char *metaPath = (char *)metaFd + szOsFile;
802+  if (metaFd == NULL) {
803+    sqlite3EndBenignMalloc();
804+    sqlite3_log(SQLITE_NOMEM_BKPT, "sqlite alloc memsize %d go wrong", szOsFile + size);
805+    return SQLITE_NOMEM_BKPT;
806+  }
807+  sqlite3_snprintf(size, metaPath, "%s-dwr", pPager->zFilename);
808+  int rc = MetaDwrCheckPerm(pVfs, openCreate, metaPath);
809+  if (rc != SQLITE_OK) {
810+    goto INIT_META_OUT;
811+  }
812+  u32 flags = (SQLITE_OPEN_READWRITE | SQLITE_OPEN_SUPER_JOURNAL);
813+  if (openCreate) {
814+    flags |= SQLITE_OPEN_CREATE;
815+  }
816+  rc = sqlite3OsOpen(pVfs, metaPath, metaFd, (int)flags, 0);
817+  if (rc != SQLITE_OK) {
818+    goto INIT_META_OUT;
819+  }
820+#if SQLITE_OS_UNIX
821+  if (pPager->metaMapPage == NULL) {
822+    sqlite3_int64 sz = META_DWR_HEADER_PAGE_SIZE;
823+    sqlite3OsFileControlHint(metaFd, SQLITE_FCNTL_CHUNK_SIZE, &sz);
824+    sqlite3OsFileControlHint(metaFd, SQLITE_FCNTL_SIZE_HINT, &sz);
825+    void *page = osMmap(0, META_DWR_HEADER_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
826+                        ((unixFile *)metaFd)->h, 0);
827+    if (page != MAP_FAILED) {
828+      pPager->metaMapPage = page;
829+    }
830+  }
831+#endif
832+  pPager->metaFd = metaFd;
833+INIT_META_OUT:
834+  sqlite3EndBenignMalloc();
835+  if (rc != SQLITE_OK && metaFd != NULL) {
836+    sqlite3_free(metaFd);
837+  }
838+  return rc;
839+}
840+
841+void MetaDwrCheckVacuum(BtShared *pBt) {
842+  if (!pBt || !pBt->pPager->metaFd) {
843+    return;
844+  }
845+  if (pBt->nPage < pBt->maxMetaPage) {
846+    pBt->pPager->metaChanged = META_SCHEMA_CHANGED;
847+  }
848+}
849+
850+static inline u8 LocalMetaHdrValid(Pager *pPager) {
851+  return pPager->metaMapPage != NULL && memcmp(pPager->metaMapPage, pPager->metaHdr,
852+    META_VERIFIED_HDR_LEN) == 0;
853+}
854+
855+static int MetaDwrLoadHdr(Pager *pPager) {
856+  if (!pPager->metaHdr) {
857+    pPager->metaHdr = AllocInitMetaHeaderDwr(pPager);
858+    if (pPager->metaHdr == NULL) {
859+      return SQLITE_NOMEM_BKPT;
860+    }
861+  }
862+  if (LocalMetaHdrValid(pPager)) {
863+    return SQLITE_OK;
864+  }
865+  return MetaDwrReadHeader(pPager, pPager->metaHdr);
866+}
867+
868+static int MetaDwrLoadAndCheckMetaFile(BtShared *pBt, u8 reportErr) {
869+  int rc = MetaDwrLoadHdr(pBt->pPager);
870+  if (rc != SQLITE_OK) {
871+    return rc;
872+  }
873+  MetaDwrHdr *hdr = pBt->pPager->metaHdr;
874+  if (hdr->pageCnt == 0) {
875+    return reportErr ? SQLITE_IOERR_DATA : SQLITE_OK;
876+  }
877+  // 28 offset: dbSize, freelist pageNo, freelist pages count, schema cookie
878+  u8 *dbHdrInfo = &pBt->pPage1->aData[28];
879+  if (hdr->dbSize != pBt->pPager->dbSize || hdr->dbSize != sqlite3Get4byte(dbHdrInfo) ||
880+      hdr->freeListPageNo != sqlite3Get4byte(dbHdrInfo + 4) ||
881+      hdr->freeListPageCnt != sqlite3Get4byte(dbHdrInfo + 8) ||
882+      hdr->schemaCookie != sqlite3Get4byte(dbHdrInfo + 12)) {
883+    sqlite3_log(SQLITE_IOERR_DATA, "Meta dwr file expect %u-%u-%u-%u-%u but gotton %u-%u-%u-%u-%u",
884+                pBt->pPager->dbSize, hdr->dbSize, sqlite3Get4byte(dbHdrInfo), sqlite3Get4byte(dbHdrInfo + 4),
885+                sqlite3Get4byte(dbHdrInfo + 8), sqlite3Get4byte(dbHdrInfo + 12), hdr->dbSize, hdr->freeListPageNo,
886+                hdr->freeListPageCnt, hdr->schemaCookie);
887+    // reinit
888+    InitMetaHeader(hdr);
889+    if (reportErr) {
890+      return SQLITE_IOERR_DATA;
891+    }
892+  }
893+  return SQLITE_OK;
894+}
895+
896+static int MetaDwrReadOnePage(Pager *pPager, MetaDwrHdr *hdr, int idx, u8 *pData) {
897+  u64 ofs = CaculateMetaDwrWriteOffset(pPager->pageSize, idx, hdr->zones[idx]);
898+  int rc = sqlite3OsRead(pPager->metaFd, pData, pPager->pageSize, ofs);
899+  CODEC1(pPager, pData, hdr->pages[idx], 3, rc = SQLITE_NOMEM_BKPT);
900+  return rc;
901+}
902+
903+static int MetaDwrRecoverHeadPage(
904+    Pager *pPager, /* The pager open on the database file */
905+    Pgno pgno,     /* Page number to fetch */
906+    DbPage **pDbPage,
907+    int flag) {
908+  if (pPager->metaFd == NULL) {
909+    return pgno == 1 ? SQLITE_NOTADB : SQLITE_CORRUPT;
910+  }
911+  sqlite3_pcache_page *pCachePage = sqlite3PcacheFetch(pPager->pPCache, pgno, 3);
912+  if (pCachePage == NULL) {
913+    sqlite3_log(SQLITE_NOMEM_BKPT, "Get meta page wrong %d", pgno);
914+    return SQLITE_NOMEM_BKPT;
915+  }
916+  DbPage *pPage = sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pCachePage);
917+  pPage->pPager = pPager;
918+  assert(pCachePage != 0);
919+  int rc = MetaDwrLoadHdr(pPager);
920+  if (rc != SQLITE_OK) {
921+    goto RELEASE_OUT;
922+  }
923+  MetaDwrHdr *hdr = pPager->metaHdr;
924+  u8 walChecked = 0;
925+#ifndef SQLITE_OMIT_WAL
926+  if (pagerUseWal(pPager)) {
927+    WalIndexHdr *pWalHdr = &pPager->pWal->hdr;
928+    if (pWalHdr->isInit && pWalHdr->mxFrame != 0) {
929+      if (hdr->mxFrameInWal != pWalHdr->mxFrame || hdr->dbSize != pWalHdr->nPage) {
930+        rc = SQLITE_NOTADB;
931+        sqlite3_log(SQLITE_WARNING_DUMP, "Meta dwr wal hdr expect %u-%u but gotten %u-%u",
932+                    hdr->mxFrameInWal, hdr->dbSize, pWalHdr->mxFrame, pWalHdr->nPage);
933+        goto RELEASE_OUT;
934+      } else {
935+        walChecked = 1;
936+      }
937+    }
938+  }
939+  if (walChecked == 0) {
940+    i64 size = 0;
941+    rc = sqlite3OsFileSize(pPager->fd, &size);
942+    if (rc != SQLITE_OK) {
943+      rc = SQLITE_NOTADB;
944+      sqlite3_log(SQLITE_WARNING_DUMP, "Meta dwr get db file size go wrong");
945+      goto RELEASE_OUT;
946+    }
947+    i64 expectSz = (i64)hdr->dbSize * (i64)hdr->pageSz;
948+    if (size != expectSz) {
949+      rc = SQLITE_NOTADB;
950+      sqlite3_log(SQLITE_WARNING_DUMP, "Meta dwr expect file size %lu but gotten size %llu",
951+                  expectSz, size);
952+      goto RELEASE_OUT;
953+    }
954+  }
955+#endif
956+  rc = SQLITE_NOTADB;
957+  for (u32 i = 0; i < hdr->pageCnt; i++) {
958+    if (hdr->pages[i] != pgno) {
959+      continue;
960+    }
961+    rc = MetaDwrReadOnePage(pPager, hdr, i, sqlite3PagerGetData(pPage));
962+    if (rc == SQLITE_OK) {
963+      *pDbPage = pPage;
964+      if (pPage->pgno == 1) {
965+        memcpy(&pPager->dbFileVers, &((u8 *)pPage->pData)[24], sizeof(pPager->dbFileVers));
966+      }
967+      pager_set_pagehash(pPage);
968+    }
969+    break;
970+  }
971+RELEASE_OUT:
972+  if (rc != SQLITE_OK && pPage != NULL) {
973+    sqlite3PcacheDrop(pPage);
974+  }
975+  return rc;
976+}
977+
978+static int MetaDwrRestoreChangedPages(Btree *pBt) {
979+  Pager *pPager = pBt->pBt->pPager;
980+  MetaDwrHdr *hdr = pPager->metaHdr;
981+  u8 *zones = sqlite3MallocZero(hdr->pageBufSize * sizeof(u8));
982+  if (zones == NULL) {
983+    sqlite3_log(SQLITE_NOMEM_BKPT, "Alloc zones buffer size %u go wrong", hdr->pageBufSize * sizeof(u8));
984+    return SQLITE_NOMEM_BKPT;
985+  }
986+  if (hdr->pageCnt > 0) {
987+    memcpy(zones, hdr->zones, hdr->pageCnt * sizeof(u8));
988+  }
989+  u32 idx = 0;
990+  PgHdr *p = 0;
991+  PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
992+  int rc = SQLITE_OK;
993+  for (p = pList; p; p = p->pDirty) {
994+    if (MetaDwrFindPageIdx(hdr, p->pgno, &idx) == 0) {
995+      continue;
996+    }
997+    rc = MetaDwrWriteOnePage(pBt, p, hdr, zones[idx], idx);
998+    if (rc != SQLITE_OK) {
999+      break;
1000+    }
1001+  }
1002+  if (rc == SQLITE_OK) {
1003+    MetaDwrUpdateHeaderDbInfo(pBt->pBt);
1004+  }
1005+  sqlite3_free(zones);
1006+  return rc;
1007+}
1008+
1009+static int MetaDwrUpdateMetaPages(Btree *pBt) {
1010+  Pager *pPager = pBt->pBt->pPager;
1011+  if (!pPager || !pPager->metaFd || pPager->memDb || pPager->readOnly || pBt->pBt->pPage1 == NULL) {
1012+    return SQLITE_OK;
1013+  }
1014+  if (pPager->metaChanged == 0) {
1015+    if ((pBt->pBt->pPage1->pDbPage->flags & PGHDR_DIRTY) == 0) {
1016+      return SQLITE_OK;
1017+    }
1018+    pPager->metaChanged = META_HEADER_CHANGED;
1019+  }
1020+  sqlite3BeginBenignMalloc();
1021+  int rc = MetaDwrLoadHdr(pPager);
1022+  if (rc != SQLITE_OK) {
1023+    goto UPDATE_OUT;
1024+  }
1025+  // only update header page
1026+  if (pPager->metaChanged == META_HEADER_CHANGED) {
1027+    rc = MetaDwrRestoreChangedPages(pBt);
1028+    goto UPDATE_OUT;
1029+  }
1030+  // update schema pages
1031+  ScanPages metaPages = {0};
1032+  rc = ScanMetaPages(pBt, &metaPages);
1033+  if (rc != SQLITE_OK) {
1034+    goto UPDATE_OUT;
1035+  }
1036+  MetaDwrHdr *hdr = pPager->metaHdr;
1037+  // rewrite
1038+  if (hdr->pageCnt == 0 || hdr->pageCnt != metaPages.pageCnt || pBt->pBt->nPage > pBt->pBt->maxMetaPage ||
1039+      memcmp(hdr->pages, metaPages.pages, hdr->pageCnt * sizeof(Pgno))) {
1040+    // if page numbers unorderred, restore all pages
1041+    rc = MetaDwrRestoreAllPages(pBt, &metaPages, hdr);
1042+  } else {
1043+    rc = MetaDwrRestoreChangedPages(pBt);
1044+  }
1045+  if (rc == SQLITE_OK) {
1046+    pBt->pBt->maxMetaPage = metaPages.maxPageNo;
1047+  }
1048+  ReleaseMetaPages(&metaPages);
1049+UPDATE_OUT:
1050+  sqlite3EndBenignMalloc();
1051+  return rc;
1052+}
1053+
1054+static int MetaDwrRecoverSinglePage(Btree *pBt, int pgno, u8 *pData) {
1055+  if (pgno < 1 || pBt == NULL) {
1056+    return SQLITE_CORRUPT_BKPT;
1057+  }
1058+  Pager *pPager = sqlite3BtreePager(pBt);
1059+  DbPage *pDbPage = NULL;
1060+  int rc = sqlite3PagerGet(pPager, pgno, &pDbPage, 0);
1061+  if (rc) {
1062+    return rc;
1063+  }
1064+  if ((rc = sqlite3PagerWrite(pDbPage)) == SQLITE_OK) {
1065+    memcpy(sqlite3PagerGetData(pDbPage), pData, pPager->pageSize);
1066+  } else {
1067+    sqlite3_log(rc, "Dwr recoverwrite meta page %d failed", pgno);
1068+  }
1069+  sqlite3PagerUnref(pDbPage);
1070+  return rc;
1071+}
1072+
1073+static int MetaDwrCheckMeta(Btree *pBt) {
1074+  int nErr = 0;
1075+  Pgno aRoot[2] = {0, 1}; // quick check and only check root btree
1076+  char *errStr = NULL;
1077+  int rc = sqlite3BtreeIntegrityCheck(pBt->db, pBt, &aRoot[0], 2, SQLITE_INTEGRITY_CHECK_ERROR_MAX,
1078+    &nErr, &errStr);
1079+  if (nErr == 0) {
1080+    assert(errStr == 0);
1081+    return SQLITE_OK;
1082+  }
1083+  if (rc != SQLITE_OK) {
1084+    sqlite3_log(rc, "Meta integrity check go wrong");
1085+    return rc;
1086+  }
1087+  sqlite3_log(SQLITE_WARNING_DUMP, "Meta integrity check %s", errStr);
1088+  sqlite3DbFree(pBt->db, errStr);
1089+  return SQLITE_CORRUPT;
1090+}
1091+
1092+static int MetaDwrBeginTrans(Btree *pBt, int wrflag) {
1093+  pBt->pBt->btsFlags &= ~BTS_READ_ONLY;
1094+  Pager *pPager = pBt->pBt->pPager;
1095+  void *xGetMethod = pPager->xGet;
1096+  pPager->xGetMethod = MetaDwrRecoverHeadPage;
1097+  pPager->xGet = MetaDwrRecoverHeadPage;
1098+  int rc = sqlite3BtreeBeginTrans(pBt, wrflag, 0);
1099+  pPager->xGet = xGetMethod;
1100+  pPager->xGetMethod = 0;
1101+  if (rc == SQLITE_OK) {
1102+    sqlite3PagerWrite(pBt->pBt->pPage1->pDbPage);
1103+    sqlite3_log(rc, "sqlite fix meta header");
1104+  }
1105+  return rc;
1106+}
1107+
1108+static int MetaDwrRecoverAndBeginTran(Btree *pBt, int wrflag, int *pSchemaVersion)
1109+{
1110+  Pager *pPager = pBt->pBt->pPager;
1111+  assert(sqlite3_mutex_held(pBt->pBt->mutex));
1112+  if (!pPager->metaFd || pBt->pBt->metaRecoverStatus || pPager->readOnly || pPager->memDb) {
1113+    return SQLITE_NOTADB;
1114+  }
1115+  int rc = MetaDwrLoadHdr(pPager);
1116+  if (rc != SQLITE_OK) {
1117+    sqlite3_log(rc, "MetaDwr load header failed");
1118+    return rc;
1119+  }
1120+  pBt->pBt->metaRecoverStatus = META_IN_RECOVERY;
1121+  rc = MetaDwrBeginTrans(pBt, 2);
1122+  if (rc != SQLITE_OK) {
1123+    return rc;
1124+  }
1125+  void *pData = NULL;
1126+  pPager->metaChanged = META_HEADER_CHANGED;
1127+  MetaDwrHdr *hdr = pPager->metaHdr;
1128+  sqlite3_log(SQLITE_WARNING_DUMP, "sqlite meta recover %u frames", hdr->pageCnt);
1129+  int szPage = sqlite3BtreeGetPageSize(pBt);
1130+  pData = sqlite3Malloc(szPage);
1131+  if (pData == NULL) {
1132+    rc = SQLITE_NOMEM;
1133+    sqlite3_log(rc, "Dwr malloc mem size %d failed", szPage);
1134+    goto DWR_RECOVER_OUT;
1135+  }
1136+  for (u32 i = 0; i < hdr->pageCnt; i++) {
1137+    rc = MetaDwrReadOnePage(pPager, hdr, i, pData);
1138+    if (rc != SQLITE_OK) {
1139+      sqlite3_log(rc, "Dwr read %d meta page failed ", i);
1140+      break;
1141+    }
1142+    rc = MetaDwrRecoverSinglePage(pBt, hdr->pages[i], pData);
1143+    if (rc != SQLITE_OK) {
1144+      sqlite3_log(rc, "Dwr recover %d meta page failed ", i);
1145+      break;
1146+    }
1147+  }
1148+DWR_RECOVER_OUT:
1149+  /* Close the transaction, if one was opened. */
1150+  if (rc == SQLITE_OK) {
1151+    sqlite3BtreeCommit(pBt);
1152+  } else {
1153+    (void)sqlite3BtreeRollback(pBt, SQLITE_ABORT_ROLLBACK, 0);
1154+  }
1155+  if (rc == SQLITE_OK) {
1156+    rc = sqlite3BtreeBeginTrans(pBt, wrflag, pSchemaVersion);
1157+  }
1158+  if (rc == SQLITE_OK) {
1159+    pBt->pBt->metaRecoverStatus = META_RECOVER_SUCCESS;
1160+  }
1161+  if (pData) {
1162+    sqlite3_free(pData);
1163+  }
1164+  return rc;
1165+}
1166+
1167+static int Sqlite3MetaDwrCheckRestore(Btree *pBt) {
1168+  Pager *pPager = pBt->pBt->pPager;
1169+  int rc = MetaDwrOpenFile(pPager, 1);
1170+  if (rc != SQLITE_OK) {
1171+    return rc;
1172+  }
1173+  ScanPages metaPages = {0};
1174+  rc = ScanMetaPages(pBt, &metaPages);
1175+  if (rc != SQLITE_OK || metaPages.pageCnt == 0) {
1176+    goto CHK_RESTORE_OUT;
1177+  }
1178+  rc = MetaDwrLoadAndCheckMetaFile(pBt->pBt, 0);
1179+  if (rc != SQLITE_OK) {
1180+    goto CHK_RESTORE_OUT;
1181+  }
1182+  MetaDwrHdr *hdr = pPager->metaHdr;
1183+  if (hdr->pageCnt == 0 || hdr->pageCnt != metaPages.pageCnt ||
1184+    memcmp(hdr->pages, metaPages.pages, hdr->pageCnt * sizeof(Pgno))) {
1185+    sqlite3_log(SQLITE_WARNING_DUMP, "sqlite meta restore all");
1186+    rc = MetaDwrRestoreAllPages(pBt, &metaPages, hdr);
1187+    if (rc == SQLITE_OK) {
1188+      pPager->metaChanged = META_SCHEMA_CHANGED;
1189+      rc = MetaDwrWriteHeader(pPager, hdr);
1190+      pPager->metaChanged = 0;
1191+    }
1192+  }
1193+  if (rc == SQLITE_OK) {
1194+    pBt->pBt->maxMetaPage = metaPages.maxPageNo;
1195+  }
1196+CHK_RESTORE_OUT:
1197+  ReleaseMetaPages(&metaPages);
1198+  return rc;
1199+}
1200+
1201+static inline u8 IsConnectionValidForCheck(Pager *pPager)
1202+{
1203+#if SQLITE_OS_UNIX
1204+    unixFile *fd = (unixFile *)pPager->fd;
1205+    // unix and only one connection exist
1206+    if (fd == NULL || fd->pInode == NULL || pPager->pVfs == NULL ||
1207+      sqlite3_stricmp(pPager->pVfs->zName, "unix") != 0 || fd->pInode->nRef != 1) {
1208+      return 0;
1209+    }
1210+    return 1;
1211+#else
1212+    return 0;
1213+#endif
1214+}
1215+
1216+static int MetaDwrOpenAndCheck(Btree *pBt)
1217+{
1218+  Pager *pPager = pBt->pBt->pPager;
1219+  if (pPager->memDb || pPager->readOnly || !IsConnectionValidForCheck(pPager)) {
1220+    return SQLITE_OK;
1221+  }
1222+#ifdef SQLITE_HAS_CODEC
1223+  // not support codec right now
1224+  if (pPager->xCodec) {
1225+    return SQLITE_OK;
1226+  }
1227+#endif
1228+  sqlite3BtreeEnter(pBt);
1229+  int rc = SQLITE_OK;
1230+  int openedTransaction = 0;
1231+  int tnxState = sqlite3BtreeTxnState(pBt);
1232+  if (tnxState == SQLITE_TXN_NONE) {
1233+    rc = sqlite3BtreeBeginTrans(pBt, 0, 0);
1234+    if (rc != SQLITE_OK) {
1235+      goto DWR_OPEN_OUT;
1236+    }
1237+    openedTransaction = 1;
1238+  }
1239+  rc = MetaDwrCheckMeta(pBt);
1240+  if (rc == SQLITE_CORRUPT || rc == SQLITE_NOTADB) {
1241+    // keep txn status after recover
1242+    rc = MetaDwrRecoverAndBeginTran(pBt, tnxState == SQLITE_TXN_WRITE ? 1 : 0, 0);
1243+    goto DWR_OPEN_OUT;
1244+  }
1245+  rc = Sqlite3MetaDwrCheckRestore(pBt);
1246+DWR_OPEN_OUT:
1247+  if (rc == SQLITE_OK && pBt->pBt->metaRecoverStatus == META_RECOVER_SUCCESS) {
1248+    rc = MetaDwrCheckMeta(pBt);
1249+    if (rc == SQLITE_OK) {
1250+      rc = SQLITE_META_RECOVERED;
1251+    }
1252+  }
1253+  /* Close the transaction, if one was opened. */
1254+  if (openedTransaction) {
1255+    sqlite3BtreeCommit(pBt);
1256+  }
1257+  sqlite3BtreeLeave(pBt);
1258+  return rc;
1259+}
1260
1261+static void MetaDwrDisable(Btree *pBt)
1262+{
1263+  Pager *pPager = pBt->pBt->pPager;
1264+  if (pPager->metaFd == NULL || pPager->memDb || pPager->readOnly || !IsConnectionValidForCheck(pPager)) {
1265+    return;
1266+  }
1267+#ifdef SQLITE_HAS_CODEC
1268+  // not support codec right now
1269+  if (pPager->xCodec) {
1270+    return;
1271+  }
1272+#endif
1273+  sqlite3BtreeEnter(pBt);
1274+  MetaDwrCloseFile(pPager);
1275+  MetaDwrReleaseHdr(pPager->metaHdr);
1276+  pPager->metaHdr = NULL;
1277+  const char *metaPath = GetMetaFilePath(pPager);
1278+  if (metaPath != NULL) {
1279+    (void)osUnlink(metaPath);
1280+  }
1281+  if (pPager->metaFd) {
1282+    sqlite3_free(pPager->metaFd);
1283+    pPager->metaFd = NULL;
1284+  }
1285+  sqlite3BtreeLeave(pBt);
1286+}
1287+#endif
1288 #if SQLITE_OS_UNIX
1289 #include <unistd.h>
1290 #include <sys/syscall.h>
1291--
12922.47.0.windows.2
1293
1294