diff options
author | Slávek Banko <slavek.banko@axis.cz> | 2021-11-05 13:28:23 +0100 |
---|---|---|
committer | Slávek Banko <slavek.banko@axis.cz> | 2021-11-05 13:28:23 +0100 |
commit | 8c787c3591c1c885b91a54128835b400858c5cca (patch) | |
tree | eca1b776912a305c4d45b3964038278a2fae1ead /debian/htdig/htdig-3.2.0b6/db/hash_verify.c | |
parent | fe188b907cdf30dfdfe0eba9412e7f8749fec158 (diff) | |
download | extra-dependencies-8c787c3591c1c885b91a54128835b400858c5cca.tar.gz extra-dependencies-8c787c3591c1c885b91a54128835b400858c5cca.zip |
DEB htdig: Added to repository.
Signed-off-by: Slávek Banko <slavek.banko@axis.cz>
Diffstat (limited to 'debian/htdig/htdig-3.2.0b6/db/hash_verify.c')
-rw-r--r-- | debian/htdig/htdig-3.2.0b6/db/hash_verify.c | 1040 |
1 files changed, 1040 insertions, 0 deletions
diff --git a/debian/htdig/htdig-3.2.0b6/db/hash_verify.c b/debian/htdig/htdig-3.2.0b6/db/hash_verify.c new file mode 100644 index 00000000..93be7b07 --- /dev/null +++ b/debian/htdig/htdig-3.2.0b6/db/hash_verify.c @@ -0,0 +1,1040 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1999, 2000 + * Sleepycat Software. All rights reserved. + * + * $Id: hash_verify.c,v 1.2 2002/02/02 18:18:05 ghutchis Exp $ + */ + +#include "htconfig.h" + +#ifndef lint +static const char revid[] = "$Id: hash_verify.c,v 1.2 2002/02/02 18:18:05 ghutchis Exp $"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <errno.h> +#include <string.h> +#endif + +#include "db_int.h" +#include "db_page.h" +#include "db_verify.h" +#include "btree.h" +#include "hash.h" + +static int __ham_dups_unsorted __P((DB *, u_int8_t *, u_int32_t)); +static int __ham_vrfy_bucket __P((DB *, VRFY_DBINFO *, HMETA *, u_int32_t, + u_int32_t)); +static int __ham_vrfy_item __P((DB *, + VRFY_DBINFO *, db_pgno_t, PAGE *, u_int32_t, u_int32_t)); + +/* + * CDB___ham_vrfy_meta -- + * Verify the hash-specific part of a metadata page. + * + * Note that unlike btree, we don't save things off, because we + * will need most everything again to verify each page and the + * amount of state here is significant. + * + * PUBLIC: int CDB___ham_vrfy_meta __P((DB *, VRFY_DBINFO *, HMETA *, + * PUBLIC: db_pgno_t, u_int32_t)); + */ +int +CDB___ham_vrfy_meta(dbp, vdp, m, pgno, flags) + DB *dbp; + VRFY_DBINFO *vdp; + HMETA *m; + db_pgno_t pgno; + u_int32_t flags; +{ + HASH *hashp; + VRFY_PAGEINFO *pip; + int i, ret, t_ret, isbad; + u_int32_t pwr, mbucket; + u_int32_t (*hfunc) __P((const void *, u_int32_t)); + + if ((ret = CDB___db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0) + return (ret); + isbad = 0; + + hashp = dbp->h_internal; + + if (hashp != NULL && hashp->h_hash != NULL) + hfunc = hashp->h_hash; + else + hfunc = CDB___ham_func5; + + /* + * If we haven't already checked the common fields in pagezero, + * check them. + */ + if (!F_ISSET(pip, VRFY_INCOMPLETE) && + (ret = CDB___db_vrfy_meta(dbp, vdp, &m->dbmeta, pgno, flags)) != 0) { + if (ret == DB_VERIFY_BAD) + isbad = 1; + else + goto err; + } + + /* h_charkey */ + if (!LF_ISSET(DB_NOORDERCHK)) + if (m->h_charkey != hfunc(CHARKEY, sizeof(CHARKEY))) { + EPRINT((dbp->dbenv, +"Database has different custom hash function; reverify with DB_NOORDERCHK set" + )); + /* + * Return immediately; this is probably a sign + * of user error rather than database corruption, so + * we want to avoid extraneous errors. + */ + isbad = 1; + goto err; + } + + /* max_bucket must be less than the last pgno. */ + if (m->max_bucket > vdp->last_pgno) { + EPRINT((dbp->dbenv, + "Impossible max_bucket %lu on meta page %lu", + m->max_bucket, pgno)); + /* + * Most other fields depend somehow on max_bucket, so + * we just return--there will be lots of extraneous + * errors. + */ + isbad = 1; + goto err; + } + + /* + * max_bucket, high_mask and low_mask: high_mask must be one + * less than the next power of two above max_bucket, and + * low_mask must be one less than the power of two below it. + * + * + */ + pwr = (m->max_bucket == 0) ? 1 : 1 << CDB___db_log2(m->max_bucket + 1); + if (m->high_mask != pwr - 1) { + EPRINT((dbp->dbenv, + "Incorrect high_mask %lu on page %lu, should be %lu", + m->high_mask, pgno, pwr - 1)); + isbad = 1; + } + pwr >>= 1; + if (m->low_mask != pwr - 1) { + EPRINT((dbp->dbenv, + "Incorrect low_mask %lu on page %lu, should be %lu", + m->low_mask, pgno, pwr - 1)); + isbad = 1; + } + + /* ffactor: no check possible. */ + pip->h_ffactor = m->ffactor; + + /* + * nelem: just make sure it's not astronomical for now. This is the + * same check that hash_upgrade does, since there was a bug in 2.X + * which could make nelem go "negative". + */ + if (m->nelem > 0x80000000) { + EPRINT((dbp->dbenv, + "Suspiciously high nelem of %lu on page %lu", + m->nelem, pgno)); + isbad = 1; + pip->h_nelem = 0; + } else + pip->h_nelem = m->nelem; + + /* flags */ + if (F_ISSET(&m->dbmeta, DB_HASH_DUP)) + F_SET(pip, VRFY_HAS_DUPS); + if (F_ISSET(&m->dbmeta, DB_HASH_DUPSORT)) + F_SET(pip, VRFY_HAS_DUPSORT); + /* XXX: Why is the DB_HASH_SUBDB flag necessary? */ + + /* spares array */ + for (i = 0; m->spares[i] != 0 && i < NCACHED; i++) { + /* + * We set mbucket to the maximum bucket that would use a given + * spares entry; we want to ensure that it's always less + * than last_pgno. + */ + mbucket = (1 << i) - 1; + if (mbucket + m->spares[CDB___db_log2(mbucket + 1)] > + vdp->last_pgno) { + EPRINT((dbp->dbenv, + "Spares array entry %lu, page %lu is invalid", + i, pgno)); + isbad = 1; + } + } + +err: if ((t_ret = CDB___db_vrfy_putpageinfo(vdp, pip)) != 0 && ret == 0) + ret = t_ret; + return ((ret == 0 && isbad == 1) ? DB_VERIFY_BAD : ret); +} + +/* + * CDB___ham_vrfy -- + * Verify hash page. + * + * PUBLIC: int CDB___ham_vrfy __P((DB *, VRFY_DBINFO *, PAGE *, db_pgno_t, + * PUBLIC: u_int32_t)); + */ +int +CDB___ham_vrfy(dbp, vdp, h, pgno, flags) + DB *dbp; + VRFY_DBINFO *vdp; + PAGE *h; + db_pgno_t pgno; + u_int32_t flags; +{ + VRFY_PAGEINFO *pip; + db_indx_t ent, himark, inpend; + int isbad, ret, t_ret; + + isbad = 0; + if ((ret = CDB___db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0) + return (ret); + + /* Sanity check our flags and page type. */ + if ((ret = CDB___db_fchk(dbp->dbenv, "CDB___ham_vrfy", + flags, DB_AGGRESSIVE | DB_NOORDERCHK | DB_SALVAGE)) != 0) + goto err; + + if (TYPE(h) != P_HASH) { + TYPE_ERR_PRINT(dbp->dbenv, "CDB___ham_vrfy", pgno, TYPE(h)); + DB_ASSERT(0); + ret = EINVAL; + goto err; + } + + /* Verify and save off fields common to all PAGEs. */ + if ((ret = CDB___db_vrfy_datapage(dbp, vdp, h, pgno, flags)) != 0) { + if (ret == DB_VERIFY_BAD) + isbad = 1; + else + goto err; + } + + /* + * Verify inp[]. Each offset from 0 to NUM_ENT(h) must be lower + * than the previous one, higher than the current end of the inp array, + * and lower than the page size. + * + * In any case, we return immediately if things are bad, as it would + * be unsafe to proceed. + */ + for (ent = 0, himark = dbp->pgsize, + inpend = (u_int8_t *)h->inp - (u_int8_t *)h; + ent < NUM_ENT(h); ent++) + if (h->inp[ent] >= himark) { + EPRINT((dbp->dbenv, + "Item %lu on page %lu out of order or nonsensical", + ent, pgno)); + isbad = 1; + goto err; + } else if (inpend >= himark) { + EPRINT((dbp->dbenv, + "inp array collided with data on page %lu", + pgno)); + isbad = 1; + goto err; + + } else { + himark = h->inp[ent]; + inpend += sizeof(db_indx_t); + if ((ret = __ham_vrfy_item( + dbp, vdp, pgno, h, ent, flags)) != 0) + goto err; + } + +err: if ((t_ret = CDB___db_vrfy_putpageinfo(vdp, pip)) != 0 && ret == 0) + ret = t_ret; + return (ret == 0 && isbad == 1 ? DB_VERIFY_BAD : ret); +} + +/* + * __ham_vrfy_item -- + * Given a hash page and an offset, sanity-check the item itself, + * and save off any overflow items or off-page dup children as necessary. + */ +static int +__ham_vrfy_item(dbp, vdp, pgno, h, i, flags) + DB *dbp; + VRFY_DBINFO *vdp; + db_pgno_t pgno; + PAGE *h; + u_int32_t i, flags; +{ + HOFFPAGE hop; + HOFFDUP hod; + VRFY_CHILDINFO child; + VRFY_PAGEINFO *pip; + db_indx_t offset, len, dlen, elen; + int ret, t_ret; + u_int8_t *databuf; + + if ((ret = CDB___db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0) + return (ret); + + switch (HPAGE_TYPE(h, i)) { + case H_KEYDATA: + /* Nothing to do here--everything but the type field is data */ + break; + case H_DUPLICATE: + /* Are we a datum or a key? Better be the former. */ + if (i % 2 == 0) { + EPRINT((dbp->dbenv, + "Hash key stored as duplicate at page %lu item %lu", + pip->pgno, i)); + } + /* + * Dups are encoded as a series within a single HKEYDATA, + * in which each dup is surrounded by a copy of its length + * on either side (so that the series can be walked in either + * direction. We loop through this series and make sure + * each dup is reasonable. + * + * Note that at this point, we've verified item i-1, so + * it's safe to use LEN_HKEYDATA (which looks at inp[i-1]). + */ + len = LEN_HKEYDATA(h, dbp->pgsize, i); + databuf = HKEYDATA_DATA(P_ENTRY(h, i)); + for (offset = 0; offset < len; offset += DUP_SIZE(dlen)) { + memcpy(&dlen, databuf + offset, sizeof(db_indx_t)); + + /* Make sure the length is plausible. */ + if (offset + DUP_SIZE(dlen) > len) { + EPRINT((dbp->dbenv, + "Duplicate item %lu, page %lu has bad length", + i, pip->pgno)); + ret = DB_VERIFY_BAD; + goto err; + } + + /* + * Make sure the second copy of the length is the + * same as the first. + */ + memcpy(&elen, + databuf + offset + dlen + sizeof(db_indx_t), + sizeof(db_indx_t)); + if (elen != dlen) { + EPRINT((dbp->dbenv, + "Duplicate item %lu, page %lu has two different lengths", + i, pip->pgno)); + ret = DB_VERIFY_BAD; + goto err; + } + } + F_SET(pip, VRFY_HAS_DUPS); + if (!LF_ISSET(DB_NOORDERCHK) && + __ham_dups_unsorted(dbp, databuf, len)) + F_SET(pip, VRFY_DUPS_UNSORTED); + break; + case H_OFFPAGE: + /* Offpage item. Make sure pgno is sane, save off. */ + memcpy(&hop, P_ENTRY(h, i), HOFFPAGE_SIZE); + if (!IS_VALID_PGNO(hop.pgno) || hop.pgno == pip->pgno || + hop.pgno == PGNO_INVALID) { + EPRINT((dbp->dbenv, + "Offpage item %lu, page %lu has bad page number", + i, pip->pgno)); + ret = DB_VERIFY_BAD; + goto err; + } + memset(&child, 0, sizeof(VRFY_CHILDINFO)); + child.pgno = hop.pgno; + child.type = V_OVERFLOW; + child.tlen = hop.tlen; /* This will get checked later. */ + if ((ret = CDB___db_vrfy_childput(vdp, pip->pgno, &child)) != 0) + goto err; + break; + case H_OFFDUP: + /* Offpage duplicate item. Same drill. */ + memcpy(&hod, P_ENTRY(h, i), HOFFDUP_SIZE); + if (!IS_VALID_PGNO(hod.pgno) || hod.pgno == pip->pgno || + hod.pgno == PGNO_INVALID) { + EPRINT((dbp->dbenv, + "Offpage item %lu, page %lu has bad page number", + i, pip->pgno)); + ret = DB_VERIFY_BAD; + goto err; + } + memset(&child, 0, sizeof(VRFY_CHILDINFO)); + child.pgno = hod.pgno; + child.type = V_DUPLICATE; + if ((ret = CDB___db_vrfy_childput(vdp, pip->pgno, &child)) != 0) + goto err; + F_SET(pip, VRFY_HAS_DUPS); + break; + default: + EPRINT((dbp->dbenv, + "Item %i, page %lu has bad type", i, pip->pgno)); + ret = DB_VERIFY_BAD; + break; + } + +err: if ((t_ret = CDB___db_vrfy_putpageinfo(vdp, pip)) != 0 && ret == 0) + ret = t_ret; + return (ret); +} + +/* + * CDB___ham_vrfy_structure -- + * Verify the structure of a hash database. + * + * PUBLIC: int CDB___ham_vrfy_structure __P((DB *, VRFY_DBINFO *, db_pgno_t, + * PUBLIC: u_int32_t)); + */ +int +CDB___ham_vrfy_structure(dbp, vdp, meta_pgno, flags) + DB *dbp; + VRFY_DBINFO *vdp; + db_pgno_t meta_pgno; + u_int32_t flags; +{ + DB *pgset; + HMETA *m; + PAGE *h; + VRFY_PAGEINFO *pip; + int isbad, p, ret, t_ret; + db_pgno_t pgno; + u_int32_t bucket; + + ret = isbad = 0; + h = NULL; + pgset = vdp->pgset; + + if ((ret = CDB___db_vrfy_pgset_get(pgset, meta_pgno, &p)) != 0) + return (ret); + if (p != 0) { + EPRINT((dbp->dbenv, + "Hash meta page %lu referenced twice", meta_pgno)); + return (DB_VERIFY_BAD); + } + if ((ret = CDB___db_vrfy_pgset_inc(pgset, meta_pgno)) != 0) + return (ret); + + /* Get the meta page; we'll need it frequently. */ + if ((ret = CDB_memp_fget(dbp->mpf, &meta_pgno, 0, &m)) != 0) + return (ret); + + /* Loop through bucket by bucket. */ + for (bucket = 0; bucket <= m->max_bucket; bucket++) + if ((ret = + __ham_vrfy_bucket(dbp, vdp, m, bucket, flags)) != 0) { + if (ret == DB_VERIFY_BAD) + isbad = 1; + else + goto err; + } + + /* + * There may be unused hash pages corresponding to buckets + * that are part of the current doubling but which are higher + * than max_bucket. Loop through them, too, and make sure they're + * all empty. + */ + for (bucket = m->max_bucket + 1; bucket <= m->high_mask; bucket++) { + pgno = bucket + m->spares[CDB___db_log2(bucket + 1)]; + if ((ret = CDB___db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0) + goto err; + + /* It's okay if these pages are totally zeroed; unmark it. */ + F_CLR(pip, VRFY_IS_ALLZEROES); + + if (pip->type != P_HASH) { + EPRINT((dbp->dbenv, + "Hash bucket %lu maps to non-hash page %lu", + bucket, pgno)); + isbad = 1; + } else if (pip->entries != 0) { + EPRINT((dbp->dbenv, + "Non-empty page %lu in unused hash bucket %lu", + pgno, bucket)); + isbad = 1; + } else { + if ((ret = CDB___db_vrfy_pgset_get(pgset, pgno, &p)) != 0) + goto err; + if (p != 0) { + EPRINT((dbp->dbenv, + "Hash page %lu above max_bucket referenced", + pgno)); + isbad = 1; + } else { + if ((ret = + CDB___db_vrfy_pgset_inc(pgset, pgno)) != 0) + goto err; + if ((ret = + CDB___db_vrfy_putpageinfo(vdp, pip)) != 0) + goto err; + continue; + } + } + + /* If we got here, it's an error. */ + (void)CDB___db_vrfy_putpageinfo(vdp, pip); + goto err; + } + +err: if ((t_ret = CDB_memp_fput(dbp->mpf, m, 0)) != 0) + return (t_ret); + if (h != NULL && (t_ret = CDB_memp_fput(dbp->mpf, h, 0)) != 0) + return (t_ret); + return ((isbad == 1 && ret == 0) ? DB_VERIFY_BAD: ret); +} + +/* + * __ham_vrfy_bucket -- + * Verify a given bucket. + */ +static int +__ham_vrfy_bucket(dbp, vdp, m, bucket, flags) + DB *dbp; + VRFY_DBINFO *vdp; + HMETA *m; + u_int32_t bucket, flags; +{ + HASH *hashp; + VRFY_CHILDINFO *child; + VRFY_PAGEINFO *mip, *pip; + int ret, t_ret, isbad, p; + db_pgno_t pgno, next_pgno; + DBC *cc; + u_int32_t (*hfunc) __P((const void *, u_int32_t)); + + isbad = 0; + pip = NULL; + cc = NULL; + + hashp = dbp->h_internal; + if (hashp != NULL && hashp->h_hash != NULL) + hfunc = hashp->h_hash; + else + hfunc = CDB___ham_func5; + + if ((ret = CDB___db_vrfy_getpageinfo(vdp, PGNO(m), &mip)) != 0) + return (ret); + + /* Calculate the first pgno for this bucket. */ + pgno = bucket + m->spares[CDB___db_log2(bucket + 1)]; + + if ((ret = CDB___db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0) + goto err; + + /* Make sure we got a plausible page number. */ + if (pgno > vdp->last_pgno || pip->type != P_HASH) { + EPRINT((dbp->dbenv, "Bucket %lu has impossible first page %lu", + bucket, pgno)); + /* Unsafe to continue. */ + isbad = 1; + goto err; + } + + if (pip->prev_pgno != PGNO_INVALID) { + EPRINT((dbp->dbenv, + "First hash page %lu in bucket %lu has a prev_pgno", pgno)); + isbad = 1; + } + + /* + * Set flags for dups and sorted dups. + */ + flags |= F_ISSET(mip, VRFY_HAS_DUPS) ? ST_DUPOK : 0; + flags |= F_ISSET(mip, VRFY_HAS_DUPSORT) ? ST_DUPSORT : 0; + + /* Loop until we find a fatal bug, or until we run out of pages. */ + for (;;) { + if ((ret = CDB___db_vrfy_pgset_get(vdp->pgset, pgno, &p)) != 0) + goto err; + if (p != 0) { + EPRINT((dbp->dbenv, + "Hash page %lu referenced twice", pgno)); + isbad = 1; + /* Unsafe to continue. */ + goto err; + } else if ((ret = CDB___db_vrfy_pgset_inc(vdp->pgset, pgno)) != 0) + goto err; + + /* + * Hash pages that nothing has ever hashed to may never + * have actually come into existence, and may appear to be + * entirely zeroed. This is acceptable, and since there's + * no real way for us to know whether this has actually + * occurred, we clear the "wholly zeroed" flag on every + * hash page. A wholly zeroed page, by nature, will appear + * to have no flags set and zero entries, so should + * otherwise verify correctly. + */ + F_CLR(pip, VRFY_IS_ALLZEROES); + + /* If we have dups, our meta page had better know about it. */ + if (F_ISSET(pip, VRFY_HAS_DUPS) + && !F_ISSET(mip, VRFY_HAS_DUPS)) { + EPRINT((dbp->dbenv, + "Duplicates present in non-duplicate database, page %lu", + pgno)); + isbad = 1; + } + + /* + * If the database has sorted dups, this page had better + * not have unsorted ones. + */ + if (F_ISSET(mip, VRFY_HAS_DUPSORT) && + F_ISSET(pip, VRFY_DUPS_UNSORTED)) { + EPRINT((dbp->dbenv, + "Unsorted dups in sorted-dup database, page %lu", + pgno)); + isbad = 1; + } + + /* Walk overflow chains and offpage dup trees. */ + if ((ret = CDB___db_vrfy_childcursor(vdp, &cc)) != 0) + goto err; + for (ret = CDB___db_vrfy_ccset(cc, pip->pgno, &child); ret == 0; + ret = CDB___db_vrfy_ccnext(cc, &child)) + if (child->type == V_OVERFLOW) { + if ((ret = CDB___db_vrfy_ovfl_structure(dbp, vdp, + child->pgno, child->tlen, flags)) != 0) { + if (ret == DB_VERIFY_BAD) + isbad = 1; + else + goto err; + } + } else if (child->type == V_DUPLICATE) { + if ((ret = CDB___db_vrfy_duptype(dbp, + vdp, child->pgno, flags)) != 0) { + isbad = 1; + continue; + } + if ((ret = CDB___bam_vrfy_subtree(dbp, vdp, + child->pgno, NULL, NULL, + flags | ST_RECNUM, NULL, + NULL, NULL)) != 0) { + if (ret == DB_VERIFY_BAD) + isbad = 1; + else + goto err; + } + } + if ((ret = CDB___db_vrfy_ccclose(cc)) != 0) + goto err; + cc = NULL; + + /* If it's safe to check that things hash properly, do so. */ + if (isbad == 0 && !LF_ISSET(DB_NOORDERCHK) && + (ret = CDB___ham_vrfy_hashing(dbp, pip->entries, + m, bucket, pgno, flags, hfunc)) != 0) { + if (ret == DB_VERIFY_BAD) + isbad = 1; + else + goto err; + } + + next_pgno = pip->next_pgno; + ret = CDB___db_vrfy_putpageinfo(vdp, pip); + + pip = NULL; + if (ret != 0) + goto err; + + if (next_pgno == PGNO_INVALID) + break; /* End of the bucket. */ + + /* We already checked this, but just in case... */ + if (!IS_VALID_PGNO(next_pgno)) { + DB_ASSERT(0); + EPRINT((dbp->dbenv, + "Hash page %lu has bad next_pgno", pgno)); + isbad = 1; + goto err; + } + + if ((ret = CDB___db_vrfy_getpageinfo(vdp, next_pgno, &pip)) != 0) + goto err; + + if (pip->prev_pgno != pgno) { + EPRINT((dbp->dbenv, "Hash page %lu has bad prev_pgno", + next_pgno)); + isbad = 1; + } + pgno = next_pgno; + } + +err: if (cc != NULL && ((t_ret = CDB___db_vrfy_ccclose(cc)) != 0) && ret == 0) + ret = t_ret; + if (mip != NULL && ((t_ret = CDB___db_vrfy_putpageinfo(vdp, mip)) != 0) && + ret == 0) + ret = t_ret; + if (pip != NULL && ((t_ret = CDB___db_vrfy_putpageinfo(vdp, pip)) != 0) && + ret == 0) + ret = t_ret; + return ((ret == 0 && isbad == 1) ? DB_VERIFY_BAD : ret); +} + +/* + * CDB___ham_vrfy_hashing -- + * Verify that all items on a given hash page hash correctly. + * + * PUBLIC: int CDB___ham_vrfy_hashing __P((DB *, + * PUBLIC: u_int32_t, HMETA *, u_int32_t, db_pgno_t, u_int32_t, + * PUBLIC: u_int32_t (*) __P((const void *, u_int32_t)))); + */ +int +CDB___ham_vrfy_hashing(dbp, nentries, m, thisbucket, pgno, flags, hfunc) + DB *dbp; + u_int32_t nentries; + HMETA *m; + u_int32_t thisbucket; + db_pgno_t pgno; + u_int32_t flags; + u_int32_t (*hfunc) __P((const void *, u_int32_t)); +{ + DBT dbt; + PAGE *h; + db_indx_t i; + int ret, t_ret, isbad; + u_int32_t hval, bucket; + + ret = isbad = 0; + memset(&dbt, 0, sizeof(DBT)); + F_SET(&dbt, DB_DBT_REALLOC); + + if ((ret = CDB_memp_fget(dbp->mpf, &pgno, 0, &h)) != 0) + return (ret); + + for (i = 0; i < nentries; i += 2) { + /* + * We've already verified the page integrity and that of any + * overflow chains linked off it; it is therefore safe to use + * CDB___db_ret. It's also not all that much slower, since we have + * to copy every hash item to deal with alignment anyway; we + * can tweak this a bit if this proves to be a bottleneck, + * but for now, take the easy route. + */ + if ((ret = CDB___db_ret(dbp, h, i, &dbt, NULL, NULL)) != 0) + goto err; + hval = hfunc(dbt.data, dbt.size); + + bucket = hval & m->high_mask; + if (bucket > m->max_bucket) + bucket = bucket & m->low_mask; + + if (bucket != thisbucket) { + EPRINT((dbp->dbenv, + "Item %lu on page %lu hashes incorrectly", + i, pgno)); + isbad = 1; + } + } + +err: if (dbt.data != NULL) + CDB___os_free(dbt.data, 0); + if ((t_ret = CDB_memp_fput(dbp->mpf, h, 0)) != 0) + return (t_ret); + + return ((ret == 0 && isbad == 1) ? DB_VERIFY_BAD : ret); +} + +/* + * CDB___ham_salvage -- + * Safely dump out anything that looks like a key on an alleged + * hash page. + * + * PUBLIC: int CDB___ham_salvage __P((DB *, VRFY_DBINFO *, db_pgno_t, PAGE *, + * PUBLIC: void *, int (*)(void *, const void *), u_int32_t)); + */ +int +CDB___ham_salvage(dbp, vdp, pgno, h, handle, callback, flags) + DB *dbp; + VRFY_DBINFO *vdp; + db_pgno_t pgno; + PAGE *h; + void *handle; + int (*callback) __P((void *, const void *)); + u_int32_t flags; +{ + DBT dbt, unkdbt; + db_pgno_t dpgno; + int ret, err_ret, t_ret; + u_int32_t himark, tlen; + u_int8_t *hk; + void *buf; + db_indx_t dlen, len, i; + + memset(&dbt, 0, sizeof(DBT)); + dbt.flags = DB_DBT_REALLOC; + + memset(&unkdbt, 0, sizeof(DBT)); + unkdbt.size = strlen("UNKNOWN") + 1; + unkdbt.data = "UNKNOWN"; + + err_ret = 0; + + /* + * Allocate a buffer for overflow items. Start at one page; + * CDB___db_safe_goff will realloc as needed. + */ + if ((ret = CDB___os_malloc(dbp->dbenv, dbp->pgsize, NULL, &buf)) != 0) + return (ret); + + himark = dbp->pgsize; + for (i = 0;; i++) { + /* If we're not aggressive, break when we hit NUM_ENT(h). */ + if (!LF_ISSET(DB_AGGRESSIVE) && i >= NUM_ENT(h)) + break; + + /* Verify the current item. */ + ret = CDB___db_vrfy_inpitem(dbp, + h, pgno, i, 0, flags, &himark, NULL); + /* If this returned a fatality, it's time to break. */ + if (ret == DB_VERIFY_FATAL) + break; + + if (ret == 0) { + hk = P_ENTRY(h, i); + len = LEN_HKEYDATA(h, dbp->pgsize, i); + if ((u_int32_t)(hk + len - (u_int8_t *)h) > + dbp->pgsize) { + /* + * Item is unsafely large; either continue + * or set it to the whole page, depending on + * aggressiveness. + */ + if (!LF_ISSET(DB_AGGRESSIVE)) + continue; + len = dbp->pgsize - + (u_int32_t)(hk - (u_int8_t *)h); + err_ret = DB_VERIFY_BAD; + } + switch (HPAGE_PTYPE(hk)) { + default: + if (!LF_ISSET(DB_AGGRESSIVE)) + break; + err_ret = DB_VERIFY_BAD; + /* FALLTHROUGH */ + case H_KEYDATA: +keydata: memcpy(buf, HKEYDATA_DATA(hk), len); + dbt.size = len; + dbt.data = buf; + if ((ret = CDB___db_prdbt(&dbt, + 0, " ", handle, callback, 0, NULL)) != 0) + err_ret = ret; + break; + case H_OFFPAGE: + if (len < HOFFPAGE_SIZE) { + err_ret = DB_VERIFY_BAD; + continue; + } + memcpy(&dpgno, + HOFFPAGE_PGNO(hk), sizeof(dpgno)); + if ((ret = CDB___db_safe_goff(dbp, vdp, + dpgno, &dbt, &buf, flags)) != 0) { + err_ret = ret; + (void)CDB___db_prdbt(&unkdbt, 0, " ", + handle, callback, 0, NULL); + break; + } + if ((ret = CDB___db_prdbt(&dbt, + 0, " ", handle, callback, 0, NULL)) != 0) + err_ret = ret; + break; + case H_OFFDUP: + if (len < HOFFPAGE_SIZE) { + err_ret = DB_VERIFY_BAD; + continue; + } + memcpy(&dpgno, + HOFFPAGE_PGNO(hk), sizeof(dpgno)); + /* UNKNOWN iff pgno is bad or we're a key. */ + if (!IS_VALID_PGNO(dpgno) || (i % 2 == 0)) { + if ((ret = CDB___db_prdbt(&unkdbt, 0, " ", + handle, callback, 0, NULL)) != 0) + err_ret = ret; + } else if ((ret = CDB___db_salvage_duptree(dbp, + vdp, dpgno, &dbt, handle, callback, + flags | SA_SKIPFIRSTKEY)) != 0) + err_ret = ret; + break; + case H_DUPLICATE: + /* + * We're a key; printing dups will seriously + * foul the output. If we're being aggressive, + * pretend this is a key and let the app. + * programmer sort out the mess. + */ + if (i % 2 == 0) { + err_ret = ret; + if (LF_ISSET(DB_AGGRESSIVE)) + goto keydata; + break; + } + + /* Too small to have any data. */ + if (len < + HKEYDATA_SIZE(2 * sizeof(db_indx_t))) { + err_ret = DB_VERIFY_BAD; + continue; + } + + /* Loop until we hit the total length. */ + for (tlen = 0; tlen + sizeof(db_indx_t) < len; + tlen += dlen) { + tlen += sizeof(db_indx_t); + memcpy(&dlen, hk, sizeof(db_indx_t)); + /* + * If dlen is too long, print all the + * rest of the dup set in a chunk. + */ + if (dlen + tlen > len) + dlen = len - tlen; + memcpy(buf, hk + tlen, dlen); + dbt.size = dlen; + dbt.data = buf; + if ((ret = CDB___db_prdbt(&dbt, 0, " ", + handle, callback, 0, NULL)) != 0) + err_ret = ret; + tlen += sizeof(db_indx_t); + } + break; + } + } + } + + if ((t_ret = CDB___db_salvage_markdone(vdp, pgno)) != 0) + return (t_ret); + return ((ret == 0 && err_ret != 0) ? err_ret : ret); +} + +/* + * CDB___ham_meta2pgset -- + * Return the set of hash pages corresponding to the given + * known-good meta page. + * + * PUBLIC: int CDB___ham_meta2pgset __P((DB *, VRFY_DBINFO *, HMETA *, u_int32_t, + * PUBLIC: DB *)); + */ +int CDB___ham_meta2pgset(dbp, vdp, hmeta, flags, pgset) + DB *dbp; + VRFY_DBINFO *vdp; + HMETA *hmeta; + u_int32_t flags; + DB *pgset; +{ + PAGE *h; + db_pgno_t pgno; + u_int32_t bucket, totpgs; + int ret, val; + + /* + * We don't really need flags, but leave them for consistency with + * CDB___bam_meta2pgset. + */ + COMPQUIET(flags, 0); + + DB_ASSERT(pgset != NULL); + + totpgs = 0; + + /* + * Loop through all the buckets, pushing onto pgset the corresponding + * page(s) for each one. + */ + for (bucket = 0; bucket <= hmeta->max_bucket; bucket++) { + pgno = bucket + hmeta->spares[CDB___db_log2(bucket + 1)]; + + /* + * We know the initial pgno is safe because the spares array has + * been verified. + * + * Safely walk the list of pages in this bucket. + */ + for (;;) { + if ((ret = CDB_memp_fget(dbp->mpf, &pgno, 0, &h)) != 0) + return (ret); + if (TYPE(h) == P_HASH) { + + /* + * Make sure we don't go past the end of + * pgset. + */ + if (++totpgs > vdp->last_pgno) { + (void)CDB_memp_fput(dbp->mpf, h, 0); + return (DB_VERIFY_BAD); + } + if ((ret = + CDB___db_vrfy_pgset_inc(pgset, pgno)) != 0) + return (ret); + + pgno = NEXT_PGNO(h); + } else + pgno = PGNO_INVALID; + + if ((ret = CDB_memp_fput(dbp->mpf, h, 0)) != 0) + return (ret); + + /* If the new pgno is wonky, go onto the next bucket. */ + if (!IS_VALID_PGNO(pgno) || + pgno == PGNO_INVALID) + goto nextbucket; + + /* + * If we've touched this page before, we have a cycle; + * go on to the next bucket. + */ + if ((ret = CDB___db_vrfy_pgset_get(pgset, pgno, &val)) != 0) + return (ret); + if (val != 0) + goto nextbucket; + } +nextbucket: ; + } + return (0); +} + +/* + * __ham_dups_unsorted -- + * Takes a known-safe hash duplicate set and its total length. + * Returns 1 if there are out-of-order duplicates in this set, + * 0 if there are not. + */ +static int +__ham_dups_unsorted(dbp, buf, len) + DB *dbp; + u_int8_t *buf; + u_int32_t len; +{ + DBT a, b; + db_indx_t offset, dlen; + int (*func) __P((const DBT *, const DBT *)); + + memset(&a, 0, sizeof(DBT)); + memset(&b, 0, sizeof(DBT)); + + func = (dbp->dup_compare == NULL) ? CDB___bam_defcmp : dbp->dup_compare; + + /* + * Loop through the dup set until we hit the end or we find + * a pair of dups that's out of order. b is always the current + * dup, a the one before it. + */ + for (offset = 0; offset < len; offset += DUP_SIZE(dlen)) { + memcpy(&dlen, buf + offset, sizeof(db_indx_t)); + b.data = buf + offset + sizeof(db_indx_t); + b.size = dlen; + + if (a.data != NULL && func(&a, &b) > 0) + return (1); + + a.data = b.data; + a.size = b.size; + } + + return (0); +} |