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