db

hash_verify.c

Go to the documentation of this file.
00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 1999, 2000
00005  * Sleepycat Software. All rights reserved.
00006  *
00007  * $Id: hash__verify_8c-source.html,v 1.1 2008年06月08日 10:19:40 sebdiaz Exp $
00008  */
00009 
00010 #include "config.h"
00011 
00012 #ifndef lint
00013 static const char revid[] = "$Id: hash__verify_8c-source.html,v 1.1 2008年06月08日 10:19:40 sebdiaz Exp $";
00014 #endif /* not lint */
00015 
00016 #ifndef NO_SYSTEM_INCLUDES
00017 #include <sys/types.h>
00018 
00019 #include <errno.h>
00020 #include <string.h>
00021 #endif
00022 
00023 #include "db_int.h"
00024 #include "db_page.h"
00025 #include "db_verify.h"
00026 #include "btree.h"
00027 #include "hash.h"
00028 
00029 static int __ham_dups_unsorted __P((DB *, u_int8_t *, u_int32_t));
00030 static int __ham_vrfy_bucket __P((DB *, VRFY_DBINFO *, HMETA *, u_int32_t,
00031 u_int32_t));
00032 static int __ham_vrfy_item __P((DB *,
00033 VRFY_DBINFO *, db_pgno_t, PAGE *, u_int32_t, u_int32_t));
00034 
00035 /*
00036  * CDB___ham_vrfy_meta --
00037  * Verify the hash-specific part of a metadata page.
00038  *
00039  * Note that unlike btree, we don't save things off, because we
00040  * will need most everything again to verify each page and the
00041  * amount of state here is significant.
00042  *
00043  * PUBLIC: int CDB___ham_vrfy_meta __P((DB *, VRFY_DBINFO *, HMETA *,
00044  * PUBLIC: db_pgno_t, u_int32_t));
00045  */
00046 int
00047 CDB___ham_vrfy_meta(dbp, vdp, m, pgno, flags)
00048 DB *dbp;
00049 VRFY_DBINFO *vdp;
00050 HMETA *m;
00051 db_pgno_t pgno;
00052 u_int32_t flags;
00053 {
00054 HASH *hashp;
00055 VRFY_PAGEINFO *pip;
00056 int i, ret, t_ret, isbad;
00057 u_int32_t pwr, mbucket;
00058 u_int32_t (*hfunc) __P((const void *, u_int32_t));
00059 
00060 if ((ret = CDB___db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0)
00061 return (ret);
00062 isbad = 0;
00063 
00064 hashp = dbp->h_internal;
00065 
00066 if (hashp != NULL && hashp->h_hash != NULL)
00067 hfunc = hashp->h_hash;
00068 else
00069 hfunc = CDB___ham_func5;
00070 
00071 /*
00072  * If we haven't already checked the common fields in pagezero,
00073  * check them.
00074  */
00075 if (!F_ISSET(pip, VRFY_INCOMPLETE) &&
00076 (ret = CDB___db_vrfy_meta(dbp, vdp, &m->dbmeta, pgno, flags)) != 0) {
00077 if (ret == DB_VERIFY_BAD)
00078 isbad = 1;
00079 else
00080 goto err;
00081 }
00082 
00083 /* h_charkey */
00084 if (!LF_ISSET(DB_NOORDERCHK))
00085 if (m->h_charkey != hfunc(CHARKEY, sizeof(CHARKEY))) {
00086 EPRINT((dbp->dbenv,
00087 "Database has different custom hash function; reverify with DB_NOORDERCHK set"
00088 ));
00089 /*
00090  * Return immediately; this is probably a sign
00091  * of user error rather than database corruption, so
00092  * we want to avoid extraneous errors.
00093  */
00094 isbad = 1;
00095 goto err;
00096 }
00097 
00098 /* max_bucket must be less than the last pgno. */
00099 if (m->max_bucket > vdp->last_pgno) {
00100 EPRINT((dbp->dbenv,
00101 "Impossible max_bucket %lu on meta page %lu",
00102 m->max_bucket, pgno));
00103 /*
00104  * Most other fields depend somehow on max_bucket, so
00105  * we just return--there will be lots of extraneous
00106  * errors.
00107  */
00108 isbad = 1;
00109 goto err;
00110 }
00111 
00112 /*
00113  * max_bucket, high_mask and low_mask: high_mask must be one
00114  * less than the next power of two above max_bucket, and
00115  * low_mask must be one less than the power of two below it.
00116  *
00117  *
00118  */
00119 pwr = (m->max_bucket == 0) ? 1 : 1 << CDB___db_log2(m->max_bucket + 1);
00120 if (m->high_mask != pwr - 1) {
00121 EPRINT((dbp->dbenv,
00122 "Incorrect high_mask %lu on page %lu, should be %lu",
00123 m->high_mask, pgno, pwr - 1));
00124 isbad = 1;
00125 }
00126 pwr >>= 1;
00127 if (m->low_mask != pwr - 1) {
00128 EPRINT((dbp->dbenv,
00129 "Incorrect low_mask %lu on page %lu, should be %lu",
00130 m->low_mask, pgno, pwr - 1));
00131 isbad = 1;
00132 }
00133 
00134 /* ffactor: no check possible. */
00135 pip->h_ffactor = m->ffactor;
00136 
00137 /*
00138  * nelem: just make sure it's not astronomical for now. This is the
00139  * same check that hash_upgrade does, since there was a bug in 2.X
00140  * which could make nelem go "negative".
00141  */
00142 if (m->nelem > 0x80000000) {
00143 EPRINT((dbp->dbenv,
00144 "Suspiciously high nelem of %lu on page %lu",
00145 m->nelem, pgno));
00146 isbad = 1;
00147 pip->h_nelem = 0;
00148 } else
00149 pip->h_nelem = m->nelem;
00150 
00151 /* flags */
00152 if (F_ISSET(&m->dbmeta, DB_HASH_DUP))
00153 F_SET(pip, VRFY_HAS_DUPS);
00154 if (F_ISSET(&m->dbmeta, DB_HASH_DUPSORT))
00155 F_SET(pip, VRFY_HAS_DUPSORT);
00156 /* XXX: Why is the DB_HASH_SUBDB flag necessary? */
00157 
00158 /* spares array */
00159 for (i = 0; m->spares[i] != 0 && i < NCACHED; i++) {
00160 /*
00161  * We set mbucket to the maximum bucket that would use a given
00162  * spares entry; we want to ensure that it's always less
00163  * than last_pgno.
00164  */
00165 mbucket = (1 << i) - 1;
00166 if (mbucket + m->spares[CDB___db_log2(mbucket + 1)] >
00167 vdp->last_pgno) {
00168 EPRINT((dbp->dbenv,
00169 "Spares array entry %lu, page %lu is invalid",
00170 i, pgno));
00171 isbad = 1;
00172 }
00173 }
00174 
00175 err: if ((t_ret = CDB___db_vrfy_putpageinfo(vdp, pip)) != 0 && ret == 0)
00176 ret = t_ret;
00177 return ((ret == 0 && isbad == 1) ? DB_VERIFY_BAD : ret);
00178 }
00179 
00180 /*
00181  * CDB___ham_vrfy --
00182  * Verify hash page.
00183  *
00184  * PUBLIC: int CDB___ham_vrfy __P((DB *, VRFY_DBINFO *, PAGE *, db_pgno_t,
00185  * PUBLIC: u_int32_t));
00186  */
00187 int
00188 CDB___ham_vrfy(dbp, vdp, h, pgno, flags)
00189 DB *dbp;
00190 VRFY_DBINFO *vdp;
00191 PAGE *h;
00192 db_pgno_t pgno;
00193 u_int32_t flags;
00194 {
00195 VRFY_PAGEINFO *pip;
00196 db_indx_t ent, himark, inpend;
00197 int isbad, ret, t_ret;
00198 
00199 isbad = 0;
00200 if ((ret = CDB___db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0)
00201 return (ret);
00202 
00203 /* Sanity check our flags and page type. */
00204 if ((ret = CDB___db_fchk(dbp->dbenv, "CDB___ham_vrfy",
00205 flags, DB_AGGRESSIVE | DB_NOORDERCHK | DB_SALVAGE)) != 0)
00206 goto err;
00207 
00208 if (TYPE(h) != P_HASH) {
00209 TYPE_ERR_PRINT(dbp->dbenv, "CDB___ham_vrfy", pgno, TYPE(h));
00210 DB_ASSERT(0);
00211 ret = EINVAL;
00212 goto err;
00213 }
00214 
00215 /* Verify and save off fields common to all PAGEs. */
00216 if ((ret = CDB___db_vrfy_datapage(dbp, vdp, h, pgno, flags)) != 0) {
00217 if (ret == DB_VERIFY_BAD)
00218 isbad = 1;
00219 else
00220 goto err;
00221 }
00222 
00223 /*
00224  * Verify inp[]. Each offset from 0 to NUM_ENT(h) must be lower
00225  * than the previous one, higher than the current end of the inp array,
00226  * and lower than the page size.
00227  *
00228  * In any case, we return immediately if things are bad, as it would
00229  * be unsafe to proceed.
00230  */
00231 for (ent = 0, himark = dbp->pgsize,
00232 inpend = (u_int8_t *)h->inp - (u_int8_t *)h;
00233 ent < NUM_ENT(h); ent++)
00234 if (h->inp[ent] >= himark) {
00235 EPRINT((dbp->dbenv,
00236 "Item %lu on page %lu out of order or nonsensical",
00237 ent, pgno));
00238 isbad = 1;
00239 goto err;
00240 } else if (inpend >= himark) {
00241 EPRINT((dbp->dbenv,
00242 "inp array collided with data on page %lu",
00243 pgno));
00244 isbad = 1;
00245 goto err;
00246 
00247 } else {
00248 himark = h->inp[ent];
00249 inpend += sizeof(db_indx_t);
00250 if ((ret = __ham_vrfy_item(
00251 dbp, vdp, pgno, h, ent, flags)) != 0)
00252 goto err;
00253 }
00254 
00255 err: if ((t_ret = CDB___db_vrfy_putpageinfo(vdp, pip)) != 0 && ret == 0)
00256 ret = t_ret;
00257 return (ret == 0 && isbad == 1 ? DB_VERIFY_BAD : ret);
00258 }
00259 
00260 /*
00261  * __ham_vrfy_item --
00262  * Given a hash page and an offset, sanity-check the item itself,
00263  * and save off any overflow items or off-page dup children as necessary.
00264  */
00265 static int
00266 __ham_vrfy_item(dbp, vdp, pgno, h, i, flags)
00267 DB *dbp;
00268 VRFY_DBINFO *vdp;
00269 db_pgno_t pgno;
00270 PAGE *h;
00271 u_int32_t i, flags;
00272 {
00273 HOFFPAGE hop;
00274 HOFFDUP hod;
00275 VRFY_CHILDINFO child;
00276 VRFY_PAGEINFO *pip;
00277 db_indx_t offset, len, dlen, elen;
00278 int ret, t_ret;
00279 u_int8_t *databuf;
00280 
00281 if ((ret = CDB___db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0)
00282 return (ret);
00283 
00284 switch (HPAGE_TYPE(h, i)) {
00285 case H_KEYDATA:
00286 /* Nothing to do here--everything but the type field is data */
00287 break;
00288 case H_DUPLICATE:
00289 /* Are we a datum or a key? Better be the former. */
00290 if (i % 2 == 0) {
00291 EPRINT((dbp->dbenv,
00292 "Hash key stored as duplicate at page %lu item %lu",
00293 pip->pgno, i));
00294 }
00295 /*
00296  * Dups are encoded as a series within a single HKEYDATA,
00297  * in which each dup is surrounded by a copy of its length
00298  * on either side (so that the series can be walked in either
00299  * direction. We loop through this series and make sure
00300  * each dup is reasonable.
00301  *
00302  * Note that at this point, we've verified item i-1, so
00303  * it's safe to use LEN_HKEYDATA (which looks at inp[i-1]).
00304  */
00305 len = LEN_HKEYDATA(h, dbp->pgsize, i);
00306 databuf = HKEYDATA_DATA(P_ENTRY(h, i));
00307 for (offset = 0; offset < len; offset += DUP_SIZE(dlen)) {
00308 memcpy(&dlen, databuf + offset, sizeof(db_indx_t));
00309 
00310 /* Make sure the length is plausible. */
00311 if (offset + DUP_SIZE(dlen) > len) {
00312 EPRINT((dbp->dbenv,
00313 "Duplicate item %lu, page %lu has bad length",
00314 i, pip->pgno));
00315 ret = DB_VERIFY_BAD;
00316 goto err;
00317 }
00318 
00319 /*
00320  * Make sure the second copy of the length is the
00321  * same as the first.
00322  */
00323 memcpy(&elen,
00324 databuf + offset + dlen + sizeof(db_indx_t),
00325 sizeof(db_indx_t));
00326 if (elen != dlen) {
00327 EPRINT((dbp->dbenv,
00328 "Duplicate item %lu, page %lu has two different lengths",
00329 i, pip->pgno));
00330 ret = DB_VERIFY_BAD;
00331 goto err;
00332 }
00333 }
00334 F_SET(pip, VRFY_HAS_DUPS);
00335 if (!LF_ISSET(DB_NOORDERCHK) &&
00336 __ham_dups_unsorted(dbp, databuf, len))
00337 F_SET(pip, VRFY_DUPS_UNSORTED);
00338 break;
00339 case H_OFFPAGE:
00340 /* Offpage item. Make sure pgno is sane, save off. */
00341 memcpy(&hop, P_ENTRY(h, i), HOFFPAGE_SIZE);
00342 if (!IS_VALID_PGNO(hop.pgno) || hop.pgno == pip->pgno ||
00343 hop.pgno == PGNO_INVALID) {
00344 EPRINT((dbp->dbenv,
00345 "Offpage item %lu, page %lu has bad page number",
00346 i, pip->pgno));
00347 ret = DB_VERIFY_BAD;
00348 goto err;
00349 }
00350 memset(&child, 0, sizeof(VRFY_CHILDINFO));
00351 child.pgno = hop.pgno;
00352 child.type = V_OVERFLOW;
00353 child.tlen = hop.tlen; /* This will get checked later. */
00354 if ((ret = CDB___db_vrfy_childput(vdp, pip->pgno, &child)) != 0)
00355 goto err;
00356 break;
00357 case H_OFFDUP:
00358 /* Offpage duplicate item. Same drill. */
00359 memcpy(&hod, P_ENTRY(h, i), HOFFDUP_SIZE);
00360 if (!IS_VALID_PGNO(hod.pgno) || hod.pgno == pip->pgno ||
00361 hod.pgno == PGNO_INVALID) {
00362 EPRINT((dbp->dbenv,
00363 "Offpage item %lu, page %lu has bad page number",
00364 i, pip->pgno));
00365 ret = DB_VERIFY_BAD;
00366 goto err;
00367 }
00368 memset(&child, 0, sizeof(VRFY_CHILDINFO));
00369 child.pgno = hod.pgno;
00370 child.type = V_DUPLICATE;
00371 if ((ret = CDB___db_vrfy_childput(vdp, pip->pgno, &child)) != 0)
00372 goto err;
00373 F_SET(pip, VRFY_HAS_DUPS);
00374 break;
00375 default:
00376 EPRINT((dbp->dbenv,
00377 "Item %i, page %lu has bad type", i, pip->pgno));
00378 ret = DB_VERIFY_BAD;
00379 break;
00380 }
00381 
00382 err: if ((t_ret = CDB___db_vrfy_putpageinfo(vdp, pip)) != 0 && ret == 0)
00383 ret = t_ret;
00384 return (ret);
00385 }
00386 
00387 /*
00388  * CDB___ham_vrfy_structure --
00389  * Verify the structure of a hash database.
00390  *
00391  * PUBLIC: int CDB___ham_vrfy_structure __P((DB *, VRFY_DBINFO *, db_pgno_t,
00392  * PUBLIC: u_int32_t));
00393  */
00394 int
00395 CDB___ham_vrfy_structure(dbp, vdp, meta_pgno, flags)
00396 DB *dbp;
00397 VRFY_DBINFO *vdp;
00398 db_pgno_t meta_pgno;
00399 u_int32_t flags;
00400 {
00401 DB *pgset;
00402 HMETA *m;
00403 PAGE *h;
00404 VRFY_PAGEINFO *pip;
00405 int isbad, p, ret, t_ret;
00406 db_pgno_t pgno;
00407 u_int32_t bucket;
00408 
00409 ret = isbad = 0;
00410 h = NULL;
00411 pgset = vdp->pgset;
00412 
00413 if ((ret = CDB___db_vrfy_pgset_get(pgset, meta_pgno, &p)) != 0)
00414 return (ret);
00415 if (p != 0) {
00416 EPRINT((dbp->dbenv,
00417 "Hash meta page %lu referenced twice", meta_pgno));
00418 return (DB_VERIFY_BAD);
00419 }
00420 if ((ret = CDB___db_vrfy_pgset_inc(pgset, meta_pgno)) != 0)
00421 return (ret);
00422 
00423 /* Get the meta page; we'll need it frequently. */
00424 if ((ret = CDB_memp_fget(dbp->mpf, &meta_pgno, 0, &m)) != 0)
00425 return (ret);
00426 
00427 /* Loop through bucket by bucket. */
00428 for (bucket = 0; bucket <= m->max_bucket; bucket++)
00429 if ((ret =
00430 __ham_vrfy_bucket(dbp, vdp, m, bucket, flags)) != 0) {
00431 if (ret == DB_VERIFY_BAD)
00432 isbad = 1;
00433 else
00434 goto err;
00435 }
00436 
00437 /*
00438  * There may be unused hash pages corresponding to buckets
00439  * that are part of the current doubling but which are higher
00440  * than max_bucket. Loop through them, too, and make sure they're
00441  * all empty.
00442  */
00443 for (bucket = m->max_bucket + 1; bucket <= m->high_mask; bucket++) {
00444 pgno = bucket + m->spares[CDB___db_log2(bucket + 1)];
00445 if ((ret = CDB___db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0)
00446 goto err;
00447 
00448 /* It's okay if these pages are totally zeroed; unmark it. */
00449 F_CLR(pip, VRFY_IS_ALLZEROES);
00450 
00451 if (pip->type != P_HASH) {
00452 EPRINT((dbp->dbenv,
00453 "Hash bucket %lu maps to non-hash page %lu",
00454 bucket, pgno));
00455 isbad = 1;
00456 } else if (pip->entries != 0) {
00457 EPRINT((dbp->dbenv,
00458 "Non-empty page %lu in unused hash bucket %lu",
00459 pgno, bucket));
00460 isbad = 1;
00461 } else {
00462 if ((ret = CDB___db_vrfy_pgset_get(pgset, pgno, &p)) != 0)
00463 goto err;
00464 if (p != 0) {
00465 EPRINT((dbp->dbenv,
00466 "Hash page %lu above max_bucket referenced",
00467 pgno));
00468 isbad = 1;
00469 } else {
00470 if ((ret =
00471 CDB___db_vrfy_pgset_inc(pgset, pgno)) != 0)
00472 goto err;
00473 if ((ret =
00474 CDB___db_vrfy_putpageinfo(vdp, pip)) != 0)
00475 goto err;
00476 continue;
00477 }
00478 }
00479 
00480 /* If we got here, it's an error. */
00481 (void)CDB___db_vrfy_putpageinfo(vdp, pip);
00482 goto err;
00483 }
00484 
00485 err: if ((t_ret = CDB_memp_fput(dbp->mpf, m, 0)) != 0)
00486 return (t_ret);
00487 if (h != NULL && (t_ret = CDB_memp_fput(dbp->mpf, h, 0)) != 0)
00488 return (t_ret);
00489 return ((isbad == 1 && ret == 0) ? DB_VERIFY_BAD: ret);
00490 }
00491 
00492 /*
00493  * __ham_vrfy_bucket --
00494  * Verify a given bucket.
00495  */
00496 static int
00497 __ham_vrfy_bucket(dbp, vdp, m, bucket, flags)
00498 DB *dbp;
00499 VRFY_DBINFO *vdp;
00500 HMETA *m;
00501 u_int32_t bucket, flags;
00502 {
00503 HASH *hashp;
00504 VRFY_CHILDINFO *child;
00505 VRFY_PAGEINFO *mip, *pip;
00506 int ret, t_ret, isbad, p;
00507 db_pgno_t pgno, next_pgno;
00508 DBC *cc;
00509 u_int32_t (*hfunc) __P((const void *, u_int32_t));
00510 
00511 isbad = 0;
00512 pip = NULL;
00513 cc = NULL;
00514 
00515 hashp = dbp->h_internal;
00516 if (hashp != NULL && hashp->h_hash != NULL)
00517 hfunc = hashp->h_hash;
00518 else
00519 hfunc = CDB___ham_func5;
00520 
00521 if ((ret = CDB___db_vrfy_getpageinfo(vdp, PGNO(m), &mip)) != 0)
00522 return (ret);
00523 
00524 /* Calculate the first pgno for this bucket. */
00525 pgno = bucket + m->spares[CDB___db_log2(bucket + 1)];
00526 
00527 if ((ret = CDB___db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0)
00528 goto err;
00529 
00530 /* Make sure we got a plausible page number. */
00531 if (pgno > vdp->last_pgno || pip->type != P_HASH) {
00532 EPRINT((dbp->dbenv, "Bucket %lu has impossible first page %lu",
00533 bucket, pgno));
00534 /* Unsafe to continue. */
00535 isbad = 1;
00536 goto err;
00537 }
00538 
00539 if (pip->prev_pgno != PGNO_INVALID) {
00540 EPRINT((dbp->dbenv,
00541 "First hash page %lu in bucket %lu has a prev_pgno", pgno));
00542 isbad = 1;
00543 }
00544 
00545 /*
00546  * Set flags for dups and sorted dups.
00547  */
00548 flags |= F_ISSET(mip, VRFY_HAS_DUPS) ? ST_DUPOK : 0;
00549 flags |= F_ISSET(mip, VRFY_HAS_DUPSORT) ? ST_DUPSORT : 0;
00550 
00551 /* Loop until we find a fatal bug, or until we run out of pages. */
00552 for (;;) {
00553 if ((ret = CDB___db_vrfy_pgset_get(vdp->pgset, pgno, &p)) != 0)
00554 goto err;
00555 if (p != 0) {
00556 EPRINT((dbp->dbenv,
00557 "Hash page %lu referenced twice", pgno));
00558 isbad = 1;
00559 /* Unsafe to continue. */
00560 goto err;
00561 } else if ((ret = CDB___db_vrfy_pgset_inc(vdp->pgset, pgno)) != 0)
00562 goto err;
00563 
00564 /*
00565  * Hash pages that nothing has ever hashed to may never
00566  * have actually come into existence, and may appear to be
00567  * entirely zeroed. This is acceptable, and since there's
00568  * no real way for us to know whether this has actually
00569  * occurred, we clear the "wholly zeroed" flag on every
00570  * hash page. A wholly zeroed page, by nature, will appear
00571  * to have no flags set and zero entries, so should
00572  * otherwise verify correctly.
00573  */
00574 F_CLR(pip, VRFY_IS_ALLZEROES);
00575 
00576 /* If we have dups, our meta page had better know about it. */
00577 if (F_ISSET(pip, VRFY_HAS_DUPS)
00578 && !F_ISSET(mip, VRFY_HAS_DUPS)) {
00579 EPRINT((dbp->dbenv,
00580 "Duplicates present in non-duplicate database, page %lu",
00581 pgno));
00582 isbad = 1;
00583 }
00584 
00585 /*
00586  * If the database has sorted dups, this page had better
00587  * not have unsorted ones.
00588  */
00589 if (F_ISSET(mip, VRFY_HAS_DUPSORT) &&
00590 F_ISSET(pip, VRFY_DUPS_UNSORTED)) {
00591 EPRINT((dbp->dbenv,
00592 "Unsorted dups in sorted-dup database, page %lu",
00593 pgno));
00594 isbad = 1;
00595 }
00596 
00597 /* Walk overflow chains and offpage dup trees. */
00598 if ((ret = CDB___db_vrfy_childcursor(vdp, &cc)) != 0)
00599 goto err;
00600 for (ret = CDB___db_vrfy_ccset(cc, pip->pgno, &child); ret == 0;
00601 ret = CDB___db_vrfy_ccnext(cc, &child))
00602 if (child->type == V_OVERFLOW) {
00603 if ((ret = CDB___db_vrfy_ovfl_structure(dbp, vdp,
00604 child->pgno, child->tlen, flags)) != 0) {
00605 if (ret == DB_VERIFY_BAD)
00606 isbad = 1;
00607 else
00608 goto err;
00609 }
00610 } else if (child->type == V_DUPLICATE) {
00611 if ((ret = CDB___db_vrfy_duptype(dbp,
00612 vdp, child->pgno, flags)) != 0) {
00613 isbad = 1;
00614 continue;
00615 }
00616 if ((ret = CDB___bam_vrfy_subtree(dbp, vdp,
00617 child->pgno, NULL, NULL,
00618 flags | ST_RECNUM, NULL,
00619 NULL, NULL)) != 0) {
00620 if (ret == DB_VERIFY_BAD)
00621 isbad = 1;
00622 else
00623 goto err;
00624 }
00625 }
00626 if ((ret = CDB___db_vrfy_ccclose(cc)) != 0)
00627 goto err;
00628 cc = NULL;
00629 
00630 /* If it's safe to check that things hash properly, do so. */
00631 if (isbad == 0 && !LF_ISSET(DB_NOORDERCHK) &&
00632 (ret = CDB___ham_vrfy_hashing(dbp, pip->entries,
00633 m, bucket, pgno, flags, hfunc)) != 0) {
00634 if (ret == DB_VERIFY_BAD)
00635 isbad = 1;
00636 else
00637 goto err;
00638 }
00639 
00640 next_pgno = pip->next_pgno;
00641 ret = CDB___db_vrfy_putpageinfo(vdp, pip);
00642 
00643 pip = NULL;
00644 if (ret != 0)
00645 goto err;
00646 
00647 if (next_pgno == PGNO_INVALID)
00648 break; /* End of the bucket. */
00649 
00650 /* We already checked this, but just in case... */
00651 if (!IS_VALID_PGNO(next_pgno)) {
00652 DB_ASSERT(0);
00653 EPRINT((dbp->dbenv,
00654 "Hash page %lu has bad next_pgno", pgno));
00655 isbad = 1;
00656 goto err;
00657 }
00658 
00659 if ((ret = CDB___db_vrfy_getpageinfo(vdp, next_pgno, &pip)) != 0)
00660 goto err;
00661 
00662 if (pip->prev_pgno != pgno) {
00663 EPRINT((dbp->dbenv, "Hash page %lu has bad prev_pgno",
00664 next_pgno));
00665 isbad = 1;
00666 }
00667 pgno = next_pgno;
00668 }
00669 
00670 err: if (cc != NULL && ((t_ret = CDB___db_vrfy_ccclose(cc)) != 0) && ret == 0)
00671 ret = t_ret;
00672 if (mip != NULL && ((t_ret = CDB___db_vrfy_putpageinfo(vdp, mip)) != 0) &&
00673 ret == 0)
00674 ret = t_ret;
00675 if (pip != NULL && ((t_ret = CDB___db_vrfy_putpageinfo(vdp, pip)) != 0) &&
00676 ret == 0)
00677 ret = t_ret;
00678 return ((ret == 0 && isbad == 1) ? DB_VERIFY_BAD : ret);
00679 }
00680 
00681 /*
00682  * CDB___ham_vrfy_hashing --
00683  * Verify that all items on a given hash page hash correctly.
00684  *
00685  * PUBLIC: int CDB___ham_vrfy_hashing __P((DB *,
00686  * PUBLIC: u_int32_t, HMETA *, u_int32_t, db_pgno_t, u_int32_t,
00687  * PUBLIC: u_int32_t (*) __P((const void *, u_int32_t))));
00688  */
00689 int
00690 CDB___ham_vrfy_hashing(dbp, nentries, m, thisbucket, pgno, flags, hfunc)
00691 DB *dbp;
00692 u_int32_t nentries;
00693 HMETA *m;
00694 u_int32_t thisbucket;
00695 db_pgno_t pgno;
00696 u_int32_t flags;
00697 u_int32_t (*hfunc) __P((const void *, u_int32_t));
00698 {
00699 DBT dbt;
00700 PAGE *h;
00701 db_indx_t i;
00702 int ret, t_ret, isbad;
00703 u_int32_t hval, bucket;
00704 
00705 ret = isbad = 0;
00706 memset(&dbt, 0, sizeof(DBT));
00707 F_SET(&dbt, DB_DBT_REALLOC);
00708 
00709 if ((ret = CDB_memp_fget(dbp->mpf, &pgno, 0, &h)) != 0)
00710 return (ret);
00711 
00712 for (i = 0; i < nentries; i += 2) {
00713 /*
00714  * We've already verified the page integrity and that of any
00715  * overflow chains linked off it; it is therefore safe to use
00716  * CDB___db_ret. It's also not all that much slower, since we have
00717  * to copy every hash item to deal with alignment anyway; we
00718  * can tweak this a bit if this proves to be a bottleneck,
00719  * but for now, take the easy route.
00720  */
00721 if ((ret = CDB___db_ret(dbp, h, i, &dbt, NULL, NULL)) != 0)
00722 goto err;
00723 hval = hfunc(dbt.data, dbt.size);
00724 
00725 bucket = hval & m->high_mask;
00726 if (bucket > m->max_bucket)
00727 bucket = bucket & m->low_mask;
00728 
00729 if (bucket != thisbucket) {
00730 EPRINT((dbp->dbenv,
00731 "Item %lu on page %lu hashes incorrectly",
00732 i, pgno));
00733 isbad = 1;
00734 }
00735 }
00736 
00737 err: if (dbt.data != NULL)
00738 CDB___os_free(dbt.data, 0);
00739 if ((t_ret = CDB_memp_fput(dbp->mpf, h, 0)) != 0)
00740 return (t_ret);
00741 
00742 return ((ret == 0 && isbad == 1) ? DB_VERIFY_BAD : ret);
00743 }
00744 
00745 /*
00746  * CDB___ham_salvage --
00747  * Safely dump out anything that looks like a key on an alleged
00748  * hash page.
00749  *
00750  * PUBLIC: int CDB___ham_salvage __P((DB *, VRFY_DBINFO *, db_pgno_t, PAGE *,
00751  * PUBLIC: void *, int (*)(void *, const void *), u_int32_t));
00752  */
00753 int
00754 CDB___ham_salvage(dbp, vdp, pgno, h, handle, callback, flags)
00755 DB *dbp;
00756 VRFY_DBINFO *vdp;
00757 db_pgno_t pgno;
00758 PAGE *h;
00759 void *handle;
00760 int (*callback) __P((void *, const void *));
00761 u_int32_t flags;
00762 {
00763 DBT dbt, unkdbt;
00764 db_pgno_t dpgno;
00765 int ret, err_ret, t_ret;
00766 u_int32_t himark, tlen;
00767 u_int8_t *hk;
00768 void *buf;
00769 db_indx_t dlen, len, i;
00770 
00771 memset(&dbt, 0, sizeof(DBT));
00772 dbt.flags = DB_DBT_REALLOC;
00773 
00774 memset(&unkdbt, 0, sizeof(DBT));
00775 unkdbt.size = strlen("UNKNOWN") + 1;
00776 unkdbt.data = "UNKNOWN";
00777 
00778 err_ret = 0;
00779 
00780 /*
00781  * Allocate a buffer for overflow items. Start at one page;
00782  * CDB___db_safe_goff will realloc as needed.
00783  */
00784 if ((ret = CDB___os_malloc(dbp->dbenv, dbp->pgsize, NULL, &buf)) != 0)
00785 return (ret);
00786 
00787 himark = dbp->pgsize;
00788 for (i = 0;; i++) {
00789 /* If we're not aggressive, break when we hit NUM_ENT(h). */
00790 if (!LF_ISSET(DB_AGGRESSIVE) && i >= NUM_ENT(h))
00791 break;
00792 
00793 /* Verify the current item. */
00794 ret = CDB___db_vrfy_inpitem(dbp,
00795 h, pgno, i, 0, flags, &himark, NULL);
00796 /* If this returned a fatality, it's time to break. */
00797 if (ret == DB_VERIFY_FATAL)
00798 break;
00799 
00800 if (ret == 0) {
00801 hk = P_ENTRY(h, i);
00802 len = LEN_HKEYDATA(h, dbp->pgsize, i);
00803 if ((u_int32_t)(hk + len - (u_int8_t *)h) >
00804 dbp->pgsize) {
00805 /*
00806  * Item is unsafely large; either continue
00807  * or set it to the whole page, depending on
00808  * aggressiveness.
00809  */
00810 if (!LF_ISSET(DB_AGGRESSIVE))
00811 continue;
00812 len = dbp->pgsize -
00813 (u_int32_t)(hk - (u_int8_t *)h);
00814 err_ret = DB_VERIFY_BAD;
00815 }
00816 switch (HPAGE_PTYPE(hk)) {
00817 default:
00818 if (!LF_ISSET(DB_AGGRESSIVE))
00819 break;
00820 err_ret = DB_VERIFY_BAD;
00821 /* FALLTHROUGH */
00822 case H_KEYDATA:
00823 keydata: memcpy(buf, HKEYDATA_DATA(hk), len);
00824 dbt.size = len;
00825 dbt.data = buf;
00826 if ((ret = CDB___db_prdbt(&dbt,
00827 0, " ", handle, callback, 0, NULL)) != 0)
00828 err_ret = ret;
00829 break;
00830 case H_OFFPAGE:
00831 if (len < HOFFPAGE_SIZE) {
00832 err_ret = DB_VERIFY_BAD;
00833 continue;
00834 }
00835 memcpy(&dpgno,
00836 HOFFPAGE_PGNO(hk), sizeof(dpgno));
00837 if ((ret = CDB___db_safe_goff(dbp, vdp,
00838 dpgno, &dbt, &buf, flags)) != 0) {
00839 err_ret = ret;
00840 (void)CDB___db_prdbt(&unkdbt, 0, " ",
00841 handle, callback, 0, NULL);
00842 break;
00843 }
00844 if ((ret = CDB___db_prdbt(&dbt,
00845 0, " ", handle, callback, 0, NULL)) != 0)
00846 err_ret = ret;
00847 break;
00848 case H_OFFDUP:
00849 if (len < HOFFPAGE_SIZE) {
00850 err_ret = DB_VERIFY_BAD;
00851 continue;
00852 }
00853 memcpy(&dpgno,
00854 HOFFPAGE_PGNO(hk), sizeof(dpgno));
00855 /* UNKNOWN iff pgno is bad or we're a key. */
00856 if (!IS_VALID_PGNO(dpgno) || (i % 2 == 0)) {
00857 if ((ret = CDB___db_prdbt(&unkdbt, 0, " ",
00858 handle, callback, 0, NULL)) != 0)
00859 err_ret = ret;
00860 } else if ((ret = CDB___db_salvage_duptree(dbp,
00861 vdp, dpgno, &dbt, handle, callback,
00862 flags | SA_SKIPFIRSTKEY)) != 0)
00863 err_ret = ret;
00864 break;
00865 case H_DUPLICATE:
00866 /*
00867  * We're a key; printing dups will seriously
00868  * foul the output. If we're being aggressive,
00869  * pretend this is a key and let the app.
00870  * programmer sort out the mess.
00871  */
00872 if (i % 2 == 0) {
00873 err_ret = ret;
00874 if (LF_ISSET(DB_AGGRESSIVE))
00875 goto keydata;
00876 break;
00877 }
00878 
00879 /* Too small to have any data. */
00880 if (len <
00881 HKEYDATA_SIZE(2 * sizeof(db_indx_t))) {
00882 err_ret = DB_VERIFY_BAD;
00883 continue;
00884 }
00885 
00886 /* Loop until we hit the total length. */
00887 for (tlen = 0; tlen + sizeof(db_indx_t) < len;
00888 tlen += dlen) {
00889 tlen += sizeof(db_indx_t);
00890 memcpy(&dlen, hk, sizeof(db_indx_t));
00891 /*
00892  * If dlen is too long, print all the
00893  * rest of the dup set in a chunk.
00894  */
00895 if (dlen + tlen > len)
00896 dlen = len - tlen;
00897 memcpy(buf, hk + tlen, dlen);
00898 dbt.size = dlen;
00899 dbt.data = buf;
00900 if ((ret = CDB___db_prdbt(&dbt, 0, " ",
00901 handle, callback, 0, NULL)) != 0)
00902 err_ret = ret;
00903 tlen += sizeof(db_indx_t);
00904 }
00905 break;
00906 }
00907 }
00908 }
00909 
00910 if ((t_ret = CDB___db_salvage_markdone(vdp, pgno)) != 0)
00911 return (t_ret);
00912 return ((ret == 0 && err_ret != 0) ? err_ret : ret);
00913 }
00914 
00915 /*
00916  * CDB___ham_meta2pgset --
00917  * Return the set of hash pages corresponding to the given
00918  * known-good meta page.
00919  *
00920  * PUBLIC: int CDB___ham_meta2pgset __P((DB *, VRFY_DBINFO *, HMETA *, u_int32_t,
00921  * PUBLIC: DB *));
00922  */
00923 int CDB___ham_meta2pgset(dbp, vdp, hmeta, flags, pgset)
00924 DB *dbp;
00925 VRFY_DBINFO *vdp;
00926 HMETA *hmeta;
00927 u_int32_t flags;
00928 DB *pgset;
00929 {
00930 PAGE *h;
00931 db_pgno_t pgno;
00932 u_int32_t bucket, totpgs;
00933 int ret, val;
00934 
00935 /*
00936  * We don't really need flags, but leave them for consistency with
00937  * CDB___bam_meta2pgset.
00938  */
00939 COMPQUIET(flags, 0);
00940 
00941 DB_ASSERT(pgset != NULL);
00942 
00943 totpgs = 0;
00944 
00945 /*
00946  * Loop through all the buckets, pushing onto pgset the corresponding
00947  * page(s) for each one.
00948  */
00949 for (bucket = 0; bucket <= hmeta->max_bucket; bucket++) {
00950 pgno = bucket + hmeta->spares[CDB___db_log2(bucket + 1)];
00951 
00952 /*
00953  * We know the initial pgno is safe because the spares array has
00954  * been verified.
00955  *
00956  * Safely walk the list of pages in this bucket.
00957  */
00958 for (;;) {
00959 if ((ret = CDB_memp_fget(dbp->mpf, &pgno, 0, &h)) != 0)
00960 return (ret);
00961 if (TYPE(h) == P_HASH) {
00962 
00963 /*
00964  * Make sure we don't go past the end of
00965  * pgset.
00966  */
00967 if (++totpgs > vdp->last_pgno) {
00968 (void)CDB_memp_fput(dbp->mpf, h, 0);
00969 return (DB_VERIFY_BAD);
00970 }
00971 if ((ret =
00972 CDB___db_vrfy_pgset_inc(pgset, pgno)) != 0)
00973 return (ret);
00974 
00975 pgno = NEXT_PGNO(h);
00976 } else
00977 pgno = PGNO_INVALID;
00978 
00979 if ((ret = CDB_memp_fput(dbp->mpf, h, 0)) != 0)
00980 return (ret);
00981 
00982 /* If the new pgno is wonky, go onto the next bucket. */
00983 if (!IS_VALID_PGNO(pgno) ||
00984 pgno == PGNO_INVALID)
00985 goto nextbucket;
00986 
00987 /*
00988  * If we've touched this page before, we have a cycle;
00989  * go on to the next bucket.
00990  */
00991 if ((ret = CDB___db_vrfy_pgset_get(pgset, pgno, &val)) != 0)
00992 return (ret);
00993 if (val != 0)
00994 goto nextbucket;
00995 }
00996 nextbucket: ;
00997 }
00998 return (0);
00999 }
01000 
01001 /*
01002  * __ham_dups_unsorted --
01003  * Takes a known-safe hash duplicate set and its total length.
01004  * Returns 1 if there are out-of-order duplicates in this set,
01005  * 0 if there are not.
01006  */
01007 static int
01008 __ham_dups_unsorted(dbp, buf, len)
01009 DB *dbp;
01010 u_int8_t *buf;
01011 u_int32_t len;
01012 {
01013 DBT a, b;
01014 db_indx_t offset, dlen;
01015 int (*func) __P((const DBT *, const DBT *));
01016 
01017 memset(&a, 0, sizeof(DBT));
01018 memset(&b, 0, sizeof(DBT));
01019 
01020 func = (dbp->dup_compare == NULL) ? CDB___bam_defcmp : dbp->dup_compare;
01021 
01022 /*
01023  * Loop through the dup set until we hit the end or we find
01024  * a pair of dups that's out of order. b is always the current
01025  * dup, a the one before it.
01026  */
01027 for (offset = 0; offset < len; offset += DUP_SIZE(dlen)) {
01028 memcpy(&dlen, buf + offset, sizeof(db_indx_t));
01029 b.data = buf + offset + sizeof(db_indx_t);
01030 b.size = dlen;
01031 
01032 if (a.data != NULL && func(&a, &b) > 0)
01033 return (1);
01034 
01035 a.data = b.data;
01036 a.size = b.size;
01037 }
01038 
01039 return (0);
01040 }

Generated on Sun Jun 8 10:56:37 2008 for GNUmifluz by doxygen 1.5.5

AltStyle によって変換されたページ (->オリジナル) /