1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 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/btree.h"
14 #include "dbinc/hash.h"
15 #include "dbinc/heap.h"
16 #include "dbinc/mp.h"
17 #include "dbinc/partition.h"
18 #include "dbinc/qam.h"
19 #include "dbinc/db_verify.h"
20 
21 static int	 __db_bmeta __P((ENV *, DB *, BTMETA *, u_int32_t));
22 static int	 __db_heapmeta __P((ENV *, DB *, HEAPMETA *, u_int32_t));
23 static int	 __db_heapint __P((DB *, HEAPPG *, u_int32_t));
24 static int	 __db_hmeta __P((ENV *, DB *, HMETA *, u_int32_t));
25 static void	 __db_meta __P((ENV *, DB *, DBMETA *, FN const *, u_int32_t));
26 static void	 __db_proff __P((ENV *, DB_MSGBUF *, void *));
27 static int	 __db_qmeta __P((ENV *, DB *, QMETA *, u_int32_t));
28 #ifdef HAVE_STATISTICS
29 static void	 __db_prdb __P((DB *, u_int32_t));
30 static int	 __db_prtree __P((DB *, DB_TXN *,
31 		    u_int32_t, db_pgno_t, db_pgno_t));
32 #endif
33 
34 /*
35  * __db_loadme --
36  *	A nice place to put a breakpoint.
37  *
38  * PUBLIC: void __db_loadme __P((void));
39  */
40 void
__db_loadme()41 __db_loadme()
42 {
43 	pid_t pid;
44 
45 	__os_id(NULL, &pid, NULL);
46 }
47 
48 #ifdef HAVE_STATISTICS
49 /*
50  * __db_dumptree --
51  *	Dump the tree to a file.
52  *
53  * PUBLIC: int __db_dumptree __P((DB *, DB_TXN *,
54  * PUBLIC:     char *, char *, db_pgno_t, db_pgno_t));
55  */
56 int
__db_dumptree(dbp,txn,op,name,first,last)57 __db_dumptree(dbp, txn, op, name, first, last)
58 	DB *dbp;
59 	DB_TXN *txn;
60 	char *op, *name;
61 	db_pgno_t first, last;
62 {
63 	ENV *env;
64 	FILE *fp, *orig_fp;
65 	u_int32_t flags;
66 	int ret;
67 
68 	env = dbp->env;
69 
70 	for (flags = 0; *op != '\0'; ++op)
71 		switch (*op) {
72 		case 'a':
73 			LF_SET(DB_PR_PAGE);
74 			break;
75 		case 'h':
76 			break;
77 		case 'r':
78 			LF_SET(DB_PR_RECOVERYTEST);
79 			break;
80 		default:
81 			return (EINVAL);
82 		}
83 
84 	if (name != NULL) {
85 		if ((fp = fopen(name, "w")) == NULL)
86 			return (__os_get_errno());
87 
88 		orig_fp = dbp->dbenv->db_msgfile;
89 		dbp->dbenv->db_msgfile = fp;
90 	} else
91 		fp = orig_fp = NULL;
92 
93 	__db_prdb(dbp, flags);
94 
95 	__db_msg(env, "%s", DB_GLOBAL(db_line));
96 
97 	ret = __db_prtree(dbp, txn, flags, first, last);
98 
99 	if (fp != NULL) {
100 		(void)fclose(fp);
101 		env->dbenv->db_msgfile = orig_fp;
102 	}
103 
104 	return (ret);
105 }
106 
107 static const FN __db_flags_fn[] = {
108 	{ DB_AM_CHKSUM,			"checksumming" },
109 	{ DB_AM_COMPENSATE,		"created by compensating transaction" },
110 	{ DB_AM_CREATED,		"database created" },
111 	{ DB_AM_CREATED_MSTR,		"encompassing file created" },
112 	{ DB_AM_DBM_ERROR,		"dbm/ndbm error" },
113 	{ DB_AM_DELIMITER,		"variable length" },
114 	{ DB_AM_DISCARD,		"discard cached pages" },
115 	{ DB_AM_DUP,			"duplicates" },
116 	{ DB_AM_DUPSORT,		"sorted duplicates" },
117 	{ DB_AM_ENCRYPT,		"encrypted" },
118 	{ DB_AM_FIXEDLEN,		"fixed-length records" },
119 	{ DB_AM_INMEM,			"in-memory" },
120 	{ DB_AM_IN_RENAME,		"file is being renamed" },
121 	{ DB_AM_NOT_DURABLE,		"changes not logged" },
122 	{ DB_AM_OPEN_CALLED,		"open called" },
123 	{ DB_AM_PAD,			"pad value" },
124 	{ DB_AM_PGDEF,			"default page size" },
125 	{ DB_AM_RDONLY,			"read-only" },
126 	{ DB_AM_READ_UNCOMMITTED,	"read-uncommitted" },
127 	{ DB_AM_RECNUM,			"Btree record numbers" },
128 	{ DB_AM_RECOVER,		"opened for recovery" },
129 	{ DB_AM_RENUMBER,		"renumber" },
130 	{ DB_AM_REVSPLITOFF,		"no reverse splits" },
131 	{ DB_AM_SECONDARY,		"secondary" },
132 	{ DB_AM_SNAPSHOT,		"load on open" },
133 	{ DB_AM_SUBDB,			"subdatabases" },
134 	{ DB_AM_SWAP,			"needswap" },
135 	{ DB_AM_TXN,			"transactional" },
136 	{ DB_AM_VERIFYING,		"verifier" },
137 	{ 0,				NULL }
138 };
139 
140 /*
141  * __db_get_flags_fn --
142  *	Return the __db_flags_fn array.
143  *
144  * PUBLIC: const FN * __db_get_flags_fn __P((void));
145  */
146 const FN *
__db_get_flags_fn()147 __db_get_flags_fn()
148 {
149 	return (__db_flags_fn);
150 }
151 
152 /*
153  * __db_prdb --
154  *	Print out the DB structure information.
155  */
156 static void
__db_prdb(dbp,flags)157 __db_prdb(dbp, flags)
158 	DB *dbp;
159 	u_int32_t flags;
160 {
161 	BTREE *bt;
162 	DB_MSGBUF mb;
163 	ENV *env;
164 	HASH *h;
165 	QUEUE *q;
166 	HEAP *hp;
167 
168 	env = dbp->env;
169 
170 	DB_MSGBUF_INIT(&mb);
171 	__db_msg(env, "In-memory DB structure:");
172 	__db_msgadd(env, &mb, "%s: %#lx",
173 	    __db_dbtype_to_string(dbp->type), (u_long)dbp->flags);
174 	__db_prflags(env, &mb, dbp->flags, __db_flags_fn, " (", ")");
175 	DB_MSGBUF_FLUSH(env, &mb);
176 
177 	switch (dbp->type) {
178 	case DB_BTREE:
179 	case DB_RECNO:
180 		bt = dbp->bt_internal;
181 		__db_msg(env, "bt_meta: %lu bt_root: %lu",
182 		    (u_long)bt->bt_meta, (u_long)bt->bt_root);
183 		__db_msg(env, "bt_minkey: %lu", (u_long)bt->bt_minkey);
184 		if (!LF_ISSET(DB_PR_RECOVERYTEST))
185 			__db_msg(env, "bt_compare: %#lx bt_prefix: %#lx",
186 			    P_TO_ULONG(bt->bt_compare),
187 			    P_TO_ULONG(bt->bt_prefix));
188 #ifdef HAVE_COMPRESSION
189 		if (!LF_ISSET(DB_PR_RECOVERYTEST))
190 			__db_msg(env, "bt_compress: %#lx bt_decompress: %#lx",
191 			    P_TO_ULONG(bt->bt_compress),
192 			    P_TO_ULONG(bt->bt_decompress));
193 #endif
194 		__db_msg(env, "bt_lpgno: %lu", (u_long)bt->bt_lpgno);
195 		if (dbp->type == DB_RECNO) {
196 			__db_msg(env,
197 		    "re_pad: %#lx re_delim: %#lx re_len: %lu re_source: %s",
198 			    (u_long)bt->re_pad, (u_long)bt->re_delim,
199 			    (u_long)bt->re_len,
200 			    bt->re_source == NULL ? "" : bt->re_source);
201 			__db_msg(env,
202 			    "re_modified: %d re_eof: %d re_last: %lu",
203 			    bt->re_modified, bt->re_eof, (u_long)bt->re_last);
204 		}
205 		break;
206 	case DB_HASH:
207 		h = dbp->h_internal;
208 		__db_msg(env, "meta_pgno: %lu", (u_long)h->meta_pgno);
209 		__db_msg(env, "h_ffactor: %lu", (u_long)h->h_ffactor);
210 		__db_msg(env, "h_nelem: %lu", (u_long)h->h_nelem);
211 		if (!LF_ISSET(DB_PR_RECOVERYTEST))
212 			__db_msg(env, "h_hash: %#lx", P_TO_ULONG(h->h_hash));
213 		break;
214 	case DB_QUEUE:
215 		q = dbp->q_internal;
216 		__db_msg(env, "q_meta: %lu", (u_long)q->q_meta);
217 		__db_msg(env, "q_root: %lu", (u_long)q->q_root);
218 		__db_msg(env, "re_pad: %#lx re_len: %lu",
219 		    (u_long)q->re_pad, (u_long)q->re_len);
220 		__db_msg(env, "rec_page: %lu", (u_long)q->rec_page);
221 		__db_msg(env, "page_ext: %lu", (u_long)q->page_ext);
222 		break;
223 	case DB_HEAP:
224 		hp = dbp->heap_internal;
225 		__db_msg(env, "gbytes: %lu", (u_long)hp->gbytes);
226 		__db_msg(env, "bytes: %lu", (u_long)hp->bytes);
227 		__db_msg(env, "curregion: %lu", (u_long)hp->curregion);
228 		__db_msg(env, "region_size: %lu", (u_long)hp->region_size);
229 		__db_msg(env, "maxpgno: %lu", (u_long)hp->maxpgno);
230 		break;
231 	case DB_UNKNOWN:
232 	default:
233 		break;
234 	}
235 }
236 
237 /*
238  * __db_prtree --
239  *	Print out the entire tree.
240  */
241 static int
__db_prtree(dbp,txn,flags,first,last)242 __db_prtree(dbp, txn, flags, first, last)
243 	DB *dbp;
244 	DB_TXN *txn;
245 	u_int32_t flags;
246 	db_pgno_t first, last;
247 {
248 	DB_MPOOLFILE *mpf;
249 	PAGE *h;
250 	db_pgno_t i;
251 	int ret;
252 
253 	mpf = dbp->mpf;
254 
255 	if (dbp->type == DB_QUEUE)
256 		return (__db_prqueue(dbp, flags));
257 
258 	/*
259 	 * Find out the page number of the last page in the database, then
260 	 * dump each page.
261 	 */
262 	if (last == PGNO_INVALID &&
263 	    (ret = __memp_get_last_pgno(mpf, &last)) != 0)
264 		return (ret);
265 	for (i = first; i <= last; ++i) {
266 		if ((ret = __memp_fget(mpf, &i, NULL, txn, 0, &h)) != 0)
267 			return (ret);
268 		(void)__db_prpage(dbp, h, flags);
269 		if ((ret = __memp_fput(mpf, NULL, h, dbp->priority)) != 0)
270 			return (ret);
271 	}
272 
273 	return (0);
274 }
275 
276 /*
277  * __db_prnpage
278  *	-- Print out a specific page.
279  *
280  * PUBLIC: int __db_prnpage __P((DB *, DB_TXN *, db_pgno_t));
281  */
282 int
__db_prnpage(dbp,txn,pgno)283 __db_prnpage(dbp, txn, pgno)
284 	DB *dbp;
285 	DB_TXN *txn;
286 	db_pgno_t pgno;
287 {
288 	DB_MPOOLFILE *mpf;
289 	PAGE *h;
290 	int ret, t_ret;
291 
292 	mpf = dbp->mpf;
293 
294 	if ((ret = __memp_fget(mpf, &pgno, NULL, txn, 0, &h)) != 0)
295 		return (ret);
296 
297 	ret = __db_prpage(dbp, h, DB_PR_PAGE);
298 
299 	if ((t_ret = __memp_fput(mpf, NULL, h, dbp->priority)) != 0 && ret == 0)
300 		ret = t_ret;
301 
302 	return (ret);
303 }
304 
305 /*
306  * __db_prpage
307  *	-- Print out a page.
308  *
309  * PUBLIC: int __db_prpage __P((DB *, PAGE *, u_int32_t));
310  */
311 int
__db_prpage(dbp,h,flags)312 __db_prpage(dbp, h, flags)
313 	DB *dbp;
314 	PAGE *h;
315 	u_int32_t flags;
316 {
317 	DB_MSGBUF mb;
318 	u_int32_t pagesize;
319 	/*
320 	 * !!!
321 	 * Find out the page size.  We don't want to do it the "right" way,
322 	 * by reading the value from the meta-data page, that's going to be
323 	 * slow.  Reach down into the mpool region.
324 	 */
325 	pagesize = (u_int32_t)dbp->mpf->mfp->pagesize;
326 	DB_MSGBUF_INIT(&mb);
327 	return (__db_prpage_int(dbp->env,
328 	    &mb, dbp, "", h, pagesize, NULL, flags));
329 }
330 
331 /*
332  * __db_lockmode_to_string --
333  *	Return the name of the lock mode.
334  *
335  * PUBLIC: const char * __db_lockmode_to_string __P((db_lockmode_t));
336  */
337 const char *
__db_lockmode_to_string(mode)338 __db_lockmode_to_string(mode)
339 	db_lockmode_t mode;
340 {
341 	switch (mode) {
342 	case DB_LOCK_NG:
343 		return ("Not granted");
344 	case DB_LOCK_READ:
345 		return ("Shared/read");
346 	case DB_LOCK_WRITE:
347 		return ("Exclusive/write");
348 	case DB_LOCK_WAIT:
349 		return ("Wait for event");
350 	case DB_LOCK_IWRITE:
351 		return ("Intent exclusive/write");
352 	case DB_LOCK_IREAD:
353 		return ("Intent shared/read");
354 	case DB_LOCK_IWR:
355 		return ("Intent to read/write");
356 	case DB_LOCK_READ_UNCOMMITTED:
357 		return ("Read uncommitted");
358 	case DB_LOCK_WWRITE:
359 		return ("Was written");
360 	default:
361 		break;
362 	}
363 	return ("UNKNOWN LOCK MODE");
364 }
365 
366 #else /* !HAVE_STATISTICS */
367 
368 /*
369  * __db_dumptree --
370  *	Dump the tree to a file.
371  *
372  * PUBLIC: int __db_dumptree __P((DB *, DB_TXN *,
373  * PUBLIC:     char *, char *, db_pgno_t, db_pgno_t));
374  */
375 int
__db_dumptree(dbp,txn,op,name,first,last)376 __db_dumptree(dbp, txn, op, name, first, last)
377 	DB *dbp;
378 	DB_TXN *txn;
379 	char *op, *name;
380 	db_pgno_t first, last;
381 {
382 	COMPQUIET(txn, NULL);
383 	COMPQUIET(op, NULL);
384 	COMPQUIET(name, NULL);
385 	COMPQUIET(first, last);
386 
387 	return (__db_stat_not_built(dbp->env));
388 }
389 
390 /*
391  * __db_get_flags_fn --
392  *	Return the __db_flags_fn array.
393  *
394  * PUBLIC: const FN * __db_get_flags_fn __P((void));
395  */
396 const FN *
__db_get_flags_fn()397 __db_get_flags_fn()
398 {
399 	/*
400 	 * !!!
401 	 * The Tcl API uses this interface, stub it off.
402 	 */
403 	return (NULL);
404 }
405 #endif
406 
407 /*
408  * __db_meta --
409  *	Print out common metadata information.
410  */
411 static void
__db_meta(env,dbp,dbmeta,fn,flags)412 __db_meta(env, dbp, dbmeta, fn, flags)
413 	DB *dbp;
414 	ENV *env;
415 	DBMETA *dbmeta;
416 	FN const *fn;
417 	u_int32_t flags;
418 {
419 	DB_MPOOLFILE *mpf;
420 	DB_MSGBUF mb;
421 	PAGE *h;
422 	db_pgno_t pgno;
423 	u_int8_t *p;
424 	int cnt, ret;
425 	const char *sep;
426 
427 	DB_MSGBUF_INIT(&mb);
428 
429 	__db_msg(env, "\tmagic: %#lx", (u_long)dbmeta->magic);
430 	__db_msg(env, "\tversion: %lu", (u_long)dbmeta->version);
431 	__db_msg(env, "\tpagesize: %lu", (u_long)dbmeta->pagesize);
432 	__db_msg(env, "\ttype: %lu", (u_long)dbmeta->type);
433 	__db_msg(env, "\tmetaflags %#lx", (u_long)dbmeta->metaflags);
434 	__db_msg(env, "\tkeys: %lu\trecords: %lu",
435 	    (u_long)dbmeta->key_count, (u_long)dbmeta->record_count);
436 	if (dbmeta->nparts)
437 		__db_msg(env, "\tnparts: %lu", (u_long)dbmeta->nparts);
438 
439 	/*
440 	 * If we're doing recovery testing, don't display the free list,
441 	 * it may have changed and that makes the dump diff not work.
442 	 */
443 	if (dbp != NULL && !LF_ISSET(DB_PR_RECOVERYTEST)) {
444 		mpf = dbp->mpf;
445 		__db_msgadd(
446 		    env, &mb, "\tfree list: %lu", (u_long)dbmeta->free);
447 		for (pgno = dbmeta->free,
448 		    cnt = 0, sep = ", "; pgno != PGNO_INVALID;) {
449 			if ((ret = __memp_fget(mpf,
450 			     &pgno, NULL, NULL, 0, &h)) != 0) {
451 				DB_MSGBUF_FLUSH(env, &mb);
452 				__db_msg(env,
453 			    "Unable to retrieve free-list page: %lu: %s",
454 				    (u_long)pgno, db_strerror(ret));
455 				break;
456 			}
457 			pgno = h->next_pgno;
458 			(void)__memp_fput(mpf, NULL, h, dbp->priority);
459 			__db_msgadd(env, &mb, "%s%lu", sep, (u_long)pgno);
460 			if (++cnt % 10 == 0) {
461 				DB_MSGBUF_FLUSH(env, &mb);
462 				cnt = 0;
463 				sep = "\t";
464 			} else
465 				sep = ", ";
466 		}
467 		DB_MSGBUF_FLUSH(env, &mb);
468 		__db_msg(env, "\tlast_pgno: %lu", (u_long)dbmeta->last_pgno);
469 	}
470 
471 	if (fn != NULL) {
472 		DB_MSGBUF_FLUSH(env, &mb);
473 		__db_msgadd(env, &mb, "\tflags: %#lx", (u_long)dbmeta->flags);
474 		__db_prflags(env, &mb, dbmeta->flags, fn, " (", ")");
475 	}
476 
477 	DB_MSGBUF_FLUSH(env, &mb);
478 	__db_msgadd(env, &mb, "\tuid: ");
479 	for (p = (u_int8_t *)dbmeta->uid,
480 	    cnt = 0; cnt < DB_FILE_ID_LEN; ++cnt) {
481 		__db_msgadd(env, &mb, "%x", *p++);
482 		if (cnt < DB_FILE_ID_LEN - 1)
483 			__db_msgadd(env, &mb, " ");
484 	}
485 	DB_MSGBUF_FLUSH(env, &mb);
486 }
487 
488 /*
489  * __db_bmeta --
490  *	Print out the btree meta-data page.
491  */
492 static int
__db_bmeta(env,dbp,h,flags)493 __db_bmeta(env, dbp, h, flags)
494 	ENV *env;
495 	DB *dbp;
496 	BTMETA *h;
497 	u_int32_t flags;
498 {
499 	static const FN fn[] = {
500 		{ BTM_DUP,	"duplicates" },
501 		{ BTM_RECNO,	"recno" },
502 		{ BTM_RECNUM,	"btree:recnum" },
503 		{ BTM_FIXEDLEN,	"recno:fixed-length" },
504 		{ BTM_RENUMBER,	"recno:renumber" },
505 		{ BTM_SUBDB,	"multiple-databases" },
506 		{ BTM_DUPSORT,	"sorted duplicates" },
507 		{ BTM_COMPRESS,	"compressed" },
508 		{ 0,		NULL }
509 	};
510 
511 	__db_meta(env, dbp, (DBMETA *)h, fn, flags);
512 
513 	__db_msg(env, "\tminkey: %lu", (u_long)h->minkey);
514 	if (F_ISSET(&h->dbmeta, BTM_RECNO))
515 		__db_msg(env, "\tre_len: %#lx re_pad: %#lx",
516 		    (u_long)h->re_len, (u_long)h->re_pad);
517 	__db_msg(env, "\troot: %lu", (u_long)h->root);
518 
519 	return (0);
520 }
521 
522 /*
523  * __db_hmeta --
524  *	Print out the hash meta-data page.
525  */
526 static int
__db_hmeta(env,dbp,h,flags)527 __db_hmeta(env, dbp, h, flags)
528 	ENV *env;
529 	DB *dbp;
530 	HMETA *h;
531 	u_int32_t flags;
532 {
533 	static const FN fn[] = {
534 		{ DB_HASH_DUP,		"duplicates" },
535 		{ DB_HASH_SUBDB,	"multiple-databases" },
536 		{ DB_HASH_DUPSORT,	"sorted duplicates" },
537 		{ 0,			NULL }
538 	};
539 	DB_MSGBUF mb;
540 	int i;
541 
542 	DB_MSGBUF_INIT(&mb);
543 
544 	__db_meta(env, dbp, (DBMETA *)h, fn, flags);
545 
546 	__db_msg(env, "\tmax_bucket: %lu", (u_long)h->max_bucket);
547 	__db_msg(env, "\thigh_mask: %#lx", (u_long)h->high_mask);
548 	__db_msg(env, "\tlow_mask:  %#lx", (u_long)h->low_mask);
549 	__db_msg(env, "\tffactor: %lu", (u_long)h->ffactor);
550 	__db_msg(env, "\tnelem: %lu", (u_long)h->nelem);
551 	__db_msg(env, "\th_charkey: %#lx", (u_long)h->h_charkey);
552 	__db_msgadd(env, &mb, "\tspare points:\n\t");
553 	for (i = 0; i < NCACHED; i++) {
554 		__db_msgadd(env, &mb, "%lu (%lu) ", (u_long)h->spares[i],
555 		    (u_long)(h->spares[i] == 0 ?
556 		    0 : h->spares[i] + (i == 0 ? 0 : 1 << (i-1))));
557 		if ((i + 1) % 8 == 0)
558 			__db_msgadd(env, &mb, "\n\t");
559 	}
560 	DB_MSGBUF_FLUSH(env, &mb);
561 
562 	return (0);
563 }
564 
565 /*
566  * __db_qmeta --
567  *	Print out the queue meta-data page.
568  */
569 static int
__db_qmeta(env,dbp,h,flags)570 __db_qmeta(env, dbp, h, flags)
571 	ENV *env;
572 	DB *dbp;
573 	QMETA *h;
574 	u_int32_t flags;
575 {
576 
577 	__db_meta(env, dbp, (DBMETA *)h, NULL, flags);
578 
579 	__db_msg(env, "\tfirst_recno: %lu", (u_long)h->first_recno);
580 	__db_msg(env, "\tcur_recno: %lu", (u_long)h->cur_recno);
581 	__db_msg(env, "\tre_len: %#lx re_pad: %lu",
582 	    (u_long)h->re_len, (u_long)h->re_pad);
583 	__db_msg(env, "\trec_page: %lu", (u_long)h->rec_page);
584 	__db_msg(env, "\tpage_ext: %lu", (u_long)h->page_ext);
585 
586 	return (0);
587 }
588 
589 /*
590  * __db_heapmeta --
591  *	Print out the heap meta-data page.
592  */
593 static int
__db_heapmeta(env,dbp,h,flags)594 __db_heapmeta(env, dbp, h, flags)
595 	ENV *env;
596 	DB *dbp;
597 	HEAPMETA *h;
598 	u_int32_t flags;
599 {
600 	__db_meta(env, dbp, (DBMETA *)h, NULL, flags);
601 
602 	__db_msg(env, "\tcurregion: %lu", (u_long)h->curregion);
603 	__db_msg(env, "\tregion_size: %lu", (u_long)h->region_size);
604 	__db_msg(env, "\tnregions: %lu", (u_long)h->nregions);
605 	__db_msg(env, "\tgbytes: %lu", (u_long)h->gbytes);
606 	__db_msg(env, "\tbytes: %lu", (u_long)h->bytes);
607 
608 	return (0);
609 }
610 
611 /*
612  * __db_heapint --
613  *	Print out the heap internal-data page.
614  */
615 static int
__db_heapint(dbp,h,flags)616 __db_heapint(dbp, h, flags)
617 	DB *dbp;
618 	HEAPPG *h;
619 	u_int32_t flags;
620 {
621 	DB_MSGBUF mb;
622 	ENV *env;
623 	int count, printed;
624 	u_int32_t i, max;
625 	u_int8_t avail;
626 
627 	env = dbp->env;
628 	DB_MSGBUF_INIT(&mb);
629 	count = printed = 0;
630 	COMPQUIET(flags, 0);
631 
632 	__db_msgadd(env, &mb, "\thigh: %4lu\n", (u_long)h->high_pgno);
633 	/* How many entries could there be on a page */
634 	max = HEAP_REGION_SIZE(dbp);
635 
636 	for (i = 0; i < max; i++, count++) {
637 		avail = HEAP_SPACE(dbp, h, i);
638 		if (avail != 0) {
639 			__db_msgadd(env, &mb,
640 			    "%5lu:%1lu ", (u_long)i, (u_long)avail);
641 			printed = 1;
642 		}
643 		/* We get 10 entries per line this way */
644 		if (count == 9) {
645 			DB_MSGBUF_FLUSH(env, &mb);
646 			count = -1;
647 		}
648 	}
649 	/* All pages were less than 33% full */
650 	if (printed == 0)
651 		 __db_msgadd(env, &mb,
652 		    "All pages in this region less than 33 percent full");
653 
654 	DB_MSGBUF_FLUSH(env, &mb);
655 	return (0);
656 }
657 
658 /*
659  * For printing pages from the log we may be passed the data segment
660  * separate from the header, if so then it starts at HOFFSET.
661  */
662 #define	PR_ENTRY(dbp, h, i, data)				\
663 	(data == NULL ? P_ENTRY(dbp, h, i) :			\
664 	    (u_int8_t *)data + P_INP(dbp, h)[i] - HOFFSET(h))
665 /*
666  * __db_prpage_int
667  *	-- Print out a page.
668  *
669  * PUBLIC: int __db_prpage_int __P((ENV *, DB_MSGBUF *,
670  * PUBLIC:      DB *, char *, PAGE *, u_int32_t, u_int8_t *, u_int32_t));
671  */
672 int
__db_prpage_int(env,mbp,dbp,lead,h,pagesize,data,flags)673 __db_prpage_int(env, mbp, dbp, lead, h, pagesize, data, flags)
674 	ENV *env;
675 	DB_MSGBUF *mbp;
676 	DB *dbp;
677 	char *lead;
678 	PAGE *h;
679 	u_int32_t pagesize;
680 	u_int8_t *data;
681 	u_int32_t flags;
682 {
683 	BINTERNAL *bi;
684 	BKEYDATA *bk;
685 	HOFFPAGE a_hkd;
686 	QAMDATA *qp, *qep;
687 	RINTERNAL *ri;
688 	HEAPHDR *hh;
689 	HEAPSPLITHDR *hs;
690 	db_indx_t dlen, len, i, *inp, max;
691 	db_pgno_t pgno;
692 	db_recno_t recno;
693 	u_int32_t qlen;
694 	u_int8_t *ep, *hk, *p;
695 	int deleted, ret;
696 	const char *s;
697 	void *hdata, *sp;
698 
699 	/*
700 	 * If we're doing recovery testing and this page is P_INVALID,
701 	 * assume it's a page that's on the free list, and don't display it.
702 	 */
703 	if (LF_ISSET(DB_PR_RECOVERYTEST) && TYPE(h) == P_INVALID)
704 		return (0);
705 
706 	if ((s = __db_pagetype_to_string(TYPE(h))) == NULL) {
707 		__db_msg(env, "%sILLEGAL PAGE TYPE: page: %lu type: %lu",
708 		    lead, (u_long)h->pgno, (u_long)TYPE(h));
709 		return (EINVAL);
710 	}
711 
712 	/* Page number, page type. */
713 	__db_msgadd(env, mbp, "%spage %lu: %s:", lead, (u_long)h->pgno, s);
714 
715 	/*
716 	 * LSNs on a metadata page will be different from the original after an
717 	 * abort, in some cases.  Don't display them if we're testing recovery.
718 	 */
719 	if (!LF_ISSET(DB_PR_RECOVERYTEST) ||
720 	    (TYPE(h) != P_BTREEMETA && TYPE(h) != P_HASHMETA &&
721 	    TYPE(h) != P_QAMMETA && TYPE(h) != P_QAMDATA &&
722 	    TYPE(h) != P_HEAPMETA))
723 		__db_msgadd(env, mbp, " LSN [%lu][%lu]:",
724 		    (u_long)LSN(h).file, (u_long)LSN(h).offset);
725 
726 	/*
727 	 * Page level (only applicable for Btree/Recno, but we always display
728 	 * it, for no particular reason, except for Heap.
729 	 */
730 	if (!HEAPTYPE(h))
731 	    __db_msgadd(env, mbp, " level %lu", (u_long)h->level);
732 
733 	/* Record count. */
734 	if (TYPE(h) == P_IBTREE || TYPE(h) == P_IRECNO ||
735 	    (dbp != NULL && TYPE(h) == P_LRECNO &&
736 	    h->pgno == ((BTREE *)dbp->bt_internal)->bt_root))
737 		__db_msgadd(env, mbp, " records: %lu", (u_long)RE_NREC(h));
738 	DB_MSGBUF_FLUSH(env, mbp);
739 
740 	switch (TYPE(h)) {
741 	case P_BTREEMETA:
742 		return (__db_bmeta(env, dbp, (BTMETA *)h, flags));
743 	case P_HASHMETA:
744 		return (__db_hmeta(env, dbp, (HMETA *)h, flags));
745 	case P_QAMMETA:
746 		return (__db_qmeta(env, dbp, (QMETA *)h, flags));
747 	case P_QAMDATA:				/* Should be meta->start. */
748 		if (!LF_ISSET(DB_PR_PAGE) || dbp == NULL)
749 			return (0);
750 
751 		qlen = ((QUEUE *)dbp->q_internal)->re_len;
752 		recno = (h->pgno - 1) * QAM_RECNO_PER_PAGE(dbp) + 1;
753 		i = 0;
754 		qep = (QAMDATA *)((u_int8_t *)h + pagesize - qlen);
755 		for (qp = QAM_GET_RECORD(dbp, h, i); qp < qep;
756 		    recno++, i++, qp = QAM_GET_RECORD(dbp, h, i)) {
757 			if (!F_ISSET(qp, QAM_SET))
758 				continue;
759 
760 			__db_msgadd(env, mbp, "%s",
761 			    F_ISSET(qp, QAM_VALID) ? "\t" : "       D");
762 			__db_msgadd(env, mbp, "[%03lu] %4lu ", (u_long)recno,
763 			    (u_long)((u_int8_t *)qp - (u_int8_t *)h));
764 			__db_prbytes(env, mbp, qp->data, qlen);
765 		}
766 		return (0);
767 	case P_HEAPMETA:
768 		return (__db_heapmeta(env, dbp, (HEAPMETA *)h, flags));
769 	case P_IHEAP:
770 		if (!LF_ISSET(DB_PR_PAGE) || dbp == NULL)
771 			return (0);
772 		return (__db_heapint(dbp, (HEAPPG *)h, flags));
773 	default:
774 		break;
775 	}
776 
777 	s = "\t";
778 	if (!HEAPTYPE(h) && TYPE(h) != P_IBTREE && TYPE(h) != P_IRECNO) {
779 		__db_msgadd(env, mbp, "%sprev: %4lu next: %4lu",
780 		    s, (u_long)PREV_PGNO(h), (u_long)NEXT_PGNO(h));
781 		s = " ";
782 	}
783 
784 	if (HEAPTYPE(h)) {
785 		__db_msgadd(env, mbp, "%shigh indx: %4lu free indx: %4lu", s,
786 		    (u_long)HEAP_HIGHINDX(h), (u_long)HEAP_FREEINDX(h));
787 		s = " ";
788 	}
789 
790 	if (TYPE(h) == P_OVERFLOW) {
791 		__db_msgadd(env, mbp,
792 		    "%sref cnt: %4lu ", s, (u_long)OV_REF(h));
793 		if (dbp == NULL)
794 			__db_msgadd(env, mbp,
795 			    " len: %4lu ", (u_long)OV_LEN(h));
796 		else
797 			__db_prbytes(env,
798 			    mbp, (u_int8_t *)h + P_OVERHEAD(dbp), OV_LEN(h));
799 		return (0);
800 	}
801 	__db_msgadd(env, mbp, "%sentries: %4lu", s, (u_long)NUM_ENT(h));
802 	__db_msgadd(env, mbp, " offset: %4lu", (u_long)HOFFSET(h));
803 	DB_MSGBUF_FLUSH(env, mbp);
804 
805 	if (dbp == NULL || TYPE(h) == P_INVALID || !LF_ISSET(DB_PR_PAGE))
806 		return (0);
807 
808 	if (data != NULL)
809 		pagesize += HOFFSET(h);
810 	else if (pagesize < HOFFSET(h))
811 		return (0);
812 
813 	ret = 0;
814 	inp = P_INP(dbp, h);
815 	max = TYPE(h) == P_HEAP ? HEAP_HIGHINDX(h) + 1 : NUM_ENT(h);
816 	for (i = 0; i < max; i++) {
817 		if (TYPE(h) == P_HEAP && inp[i] == 0)
818 			continue;
819 		if ((uintptr_t)(P_ENTRY(dbp, h, i) - (u_int8_t *)h) <
820 		    (uintptr_t)(P_OVERHEAD(dbp)) ||
821 		    (size_t)(P_ENTRY(dbp, h, i) - (u_int8_t *)h) >= pagesize) {
822 			__db_msg(env,
823 			    "ILLEGAL PAGE OFFSET: indx: %lu of %lu",
824 			    (u_long)i, (u_long)inp[i]);
825 			ret = EINVAL;
826 			continue;
827 		}
828 		deleted = 0;
829 		switch (TYPE(h)) {
830 		case P_HASH_UNSORTED:
831 		case P_HASH:
832 		case P_IBTREE:
833 		case P_IRECNO:
834 			sp = PR_ENTRY(dbp, h, i, data);
835 			break;
836 		case P_HEAP:
837 			sp = P_ENTRY(dbp, h, i);
838 			break;
839 		case P_LBTREE:
840 			sp = PR_ENTRY(dbp, h, i, data);
841 			deleted = i % 2 == 0 &&
842 			    B_DISSET(GET_BKEYDATA(dbp, h, i + O_INDX)->type);
843 			break;
844 		case P_LDUP:
845 		case P_LRECNO:
846 			sp = PR_ENTRY(dbp, h, i, data);
847 			deleted = B_DISSET(GET_BKEYDATA(dbp, h, i)->type);
848 			break;
849 		default:
850 			goto type_err;
851 		}
852 		__db_msgadd(env, mbp, "%s", deleted ? "       D" : "\t");
853 		__db_msgadd(
854 		    env, mbp, "[%03lu] %4lu ", (u_long)i, (u_long)inp[i]);
855 		switch (TYPE(h)) {
856 		case P_HASH_UNSORTED:
857 		case P_HASH:
858 			hk = sp;
859 			switch (HPAGE_PTYPE(hk)) {
860 			case H_OFFDUP:
861 				memcpy(&pgno,
862 				    HOFFDUP_PGNO(hk), sizeof(db_pgno_t));
863 				__db_msgadd(env, mbp,
864 				    "%4lu [offpage dups]", (u_long)pgno);
865 				DB_MSGBUF_FLUSH(env, mbp);
866 				break;
867 			case H_DUPLICATE:
868 				/*
869 				 * If this is the first item on a page, then
870 				 * we cannot figure out how long it is, so
871 				 * we only print the first one in the duplicate
872 				 * set.
873 				 */
874 				if (i != 0)
875 					len = LEN_HKEYDATA(dbp, h, 0, i);
876 				else
877 					len = 1;
878 
879 				__db_msgadd(env, mbp, "Duplicates:");
880 				DB_MSGBUF_FLUSH(env, mbp);
881 				for (p = HKEYDATA_DATA(hk),
882 				    ep = p + len; p < ep;) {
883 					memcpy(&dlen, p, sizeof(db_indx_t));
884 					p += sizeof(db_indx_t);
885 					__db_msgadd(env, mbp, "\t\t");
886 					__db_prbytes(env, mbp, p, dlen);
887 					p += sizeof(db_indx_t) + dlen;
888 				}
889 				break;
890 			case H_KEYDATA:
891 				__db_prbytes(env, mbp, HKEYDATA_DATA(hk),
892 				    LEN_HKEYDATA(dbp, h, i == 0 ?
893 				    pagesize : 0, i));
894 				break;
895 			case H_OFFPAGE:
896 				memcpy(&a_hkd, hk, HOFFPAGE_SIZE);
897 				__db_msgadd(env, mbp,
898 				    "overflow: total len: %4lu page: %4lu",
899 				    (u_long)a_hkd.tlen, (u_long)a_hkd.pgno);
900 				DB_MSGBUF_FLUSH(env, mbp);
901 				break;
902 			default:
903 				DB_MSGBUF_FLUSH(env, mbp);
904 				__db_msg(env, "ILLEGAL HASH PAGE TYPE: %lu",
905 				    (u_long)HPAGE_PTYPE(hk));
906 				ret = EINVAL;
907 				break;
908 			}
909 			break;
910 		case P_IBTREE:
911 			bi = sp;
912 
913 			if (F_ISSET(dbp, DB_AM_RECNUM))
914 				__db_msgadd(env, mbp,
915 				    "count: %4lu ", (u_long)bi->nrecs);
916 			__db_msgadd(env, mbp,
917 			    "pgno: %4lu type: %lu ",
918 			    (u_long)bi->pgno, (u_long)bi->type);
919 			switch (B_TYPE(bi->type)) {
920 			case B_KEYDATA:
921 				__db_prbytes(env, mbp, bi->data, bi->len);
922 				break;
923 			case B_DUPLICATE:
924 			case B_OVERFLOW:
925 				__db_proff(env, mbp, bi->data);
926 				break;
927 			default:
928 				DB_MSGBUF_FLUSH(env, mbp);
929 				__db_msg(env, "ILLEGAL BINTERNAL TYPE: %lu",
930 				    (u_long)B_TYPE(bi->type));
931 				ret = EINVAL;
932 				break;
933 			}
934 			break;
935 		case P_IRECNO:
936 			ri = sp;
937 			__db_msgadd(env, mbp, "entries %4lu pgno %4lu",
938 			    (u_long)ri->nrecs, (u_long)ri->pgno);
939 			DB_MSGBUF_FLUSH(env, mbp);
940 			break;
941 		case P_LBTREE:
942 		case P_LDUP:
943 		case P_LRECNO:
944 			bk = sp;
945 			switch (B_TYPE(bk->type)) {
946 			case B_KEYDATA:
947 				__db_prbytes(env, mbp, bk->data, bk->len);
948 				break;
949 			case B_DUPLICATE:
950 			case B_OVERFLOW:
951 				__db_proff(env, mbp, bk);
952 				break;
953 			default:
954 				DB_MSGBUF_FLUSH(env, mbp);
955 				__db_msg(env,
956 			    "ILLEGAL DUPLICATE/LBTREE/LRECNO TYPE: %lu",
957 				    (u_long)B_TYPE(bk->type));
958 				ret = EINVAL;
959 				break;
960 			}
961 			break;
962 		case P_HEAP:
963 			hh = sp;
964 			if (!F_ISSET(hh,HEAP_RECSPLIT))
965 				hdata = (u_int8_t *)hh + sizeof(HEAPHDR);
966 			else {
967 				hs = sp;
968 				__db_msgadd(env, mbp,
969 				     "split: 0x%02x tsize: %lu next: %lu.%lu ",
970 				     hh->flags, (u_long)hs->tsize,
971 				     (u_long)hs->nextpg, (u_long)hs->nextindx);
972 
973 				hdata = (u_int8_t *)hh + sizeof(HEAPSPLITHDR);
974 			}
975 			__db_prbytes(env, mbp, hdata, hh->size);
976 			break;
977 		default:
978 type_err:		DB_MSGBUF_FLUSH(env, mbp);
979 			__db_msg(env,
980 			    "ILLEGAL PAGE TYPE: %lu", (u_long)TYPE(h));
981 			ret = EINVAL;
982 			continue;
983 		}
984 	}
985 	return (ret);
986 }
987 
988 /*
989  * __db_prbytes --
990  *	Print out a data element.
991  *
992  * PUBLIC: void __db_prbytes __P((ENV *, DB_MSGBUF *, u_int8_t *, u_int32_t));
993  */
994 void
__db_prbytes(env,mbp,bytes,len)995 __db_prbytes(env, mbp, bytes, len)
996 	ENV *env;
997 	DB_MSGBUF *mbp;
998 	u_int8_t *bytes;
999 	u_int32_t len;
1000 {
1001 	u_int8_t *p;
1002 	u_int32_t i, not_printable;
1003 	int msg_truncated;
1004 
1005 	__db_msgadd(env, mbp, "len: %3lu", (u_long)len);
1006 	if (len != 0) {
1007 		__db_msgadd(env, mbp, " data: ");
1008 
1009 		/*
1010 		 * Print the first N bytes of the data.   If that
1011 		 * chunk is at least 3/4  printable characters, print
1012 		 * it as text, else print it in hex.  We have this
1013 		 * heuristic because we're displaying things like
1014 		 * lock objects that could be either text or data.
1015 		 */
1016 		if (len > env->data_len) {
1017 			len = env->data_len;
1018 			msg_truncated = 1;
1019 		} else
1020 			msg_truncated = 0;
1021 		not_printable = 0;
1022 		for (p = bytes, i = 0; i < len; ++i, ++p) {
1023 			if (!isprint((int)*p) && *p != '\t' && *p != '\n') {
1024 				if (i == len - 1 && *p == '\0')
1025 					break;
1026 				if (++not_printable >= (len >> 2))
1027 					break;
1028 			}
1029 		}
1030 		if (not_printable < (len >> 2))
1031 			for (p = bytes, i = len; i > 0; --i, ++p) {
1032 				if (isprint((int)*p))
1033 					__db_msgadd(env, mbp, "%c", *p);
1034 				else
1035 					__db_msgadd(env,
1036 					    mbp, "\\%x", (u_int)*p);
1037 			}
1038 		else
1039 			for (p = bytes, i = len; i > 0; --i, ++p)
1040 				__db_msgadd(env, mbp, "%.2x", (u_int)*p);
1041 		if (msg_truncated)
1042 			__db_msgadd(env, mbp, "...");
1043 	}
1044 	DB_MSGBUF_FLUSH(env, mbp);
1045 }
1046 
1047 /*
1048  * __db_proff --
1049  *	Print out an off-page element.
1050  */
1051 static void
__db_proff(env,mbp,vp)1052 __db_proff(env, mbp, vp)
1053 	ENV *env;
1054 	DB_MSGBUF *mbp;
1055 	void *vp;
1056 {
1057 	BOVERFLOW *bo;
1058 
1059 	bo = vp;
1060 	switch (B_TYPE(bo->type)) {
1061 	case B_OVERFLOW:
1062 		__db_msgadd(env, mbp, "overflow: total len: %4lu page: %4lu",
1063 		    (u_long)bo->tlen, (u_long)bo->pgno);
1064 		break;
1065 	case B_DUPLICATE:
1066 		__db_msgadd(
1067 		    env, mbp, "duplicate: page: %4lu", (u_long)bo->pgno);
1068 		break;
1069 	default:
1070 		/* NOTREACHED */
1071 		break;
1072 	}
1073 	DB_MSGBUF_FLUSH(env, mbp);
1074 }
1075 
1076 /*
1077  * __db_prflags --
1078  *	Print out flags values.
1079  *
1080  * PUBLIC: void __db_prflags __P((ENV *, DB_MSGBUF *,
1081  * PUBLIC:     u_int32_t, const FN *, const char *, const char *));
1082  */
1083 void
__db_prflags(env,mbp,flags,fn,prefix,suffix)1084 __db_prflags(env, mbp, flags, fn, prefix, suffix)
1085 	ENV *env;
1086 	DB_MSGBUF *mbp;
1087 	u_int32_t flags;
1088 	FN const *fn;
1089 	const char *prefix, *suffix;
1090 {
1091 	DB_MSGBUF mb;
1092 	const FN *fnp;
1093 	int found, standalone;
1094 	const char *sep;
1095 
1096 	if (fn == NULL)
1097 		return;
1098 
1099 	/*
1100 	 * If it's a standalone message, output the suffix (which will be the
1101 	 * label), regardless of whether we found anything or not, and flush
1102 	 * the line.
1103 	 */
1104 	if (mbp == NULL) {
1105 		standalone = 1;
1106 		mbp = &mb;
1107 		DB_MSGBUF_INIT(mbp);
1108 	} else
1109 		standalone = 0;
1110 
1111 	sep = prefix == NULL ? "" : prefix;
1112 	for (found = 0, fnp = fn; fnp->mask != 0; ++fnp)
1113 		if (LF_ISSET(fnp->mask)) {
1114 			__db_msgadd(env, mbp, "%s%s", sep, fnp->name);
1115 			sep = ", ";
1116 			found = 1;
1117 		}
1118 
1119 	if ((standalone || found) && suffix != NULL)
1120 		__db_msgadd(env, mbp, "%s", suffix);
1121 	if (standalone)
1122 		DB_MSGBUF_FLUSH(env, mbp);
1123 }
1124 
1125 /*
1126  * __db_name_to_val --
1127  *	Return the integral value associated with the string, or -1 if missing.
1128  *	It is intended for looking up string names of enums and single bit
1129  *	in order to get a numeric value.
1130  *
1131  * PUBLIC: int __db_name_to_val __P((FN const *, char *));
1132  */
1133 int
__db_name_to_val(strtable,s)1134 __db_name_to_val(strtable, s)
1135 	FN const *strtable;
1136 	char *s;
1137 {
1138 	if (s != NULL) {
1139 		do {
1140 			if (strcasecmp(strtable->name, s) == 0)
1141 				return ((int)strtable->mask);
1142 		} while ((++strtable)->name != NULL);
1143 	}
1144 	return (-1);
1145 }
1146 
1147 /*
1148  * __db_pagetype_to_string --
1149  *	Return the name of the specified page type.
1150  * PUBLIC: const char *__db_pagetype_to_string __P((u_int32_t));
1151  */
1152 const char *
__db_pagetype_to_string(type)1153 __db_pagetype_to_string(type)
1154 	u_int32_t type;
1155 {
1156 	char *s;
1157 
1158 	s = NULL;
1159 	switch (type) {
1160 	case P_BTREEMETA:
1161 		s = "btree metadata";
1162 		break;
1163 	case P_LDUP:
1164 		s = "duplicate";
1165 		break;
1166 	case P_HASH_UNSORTED:
1167 		s = "hash unsorted";
1168 		break;
1169 	case P_HASH:
1170 		s = "hash";
1171 		break;
1172 	case P_HASHMETA:
1173 		s = "hash metadata";
1174 		break;
1175 	case P_IBTREE:
1176 		s = "btree internal";
1177 		break;
1178 	case P_INVALID:
1179 		s = "invalid";
1180 		break;
1181 	case P_IRECNO:
1182 		s = "recno internal";
1183 		break;
1184 	case P_LBTREE:
1185 		s = "btree leaf";
1186 		break;
1187 	case P_LRECNO:
1188 		s = "recno leaf";
1189 		break;
1190 	case P_OVERFLOW:
1191 		s = "overflow";
1192 		break;
1193 	case P_QAMMETA:
1194 		s = "queue metadata";
1195 		break;
1196 	case P_QAMDATA:
1197 		s = "queue";
1198 		break;
1199 	case P_HEAPMETA:
1200 		s = "heap metadata";
1201 		break;
1202 	case P_HEAP:
1203 		s = "heap data";
1204 		break;
1205 	case P_IHEAP:
1206 		s = "heap internal";
1207 		break;
1208 	default:
1209 		/* Just return a NULL. */
1210 		break;
1211 	}
1212 	return (s);
1213 }
1214 
1215 /*
1216  * __db_dump_pp --
1217  *	DB->dump pre/post processing.
1218  *
1219  * PUBLIC: int __db_dump_pp __P((DB *, const char *,
1220  * PUBLIC:     int (*)(void *, const void *), void *, int, int));
1221  */
1222 int
__db_dump_pp(dbp,subname,callback,handle,pflag,keyflag)1223 __db_dump_pp(dbp, subname, callback, handle, pflag, keyflag)
1224 	DB *dbp;
1225 	const char *subname;
1226 	int (*callback) __P((void *, const void *));
1227 	void *handle;
1228 	int pflag, keyflag;
1229 {
1230 	DB_THREAD_INFO *ip;
1231 	ENV *env;
1232 	int handle_check, ret, t_ret;
1233 
1234 	env = dbp->env;
1235 
1236 	DB_ILLEGAL_BEFORE_OPEN(dbp, "DB->dump");
1237 
1238 	ENV_ENTER(env, ip);
1239 
1240 	/* Check for replication block. */
1241 	handle_check = IS_ENV_REPLICATED(env);
1242 	if (handle_check && (ret = __db_rep_enter(dbp, 1, 0, 1)) != 0) {
1243 		handle_check = 0;
1244 		goto err;
1245 	}
1246 
1247 	ret = __db_dump(dbp, subname, callback, handle, pflag, keyflag);
1248 
1249 	/* Release replication block. */
1250 	if (handle_check && (t_ret = __env_db_rep_exit(env)) != 0 && ret == 0)
1251 		ret = t_ret;
1252 
1253 err:	ENV_LEAVE(env, ip);
1254 	return (ret);
1255 }
1256 
1257 /*
1258  * __db_dump --
1259  *	DB->dump.
1260  *
1261  * PUBLIC: int __db_dump __P((DB *, const char *,
1262  * PUBLIC:     int (*)(void *, const void *), void *, int, int));
1263  */
1264 int
__db_dump(dbp,subname,callback,handle,pflag,keyflag)1265 __db_dump(dbp, subname, callback, handle, pflag, keyflag)
1266 	DB *dbp;
1267 	const char *subname;
1268 	int (*callback) __P((void *, const void *));
1269 	void *handle;
1270 	int pflag, keyflag;
1271 {
1272 	DBC *dbcp;
1273 	DBT key, data;
1274 	DBT keyret, dataret;
1275 	DB_HEAP_RID rid;
1276 	ENV *env;
1277 	db_recno_t recno;
1278 	int is_recno, is_heap, ret, t_ret;
1279 	void *pointer;
1280 
1281 	env = dbp->env;
1282 	is_heap = 0;
1283 
1284 	if ((ret = __db_prheader(
1285 	    dbp, subname, pflag, keyflag, handle, callback, NULL, 0)) != 0)
1286 		return (ret);
1287 
1288 	/*
1289 	 * Get a cursor and step through the database, printing out each
1290 	 * key/data pair.
1291 	 */
1292 	if ((ret = __db_cursor(dbp, NULL, NULL, &dbcp, 0)) != 0)
1293 		return (ret);
1294 
1295 	memset(&key, 0, sizeof(key));
1296 	memset(&data, 0, sizeof(data));
1297 	if ((ret = __os_malloc(env, 1024 * 1024, &data.data)) != 0)
1298 		goto err;
1299 	data.ulen = 1024 * 1024;
1300 	data.flags = DB_DBT_USERMEM;
1301 	is_recno = (dbp->type == DB_RECNO || dbp->type == DB_QUEUE);
1302 	keyflag = is_recno ? keyflag : 1;
1303 	if (is_recno) {
1304 		keyret.data = &recno;
1305 		keyret.size = sizeof(recno);
1306 	}
1307 
1308 	if (dbp->type == DB_HEAP) {
1309 		is_heap = 1;
1310 		key.data = &rid;
1311 		key.size = key.ulen = sizeof(DB_HEAP_RID);
1312 		key.flags = DB_DBT_USERMEM;
1313 	}
1314 
1315 retry: while ((ret =
1316 	    __dbc_get(dbcp, &key, &data,
1317 	    !is_heap ? DB_NEXT | DB_MULTIPLE_KEY : DB_NEXT )) == 0) {
1318 		if (is_heap) {
1319 			/* Never dump keys for HEAP */
1320 			if ((ret = __db_prdbt(
1321 			    &data, pflag, " ", handle, callback, 0, 0)) != 0)
1322 				goto err;
1323 			continue;
1324 		}
1325 		DB_MULTIPLE_INIT(pointer, &data);
1326 		for (;;) {
1327 			if (is_recno)
1328 				DB_MULTIPLE_RECNO_NEXT(pointer, &data,
1329 				    recno, dataret.data, dataret.size);
1330 			else
1331 				DB_MULTIPLE_KEY_NEXT(pointer, &data,
1332 				    keyret.data, keyret.size,
1333 				    dataret.data, dataret.size);
1334 
1335 			if (dataret.data == NULL)
1336 				break;
1337 
1338 			if ((keyflag &&
1339 			    (ret = __db_prdbt(&keyret, pflag, " ",
1340 			    handle, callback, is_recno, 0)) != 0) ||
1341 			    (ret = __db_prdbt(&dataret, pflag, " ",
1342 			    handle, callback, 0, 0)) != 0)
1343 					goto err;
1344 		}
1345 	}
1346 	if (ret == DB_BUFFER_SMALL) {
1347 		data.size = (u_int32_t)DB_ALIGN(data.size, 1024);
1348 		if ((ret = __os_realloc(env, data.size, &data.data)) != 0)
1349 			goto err;
1350 		data.ulen = data.size;
1351 		goto retry;
1352 	}
1353 	if (ret == DB_NOTFOUND)
1354 		ret = 0;
1355 
1356 	if ((t_ret = __db_prfooter(handle, callback)) != 0 && ret == 0)
1357 		ret = t_ret;
1358 
1359 err:	if ((t_ret = __dbc_close(dbcp)) != 0 && ret == 0)
1360 		ret = t_ret;
1361 	if (data.data != NULL)
1362 		__os_free(env, data.data);
1363 
1364 	return (ret);
1365 }
1366 
1367 /*
1368  * __db_prdbt --
1369  *	Print out a DBT data element.
1370  *
1371  * PUBLIC: int __db_prdbt __P((DBT *, int, const char *, void *,
1372  * PUBLIC:     int (*)(void *, const void *), int, int));
1373  */
1374 int
__db_prdbt(dbtp,checkprint,prefix,handle,callback,is_recno,is_heap)1375 __db_prdbt(dbtp, checkprint, prefix, handle, callback, is_recno, is_heap)
1376 	DBT *dbtp;
1377 	int checkprint;
1378 	const char *prefix;
1379 	void *handle;
1380 	int (*callback) __P((void *, const void *));
1381 	int is_recno;
1382 	int is_heap;
1383 {
1384 	static const u_char hex[] = "0123456789abcdef";
1385 	db_recno_t recno;
1386 	DB_HEAP_RID rid;
1387 	size_t len;
1388 	int ret;
1389 #define	DBTBUFLEN	100
1390 	u_int8_t *p, *hp;
1391 	char buf[DBTBUFLEN], hbuf[DBTBUFLEN];
1392 
1393 	/*
1394 	 * !!!
1395 	 * This routine is the routine that dumps out items in the format
1396 	 * used by db_dump(1) and db_load(1).  This means that the format
1397 	 * cannot change.
1398 	 */
1399 	if (prefix != NULL && (ret = callback(handle, prefix)) != 0)
1400 		return (ret);
1401 	if (is_recno) {
1402 		/*
1403 		 * We're printing a record number, and this has to be done
1404 		 * in a platform-independent way.  So we use the numeral in
1405 		 * straight ASCII.
1406 		 */
1407 		(void)__ua_memcpy(&recno, dbtp->data, sizeof(recno));
1408 		snprintf(buf, DBTBUFLEN, "%lu", (u_long)recno);
1409 
1410 		/* If we're printing data as hex, print keys as hex too. */
1411 		if (!checkprint) {
1412 			for (len = strlen(buf), p = (u_int8_t *)buf,
1413 			    hp = (u_int8_t *)hbuf; len-- > 0; ++p) {
1414 				*hp++ = hex[(u_int8_t)(*p & 0xf0) >> 4];
1415 				*hp++ = hex[*p & 0x0f];
1416 			}
1417 			*hp = '\0';
1418 			ret = callback(handle, hbuf);
1419 		} else
1420 			ret = callback(handle, buf);
1421 
1422 		if (ret != 0)
1423 			return (ret);
1424 	} else if (is_heap) {
1425 		/*
1426 		 * We're printing a heap record number, and this has to be
1427 		 * done in a platform-independent way.  So we use the numeral
1428 		 * in straight ASCII.
1429 		 */
1430 		(void)__ua_memcpy(&rid, dbtp->data, sizeof(rid));
1431 		snprintf(buf, DBTBUFLEN, "%lu %hu",
1432 		    (u_long)rid.pgno, (u_short)rid.indx);
1433 
1434 		/* If we're printing data as hex, print keys as hex too. */
1435 		if (!checkprint) {
1436 			for (len = strlen(buf), p = (u_int8_t *)buf,
1437 			    hp = (u_int8_t *)hbuf; len-- > 0; ++p) {
1438 				*hp++ = hex[(u_int8_t)(*p & 0xf0) >> 4];
1439 				*hp++ = hex[*p & 0x0f];
1440 			}
1441 			*hp = '\0';
1442 			ret = callback(handle, hbuf);
1443 		} else
1444 			ret = callback(handle, buf);
1445 
1446 		if (ret != 0)
1447 			return (ret);
1448 	} else if (checkprint) {
1449 		for (len = dbtp->size, p = dbtp->data; len--; ++p)
1450 			if (isprint((int)*p)) {
1451 				if (*p == '\\' &&
1452 				    (ret = callback(handle, "\\")) != 0)
1453 					return (ret);
1454 				snprintf(buf, DBTBUFLEN, "%c", *p);
1455 				if ((ret = callback(handle, buf)) != 0)
1456 					return (ret);
1457 			} else {
1458 				snprintf(buf, DBTBUFLEN, "\\%c%c",
1459 				    hex[(u_int8_t)(*p & 0xf0) >> 4],
1460 				    hex[*p & 0x0f]);
1461 				if ((ret = callback(handle, buf)) != 0)
1462 					return (ret);
1463 			}
1464 	} else
1465 		for (len = dbtp->size, p = dbtp->data; len--; ++p) {
1466 			snprintf(buf, DBTBUFLEN, "%c%c",
1467 			    hex[(u_int8_t)(*p & 0xf0) >> 4],
1468 			    hex[*p & 0x0f]);
1469 			if ((ret = callback(handle, buf)) != 0)
1470 				return (ret);
1471 		}
1472 
1473 	return (callback(handle, "\n"));
1474 }
1475 
1476 /*
1477  * __db_prheader --
1478  *	Write out header information in the format expected by db_load.
1479  *
1480  * PUBLIC: int	__db_prheader __P((DB *, const char *, int, int, void *,
1481  * PUBLIC:     int (*)(void *, const void *), VRFY_DBINFO *, db_pgno_t));
1482  */
1483 int
__db_prheader(dbp,subname,pflag,keyflag,handle,callback,vdp,meta_pgno)1484 __db_prheader(dbp, subname, pflag, keyflag, handle, callback, vdp, meta_pgno)
1485 	DB *dbp;
1486 	const char *subname;
1487 	int pflag, keyflag;
1488 	void *handle;
1489 	int (*callback) __P((void *, const void *));
1490 	VRFY_DBINFO *vdp;
1491 	db_pgno_t meta_pgno;
1492 {
1493 	DBT dbt;
1494 	DBTYPE dbtype;
1495 	ENV *env;
1496 	VRFY_PAGEINFO *pip;
1497 	u_int32_t flags, tmp_u_int32;
1498 	size_t buflen;
1499 	char *buf;
1500 	int using_vdp, ret, t_ret, tmp_int;
1501 #ifdef HAVE_HEAP
1502 	u_int32_t tmp2_u_int32;
1503 #endif
1504 
1505 	ret = 0;
1506 	buf = NULL;
1507 	COMPQUIET(buflen, 0);
1508 
1509 	/*
1510 	 * If dbp is NULL, then pip is guaranteed to be non-NULL; we only ever
1511 	 * call __db_prheader with a NULL dbp from one case inside __db_prdbt,
1512 	 * and this is a special subdatabase for "lost" items.  In this case
1513 	 * we have a vdp (from which we'll get a pip).  In all other cases, we
1514 	 * will have a non-NULL dbp (and vdp may or may not be NULL depending
1515 	 * on whether we're salvaging).
1516 	 */
1517 	if (dbp == NULL)
1518 		env = NULL;
1519 	else
1520 		env = dbp->env;
1521 	DB_ASSERT(env, dbp != NULL || vdp != NULL);
1522 
1523 	/*
1524 	 * If we've been passed a verifier statistics object, use that;  we're
1525 	 * being called in a context where dbp->stat is unsafe.
1526 	 *
1527 	 * Also, the verifier may set the pflag on a per-salvage basis.  If so,
1528 	 * respect that.
1529 	 */
1530 	if (vdp != NULL) {
1531 		if ((ret = __db_vrfy_getpageinfo(vdp, meta_pgno, &pip)) != 0)
1532 			return (ret);
1533 
1534 		if (F_ISSET(vdp, SALVAGE_PRINTABLE))
1535 			pflag = 1;
1536 		using_vdp = 1;
1537 	} else {
1538 		pip = NULL;
1539 		using_vdp = 0;
1540 	}
1541 
1542 	/*
1543 	 * If dbp is NULL, make it a btree.  Otherwise, set dbtype to whatever
1544 	 * appropriate type for the specified meta page, or the type of the dbp.
1545 	 */
1546 	if (dbp == NULL)
1547 		dbtype = DB_BTREE;
1548 	else if (using_vdp)
1549 		switch (pip->type) {
1550 		case P_BTREEMETA:
1551 			if (F_ISSET(pip, VRFY_IS_RECNO))
1552 				dbtype = DB_RECNO;
1553 			else
1554 				dbtype = DB_BTREE;
1555 			break;
1556 		case P_HASHMETA:
1557 			dbtype = DB_HASH;
1558 			break;
1559 		case P_HEAPMETA:
1560 			dbtype = DB_HEAP;
1561 			break;
1562 		case P_QAMMETA:
1563 			dbtype = DB_QUEUE;
1564 			break;
1565 		default:
1566 			/*
1567 			 * If the meta page is of a bogus type, it's because
1568 			 * we have a badly corrupt database.  (We must be in
1569 			 * the verifier for pip to be non-NULL.) Pretend we're
1570 			 * a Btree and salvage what we can.
1571 			 */
1572 			DB_ASSERT(env, F_ISSET(dbp, DB_AM_VERIFYING));
1573 			dbtype = DB_BTREE;
1574 			break;
1575 		}
1576 	else
1577 		dbtype = dbp->type;
1578 
1579 	if ((ret = callback(handle, "VERSION=3\n")) != 0)
1580 		goto err;
1581 	if (pflag) {
1582 		if ((ret = callback(handle, "format=print\n")) != 0)
1583 			goto err;
1584 	} else if ((ret = callback(handle, "format=bytevalue\n")) != 0)
1585 		goto err;
1586 
1587 	/*
1588 	 * 64 bytes is long enough, as a minimum bound, for any of the
1589 	 * fields besides subname.  Subname uses __db_prdbt and therefore
1590 	 * does not need buffer space here.
1591 	 */
1592 	buflen = 64;
1593 	if ((ret = __os_malloc(env, buflen, &buf)) != 0)
1594 		goto err;
1595 	if (subname != NULL) {
1596 		snprintf(buf, buflen, "database=");
1597 		if ((ret = callback(handle, buf)) != 0)
1598 			goto err;
1599 		DB_INIT_DBT(dbt, subname, strlen(subname));
1600 		if ((ret = __db_prdbt(&dbt, 1,
1601 		    NULL, handle, callback, 0, 0)) != 0)
1602 			goto err;
1603 	}
1604 	switch (dbtype) {
1605 	case DB_BTREE:
1606 		if ((ret = callback(handle, "type=btree\n")) != 0)
1607 			goto err;
1608 		if (using_vdp)
1609 			tmp_int = F_ISSET(pip, VRFY_HAS_RECNUMS) ? 1 : 0;
1610 		else {
1611 			if ((ret = __db_get_flags(dbp, &flags)) != 0) {
1612 				__db_err(env, ret, "DB->get_flags");
1613 				goto err;
1614 			}
1615 			tmp_int = F_ISSET(dbp, DB_AM_RECNUM) ? 1 : 0;
1616 		}
1617 		if (tmp_int && (ret = callback(handle, "recnum=1\n")) != 0)
1618 			goto err;
1619 
1620 		if (using_vdp)
1621 			tmp_u_int32 = pip->bt_minkey;
1622 		else
1623 			if ((ret =
1624 			    __bam_get_bt_minkey(dbp, &tmp_u_int32)) != 0) {
1625 				__db_err(env, ret, "DB->get_bt_minkey");
1626 				goto err;
1627 			}
1628 		if (tmp_u_int32 != 0 && tmp_u_int32 != DEFMINKEYPAGE) {
1629 			snprintf(buf, buflen,
1630 			    "bt_minkey=%lu\n", (u_long)tmp_u_int32);
1631 			if ((ret = callback(handle, buf)) != 0)
1632 				goto err;
1633 		}
1634 		break;
1635 	case DB_HASH:
1636 #ifdef HAVE_HASH
1637 		if ((ret = callback(handle, "type=hash\n")) != 0)
1638 			goto err;
1639 		if (using_vdp)
1640 			tmp_u_int32 = pip->h_ffactor;
1641 		else
1642 			if ((ret =
1643 			    __ham_get_h_ffactor(dbp, &tmp_u_int32)) != 0) {
1644 				__db_err(env, ret, "DB->get_h_ffactor");
1645 				goto err;
1646 			}
1647 		if (tmp_u_int32 != 0) {
1648 			snprintf(buf, buflen,
1649 			    "h_ffactor=%lu\n", (u_long)tmp_u_int32);
1650 			if ((ret = callback(handle, buf)) != 0)
1651 				goto err;
1652 		}
1653 
1654 		if (using_vdp)
1655 			tmp_u_int32 = pip->h_nelem;
1656 		else
1657 			if ((ret = __ham_get_h_nelem(dbp, &tmp_u_int32)) != 0) {
1658 				__db_err(env, ret, "DB->get_h_nelem");
1659 				goto err;
1660 			}
1661 		/*
1662 		 * Hash databases have an h_nelem field of 0 or 1, neither
1663 		 * of those values is interesting.
1664 		 */
1665 		if (tmp_u_int32 > 1) {
1666 			snprintf(buf, buflen,
1667 			    "h_nelem=%lu\n", (u_long)tmp_u_int32);
1668 			if ((ret = callback(handle, buf)) != 0)
1669 				goto err;
1670 		}
1671 		break;
1672 #else
1673 		ret = __db_no_hash_am(env);
1674 		goto err;
1675 #endif
1676 	case DB_HEAP:
1677 #ifdef HAVE_HEAP
1678 		if ((ret = callback(handle, "type=heap\n")) != 0)
1679 			goto err;
1680 
1681 		if ((ret = __heap_get_heapsize(
1682 		    dbp, &tmp_u_int32, &tmp2_u_int32)) != 0) {
1683 			__db_err(env, ret, "DB->get_heapsize");
1684 			goto err;
1685 		}
1686 		if (tmp_u_int32 != 0) {
1687 			snprintf(buf,
1688 			    buflen, "heap_gbytes=%lu\n", (u_long)tmp_u_int32);
1689 			if ((ret = callback(handle, buf)) != 0)
1690 				goto err;
1691 		}
1692 		if (tmp2_u_int32 != 0) {
1693 			snprintf(buf,
1694 			    buflen, "heap_bytes=%lu\n", (u_long)tmp2_u_int32);
1695 			if ((ret = callback(handle, buf)) != 0)
1696 				goto err;
1697 		}
1698 
1699 		if ((ret =
1700 		    __heap_get_heap_regionsize(dbp, &tmp_u_int32)) != 0) {
1701 			__db_err(env, ret, "DB->get_heap_regionsize");
1702 			goto err;
1703 		}
1704 		if (tmp_u_int32 != 0) {
1705 			snprintf(buf, buflen,
1706 			    "heap_regionsize=%lu\n", (u_long)tmp_u_int32);
1707 			if ((ret = callback(handle, buf)) != 0)
1708 				goto err;
1709 		}
1710 		break;
1711 #else
1712 		ret = __db_no_heap_am(env);
1713 		goto err;
1714 #endif
1715 	case DB_QUEUE:
1716 #ifdef HAVE_QUEUE
1717 		if ((ret = callback(handle, "type=queue\n")) != 0)
1718 			goto err;
1719 		if (using_vdp)
1720 			tmp_u_int32 = vdp->re_len;
1721 		else
1722 			if ((ret = __ram_get_re_len(dbp, &tmp_u_int32)) != 0) {
1723 				__db_err(env, ret, "DB->get_re_len");
1724 				goto err;
1725 			}
1726 		snprintf(buf, buflen, "re_len=%lu\n", (u_long)tmp_u_int32);
1727 		if ((ret = callback(handle, buf)) != 0)
1728 			goto err;
1729 
1730 		if (using_vdp)
1731 			tmp_int = (int)vdp->re_pad;
1732 		else
1733 			if ((ret = __ram_get_re_pad(dbp, &tmp_int)) != 0) {
1734 				__db_err(env, ret, "DB->get_re_pad");
1735 				goto err;
1736 			}
1737 		if (tmp_int != 0 && tmp_int != ' ') {
1738 			snprintf(buf, buflen, "re_pad=%#x\n", tmp_int);
1739 			if ((ret = callback(handle, buf)) != 0)
1740 				goto err;
1741 		}
1742 
1743 		if (using_vdp)
1744 			tmp_u_int32 = vdp->page_ext;
1745 		else
1746 			if ((ret =
1747 			    __qam_get_extentsize(dbp, &tmp_u_int32)) != 0) {
1748 				__db_err(env, ret, "DB->get_q_extentsize");
1749 				goto err;
1750 			}
1751 		if (tmp_u_int32 != 0) {
1752 			snprintf(buf, buflen,
1753 			    "extentsize=%lu\n", (u_long)tmp_u_int32);
1754 			if ((ret = callback(handle, buf)) != 0)
1755 				goto err;
1756 		}
1757 		break;
1758 #else
1759 		ret = __db_no_queue_am(env);
1760 		goto err;
1761 #endif
1762 	case DB_RECNO:
1763 		if ((ret = callback(handle, "type=recno\n")) != 0)
1764 			goto err;
1765 		if (using_vdp)
1766 			tmp_int = F_ISSET(pip, VRFY_IS_RRECNO) ? 1 : 0;
1767 		else
1768 			tmp_int = F_ISSET(dbp, DB_AM_RENUMBER) ? 1 : 0;
1769 		if (tmp_int != 0 &&
1770 		    (ret = callback(handle, "renumber=1\n")) != 0)
1771 				goto err;
1772 
1773 		if (using_vdp)
1774 			tmp_int = F_ISSET(pip, VRFY_IS_FIXEDLEN) ? 1 : 0;
1775 		else
1776 			tmp_int = F_ISSET(dbp, DB_AM_FIXEDLEN) ? 1 : 0;
1777 		if (tmp_int) {
1778 			if (using_vdp)
1779 				tmp_u_int32 = pip->re_len;
1780 			else
1781 				if ((ret =
1782 				    __ram_get_re_len(dbp, &tmp_u_int32)) != 0) {
1783 					__db_err(env, ret, "DB->get_re_len");
1784 					goto err;
1785 				}
1786 			snprintf(buf, buflen,
1787 			    "re_len=%lu\n", (u_long)tmp_u_int32);
1788 			if ((ret = callback(handle, buf)) != 0)
1789 				goto err;
1790 
1791 			if (using_vdp)
1792 				tmp_int = (int)pip->re_pad;
1793 			else
1794 				if ((ret =
1795 				    __ram_get_re_pad(dbp, &tmp_int)) != 0) {
1796 					__db_err(env, ret, "DB->get_re_pad");
1797 					goto err;
1798 				}
1799 			if (tmp_int != 0 && tmp_int != ' ') {
1800 				snprintf(buf,
1801 				    buflen, "re_pad=%#x\n", (u_int)tmp_int);
1802 				if ((ret = callback(handle, buf)) != 0)
1803 					goto err;
1804 			}
1805 		}
1806 		break;
1807 	case DB_UNKNOWN:			/* Impossible. */
1808 		ret = __db_unknown_path(env, "__db_prheader");
1809 		goto err;
1810 	}
1811 
1812 	if (using_vdp) {
1813 		if (F_ISSET(pip, VRFY_HAS_CHKSUM))
1814 			if ((ret = callback(handle, "chksum=1\n")) != 0)
1815 				goto err;
1816 		if (F_ISSET(pip, VRFY_HAS_DUPS))
1817 			if ((ret = callback(handle, "duplicates=1\n")) != 0)
1818 				goto err;
1819 		if (F_ISSET(pip, VRFY_HAS_DUPSORT))
1820 			if ((ret = callback(handle, "dupsort=1\n")) != 0)
1821 				goto err;
1822 #ifdef HAVE_COMPRESSION
1823 		if (F_ISSET(pip, VRFY_HAS_COMPRESS))
1824 			if ((ret = callback(handle, "compressed=1\n")) != 0)
1825 				goto err;
1826 #endif
1827 		/*
1828 		 * !!!
1829 		 * We don't know if the page size was the default if we're
1830 		 * salvaging.  It doesn't seem that interesting to have, so
1831 		 * we ignore it for now.
1832 		 */
1833 	} else {
1834 		if (F_ISSET(dbp, DB_AM_CHKSUM))
1835 			if ((ret = callback(handle, "chksum=1\n")) != 0)
1836 				goto err;
1837 		if (F_ISSET(dbp, DB_AM_DUP))
1838 			if ((ret = callback(handle, "duplicates=1\n")) != 0)
1839 				goto err;
1840 		if (F_ISSET(dbp, DB_AM_DUPSORT))
1841 			if ((ret = callback(handle, "dupsort=1\n")) != 0)
1842 				goto err;
1843 #ifdef HAVE_COMPRESSION
1844 		if (DB_IS_COMPRESSED(dbp))
1845 			if ((ret = callback(handle, "compressed=1\n")) != 0)
1846 				goto err;
1847 #endif
1848 		if (!F_ISSET(dbp, DB_AM_PGDEF)) {
1849 			snprintf(buf, buflen,
1850 			    "db_pagesize=%lu\n", (u_long)dbp->pgsize);
1851 			if ((ret = callback(handle, buf)) != 0)
1852 				goto err;
1853 		}
1854 	}
1855 
1856 #ifdef HAVE_PARTITION
1857 	if (dbp != NULL && DB_IS_PARTITIONED(dbp) &&
1858 	    F_ISSET((DB_PARTITION *)dbp->p_internal, PART_RANGE)) {
1859 		DBT *keys;
1860 		u_int32_t i;
1861 
1862 		if ((ret = __partition_get_keys(dbp, &tmp_u_int32, &keys)) != 0)
1863 			goto err;
1864 		if (tmp_u_int32 != 0) {
1865 			snprintf(buf,
1866 			     buflen, "nparts=%lu\n", (u_long)tmp_u_int32);
1867 			if ((ret = callback(handle, buf)) != 0)
1868 				goto err;
1869 			for (i = 0; i < tmp_u_int32 - 1; i++)
1870 			    if ((ret = __db_prdbt(&keys[i],
1871 				pflag, " ", handle, callback, 0, 0)) != 0)
1872 					goto err;
1873 		}
1874 	}
1875 #endif
1876 
1877 	if (keyflag && (ret = callback(handle, "keys=1\n")) != 0)
1878 		goto err;
1879 
1880 	ret = callback(handle, "HEADER=END\n");
1881 
1882 err:	if (using_vdp &&
1883 	    (t_ret = __db_vrfy_putpageinfo(env, vdp, pip)) != 0 && ret == 0)
1884 		ret = t_ret;
1885 	if (buf != NULL)
1886 		__os_free(env, buf);
1887 
1888 	return (ret);
1889 }
1890 
1891 /*
1892  * __db_prfooter --
1893  *	Print the footer that marks the end of a DB dump.  This is trivial,
1894  *	but for consistency's sake we don't want to put its literal contents
1895  *	in multiple places.
1896  *
1897  * PUBLIC: int __db_prfooter __P((void *, int (*)(void *, const void *)));
1898  */
1899 int
__db_prfooter(handle,callback)1900 __db_prfooter(handle, callback)
1901 	void *handle;
1902 	int (*callback) __P((void *, const void *));
1903 {
1904 	return (callback(handle, "DATA=END\n"));
1905 }
1906 
1907 /*
1908  * __db_pr_callback --
1909  *	Callback function for using pr_* functions from C.
1910  *
1911  * PUBLIC: int  __db_pr_callback __P((void *, const void *));
1912  */
1913 int
__db_pr_callback(handle,str_arg)1914 __db_pr_callback(handle, str_arg)
1915 	void *handle;
1916 	const void *str_arg;
1917 {
1918 	char *str;
1919 	FILE *f;
1920 
1921 	str = (char *)str_arg;
1922 	f = (FILE *)handle;
1923 
1924 	if (fprintf(f, "%s", str) != (int)strlen(str))
1925 		return (EIO);
1926 
1927 	return (0);
1928 }
1929 
1930 /*
1931  * __db_dbtype_to_string --
1932  *	Return the name of the database type.
1933  *
1934  * PUBLIC: const char * __db_dbtype_to_string __P((DBTYPE));
1935  */
1936 const char *
__db_dbtype_to_string(type)1937 __db_dbtype_to_string(type)
1938 	DBTYPE type;
1939 {
1940 	switch (type) {
1941 	case DB_BTREE:
1942 		return ("btree");
1943 	case DB_HASH:
1944 		return ("hash");
1945 	case DB_RECNO:
1946 		return ("recno");
1947 	case DB_QUEUE:
1948 		return ("queue");
1949 	case DB_HEAP:
1950 		return ("heap");
1951 	case DB_UNKNOWN:
1952 	default:
1953 		break;
1954 	}
1955 	return ("UNKNOWN TYPE");
1956 }
1957