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 static void pageReinit(DbPage pData) { var pPage = Pager.sqlite3PagerGetExtra <MemPage>(pData); Debug.Assert(Pager.GetPageRefCount(pData) > 0); if (pPage.HasInit) { Debug.Assert(MutexEx.Held(pPage.Shared.Mutex)); pPage.HasInit = false; if (Pager.GetPageRefCount(pData) > 1) { // pPage might not be a btree page; it might be an overflow page or ptrmap page or a free page. In those cases, the following // call to btreeInitPage() will likely return SQLITE_CORRUPT. But no harm is done by this. And it is very important that // btreeInitPage() be called on every btree page so we make the call for every page that comes in for re-initing. pPage.btreeInitPage(); } } }
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); }