1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2000,2008 Oracle.  All rights reserved.
5 *
6 * $Id: db_vrfyutil.c,v 12.22 2008/03/18 17:29:39 mbrey Exp $
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12#include "dbinc/db_page.h"
13#include "dbinc/db_verify.h"
14#include "dbinc/db_am.h"
15
16static int __db_vrfy_childinc __P((DBC *, VRFY_CHILDINFO *));
17static int __db_vrfy_pageinfo_create __P((ENV *, VRFY_PAGEINFO **));
18
19/*
20 * __db_vrfy_dbinfo_create --
21 *	Allocate and initialize a VRFY_DBINFO structure.
22 *
23 * PUBLIC: int __db_vrfy_dbinfo_create
24 * PUBLIC:     __P((ENV *, DB_THREAD_INFO *, u_int32_t, VRFY_DBINFO **));
25 */
26int
27__db_vrfy_dbinfo_create(env, ip, pgsize, vdpp)
28	ENV *env;
29	DB_THREAD_INFO *ip;
30	u_int32_t pgsize;
31	VRFY_DBINFO **vdpp;
32{
33	DB *cdbp, *pgdbp, *pgset;
34	VRFY_DBINFO *vdp;
35	int ret;
36
37	vdp = NULL;
38	cdbp = pgdbp = pgset = NULL;
39
40	if ((ret = __os_calloc(NULL, 1, sizeof(VRFY_DBINFO), &vdp)) != 0)
41		goto err;
42
43	if ((ret = __db_create_internal(&cdbp, env, 0)) != 0)
44		goto err;
45
46	if ((ret = __db_set_flags(cdbp, DB_DUP)) != 0)
47		goto err;
48
49	if ((ret = __db_set_pagesize(cdbp, pgsize)) != 0)
50		goto err;
51
52	/* If transactional, make sure we don't log. */
53	if (TXN_ON(env) &&
54	    (ret = __db_set_flags(cdbp, DB_TXN_NOT_DURABLE)) != 0)
55		goto err;
56	if ((ret = __db_open(cdbp, ip,
57	    NULL, NULL, NULL, DB_BTREE, DB_CREATE, 0600, PGNO_BASE_MD)) != 0)
58		goto err;
59
60	if ((ret = __db_create_internal(&pgdbp, env, 0)) != 0)
61		goto err;
62
63	if ((ret = __db_set_pagesize(pgdbp, pgsize)) != 0)
64		goto err;
65
66	/* If transactional, make sure we don't log. */
67	if (TXN_ON(env) &&
68	    (ret = __db_set_flags(pgdbp, DB_TXN_NOT_DURABLE)) != 0)
69		goto err;
70
71	if ((ret = __db_open(pgdbp, ip,
72	    NULL, NULL, NULL, DB_BTREE, DB_CREATE, 0600, PGNO_BASE_MD)) != 0)
73		goto err;
74
75	if ((ret = __db_vrfy_pgset(env, ip, pgsize, &pgset)) != 0)
76		goto err;
77
78	LIST_INIT(&vdp->subdbs);
79	LIST_INIT(&vdp->activepips);
80
81	vdp->cdbp = cdbp;
82	vdp->pgdbp = pgdbp;
83	vdp->pgset = pgset;
84	vdp->thread_info = ip;
85	*vdpp = vdp;
86	return (0);
87
88err:	if (cdbp != NULL)
89		(void)__db_close(cdbp, NULL, 0);
90	if (pgdbp != NULL)
91		(void)__db_close(pgdbp, NULL, 0);
92	if (vdp != NULL)
93		__os_free(env, vdp);
94	return (ret);
95}
96
97/*
98 * __db_vrfy_dbinfo_destroy --
99 *	Destructor for VRFY_DBINFO.  Destroys VRFY_PAGEINFOs and deallocates
100 *	structure.
101 *
102 * PUBLIC: int __db_vrfy_dbinfo_destroy __P((ENV *, VRFY_DBINFO *));
103 */
104int
105__db_vrfy_dbinfo_destroy(env, vdp)
106	ENV *env;
107	VRFY_DBINFO *vdp;
108{
109	VRFY_CHILDINFO *c;
110	int t_ret, ret;
111
112	ret = 0;
113
114	/*
115	 * Discard active page structures.  Ideally there wouldn't be any,
116	 * but in some error cases we may not have cleared them all out.
117	 */
118	while (LIST_FIRST(&vdp->activepips) != NULL)
119		if ((t_ret = __db_vrfy_putpageinfo(
120		    env, vdp, LIST_FIRST(&vdp->activepips))) != 0) {
121			if (ret == 0)
122				ret = t_ret;
123			break;
124		}
125
126	/* Discard subdatabase list structures. */
127	while ((c = LIST_FIRST(&vdp->subdbs)) != NULL) {
128		LIST_REMOVE(c, links);
129		__os_free(NULL, c);
130	}
131
132	if ((t_ret = __db_close(vdp->pgdbp, NULL, 0)) != 0)
133		ret = t_ret;
134
135	if ((t_ret = __db_close(vdp->cdbp, NULL, 0)) != 0 && ret == 0)
136		ret = t_ret;
137
138	if ((t_ret = __db_close(vdp->pgset, NULL, 0)) != 0 && ret == 0)
139		ret = t_ret;
140
141	if (vdp->extents != NULL)
142		__os_free(env, vdp->extents);
143	__os_free(env, vdp);
144	return (ret);
145}
146
147/*
148 * __db_vrfy_getpageinfo --
149 *	Get a PAGEINFO structure for a given page, creating it if necessary.
150 *
151 * PUBLIC: int __db_vrfy_getpageinfo
152 * PUBLIC:     __P((VRFY_DBINFO *, db_pgno_t, VRFY_PAGEINFO **));
153 */
154int
155__db_vrfy_getpageinfo(vdp, pgno, pipp)
156	VRFY_DBINFO *vdp;
157	db_pgno_t pgno;
158	VRFY_PAGEINFO **pipp;
159{
160	DB *pgdbp;
161	DBT key, data;
162	ENV *env;
163	VRFY_PAGEINFO *pip;
164	int ret;
165
166	/*
167	 * We want a page info struct.  There are three places to get it from,
168	 * in decreasing order of preference:
169	 *
170	 * 1. vdp->activepips.  If it's already "checked out", we're
171	 *	already using it, we return the same exact structure with a
172	 *	bumped refcount.  This is necessary because this code is
173	 *	replacing array accesses, and it's common for f() to make some
174	 *	changes to a pip, and then call g() and h() which each make
175	 *	changes to the same pip.  vdps are never shared between threads
176	 *	(they're never returned to the application), so this is safe.
177	 * 2. The pgdbp.  It's not in memory, but it's in the database, so
178	 *	get it, give it a refcount of 1, and stick it on activepips.
179	 * 3. malloc.  It doesn't exist yet;  create it, then stick it on
180	 *	activepips.  We'll put it in the database when we putpageinfo
181	 *	later.
182	 */
183
184	/* Case 1. */
185	LIST_FOREACH(pip, &vdp->activepips, links)
186		if (pip->pgno == pgno)
187			goto found;
188
189	/* Case 2. */
190	pgdbp = vdp->pgdbp;
191	env = pgdbp->env;
192	memset(&key, 0, sizeof(DBT));
193	memset(&data, 0, sizeof(DBT));
194	F_SET(&data, DB_DBT_MALLOC);
195	key.data = &pgno;
196	key.size = sizeof(db_pgno_t);
197
198	if ((ret = __db_get(pgdbp,
199	    vdp->thread_info, NULL, &key, &data, 0)) == 0) {
200		/* Found it. */
201		DB_ASSERT(env, data.size == sizeof(VRFY_PAGEINFO));
202		pip = data.data;
203		LIST_INSERT_HEAD(&vdp->activepips, pip, links);
204		goto found;
205	} else if (ret != DB_NOTFOUND)	/* Something nasty happened. */
206		return (ret);
207
208	/* Case 3 */
209	if ((ret = __db_vrfy_pageinfo_create(env, &pip)) != 0)
210		return (ret);
211
212	LIST_INSERT_HEAD(&vdp->activepips, pip, links);
213found:	pip->pi_refcount++;
214
215	*pipp = pip;
216	return (0);
217}
218
219/*
220 * __db_vrfy_putpageinfo --
221 *	Put back a VRFY_PAGEINFO that we're done with.
222 *
223 * PUBLIC: int __db_vrfy_putpageinfo __P((ENV *,
224 * PUBLIC:     VRFY_DBINFO *, VRFY_PAGEINFO *));
225 */
226int
227__db_vrfy_putpageinfo(env, vdp, pip)
228	ENV *env;
229	VRFY_DBINFO *vdp;
230	VRFY_PAGEINFO *pip;
231{
232	DB *pgdbp;
233	DBT key, data;
234	VRFY_PAGEINFO *p;
235	int ret;
236
237	if (--pip->pi_refcount > 0)
238		return (0);
239
240	pgdbp = vdp->pgdbp;
241	memset(&key, 0, sizeof(DBT));
242	memset(&data, 0, sizeof(DBT));
243
244	key.data = &pip->pgno;
245	key.size = sizeof(db_pgno_t);
246	data.data = pip;
247	data.size = sizeof(VRFY_PAGEINFO);
248
249	if ((ret = __db_put(pgdbp,
250	     vdp->thread_info, NULL, &key, &data, 0)) != 0)
251		return (ret);
252
253	LIST_FOREACH(p, &vdp->activepips, links)
254		if (p == pip)
255			break;
256	if (p != NULL)
257		LIST_REMOVE(p, links);
258
259	__os_ufree(env, p);
260	return (0);
261}
262
263/*
264 * __db_vrfy_pgset --
265 *	Create a temporary database for the storing of sets of page numbers.
266 *	(A mapping from page number to int, used by the *_meta2pgset functions,
267 *	as well as for keeping track of which pages the verifier has seen.)
268 *
269 * PUBLIC: int __db_vrfy_pgset __P((ENV *,
270 * PUBLIC:     DB_THREAD_INFO *, u_int32_t, DB **));
271 */
272int
273__db_vrfy_pgset(env, ip, pgsize, dbpp)
274	ENV *env;
275	DB_THREAD_INFO *ip;
276	u_int32_t pgsize;
277	DB **dbpp;
278{
279	DB *dbp;
280	int ret;
281
282	if ((ret = __db_create_internal(&dbp, env, 0)) != 0)
283		return (ret);
284	if ((ret = __db_set_pagesize(dbp, pgsize)) != 0)
285		goto err;
286
287	/* If transactional, make sure we don't log. */
288	if (TXN_ON(env) &&
289	    (ret = __db_set_flags(dbp, DB_TXN_NOT_DURABLE)) != 0)
290		goto err;
291	if ((ret = __db_open(dbp, ip,
292	    NULL, NULL, NULL, DB_BTREE, DB_CREATE, 0600, PGNO_BASE_MD)) == 0)
293		*dbpp = dbp;
294	else
295err:		(void)__db_close(dbp, NULL, 0);
296
297	return (ret);
298}
299
300/*
301 * __db_vrfy_pgset_get --
302 *	Get the value associated in a page set with a given pgno.  Return
303 *	a 0 value (and succeed) if we've never heard of this page.
304 *
305 * PUBLIC: int __db_vrfy_pgset_get __P((DB *, DB_THREAD_INFO *, db_pgno_t,
306 * PUBLIC:     int *));
307 */
308int
309__db_vrfy_pgset_get(dbp, ip, pgno, valp)
310	DB *dbp;
311	DB_THREAD_INFO *ip;
312	db_pgno_t pgno;
313	int *valp;
314{
315	DBT key, data;
316	int ret, val;
317
318	memset(&key, 0, sizeof(DBT));
319	memset(&data, 0, sizeof(DBT));
320
321	key.data = &pgno;
322	key.size = sizeof(db_pgno_t);
323	data.data = &val;
324	data.ulen = sizeof(int);
325	F_SET(&data, DB_DBT_USERMEM);
326
327	if ((ret = __db_get(dbp, ip, NULL, &key, &data, 0)) == 0) {
328		DB_ASSERT(dbp->env, data.size == sizeof(int));
329	} else if (ret == DB_NOTFOUND)
330		val = 0;
331	else
332		return (ret);
333
334	*valp = val;
335	return (0);
336}
337
338/*
339 * __db_vrfy_pgset_inc --
340 *	Increment the value associated with a pgno by 1.
341 *
342 * PUBLIC: int __db_vrfy_pgset_inc __P((DB *, DB_THREAD_INFO *, db_pgno_t));
343 */
344int
345__db_vrfy_pgset_inc(dbp, ip, pgno)
346	DB *dbp;
347	DB_THREAD_INFO *ip;
348	db_pgno_t pgno;
349{
350	DBT key, data;
351	int ret;
352	int val;
353
354	memset(&key, 0, sizeof(DBT));
355	memset(&data, 0, sizeof(DBT));
356
357	val = 0;
358
359	key.data = &pgno;
360	key.size = sizeof(db_pgno_t);
361	data.data = &val;
362	data.ulen = sizeof(int);
363	F_SET(&data, DB_DBT_USERMEM);
364
365	if ((ret = __db_get(dbp, ip, NULL, &key, &data, 0)) == 0) {
366		DB_ASSERT(dbp->env, data.size == sizeof(int));
367	} else if (ret != DB_NOTFOUND)
368		return (ret);
369
370	data.size = sizeof(int);
371	++val;
372
373	return (__db_put(dbp, ip, NULL, &key, &data, 0));
374}
375
376/*
377 * __db_vrfy_pgset_next --
378 *	Given a cursor open in a pgset database, get the next page in the
379 *	set.
380 *
381 * PUBLIC: int __db_vrfy_pgset_next __P((DBC *, db_pgno_t *));
382 */
383int
384__db_vrfy_pgset_next(dbc, pgnop)
385	DBC *dbc;
386	db_pgno_t *pgnop;
387{
388	DBT key, data;
389	db_pgno_t pgno;
390	int ret;
391
392	memset(&key, 0, sizeof(DBT));
393	memset(&data, 0, sizeof(DBT));
394	/* We don't care about the data, just the keys. */
395	F_SET(&data, DB_DBT_USERMEM | DB_DBT_PARTIAL);
396	F_SET(&key, DB_DBT_USERMEM);
397	key.data = &pgno;
398	key.ulen = sizeof(db_pgno_t);
399
400	if ((ret = __dbc_get(dbc, &key, &data, DB_NEXT)) != 0)
401		return (ret);
402
403	DB_ASSERT(dbc->env, key.size == sizeof(db_pgno_t));
404	*pgnop = pgno;
405
406	return (0);
407}
408
409/*
410 * __db_vrfy_childcursor --
411 *	Create a cursor to walk the child list with.  Returns with a nonzero
412 *	final argument if the specified page has no children.
413 *
414 * PUBLIC: int __db_vrfy_childcursor __P((VRFY_DBINFO *, DBC **));
415 */
416int
417__db_vrfy_childcursor(vdp, dbcp)
418	VRFY_DBINFO *vdp;
419	DBC **dbcp;
420{
421	DB *cdbp;
422	DBC *dbc;
423	int ret;
424
425	cdbp = vdp->cdbp;
426
427	if ((ret = __db_cursor(cdbp, vdp->thread_info, NULL, &dbc, 0)) == 0)
428		*dbcp = dbc;
429
430	return (ret);
431}
432
433/*
434 * __db_vrfy_childput --
435 *	Add a child structure to the set for a given page.
436 *
437 * PUBLIC: int __db_vrfy_childput
438 * PUBLIC:     __P((VRFY_DBINFO *, db_pgno_t, VRFY_CHILDINFO *));
439 */
440int
441__db_vrfy_childput(vdp, pgno, cip)
442	VRFY_DBINFO *vdp;
443	db_pgno_t pgno;
444	VRFY_CHILDINFO *cip;
445{
446	DB *cdbp;
447	DBC *cc;
448	DBT key, data;
449	VRFY_CHILDINFO *oldcip;
450	int ret;
451
452	cdbp = vdp->cdbp;
453	memset(&key, 0, sizeof(DBT));
454	memset(&data, 0, sizeof(DBT));
455
456	key.data = &pgno;
457	key.size = sizeof(db_pgno_t);
458
459	/*
460	 * We want to avoid adding multiple entries for a single child page;
461	 * we only need to verify each child once, even if a child (such
462	 * as an overflow key) is multiply referenced.
463	 *
464	 * However, we also need to make sure that when walking the list
465	 * of children, we encounter them in the order they're referenced
466	 * on a page.  (This permits us, for example, to verify the
467	 * prev_pgno/next_pgno chain of Btree leaf pages.)
468	 *
469	 * Check the child database to make sure that this page isn't
470	 * already a child of the specified page number.  If it's not,
471	 * put it at the end of the duplicate set.
472	 */
473	if ((ret = __db_vrfy_childcursor(vdp, &cc)) != 0)
474		return (ret);
475	for (ret = __db_vrfy_ccset(cc, pgno, &oldcip); ret == 0;
476	    ret = __db_vrfy_ccnext(cc, &oldcip))
477		if (oldcip->pgno == cip->pgno) {
478			/*
479			 * Found a matching child.  Increment its reference
480			 * count--we've run into it again--but don't put it
481			 * again.
482			 */
483			if ((ret = __db_vrfy_childinc(cc, oldcip)) != 0 ||
484			    (ret = __db_vrfy_ccclose(cc)) != 0)
485				return (ret);
486			return (0);
487		}
488	if (ret != DB_NOTFOUND) {
489		(void)__db_vrfy_ccclose(cc);
490		return (ret);
491	}
492	if ((ret = __db_vrfy_ccclose(cc)) != 0)
493		return (ret);
494
495	cip->refcnt = 1;
496	data.data = cip;
497	data.size = sizeof(VRFY_CHILDINFO);
498
499	return (__db_put(cdbp, vdp->thread_info, NULL, &key, &data, 0));
500}
501
502/*
503 * __db_vrfy_childinc --
504 *	Increment the refcount of the VRFY_CHILDINFO struct that the child
505 * cursor is pointing to.  (The caller has just retrieved this struct, and
506 * passes it in as cip to save us a get.)
507 */
508static int
509__db_vrfy_childinc(dbc, cip)
510	DBC *dbc;
511	VRFY_CHILDINFO *cip;
512{
513	DBT key, data;
514
515	memset(&key, 0, sizeof(DBT));
516	memset(&data, 0, sizeof(DBT));
517
518	cip->refcnt++;
519	data.data = cip;
520	data.size = sizeof(VRFY_CHILDINFO);
521
522	return (__dbc_put(dbc, &key, &data, DB_CURRENT));
523}
524
525/*
526 * __db_vrfy_ccset --
527 *	Sets a cursor created with __db_vrfy_childcursor to the first
528 *	child of the given pgno, and returns it in the third arg.
529 *
530 * PUBLIC: int __db_vrfy_ccset __P((DBC *, db_pgno_t, VRFY_CHILDINFO **));
531 */
532int
533__db_vrfy_ccset(dbc, pgno, cipp)
534	DBC *dbc;
535	db_pgno_t pgno;
536	VRFY_CHILDINFO **cipp;
537{
538	DBT key, data;
539	int ret;
540
541	memset(&key, 0, sizeof(DBT));
542	memset(&data, 0, sizeof(DBT));
543
544	key.data = &pgno;
545	key.size = sizeof(db_pgno_t);
546
547	if ((ret = __dbc_get(dbc, &key, &data, DB_SET)) != 0)
548		return (ret);
549
550	DB_ASSERT(dbc->env, data.size == sizeof(VRFY_CHILDINFO));
551	*cipp = (VRFY_CHILDINFO *)data.data;
552
553	return (0);
554}
555
556/*
557 * __db_vrfy_ccnext --
558 *	Gets the next child of the given cursor created with
559 *	__db_vrfy_childcursor, and returns it in the memory provided in the
560 *	second arg.
561 *
562 * PUBLIC: int __db_vrfy_ccnext __P((DBC *, VRFY_CHILDINFO **));
563 */
564int
565__db_vrfy_ccnext(dbc, cipp)
566	DBC *dbc;
567	VRFY_CHILDINFO **cipp;
568{
569	DBT key, data;
570	int ret;
571
572	memset(&key, 0, sizeof(DBT));
573	memset(&data, 0, sizeof(DBT));
574
575	if ((ret = __dbc_get(dbc, &key, &data, DB_NEXT_DUP)) != 0)
576		return (ret);
577
578	DB_ASSERT(dbc->env, data.size == sizeof(VRFY_CHILDINFO));
579	*cipp = (VRFY_CHILDINFO *)data.data;
580
581	return (0);
582}
583
584/*
585 * __db_vrfy_ccclose --
586 *	Closes the cursor created with __db_vrfy_childcursor.
587 *
588 *	This doesn't actually do anything interesting now, but it's
589 *	not inconceivable that we might change the internal database usage
590 *	and keep the interfaces the same, and a function call here or there
591 *	seldom hurts anyone.
592 *
593 * PUBLIC: int __db_vrfy_ccclose __P((DBC *));
594 */
595int
596__db_vrfy_ccclose(dbc)
597	DBC *dbc;
598{
599
600	return (__dbc_close(dbc));
601}
602
603/*
604 * __db_vrfy_pageinfo_create --
605 *	Constructor for VRFY_PAGEINFO;  allocates and initializes.
606 */
607static int
608__db_vrfy_pageinfo_create(env, pipp)
609	ENV *env;
610	VRFY_PAGEINFO **pipp;
611{
612	VRFY_PAGEINFO *pip;
613	int ret;
614
615	/*
616	 * pageinfo structs are sometimes allocated here and sometimes
617	 * allocated by fetching them from a database with DB_DBT_MALLOC.
618	 * There's no easy way for the destructor to tell which was
619	 * used, and so we always allocate with __os_umalloc so we can free
620	 * with __os_ufree.
621	 */
622	if ((ret = __os_umalloc(env, sizeof(VRFY_PAGEINFO), &pip)) != 0)
623		return (ret);
624	memset(pip, 0, sizeof(VRFY_PAGEINFO));
625
626	*pipp = pip;
627	return (0);
628}
629
630/*
631 * __db_salvage_init --
632 *	Set up salvager database.
633 *
634 * PUBLIC: int  __db_salvage_init __P((VRFY_DBINFO *));
635 */
636int
637__db_salvage_init(vdp)
638	VRFY_DBINFO *vdp;
639{
640	DB *dbp;
641	int ret;
642
643	if ((ret = __db_create_internal(&dbp, NULL, 0)) != 0)
644		return (ret);
645
646	if ((ret = __db_set_pagesize(dbp, 1024)) != 0)
647		goto err;
648
649	if ((ret = __db_open(dbp, vdp->thread_info,
650	    NULL, NULL, NULL, DB_BTREE, DB_CREATE, 0, PGNO_BASE_MD)) != 0)
651		goto err;
652
653	vdp->salvage_pages = dbp;
654	return (0);
655
656err:	(void)__db_close(dbp, NULL, 0);
657	return (ret);
658}
659
660/*
661 * __db_salvage_destroy --
662 *	Close salvager database.
663 * PUBLIC: void  __db_salvage_destroy __P((VRFY_DBINFO *));
664 */
665void
666__db_salvage_destroy(vdp)
667	VRFY_DBINFO *vdp;
668{
669	(void)__db_close(vdp->salvage_pages, NULL, 0);
670}
671
672/*
673 * __db_salvage_getnext --
674 *	Get the next (first) unprinted page in the database of pages we need to
675 *	print still.  Delete entries for any already-printed pages we encounter
676 *	in this search, as well as the page we're returning.
677 *
678 * PUBLIC: int __db_salvage_getnext
679 * PUBLIC:     __P((VRFY_DBINFO *, DBC **, db_pgno_t *, u_int32_t *, int));
680 */
681int
682__db_salvage_getnext(vdp, dbcp, pgnop, pgtypep, skip_overflow)
683	VRFY_DBINFO *vdp;
684	DBC **dbcp;
685	db_pgno_t *pgnop;
686	u_int32_t *pgtypep;
687	int skip_overflow;
688{
689	DB *dbp;
690	DBT key, data;
691	int ret;
692	u_int32_t pgtype;
693
694	dbp = vdp->salvage_pages;
695
696	memset(&key, 0, sizeof(DBT));
697	memset(&data, 0, sizeof(DBT));
698
699	if (*dbcp == NULL &&
700	    (ret = __db_cursor(dbp, vdp->thread_info, NULL, dbcp, 0)) != 0)
701		return (ret);
702
703	while ((ret = __dbc_get(*dbcp, &key, &data, DB_NEXT)) == 0) {
704		DB_ASSERT(dbp->env, data.size == sizeof(u_int32_t));
705		memcpy(&pgtype, data.data, sizeof(pgtype));
706
707		if (skip_overflow && pgtype == SALVAGE_OVERFLOW)
708			continue;
709
710		if ((ret = __dbc_del(*dbcp, 0)) != 0)
711			return (ret);
712		if (pgtype != SALVAGE_IGNORE) {
713			DB_ASSERT(dbp->env, key.size == sizeof(db_pgno_t));
714			DB_ASSERT(dbp->env, data.size == sizeof(u_int32_t));
715
716			*pgnop = *(db_pgno_t *)key.data;
717			*pgtypep = *(u_int32_t *)data.data;
718			break;
719		}
720	}
721
722	return (ret);
723}
724
725/*
726 * __db_salvage_isdone --
727 *	Return whether or not the given pgno is already marked
728 *	SALVAGE_IGNORE (meaning that we don't need to print it again).
729 *
730 *	Returns DB_KEYEXIST if it is marked, 0 if not, or another error on
731 *	error.
732 *
733 * PUBLIC: int __db_salvage_isdone __P((VRFY_DBINFO *, db_pgno_t));
734 */
735int
736__db_salvage_isdone(vdp, pgno)
737	VRFY_DBINFO *vdp;
738	db_pgno_t pgno;
739{
740	DB *dbp;
741	DBT key, data;
742	int ret;
743	u_int32_t currtype;
744
745	dbp = vdp->salvage_pages;
746
747	memset(&key, 0, sizeof(DBT));
748	memset(&data, 0, sizeof(DBT));
749
750	currtype = SALVAGE_INVALID;
751	data.data = &currtype;
752	data.ulen = sizeof(u_int32_t);
753	data.flags = DB_DBT_USERMEM;
754
755	key.data = &pgno;
756	key.size = sizeof(db_pgno_t);
757
758	/*
759	 * Put an entry for this page, with pgno as key and type as data,
760	 * unless it's already there and is marked done.
761	 * If it's there and is marked anything else, that's fine--we
762	 * want to mark it done.
763	 */
764	if ((ret = __db_get(dbp,
765	    vdp->thread_info, NULL, &key, &data, 0)) == 0) {
766		/*
767		 * The key's already here.  Check and see if it's already
768		 * marked done.  If it is, return DB_KEYEXIST.  If it's not,
769		 * return 0.
770		 */
771		if (currtype == SALVAGE_IGNORE)
772			return (DB_KEYEXIST);
773		else
774			return (0);
775	} else if (ret != DB_NOTFOUND)
776		return (ret);
777
778	/* The pgno is not yet marked anything; return 0. */
779	return (0);
780}
781
782/*
783 * __db_salvage_markdone --
784 *	Mark as done a given page.
785 *
786 * PUBLIC: int __db_salvage_markdone __P((VRFY_DBINFO *, db_pgno_t));
787 */
788int
789__db_salvage_markdone(vdp, pgno)
790	VRFY_DBINFO *vdp;
791	db_pgno_t pgno;
792{
793	DB *dbp;
794	DBT key, data;
795	int pgtype, ret;
796	u_int32_t currtype;
797
798	pgtype = SALVAGE_IGNORE;
799	dbp = vdp->salvage_pages;
800
801	memset(&key, 0, sizeof(DBT));
802	memset(&data, 0, sizeof(DBT));
803
804	currtype = SALVAGE_INVALID;
805	data.data = &currtype;
806	data.ulen = sizeof(u_int32_t);
807	data.flags = DB_DBT_USERMEM;
808
809	key.data = &pgno;
810	key.size = sizeof(db_pgno_t);
811
812	/*
813	 * Put an entry for this page, with pgno as key and type as data,
814	 * unless it's already there and is marked done.
815	 * If it's there and is marked anything else, that's fine--we
816	 * want to mark it done, but db_salvage_isdone only lets
817	 * us know if it's marked IGNORE.
818	 *
819	 * We don't want to return DB_KEYEXIST, though;  this will
820	 * likely get passed up all the way and make no sense to the
821	 * application.  Instead, use DB_VERIFY_BAD to indicate that
822	 * we've seen this page already--it probably indicates a
823	 * multiply-linked page.
824	 */
825	if ((ret = __db_salvage_isdone(vdp, pgno)) != 0)
826		return (ret == DB_KEYEXIST ? DB_VERIFY_BAD : ret);
827
828	data.size = sizeof(u_int32_t);
829	data.data = &pgtype;
830
831	return (__db_put(dbp, vdp->thread_info, NULL, &key, &data, 0));
832}
833
834/*
835 * __db_salvage_markneeded --
836 *	If it has not yet been printed, make note of the fact that a page
837 *	must be dealt with later.
838 *
839 * PUBLIC: int __db_salvage_markneeded
840 * PUBLIC:     __P((VRFY_DBINFO *, db_pgno_t, u_int32_t));
841 */
842int
843__db_salvage_markneeded(vdp, pgno, pgtype)
844	VRFY_DBINFO *vdp;
845	db_pgno_t pgno;
846	u_int32_t pgtype;
847{
848	DB *dbp;
849	DBT key, data;
850	int ret;
851
852	dbp = vdp->salvage_pages;
853
854	memset(&key, 0, sizeof(DBT));
855	memset(&data, 0, sizeof(DBT));
856
857	key.data = &pgno;
858	key.size = sizeof(db_pgno_t);
859
860	data.data = &pgtype;
861	data.size = sizeof(u_int32_t);
862
863	/*
864	 * Put an entry for this page, with pgno as key and type as data,
865	 * unless it's already there, in which case it's presumably
866	 * already been marked done.
867	 */
868	ret = __db_put(dbp,
869	     vdp->thread_info, NULL, &key, &data, DB_NOOVERWRITE);
870	return (ret == DB_KEYEXIST ? 0 : ret);
871}
872
873/*
874 * __db_vrfy_prdbt --
875 *	Print out a DBT data element from a verification routine.
876 *
877 * PUBLIC: int __db_vrfy_prdbt __P((DBT *, int, const char *, void *,
878 * PUBLIC:     int (*)(void *, const void *), int, VRFY_DBINFO *));
879 */
880int
881__db_vrfy_prdbt(dbtp, checkprint, prefix, handle, callback, is_recno, vdp)
882	DBT *dbtp;
883	int checkprint;
884	const char *prefix;
885	void *handle;
886	int (*callback) __P((void *, const void *));
887	int is_recno;
888	VRFY_DBINFO *vdp;
889{
890	if (vdp != NULL) {
891		/*
892		 * If vdp is non-NULL, we might be the first key in the
893		 * "fake" subdatabase used for key/data pairs we can't
894		 * associate with a known subdb.
895		 *
896		 * Check and clear the SALVAGE_PRINTHEADER flag;  if
897		 * it was set, print a subdatabase header.
898		 */
899		if (F_ISSET(vdp, SALVAGE_PRINTHEADER)) {
900			(void)__db_prheader(
901			    NULL, "__OTHER__", 0, 0, handle, callback, vdp, 0);
902			F_CLR(vdp, SALVAGE_PRINTHEADER);
903			F_SET(vdp, SALVAGE_PRINTFOOTER);
904		}
905
906		/*
907		 * Even if the printable flag wasn't set by our immediate
908		 * caller, it may be set on a salvage-wide basis.
909		 */
910		if (F_ISSET(vdp, SALVAGE_PRINTABLE))
911			checkprint = 1;
912	}
913	return (
914	    __db_prdbt(dbtp, checkprint, prefix, handle, callback, is_recno));
915}
916