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