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