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