📄 db_vrfy.c
字号:
switch (pip->type) { case P_IBTREE: case P_LDUP: if (!LF_ISSET(ST_DUPSORT)) { EPRINT((dbenv, "Page %lu: sorted duplicate set in unsorted-dup database", (u_long)pgno)); isbad = 1; } break; case P_IRECNO: case P_LRECNO: if (LF_ISSET(ST_DUPSORT)) { EPRINT((dbenv, "Page %lu: unsorted duplicate set in sorted-dup database", (u_long)pgno)); isbad = 1; } break; default: /* * 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(dbenv, pgno, "duplicate page"); else EPRINT((dbenv, "Page %lu: duplicate page of inappropriate type %lu", (u_long)pgno, (u_long)pip->type)); isbad = 1; break; } if ((ret = __db_vrfy_putpageinfo(dbenv, vdp, pip)) != 0) return (ret); return (isbad == 1 ? DB_VERIFY_BAD : 0);}/* * __db_salvage_duptree -- * Attempt to salvage a given duplicate tree, given its alleged root. * * The key that corresponds to this dup set has been passed to us * in DBT *key. Because data items follow keys, though, it has been * printed once already. * * The basic idea here is that pgno ought to be a P_LDUP, a P_LRECNO, a * P_IBTREE, or a P_IRECNO. If it's an internal page, use the verifier * functions to make sure it's safe; if it's not, we simply bail and the * data will have to be printed with no key later on. if it is safe, * recurse on each of its children. * * Whether or not it's safe, if it's a leaf page, __bam_salvage it. * * At all times, use the DB hanging off vdp to mark and check what we've * done, so each page gets printed exactly once and we don't get caught * in any cycles. * * PUBLIC: int __db_salvage_duptree __P((DB *, VRFY_DBINFO *, db_pgno_t, * PUBLIC: DBT *, void *, int (*)(void *, const void *), u_int32_t)); */int__db_salvage_duptree(dbp, vdp, pgno, key, handle, callback, flags) DB *dbp; VRFY_DBINFO *vdp; db_pgno_t pgno; DBT *key; void *handle; int (*callback) __P((void *, const void *)); u_int32_t flags;{ DB_MPOOLFILE *mpf; PAGE *h; int ret, t_ret; mpf = dbp->mpf; if (pgno == PGNO_INVALID || !IS_VALID_PGNO(pgno)) return (DB_VERIFY_BAD); /* We have a plausible page. Try it. */ if ((ret = mpf->get(mpf, &pgno, 0, &h)) != 0) return (ret); switch (TYPE(h)) { case P_IBTREE: case P_IRECNO: if ((ret = __db_vrfy_common(dbp, vdp, h, pgno, flags)) != 0) goto err; if ((ret = __bam_vrfy(dbp, vdp, h, pgno, flags | DB_NOORDERCHK)) != 0 || (ret = __db_salvage_markdone(vdp, pgno)) != 0) goto err; /* * We have a known-healthy internal page. Walk it. */ if ((ret = __bam_salvage_walkdupint(dbp, vdp, h, key, handle, callback, flags)) != 0) goto err; break; case P_LRECNO: case P_LDUP: if ((ret = __bam_salvage(dbp, vdp, pgno, TYPE(h), h, handle, callback, key, flags)) != 0) goto err; break; default: ret = DB_VERIFY_BAD; goto err; /* NOTREACHED */ }err: if ((t_ret = mpf->put(mpf, h, 0)) != 0 && ret == 0) ret = t_ret; return (ret);}/* * __db_salvage_subdbs -- * Check and see if this database has subdbs; if so, try to salvage * them independently. */static int__db_salvage_subdbs(dbp, vdp, handle, callback, flags, hassubsp) DB *dbp; VRFY_DBINFO *vdp; void *handle; int (*callback) __P((void *, const void *)); u_int32_t flags; int *hassubsp;{ BTMETA *btmeta; DB *pgset; DBC *pgsc; DB_MPOOLFILE *mpf; PAGE *h; db_pgno_t p, meta_pgno; int ret, err_ret; pgset = NULL; pgsc = NULL; mpf = dbp->mpf; err_ret = 0; meta_pgno = PGNO_BASE_MD; if ((ret = mpf->get(mpf, &meta_pgno, 0, &h)) != 0) return (ret); if (TYPE(h) == P_BTREEMETA) btmeta = (BTMETA *)h; else { /* Not a btree metadata, ergo no subdbs, so just return. */ ret = 0; goto err; } /* If it's not a safe page, bail on the attempt. */ if ((ret = __db_vrfy_common(dbp, vdp, h, PGNO_BASE_MD, flags)) != 0 || (ret = __bam_vrfy_meta(dbp, vdp, btmeta, PGNO_BASE_MD, flags)) != 0) goto err; if (!F_ISSET(&btmeta->dbmeta, BTM_SUBDB)) { /* No subdbs, just return. */ ret = 0; goto err; } /* We think we've got subdbs. Mark it so. */ *hassubsp = 1; if ((ret = mpf->put(mpf, h, 0)) != 0) return (ret); /* * We have subdbs. Try to crack them. * * To do so, get a set of leaf pages in the master * database, and then walk each of the valid ones, salvaging * subdbs as we go. If any prove invalid, just drop them; we'll * pick them up on a later pass. */ if ((ret = __db_vrfy_pgset(dbp->dbenv, dbp->pgsize, &pgset)) != 0) return (ret); if ((ret = __db_meta2pgset(dbp, vdp, PGNO_BASE_MD, flags, pgset)) != 0) goto err; if ((ret = pgset->cursor(pgset, NULL, &pgsc, 0)) != 0) goto err; while ((ret = __db_vrfy_pgset_next(pgsc, &p)) == 0) { if ((ret = mpf->get(mpf, &p, 0, &h)) != 0) { err_ret = ret; continue; } if ((ret = __db_vrfy_common(dbp, vdp, h, p, flags)) != 0 || (ret = __bam_vrfy(dbp, vdp, h, p, flags | DB_NOORDERCHK)) != 0) goto nextpg; if (TYPE(h) != P_LBTREE) goto nextpg; else if ((ret = __db_salvage_subdbpg( dbp, vdp, h, handle, callback, flags)) != 0) err_ret = ret;nextpg: if ((ret = mpf->put(mpf, h, 0)) != 0) err_ret = ret; } if (ret != DB_NOTFOUND) goto err; if ((ret = pgsc->c_close(pgsc)) != 0) goto err; ret = pgset->close(pgset, 0); return ((ret == 0 && err_ret != 0) ? err_ret : ret); /* NOTREACHED */err: if (pgsc != NULL) (void)pgsc->c_close(pgsc); if (pgset != NULL) (void)pgset->close(pgset, 0); (void)mpf->put(mpf, h, 0); return (ret);}/* * __db_salvage_subdbpg -- * Given a known-good leaf page in the master database, salvage all * leaf pages corresponding to each subdb. */static int__db_salvage_subdbpg(dbp, vdp, master, handle, callback, flags) DB *dbp; VRFY_DBINFO *vdp; PAGE *master; void *handle; int (*callback) __P((void *, const void *)); u_int32_t flags;{ BKEYDATA *bkkey, *bkdata; BOVERFLOW *bo; DB *pgset; DBC *pgsc; DBT key; DB_ENV *dbenv; DB_MPOOLFILE *mpf; PAGE *subpg; db_indx_t i; db_pgno_t meta_pgno, p; int ret, err_ret, t_ret; char *subdbname; dbenv = dbp->dbenv; mpf = dbp->mpf; ret = err_ret = 0; subdbname = NULL; if ((ret = __db_vrfy_pgset(dbenv, dbp->pgsize, &pgset)) != 0) return (ret); /* * For each entry, get and salvage the set of pages * corresponding to that entry. */ for (i = 0; i < NUM_ENT(master); i += P_INDX) { bkkey = GET_BKEYDATA(dbp, master, i); bkdata = GET_BKEYDATA(dbp, master, i + O_INDX); /* Get the subdatabase name. */ if (B_TYPE(bkkey->type) == B_OVERFLOW) { /* * We can, in principle anyway, have a subdb * name so long it overflows. Ick. */ bo = (BOVERFLOW *)bkkey; if ((ret = __db_safe_goff(dbp, vdp, bo->pgno, &key, (void **)&subdbname, flags)) != 0) { err_ret = DB_VERIFY_BAD; continue; } /* Nul-terminate it. */ if ((ret = __os_realloc(dbenv, key.size + 1, &subdbname)) != 0) goto err; subdbname[key.size] = '\0'; } else if (B_TYPE(bkkey->type == B_KEYDATA)) { if ((ret = __os_realloc(dbenv, bkkey->len + 1, &subdbname)) != 0) goto err; memcpy(subdbname, bkkey->data, bkkey->len); subdbname[bkkey->len] = '\0'; } /* Get the corresponding pgno. */ if (bkdata->len != sizeof(db_pgno_t)) { err_ret = DB_VERIFY_BAD; continue; } memcpy(&meta_pgno, bkdata->data, sizeof(db_pgno_t)); /* * Subdatabase meta pgnos are stored in network byte * order for cross-endian compatibility. Swap if appropriate. */ DB_NTOHL(&meta_pgno); /* If we can't get the subdb meta page, just skip the subdb. */ if (!IS_VALID_PGNO(meta_pgno) || (ret = mpf->get(mpf, &meta_pgno, 0, &subpg)) != 0) { err_ret = ret; continue; } /* * Verify the subdatabase meta page. This has two functions. * First, if it's bad, we have no choice but to skip the subdb * and let the pages just get printed on a later pass. Second, * the access-method-specific meta verification routines record * the various state info (such as the presence of dups) * that we need for __db_prheader(). */ if ((ret = __db_vrfy_common(dbp, vdp, subpg, meta_pgno, flags)) != 0) { err_ret = ret; (void)mpf->put(mpf, subpg, 0); continue; } switch (TYPE(subpg)) { case P_BTREEMETA: if ((ret = __bam_vrfy_meta(dbp, vdp, (BTMETA *)subpg, meta_pgno, flags)) != 0) { err_ret = ret; (void)mpf->put(mpf, subpg, 0); continue; } break; case P_HASHMETA: if ((ret = __ham_vrfy_meta(dbp, vdp, (HMETA *)subpg, meta_pgno, flags)) != 0) { err_ret = ret; (void)mpf->put(mpf, subpg, 0); continue; } break; default: /* This isn't an appropriate page; skip this subdb. */ err_ret = DB_VERIFY_BAD; continue; /* NOTREACHED */ } if ((ret = mpf->put(mpf, subpg, 0)) != 0) { err_ret = ret; continue; } /* Print a subdatabase header. */ if ((ret = __db_prheader(dbp, subdbname, 0, 0, handle, callback, vdp, meta_pgno)) != 0) goto err; if ((ret = __db_meta2pgset(dbp, vdp, meta_pgno, flags, pgset)) != 0) { err_ret = ret; continue; } if ((ret = pgset->cursor(pgset, NULL, &pgsc, 0)) != 0) goto err; while ((ret = __db_vrfy_pgset_next(pgsc, &p)) == 0) { if ((ret = mpf->get(mpf, &p, 0, &subpg)) != 0) { err_ret = ret; continue; } if ((ret = __db_salvage(dbp, vdp, p, subpg, handle, callback, flags)) != 0) err_ret = ret; if ((ret = mpf->put(mpf, subpg, 0)) != 0) err_ret = ret; } if (ret != DB_NOTFOUND) goto err; if ((ret = pgsc->c_close(pgsc)) != 0) goto err; if ((ret = __db_prfooter(handle, callback)) != 0) goto err; }err: if (subdbname) __os_free(dbenv, subdbname); if ((t_ret = pgset->close(pgset, 0)) != 0) ret = t_ret; if ((t_ret = __db_salvage_markdone(vdp, PGNO(master))) != 0) return (t_ret); return ((err_ret != 0) ? err_ret : ret);}/* * __db_meta2pgset -- * Given a known-safe meta page number, return the set of pages * corresponding to the database it represents. Return DB_VERIFY_BAD if * it's not a suitable meta page or is invalid. */static int__db_meta2pgset(dbp, vdp, pgno, flags, pgset) DB *dbp; VRFY_DBINFO *vdp; db_pgno_t pgno; u_int32_t flags; DB *pgset;{ DB_MPOOLFILE *mpf; PAGE *h; int ret, t_ret; mpf = dbp->mpf; if ((ret = mpf->get(mpf, &pgno, 0, &h)) != 0) return (ret); switch (TYPE(h)) { case P_BTREEMETA: ret = __bam_meta2pgset(dbp, vdp, (BTMETA *)h, flags, pgset); break; case P_HASHMETA: ret = __ham_meta2pgset(dbp, vdp, (HMETA *)h, flags, pgset); break; default: ret = DB_VERIFY_BAD; break; } if ((t_ret = mpf->put(mpf, h, 0)) != 0) return (t_ret); return (ret);}/* * __db_guesspgsize -- * Try to guess what the pagesize is if the one on the meta page * and the one in the db are invalid. */static int__db_guesspgsize(dbenv, fhp) DB_ENV *dbenv; DB_FH *fhp;{ db_pgno_t i; size_t nr; u_int32_t guess; u_int8_t type; for (guess = DB_MAX_PGSIZE; guess >= DB_MIN_PGSIZE; guess >>= 1) { /* * We try to read three pages ahead after the first one * and make sure we have plausible types for all of them. * If the seeks fail, continue with a smaller size; * we're probably just looking past the end of the database. * If they succeed and the types are reasonable, also continue * with a size smaller; we may be looking at pages N, * 2N, and 3N for some N > 1. * * As soon as we hit an invalid type, we stop and return * our previous guess; that last one was probably the page size. */ for (i = 1; i <= 3; i++) { if (__os_seek(dbenv, fhp, guess, i, SSZ(DBMETA, type), 0, DB_OS_SEEK_SET) != 0) break; if (__os_read(dbenv, fhp, &type, 1, &nr) != 0 || nr == 0) break; if (type == P_INVALID || type >= P_PAGETYPE_MAX) return (guess << 1); } } /* * If we're just totally confused--the corruption takes up most of the * beginning pages of the database--go with the default size. */ return (DB_DEF_IOSIZE);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -