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