#include "db_config.h"
#include "db_int.h"
#include "dbinc/db_page.h"
#include "dbinc/db_verify.h"
#include "dbinc/btree.h"
#include "dbinc/mp.h"
static int __bam_safe_getdata __P((DB *, DB_THREAD_INFO *,
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_THREAD_INFO *, 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));
int
__bam_vrfy_meta(dbp, vdp, meta, pgno, flags)
DB *dbp;
VRFY_DBINFO *vdp;
BTMETA *meta;
db_pgno_t pgno;
u_int32_t flags;
{
ENV *env;
VRFY_PAGEINFO *pip;
int isbad, t_ret, ret;
db_indx_t ovflsize;
env = dbp->env;
isbad = 0;
if ((ret = __db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0)
return (ret);
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;
}
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((env,
"Page %lu: nonsensical bt_minkey value %lu on metadata page",
(u_long)pgno, (u_long)meta->minkey));
} else
pip->bt_minkey = meta->minkey;
pip->re_pad = meta->re_pad;
pip->re_len = meta->re_len;
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((env,
"Page %lu: nonsensical root page %lu on metadata page",
(u_long)pgno, (u_long)meta->root));
} else
pip->root = meta->root;
if (F_ISSET(&meta->dbmeta, BTM_RENUMBER))
F_SET(pip, VRFY_IS_RRECNO);
if (F_ISSET(&meta->dbmeta, BTM_SUBDB)) {
if (F_ISSET(&meta->dbmeta, BTM_DUP) && pgno == PGNO_BASE_MD) {
isbad = 1;
EPRINT((env,
"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((env,
"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((env,
"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((env,
"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) {
isbad = 1;
EPRINT((env,
"Page %lu: re_len of %lu in non-fixed-length database",
(u_long)pgno, (u_long)pip->re_len));
}
err: if ((t_ret = __db_vrfy_putpageinfo(env, vdp, pip)) != 0 && ret == 0)
ret = t_ret;
if (LF_ISSET(DB_SALVAGE) &&
(t_ret = __db_salvage_markdone(vdp, pgno)) != 0 && ret == 0)
ret = t_ret;
return ((ret == 0 && isbad == 1) ? DB_VERIFY_BAD : ret);
}
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;
ENV *env;
VRFY_PAGEINFO *pip;
db_indx_t i;
int ret, t_ret, isbad;
u_int32_t re_len_guess, len;
env = dbp->env;
isbad = 0;
if ((ret = __db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0)
return (ret);
if (TYPE(h) != P_LRECNO) {
ret = __db_unknown_path(env, "__ram_vrfy_leaf");
goto err;
}
if ((ret = __db_vrfy_datapage(dbp, vdp, h, pgno, flags)) != 0) {
if (ret == DB_VERIFY_BAD)
isbad = 1;
else
goto err;
}
if ((ret = __bam_vrfy_inp(dbp,
vdp, h, pgno, &pip->entries, flags)) != 0)
goto err;
if (F_ISSET(pip, VRFY_HAS_DUPS)) {
EPRINT((env,
"Page %lu: Recno database has dups", (u_long)pgno));
ret = DB_VERIFY_BAD;
goto err;
}
re_len_guess = 0;
for (i = 0; i < NUM_ENT(h); i++) {
bk = GET_BKEYDATA(dbp, h, i);
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((env,
"Page %lu: nonsensical type for item %lu",
(u_long)pgno, (u_long)i));
continue;
}
if (re_len_guess == 0)
re_len_guess = len;
if (re_len_guess != len) {
re_len_guess = 0;
break;
}
}
pip->re_len = re_len_guess;
pip->rec_cnt = NUM_ENT(h);
err: if ((t_ret = __db_vrfy_putpageinfo(env, vdp, pip)) != 0 && ret == 0)
ret = t_ret;
return ((ret == 0 && isbad == 1) ? DB_VERIFY_BAD : ret);
}
int
__bam_vrfy(dbp, vdp, h, pgno, flags)
DB *dbp;
VRFY_DBINFO *vdp;
PAGE *h;
db_pgno_t pgno;
u_int32_t flags;
{
ENV *env;
VRFY_PAGEINFO *pip;
int ret, t_ret, isbad;
env = dbp->env;
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:
ret = __db_unknown_path(env, "__bam_vrfy");
goto err;
}
if ((ret = __db_vrfy_datapage(dbp, vdp, h, pgno, flags)) != 0) {
if (ret == DB_VERIFY_BAD)
isbad = 1;
else
goto err;
}
pip->rec_cnt = RE_NREC(h);
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((env,
"Page %lu: item order check unsafe: skipping",
(u_long)pgno));
} else if (!LF_ISSET(DB_NOORDERCHK) && (ret =
__bam_vrfy_itemorder(dbp,
vdp, vdp->thread_info, h, pgno, 0, 0, 0, flags)) != 0) {
if (ret == DB_VERIFY_BAD)
isbad = 1;
else
goto err;
}
err: if ((t_ret = __db_vrfy_putpageinfo(env, vdp, pip)) != 0 && ret == 0)
ret = t_ret;
return ((ret == 0 && isbad == 1) ? DB_VERIFY_BAD : ret);
}
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;
{
ENV *env;
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;
env = dbp->env;
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) {
ret = __db_unknown_path(env, "__ram_vrfy_inp");
goto err;
}
himark = dbp->pgsize;
if ((ret = __os_malloc(env, 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((env,
"Page %lu: entries listing %lu overlaps data",
(u_long)pgno, (u_long)i));
ret = DB_VERIFY_BAD;
goto err;
}
offset = inp[i];
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((env,
"Page %lu: bad offset %lu at index %lu",
(u_long)pgno, (u_long)offset, (u_long)i));
continue;
}
if (offset < himark)
himark = offset;
nentries++;
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((env,
"Page %lu: RINTERNAL structure at offset %lu referenced twice",
(u_long)pgno, (u_long)offset));
isbad = 1;
}
}
for (p = pagelayout + himark;
p < pagelayout + dbp->pgsize;
p += RINTERNAL_SIZE)
if (*p != 1) {
EPRINT((env,
"Page %lu: gap between items at offset %lu",
(u_long)pgno, (u_long)(p - pagelayout)));
isbad = 1;
}
if ((db_indx_t)himark != HOFFSET(h)) {
EPRINT((env,
"Page %lu: bad HOFFSET %lu, appears to be %lu",
(u_long)pgno, (u_long)(HOFFSET(h)), (u_long)himark));
isbad = 1;
}
*nentriesp = nentries;
err: if ((t_ret = __db_vrfy_putpageinfo(env, vdp, pip)) != 0 && ret == 0)
ret = t_ret;
if (pagelayout != NULL)
__os_free(env, pagelayout);
return ((ret == 0 && isbad == 1) ? DB_VERIFY_BAD : ret);
}
typedef enum { VRFY_ITEM_NOTSET=0, VRFY_ITEM_BEGIN, VRFY_ITEM_END } VRFY_ITEM;
static int
__bam_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;
{
BKEYDATA *bk;
BOVERFLOW *bo;
ENV *env;
VRFY_CHILDINFO child;
VRFY_ITEM *pagelayout;
VRFY_PAGEINFO *pip;
u_int32_t himark, offset;
u_int32_t i, endoff, nentries;
int isbad, initem, isdupitem, ret, t_ret;
env = dbp->env;
isbad = isdupitem = 0;
nentries = 0;
memset(&child, 0, sizeof(VRFY_CHILDINFO));
if ((ret = __db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0)
return (ret);
switch (TYPE(h)) {
case P_IBTREE:
case P_LBTREE:
case P_LDUP:
case P_LRECNO:
break;
default:
if (LF_ISSET(DB_SALVAGE))
break;
ret = __db_unknown_path(env, "__bam_vrfy_inp");
goto err;
}
himark = dbp->pgsize;
if ((ret = __os_calloc(
env, dbp->pgsize, sizeof(pagelayout[0]), &pagelayout)) != 0)
goto err;
for (i = 0; i < NUM_ENT(h); i++) {
switch (ret = __db_vrfy_inpitem(dbp,
h, pgno, i, 1, flags, &himark, &offset)) {
case 0:
break;
case DB_VERIFY_BAD:
isbad = 1;
continue;
case DB_VERIFY_FATAL:
isbad = 1;
goto err;
default:
DB_ASSERT(env, ret != 0);
break;
}
bk = GET_BKEYDATA(dbp, h, i);
if (pagelayout[offset] == VRFY_ITEM_NOTSET)
pagelayout[offset] = VRFY_ITEM_BEGIN;
else if (pagelayout[offset] == VRFY_ITEM_BEGIN) {
if ((i % P_INDX == 0) && (TYPE(h) == P_LBTREE)) {
F_SET(pip, VRFY_HAS_DUPS);
nentries++;
isdupitem = 1;
} else {
isbad = 1;
EPRINT((env, "Page %lu: duplicated item %lu",
(u_long)pgno, (u_long)i));
}
}
switch (B_TYPE(bk->type)) {
case B_KEYDATA:
if (TYPE(h) == P_IBTREE)
endoff = offset + BINTERNAL_SIZE(bk->len) - 1;
else
endoff = offset + BKEYDATA_SIZE(bk->len) - 1;
break;
case B_DUPLICATE:
F_SET(pip, VRFY_HAS_DUPS);
case B_OVERFLOW:
endoff = offset +
((TYPE(h) == P_IBTREE) ?
BINTERNAL_SIZE(BOVERFLOW_SIZE) :
BOVERFLOW_SIZE) - 1;
break;
default:
endoff = offset + BKEYDATA_SIZE(0) - 1;
break;
}
if (isdupitem && pagelayout[endoff] != VRFY_ITEM_END) {
EPRINT((env, "Page %lu: duplicated item %lu",
(u_long)pgno, (u_long)i));
isbad = 1;
} else if (pagelayout[endoff] == VRFY_ITEM_NOTSET)
pagelayout[endoff] = VRFY_ITEM_END;
isdupitem = 0;
if (B_DISSET(bk->type) && TYPE(h) != P_LRECNO) {
isbad = 1;
EPRINT((env, "Page %lu: item %lu marked deleted",
(u_long)pgno, (u_long)i));
}
switch (B_TYPE(bk->type)) {
case B_KEYDATA:
break;
case B_DUPLICATE:
if (TYPE(h) == P_IBTREE) {
isbad = 1;
EPRINT((env,
"Page %lu: duplicate page referenced by internal btree page at item %lu",
(u_long)pgno, (u_long)i));
break;
} else if (TYPE(h) == P_LRECNO) {
isbad = 1;
EPRINT((env,
"Page %lu: duplicate page referenced by recno page at item %lu",
(u_long)pgno, (u_long)i));
break;
}
case B_OVERFLOW:
bo = (TYPE(h) == P_IBTREE) ?
(BOVERFLOW *)(((BINTERNAL *)bk)->data) :
(BOVERFLOW *)bk;
if (B_TYPE(bk->type) == B_OVERFLOW)
if (bo->tlen > dbp->pgsize * vdp->last_pgno) {
isbad = 1;
EPRINT((env,
"Page %lu: impossible tlen %lu, item %lu",
(u_long)pgno,
(u_long)bo->tlen, (u_long)i));
break;
}
if (!IS_VALID_PGNO(bo->pgno) || bo->pgno == pgno ||
bo->pgno == PGNO_INVALID) {
isbad = 1;
EPRINT((env,
"Page %lu: offpage item %lu has bad pgno %lu",
(u_long)pgno, (u_long)i, (u_long)bo->pgno));
break;
}
child.pgno = bo->pgno;
child.type = (B_TYPE(bk->type) == B_OVERFLOW ?
V_OVERFLOW : V_DUPLICATE);
child.tlen = bo->tlen;
if ((ret = __db_vrfy_childput(vdp, pgno, &child)) != 0)
goto err;
break;
default:
isbad = 1;
EPRINT((env, "Page %lu: item %lu of invalid type %lu",
(u_long)pgno, (u_long)i, (u_long)B_TYPE(bk->type)));
break;
}
}
initem = 0;
for (i = himark; i < dbp->pgsize; i++)
if (initem == 0)
switch (pagelayout[i]) {
case VRFY_ITEM_NOTSET:
if (i != DB_ALIGN(i, sizeof(u_int32_t)))
continue;
isbad = 1;
EPRINT((env,
"Page %lu: gap between items at offset %lu",
(u_long)pgno, (u_long)i));
for (; pagelayout[i + 1] == VRFY_ITEM_NOTSET &&
(size_t)(i + 1) < dbp->pgsize; i++)
;
break;
case VRFY_ITEM_BEGIN:
if (i != DB_ALIGN(i, sizeof(u_int32_t))) {
isbad = 1;
EPRINT((env,
"Page %lu: offset %lu unaligned",
(u_long)pgno, (u_long)i));
}
initem = 1;
nentries++;
break;
case VRFY_ITEM_END:
isbad = 1;
EPRINT((env,
"Page %lu: overlapping items at offset %lu",
(u_long)pgno, (u_long)i));
break;
}
else
switch (pagelayout[i]) {
case VRFY_ITEM_NOTSET:
break;
case VRFY_ITEM_END:
initem = 0;
break;
case VRFY_ITEM_BEGIN:
isbad = 1;
EPRINT((env,
"Page %lu: overlapping items at offset %lu",
(u_long)pgno, (u_long)i));
break;
}
__os_free(env, pagelayout);
if ((db_indx_t)himark != HOFFSET(h)) {
EPRINT((env, "Page %lu: bad HOFFSET %lu, appears to be %lu",
(u_long)pgno, (u_long)HOFFSET(h), (u_long)himark));
isbad = 1;
}
err: if (nentriesp != NULL)
*nentriesp = nentries;
if ((t_ret = __db_vrfy_putpageinfo(env, vdp, pip)) != 0 && ret == 0)
ret = t_ret;
return ((isbad == 1 && ret == 0) ? DB_VERIFY_BAD : ret);
}
int
__bam_vrfy_itemorder(dbp, vdp, ip, h, pgno, nentries, ovflok, hasdups, flags)
DB *dbp;
VRFY_DBINFO *vdp;
DB_THREAD_INFO *ip;
PAGE *h;
db_pgno_t pgno;
u_int32_t nentries;
int ovflok, hasdups;
u_int32_t flags;
{
BINTERNAL *bi;
BKEYDATA *bk;
BOVERFLOW *bo;
BTREE *bt;
DBT dbta, dbtb, dup_1, dup_2, *p1, *p2, *tmp;
ENV *env;
VRFY_PAGEINFO *pip;
db_indx_t i, *inp;
int adj, cmp, freedup_1, freedup_2, isbad, ret, t_ret;
int (*dupfunc) __P((DB *, const DBT *, const DBT *));
int (*func) __P((DB *, const DBT *, const DBT *));
void *buf1, *buf2, *tmpbuf;
if (vdp != NULL) {
if ((ret = __db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0)
return (ret);
nentries = pip->entries;
} else
pip = NULL;
env = dbp->env;
ret = isbad = 0;
bo = NULL;
memset(&dbta, 0, sizeof(DBT));
F_SET(&dbta, DB_DBT_REALLOC);
memset(&dbtb, 0, sizeof(DBT));
F_SET(&dbtb, DB_DBT_REALLOC);
buf1 = buf2 = NULL;
DB_ASSERT(env, !LF_ISSET(DB_NOORDERCHK));
dupfunc = (dbp->dup_compare == NULL) ? __bam_defcmp : dbp->dup_compare;
if (TYPE(h) == P_LDUP)
func = dupfunc;
else {
func = __bam_defcmp;
if (dbp->bt_internal != NULL) {
bt = (BTREE *)dbp->bt_internal;
if (bt->bt_compare != NULL)
func = bt->bt_compare;
}
}
p1 = &dbta;
p2 = &dbtb;
inp = P_INP(dbp, h);
adj = (TYPE(h) == P_LBTREE) ? P_INDX : O_INDX;
for (i = (TYPE(h) == P_IBTREE) ? 1 : 0; i < nentries; i += adj) {
tmp = p1;
p1 = p2;
p2 = tmp;
tmpbuf = buf1;
buf1 = buf2;
buf2 = tmpbuf;
switch (TYPE(h)) {
case P_IBTREE:
bi = GET_BINTERNAL(dbp, h, i);
if (B_TYPE(bi->type) == B_OVERFLOW) {
bo = (BOVERFLOW *)(bi->data);
goto overflow;
} else {
p2->data = bi->data;
p2->size = bi->len;
}
#if 0
if (i == 0 && bi->len != 0) {
isbad = 1;
EPRINT((env,
"Page %lu: lowest key on internal page of nonzero length",
(u_long)pgno));
}
#endif
break;
case P_LBTREE:
case P_LDUP:
bk = GET_BKEYDATA(dbp, h, i);
if (B_TYPE(bk->type) == B_OVERFLOW) {
bo = (BOVERFLOW *)bk;
goto overflow;
} else {
p2->data = bk->data;
p2->size = bk->len;
}
break;
default:
ret = __db_unknown_path(env, "__bam_vrfy_itemorder");
goto err;
}
if (0) {
overflow: if (!ovflok) {
F_SET(pip, VRFY_INCOMPLETE);
goto err;
}
p2->data = buf2;
if ((ret = __db_goff(dbp, ip, NULL,
p2, bo->tlen, bo->pgno, NULL, NULL)) != 0) {
isbad = 1;
EPRINT((env,
"Page %lu: error %lu in fetching overflow item %lu",
(u_long)pgno, (u_long)ret, (u_long)i));
}
buf2 = p2->data;
}
if (p1->data != NULL && p2->data != NULL) {
cmp = inp[i] == inp[i - adj] ? 0 : func(dbp, p1, p2);
if (cmp > 0) {
isbad = 1;
EPRINT((env,
"Page %lu: out-of-order key at entry %lu",
(u_long)pgno, (u_long)i));
} else if (cmp == 0) {
if (inp[i] != inp[i - adj]) {
EPRINT((env,
"Page %lu: non-dup dup key at entry %lu",
(u_long)pgno, (u_long)i));
isbad = 1;
}
if (pip != NULL)
F_SET(pip, VRFY_HAS_DUPS);
else if (hasdups == 0) {
isbad = 1;
EPRINT((env,
"Page %lu: database with no duplicates has duplicated keys",
(u_long)pgno));
}
if (TYPE(h) == P_LBTREE) {
if (i + 1 >= (db_indx_t)nentries)
continue;
if (((ret = __bam_safe_getdata(dbp,
ip, h, i - 1, ovflok,
&dup_1, &freedup_1)) != 0) ||
((ret = __bam_safe_getdata(dbp,
ip, h, i + 1, ovflok,
&dup_2, &freedup_2)) != 0))
goto err;
if (dup_1.data == NULL ||
dup_2.data == NULL) {
DB_ASSERT(env, !ovflok);
F_SET(pip, VRFY_INCOMPLETE);
goto err;
}
if (dupfunc(dbp, &dup_1, &dup_2) > 0)
F_SET(pip, VRFY_DUPS_UNSORTED);
if (freedup_1)
__os_ufree(env, dup_1.data);
if (freedup_2)
__os_ufree(env, dup_2.data);
}
}
}
}
err: if (pip != NULL && ((t_ret =
__db_vrfy_putpageinfo(env, vdp, pip)) != 0) && ret == 0)
ret = t_ret;
if (buf1 != NULL)
__os_ufree(env, buf1);
if (buf2 != NULL)
__os_ufree(env, buf2);
return ((ret == 0 && isbad == 1) ? DB_VERIFY_BAD : ret);
}
int
__bam_vrfy_structure(dbp, vdp, meta_pgno, flags)
DB *dbp;
VRFY_DBINFO *vdp;
db_pgno_t meta_pgno;
u_int32_t flags;
{
DB *pgset;
ENV *env;
VRFY_PAGEINFO *mip, *rip;
db_pgno_t root, p;
int t_ret, ret;
u_int32_t nrecs, level, relen, stflags;
env = dbp->env;
mip = rip = 0;
pgset = vdp->pgset;
if ((ret = __db_vrfy_getpageinfo(vdp, meta_pgno, &mip)) != 0)
return (ret);
if ((ret = __db_vrfy_pgset_get(pgset,
vdp->thread_info, meta_pgno, (int *)&p)) != 0)
goto err;
if (p != 0) {
EPRINT((env,
"Page %lu: btree metadata page observed twice",
(u_long)meta_pgno));
ret = DB_VERIFY_BAD;
goto err;
}
if ((ret =
__db_vrfy_pgset_inc(pgset, vdp->thread_info, meta_pgno)) != 0)
goto err;
root = mip->root;
if (root == 0) {
EPRINT((env,
"Page %lu: btree metadata page has no root",
(u_long)meta_pgno));
ret = DB_VERIFY_BAD;
goto err;
}
if ((ret = __db_vrfy_getpageinfo(vdp, root, &rip)) != 0)
goto err;
switch (rip->type) {
case P_IBTREE:
case P_LBTREE:
stflags = flags | DB_ST_TOPLEVEL;
if (F_ISSET(mip, VRFY_HAS_DUPS))
stflags |= DB_ST_DUPOK;
if (F_ISSET(mip, VRFY_HAS_DUPSORT))
stflags |= DB_ST_DUPSORT;
if (F_ISSET(mip, VRFY_HAS_RECNUMS))
stflags |= DB_ST_RECNUM;
ret = __bam_vrfy_subtree(dbp,
vdp, root, NULL, NULL, stflags, NULL, NULL, NULL);
break;
case P_IRECNO:
case P_LRECNO:
stflags =
flags | DB_ST_RECNUM | DB_ST_IS_RECNO | DB_ST_TOPLEVEL;
if (mip->re_len > 0)
stflags |= DB_ST_RELEN;
if ((ret = __bam_vrfy_subtree(dbp, vdp,
root, NULL, NULL, stflags, &level, &nrecs, &relen)) != 0)
goto err;
if (mip->re_len > 0 && relen > 0 && mip->re_len != relen) {
EPRINT((env,
"Page %lu: recno database has bad re_len %lu",
(u_long)meta_pgno, (u_long)relen));
ret = DB_VERIFY_BAD;
goto err;
}
ret = 0;
break;
case P_LDUP:
EPRINT((env,
"Page %lu: duplicate tree referenced from metadata page",
(u_long)meta_pgno));
ret = DB_VERIFY_BAD;
break;
default:
EPRINT((env,
"Page %lu: btree root of incorrect type %lu on metadata page",
(u_long)meta_pgno, (u_long)rip->type));
ret = DB_VERIFY_BAD;
break;
}
err: if (mip != NULL && ((t_ret =
__db_vrfy_putpageinfo(env, vdp, mip)) != 0) && ret == 0)
ret = t_ret;
if (rip != NULL && ((t_ret =
__db_vrfy_putpageinfo(env, vdp, rip)) != 0) && ret == 0)
ret = t_ret;
return (ret);
}
int
__bam_vrfy_subtree(dbp, vdp, pgno, l, r, flags, levelp, nrecsp, relenp)
DB *dbp;
VRFY_DBINFO *vdp;
db_pgno_t pgno;
void *l, *r;
u_int32_t flags, *levelp, *nrecsp, *relenp;
{
BINTERNAL *li, *ri;
DB *pgset;
DBC *cc;
DB_MPOOLFILE *mpf;
ENV *env;
PAGE *h;
VRFY_CHILDINFO *child;
VRFY_PAGEINFO *pip;
db_indx_t i;
db_pgno_t next_pgno, prev_pgno;
db_recno_t child_nrecs, nrecs;
u_int32_t child_level, child_relen, j, level, relen, stflags;
u_int8_t leaf_type;
int (*func) __P((DB *, const DBT *, const DBT *));
int isbad, p, ret, t_ret, toplevel;
if (levelp != NULL)
*levelp = 0;
if (nrecsp != NULL)
*nrecsp = 0;
env = dbp->env;
mpf = dbp->mpf;
h = NULL;
next_pgno = prev_pgno = PGNO_INVALID;
nrecs = 0;
relen = 0;
leaf_type = P_INVALID;
isbad = ret = 0;
if (!LF_ISSET(DB_SALVAGE))
__db_vrfy_struct_feedback(dbp, vdp);
if ((ret = __db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0)
return (ret);
cc = NULL;
level = pip->bt_level;
toplevel = LF_ISSET(DB_ST_TOPLEVEL) ? 1 : 0;
LF_CLR(DB_ST_TOPLEVEL);
if (toplevel) {
prev_pgno = vdp->prev_pgno;
next_pgno = vdp->next_pgno;
leaf_type = vdp->leaf_type;
vdp->next_pgno = vdp->prev_pgno = PGNO_INVALID;
vdp->leaf_type = P_INVALID;
}
switch (pip->type) {
case P_LRECNO:
case P_LDUP:
case P_LBTREE:
if (vdp->leaf_type == P_INVALID) {
vdp->leaf_type = pip->type;
if (pip->prev_pgno != PGNO_INVALID)
goto bad_prev;
} else {
if (pip->type != vdp->leaf_type) {
isbad = 1;
EPRINT((env,
"Page %lu: unexpected page type %lu found in leaf chain (expected %lu)",
(u_long)pip->pgno, (u_long)pip->type,
(u_long)vdp->leaf_type));
}
if (!F_ISSET(vdp, VRFY_LEAFCHAIN_BROKEN)) {
if (pip->pgno != vdp->next_pgno) {
isbad = 1;
EPRINT((env,
"Page %lu: incorrect next_pgno %lu found in leaf chain (should be %lu)",
(u_long)vdp->prev_pgno,
(u_long)vdp->next_pgno,
(u_long)pip->pgno));
}
if (pip->prev_pgno != vdp->prev_pgno) {
bad_prev: isbad = 1;
EPRINT((env,
"Page %lu: incorrect prev_pgno %lu found in leaf chain (should be %lu)",
(u_long)pip->pgno,
(u_long)pip->prev_pgno,
(u_long)vdp->prev_pgno));
}
}
}
vdp->prev_pgno = pip->pgno;
vdp->next_pgno = pip->next_pgno;
F_CLR(vdp, VRFY_LEAFCHAIN_BROKEN);
if ((ret = __db_vrfy_childcursor(vdp, &cc)) != 0)
goto err;
for (ret = __db_vrfy_ccset(cc, pgno, &child); ret == 0;
ret = __db_vrfy_ccnext(cc, &child))
if (child->type == V_OVERFLOW &&
(ret = __db_vrfy_ovfl_structure(dbp, vdp,
child->pgno, child->tlen,
flags | DB_ST_OVFL_LEAF)) != 0) {
if (ret == DB_VERIFY_BAD)
isbad = 1;
else
goto done;
}
if ((ret = __db_vrfy_ccclose(cc)) != 0)
goto err;
cc = NULL;
if (pip->type == P_LRECNO) {
if (!LF_ISSET(DB_ST_IS_RECNO) &&
!(LF_ISSET(DB_ST_DUPOK) &&
!LF_ISSET(DB_ST_DUPSORT))) {
isbad = 1;
EPRINT((env,
"Page %lu: recno leaf page non-recno tree",
(u_long)pgno));
goto done;
}
goto leaf;
} else if (LF_ISSET(DB_ST_IS_RECNO)) {
isbad = 1;
EPRINT((env,
"Page %lu: non-recno leaf page in recno tree",
(u_long)pgno));
goto done;
}
if (pip->type == P_LDUP)
goto leaf;
if (F_ISSET(pip, VRFY_HAS_DUPS)) {
if (!LF_ISSET(DB_ST_DUPOK)) {
isbad = 1;
EPRINT((env,
"Page %lu: duplicates in non-dup btree",
(u_long)pgno));
} else {
if ((ret =
__db_vrfy_childcursor(vdp, &cc)) != 0)
goto err;
for (ret = __db_vrfy_ccset(cc, pgno, &child);
ret == 0;
ret = __db_vrfy_ccnext(cc, &child)) {
stflags =
flags | DB_ST_RECNUM | DB_ST_DUPSET;
if (child->type == V_DUPLICATE) {
if ((ret = __db_vrfy_duptype(
dbp, vdp, child->pgno,
stflags)) != 0) {
isbad = 1;
continue;
}
if ((ret = __bam_vrfy_subtree(
dbp, vdp, child->pgno,
NULL, NULL,
stflags | DB_ST_TOPLEVEL,
NULL, NULL, NULL)) != 0) {
if (ret ==
DB_VERIFY_BAD)
isbad = 1;
else
goto err;
}
}
}
if ((ret = __db_vrfy_ccclose(cc)) != 0)
goto err;
cc = NULL;
if (F_ISSET(pip, VRFY_DUPS_UNSORTED) &&
LF_ISSET(DB_ST_DUPSORT)) {
isbad = 1;
EPRINT((env,
"Page %lu: unsorted duplicate set in sorted-dup database",
(u_long)pgno));
}
}
}
goto leaf;
case P_IBTREE:
case P_IRECNO:
break;
default:
if (F_ISSET(pip, VRFY_IS_ALLZEROES))
ZEROPG_ERR_PRINT(env, pgno, "btree or recno page");
else
EPRINT((env,
"Page %lu: btree or recno page is of inappropriate type %lu",
(u_long)pgno, (u_long)pip->type));
F_SET(vdp, VRFY_LEAFCHAIN_BROKEN);
ret = DB_VERIFY_BAD;
goto err;
}
if ((ret = __db_vrfy_childcursor(vdp, &cc)) != 0)
goto err;
for (ret = __db_vrfy_ccset(cc, pgno, &child); ret == 0;
ret = __db_vrfy_ccnext(cc, &child))
if (child->type == V_RECNO) {
if (pip->type != P_IRECNO) {
ret = __db_unknown_path(
env, "__bam_vrfy_subtree");
goto err;
}
if ((ret = __bam_vrfy_subtree(dbp, vdp, child->pgno,
NULL, NULL, flags, &child_level, &child_nrecs,
&child_relen)) != 0) {
if (ret == DB_VERIFY_BAD)
isbad = 1;
else
goto done;
}
if (LF_ISSET(DB_ST_RELEN)) {
if (relen == 0)
relen = child_relen;
else if (child_relen > 0 &&
relen != child_relen) {
isbad = 1;
EPRINT((env,
"Page %lu: recno page returned bad re_len %lu",
(u_long)child->pgno,
(u_long)child_relen));
}
if (relenp)
*relenp = relen;
}
if (LF_ISSET(DB_ST_RECNUM)) {
if (child->nrecs != child_nrecs) {
isbad = 1;
EPRINT((env,
"Page %lu: record count incorrect: actual %lu, in record %lu",
(u_long)child->pgno,
(u_long)child_nrecs,
(u_long)child->nrecs));
}
nrecs += child_nrecs;
}
if (isbad == 0 && level != child_level + 1) {
isbad = 1;
EPRINT((env,
"Page %lu: recno level incorrect: got %lu, expected %lu",
(u_long)child->pgno, (u_long)child_level,
(u_long)(level - 1)));
}
} else if (child->type == V_OVERFLOW) {
DB_ASSERT(env, child->refcnt >= 1);
if (child->refcnt > 2) {
isbad = 1;
EPRINT((env,
"Page %lu: overflow page %lu referenced more than twice from internal page",
(u_long)pgno, (u_long)child->pgno));
} else
for (j = 0; j < child->refcnt; j++)
if ((ret = __db_vrfy_ovfl_structure(dbp,
vdp, child->pgno, child->tlen,
flags)) != 0) {
if (ret == DB_VERIFY_BAD)
isbad = 1;
else
goto done;
}
}
if ((ret = __db_vrfy_ccclose(cc)) != 0)
goto err;
cc = NULL;
if (pip->type == P_IRECNO)
goto done;
if (h == NULL &&
(ret = __memp_fget(mpf, &pgno, vdp->thread_info, NULL, 0, &h)) != 0)
goto err;
for (i = 0; i < pip->entries; i += O_INDX) {
li = GET_BINTERNAL(dbp, h, i);
ri = (i + O_INDX < pip->entries) ?
GET_BINTERNAL(dbp, h, i + O_INDX) : r;
if ((ret = __bam_vrfy_subtree(dbp, vdp, li->pgno,
i == 0 ? NULL : li, ri, flags, &child_level,
&child_nrecs, NULL)) != 0) {
if (ret == DB_VERIFY_BAD)
isbad = 1;
else
goto done;
}
if (LF_ISSET(DB_ST_RECNUM)) {
nrecs += child_nrecs;
if (li->nrecs != child_nrecs) {
isbad = 1;
EPRINT((env,
"Page %lu: item %lu has incorrect record count of %lu, should be %lu",
(u_long)pgno, (u_long)i, (u_long)li->nrecs,
(u_long)child_nrecs));
}
}
if (level != child_level + 1) {
isbad = 1;
EPRINT((env,
"Page %lu: Btree level incorrect: got %lu, expected %lu",
(u_long)li->pgno,
(u_long)child_level, (u_long)(level - 1)));
}
}
if (0) {
leaf: level = LEAFLEVEL;
if (LF_ISSET(DB_ST_RECNUM))
nrecs = pip->rec_cnt;
if (LF_ISSET(DB_ST_RELEN) && relenp)
*relenp = pip->re_len;
}
done: if (F_ISSET(pip, VRFY_INCOMPLETE) && isbad == 0 && ret == 0) {
if (h == NULL && (ret = __memp_fget(mpf, &pgno,
vdp->thread_info, NULL, 0, &h)) != 0)
goto err;
if ((ret = __bam_vrfy_itemorder(dbp,
vdp, vdp->thread_info, h, pgno, 0, 1, 0, flags)) != 0)
goto err;
F_CLR(pip, VRFY_INCOMPLETE);
}
if (isbad == 0 && ret == 0) {
if (h == NULL && (ret = __memp_fget(mpf, &pgno,
vdp->thread_info, NULL, 0, &h)) != 0)
goto err;
if (NUM_ENT(h) == 0 && ISINTERNAL(h)) {
isbad = 1;
EPRINT((env,
"Page %lu: internal page is empty and should not be",
(u_long)pgno));
goto err;
}
}
if (isbad == 0 && ret == 0 && !LF_ISSET(DB_NOORDERCHK) &&
pip->type != P_IRECNO && pip->type != P_LRECNO) {
if (h == NULL && (ret = __memp_fget(mpf, &pgno,
vdp->thread_info, NULL, 0, &h)) != 0)
goto err;
func = LF_ISSET(DB_ST_DUPSET) ? dbp->dup_compare :
((BTREE *)dbp->bt_internal)->bt_compare;
if (func == NULL)
func = __bam_defcmp;
if ((ret = __bam_vrfy_treeorder(dbp,
vdp->thread_info, h, l, r, func, flags)) != 0) {
if (ret == DB_VERIFY_BAD)
isbad = 1;
else
goto err;
}
}
if (LF_ISSET(DB_ST_RECNUM) && nrecs != pip->rec_cnt && toplevel) {
isbad = 1;
EPRINT((env,
"Page %lu: bad record count: has %lu records, claims %lu",
(u_long)pgno, (u_long)nrecs, (u_long)pip->rec_cnt));
}
if (levelp)
*levelp = level;
if (nrecsp)
*nrecsp = nrecs;
pgset = vdp->pgset;
if ((ret = __db_vrfy_pgset_get(pgset,
vdp->thread_info, pgno, &p)) != 0)
goto err;
if (p != 0) {
isbad = 1;
EPRINT((env, "Page %lu: linked twice", (u_long)pgno));
} else if ((ret =
__db_vrfy_pgset_inc(pgset, vdp->thread_info, pgno)) != 0)
goto err;
if (toplevel)
if (vdp->next_pgno != PGNO_INVALID) {
isbad = 1;
EPRINT((env, "Page %lu: unterminated leaf chain",
(u_long)vdp->prev_pgno));
}
err: if (toplevel) {
vdp->next_pgno = next_pgno;
vdp->prev_pgno = prev_pgno;
vdp->leaf_type = leaf_type;
}
if (h != NULL && (t_ret = __memp_fput(mpf,
vdp->thread_info, h, DB_PRIORITY_UNCHANGED)) != 0 && ret == 0)
ret = t_ret;
if ((t_ret = __db_vrfy_putpageinfo(env, vdp, pip)) != 0 && ret == 0)
ret = t_ret;
if (cc != NULL && ((t_ret = __db_vrfy_ccclose(cc)) != 0) && ret == 0)
ret = t_ret;
return ((ret == 0 && isbad == 1) ? DB_VERIFY_BAD : ret);
}
static int
__bam_vrfy_treeorder(dbp, ip, h, lp, rp, func, flags)
DB *dbp;
DB_THREAD_INFO *ip;
PAGE *h;
BINTERNAL *lp, *rp;
int (*func) __P((DB *, const DBT *, const DBT *));
u_int32_t flags;
{
BOVERFLOW *bo;
DBT dbt;
ENV *env;
db_indx_t last;
int ret, cmp;
env = dbp->env;
memset(&dbt, 0, sizeof(DBT));
F_SET(&dbt, DB_DBT_MALLOC);
ret = 0;
if (NUM_ENT(h) == 0)
return (0);
switch (TYPE(h)) {
case P_IBTREE:
case P_LDUP:
last = NUM_ENT(h) - O_INDX;
break;
case P_LBTREE:
last = NUM_ENT(h) - P_INDX;
break;
default:
return (__db_unknown_path(env, "__bam_vrfy_treeorder"));
}
if (lp != NULL && TYPE(h) != P_IBTREE) {
if (lp->type == B_KEYDATA) {
dbt.data = lp->data;
dbt.size = lp->len;
} else if (lp->type == B_OVERFLOW) {
bo = (BOVERFLOW *)lp->data;
if ((ret = __db_goff(dbp, ip, NULL, &dbt,
bo->tlen, bo->pgno, NULL, NULL)) != 0)
return (ret);
} else
return (
__db_unknown_path(env, "__bam_vrfy_treeorder"));
if ((ret = __bam_cmp(dbp, ip,
NULL, &dbt, h, 0, func, &cmp)) == 0) {
if (cmp > 0) {
EPRINT((env,
"Page %lu: first item on page sorted greater than parent entry",
(u_long)PGNO(h)));
ret = DB_VERIFY_BAD;
}
} else
EPRINT((env,
"Page %lu: first item on page had comparison error",
(u_long)PGNO(h)));
if (dbt.data != lp->data)
__os_ufree(env, dbt.data);
if (ret != 0)
return (ret);
}
if (rp != NULL) {
if (rp->type == B_KEYDATA) {
dbt.data = rp->data;
dbt.size = rp->len;
} else if (rp->type == B_OVERFLOW) {
bo = (BOVERFLOW *)rp->data;
if ((ret = __db_goff(dbp, ip, NULL, &dbt,
bo->tlen, bo->pgno, NULL, NULL)) != 0)
return (ret);
} else
return (
__db_unknown_path(env, "__bam_vrfy_treeorder"));
if ((ret = __bam_cmp(dbp, ip,
NULL, &dbt, h, last, func, &cmp)) == 0) {
if (cmp < 0) {
EPRINT((env,
"Page %lu: last item on page sorted greater than parent entry",
(u_long)PGNO(h)));
ret = DB_VERIFY_BAD;
}
} else
EPRINT((env,
"Page %lu: last item on page had comparison error",
(u_long)PGNO(h)));
if (dbt.data != rp->data)
__os_ufree(env, dbt.data);
}
return (ret);
}
int
__bam_salvage(dbp, vdp, pgno, pgtype, h, handle, callback, key, flags)
DB *dbp;
VRFY_DBINFO *vdp;
db_pgno_t pgno;
u_int32_t pgtype;
PAGE *h;
void *handle;
int (*callback) __P((void *, const void *));
DBT *key;
u_int32_t flags;
{
BKEYDATA *bk;
BOVERFLOW *bo;
DBT dbt, repldbt, unknown_key, unknown_data;
ENV *env;
VRFY_ITEM *pgmap;
db_indx_t i, last, beg, end, *inp;
db_pgno_t ovflpg;
u_int32_t himark;
void *ovflbuf;
int adj, ret, t_ret, t2_ret;
env = dbp->env;
ovflbuf = pgmap = NULL;
inp = P_INP(dbp, h);
memset(&dbt, 0, sizeof(DBT));
dbt.flags = DB_DBT_REALLOC;
memset(&repldbt, 0, sizeof(DBT));
DB_INIT_DBT(unknown_key, "UNKNOWN_KEY", sizeof("UNKNOWN_KEY") - 1);
DB_INIT_DBT(unknown_data, "UNKNOWN_DATA", sizeof("UNKNOWN_DATA") - 1);
if ((ret = __os_malloc(env, dbp->pgsize, &ovflbuf)) != 0)
goto err;
if (LF_ISSET(DB_AGGRESSIVE) && (ret =
__os_calloc(env, dbp->pgsize, sizeof(pgmap[0]), &pgmap)) != 0)
goto err;
himark = dbp->pgsize;
for (i = 0, last = UINT16_MAX;; i += O_INDX) {
if (!LF_ISSET(DB_AGGRESSIVE) && i >= NUM_ENT(h))
break;
t_ret =
__db_vrfy_inpitem(dbp, h, pgno, i, 1, flags, &himark, NULL);
if (t_ret != 0) {
if (pgtype == P_LBTREE && i % P_INDX == 1 &&
last == i - 1 && (t2_ret = __db_vrfy_prdbt(
&unknown_data,
0, " ", handle, callback, 0, vdp)) != 0) {
if (ret == 0)
ret = t2_ret;
goto err;
}
if (t_ret == DB_VERIFY_FATAL) {
if (i < NUM_ENT(h) && ret == 0)
ret = DB_VERIFY_BAD;
break;
}
continue;
}
bk = GET_BKEYDATA(dbp, h, i);
if (!LF_ISSET(DB_AGGRESSIVE) && B_DISSET(bk->type))
continue;
if (pgtype == P_LBTREE && i % P_INDX == 1 &&
last != i - 1 && (t_ret = __db_vrfy_prdbt(
&unknown_key, 0, " ", handle, callback, 0, vdp)) != 0) {
if (ret == 0)
ret = t_ret;
goto err;
}
last = i;
if (key != NULL && (i != 0 || !LF_ISSET(DB_SA_SKIPFIRSTKEY)))
if ((t_ret = __db_vrfy_prdbt(key,
0, " ", handle, callback, 0, vdp)) != 0) {
if (ret == 0)
ret = t_ret;
goto err;
}
beg = end = inp[i];
switch (B_TYPE(bk->type)) {
case B_DUPLICATE:
if (pgtype == P_IBTREE)
break;
end = beg + BOVERFLOW_SIZE - 1;
if (pgtype != P_LBTREE)
break;
bo = (BOVERFLOW *)bk;
if (!IS_VALID_PGNO(bo->pgno) || (i % P_INDX == 0)) {
if ((t_ret = __db_vrfy_prdbt(&unknown_key,
0, " ", handle, callback, 0, vdp)) != 0) {
if (ret == 0)
ret = t_ret;
goto err;
}
break;
}
if ((t_ret = __db_salvage_duptree(dbp,
vdp, bo->pgno, &dbt, handle, callback,
flags | DB_SA_SKIPFIRSTKEY)) != 0 && ret == 0)
ret = t_ret;
break;
case B_KEYDATA:
if (pgtype == P_IBTREE)
break;
end = (db_indx_t)DB_ALIGN(
beg + bk->len, sizeof(u_int32_t)) - 1;
dbt.data = bk->data;
dbt.size = bk->len;
if ((t_ret = __db_vrfy_prdbt(&dbt,
0, " ", handle, callback, 0, vdp)) != 0) {
if (ret == 0)
ret = t_ret;
goto err;
}
break;
case B_OVERFLOW:
if (pgtype != P_IBTREE)
end = beg + BOVERFLOW_SIZE - 1;
bo = (BOVERFLOW *)bk;
adj = pgtype == P_IBTREE ? O_INDX : P_INDX;
if (i > adj - 1 && i % adj == 0 &&
inp[i] == inp[i - adj]) {
DB_ASSERT(env, pgtype != P_IBTREE);
dbt = repldbt;
} else {
if (pgtype == P_IBTREE) {
ovflpg = ((BOVERFLOW *)
((BINTERNAL *)bk)->data)->pgno;
if (__db_salvage_isdone(vdp,
ovflpg) == 0 && (t_ret =
__db_safe_goff(dbp, vdp, ovflpg,
&dbt, &ovflbuf, flags)) != 0 &&
ret == 0)
ret = t_ret;
break;
}
if ((t_ret = __db_safe_goff(dbp, vdp, bo->pgno,
&dbt, &ovflbuf, flags)) != 0 && ret == 0)
ret = t_ret;
if (i % P_INDX == 0) {
if ((ret = __os_realloc(env,
dbt.size, &repldbt.data)) != 0)
goto err;
memcpy(repldbt.data, dbt.data,
dbt.size);
repldbt.size = dbt.size;
}
}
if ((t_ret = __db_vrfy_prdbt(
t_ret == 0 ? &dbt : &unknown_key,
0, " ", handle, callback, 0, vdp)) != 0 && ret == 0)
ret = t_ret;
break;
default:
t_ret = __db_unknown_path(env, "__bam_salvage");
if (ret == 0)
ret = t_ret;
goto err;
}
if (LF_ISSET(DB_AGGRESSIVE) && pgtype != P_IBTREE) {
pgmap[beg] = VRFY_ITEM_BEGIN;
pgmap[end] = VRFY_ITEM_END;
}
}
err: if (pgmap != NULL)
__os_free(env, pgmap);
if (ovflbuf != NULL)
__os_free(env, ovflbuf);
if (repldbt.data != NULL)
__os_free(env, repldbt.data);
if ((t_ret = __db_salvage_markdone(vdp, pgno)) != 0 && ret == 0)
ret = t_ret;
return (ret);
}
int
__bam_salvage_walkdupint(dbp, vdp, h, key, handle, callback, flags)
DB *dbp;
VRFY_DBINFO *vdp;
PAGE *h;
DBT *key;
void *handle;
int (*callback) __P((void *, const void *));
u_int32_t flags;
{
BINTERNAL *bi;
ENV *env;
RINTERNAL *ri;
int ret, t_ret;
db_indx_t i;
env = dbp->env;
ret = 0;
for (i = 0; i < NUM_ENT(h); i++) {
switch (TYPE(h)) {
case P_IBTREE:
bi = GET_BINTERNAL(dbp, h, i);
if ((t_ret = __db_salvage_duptree(dbp,
vdp, bi->pgno, key, handle, callback, flags)) != 0)
ret = t_ret;
break;
case P_IRECNO:
ri = GET_RINTERNAL(dbp, h, i);
if ((t_ret = __db_salvage_duptree(dbp,
vdp, ri->pgno, key, handle, callback, flags)) != 0)
ret = t_ret;
break;
default:
return (__db_unknown_path(
env, "__bam_salvage_walkdupint"));
}
flags &= ~LF_ISSET(DB_SA_SKIPFIRSTKEY);
}
return (ret);
}
int
__bam_meta2pgset(dbp, vdp, btmeta, flags, pgset)
DB *dbp;
VRFY_DBINFO *vdp;
BTMETA *btmeta;
u_int32_t flags;
DB *pgset;
{
BINTERNAL *bi;
DB_MPOOLFILE *mpf;
PAGE *h;
RINTERNAL *ri;
db_pgno_t current, p;
int err_ret, ret;
DB_ASSERT(dbp->env, pgset != NULL);
mpf = dbp->mpf;
h = NULL;
ret = err_ret = 0;
for (current = btmeta->root;;) {
if (!IS_VALID_PGNO(current) || current == PGNO(btmeta)) {
err_ret = DB_VERIFY_BAD;
goto err;
}
if ((ret = __memp_fget(mpf, ¤t,
vdp->thread_info, NULL, 0, &h)) != 0) {
err_ret = ret;
goto err;
}
switch (TYPE(h)) {
case P_IBTREE:
case P_IRECNO:
if ((ret = __bam_vrfy(dbp,
vdp, h, current, flags | DB_NOORDERCHK)) != 0) {
err_ret = ret;
goto err;
}
if (TYPE(h) == P_IBTREE) {
bi = GET_BINTERNAL(dbp, h, 0);
current = bi->pgno;
} else {
ri = GET_RINTERNAL(dbp, h, 0);
current = ri->pgno;
}
break;
case P_LBTREE:
case P_LRECNO:
goto traverse;
default:
err_ret = DB_VERIFY_BAD;
goto err;
}
if ((ret = __memp_fput(mpf,
vdp->thread_info, h, DB_PRIORITY_UNCHANGED)) != 0)
err_ret = ret;
h = NULL;
}
traverse:
while (IS_VALID_PGNO(current) && current != PGNO_INVALID) {
if (h == NULL && (ret = __memp_fget(mpf,
¤t, vdp->thread_info, NULL, 0, &h)) != 0) {
err_ret = ret;
break;
}
if ((ret = __db_vrfy_pgset_get(pgset,
vdp->thread_info, current, (int *)&p)) != 0)
goto err;
if (p != 0) {
break;
}
if ((ret =
__db_vrfy_pgset_inc(pgset, vdp->thread_info, current)) != 0)
goto err;
current = NEXT_PGNO(h);
if ((ret = __memp_fput(mpf,
vdp->thread_info, h, DB_PRIORITY_UNCHANGED)) != 0)
err_ret = ret;
h = NULL;
}
err: if (h != NULL)
(void)__memp_fput(mpf,
vdp->thread_info, h, DB_PRIORITY_UNCHANGED);
return (ret == 0 ? err_ret : ret);
}
static int
__bam_safe_getdata(dbp, ip, h, i, ovflok, dbt, freedbtp)
DB *dbp;
DB_THREAD_INFO *ip;
PAGE *h;
u_int32_t i;
int ovflok;
DBT *dbt;
int *freedbtp;
{
BKEYDATA *bk;
BOVERFLOW *bo;
memset(dbt, 0, sizeof(DBT));
*freedbtp = 0;
bk = GET_BKEYDATA(dbp, h, i);
if (B_TYPE(bk->type) == B_OVERFLOW) {
if (!ovflok)
return (0);
bo = (BOVERFLOW *)bk;
F_SET(dbt, DB_DBT_MALLOC);
*freedbtp = 1;
return (__db_goff(dbp, ip, NULL, dbt,
bo->tlen, bo->pgno, NULL, NULL));
} else {
dbt->data = bk->data;
dbt->size = bk->len;
}
return (0);
}