#include "db_config.h"
#ifndef lint
static const char revid[] = "$Id: db_vrfy.c,v 1.2 2004/03/30 01:21:24 jtownsen Exp $";
#endif
#ifndef NO_SYSTEM_INCLUDES
#include <sys/types.h>
#include <string.h>
#endif
#include "db_int.h"
#include "dbinc/db_page.h"
#include "dbinc/db_shash.h"
#include "dbinc/db_swap.h"
#include "dbinc/db_verify.h"
#include "dbinc/btree.h"
#include "dbinc/hash.h"
#include "dbinc/lock.h"
#include "dbinc/mp.h"
#include "dbinc/qam.h"
#include "dbinc/txn.h"
static u_int __db_guesspgsize __P((DB_ENV *, DB_FH *));
static int __db_is_valid_magicno __P((u_int32_t, DBTYPE *));
static int __db_is_valid_pagetype __P((u_int32_t));
static int __db_meta2pgset
__P((DB *, VRFY_DBINFO *, db_pgno_t, u_int32_t, DB *));
static int __db_salvage_subdbpg __P((DB *, VRFY_DBINFO *,
PAGE *, void *, int (*)(void *, const void *), u_int32_t));
static int __db_salvage_subdbs __P((DB *, VRFY_DBINFO *, void *,
int(*)(void *, const void *), u_int32_t, int *));
static int __db_salvage_unknowns __P((DB *, VRFY_DBINFO *, void *,
int (*)(void *, const void *), u_int32_t));
static int __db_verify_arg __P((DB *, const char *, u_int32_t));
static int __db_vrfy_freelist
__P((DB *, VRFY_DBINFO *, db_pgno_t, u_int32_t));
static int __db_vrfy_invalid
__P((DB *, VRFY_DBINFO *, PAGE *, db_pgno_t, u_int32_t));
static int __db_vrfy_orderchkonly __P((DB *,
VRFY_DBINFO *, const char *, const char *, u_int32_t));
static int __db_vrfy_pagezero __P((DB *, VRFY_DBINFO *, DB_FH *, u_int32_t));
static int __db_vrfy_subdbs
__P((DB *, VRFY_DBINFO *, const char *, u_int32_t));
static int __db_vrfy_structure
__P((DB *, VRFY_DBINFO *, const char *, db_pgno_t, u_int32_t));
static int __db_vrfy_walkpages __P((DB *, VRFY_DBINFO *,
void *, int (*)(void *, const void *), u_int32_t));
int
__db_verify_pp(dbp, file, database, outfile, flags)
DB *dbp;
const char *file, *database;
FILE *outfile;
u_int32_t flags;
{
return (__db_verify_internal(dbp,
file, database, outfile, __db_pr_callback, flags));
}
int
__db_verify_internal(dbp, fname, dname, handle, callback, flags)
DB *dbp;
const char *fname, *dname;
void *handle;
int (*callback) __P((void *, const void *));
u_int32_t flags;
{
DB_ENV *dbenv;
int ret;
dbenv = dbp->dbenv;
PANIC_CHECK(dbenv);
DB_ILLEGAL_AFTER_OPEN(dbp, "DB->verify");
if ((ret = __db_verify_arg(dbp, dname, flags)) != 0)
return (ret);
if (TXN_ON(dbenv) || LOCKING_ON(dbenv) || LOGGING_ON(dbenv)) {
__db_err(dbenv,
"DB->verify may not be used with transactions, logging, or locking");
return (EINVAL);
}
return (__db_verify(dbp, fname, dname, handle, callback, flags));
}
static int
__db_verify_arg(dbp, dname, flags)
DB *dbp;
const char *dname;
u_int32_t flags;
{
DB_ENV *dbenv;
int ret;
dbenv = dbp->dbenv;
#undef OKFLAGS
#define OKFLAGS (DB_AGGRESSIVE | DB_NOORDERCHK | DB_ORDERCHKONLY | \
DB_PRINTABLE | DB_SALVAGE)
if ((ret = __db_fchk(dbenv, "DB->verify", flags, OKFLAGS)) != 0)
return (ret);
if (LF_ISSET(DB_SALVAGE) &&
(flags & ~DB_AGGRESSIVE & ~DB_PRINTABLE) != DB_SALVAGE)
return (__db_ferr(dbenv, "__db_verify", 1));
if ((LF_ISSET(DB_AGGRESSIVE) || LF_ISSET(DB_PRINTABLE)) &&
!LF_ISSET(DB_SALVAGE))
return (__db_ferr(dbenv, "__db_verify", 1));
if (LF_ISSET(DB_ORDERCHKONLY) && flags != DB_ORDERCHKONLY)
return (__db_ferr(dbenv, "__db_verify", 1));
if (LF_ISSET(DB_ORDERCHKONLY) && dname == NULL) {
__db_err(dbenv, "DB_ORDERCHKONLY requires a database name");
return (EINVAL);
}
return (0);
}
int
__db_verify(dbp, name, subdb, handle, callback, flags)
DB *dbp;
const char *name, *subdb;
void *handle;
int (*callback) __P((void *, const void *));
u_int32_t flags;
{
DB_ENV *dbenv;
DB_FH *fhp;
VRFY_DBINFO *vdp;
int has, isbad, ret, t_ret;
char *real_name;
dbenv = dbp->dbenv;
fhp = NULL;
vdp = NULL;
real_name = NULL;
has = ret = isbad = 0;
F_SET(dbp, DB_AM_VERIFYING);
if (!LF_ISSET(DB_SALVAGE) && dbp->db_feedback != NULL)
dbp->db_feedback(dbp, DB_VERIFY, 0);
if ((ret = __db_vrfy_dbinfo_create(dbenv, 1024, &vdp)) != 0)
goto err;
if (LF_ISSET(DB_PRINTABLE))
F_SET(vdp, SALVAGE_PRINTABLE);
if ((ret = __db_appname(dbenv,
DB_APP_DATA, name, 0, NULL, &real_name)) != 0)
goto err;
if ((ret = __os_open(dbenv, real_name, DB_OSO_RDONLY, 0444, &fhp)) != 0)
goto err;
if ((ret = __db_vrfy_pagezero(dbp, vdp, fhp, flags)) != 0) {
if (ret == DB_VERIFY_BAD)
isbad = 1;
else
goto err;
}
if ((ret = __db_dbenv_setup(dbp, NULL,
name, TXN_INVALID, DB_ODDFILESIZE | DB_RDONLY)) != 0)
return (ret);
if (dbp->type == DB_QUEUE &&
(ret = __qam_set_ext_data(dbp, name)) != 0)
return (ret);
F_SET(dbp, DB_AM_OPEN_CALLED);
__memp_last_pgno(dbp->mpf, &vdp->last_pgno);
if (LF_ISSET(DB_ORDERCHKONLY)) {
ret = __db_vrfy_orderchkonly(dbp, vdp, name, subdb, flags);
goto done;
}
if (LF_ISSET(DB_SALVAGE)) {
if ((ret = __db_salvage_init(vdp)) != 0)
return (ret);
if (!LF_ISSET(DB_AGGRESSIVE) && (__db_salvage_subdbs(dbp,
vdp, handle, callback, flags, &has)) != 0)
isbad = 1;
if (has == 1)
F_SET(vdp, SALVAGE_PRINTHEADER);
}
if ((ret =
__db_vrfy_walkpages(dbp, vdp, handle, callback, flags)) != 0) {
if (ret == DB_VERIFY_BAD)
isbad = 1;
else if (ret != 0)
goto err;
}
if (!LF_ISSET(DB_SALVAGE) && isbad == 0)
if ((ret =
__db_vrfy_structure(dbp, vdp, name, 0, flags)) != 0) {
if (ret == DB_VERIFY_BAD)
isbad = 1;
else if (ret != 0)
goto err;
}
if (LF_ISSET(DB_SALVAGE)) {
if ((ret = __db_salvage_unknowns(dbp,
vdp, handle, callback, flags)) != 0)
isbad = 1;
__db_salvage_destroy(vdp);
}
if (0) {
err: if (ret == DB_VERIFY_FATAL)
ret = DB_VERIFY_BAD;
__db_err(dbenv, "%s: %s", name, db_strerror(ret));
}
if (LF_ISSET(DB_SALVAGE) &&
(has == 0 || F_ISSET(vdp, SALVAGE_PRINTFOOTER)))
(void)__db_prfooter(handle, callback);
done: if (!LF_ISSET(DB_SALVAGE) && dbp->db_feedback != NULL)
dbp->db_feedback(dbp, DB_VERIFY, 100);
if (fhp != NULL &&
(t_ret = __os_closehandle(dbenv, fhp)) != 0 && ret == 0)
ret = t_ret;
if (dbp != NULL && (t_ret = __db_close(dbp, NULL, 0)) != 0 && ret == 0)
ret = t_ret;
if (vdp != NULL &&
(t_ret = __db_vrfy_dbinfo_destroy(dbenv, vdp)) != 0 && ret == 0)
ret = t_ret;
if (real_name != NULL)
__os_free(dbenv, real_name);
if ((ret == 0 && isbad == 1) || ret == DB_VERIFY_FATAL)
ret = DB_VERIFY_BAD;
return (ret);
}
static int
__db_vrfy_pagezero(dbp, vdp, fhp, flags)
DB *dbp;
VRFY_DBINFO *vdp;
DB_FH *fhp;
u_int32_t flags;
{
DBMETA *meta;
DB_ENV *dbenv;
VRFY_PAGEINFO *pip;
db_pgno_t freelist;
size_t nr;
int isbad, ret, swapped;
u_int8_t mbuf[DBMETASIZE];
isbad = ret = swapped = 0;
freelist = 0;
dbenv = dbp->dbenv;
meta = (DBMETA *)mbuf;
dbp->type = DB_UNKNOWN;
if ((ret = __os_seek(dbenv, fhp, 0, 0, 0, 0, DB_OS_SEEK_SET)) != 0 ||
(ret = __os_read(dbenv, fhp, mbuf, DBMETASIZE, &nr)) != 0) {
__db_err(dbenv,
"Metadata page %lu cannot be read: %s",
(u_long)PGNO_BASE_MD, db_strerror(ret));
return (ret);
}
if (nr != DBMETASIZE) {
EPRINT((dbenv,
"Page %lu: Incomplete metadata page",
(u_long)PGNO_BASE_MD));
return (DB_VERIFY_FATAL);
}
if ((ret = __db_chk_meta(dbenv, dbp, meta, 1)) != 0) {
EPRINT((dbenv,
"Page %lu: metadata page corrupted", (u_long)PGNO_BASE_MD));
isbad = 1;
if (ret != -1) {
EPRINT((dbenv,
"Page %lu: could not check metadata page",
(u_long)PGNO_BASE_MD));
return (DB_VERIFY_FATAL);
}
}
if (meta->pgno != PGNO_BASE_MD) {
isbad = 1;
EPRINT((dbenv, "Page %lu: pgno incorrectly set to %lu",
(u_long)PGNO_BASE_MD, (u_long)meta->pgno));
}
if (__db_is_valid_magicno(meta->magic, &dbp->type))
swapped = 0;
else {
M_32_SWAP(meta->magic);
if (__db_is_valid_magicno(meta->magic,
&dbp->type))
swapped = 1;
else {
isbad = 1;
EPRINT((dbenv,
"Page %lu: bad magic number %lu",
(u_long)PGNO_BASE_MD, (u_long)meta->magic));
}
}
if (swapped)
M_32_SWAP(meta->version);
if ((dbp->type == DB_BTREE &&
(meta->version > DB_BTREEVERSION ||
meta->version < DB_BTREEOLDVER)) ||
(dbp->type == DB_HASH &&
(meta->version > DB_HASHVERSION ||
meta->version < DB_HASHOLDVER)) ||
(dbp->type == DB_QUEUE &&
(meta->version > DB_QAMVERSION ||
meta->version < DB_QAMOLDVER))) {
isbad = 1;
EPRINT((dbenv,
"Page %lu: unsupported DB version %lu; extraneous errors may result",
(u_long)PGNO_BASE_MD, (u_long)meta->version));
}
if (swapped)
M_32_SWAP(meta->pagesize);
if (IS_VALID_PAGESIZE(meta->pagesize))
dbp->pgsize = meta->pagesize;
else {
isbad = 1;
EPRINT((dbenv, "Page %lu: bad page size %lu",
(u_long)PGNO_BASE_MD, (u_long)meta->pagesize));
if (!IS_VALID_PAGESIZE(dbp->pgsize))
dbp->pgsize = __db_guesspgsize(dbenv, fhp);
}
if ((dbp->type == DB_BTREE && meta->type != P_BTREEMETA) ||
(dbp->type == DB_HASH && meta->type != P_HASHMETA) ||
(dbp->type == DB_QUEUE && meta->type != P_QAMMETA)) {
isbad = 1;
EPRINT((dbenv, "Page %lu: bad page type %lu",
(u_long)PGNO_BASE_MD, (u_long)meta->type));
}
if (swapped)
M_32_SWAP(meta->free);
freelist = meta->free;
if ((ret = __db_vrfy_getpageinfo(vdp, PGNO_BASE_MD, &pip)) != 0)
return (ret);
pip->pgno = PGNO_BASE_MD;
pip->type = meta->type;
F_SET(pip, VRFY_INCOMPLETE);
pip->free = freelist;
if ((ret = __db_vrfy_putpageinfo(dbenv, vdp, pip)) != 0)
return (ret);
memcpy(dbp->fileid, meta->uid, DB_FILE_ID_LEN);
if (swapped == 1)
F_SET(dbp, DB_AM_SWAP);
return (isbad ? DB_VERIFY_BAD : 0);
}
static int
__db_vrfy_walkpages(dbp, vdp, handle, callback, flags)
DB *dbp;
VRFY_DBINFO *vdp;
void *handle;
int (*callback) __P((void *, const void *));
u_int32_t flags;
{
DB_ENV *dbenv;
DB_MPOOLFILE *mpf;
PAGE *h;
db_pgno_t i;
int ret, t_ret, isbad;
dbenv = dbp->dbenv;
mpf = dbp->mpf;
ret = isbad = t_ret = 0;
#define OKFLAGS (DB_AGGRESSIVE | DB_NOORDERCHK | DB_ORDERCHKONLY | \
DB_PRINTABLE | DB_SALVAGE)
if ((ret = __db_fchk(dbenv,
"__db_vrfy_walkpages", flags, OKFLAGS)) != 0)
return (ret);
for (i = 0; i <= vdp->last_pgno; i++) {
if (LF_ISSET(DB_SALVAGE) && (__db_salvage_isdone(vdp, i) != 0))
continue;
if ((t_ret = __memp_fget(mpf, &i, 0, &h)) != 0) {
if (ret == 0)
ret = t_ret;
if (LF_ISSET(DB_SALVAGE))
continue;
else
return (ret);
}
if (LF_ISSET(DB_SALVAGE)) {
if ((t_ret = __db_salvage(dbp,
vdp, i, h, handle, callback, flags)) != 0) {
if (ret == 0)
ret = t_ret;
isbad = 1;
}
} else {
if (i != PGNO_BASE_MD) {
ret = __db_vrfy_common(dbp, vdp, h, i, flags);
if (ret == DB_VERIFY_BAD)
isbad = 1;
else if (ret != 0)
goto err;
}
switch (TYPE(h)) {
case P_INVALID:
ret = __db_vrfy_invalid(dbp, vdp, h, i, flags);
break;
case __P_DUPLICATE:
isbad = 1;
EPRINT((dbenv,
"Page %lu: old-style duplicate page",
(u_long)i));
break;
case P_HASH:
ret = __ham_vrfy(dbp, vdp, h, i, flags);
break;
case P_IBTREE:
case P_IRECNO:
case P_LBTREE:
case P_LDUP:
ret = __bam_vrfy(dbp, vdp, h, i, flags);
break;
case P_LRECNO:
ret = __ram_vrfy_leaf(dbp, vdp, h, i, flags);
break;
case P_OVERFLOW:
ret = __db_vrfy_overflow(dbp, vdp, h, i, flags);
break;
case P_HASHMETA:
ret = __ham_vrfy_meta(dbp,
vdp, (HMETA *)h, i, flags);
break;
case P_BTREEMETA:
ret = __bam_vrfy_meta(dbp,
vdp, (BTMETA *)h, i, flags);
break;
case P_QAMMETA:
ret = __qam_vrfy_meta(dbp,
vdp, (QMETA *)h, i, flags);
break;
case P_QAMDATA:
ret = __qam_vrfy_data(dbp,
vdp, (QPAGE *)h, i, flags);
break;
default:
EPRINT((dbenv,
"Page %lu: unknown page type %lu",
(u_long)i, (u_long)TYPE(h)));
isbad = 1;
break;
}
if (ret == DB_VERIFY_BAD)
isbad = 1;
else if (ret != 0)
goto err;
if (dbp->db_feedback != NULL)
dbp->db_feedback(dbp, DB_VERIFY,
(int)((i + 1) * 50 / (vdp->last_pgno + 1)));
}
if ((t_ret = __memp_fput(mpf, h, 0)) != 0) {
if (ret == 0)
ret = t_ret;
if (!LF_ISSET(DB_SALVAGE))
return (ret);
}
}
if (F_ISSET(vdp, VRFY_QMETA_SET) && (t_ret =
__qam_vrfy_walkqueue(dbp, vdp, handle, callback, flags)) != 0) {
if (ret == 0)
ret = t_ret;
if (t_ret == DB_VERIFY_BAD)
isbad = 1;
else if (!LF_ISSET(DB_SALVAGE))
return (ret);
}
if (0) {
err: if ((t_ret = __memp_fput(mpf, h, 0)) != 0)
return (ret == 0 ? t_ret : ret);
}
return ((isbad == 1 && ret == 0) ? DB_VERIFY_BAD : ret);
}
static int
__db_vrfy_structure(dbp, vdp, dbname, meta_pgno, flags)
DB *dbp;
VRFY_DBINFO *vdp;
const char *dbname;
db_pgno_t meta_pgno;
u_int32_t flags;
{
DB *pgset;
DB_ENV *dbenv;
VRFY_PAGEINFO *pip;
db_pgno_t i;
int ret, isbad, hassubs, p;
isbad = 0;
pip = NULL;
dbenv = dbp->dbenv;
pgset = vdp->pgset;
if ((ret = __db_fchk(dbenv, "DB->verify", flags, OKFLAGS)) != 0)
return (ret);
if (LF_ISSET(DB_SALVAGE)) {
__db_err(dbenv, "__db_vrfy_structure called with DB_SALVAGE");
return (EINVAL);
}
if (dbp->db_feedback != NULL)
vdp->pgs_remaining = vdp->last_pgno + 1;
switch (dbp->type) {
case DB_BTREE:
case DB_RECNO:
if ((ret = __bam_vrfy_structure(dbp, vdp, 0, flags)) != 0) {
if (ret == DB_VERIFY_BAD)
isbad = 1;
else
goto err;
}
if ((ret = __db_vrfy_getpageinfo(vdp, 0, &pip)) != 0)
goto err;
hassubs = F_ISSET(pip, VRFY_HAS_SUBDBS) ? 1 : 0;
if ((ret = __db_vrfy_putpageinfo(dbenv, vdp, pip)) != 0)
goto err;
pip = NULL;
if (isbad == 0 && hassubs)
if ((ret =
__db_vrfy_subdbs(dbp, vdp, dbname, flags)) != 0) {
if (ret == DB_VERIFY_BAD)
isbad = 1;
else
goto err;
}
break;
case DB_HASH:
if ((ret = __ham_vrfy_structure(dbp, vdp, 0, flags)) != 0) {
if (ret == DB_VERIFY_BAD)
isbad = 1;
else
goto err;
}
break;
case DB_QUEUE:
if ((ret = __qam_vrfy_structure(dbp, vdp, flags)) != 0) {
if (ret == DB_VERIFY_BAD)
isbad = 1;
}
goto err;
case DB_UNKNOWN:
default:
DB_ASSERT(0);
ret = EINVAL;
goto err;
}
if ((ret =
__db_vrfy_freelist(dbp, vdp, meta_pgno, flags)) == DB_VERIFY_BAD)
isbad = 1;
if (ret != 0 || isbad == 1)
goto err;
for (i = 0; i < vdp->last_pgno + 1; i++) {
if ((ret = __db_vrfy_getpageinfo(vdp, i, &pip)) != 0)
goto err;
if ((ret = __db_vrfy_pgset_get(pgset, i, &p)) != 0)
goto err;
if (pip->type == P_OVERFLOW) {
if ((u_int32_t)p != pip->refcount) {
EPRINT((dbenv,
"Page %lu: overflow refcount %lu, referenced %lu times",
(u_long)i,
(u_long)pip->refcount, (u_long)p));
isbad = 1;
}
} else if (p == 0) {
EPRINT((dbenv,
"Page %lu: unreferenced page", (u_long)i));
isbad = 1;
}
if (F_ISSET(pip, VRFY_IS_ALLZEROES)) {
EPRINT((dbenv,
"Page %lu: totally zeroed page", (u_long)i));
isbad = 1;
}
if ((ret = __db_vrfy_putpageinfo(dbenv, vdp, pip)) != 0)
goto err;
pip = NULL;
}
err: if (pip != NULL)
(void)__db_vrfy_putpageinfo(dbenv, vdp, pip);
return ((isbad == 1 && ret == 0) ? DB_VERIFY_BAD : ret);
}
static int
__db_is_valid_pagetype(type)
u_int32_t type;
{
switch (type) {
case P_INVALID:
case P_HASH:
case P_IBTREE:
case P_IRECNO:
case P_LBTREE:
case P_LRECNO:
case P_OVERFLOW:
case P_HASHMETA:
case P_BTREEMETA:
case P_QAMMETA:
case P_QAMDATA:
case P_LDUP:
return (1);
default:
break;
}
return (0);
}
static int
__db_is_valid_magicno(magic, typep)
u_int32_t magic;
DBTYPE *typep;
{
switch (magic) {
case DB_BTREEMAGIC:
*typep = DB_BTREE;
return (1);
case DB_HASHMAGIC:
*typep = DB_HASH;
return (1);
case DB_QAMMAGIC:
*typep = DB_QUEUE;
return (1);
default:
break;
}
*typep = DB_UNKNOWN;
return (0);
}
int
__db_vrfy_common(dbp, vdp, h, pgno, flags)
DB *dbp;
VRFY_DBINFO *vdp;
PAGE *h;
db_pgno_t pgno;
u_int32_t flags;
{
DB_ENV *dbenv;
VRFY_PAGEINFO *pip;
int ret, t_ret;
u_int8_t *p;
dbenv = dbp->dbenv;
if ((ret = __db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0)
return (ret);
pip->pgno = pgno;
F_CLR(pip, VRFY_IS_ALLZEROES);
if (pgno != 0 && PGNO(h) == 0) {
for (p = (u_int8_t *)h; p < (u_int8_t *)h + dbp->pgsize; p++)
if (*p != 0) {
EPRINT((dbenv,
"Page %lu: partially zeroed page",
(u_long)pgno));
ret = DB_VERIFY_BAD;
goto err;
}
pip->type = P_HASH;
F_SET(pip, VRFY_IS_ALLZEROES);
ret = 0;
goto err;
}
if (PGNO(h) != pgno) {
EPRINT((dbenv, "Page %lu: bad page number %lu",
(u_long)pgno, (u_long)h->pgno));
ret = DB_VERIFY_BAD;
}
if (!__db_is_valid_pagetype(h->type)) {
EPRINT((dbenv, "Page %lu: bad page type %lu",
(u_long)pgno, (u_long)h->type));
ret = DB_VERIFY_BAD;
}
pip->type = h->type;
err: if ((t_ret = __db_vrfy_putpageinfo(dbenv, vdp, pip)) != 0 && ret == 0)
ret = t_ret;
return (ret);
}
static int
__db_vrfy_invalid(dbp, vdp, h, pgno, flags)
DB *dbp;
VRFY_DBINFO *vdp;
PAGE *h;
db_pgno_t pgno;
u_int32_t flags;
{
DB_ENV *dbenv;
VRFY_PAGEINFO *pip;
int ret, t_ret;
dbenv = dbp->dbenv;
if ((ret = __db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0)
return (ret);
pip->next_pgno = pip->prev_pgno = 0;
if (!IS_VALID_PGNO(NEXT_PGNO(h))) {
EPRINT((dbenv, "Page %lu: invalid next_pgno %lu",
(u_long)pgno, (u_long)NEXT_PGNO(h)));
ret = DB_VERIFY_BAD;
} else
pip->next_pgno = NEXT_PGNO(h);
if ((t_ret = __db_vrfy_putpageinfo(dbenv, vdp, pip)) != 0 && ret == 0)
ret = t_ret;
return (ret);
}
int
__db_vrfy_datapage(dbp, vdp, h, pgno, flags)
DB *dbp;
VRFY_DBINFO *vdp;
PAGE *h;
db_pgno_t pgno;
u_int32_t flags;
{
DB_ENV *dbenv;
VRFY_PAGEINFO *pip;
int isbad, ret, t_ret;
dbenv = dbp->dbenv;
if ((ret = __db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0)
return (ret);
isbad = 0;
if (TYPE(h) != P_IBTREE && TYPE(h) != P_IRECNO) {
if (!IS_VALID_PGNO(PREV_PGNO(h)) || PREV_PGNO(h) == pip->pgno) {
isbad = 1;
EPRINT((dbenv, "Page %lu: invalid prev_pgno %lu",
(u_long)pip->pgno, (u_long)PREV_PGNO(h)));
}
if (!IS_VALID_PGNO(NEXT_PGNO(h)) || NEXT_PGNO(h) == pip->pgno) {
isbad = 1;
EPRINT((dbenv, "Page %lu: invalid next_pgno %lu",
(u_long)pip->pgno, (u_long)NEXT_PGNO(h)));
}
pip->prev_pgno = PREV_PGNO(h);
pip->next_pgno = NEXT_PGNO(h);
}
if (TYPE(h) != P_OVERFLOW) {
if (BKEYDATA_PSIZE(0) * NUM_ENT(h) > dbp->pgsize) {
isbad = 1;
EPRINT((dbenv, "Page %lu: too many entries: %lu",
(u_long)pgno, (u_long)NUM_ENT(h)));
}
pip->entries = NUM_ENT(h);
}
switch (TYPE(h)) {
case P_IBTREE:
case P_IRECNO:
if (LEVEL(h) < LEAFLEVEL + 1) {
isbad = 1;
EPRINT((dbenv, "Page %lu: bad btree level %lu",
(u_long)pgno, (u_long)LEVEL(h)));
}
pip->bt_level = LEVEL(h);
break;
case P_LBTREE:
case P_LDUP:
case P_LRECNO:
if (LEVEL(h) != LEAFLEVEL) {
isbad = 1;
EPRINT((dbenv,
"Page %lu: btree leaf page has incorrect level %lu",
(u_long)pgno, (u_long)LEVEL(h)));
}
break;
default:
if (LEVEL(h) != 0) {
isbad = 1;
EPRINT((dbenv,
"Page %lu: nonzero level %lu in non-btree database",
(u_long)pgno, (u_long)LEVEL(h)));
}
break;
}
if ((t_ret = __db_vrfy_putpageinfo(dbenv, vdp, pip)) != 0 && ret == 0)
ret = t_ret;
return ((ret == 0 && isbad == 1) ? DB_VERIFY_BAD : ret);
}
int
__db_vrfy_meta(dbp, vdp, meta, pgno, flags)
DB *dbp;
VRFY_DBINFO *vdp;
DBMETA *meta;
db_pgno_t pgno;
u_int32_t flags;
{
DB_ENV *dbenv;
DBTYPE dbtype, magtype;
VRFY_PAGEINFO *pip;
int isbad, ret, t_ret;
isbad = 0;
dbenv = dbp->dbenv;
if ((ret = __db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0)
return (ret);
switch (meta->type) {
case P_BTREEMETA:
dbtype = DB_BTREE;
break;
case P_HASHMETA:
dbtype = DB_HASH;
break;
case P_QAMMETA:
dbtype = DB_QUEUE;
break;
default:
DB_ASSERT(0);
ret = EINVAL;
goto err;
}
if (!__db_is_valid_magicno(meta->magic, &magtype)) {
isbad = 1;
EPRINT((dbenv,
"Page %lu: invalid magic number", (u_long)pgno));
}
if (magtype != dbtype) {
isbad = 1;
EPRINT((dbenv,
"Page %lu: magic number does not match database type",
(u_long)pgno));
}
if ((dbtype == DB_BTREE &&
(meta->version > DB_BTREEVERSION ||
meta->version < DB_BTREEOLDVER)) ||
(dbtype == DB_HASH &&
(meta->version > DB_HASHVERSION ||
meta->version < DB_HASHOLDVER)) ||
(dbtype == DB_QUEUE &&
(meta->version > DB_QAMVERSION ||
meta->version < DB_QAMOLDVER))) {
isbad = 1;
EPRINT((dbenv,
"Page %lu: unsupported database version %lu; extraneous errors may result",
(u_long)pgno, (u_long)meta->version));
}
if (meta->pagesize != dbp->pgsize) {
isbad = 1;
EPRINT((dbenv, "Page %lu: invalid pagesize %lu",
(u_long)pgno, (u_long)meta->pagesize));
}
if (pgno != PGNO_BASE_MD && meta->free != PGNO_INVALID) {
isbad = 1;
EPRINT((dbenv,
"Page %lu: nonempty free list on subdatabase metadata page",
(u_long)pgno));
}
if (meta->free != PGNO_INVALID && IS_VALID_PGNO(meta->free))
pip->free = meta->free;
else if (!IS_VALID_PGNO(meta->free)) {
isbad = 1;
EPRINT((dbenv,
"Page %lu: nonsensical free list pgno %lu",
(u_long)pgno, (u_long)meta->free));
}
F_CLR(pip, VRFY_INCOMPLETE);
err: if ((t_ret = __db_vrfy_putpageinfo(dbenv, vdp, pip)) != 0 && ret == 0)
ret = t_ret;
return ((ret == 0 && isbad == 1) ? DB_VERIFY_BAD : ret);
}
static int
__db_vrfy_freelist(dbp, vdp, meta, flags)
DB *dbp;
VRFY_DBINFO *vdp;
db_pgno_t meta;
u_int32_t flags;
{
DB *pgset;
DB_ENV *dbenv;
VRFY_PAGEINFO *pip;
db_pgno_t cur_pgno, next_pgno;
int p, ret, t_ret;
pgset = vdp->pgset;
DB_ASSERT(pgset != NULL);
dbenv = dbp->dbenv;
if ((ret = __db_vrfy_getpageinfo(vdp, meta, &pip)) != 0)
return (ret);
for (next_pgno = pip->free;
next_pgno != PGNO_INVALID; next_pgno = pip->next_pgno) {
cur_pgno = pip->pgno;
if ((ret = __db_vrfy_putpageinfo(dbenv, vdp, pip)) != 0)
return (ret);
if (!IS_VALID_PGNO(next_pgno)) {
EPRINT((dbenv,
"Page %lu: invalid next_pgno %lu on free list page",
(u_long)cur_pgno, (u_long)next_pgno));
return (DB_VERIFY_BAD);
}
if ((ret = __db_vrfy_pgset_get(pgset, next_pgno, &p)) != 0)
return (ret);
if (p != 0) {
EPRINT((dbenv,
"Page %lu: page %lu encountered a second time on free list",
(u_long)cur_pgno, (u_long)next_pgno));
return (DB_VERIFY_BAD);
}
if ((ret = __db_vrfy_pgset_inc(pgset, next_pgno)) != 0)
return (ret);
if ((ret = __db_vrfy_getpageinfo(vdp, next_pgno, &pip)) != 0)
return (ret);
if (pip->type != P_INVALID) {
EPRINT((dbenv,
"Page %lu: non-invalid page %lu on free list",
(u_long)cur_pgno, (u_long)next_pgno));
ret = DB_VERIFY_BAD;
break;
}
}
if ((t_ret = __db_vrfy_putpageinfo(dbenv, vdp, pip)) != 0)
ret = t_ret;
return (ret);
}
static int
__db_vrfy_subdbs(dbp, vdp, dbname, flags)
DB *dbp;
VRFY_DBINFO *vdp;
const char *dbname;
u_int32_t flags;
{
DB *mdbp;
DBC *dbc;
DBT key, data;
DB_ENV *dbenv;
VRFY_PAGEINFO *pip;
db_pgno_t meta_pgno;
int ret, t_ret, isbad;
u_int8_t type;
isbad = 0;
dbc = NULL;
dbenv = dbp->dbenv;
if ((ret =
__db_master_open(dbp, NULL, dbname, DB_RDONLY, 0, &mdbp)) != 0)
return (ret);
if ((ret = __db_cursor_int(mdbp,
NULL, DB_BTREE, PGNO_INVALID, 0, DB_LOCK_INVALIDID, &dbc)) != 0)
goto err;
memset(&key, 0, sizeof(key));
memset(&data, 0, sizeof(data));
while ((ret = __db_c_get(dbc, &key, &data, DB_NEXT)) == 0) {
if (data.size != sizeof(db_pgno_t)) {
EPRINT((dbenv,
"Subdatabase entry not page-number size"));
isbad = 1;
goto err;
}
memcpy(&meta_pgno, data.data, data.size);
DB_NTOHL(&meta_pgno);
if (meta_pgno == PGNO_INVALID || meta_pgno > vdp->last_pgno) {
EPRINT((dbenv,
"Subdatabase entry references invalid page %lu",
(u_long)meta_pgno));
isbad = 1;
goto err;
}
if ((ret = __db_vrfy_getpageinfo(vdp, meta_pgno, &pip)) != 0)
goto err;
type = pip->type;
if ((ret = __db_vrfy_putpageinfo(dbenv, vdp, pip)) != 0)
goto err;
switch (type) {
case P_BTREEMETA:
if ((ret = __bam_vrfy_structure(
dbp, vdp, meta_pgno, flags)) != 0) {
if (ret == DB_VERIFY_BAD)
isbad = 1;
else
goto err;
}
break;
case P_HASHMETA:
if ((ret = __ham_vrfy_structure(
dbp, vdp, meta_pgno, flags)) != 0) {
if (ret == DB_VERIFY_BAD)
isbad = 1;
else
goto err;
}
break;
case P_QAMMETA:
default:
EPRINT((dbenv,
"Subdatabase entry references page %lu of invalid type %lu",
(u_long)meta_pgno, (u_long)type));
ret = DB_VERIFY_BAD;
goto err;
}
}
if (ret == DB_NOTFOUND)
ret = 0;
err: if (dbc != NULL && (t_ret = __db_c_close(dbc)) != 0 && ret == 0)
ret = t_ret;
if ((t_ret = __db_close(mdbp, NULL, 0)) != 0 && ret == 0)
ret = t_ret;
return ((ret == 0 && isbad == 1) ? DB_VERIFY_BAD : ret);
}
void
__db_vrfy_struct_feedback(dbp, vdp)
DB *dbp;
VRFY_DBINFO *vdp;
{
int progress;
if (dbp->db_feedback == NULL)
return;
if (vdp->pgs_remaining > 0)
vdp->pgs_remaining--;
progress = 100 - (int)(vdp->pgs_remaining * 50 / (vdp->last_pgno + 1));
dbp->db_feedback(dbp, DB_VERIFY, progress == 100 ? 99 : progress);
}
static int
__db_vrfy_orderchkonly(dbp, vdp, name, subdb, flags)
DB *dbp;
VRFY_DBINFO *vdp;
const char *name, *subdb;
u_int32_t flags;
{
BTMETA *btmeta;
DB *mdbp, *pgset;
DBC *pgsc;
DBT key, data;
DB_ENV *dbenv;
DB_MPOOLFILE *mpf;
HASH *h_internal;
HMETA *hmeta;
PAGE *h, *currpg;
db_pgno_t meta_pgno, p, pgno;
u_int32_t bucket;
int t_ret, ret;
pgset = NULL;
pgsc = NULL;
dbenv = dbp->dbenv;
mpf = dbp->mpf;
currpg = h = NULL;
LF_CLR(DB_NOORDERCHK);
if ((ret = __db_master_open(dbp, NULL, name, DB_RDONLY, 0, &mdbp)) != 0)
goto err;
memset(&key, 0, sizeof(key));
key.data = (void *)subdb;
key.size = (u_int32_t)strlen(subdb);
memset(&data, 0, sizeof(data));
if ((ret = __db_get(mdbp, NULL, &key, &data, 0)) != 0)
goto err;
if (data.size != sizeof(db_pgno_t)) {
EPRINT((dbenv, "Subdatabase entry of invalid size"));
ret = DB_VERIFY_BAD;
goto err;
}
memcpy(&meta_pgno, data.data, data.size);
DB_NTOHL(&meta_pgno);
if ((ret = __memp_fget(mpf, &meta_pgno, 0, &h)) != 0)
goto err;
if ((ret = __db_vrfy_pgset(dbenv, dbp->pgsize, &pgset)) != 0)
goto err;
switch (TYPE(h)) {
case P_BTREEMETA:
btmeta = (BTMETA *)h;
if (F_ISSET(&btmeta->dbmeta, BTM_RECNO)) {
ret = 0;
goto err;
}
if ((ret =
__db_meta2pgset(dbp, vdp, meta_pgno, flags, pgset)) != 0)
goto err;
if ((ret = __db_cursor_int(pgset, NULL, dbp->type,
PGNO_INVALID, 0, DB_LOCK_INVALIDID, &pgsc)) != 0)
goto err;
while ((ret = __db_vrfy_pgset_next(pgsc, &p)) == 0) {
if ((ret = __memp_fget(mpf, &p, 0, &currpg)) != 0)
goto err;
if ((ret = __bam_vrfy_itemorder(dbp,
NULL, currpg, p, NUM_ENT(currpg), 1,
F_ISSET(&btmeta->dbmeta, BTM_DUP), flags)) != 0)
goto err;
if ((ret = __memp_fput(mpf, currpg, 0)) != 0)
goto err;
currpg = NULL;
}
if (ret == DB_NOTFOUND)
ret = 0;
break;
case P_HASHMETA:
hmeta = (HMETA *)h;
h_internal = (HASH *)dbp->h_internal;
if (h_internal == NULL) {
EPRINT((dbenv,
"Page %lu: DB->h_internal field is NULL",
(u_long)meta_pgno));
ret = DB_VERIFY_BAD;
goto err;
}
if (h_internal->h_hash == NULL)
h_internal->h_hash = hmeta->dbmeta.version < 5
? __ham_func4 : __ham_func5;
if (hmeta->h_charkey !=
h_internal->h_hash(dbp, CHARKEY, sizeof(CHARKEY))) {
EPRINT((dbenv,
"Page %lu: incorrect hash function for database",
(u_long)meta_pgno));
ret = DB_VERIFY_BAD;
goto err;
}
for (bucket = 0; bucket <= hmeta->max_bucket; bucket++) {
pgno = BS_TO_PAGE(bucket, hmeta->spares);
while (pgno != PGNO_INVALID) {
if ((ret = __memp_fget(mpf,
&pgno, 0, &currpg)) != 0)
goto err;
if ((ret = __ham_vrfy_hashing(dbp,
NUM_ENT(currpg), hmeta, bucket, pgno,
flags, h_internal->h_hash)) != 0)
goto err;
pgno = NEXT_PGNO(currpg);
if ((ret = __memp_fput(mpf, currpg, 0)) != 0)
goto err;
currpg = NULL;
}
}
break;
default:
EPRINT((dbenv, "Page %lu: database metapage of bad type %lu",
(u_long)meta_pgno, (u_long)TYPE(h)));
ret = DB_VERIFY_BAD;
break;
}
err: if (pgsc != NULL && (t_ret = __db_c_close(pgsc)) != 0 && ret == 0)
ret = t_ret;
if (pgset != NULL &&
(t_ret = __db_close(pgset, NULL, 0)) != 0 && ret == 0)
ret = t_ret;
if (h != NULL && (t_ret = __memp_fput(mpf, h, 0)) != 0)
ret = t_ret;
if (currpg != NULL && (t_ret = __memp_fput(mpf, currpg, 0)) != 0)
ret = t_ret;
if ((t_ret = __db_close(mdbp, NULL, 0)) != 0)
ret = t_ret;
return (ret);
}
int
__db_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;
{
int ret;
DB_ASSERT(LF_ISSET(DB_SALVAGE));
if (__db_salvage_isdone(vdp, pgno))
return (0);
switch (TYPE(h)) {
case P_HASHMETA:
ret = __ham_vrfy_meta(dbp, vdp, (HMETA *)h, pgno, flags);
break;
case P_BTREEMETA:
ret = __bam_vrfy_meta(dbp, vdp, (BTMETA *)h, pgno, flags);
break;
case P_QAMMETA:
ret = __qam_vrfy_meta(dbp, vdp, (QMETA *)h, pgno, flags);
break;
case P_HASH:
return (__ham_salvage(dbp,
vdp, pgno, h, handle, callback, flags));
case P_LBTREE:
return (__bam_salvage(dbp,
vdp, pgno, P_LBTREE, h, handle, callback, NULL, flags));
case P_LDUP:
return (__db_salvage_markneeded(vdp, pgno, SALVAGE_LDUP));
case P_OVERFLOW:
return (__db_salvage_markneeded(vdp, pgno, SALVAGE_OVERFLOW));
case P_LRECNO:
return (__db_salvage_markneeded(vdp, pgno, SALVAGE_LRECNO));
case P_QAMDATA:
return (__qam_salvage(dbp,
vdp, pgno, h, handle, callback, flags));
case P_IBTREE:
case P_INVALID:
case P_IRECNO:
case __P_DUPLICATE:
default:
return (0);
}
return (ret != 0 ? ret :
__db_prheader(dbp, NULL, 0, 1,
handle, callback, vdp, PGNO_BASE_MD));
}
static int
__db_salvage_unknowns(dbp, vdp, handle, callback, flags)
DB *dbp;
VRFY_DBINFO *vdp;
void *handle;
int (*callback) __P((void *, const void *));
u_int32_t flags;
{
DBT unkdbt, key, *dbt;
DB_ENV *dbenv;
DB_MPOOLFILE *mpf;
PAGE *h;
db_pgno_t pgno;
u_int32_t pgtype;
int ret, err_ret;
void *ovflbuf;
dbenv = dbp->dbenv;
mpf = dbp->mpf;
memset(&unkdbt, 0, sizeof(DBT));
unkdbt.size = (u_int32_t)strlen("UNKNOWN") + 1;
unkdbt.data = "UNKNOWN";
if ((ret = __os_malloc(dbenv, dbp->pgsize, &ovflbuf)) != 0)
return (ret);
err_ret = 0;
while ((ret = __db_salvage_getnext(vdp, &pgno, &pgtype)) == 0) {
dbt = NULL;
if ((ret = __memp_fget(mpf, &pgno, 0, &h)) != 0) {
err_ret = ret;
continue;
}
switch (pgtype) {
case SALVAGE_LDUP:
case SALVAGE_LRECNODUP:
dbt = &unkdbt;
case SALVAGE_LBTREE:
case SALVAGE_LRECNO:
if ((ret = __bam_salvage(dbp, vdp, pgno, pgtype,
h, handle, callback, dbt, flags)) != 0)
err_ret = ret;
break;
case SALVAGE_OVERFLOW:
if ((ret = __db_safe_goff(dbp,
vdp, pgno, &key, &ovflbuf, flags)) != 0 ||
(ret = __db_prdbt(&key,
0, " ", handle, callback, 0, vdp)) != 0 ||
(ret = __db_prdbt(&unkdbt,
0, " ", handle, callback, 0, vdp)) != 0)
err_ret = ret;
break;
case SALVAGE_HASH:
if ((ret = __ham_salvage(
dbp, vdp, pgno, h, handle, callback, flags)) != 0)
err_ret = ret;
break;
case SALVAGE_INVALID:
case SALVAGE_IGNORE:
default:
DB_ASSERT(0);
break;
}
if ((ret = __memp_fput(mpf, h, 0)) != 0)
err_ret = ret;
}
__os_free(dbenv, ovflbuf);
if (err_ret != 0 && ret == 0)
ret = err_ret;
return (ret == DB_NOTFOUND ? 0 : ret);
}
#define INP_OFFSET(dbp, h, i) \
((db_indx_t)((u_int8_t *)((P_INP(dbp,(h))) + (i)) - (u_int8_t *)(h)))
int
__db_vrfy_inpitem(dbp, h, pgno, i, is_btree, flags, himarkp, offsetp)
DB *dbp;
PAGE *h;
db_pgno_t pgno;
u_int32_t i;
int is_btree;
u_int32_t flags, *himarkp, *offsetp;
{
BKEYDATA *bk;
DB_ENV *dbenv;
db_indx_t *inp, offset, len;
dbenv = dbp->dbenv;
DB_ASSERT(himarkp != NULL);
inp = P_INP(dbp, h);
if (inp + i >= (db_indx_t *)((u_int8_t *)h + *himarkp)) {
EPRINT((dbenv, "Page %lu: entries listing %lu overlaps data",
(u_long)pgno, (u_long)i));
return (DB_VERIFY_FATAL);
}
offset = inp[i];
if (offset <= INP_OFFSET(dbp, h, i) || offset > dbp->pgsize) {
EPRINT((dbenv, "Page %lu: bad offset %lu at page index %lu",
(u_long)pgno, (u_long)offset, (u_long)i));
return (DB_VERIFY_BAD);
}
if (offset < *himarkp)
*himarkp = offset;
if (is_btree) {
if (offset != ALIGN(offset, sizeof(u_int32_t))) {
EPRINT((dbenv,
"Page %lu: unaligned offset %lu at page index %lu",
(u_long)pgno, (u_long)offset, (u_long)i));
return (DB_VERIFY_BAD);
}
bk = GET_BKEYDATA(dbp, h, i);
switch (B_TYPE(bk->type)) {
case B_KEYDATA:
len = bk->len;
break;
case B_DUPLICATE:
case B_OVERFLOW:
len = BOVERFLOW_SIZE;
break;
default:
EPRINT((dbenv,
"Page %lu: item %lu of unrecognizable type",
(u_long)pgno, (u_long)i));
return (DB_VERIFY_BAD);
}
if ((size_t)(offset + len) > dbp->pgsize) {
EPRINT((dbenv,
"Page %lu: item %lu extends past page boundary",
(u_long)pgno, (u_long)i));
return (DB_VERIFY_BAD);
}
}
if (offsetp != NULL)
*offsetp = offset;
return (0);
}
int
__db_vrfy_duptype(dbp, vdp, pgno, flags)
DB *dbp;
VRFY_DBINFO *vdp;
db_pgno_t pgno;
u_int32_t flags;
{
DB_ENV *dbenv;
VRFY_PAGEINFO *pip;
int ret, isbad;
dbenv = dbp->dbenv;
isbad = 0;
if ((ret = __db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0)
return (ret);
switch (pip->type) {
case P_IBTREE:
case P_LDUP:
if (!LF_ISSET(ST_DUPSORT)) {
EPRINT((dbenv,
"Page %lu: sorted duplicate set in unsorted-dup database",
(u_long)pgno));
isbad = 1;
}
break;
case P_IRECNO:
case P_LRECNO:
if (LF_ISSET(ST_DUPSORT)) {
EPRINT((dbenv,
"Page %lu: unsorted duplicate set in sorted-dup database",
(u_long)pgno));
isbad = 1;
}
break;
default:
if (F_ISSET(pip, VRFY_IS_ALLZEROES))
ZEROPG_ERR_PRINT(dbenv, pgno, "duplicate page");
else
EPRINT((dbenv,
"Page %lu: duplicate page of inappropriate type %lu",
(u_long)pgno, (u_long)pip->type));
isbad = 1;
break;
}
if ((ret = __db_vrfy_putpageinfo(dbenv, vdp, pip)) != 0)
return (ret);
return (isbad == 1 ? DB_VERIFY_BAD : 0);
}
int
__db_salvage_duptree(dbp, vdp, pgno, key, handle, callback, flags)
DB *dbp;
VRFY_DBINFO *vdp;
db_pgno_t pgno;
DBT *key;
void *handle;
int (*callback) __P((void *, const void *));
u_int32_t flags;
{
DB_MPOOLFILE *mpf;
PAGE *h;
int ret, t_ret;
mpf = dbp->mpf;
if (pgno == PGNO_INVALID || !IS_VALID_PGNO(pgno))
return (DB_VERIFY_BAD);
if ((ret = __memp_fget(mpf, &pgno, 0, &h)) != 0)
return (ret);
switch (TYPE(h)) {
case P_IBTREE:
case P_IRECNO:
if ((ret = __db_vrfy_common(dbp, vdp, h, pgno, flags)) != 0)
goto err;
if ((ret = __bam_vrfy(dbp,
vdp, h, pgno, flags | DB_NOORDERCHK)) != 0 ||
(ret = __db_salvage_markdone(vdp, pgno)) != 0)
goto err;
if ((ret = __bam_salvage_walkdupint(dbp, vdp, h, key,
handle, callback, flags)) != 0)
goto err;
break;
case P_LRECNO:
case P_LDUP:
if ((ret = __bam_salvage(dbp,
vdp, pgno, TYPE(h), h, handle, callback, key, flags)) != 0)
goto err;
break;
default:
ret = DB_VERIFY_BAD;
goto err;
}
err: if ((t_ret = __memp_fput(mpf, h, 0)) != 0 && ret == 0)
ret = t_ret;
return (ret);
}
static int
__db_salvage_subdbs(dbp, vdp, handle, callback, flags, hassubsp)
DB *dbp;
VRFY_DBINFO *vdp;
void *handle;
int (*callback) __P((void *, const void *));
u_int32_t flags;
int *hassubsp;
{
BTMETA *btmeta;
DB *pgset;
DBC *pgsc;
DB_MPOOLFILE *mpf;
PAGE *h;
db_pgno_t p, meta_pgno;
int ret, err_ret;
pgset = NULL;
pgsc = NULL;
mpf = dbp->mpf;
err_ret = 0;
meta_pgno = PGNO_BASE_MD;
if ((ret = __memp_fget(mpf, &meta_pgno, 0, &h)) != 0)
return (ret);
if (TYPE(h) == P_BTREEMETA)
btmeta = (BTMETA *)h;
else {
ret = 0;
goto err;
}
if ((ret = __db_vrfy_common(dbp, vdp, h, PGNO_BASE_MD, flags)) != 0 ||
(ret = __bam_vrfy_meta(dbp, vdp, btmeta, PGNO_BASE_MD, flags)) != 0)
goto err;
if (!F_ISSET(&btmeta->dbmeta, BTM_SUBDB)) {
ret = 0;
goto err;
}
*hassubsp = 1;
if ((ret = __memp_fput(mpf, h, 0)) != 0)
return (ret);
if ((ret = __db_vrfy_pgset(dbp->dbenv, dbp->pgsize, &pgset)) != 0)
return (ret);
if ((ret =
__db_meta2pgset(dbp, vdp, PGNO_BASE_MD, flags, pgset)) != 0)
goto err;
if ((ret = __db_cursor(pgset, NULL, &pgsc, 0)) != 0)
goto err;
while ((ret = __db_vrfy_pgset_next(pgsc, &p)) == 0) {
if ((ret = __memp_fget(mpf, &p, 0, &h)) != 0) {
err_ret = ret;
continue;
}
if ((ret = __db_vrfy_common(dbp, vdp, h, p, flags)) != 0 ||
(ret = __bam_vrfy(dbp,
vdp, h, p, flags | DB_NOORDERCHK)) != 0)
goto nextpg;
if (TYPE(h) != P_LBTREE)
goto nextpg;
else if ((ret = __db_salvage_subdbpg(
dbp, vdp, h, handle, callback, flags)) != 0)
err_ret = ret;
nextpg: if ((ret = __memp_fput(mpf, h, 0)) != 0)
err_ret = ret;
}
if (ret != DB_NOTFOUND)
goto err;
if ((ret = __db_c_close(pgsc)) != 0)
goto err;
ret = __db_close(pgset, NULL, 0);
return ((ret == 0 && err_ret != 0) ? err_ret : ret);
err: if (pgsc != NULL)
(void)__db_c_close(pgsc);
if (pgset != NULL)
(void)__db_close(pgset, NULL, 0);
(void)__memp_fput(mpf, h, 0);
return (ret);
}
static int
__db_salvage_subdbpg(dbp, vdp, master, handle, callback, flags)
DB *dbp;
VRFY_DBINFO *vdp;
PAGE *master;
void *handle;
int (*callback) __P((void *, const void *));
u_int32_t flags;
{
BKEYDATA *bkkey, *bkdata;
BOVERFLOW *bo;
DB *pgset;
DBC *pgsc;
DBT key;
DB_ENV *dbenv;
DB_MPOOLFILE *mpf;
PAGE *subpg;
db_indx_t i;
db_pgno_t meta_pgno, p;
int ret, err_ret, t_ret;
char *subdbname;
dbenv = dbp->dbenv;
mpf = dbp->mpf;
ret = err_ret = 0;
subdbname = NULL;
if ((ret = __db_vrfy_pgset(dbenv, dbp->pgsize, &pgset)) != 0)
return (ret);
for (i = 0; i < NUM_ENT(master); i += P_INDX) {
bkkey = GET_BKEYDATA(dbp, master, i);
bkdata = GET_BKEYDATA(dbp, master, i + O_INDX);
if (B_TYPE(bkkey->type) == B_OVERFLOW) {
bo = (BOVERFLOW *)bkkey;
if ((ret = __db_safe_goff(dbp, vdp,
bo->pgno, &key, &subdbname, flags)) != 0) {
err_ret = DB_VERIFY_BAD;
continue;
}
if ((ret = __os_realloc(dbenv,
key.size + 1, &subdbname)) != 0)
goto err;
subdbname[key.size] = '\0';
} else if (B_TYPE(bkkey->type) == B_KEYDATA) {
if ((ret = __os_realloc(dbenv,
bkkey->len + 1, &subdbname)) != 0)
goto err;
memcpy(subdbname, bkkey->data, bkkey->len);
subdbname[bkkey->len] = '\0';
}
if (bkdata->len != sizeof(db_pgno_t)) {
err_ret = DB_VERIFY_BAD;
continue;
}
memcpy(&meta_pgno, bkdata->data, sizeof(db_pgno_t));
DB_NTOHL(&meta_pgno);
if (!IS_VALID_PGNO(meta_pgno) ||
(ret = __memp_fget(mpf, &meta_pgno, 0, &subpg)) != 0) {
err_ret = ret;
continue;
}
if ((ret =
__db_vrfy_common(dbp, vdp, subpg, meta_pgno, flags)) != 0) {
err_ret = ret;
(void)__memp_fput(mpf, subpg, 0);
continue;
}
switch (TYPE(subpg)) {
case P_BTREEMETA:
if ((ret = __bam_vrfy_meta(dbp,
vdp, (BTMETA *)subpg, meta_pgno, flags)) != 0) {
err_ret = ret;
(void)__memp_fput(mpf, subpg, 0);
continue;
}
break;
case P_HASHMETA:
if ((ret = __ham_vrfy_meta(dbp,
vdp, (HMETA *)subpg, meta_pgno, flags)) != 0) {
err_ret = ret;
(void)__memp_fput(mpf, subpg, 0);
continue;
}
break;
default:
err_ret = DB_VERIFY_BAD;
continue;
}
if ((ret = __memp_fput(mpf, subpg, 0)) != 0) {
err_ret = ret;
continue;
}
if ((ret = __db_prheader(dbp,
subdbname, 0, 0, handle, callback, vdp, meta_pgno)) != 0)
goto err;
if ((ret = __db_meta2pgset(dbp, vdp, meta_pgno,
flags, pgset)) != 0) {
err_ret = ret;
continue;
}
if ((ret = __db_cursor(pgset, NULL, &pgsc, 0)) != 0)
goto err;
while ((ret = __db_vrfy_pgset_next(pgsc, &p)) == 0) {
if ((ret = __memp_fget(mpf, &p, 0, &subpg)) != 0) {
err_ret = ret;
continue;
}
if ((ret = __db_salvage(dbp, vdp, p, subpg,
handle, callback, flags)) != 0)
err_ret = ret;
if ((ret = __memp_fput(mpf, subpg, 0)) != 0)
err_ret = ret;
}
if (ret != DB_NOTFOUND)
goto err;
if ((ret = __db_c_close(pgsc)) != 0)
goto err;
if ((ret = __db_prfooter(handle, callback)) != 0)
goto err;
}
err: if (subdbname)
__os_free(dbenv, subdbname);
if ((t_ret = __db_close(pgset, NULL, 0)) != 0)
ret = t_ret;
if ((t_ret = __db_salvage_markdone(vdp, PGNO(master))) != 0)
return (t_ret);
return ((err_ret != 0) ? err_ret : ret);
}
static int
__db_meta2pgset(dbp, vdp, pgno, flags, pgset)
DB *dbp;
VRFY_DBINFO *vdp;
db_pgno_t pgno;
u_int32_t flags;
DB *pgset;
{
DB_MPOOLFILE *mpf;
PAGE *h;
int ret, t_ret;
mpf = dbp->mpf;
if ((ret = __memp_fget(mpf, &pgno, 0, &h)) != 0)
return (ret);
switch (TYPE(h)) {
case P_BTREEMETA:
ret = __bam_meta2pgset(dbp, vdp, (BTMETA *)h, flags, pgset);
break;
case P_HASHMETA:
ret = __ham_meta2pgset(dbp, vdp, (HMETA *)h, flags, pgset);
break;
default:
ret = DB_VERIFY_BAD;
break;
}
if ((t_ret = __memp_fput(mpf, h, 0)) != 0)
return (t_ret);
return (ret);
}
static u_int
__db_guesspgsize(dbenv, fhp)
DB_ENV *dbenv;
DB_FH *fhp;
{
db_pgno_t i;
size_t nr;
u_int32_t guess;
u_int8_t type;
for (guess = DB_MAX_PGSIZE; guess >= DB_MIN_PGSIZE; guess >>= 1) {
for (i = 1; i <= 3; i++) {
if (__os_seek(dbenv, fhp, guess,
i, SSZ(DBMETA, type), 0, DB_OS_SEEK_SET) != 0)
break;
if (__os_read(dbenv,
fhp, &type, 1, &nr) != 0 || nr == 0)
break;
if (type == P_INVALID || type >= P_PAGETYPE_MAX)
return (guess << 1);
}
}
return (DB_DEF_IOSIZE);
}