📄 db_vrfy.c
字号:
/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2000-2002 * Sleepycat Software. All rights reserved. * * $Id: db_vrfy.c,v 1.107 2002/09/03 17:27:15 bostic Exp $ */#include "db_config.h"#ifndef lintstatic const char revid[] = "$Id: db_vrfy.c,v 1.107 2002/09/03 17:27:15 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_shash.h"#include "dbinc/db_swap.h"#include "dbinc/db_verify.h"#include "dbinc/btree.h"#include "dbinc/hash.h"#include "dbinc/lock.h"#include "dbinc/qam.h"#include "dbinc/txn.h"static int __db_guesspgsize __P((DB_ENV *, DB_FH *));static int __db_is_valid_magicno __P((u_int32_t, DBTYPE *));static int __db_is_valid_pagetype __P((u_int32_t));static int __db_meta2pgset __P((DB *, VRFY_DBINFO *, db_pgno_t, u_int32_t, DB *));static int __db_salvage __P((DB *, VRFY_DBINFO *, db_pgno_t, PAGE *, void *, int (*)(void *, const void *), u_int32_t));static int __db_salvage_subdbpg __P((DB *, VRFY_DBINFO *, PAGE *, void *, int (*)(void *, const void *), u_int32_t));static int __db_salvage_subdbs __P((DB *, VRFY_DBINFO *, void *, int(*)(void *, const void *), u_int32_t, int *));static int __db_salvage_unknowns __P((DB *, VRFY_DBINFO *, void *, int (*)(void *, const void *), u_int32_t));static int __db_vrfy_common __P((DB *, VRFY_DBINFO *, PAGE *, db_pgno_t, u_int32_t));static int __db_vrfy_freelist __P((DB *, VRFY_DBINFO *, db_pgno_t, u_int32_t));static int __db_vrfy_invalid __P((DB *, VRFY_DBINFO *, PAGE *, db_pgno_t, u_int32_t));static int __db_vrfy_orderchkonly __P((DB *, VRFY_DBINFO *, const char *, const char *, u_int32_t));static int __db_vrfy_pagezero __P((DB *, VRFY_DBINFO *, DB_FH *, u_int32_t));static int __db_vrfy_subdbs __P((DB *, VRFY_DBINFO *, const char *, u_int32_t));static int __db_vrfy_structure __P((DB *, VRFY_DBINFO *, const char *, db_pgno_t, u_int32_t));static int __db_vrfy_walkpages __P((DB *, VRFY_DBINFO *, void *, int (*)(void *, const void *), u_int32_t));/* * This is the code for DB->verify, the DB database consistency checker. * For now, it checks all subdatabases in a database, and verifies * everything it knows how to (i.e. it's all-or-nothing, and one can't * check only for a subset of possible problems). *//* * __db_verify -- * Walk the entire file page-by-page, either verifying with or without * dumping in db_dump -d format, or DB_SALVAGE-ing whatever key/data * pairs can be found and dumping them in standard (db_load-ready) * dump format. * * (Salvaging isn't really a verification operation, but we put it * here anyway because it requires essentially identical top-level * code.) * * flags may be 0, DB_NOORDERCHK, DB_ORDERCHKONLY, or DB_SALVAGE * (and optionally DB_AGGRESSIVE). * * __db_verify itself is simply a wrapper to __db_verify_internal, * which lets us pass appropriate equivalents to FILE * in from the * non-C APIs. * * PUBLIC: int __db_verify * PUBLIC: __P((DB *, const char *, const char *, FILE *, u_int32_t)); */int__db_verify(dbp, file, database, outfile, flags) DB *dbp; const char *file, *database; FILE *outfile; u_int32_t flags;{ return (__db_verify_internal(dbp, file, database, outfile, __db_verify_callback, flags));}/* * __db_verify_callback -- * Callback function for using pr_* functions from C. * * PUBLIC: int __db_verify_callback __P((void *, const void *)); */int__db_verify_callback(handle, str_arg) void *handle; const void *str_arg;{ char *str; FILE *f; str = (char *)str_arg; f = (FILE *)handle; if (fprintf(f, "%s", str) != (int)strlen(str)) return (EIO); return (0);}/* * __db_verify_internal -- * Inner meat of __db_verify. * * PUBLIC: int __db_verify_internal __P((DB *, const char *, * PUBLIC: const char *, void *, int (*)(void *, const void *), u_int32_t)); */int__db_verify_internal(dbp_orig, name, subdb, handle, callback, flags) DB *dbp_orig; const char *name, *subdb; void *handle; int (*callback) __P((void *, const void *)); u_int32_t flags;{ DB *dbp; DB_ENV *dbenv; DB_FH fh, *fhp; VRFY_DBINFO *vdp; int has, ret, isbad; char *real_name; dbenv = dbp_orig->dbenv; vdp = NULL; real_name = NULL; ret = isbad = 0; memset(&fh, 0, sizeof(fh)); fhp = &fh; PANIC_CHECK(dbenv); DB_ILLEGAL_AFTER_OPEN(dbp_orig, "verify");#define OKFLAGS (DB_AGGRESSIVE | DB_NOORDERCHK | DB_ORDERCHKONLY | \ DB_PRINTABLE | DB_SALVAGE) if ((ret = __db_fchk(dbenv, "DB->verify", flags, OKFLAGS)) != 0) return (ret); /* * DB_SALVAGE is mutually exclusive with the other flags except * DB_AGGRESSIVE and DB_PRINTABLE. */ if (LF_ISSET(DB_SALVAGE) && (flags & ~DB_AGGRESSIVE & ~DB_PRINTABLE) != DB_SALVAGE) return (__db_ferr(dbenv, "__db_verify", 1)); /* DB_AGGRESSIVE and DB_PRINTABLE are only meaningful when salvaging. */ if ((LF_ISSET(DB_AGGRESSIVE) || LF_ISSET(DB_PRINTABLE)) && !LF_ISSET(DB_SALVAGE)) return (__db_ferr(dbenv, "__db_verify", 1)); if (LF_ISSET(DB_ORDERCHKONLY) && flags != DB_ORDERCHKONLY) return (__db_ferr(dbenv, "__db_verify", 1)); if (LF_ISSET(DB_ORDERCHKONLY) && subdb == NULL) { __db_err(dbenv, "DB_ORDERCHKONLY requires a database name"); return (EINVAL); } /* * Forbid working in an environment that uses transactions or * locking; we're going to be looking at the file freely, * and while we're not going to modify it, we aren't obeying * locking conventions either. */ if (TXN_ON(dbenv) || LOCKING_ON(dbenv) || LOGGING_ON(dbenv)) { dbp_orig->errx(dbp_orig, "verify may not be used with transactions, logging, or locking"); return (EINVAL); /* NOTREACHED */ } /* Create a dbp to use internally, which we can close at our leisure. */ if ((ret = db_create(&dbp, dbenv, 0)) != 0) goto err; F_SET(dbp, DB_AM_VERIFYING); /* Copy the supplied pagesize, which we use if the file one is bogus. */ if (dbp_orig->pgsize >= DB_MIN_PGSIZE && dbp_orig->pgsize <= DB_MAX_PGSIZE) dbp->set_pagesize(dbp, dbp_orig->pgsize); /* Copy the feedback function, if present, and initialize it. */ if (!LF_ISSET(DB_SALVAGE) && dbp_orig->db_feedback != NULL) { dbp->set_feedback(dbp, dbp_orig->db_feedback); dbp->db_feedback(dbp, DB_VERIFY, 0); } /* * Copy the comparison and hashing functions. Note that * even if the database is not a hash or btree, the respective * internal structures will have been initialized. */ if (dbp_orig->dup_compare != NULL && (ret = dbp->set_dup_compare(dbp, dbp_orig->dup_compare)) != 0) goto err; if (((BTREE *)dbp_orig->bt_internal)->bt_compare != NULL && (ret = dbp->set_bt_compare(dbp, ((BTREE *)dbp_orig->bt_internal)->bt_compare)) != 0) goto err; if (((HASH *)dbp_orig->h_internal)->h_hash != NULL && (ret = dbp->set_h_hash(dbp, ((HASH *)dbp_orig->h_internal)->h_hash)) != 0) goto err; /* * We don't know how large the cache is, and if the database * in question uses a small page size--which we don't know * yet!--it may be uncomfortably small for the default page * size [#2143]. However, the things we need temporary * databases for in dbinfo are largely tiny, so using a * 1024-byte pagesize is probably not going to be a big hit, * and will make us fit better into small spaces. */ if ((ret = __db_vrfy_dbinfo_create(dbenv, 1024, &vdp)) != 0) goto err; /* * Note whether the user has requested that we use printable * chars where possible. We won't get here with this flag if * we're not salvaging. */ if (LF_ISSET(DB_PRINTABLE)) F_SET(vdp, SALVAGE_PRINTABLE); /* Find the real name of the file. */ if ((ret = __db_appname(dbenv, DB_APP_DATA, name, 0, NULL, &real_name)) != 0) goto err; /* * Our first order of business is to verify page 0, which is * the metadata page for the master database of subdatabases * or of the only database in the file. We want to do this by hand * rather than just calling __db_open in case it's corrupt--various * things in __db_open might act funny. * * Once we know the metadata page is healthy, I believe that it's * safe to open the database normally and then use the page swapping * code, which makes life easier. */ if ((ret = __os_open(dbenv, real_name, DB_OSO_RDONLY, 0444, fhp)) != 0) goto err; /* Verify the metadata page 0; set pagesize and type. */ if ((ret = __db_vrfy_pagezero(dbp, vdp, fhp, flags)) != 0) { if (ret == DB_VERIFY_BAD) isbad = 1; else goto err; } /* * We can assume at this point that dbp->pagesize and dbp->type are * set correctly, or at least as well as they can be, and that * locking, logging, and txns are not in use. Thus we can trust * the memp code not to look at the page, and thus to be safe * enough to use. * * The dbp is not open, but the file is open in the fhp, and we * cannot assume that __db_open is safe. Call __db_dbenv_setup, * the [safe] part of __db_open that initializes the environment-- * and the mpool--manually. */ if ((ret = __db_dbenv_setup(dbp, NULL, name, TXN_INVALID, DB_ODDFILESIZE | DB_RDONLY)) != 0) return (ret); /* Mark the dbp as opened, so that we correctly handle its close. */ F_SET(dbp, DB_AM_OPEN_CALLED); /* Find out the page number of the last page in the database. */ dbp->mpf->last_pgno(dbp->mpf, &vdp->last_pgno); /* * DB_ORDERCHKONLY is a special case; our file consists of * several subdatabases, which use different hash, bt_compare, * and/or dup_compare functions. Consequently, we couldn't verify * sorting and hashing simply by calling DB->verify() on the file. * DB_ORDERCHKONLY allows us to come back and check those things; it * requires a subdatabase, and assumes that everything but that * database's sorting/hashing is correct. */ if (LF_ISSET(DB_ORDERCHKONLY)) { ret = __db_vrfy_orderchkonly(dbp, vdp, name, subdb, flags); goto done; } /* * When salvaging, we use a db to keep track of whether we've seen a * given overflow or dup page in the course of traversing normal data. * If in the end we have not, we assume its key got lost and print it * with key "UNKNOWN". */ if (LF_ISSET(DB_SALVAGE)) { if ((ret = __db_salvage_init(vdp)) != 0) return (ret); /* * If we're not being aggressive, attempt to crack subdbs. * "has" will indicate whether the attempt has succeeded * (even in part), meaning that we have some semblance of * subdbs; on the walkpages pass, we print out * whichever data pages we have not seen. */ has = 0; if (!LF_ISSET(DB_AGGRESSIVE) && (__db_salvage_subdbs(dbp, vdp, handle, callback, flags, &has)) != 0) isbad = 1; /* * If we have subdatabases, we need to signal that if * any keys are found that don't belong to a subdatabase, * they'll need to have an "__OTHER__" subdatabase header * printed first. Flag this. Else, print a header for * the normal, non-subdb database. */ if (has == 1) F_SET(vdp, SALVAGE_PRINTHEADER); else if ((ret = __db_prheader(dbp, NULL, 0, 0, handle, callback, vdp, PGNO_BASE_MD)) != 0) goto err; } if ((ret = __db_vrfy_walkpages(dbp, vdp, handle, callback, flags)) != 0) { if (ret == DB_VERIFY_BAD) isbad = 1; else if (ret != 0) goto err; } /* If we're verifying, verify inter-page structure. */ if (!LF_ISSET(DB_SALVAGE) && isbad == 0) if ((ret = __db_vrfy_structure(dbp, vdp, name, 0, flags)) != 0) { if (ret == DB_VERIFY_BAD) isbad = 1; else if (ret != 0) goto err; } /* * If we're salvaging, output with key UNKNOWN any overflow or dup pages * we haven't been able to put in context. Then destroy the salvager's * state-saving database. */ if (LF_ISSET(DB_SALVAGE)) { if ((ret = __db_salvage_unknowns(dbp, vdp, handle, callback, flags)) != 0) isbad = 1; /* No return value, since there's little we can do. */ __db_salvage_destroy(vdp); } if (0) { /* Don't try to strerror() DB_VERIFY_FATAL; it's private. */err: if (ret == DB_VERIFY_FATAL) ret = DB_VERIFY_BAD; (void)__db_err(dbenv, "%s: %s", name, db_strerror(ret)); } if (LF_ISSET(DB_SALVAGE) && (has == 0 || F_ISSET(vdp, SALVAGE_PRINTFOOTER))) (void)__db_prfooter(handle, callback); /* Send feedback that we're done. */done: if (!LF_ISSET(DB_SALVAGE) && dbp->db_feedback != NULL) dbp->db_feedback(dbp, DB_VERIFY, 100); if (F_ISSET(fhp, DB_FH_VALID)) (void)__os_closehandle(dbenv, fhp); if (dbp) (void)dbp->close(dbp, 0); if (vdp) (void)__db_vrfy_dbinfo_destroy(dbenv, vdp); if (real_name) __os_free(dbenv, real_name); if ((ret == 0 && isbad == 1) || ret == DB_VERIFY_FATAL) ret = DB_VERIFY_BAD; return (ret);}/* * __db_vrfy_pagezero -- * Verify the master metadata page. Use seek, read, and a local buffer * rather than the DB paging code, for safety. * * Must correctly (or best-guess) set dbp->type and dbp->pagesize. */static int__db_vrfy_pagezero(dbp, vdp, fhp, flags) DB *dbp; VRFY_DBINFO *vdp; DB_FH *fhp; u_int32_t flags;{ DBMETA *meta; DB_ENV *dbenv; VRFY_PAGEINFO *pip; db_pgno_t freelist; size_t nr; int isbad, ret, swapped; u_int8_t mbuf[DBMETASIZE]; isbad = ret = swapped = 0; freelist = 0; dbenv = dbp->dbenv; meta = (DBMETA *)mbuf; dbp->type = DB_UNKNOWN; /* * Seek to the metadata page. * Note that if we're just starting a verification, dbp->pgsize * may be zero; this is okay, as we want page zero anyway and * 0*0 == 0. */ if ((ret = __os_seek(dbenv, fhp, 0, 0, 0, 0, DB_OS_SEEK_SET)) != 0 || (ret = __os_read(dbenv, fhp, mbuf, DBMETASIZE, &nr)) != 0) { __db_err(dbenv, "Metadata page %lu cannot be read: %s", (u_long)PGNO_BASE_MD, db_strerror(ret)); return (ret); } if (nr != DBMETASIZE) { EPRINT((dbenv, "Page %lu: Incomplete metadata page", (u_long)PGNO_BASE_MD)); return (DB_VERIFY_FATAL); } if ((ret = __db_chk_meta(dbenv, dbp, meta, 1)) != 0) { EPRINT((dbenv, "Page %lu: metadata page corrupted, (u_long)PGNO_BASE_MD")); isbad = 1; if (ret != -1) { EPRINT((dbenv, "Page %lu: could not check metadata page", (u_long)PGNO_BASE_MD)); return (DB_VERIFY_FATAL); } } /* * Check all of the fields that we can. * * 08-11: Current page number. Must == pgno. * Note that endianness doesn't matter--it's zero. */ if (meta->pgno != PGNO_BASE_MD) { isbad = 1; EPRINT((dbenv, "Page %lu: pgno incorrectly set to %lu", (u_long)PGNO_BASE_MD, (u_long)meta->pgno)); } /* 12-15: Magic number. Must be one of valid set. */ if (__db_is_valid_magicno(meta->magic, &dbp->type)) swapped = 0; else { M_32_SWAP(meta->magic); if (__db_is_valid_magicno(meta->magic, &dbp->type)) swapped = 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -