#include "db_config.h"
#include "db_int.h"
#include "dbinc/db_page.h"
#include "dbinc/db_verify.h"
#include "dbinc/heap.h"
#include "dbinc/lock.h"
#include "dbinc/mp.h"
static int __heap_safe_gsplit __P((DB *, VRFY_DBINFO *, PAGE *, db_indx_t,
DBT *));
static int __heap_verify_offset_cmp __P((const void *, const void *));
int
__heap_vrfy_meta(dbp, vdp, meta, pgno, flags)
DB *dbp;
VRFY_DBINFO *vdp;
HEAPMETA *meta;
db_pgno_t pgno;
u_int32_t flags;
{
HEAP *h;
VRFY_PAGEINFO *pip;
db_pgno_t last_pgno, max_pgno, npgs;
int isbad, ret;
if ((ret = __db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0)
return (ret);
isbad = 0;
if (!F_ISSET(pip, VRFY_INCOMPLETE))
EPRINT((dbp->env, DB_STR_A("1156",
"Page %lu: Heap databases must be one-per-file",
"%lu"), (u_long)pgno));
if ((ret = __db_vrfy_meta(dbp, vdp, &meta->dbmeta, pgno, flags)) != 0) {
if (ret == DB_VERIFY_BAD)
isbad = 1;
else
goto err;
}
h = (HEAP *)dbp->heap_internal;
h->region_size = meta->region_size;
last_pgno = meta->dbmeta.last_pgno;
if (meta->nregions != HEAP_REGION_NUM(dbp, last_pgno)) {
EPRINT((dbp->env, DB_STR_A("1157",
"Page %lu: Number of heap regions incorrect",
"%lu"), (u_long)pgno));
isbad = 1;
}
if (meta->gbytes != 0 || meta->bytes != 0) {
npgs = (db_pgno_t)(meta->gbytes * (GIGABYTE / dbp->pgsize));
npgs += (db_pgno_t)(meta->bytes / dbp->pgsize);
max_pgno = npgs - 1;
if (last_pgno > max_pgno) {
EPRINT((dbp->env, DB_STR_A("1158",
"Page %lu: last_pgno beyond end of fixed size heap",
"%lu"), (u_long)pgno));
isbad = 1;
}
}
err: if (LF_ISSET(DB_SALVAGE))
ret = __db_salvage_markdone(vdp, pgno);
return (ret == 0 && isbad == 1 ? DB_VERIFY_BAD : ret);
}
int
__heap_vrfy(dbp, vdp, h, pgno, flags)
DB *dbp;
VRFY_DBINFO *vdp;
PAGE *h;
db_pgno_t pgno;
u_int32_t flags;
{
HEAPHDR *hdr;
int cnt, i, j, ret;
db_indx_t *offsets, *offtbl, end;
if ((ret = __db_vrfy_datapage(dbp, vdp, h, pgno, flags)) != 0)
goto err;
if (TYPE(h) == P_IHEAP)
return (0);
offtbl = HEAP_OFFSETTBL(dbp, h);
if ((ret = __os_malloc(dbp->env,
NUM_ENT(h) * sizeof(db_indx_t), &offsets)) != 0)
goto err;
cnt = 0;
for (i = 0; i <= HEAP_HIGHINDX(h); i++) {
if (offtbl[i] == 0)
continue;
if (cnt >= NUM_ENT(h)) {
EPRINT((dbp->env, DB_STR_A("1159",
"Page %lu: incorrect number of entries in page's offset table",
"%lu"), (u_long)pgno));
ret = DB_VERIFY_BAD;
goto err;
}
hdr = (HEAPHDR *)P_ENTRY(dbp, h, i);
if (!F_ISSET(hdr, HEAP_RECSPLIT) &&
F_ISSET(hdr, HEAP_RECFIRST | HEAP_RECLAST)) {
EPRINT((dbp->env, DB_STR_A("1165",
"Page %lu: record %lu has invalid flags",
"%lu %lu"), (u_long)pgno, (u_long)i));
ret = DB_VERIFY_BAD;
goto err;
}
offsets[cnt] = offtbl[i];
cnt++;
}
if (cnt == 0) {
ret = 0;
goto err;
}
qsort(offsets, cnt, sizeof(db_indx_t), __heap_verify_offset_cmp);
for (i = 0; i < cnt - 1; i++) {
hdr = (HEAPHDR *)((u_int8_t *)h + offsets[i]);
end = offsets[i] + HEAP_HDRSIZE(hdr) + hdr->size;
if (end > offsets[i+1]) {
for (j = 0; j < HEAP_HIGHINDX(h); j++)
if (offtbl[j] == offsets[i])
break;
EPRINT((dbp->env, DB_STR_A("1160",
"Page %lu: record %lu (length %lu) overlaps next record",
"%lu %lu %lu"),
(u_long)pgno, (u_long)j, (u_long)hdr->size));
ret = DB_VERIFY_BAD;
}
}
hdr = (HEAPHDR *)((u_int8_t *)h + offsets[i]);
end = offsets[i] + HEAP_HDRSIZE(hdr) + hdr->size;
if (end > dbp->pgsize) {
for (j = 0; j < HEAP_HIGHINDX(h); j++)
if (offtbl[j] == offsets[i])
break;
EPRINT((dbp->env, DB_STR_A("1161",
"Page %lu: record %lu (length %lu) beyond end of page",
"%lu %lu %lu"),
(u_long)pgno, (u_long)j, (u_long)hdr->size));
ret = DB_VERIFY_BAD;
}
err: __os_free(dbp->env, offsets);
return (ret);
}
static int
__heap_verify_offset_cmp(off1, off2)
const void *off1;
const void *off2;
{
return (*(db_indx_t *)off1 - *(db_indx_t *)off2);
}
int
__heap_vrfy_structure(dbp, vdp, flags)
DB *dbp;
VRFY_DBINFO *vdp;
u_int32_t flags;
{
VRFY_PAGEINFO *pip;
db_pgno_t i, next_region, high_pgno;
int ret, isbad;
isbad = 0;
if ((ret = __db_vrfy_getpageinfo(vdp, PGNO_BASE_MD, &pip)) != 0)
return (ret);
if (pip->type != P_HEAPMETA) {
EPRINT((dbp->env, DB_STR_A("1162",
"Page %lu: heap database has no meta page", "%lu"),
(u_long)PGNO_BASE_MD));
isbad = 1;
goto err;
}
if ((ret = __db_vrfy_pgset_inc(
vdp->pgset, vdp->thread_info, vdp->txn, 0)) != 0)
goto err;
next_region = FIRST_HEAP_RPAGE;
high_pgno = 0;
for (i = 1; i <= vdp->last_pgno; i++) {
if (!LF_ISSET(DB_SALVAGE))
__db_vrfy_struct_feedback(dbp, vdp);
if ((ret = __db_vrfy_putpageinfo(dbp->env, vdp, pip)) != 0 ||
(ret = __db_vrfy_getpageinfo(vdp, i, &pip)) != 0)
return (ret);
if (i != next_region &&
pip->type != P_HEAP && pip->type != P_INVALID) {
EPRINT((dbp->env, DB_STR_A("1163",
"Page %lu: heap database page of incorrect type %lu",
"%lu %lu"), (u_long)i, (u_long)pip->type));
isbad = 1;
} else if (i == next_region && pip->type != P_IHEAP
#ifndef HAVE_FTRUNCATE
&& pip->type != P_INVALID
#endif
) {
EPRINT((dbp->env, DB_STR_A("1164",
"Page %lu: heap database missing region page (page type %lu)",
"%lu %lu"), (u_long)i, (u_long)pip->type));
isbad = 1;
} else if ((ret = __db_vrfy_pgset_inc(vdp->pgset,
vdp->thread_info, vdp->txn, i)) != 0)
goto err;
if (i == next_region) {
high_pgno = pip->prev_pgno;
next_region += HEAP_REGION_SIZE(dbp) + 1;
} else if (pip->type != P_INVALID && i > high_pgno) {
EPRINT((dbp->env, DB_STR_A("1166",
"Page %lu heap database page beyond high page in region",
"%lu"), (u_long) i));
isbad = 1;
}
}
err: if ((ret = __db_vrfy_putpageinfo(dbp->env, vdp, pip)) != 0)
return (ret);
return (isbad == 1 ? DB_VERIFY_BAD : 0);
}
int
__heap_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;
HEAPHDR *hdr;
db_indx_t i, *offtbl;
int err_ret, ret, t_ret;
COMPQUIET(flags, 0);
memset(&dbt, 0, sizeof(DBT));
offtbl = (db_indx_t *)HEAP_OFFSETTBL(dbp, h);
err_ret = ret = t_ret = 0;
for (i = 0; i <= HEAP_HIGHINDX(h); i++) {
if (offtbl[i] == 0)
continue;
hdr = (HEAPHDR *)P_ENTRY(dbp, h, i);
if (F_ISSET(hdr, HEAP_RECSPLIT)) {
if (!F_ISSET(hdr, HEAP_RECFIRST))
continue;
dbt.size = ((HEAPSPLITHDR *)hdr)->tsize;
if (dbt.size > dbp->pgsize * 4)
dbt.size = dbp->pgsize * 4;
if ((ret =
__os_malloc(dbp->env, dbt.size, &dbt.data)) != 0)
goto err;
__heap_safe_gsplit(dbp, vdp, h, i, &dbt);
} else {
dbt.data = (u_int8_t *)hdr + HEAP_HDRSIZE(hdr);
dbt.size = hdr->size;
}
if ((ret = __db_vrfy_prdbt(&dbt,
0, " ", handle, callback, 0, 0, vdp)) != 0)
err_ret = ret;
if (F_ISSET(hdr, HEAP_RECSPLIT))
__os_free(dbp->env, dbt.data);
}
err: if ((t_ret = __db_salvage_markdone(vdp, pgno)) != 0)
return (t_ret);
return ((ret == 0 && err_ret != 0) ? err_ret : ret);
}
static int
__heap_safe_gsplit(dbp, vdp, h, i, dbt)
DB *dbp;
VRFY_DBINFO *vdp;
PAGE *h;
db_indx_t i;
DBT *dbt;
{
DB_MPOOLFILE *mpf;
HEAPSPLITHDR *hdr;
int gotpg, ret, t_ret;
u_int32_t bufsz, reclen;
u_int8_t *buf;
mpf = dbp->mpf;
buf = dbt->data;
bufsz = dbt->size;
dbt->size = 0;
ret = 0;
gotpg = 0;
for (;;) {
hdr = (HEAPSPLITHDR *)P_ENTRY(dbp, h, i);
reclen = hdr->std_hdr.size;
if (dbt->size + reclen > bufsz) {
bufsz = dbt->size + reclen;
if ((ret = __os_realloc(
dbp->env, bufsz, &dbt->data)) != 0)
goto err;
buf = (u_int8_t *)dbt->data + dbt->size;
}
memcpy(buf, (u_int8_t *)hdr + sizeof(HEAPSPLITHDR), reclen);
buf += reclen;
dbt->size += reclen;
if (F_ISSET(&hdr->std_hdr, HEAP_RECLAST))
break;
if (gotpg && (ret = __memp_fput(mpf,
vdp->thread_info, h, DB_PRIORITY_UNCHANGED)) != 0)
return (ret);
gotpg = 0;
if ((ret = __memp_fget(mpf,
&hdr->nextpg, vdp->thread_info, NULL, 0, &h)) != 0)
goto err;
gotpg = 1;
i = hdr->nextindx;
}
err: if (gotpg && (t_ret = __memp_fput(
mpf, vdp->thread_info, h, DB_PRIORITY_UNCHANGED)) != 0 && ret == 0)
t_ret = ret;
return (ret);
}
int
__heap_meta2pgset(dbp, vdp, heapmeta, pgset)
DB *dbp;
VRFY_DBINFO *vdp;
HEAPMETA *heapmeta;
DB *pgset;
{
db_pgno_t pgno, last;
int ret;
COMPQUIET(dbp, NULL);
last = heapmeta->dbmeta.last_pgno;
ret = 0;
for (pgno = 1; pgno <= last; pgno++)
if ((ret = __db_vrfy_pgset_inc(
pgset, vdp->thread_info, vdp->txn, pgno)) != 0)
break;
return (ret);
}