internal RC ptrmapGet(Pgno key, ref PTRMAP pEType, ref Pgno pPgno) { Debug.Assert(MutexEx.Held(this.Mutex)); var iPtrmap = (int)MemPage.PTRMAP_PAGENO(this, key); var pDbPage = new PgHdr(); // The pointer map page var rc = this.Pager.Get((Pgno)iPtrmap, ref pDbPage); if (rc != RC.OK) { return(rc); } var pPtrmap = Pager.sqlite3PagerGetData(pDbPage);// Pointer map page data var offset = (int)MemPage.PTRMAP_PTROFFSET((Pgno)iPtrmap, key); if (offset < 0) { Pager.Unref(pDbPage); return(SysEx.SQLITE_CORRUPT_BKPT()); } Debug.Assert(offset <= (int)this.UsableSize - 5); var v = pPtrmap[offset]; if (v < 1 || v > 5) { return(SysEx.SQLITE_CORRUPT_BKPT()); } pEType = (PTRMAP)v; pPgno = ConvertEx.Get4(pPtrmap, offset + 1); Pager.Unref(pDbPage); return(RC.OK); }
internal void zeroPage(int flags) { var data = this.Data; var pBt = this.Shared; var hdr = this.HeaderOffset; Debug.Assert(Pager.GetPageID(this.DbPage) == this.ID); Debug.Assert(Pager.sqlite3PagerGetExtra <MemPage>(this.DbPage) == this); Debug.Assert(Pager.sqlite3PagerGetData(this.DbPage) == data); Debug.Assert(Pager.IsPageWriteable(this.DbPage)); Debug.Assert(MutexEx.Held(pBt.Mutex)); if (pBt.SecureDelete) { Array.Clear(data, hdr, (int)(pBt.UsableSize - hdr)); } data[hdr] = (byte)flags; var first = (ushort)(hdr + 8 + 4 * ((flags & Btree.PTF_LEAF) == 0 ? 1 : 0)); Array.Clear(data, hdr + 1, 4); data[hdr + 7] = 0; ConvertEx.Put2(data, hdr + 5, pBt.UsableSize); this.FreeBytes = (ushort)(pBt.UsableSize - first); decodeFlags(flags); this.HeaderOffset = hdr; this.CellOffset = first; this.NOverflows = 0; Debug.Assert(pBt.PageSize >= 512 && pBt.PageSize <= 65536); this.MaskPage = (ushort)(pBt.PageSize - 1); this.Cells = 0; this.HasInit = true; }
internal static MemPage btreePageFromDbPage(DbPage pDbPage, Pgno pgno, BtShared pBt) { var pPage = (MemPage)Pager.sqlite3PagerGetExtra <MemPage>(pDbPage); pPage.Data = Pager.sqlite3PagerGetData(pDbPage); pPage.DbPage = pDbPage; pPage.Shared = pBt; pPage.ID = pgno; pPage.HeaderOffset = (byte)(pPage.ID == 1 ? 100 : 0); return(pPage); }
internal void ptrmapPut(Pgno key, PTRMAP eType, Pgno parent, ref RC rRC) { if (rRC != RC.OK) { return; } Debug.Assert(MutexEx.Held(this.Mutex)); // The master-journal page number must never be used as a pointer map page Debug.Assert(!MemPage.PTRMAP_ISPAGE(this, MemPage.PENDING_BYTE_PAGE(this))); Debug.Assert(this.AutoVacuum); if (key == 0) { rRC = SysEx.SQLITE_CORRUPT_BKPT(); return; } var iPtrmap = MemPage.PTRMAP_PAGENO(this, key); var pDbPage = new PgHdr(); // The pointer map page var rc = this.Pager.Get(iPtrmap, ref pDbPage); if (rc != RC.OK) { rRC = rc; return; } var offset = (int)MemPage.PTRMAP_PTROFFSET(iPtrmap, key); if (offset < 0) { rRC = SysEx.SQLITE_CORRUPT_BKPT(); goto ptrmap_exit; } Debug.Assert(offset <= (int)this.UsableSize - 5); var pPtrmap = Pager.sqlite3PagerGetData(pDbPage); // The pointer map data if (eType != (PTRMAP)pPtrmap[offset] || ConvertEx.Get4(pPtrmap, offset + 1) != parent) { Btree.TRACE("PTRMAP_UPDATE: {0}->({1},{2})", key, eType, parent); rRC = rc = Pager.Write(pDbPage); if (rc == RC.OK) { pPtrmap[offset] = (byte)eType; ConvertEx.Put4L(pPtrmap, (uint)offset + 1, parent); } } ptrmap_exit: Pager.Unref(pDbPage); }
// was:fetchPayload private RC AccessPayload(uint offset, uint size, byte[] b, bool writeOperation) { var page = Pages[PageID]; // Btree page of current entry Debug.Assert(page != null); Debug.Assert(State == CursorState.VALID); Debug.Assert(PagesIndexs[PageID] < page.Cells); Debug.Assert(HoldsMutex()); GetCellInfo(); var payload = Info.Cells; var nKey = (uint)(page.HasIntKey ? 0 : (int)Info.nKey); var shared = Shared; // Btree this cursor belongs to if (Check.NEVER(offset + size > nKey + Info.nData) || Info.nLocal > shared.UsableSize) { // Trying to read or write past the end of the data is an error return(SysEx.SQLITE_CORRUPT_BKPT()); } // Check if data must be read/written to/from the btree page itself. var rc = RC.OK; uint bOffset = 0; if (offset < Info.nLocal) { var a = (int)size; if (a + offset > Info.nLocal) { a = (int)(Info.nLocal - offset); } rc = CopyPayload(page.DbPage, payload, (uint)(offset + Info.CellID + Info.nHeader), b, bOffset, (uint)a, writeOperation); offset = 0; bOffset += (uint)a; size -= (uint)a; } else { offset -= Info.nLocal; } var iIdx = 0; if (rc == RC.OK && size > 0) { var ovflSize = (uint)(shared.UsableSize - 4); // Bytes content per ovfl page var nextPage = (Pgno)ConvertEx.Get4(payload, Info.nLocal + Info.CellID + Info.nHeader); #if !SQLITE_OMIT_INCRBLOB // If the isIncrblobHandle flag is set and the BtCursor.aOverflow[] has not been allocated, allocate it now. The array is sized at // one entry for each overflow page in the overflow chain. The page number of the first overflow page is stored in aOverflow[0], // etc. A value of 0 in the aOverflow[] array means "not yet known" (the cache is lazily populated). if (IsIncrblob && OverflowIDs == null) { var nOvfl = (Info.nPayload - Info.nLocal + ovflSize - 1) / ovflSize; OverflowIDs = new Pgno[nOvfl]; } // If the overflow page-list cache has been allocated and the entry for the first required overflow page is valid, skip directly to it. if (OverflowIDs != null && OverflowIDs[offset / ovflSize] != 0) { iIdx = (int)(offset / ovflSize); nextPage = OverflowIDs[iIdx]; offset = (offset % ovflSize); } #endif for (; rc == RC.OK && size > 0 && nextPage != 0; iIdx++) { #if !SQLITE_OMIT_INCRBLOB // If required, populate the overflow page-list cache. if (OverflowIDs != null) { Debug.Assert(OverflowIDs[iIdx] == 0 || OverflowIDs[iIdx] == nextPage); OverflowIDs[iIdx] = nextPage; } #endif MemPage MemPageDummy = null; if (offset >= ovflSize) { // The only reason to read this page is to obtain the page number for the next page in the overflow chain. The page // data is not required. So first try to lookup the overflow page-list cache, if any, then fall back to the getOverflowPage() function. #if !SQLITE_OMIT_INCRBLOB if (OverflowIDs != null && OverflowIDs[iIdx + 1] != 0) { nextPage = OverflowIDs[iIdx + 1]; } else #endif rc = shared.getOverflowPage(nextPage, out MemPageDummy, out nextPage); offset -= ovflSize; } else { // Need to read this page properly. It contains some of the range of data that is being read (eOp==null) or written (eOp!=null). var pDbPage = new PgHdr(); var a = (int)size; rc = shared.Pager.Get(nextPage, ref pDbPage); if (rc == RC.OK) { payload = Pager.sqlite3PagerGetData(pDbPage); nextPage = ConvertEx.Get4(payload); if (a + offset > ovflSize) { a = (int)(ovflSize - offset); } rc = CopyPayload(pDbPage, payload, offset + 4, b, bOffset, (uint)a, writeOperation); Pager.Unref(pDbPage); offset = 0; size -= (uint)a; bOffset += (uint)a; } } } } if (rc == RC.OK && size > 0) { return(SysEx.SQLITE_CORRUPT_BKPT()); } return(rc); }
internal static void checkList(IntegrityCk pCheck, int isFreeList, int iPage, int N, string zContext) { int i; int expected = N; int iFirst = iPage; while (N-- > 0 && pCheck.mxErr != 0) { var pOvflPage = new PgHdr(); if (iPage < 1) { checkAppendMsg(pCheck, zContext, "%d of %d pages missing from overflow list starting at %d", N + 1, expected, iFirst); break; } if (checkRef(pCheck, (uint)iPage, zContext) != 0) { break; } byte[] pOvflData; if (Pager.sqlite3PagerGet(pCheck.pPager, (Pgno)iPage, ref pOvflPage) != 0) { checkAppendMsg(pCheck, zContext, "failed to get page %d", iPage); break; } pOvflData = Pager.sqlite3PagerGetData(pOvflPage); if (isFreeList != 0) { int n = (int)ConvertEx.sqlite3Get4byte(pOvflData, 4); #if !SQLITE_OMIT_AUTOVACUUM if (pCheck.pBt.autoVacuum) { checkPtrmap(pCheck, (uint)iPage, PTRMAP_FREEPAGE, 0, zContext); } #endif if (n > (int)pCheck.pBt.usableSize / 4 - 2) { checkAppendMsg(pCheck, zContext, "freelist leaf count too big on page %d", iPage); N--; } else { for (i = 0; i < n; i++) { Pgno iFreePage = ConvertEx.sqlite3Get4byte(pOvflData, 8 + i * 4); #if !SQLITE_OMIT_AUTOVACUUM if (pCheck.pBt.autoVacuum) { checkPtrmap(pCheck, iFreePage, PTRMAP_FREEPAGE, 0, zContext); } #endif checkRef(pCheck, iFreePage, zContext); } N -= n; } } #if !SQLITE_OMIT_AUTOVACUUM else { /* If this database supports auto-vacuum and iPage is not the last ** page in this overflow list, check that the pointer-map entry for ** the following page matches iPage. */ if (pCheck.pBt.autoVacuum && N > 0) { i = (int)ConvertEx.sqlite3Get4byte(pOvflData); checkPtrmap(pCheck, (uint)i, PTRMAP_OVERFLOW2, (uint)iPage, zContext); } } #endif iPage = (int)ConvertEx.sqlite3Get4byte(pOvflData); Pager.sqlite3PagerUnref(pOvflPage); } }
internal RC btreeInitPage() { Debug.Assert(this.Shared != null); Debug.Assert(MutexEx.Held(this.Shared.Mutex)); Debug.Assert(this.ID == Pager.GetPageID(this.DbPage)); Debug.Assert(this == Pager.sqlite3PagerGetExtra <MemPage>(this.DbPage)); Debug.Assert(this.Data == Pager.sqlite3PagerGetData(this.DbPage)); if (!this.HasInit) { var pBt = this.Shared; // The main btree structure var hdr = this.HeaderOffset; // Offset to beginning of page header var data = this.Data; if (decodeFlags(data[hdr]) != 0) { return(SysEx.SQLITE_CORRUPT_BKPT()); } Debug.Assert(pBt.PageSize >= 512 && pBt.PageSize <= 65536); this.MaskPage = (ushort)(pBt.PageSize - 1); this.NOverflows = 0; var usableSize = (int)pBt.UsableSize; // Amount of usable space on each page ushort cellOffset; // Offset from start of page to first cell pointer this.CellOffset = (cellOffset = (ushort)(hdr + 12 - 4 * this.Leaf)); var top = ConvertEx.Get2nz(data, hdr + 5); // First byte of the cell content area this.Cells = (ushort)(ConvertEx.Get2(data, hdr + 3)); if (this.Cells > Btree.MX_CELL(pBt)) { // To many cells for a single page. The page must be corrupt return(SysEx.SQLITE_CORRUPT_BKPT()); } // A malformed database page might cause us to read past the end of page when parsing a cell. // The following block of code checks early to see if a cell extends past the end of a page boundary and causes SQLITE_CORRUPT to be // returned if it does. var iCellFirst = cellOffset + 2 * this.Cells; // First allowable cell or freeblock offset var iCellLast = usableSize - 4; // Last possible cell or freeblock offset #if SQLITE_ENABLE_OVERSIZE_CELL_CHECK if (pPage.leaf == 0) { iCellLast--; } for (var i = 0; i < pPage.nCell; i++) { pc = (ushort)ConvertEx.get2byte(data, cellOffset + i * 2); if (pc < iCellFirst || pc > iCellLast) { return(SysEx.SQLITE_CORRUPT_BKPT()); } var sz = cellSizePtr(pPage, data, pc); if (pc + sz > usableSize) { return(SysEx.SQLITE_CORRUPT_BKPT()); } } if (pPage.leaf == 0) { iCellLast++; } #endif // Compute the total free space on the page var pc = (ushort)ConvertEx.Get2(data, hdr + 1); // Address of a freeblock within pPage.aData[] var nFree = (ushort)(data[hdr + 7] + top); // Number of unused bytes on the page while (pc > 0) { if (pc < iCellFirst || pc > iCellLast) { // Start of free block is off the page return(SysEx.SQLITE_CORRUPT_BKPT()); } var next = (ushort)ConvertEx.Get2(data, pc); var size = (ushort)ConvertEx.Get2(data, pc + 2); if ((next > 0 && next <= pc + size + 3) || pc + size > usableSize) { // Free blocks must be in ascending order. And the last byte of the free-block must lie on the database page. return(SysEx.SQLITE_CORRUPT_BKPT()); } nFree = (ushort)(nFree + size); pc = next; } // At this point, nFree contains the sum of the offset to the start of the cell-content area plus the number of free bytes within // the cell-content area. If this is greater than the usable-size of the page, then the page must be corrupted. This check also // serves to verify that the offset to the start of the cell-content area, according to the page header, lies within the page. if (nFree > usableSize) { return(SysEx.SQLITE_CORRUPT_BKPT()); } this.FreeBytes = (ushort)(nFree - iCellFirst); this.HasInit = true; } return(RC.OK); }