📄 bt_verify.c
字号:
/*- * See the file LICENSE for redistribution information. * * Copyright (c) 1999-2002 * Sleepycat Software. All rights reserved. * * $Id: bt_verify.c,v 1.76 2002/07/03 19:03:51 bostic Exp $ */#include "db_config.h"#ifndef lintstatic const char revid[] = "$Id: bt_verify.c,v 1.76 2002/07/03 19:03:51 bostic Exp $";#endif /* not lint */#ifndef NO_SYSTEM_INCLUDES#include <sys/types.h>#include <string.h>#endif#include "db_int.h"#include "dbinc/db_page.h"#include "dbinc/db_verify.h"#include "dbinc/btree.h"static int __bam_safe_getdata __P((DB *, PAGE *, u_int32_t, int, DBT *, int *));static int __bam_vrfy_inp __P((DB *, VRFY_DBINFO *, PAGE *, db_pgno_t, db_indx_t *, u_int32_t));static int __bam_vrfy_treeorder __P((DB *, db_pgno_t, PAGE *, BINTERNAL *, BINTERNAL *, int (*)(DB *, const DBT *, const DBT *), u_int32_t));static int __ram_vrfy_inp __P((DB *, VRFY_DBINFO *, PAGE *, db_pgno_t, db_indx_t *, u_int32_t));#define OKFLAGS (DB_AGGRESSIVE | DB_NOORDERCHK | DB_SALVAGE)/* * __bam_vrfy_meta -- * Verify the btree-specific part of a metadata page. * * PUBLIC: int __bam_vrfy_meta __P((DB *, VRFY_DBINFO *, BTMETA *, * PUBLIC: db_pgno_t, u_int32_t)); */int__bam_vrfy_meta(dbp, vdp, meta, pgno, flags) DB *dbp; VRFY_DBINFO *vdp; BTMETA *meta; db_pgno_t pgno; u_int32_t flags;{ VRFY_PAGEINFO *pip; int isbad, t_ret, ret; db_indx_t ovflsize; if ((ret = __db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0) return (ret); isbad = 0; /* * If VRFY_INCOMPLETE is not set, then we didn't come through * __db_vrfy_pagezero and didn't incompletely * check this page--we haven't checked it at all. * Thus we need to call __db_vrfy_meta and check the common fields. * * If VRFY_INCOMPLETE is set, we've already done all the same work * in __db_vrfy_pagezero, so skip the check. */ if (!F_ISSET(pip, VRFY_INCOMPLETE) && (ret = __db_vrfy_meta(dbp, vdp, &meta->dbmeta, pgno, flags)) != 0) { if (ret == DB_VERIFY_BAD) isbad = 1; else goto err; } /* bt_minkey: must be >= 2; must produce sensible ovflsize */ /* avoid division by zero */ ovflsize = meta->minkey > 0 ? B_MINKEY_TO_OVFLSIZE(dbp, meta->minkey, dbp->pgsize) : 0; if (meta->minkey < 2 || ovflsize > B_MINKEY_TO_OVFLSIZE(dbp, DEFMINKEYPAGE, dbp->pgsize)) { pip->bt_minkey = 0; isbad = 1; EPRINT((dbp->dbenv, "Page %lu: nonsensical bt_minkey value %lu on metadata page", (u_long)pgno, (u_long)meta->minkey)); } else pip->bt_minkey = meta->minkey; /* bt_maxkey: no constraints (XXX: right?) */ pip->bt_maxkey = meta->maxkey; /* re_len: no constraints on this (may be zero or huge--we make rope) */ pip->re_len = meta->re_len; /* * The root must not be current page or 0 and it must be within * database. If this metadata page is the master meta data page * of the file, then the root page had better be page 1. */ pip->root = 0; if (meta->root == PGNO_INVALID || meta->root == pgno || !IS_VALID_PGNO(meta->root) || (pgno == PGNO_BASE_MD && meta->root != 1)) { isbad = 1; EPRINT((dbp->dbenv, "Page %lu: nonsensical root page %lu on metadata page", (u_long)pgno, (u_long)meta->root)); } else pip->root = meta->root; /* Flags. */ if (F_ISSET(&meta->dbmeta, BTM_RENUMBER)) F_SET(pip, VRFY_IS_RRECNO); if (F_ISSET(&meta->dbmeta, BTM_SUBDB)) { /* * If this is a master db meta page, it had better not have * duplicates. */ if (F_ISSET(&meta->dbmeta, BTM_DUP) && pgno == PGNO_BASE_MD) { isbad = 1; EPRINT((dbp->dbenv,"Page %lu: Btree metadata page has both duplicates and multiple databases", (u_long)pgno)); } F_SET(pip, VRFY_HAS_SUBDBS); } if (F_ISSET(&meta->dbmeta, BTM_DUP)) F_SET(pip, VRFY_HAS_DUPS); if (F_ISSET(&meta->dbmeta, BTM_DUPSORT)) F_SET(pip, VRFY_HAS_DUPSORT); if (F_ISSET(&meta->dbmeta, BTM_RECNUM)) F_SET(pip, VRFY_HAS_RECNUMS); if (F_ISSET(pip, VRFY_HAS_RECNUMS) && F_ISSET(pip, VRFY_HAS_DUPS)) { EPRINT((dbp->dbenv, "Page %lu: Btree metadata page illegally has both recnums and dups", (u_long)pgno)); isbad = 1; } if (F_ISSET(&meta->dbmeta, BTM_RECNO)) { F_SET(pip, VRFY_IS_RECNO); dbp->type = DB_RECNO; } else if (F_ISSET(pip, VRFY_IS_RRECNO)) { isbad = 1; EPRINT((dbp->dbenv, "Page %lu: metadata page has renumber flag set but is not recno", (u_long)pgno)); } if (F_ISSET(pip, VRFY_IS_RECNO) && F_ISSET(pip, VRFY_HAS_DUPS)) { EPRINT((dbp->dbenv, "Page %lu: recno metadata page specifies duplicates", (u_long)pgno)); isbad = 1; } if (F_ISSET(&meta->dbmeta, BTM_FIXEDLEN)) F_SET(pip, VRFY_IS_FIXEDLEN); else if (pip->re_len > 0) { /* * It's wrong to have an re_len if it's not a fixed-length * database */ isbad = 1; EPRINT((dbp->dbenv, "Page %lu: re_len of %lu in non-fixed-length database", (u_long)pgno, (u_long)pip->re_len)); } /* * We do not check that the rest of the page is 0, because it may * not be and may still be correct. */err: if ((t_ret = __db_vrfy_putpageinfo(dbp->dbenv, vdp, pip)) != 0 && ret == 0) ret = t_ret; return ((ret == 0 && isbad == 1) ? DB_VERIFY_BAD : ret);}/* * __ram_vrfy_leaf -- * Verify a recno leaf page. * * PUBLIC: int __ram_vrfy_leaf __P((DB *, VRFY_DBINFO *, PAGE *, db_pgno_t, * PUBLIC: u_int32_t)); */int__ram_vrfy_leaf(dbp, vdp, h, pgno, flags) DB *dbp; VRFY_DBINFO *vdp; PAGE *h; db_pgno_t pgno; u_int32_t flags;{ BKEYDATA *bk; VRFY_PAGEINFO *pip; db_indx_t i; int ret, t_ret, isbad; u_int32_t re_len_guess, len; isbad = 0; if ((ret = __db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0) return (ret); if ((ret = __db_fchk(dbp->dbenv, "__ram_vrfy_leaf", flags, OKFLAGS)) != 0) goto err; if (TYPE(h) != P_LRECNO) { /* We should not have been called. */ TYPE_ERR_PRINT(dbp->dbenv, "__ram_vrfy_leaf", pgno, TYPE(h)); DB_ASSERT(0); ret = EINVAL; goto err; } /* * Verify (and, if relevant, save off) page fields common to * all PAGEs. */ if ((ret = __db_vrfy_datapage(dbp, vdp, h, pgno, flags)) != 0) { if (ret == DB_VERIFY_BAD) isbad = 1; else goto err; } /* * Verify inp[]. Return immediately if it returns DB_VERIFY_BAD; * further checks are dangerous. */ if ((ret = __bam_vrfy_inp(dbp, vdp, h, pgno, &pip->entries, flags)) != 0) goto err; if (F_ISSET(pip, VRFY_HAS_DUPS)) { EPRINT((dbp->dbenv, "Page %lu: Recno database has dups", (u_long)pgno)); ret = DB_VERIFY_BAD; goto err; } /* * Walk through inp and see if the lengths of all the records are the * same--if so, this may be a fixed-length database, and we want to * save off this value. We know inp to be safe if we've gotten this * far. */ re_len_guess = 0; for (i = 0; i < NUM_ENT(h); i++) { bk = GET_BKEYDATA(dbp, h, i); /* KEYEMPTY. Go on. */ if (B_DISSET(bk->type)) continue; if (bk->type == B_OVERFLOW) len = ((BOVERFLOW *)bk)->tlen; else if (bk->type == B_KEYDATA) len = bk->len; else { isbad = 1; EPRINT((dbp->dbenv, "Page %lu: nonsensical type for item %lu", (u_long)pgno, (u_long)i)); continue; } if (re_len_guess == 0) re_len_guess = len; /* * Is this item's len the same as the last one's? If not, * reset to 0 and break--we don't have a single re_len. * Otherwise, go on to the next item. */ if (re_len_guess != len) { re_len_guess = 0; break; } } pip->re_len = re_len_guess; /* Save off record count. */ pip->rec_cnt = NUM_ENT(h);err: if ((t_ret = __db_vrfy_putpageinfo(dbp->dbenv, vdp, pip)) != 0 && ret == 0) ret = t_ret; return ((ret == 0 && isbad == 1) ? DB_VERIFY_BAD : ret);}/* * __bam_vrfy -- * Verify a btree leaf or internal page. * * PUBLIC: int __bam_vrfy __P((DB *, VRFY_DBINFO *, PAGE *, db_pgno_t, * PUBLIC: u_int32_t)); */int__bam_vrfy(dbp, vdp, h, pgno, flags) DB *dbp; VRFY_DBINFO *vdp; PAGE *h; db_pgno_t pgno; u_int32_t flags;{ VRFY_PAGEINFO *pip; int ret, t_ret, isbad; isbad = 0; if ((ret = __db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0) return (ret); switch (TYPE(h)) { case P_IBTREE: case P_IRECNO: case P_LBTREE: case P_LDUP: break; default: TYPE_ERR_PRINT(dbp->dbenv, "__bam_vrfy", pgno, TYPE(h)); DB_ASSERT(0); ret = EINVAL; goto err; } /* * Verify (and, if relevant, save off) page fields common to * all PAGEs. */ if ((ret = __db_vrfy_datapage(dbp, vdp, h, pgno, flags)) != 0) { if (ret == DB_VERIFY_BAD) isbad = 1; else goto err; } /* * The record count is, on internal pages, stored in an overloaded * next_pgno field. Save it off; we'll verify it when we check * overall database structure. We could overload the field * in VRFY_PAGEINFO, too, but this seems gross, and space * is not at such a premium. */ pip->rec_cnt = RE_NREC(h); /* * Verify inp[]. */ if (TYPE(h) == P_IRECNO) { if ((ret = __ram_vrfy_inp(dbp, vdp, h, pgno, &pip->entries, flags)) != 0) goto err; } else if ((ret = __bam_vrfy_inp(dbp, vdp, h, pgno, &pip->entries, flags)) != 0) { if (ret == DB_VERIFY_BAD) isbad = 1; else goto err; EPRINT((dbp->dbenv, "Page %lu: item order check unsafe: skipping", (u_long)pgno)); } else if (!LF_ISSET(DB_NOORDERCHK) && (ret = __bam_vrfy_itemorder(dbp, vdp, h, pgno, 0, 0, 0, flags)) != 0) { /* * We know that the elements of inp are reasonable. * * Check that elements fall in the proper order. */ if (ret == DB_VERIFY_BAD) isbad = 1; else goto err; }err: if ((t_ret = __db_vrfy_putpageinfo(dbp->dbenv, vdp, pip)) != 0 && ret == 0) ret = t_ret; return ((ret == 0 && isbad == 1) ? DB_VERIFY_BAD : ret);}/* * __ram_vrfy_inp -- * Verify that all entries in a P_IRECNO inp[] array are reasonable, * and count them. Note that P_LRECNO uses __bam_vrfy_inp; * P_IRECNOs are a special, and simpler, case, since they have * RINTERNALs rather than BKEYDATA/BINTERNALs. */static int__ram_vrfy_inp(dbp, vdp, h, pgno, nentriesp, flags) DB *dbp; VRFY_DBINFO *vdp; PAGE *h; db_pgno_t pgno; db_indx_t *nentriesp; u_int32_t flags;{ RINTERNAL *ri; VRFY_CHILDINFO child; VRFY_PAGEINFO *pip; int ret, t_ret, isbad; u_int32_t himark, i, offset, nentries; db_indx_t *inp; u_int8_t *pagelayout, *p; isbad = 0; memset(&child, 0, sizeof(VRFY_CHILDINFO)); nentries = 0; pagelayout = NULL; if ((ret = __db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0) return (ret); if (TYPE(h) != P_IRECNO) { TYPE_ERR_PRINT(dbp->dbenv, "__ram_vrfy_inp", pgno, TYPE(h)); DB_ASSERT(0); ret = EINVAL; goto err; } himark = dbp->pgsize; if ((ret = __os_malloc(dbp->dbenv, dbp->pgsize, &pagelayout)) != 0) goto err; memset(pagelayout, 0, dbp->pgsize); inp = P_INP(dbp, h); for (i = 0; i < NUM_ENT(h); i++) { if ((u_int8_t *)inp + i >= (u_int8_t *)h + himark) { EPRINT((dbp->dbenv, "Page %lu: entries listing %lu overlaps data", (u_long)pgno, (u_long)i)); ret = DB_VERIFY_BAD; goto err; } offset = inp[i]; /* * Check that the item offset is reasonable: it points * somewhere after the inp array and before the end of the * page. */ if (offset <= (u_int32_t)((u_int8_t *)inp + i - (u_int8_t *)h) || offset > (u_int32_t)(dbp->pgsize - RINTERNAL_SIZE)) { isbad = 1; EPRINT((dbp->dbenv, "Page %lu: bad offset %lu at index %lu", (u_long)pgno, (u_long)offset, (u_long)i)); continue; } /* Update the high-water mark (what HOFFSET should be) */ if (offset < himark) himark = offset; nentries++; /* Make sure this RINTERNAL is not multiply referenced. */ ri = GET_RINTERNAL(dbp, h, i); if (pagelayout[offset] == 0) { pagelayout[offset] = 1; child.pgno = ri->pgno; child.type = V_RECNO; child.nrecs = ri->nrecs; if ((ret = __db_vrfy_childput(vdp, pgno, &child)) != 0) goto err; } else { EPRINT((dbp->dbenv, "Page %lu: RINTERNAL structure at offset %lu referenced twice", (u_long)pgno, (u_long)offset)); isbad = 1; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -