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