📄 bt_verify.c
字号:
if (ret == DB_VERIFY_BAD) isbad = 1; else goto done; } if ((ret = __db_vrfy_ccclose(cc)) != 0) goto err; cc = NULL; /* Case 1 */ if (pip->type == P_LRECNO) { if (!LF_ISSET(ST_IS_RECNO) && !(LF_ISSET(ST_DUPOK) && !LF_ISSET(ST_DUPSORT))) { isbad = 1; EPRINT((dbp->dbenv, "Page %lu: recno leaf page non-recno tree", (u_long)pgno)); goto done; } goto leaf; } else if (LF_ISSET(ST_IS_RECNO)) { /* * It's a non-recno leaf. Had better not be a recno * subtree. */ isbad = 1; EPRINT((dbp->dbenv, "Page %lu: non-recno leaf page in recno tree", (u_long)pgno)); goto done; } /* Case 2--no more work. */ if (pip->type == P_LDUP) goto leaf; /* Case 3 */ /* Check if we have any dups. */ if (F_ISSET(pip, VRFY_HAS_DUPS)) { /* If dups aren't allowed in this btree, trouble. */ if (!LF_ISSET(ST_DUPOK)) { isbad = 1; EPRINT((dbp->dbenv, "Page %lu: duplicates in non-dup btree", (u_long)pgno)); } else { /* * We correctly have dups. If any are off-page, * traverse those btrees recursively. */ if ((ret = __db_vrfy_childcursor(vdp, &cc)) != 0) goto err; for (ret = __db_vrfy_ccset(cc, pgno, &child); ret == 0; ret = __db_vrfy_ccnext(cc, &child)) { stflags = flags | ST_RECNUM | ST_DUPSET; /* Skip any overflow entries. */ if (child->type == V_DUPLICATE) { if ((ret = __db_vrfy_duptype( dbp, vdp, child->pgno, stflags)) != 0) { isbad = 1; /* Next child. */ continue; } if ((ret = __bam_vrfy_subtree( dbp, vdp, child->pgno, NULL, NULL, stflags | ST_TOPLEVEL, NULL, NULL, NULL)) != 0) { if (ret != DB_VERIFY_BAD) goto err; else isbad = 1; } } } if ((ret = __db_vrfy_ccclose(cc)) != 0) goto err; cc = NULL; /* * If VRFY_DUPS_UNSORTED is set, * ST_DUPSORT had better not be. */ if (F_ISSET(pip, VRFY_DUPS_UNSORTED) && LF_ISSET(ST_DUPSORT)) { EPRINT((dbp->dbenv, "Page %lu: unsorted duplicate set in sorted-dup database", (u_long)pgno)); isbad = 1; } } } goto leaf; case P_IBTREE: case P_IRECNO: /* We handle these below. */ break; default: /* * If a P_IBTREE or P_IRECNO contains a reference to an * invalid page, we'll wind up here; handle it gracefully. * Note that the code at the "done" label assumes that the * current page is a btree/recno one of some sort; this * is not the case here, so we goto err. * * If the page is entirely zeroed, its pip->type will be a lie * (we assumed it was a hash page, as they're allowed to be * zeroed); handle this case specially. */ if (F_ISSET(pip, VRFY_IS_ALLZEROES)) ZEROPG_ERR_PRINT(dbp->dbenv, pgno, "btree or recno page"); else EPRINT((dbp->dbenv, "Page %lu: btree or recno page is of inappropriate type %lu", (u_long)pgno, (u_long)pip->type)); ret = DB_VERIFY_BAD; goto err; } /* * Cases 4 & 5: This is a btree or recno internal page. For each child, * recurse, keeping a running count of nrecs and making sure the level * is always reasonable. */ if ((ret = __db_vrfy_childcursor(vdp, &cc)) != 0) goto err; for (ret = __db_vrfy_ccset(cc, pgno, &child); ret == 0; ret = __db_vrfy_ccnext(cc, &child)) if (child->type == V_RECNO) { if (pip->type != P_IRECNO) { TYPE_ERR_PRINT(dbp->dbenv, "__bam_vrfy_subtree", pgno, pip->type); DB_ASSERT(0); ret = EINVAL; goto err; } if ((ret = __bam_vrfy_subtree(dbp, vdp, child->pgno, NULL, NULL, flags, &child_level, &child_nrecs, &child_relen)) != 0) { if (ret != DB_VERIFY_BAD) goto done; else isbad = 1; } if (LF_ISSET(ST_RELEN)) { if (relen == 0) relen = child_relen; /* * child_relen may be zero if the child subtree * is empty. */ else if (child_relen > 0 && relen != child_relen) { isbad = 1; EPRINT((dbp->dbenv, "Page %lu: recno page returned bad re_len %lu", (u_long)child->pgno, (u_long)child_relen)); } if (relenp) *relenp = relen; } if (LF_ISSET(ST_RECNUM)) nrecs += child_nrecs; if (level != child_level + 1) { isbad = 1; EPRINT((dbp->dbenv, "Page %lu: recno level incorrect: got %lu, expected %lu", (u_long)child->pgno, (u_long)child_level, (u_long)(level - 1))); } } else if (child->type == V_OVERFLOW && (ret = __db_vrfy_ovfl_structure(dbp, vdp, child->pgno, child->tlen, flags)) != 0) { if (ret == DB_VERIFY_BAD) isbad = 1; else goto done; } if ((ret = __db_vrfy_ccclose(cc)) != 0) goto err; cc = NULL; /* We're done with case 4. */ if (pip->type == P_IRECNO) goto done; /* * Case 5. Btree internal pages. * As described above, we need to iterate through all the * items on the page and make sure that our children sort appropriately * with respect to them. * * For each entry, li will be the "left-hand" key for the entry * itself, which must sort lower than all entries on its child; * ri will be the key to its right, which must sort greater. */ if (h == NULL && (ret = mpf->get(mpf, &pgno, 0, &h)) != 0) goto err; for (i = 0; i < pip->entries; i += O_INDX) { li = GET_BINTERNAL(dbp, h, i); ri = (i + O_INDX < pip->entries) ? GET_BINTERNAL(dbp, h, i + O_INDX) : NULL; /* * The leftmost key is forcibly sorted less than all entries, * so don't bother passing it. */ if ((ret = __bam_vrfy_subtree(dbp, vdp, li->pgno, i == 0 ? NULL : li, ri, flags, &child_level, &child_nrecs, NULL)) != 0) { if (ret != DB_VERIFY_BAD) goto done; else isbad = 1; } if (LF_ISSET(ST_RECNUM)) { /* * Keep a running tally on the actual record count so * we can return it to our parent (if we have one) or * compare it to the NRECS field if we're a root page. */ nrecs += child_nrecs; /* * Make sure the actual record count of the child * is equal to the value in the BINTERNAL structure. */ if (li->nrecs != child_nrecs) { isbad = 1; EPRINT((dbp->dbenv, "Page %lu: item %lu has incorrect record count of %lu, should be %lu", (u_long)pgno, (u_long)i, (u_long)li->nrecs, (u_long)child_nrecs)); } } if (level != child_level + 1) { isbad = 1; EPRINT((dbp->dbenv, "Page %lu: Btree level incorrect: got %lu, expected %lu", (u_long)li->pgno, (u_long)child_level, (u_long)(level - 1))); } } if (0) {leaf: level = LEAFLEVEL; if (LF_ISSET(ST_RECNUM)) nrecs = pip->rec_cnt; /* XXX * We should verify that the record count on a leaf page * is the sum of the number of keys and the number of * records in its off-page dups. This requires looking * at the page again, however, and it may all be changing * soon, so for now we don't bother. */ if (LF_ISSET(ST_RELEN) && relenp) *relenp = pip->re_len; }done: if (F_ISSET(pip, VRFY_INCOMPLETE) && isbad == 0 && ret == 0) { /* * During the page-by-page pass, item order verification was * not finished due to the presence of overflow items. If * isbad == 0, though, it's now safe to do so, as we've * traversed any child overflow pages. Do it. */ if (h == NULL && (ret = mpf->get(mpf, &pgno, 0, &h)) != 0) goto err; if ((ret = __bam_vrfy_itemorder(dbp, vdp, h, pgno, 0, 1, 0, flags)) != 0) goto err; F_CLR(pip, VRFY_INCOMPLETE); } /* * It's possible to get to this point with a page that has no * items, but without having detected any sort of failure yet. * Having zero items is legal if it's a leaf--it may be the * root page in an empty tree, or the tree may have been * modified with the DB_REVSPLITOFF flag set (there's no way * to tell from what's on disk). For an internal page, * though, having no items is a problem (all internal pages * must have children). */ if (isbad == 0 && ret == 0) { if (h == NULL && (ret = mpf->get(mpf, &pgno, 0, &h)) != 0) goto err; if (NUM_ENT(h) == 0 && ISINTERNAL(h)) { EPRINT((dbp->dbenv, "Page %lu: internal page is empty and should not be", (u_long)pgno)); isbad = 1; goto err; } } /* * Our parent has sent us BINTERNAL pointers to parent records * so that we can verify our place with respect to them. If it's * appropriate--we have a default sort function--verify this. */ if (isbad == 0 && ret == 0 && !LF_ISSET(DB_NOORDERCHK) && lp != NULL) { if (h == NULL && (ret = mpf->get(mpf, &pgno, 0, &h)) != 0) goto err; /* * __bam_vrfy_treeorder needs to know what comparison function * to use. If ST_DUPSET is set, we're in a duplicate tree * and we use the duplicate comparison function; otherwise, * use the btree one. If unset, use the default, of course. */ func = LF_ISSET(ST_DUPSET) ? dbp->dup_compare : ((BTREE *)dbp->bt_internal)->bt_compare; if (func == NULL) func = __bam_defcmp; if ((ret = __bam_vrfy_treeorder( dbp, pgno, h, lp, rp, func, flags)) != 0) { if (ret == DB_VERIFY_BAD) isbad = 1; else goto err; } } /* * This is guaranteed to succeed for leaf pages, but no harm done. * * Internal pages below the top level do not store their own * record numbers, so we skip them. */ if (LF_ISSET(ST_RECNUM) && nrecs != pip->rec_cnt && toplevel) { isbad = 1; EPRINT((dbp->dbenv, "Page %lu: bad record count: has %lu records, claims %lu", (u_long)pgno, (u_long)nrecs, (u_long)pip->rec_cnt)); } if (levelp) *levelp = level; if (nrecsp) *nrecsp = nrecs; pgset = vdp->pgset; if ((ret = __db_vrfy_pgset_get(pgset, pgno, &p)) != 0) goto err; if (p != 0) { isbad = 1; EPRINT((dbp->dbenv, "Page %lu: linked twice", (u_long)pgno)); } else if ((ret = __db_vrfy_pgset_inc(pgset, pgno)) != 0) goto err; if (toplevel) /* * The last page's next_pgno in the leaf chain should have been * PGNO_INVALID. */ if (vdp->next_pgno != PGNO_INVALID) { EPRINT((dbp->dbenv, "Page %lu: unterminated leaf chain", (u_long)vdp->prev_pgno)); isbad = 1; }err: if (toplevel) { /* Restore our caller's settings. */ vdp->next_pgno = next_pgno; vdp->prev_pgno = prev_pgno; vdp->leaf_type = leaf_type; } if (h != NULL && (t_ret = mpf->put(mpf, h, 0)) != 0 && ret == 0) ret = t_ret; if ((t_ret = __db_vrfy_putpageinfo(dbp->dbenv, vdp, pip)) != 0 && ret == 0) ret = t_ret; if (cc != NULL && ((t_ret = __db_vrfy_ccclose(cc)) != 0) && ret == 0) ret = t_ret; return ((ret == 0 && isbad == 1) ? DB_VERIFY_BAD : ret);}/* * __bam_vrfy_treeorder -- * Verify that the lowest key on a page sorts greater than the * BINTERNAL which points to it (lp), and the highest key * sorts less than the BINTERNAL above that (rp). * * If lp is NULL, this means that it was the leftmost key on the * parent, which (regardless of sort function) sorts less than * all keys. No need to check it. * * If rp is NULL, lp was the highest key on the parent, so there's * no higher key we must sort less than. */static int__bam_vrfy_treeorder(dbp, pgno, h, lp, rp, func, flags) DB *dbp; db_pgno_t pgno; PAGE *h; BINTERNAL *lp, *rp; int (*func) __P((DB *, const DBT *, const DBT *)); u_int32_t flags;{ BOVERFLOW *bo; DBT dbt; db_indx_t last; int ret, cmp; memset(&dbt, 0, sizeof(DBT)); F_SET(&dbt, DB_DBT_MALLOC); ret = 0; /* * Empty pages are sorted correctly by definition. We check * to see whether they ought to be empty elsewhere; leaf * pages legally may be. */ if (NUM_ENT(h) == 0) return (0); switch (TYPE(h)) { case P_IBTREE: case P_LDUP: last = NUM_ENT(h) - O_INDX; break; case P_LBTREE: last = NUM_ENT(h) - P_INDX; break; default: TYPE_ERR_PRINT(dbp->dbenv, "__bam_vrfy_treeorder", pgno, TYPE(h)); DB_ASSERT(0); return (EINVAL); } /* * The key on page h, the child page, is more likely to be * an overflow page, so we pass its offset, rather than lp/rp's, * into __bam_cmp. This will take advantage of __db_moff. */ /* * Skip first-item check if we're an internal page--the first * entry on an internal page is treated specially by __bam_cmp, * so what's on the page shouldn't matter. (Plus, since we're passing * our page and item 0 as to __bam_cmp, we'll sort before our * parent and falsely report a failure.) */ if (lp != NULL && TYPE(h) != P_IBTREE) { if (lp->type == B_KEYDATA) { dbt.data = lp->data; dbt.size = lp->len; } else if (lp->type == B_OVERFLOW) { bo = (BOVERFLOW *)lp->data; if ((ret = __db_goff(dbp, &dbt, bo->tlen, bo->pgno, NULL, NULL)) != 0) return (ret); } else { DB_ASSERT(0); EPRINT((dbp->dbenv, "Page %lu: unknown type for internal record", (u_long)PGNO(h))); return (EINVAL); } /* On error, fall through, free if neeeded, and return. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -