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