1 /* 2 * Copyright (c) 2008 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <libutil.h> 36 37 #include "hammer.h" 38 39 #define FLAG_TOOFARLEFT 0x0001 40 #define FLAG_TOOFARRIGHT 0x0002 41 #define FLAG_BADTYPE 0x0004 42 #define FLAG_BADCHILDPARENT 0x0008 43 #define FLAG_BADMIRRORTID 0x0010 44 45 typedef struct btree_search { 46 u_int32_t lo; 47 int64_t obj_id; 48 u_int16_t rec_type; 49 int64_t key; 50 hammer_tid_t create_tid; 51 int limit; /* # of fields to test */ 52 int filter; /* filter type (default -1) */ 53 } *btree_search_t; 54 55 static void print_btree_node(hammer_off_t node_offset, btree_search_t search, 56 int depth, hammer_tid_t mirror_tid, 57 hammer_base_elm_t left_bound, 58 hammer_base_elm_t right_bound, 59 struct zone_stat *stats); 60 static const char *check_data_crc(hammer_btree_elm_t elm); 61 static void print_record(hammer_btree_elm_t elm); 62 static void print_btree_elm(hammer_btree_elm_t elm, int i, u_int8_t type, 63 int flags, const char *label, const char *ext, 64 struct zone_stat *stats); 65 static int get_elm_flags(hammer_node_ondisk_t node, hammer_off_t node_offset, 66 hammer_btree_elm_t elm, u_int8_t btype, 67 hammer_base_elm_t left_bound, 68 hammer_base_elm_t right_bound); 69 static void print_bigblock_fill(hammer_off_t offset); 70 static int init_btree_search(const char *arg, int filter, 71 btree_search_t search); 72 static int test_btree_search(hammer_btree_elm_t elm, btree_search_t search); 73 74 void 75 hammer_cmd_show(hammer_off_t node_offset, const char *arg, 76 int filter, int depth, 77 hammer_base_elm_t left_bound, hammer_base_elm_t right_bound) 78 { 79 struct volume_info *volume; 80 struct btree_search search; 81 btree_search_t searchp = NULL; 82 struct zone_stat *stats = NULL; 83 int zone; 84 85 AssertOnFailure = (DebugOpt != 0); 86 87 if (VerboseOpt) 88 stats = hammer_init_zone_stat_bits(); 89 90 if (node_offset == (hammer_off_t)-1) { 91 volume = get_volume(RootVolNo); 92 node_offset = volume->ondisk->vol0_btree_root; 93 if (QuietOpt < 3) { 94 printf("Volume header\trecords=%jd next_tid=%016jx\n", 95 (intmax_t)volume->ondisk->vol0_stat_records, 96 (uintmax_t)volume->ondisk->vol0_next_tid); 97 printf("\t\tbufoffset=%016jx\n", 98 (uintmax_t)volume->ondisk->vol_buf_beg); 99 for (zone = 0; zone < HAMMER_MAX_ZONES; ++zone) { 100 printf("\t\tzone %d\tnext_offset=%016jx\n", 101 zone, 102 (uintmax_t)volume->ondisk->vol0_blockmap[zone].next_offset 103 ); 104 } 105 } 106 rel_volume(volume); 107 } 108 109 printf("show %016jx", (uintmax_t)node_offset); 110 if (arg) { 111 assert(init_btree_search(arg, filter, &search) != -1); 112 if (search.limit >= 1) 113 printf(" lo %08x obj_id %016jx", 114 search.lo, (uintmax_t)search.obj_id); 115 if (search.limit >= 3) 116 printf(" rec_type %02x", search.rec_type); 117 if (search.limit >= 4) 118 printf(" key %016jx", (uintmax_t)search.key); 119 if (search.limit == 5) 120 printf(" create_tid %016jx\n", 121 (uintmax_t)search.create_tid); 122 searchp = &search; 123 } 124 printf(" depth %d\n", depth); 125 print_btree_node(node_offset, searchp, depth, HAMMER_MAX_TID, 126 left_bound, right_bound, stats); 127 128 AssertOnFailure = 1; 129 130 if (VerboseOpt) { 131 hammer_print_zone_stat(stats); 132 hammer_cleanup_zone_stat(stats); 133 } 134 } 135 136 static void 137 print_btree_node(hammer_off_t node_offset, btree_search_t search, 138 int depth, hammer_tid_t mirror_tid, 139 hammer_base_elm_t left_bound, hammer_base_elm_t right_bound, 140 struct zone_stat *stats) 141 { 142 struct buffer_info *buffer = NULL; 143 hammer_node_ondisk_t node; 144 hammer_btree_elm_t elm; 145 int i; 146 int flags; 147 int maxcount; 148 char badc; 149 char badm; 150 const char *ext; 151 152 node = get_node(node_offset, &buffer); 153 154 if (node == NULL) { 155 printf("BI NODE %016jx (IO ERROR)\n", 156 (uintmax_t)node_offset); 157 return; 158 } 159 160 if (crc32(&node->crc + 1, HAMMER_BTREE_CRCSIZE) == node->crc) 161 badc = ' '; 162 else 163 badc = 'B'; 164 165 if (node->mirror_tid <= mirror_tid) { 166 badm = ' '; 167 } else { 168 badm = 'M'; 169 badc = 'B'; 170 } 171 172 printf("%c%c NODE %016jx cnt=%02d p=%016jx " 173 "type=%c depth=%d", 174 badc, 175 badm, 176 (uintmax_t)node_offset, node->count, 177 (uintmax_t)node->parent, 178 (node->type ? node->type : '?'), depth); 179 printf(" mirror=%016jx", (uintmax_t)node->mirror_tid); 180 if (QuietOpt < 3) { 181 printf(" fill="); 182 print_bigblock_fill(node_offset); 183 } 184 if (node->signature) 185 printf(" sign=%04x", node->signature); 186 printf(" {\n"); 187 188 if (VerboseOpt) 189 hammer_add_zone_stat(stats, node_offset, sizeof(*node)); 190 191 maxcount = hammer_node_max_elements(node->type); 192 assert(maxcount != -1); 193 194 for (i = 0; i < node->count && i < maxcount; ++i) { 195 elm = &node->elms[i]; 196 197 if (node->type != HAMMER_BTREE_TYPE_INTERNAL) { 198 ext = NULL; 199 if (search && test_btree_search(elm, search) == 0) 200 ext = " *"; 201 } else if (search) { 202 ext = " *"; 203 if (test_btree_search(elm, search) > 0 || 204 test_btree_search(elm + 1, search) < 0) 205 ext = NULL; 206 } else { 207 ext = NULL; 208 } 209 210 flags = get_elm_flags(node, node_offset, 211 elm, elm->base.btype, 212 left_bound, right_bound); 213 print_btree_elm(elm, i, node->type, flags, "ELM", ext, stats); 214 } 215 if (node->type == HAMMER_BTREE_TYPE_INTERNAL) { 216 elm = &node->elms[i]; 217 218 flags = get_elm_flags(node, node_offset, 219 elm, 'I', 220 left_bound, right_bound); 221 print_btree_elm(elm, i, node->type, flags, "RBN", NULL, stats); 222 } 223 printf(" }\n"); 224 225 for (i = 0; i < node->count; ++i) { 226 elm = &node->elms[i]; 227 228 switch(node->type) { 229 case HAMMER_BTREE_TYPE_INTERNAL: 230 if (search && search->filter) { 231 if (test_btree_search(elm, search) > 0 || 232 test_btree_search(elm + 1, search) < 0) 233 break; 234 } 235 if (elm->internal.subtree_offset) { 236 print_btree_node(elm->internal.subtree_offset, 237 search, depth + 1, 238 elm->internal.mirror_tid, 239 &elm[0].base, &elm[1].base, 240 stats); 241 /* 242 * Cause show to do normal iteration after 243 * seeking to the lo:objid:rectype:key:tid 244 * by default 245 */ 246 if (search && search->filter == -1) /* default */ 247 search->filter = 0; 248 } 249 break; 250 default: 251 break; 252 } 253 } 254 rel_buffer(buffer); 255 } 256 257 static __inline 258 int 259 is_root_btree_beg(u_int8_t type, int i, hammer_btree_elm_t elm) 260 { 261 /* 262 * elm->base.btype depends on what the original node had 263 * so it could be anything but HAMMER_BTREE_TYPE_NONE. 264 */ 265 return (type == HAMMER_BTREE_TYPE_INTERNAL && 266 i == 0 && 267 elm->base.localization == 0 && 268 elm->base.obj_id == (int64_t)-0x8000000000000000LL && 269 elm->base.key == (int64_t)-0x8000000000000000LL && 270 elm->base.create_tid == 1 && 271 elm->base.delete_tid == 1 && 272 elm->base.rec_type == 0 && 273 elm->base.obj_type == 0 && 274 elm->base.btype != HAMMER_BTREE_TYPE_NONE); 275 } 276 277 static __inline 278 int 279 is_root_btree_end(u_int8_t type, int i, hammer_btree_elm_t elm) 280 { 281 return (type == HAMMER_BTREE_TYPE_INTERNAL && 282 i != 0 && 283 elm->base.localization == 0xFFFFFFFFU && 284 elm->base.obj_id == 0x7FFFFFFFFFFFFFFFLL && 285 elm->base.key == 0x7FFFFFFFFFFFFFFFLL && 286 elm->base.create_tid == 0xFFFFFFFFFFFFFFFFULL && 287 elm->base.delete_tid == 0 && 288 elm->base.rec_type == 0xFFFFU && 289 elm->base.obj_type == 0 && 290 elm->base.btype == HAMMER_BTREE_TYPE_NONE); 291 } 292 293 static 294 void 295 print_btree_elm(hammer_btree_elm_t elm, int i, u_int8_t type, 296 int flags, const char *label, const char *ext, 297 struct zone_stat *stats) 298 { 299 char flagstr[8] = { 0, '-', '-', '-', '-', '-', '-', 0 }; 300 char deleted; 301 char rootelm; 302 char btype; 303 304 flagstr[0] = flags ? 'B' : 'G'; 305 if (flags & FLAG_TOOFARLEFT) 306 flagstr[2] = 'L'; 307 if (flags & FLAG_TOOFARRIGHT) 308 flagstr[3] = 'R'; 309 if (flags & FLAG_BADTYPE) 310 flagstr[4] = 'T'; 311 if (flags & FLAG_BADCHILDPARENT) 312 flagstr[5] = 'C'; 313 if (flags & FLAG_BADMIRRORTID) 314 flagstr[6] = 'M'; 315 316 /* 317 * Check if elm is derived from root split 318 */ 319 if (is_root_btree_beg(type, i, elm)) 320 rootelm = '>'; 321 else if (is_root_btree_end(type, i, elm)) 322 rootelm = '<'; 323 else 324 rootelm = ' '; 325 326 if (elm->base.delete_tid) 327 deleted = 'd'; 328 else 329 deleted = ' '; 330 331 switch(elm->base.btype) { 332 case HAMMER_BTREE_TYPE_INTERNAL: 333 case HAMMER_BTREE_TYPE_LEAF: 334 case HAMMER_BTREE_TYPE_RECORD: 335 case HAMMER_BTREE_TYPE_DELETED: 336 btype = elm->base.btype; /* ascii */ 337 break; 338 case HAMMER_BTREE_TYPE_NONE: 339 btype = ' '; 340 break; 341 default: 342 btype = '?'; 343 break; 344 } 345 346 printf("%s\t%s %2d %c ", flagstr, label, i, btype); 347 printf("lo=%08x obj=%016jx rt=%02x key=%016jx ot=%02x\n", 348 elm->base.localization, 349 (uintmax_t)elm->base.obj_id, 350 elm->base.rec_type, 351 (uintmax_t)elm->base.key, 352 elm->base.obj_type); 353 printf("\t %c tids=%016jx:%016jx ", 354 (rootelm == ' ' ? deleted : rootelm), 355 (uintmax_t)elm->base.create_tid, 356 (uintmax_t)elm->base.delete_tid); 357 358 switch(type) { 359 case HAMMER_BTREE_TYPE_INTERNAL: 360 printf("suboff=%016jx", 361 (uintmax_t)elm->internal.subtree_offset); 362 if (QuietOpt < 3) { 363 printf(" mirror=%016jx", 364 (uintmax_t)elm->internal.mirror_tid); 365 } 366 if (ext) 367 printf(" %s", ext); 368 break; 369 case HAMMER_BTREE_TYPE_LEAF: 370 if (ext) 371 printf(" %s", ext); 372 switch(elm->base.btype) { 373 case HAMMER_BTREE_TYPE_RECORD: 374 if (QuietOpt < 3) 375 printf("\n%s\t ", check_data_crc(elm)); 376 else 377 printf("\n\t "); 378 printf("dataoff=%016jx/%d", 379 (uintmax_t)elm->leaf.data_offset, 380 elm->leaf.data_len); 381 if (QuietOpt < 3) { 382 printf(" crc=%04x", elm->leaf.data_crc); 383 printf("\n\t fill="); 384 print_bigblock_fill(elm->leaf.data_offset); 385 } 386 if (QuietOpt < 2) 387 print_record(elm); 388 if (VerboseOpt) 389 hammer_add_zone_stat(stats, 390 elm->leaf.data_offset, 391 elm->leaf.data_len); 392 break; 393 } 394 break; 395 default: 396 break; 397 } 398 printf("\n"); 399 } 400 401 static 402 int 403 get_elm_flags(hammer_node_ondisk_t node, hammer_off_t node_offset, 404 hammer_btree_elm_t elm, u_int8_t btype, 405 hammer_base_elm_t left_bound, hammer_base_elm_t right_bound) 406 { 407 int flags = 0; 408 409 switch(node->type) { 410 case HAMMER_BTREE_TYPE_INTERNAL: 411 if (elm->internal.subtree_offset) { 412 struct buffer_info *buffer = NULL; 413 hammer_node_ondisk_t subnode; 414 415 subnode = get_node(elm->internal.subtree_offset, 416 &buffer); 417 if (subnode == NULL) 418 flags |= FLAG_BADCHILDPARENT; 419 else if (subnode->parent != node_offset) 420 flags |= FLAG_BADCHILDPARENT; 421 rel_buffer(buffer); 422 } 423 if (elm->internal.mirror_tid > node->mirror_tid) 424 flags |= FLAG_BADMIRRORTID; 425 426 switch(btype) { 427 case HAMMER_BTREE_TYPE_INTERNAL: 428 if (left_bound == NULL || right_bound == NULL) 429 break; 430 if (hammer_btree_cmp(&elm->base, left_bound) < 0) 431 flags |= FLAG_TOOFARLEFT; 432 if (hammer_btree_cmp(&elm->base, right_bound) > 0) 433 flags |= FLAG_TOOFARRIGHT; 434 break; 435 case HAMMER_BTREE_TYPE_LEAF: 436 if (left_bound == NULL || right_bound == NULL) 437 break; 438 if (hammer_btree_cmp(&elm->base, left_bound) < 0) 439 flags |= FLAG_TOOFARLEFT; 440 if (hammer_btree_cmp(&elm->base, right_bound) >= 0) 441 flags |= FLAG_TOOFARRIGHT; 442 break; 443 default: 444 flags |= FLAG_BADTYPE; 445 break; 446 } 447 break; 448 case HAMMER_BTREE_TYPE_LEAF: 449 if (node->mirror_tid == 0 && 450 !(node->parent == 0 && node->count == 2)) { 451 flags |= FLAG_BADMIRRORTID; 452 } 453 if (elm->base.create_tid && node->mirror_tid && 454 elm->base.create_tid > node->mirror_tid) { 455 flags |= FLAG_BADMIRRORTID; 456 } 457 if (elm->base.delete_tid && node->mirror_tid && 458 elm->base.delete_tid > node->mirror_tid) { 459 flags |= FLAG_BADMIRRORTID; 460 } 461 switch(btype) { 462 case HAMMER_BTREE_TYPE_RECORD: 463 if (left_bound == NULL || right_bound == NULL) 464 break; 465 if (hammer_btree_cmp(&elm->base, left_bound) < 0) 466 flags |= FLAG_TOOFARLEFT; 467 if (hammer_btree_cmp(&elm->base, right_bound) >= 0) 468 flags |= FLAG_TOOFARRIGHT; 469 break; 470 default: 471 flags |= FLAG_BADTYPE; 472 break; 473 } 474 break; 475 default: 476 flags |= FLAG_BADTYPE; 477 break; 478 } 479 return(flags); 480 } 481 482 static 483 void 484 print_bigblock_fill(hammer_off_t offset) 485 { 486 struct hammer_blockmap_layer1 layer1; 487 struct hammer_blockmap_layer2 layer2; 488 int fill; 489 int error; 490 491 blockmap_lookup(offset, &layer1, &layer2, &error); 492 printf("z%d:v%d:%lu:%lu:%lu=", 493 HAMMER_ZONE_DECODE(offset), 494 HAMMER_VOL_DECODE(offset), 495 HAMMER_BLOCKMAP_LAYER1_INDEX(offset), 496 HAMMER_BLOCKMAP_LAYER2_INDEX(offset), 497 offset & HAMMER_BIGBLOCK_MASK64); 498 499 if (error) { 500 printf("B%d", error); 501 } else { 502 fill = layer2.bytes_free * 100 / HAMMER_BIGBLOCK_SIZE; 503 fill = 100 - fill; 504 printf("%d%%", fill); 505 } 506 } 507 508 /* 509 * Check the generic crc on a data element. Inodes record types are 510 * special in that some of their fields are not CRCed. 511 * 512 * Also check that the zone is valid. 513 */ 514 static 515 const char * 516 check_data_crc(hammer_btree_elm_t elm) 517 { 518 struct buffer_info *data_buffer; 519 hammer_off_t data_offset; 520 int32_t data_len; 521 int32_t len; 522 u_int32_t crc; 523 int error; 524 char *ptr; 525 526 data_offset = elm->leaf.data_offset; 527 data_len = elm->leaf.data_len; 528 data_buffer = NULL; 529 if (data_offset == 0 || data_len == 0) 530 return("Z"); 531 532 crc = 0; 533 error = 0; 534 while (data_len) { 535 blockmap_lookup(data_offset, NULL, NULL, &error); 536 if (error) 537 break; 538 539 ptr = get_buffer_data(data_offset, &data_buffer, 0); 540 len = HAMMER_BUFSIZE - ((int)data_offset & HAMMER_BUFMASK); 541 if (len > data_len) 542 len = (int)data_len; 543 if (elm->leaf.base.rec_type == HAMMER_RECTYPE_INODE && 544 data_len == sizeof(struct hammer_inode_data)) { 545 crc = crc32_ext(ptr, HAMMER_INODE_CRCSIZE, crc); 546 } else { 547 crc = crc32_ext(ptr, len, crc); 548 } 549 data_len -= len; 550 data_offset += len; 551 } 552 rel_buffer(data_buffer); 553 if (error) { 554 switch (error) { /* bad offset */ 555 case -1: 556 return("BO-ZL"); 557 case -2: 558 return("BO-ZG"); 559 case -3: 560 return("BO-RV"); 561 case -4: 562 return("BO-AO"); 563 case -5: 564 return("BO-DE"); 565 case -6: 566 return("BO-L1"); 567 case -7: 568 return("BO-LU"); 569 case -8: 570 return("BO-L2"); 571 case -9: 572 return("BO-LZ"); 573 default: 574 return("BO-??"); 575 } 576 } 577 if (crc == elm->leaf.data_crc) 578 return(""); 579 return("BX"); /* bad crc */ 580 } 581 582 static 583 void 584 print_config(char *cfgtxt) 585 { 586 char *token; 587 588 printf("\n%17stext=\"\n", ""); 589 while((token = strsep(&cfgtxt, "\r\n")) != NULL) { 590 printf("%17s %s\n", "", token); 591 } 592 printf("%17s\"", ""); 593 } 594 595 static 596 void 597 print_record(hammer_btree_elm_t elm) 598 { 599 struct buffer_info *data_buffer; 600 hammer_off_t data_offset; 601 int32_t data_len; 602 hammer_data_ondisk_t data; 603 u_int32_t status; 604 char *str1 = NULL; 605 char *str2 = NULL; 606 607 data_offset = elm->leaf.data_offset; 608 data_len = elm->leaf.data_len; 609 data_buffer = NULL; 610 611 if (data_offset) 612 data = get_buffer_data(data_offset, &data_buffer, 0); 613 else 614 data = NULL; 615 616 switch(elm->leaf.base.rec_type) { 617 case HAMMER_RECTYPE_UNKNOWN: 618 printf("\n%17s", ""); 619 printf("unknown"); 620 break; 621 case HAMMER_RECTYPE_INODE: 622 printf("\n%17s", ""); 623 printf("size=%jd nlinks=%jd", 624 (intmax_t)data->inode.size, 625 (intmax_t)data->inode.nlinks); 626 if (QuietOpt < 1) { 627 printf(" mode=%05o uflags=%08x\n", 628 data->inode.mode, 629 data->inode.uflags); 630 printf("%17s", ""); 631 printf("ctime=%016jx pobjid=%016jx obj_type=%d\n", 632 (uintmax_t)data->inode.ctime, 633 (uintmax_t)data->inode.parent_obj_id, 634 data->inode.obj_type); 635 printf("%17s", ""); 636 printf("mtime=%016jx", (uintmax_t)data->inode.mtime); 637 printf(" caps=%02x", data->inode.cap_flags); 638 } 639 break; 640 case HAMMER_RECTYPE_DIRENTRY: 641 printf("\n%17s", ""); 642 data_len -= HAMMER_ENTRY_NAME_OFF; 643 printf("dir-entry ino=%016jx lo=%08x name=\"%*.*s\"", 644 (uintmax_t)data->entry.obj_id, 645 data->entry.localization, 646 data_len, data_len, data->entry.name); 647 break; 648 case HAMMER_RECTYPE_FIX: 649 switch(elm->leaf.base.key) { 650 case HAMMER_FIXKEY_SYMLINK: 651 data_len -= HAMMER_SYMLINK_NAME_OFF; 652 printf("\n%17s", ""); 653 printf("symlink=\"%*.*s\"", data_len, data_len, 654 data->symlink.name); 655 break; 656 default: 657 break; 658 } 659 break; 660 case HAMMER_RECTYPE_PFS: 661 printf("\n%17s", ""); 662 printf("sync_beg_tid=%016jx sync_end_tid=%016jx\n", 663 (intmax_t)data->pfsd.sync_beg_tid, 664 (intmax_t)data->pfsd.sync_end_tid); 665 uuid_to_string(&data->pfsd.shared_uuid, &str1, &status); 666 uuid_to_string(&data->pfsd.unique_uuid, &str2, &status); 667 printf("%17s", ""); 668 printf("shared_uuid=%s\n", str1); 669 printf("%17s", ""); 670 printf("unique_uuid=%s\n", str2); 671 printf("%17s", ""); 672 printf("mirror_flags=%08x label=\"%s\"", 673 data->pfsd.mirror_flags, data->pfsd.label); 674 if (data->pfsd.snapshots[0]) 675 printf(" snapshots=\"%s\"", data->pfsd.snapshots); 676 free(str1); 677 free(str2); 678 break; 679 case HAMMER_RECTYPE_SNAPSHOT: 680 printf("\n%17s", ""); 681 printf("tid=%016jx label=\"%s\"", 682 (intmax_t)data->snap.tid, data->snap.label); 683 break; 684 case HAMMER_RECTYPE_CONFIG: 685 if (VerboseOpt > 2) { 686 print_config(data->config.text); 687 } 688 break; 689 case HAMMER_RECTYPE_DATA: 690 if (VerboseOpt > 3) { 691 printf("\n"); 692 hexdump(data, data_len, "\t\t ", 0); 693 } 694 break; 695 case HAMMER_RECTYPE_EXT: 696 case HAMMER_RECTYPE_DB: 697 if (VerboseOpt > 2) { 698 printf("\n"); 699 hexdump(data, data_len, "\t\t ", 0); 700 } 701 break; 702 default: 703 break; 704 } 705 rel_buffer(data_buffer); 706 } 707 708 static __inline 709 unsigned long 710 _strtoul(const char *p, int base) 711 { 712 unsigned long retval; 713 714 errno = 0; /* clear */ 715 retval = strtoul(p, NULL, base); 716 if (errno == ERANGE && retval == ULONG_MAX) 717 err(1, "strtoul"); 718 return retval; 719 } 720 721 static __inline 722 unsigned long long 723 _strtoull(const char *p, int base) 724 { 725 unsigned long long retval; 726 727 errno = 0; /* clear */ 728 retval = strtoull(p, NULL, base); 729 if (errno == ERANGE && retval == ULLONG_MAX) 730 err(1, "strtoull"); 731 return retval; 732 } 733 734 static int 735 init_btree_search(const char *arg, int filter, btree_search_t search) 736 { 737 char *s, *p; 738 int i = 0; 739 740 search->lo = 0; 741 search->obj_id = (int64_t)HAMMER_MIN_OBJID; 742 search->rec_type = HAMMER_RECTYPE_LOWEST; 743 search->key = 0; 744 search->create_tid = 0; 745 search->limit = 0; 746 search->filter = filter; 747 748 s = strdup(arg); 749 if (s == NULL) 750 return(-1); 751 752 while ((p = s) != NULL) { 753 if ((s = strchr(s, ':')) != NULL) 754 *s++ = 0; 755 if (++i == 1) { 756 search->lo = _strtoul(p, 16); 757 } else if (i == 2) { 758 search->obj_id = _strtoull(p, 16); 759 } else if (i == 3) { 760 search->rec_type = _strtoul(p, 16); 761 } else if (i == 4) { 762 search->key = _strtoull(p, 16); 763 } else if (i == 5) { 764 search->create_tid = _strtoull(p, 16); 765 break; 766 } 767 } 768 search->limit = i; 769 free(s); 770 return(i); 771 } 772 773 static int 774 test_btree_search(hammer_btree_elm_t elm, btree_search_t search) 775 { 776 hammer_base_elm_t base = &elm->base; 777 assert(search); 778 779 if (base->localization < search->lo) 780 return(-1); 781 if (base->localization > search->lo) 782 return(1); 783 /* fall through */ 784 785 if (base->obj_id < search->obj_id) 786 return(-2); 787 if (base->obj_id > search->obj_id) 788 return(2); 789 if (search->limit == 2) 790 return(0); /* ignore below */ 791 792 if (base->rec_type < search->rec_type) 793 return(-3); 794 if (base->rec_type > search->rec_type) 795 return(3); 796 if (search->limit == 3) 797 return(0); /* ignore below */ 798 799 if (base->key < search->key) 800 return(-4); 801 if (base->key > search->key) 802 return(4); 803 if (search->limit == 4) 804 return(0); /* ignore below */ 805 806 if (base->create_tid == 0) { 807 if (search->create_tid == 0) 808 return(0); 809 return(5); 810 } 811 if (search->create_tid == 0) 812 return(-5); 813 if (base->create_tid < search->create_tid) 814 return(-5); 815 if (base->create_tid > search->create_tid) 816 return(5); 817 return(0); 818 } 819 820 /* 821 * Dump the UNDO FIFO 822 */ 823 void 824 hammer_cmd_show_undo(void) 825 { 826 struct volume_info *volume; 827 hammer_blockmap_t rootmap; 828 hammer_off_t scan_offset; 829 hammer_fifo_any_t head; 830 struct buffer_info *data_buffer = NULL; 831 int64_t bytes; 832 833 volume = get_volume(RootVolNo); 834 rootmap = &volume->ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX]; 835 if (rootmap->first_offset <= rootmap->next_offset) 836 bytes = rootmap->next_offset - rootmap->first_offset; 837 else 838 bytes = rootmap->alloc_offset - rootmap->first_offset + 839 (rootmap->next_offset & HAMMER_OFF_LONG_MASK); 840 841 printf("Volume header UNDO %016jx-%016jx/%016jx\n", 842 (intmax_t)rootmap->first_offset, 843 (intmax_t)rootmap->next_offset, 844 (intmax_t)rootmap->alloc_offset); 845 printf("UNDO map is %jdMB\n", 846 (intmax_t)((rootmap->alloc_offset & HAMMER_OFF_LONG_MASK) / 847 (1024 * 1024))); 848 printf("UNDO being used is %jdB\n", (intmax_t)bytes); 849 850 scan_offset = HAMMER_ZONE_ENCODE(HAMMER_ZONE_UNDO_INDEX, 0); 851 while (scan_offset < rootmap->alloc_offset) { 852 head = get_buffer_data(scan_offset, &data_buffer, 0); 853 printf("%016jx ", scan_offset); 854 855 switch(head->head.hdr_type) { 856 case HAMMER_HEAD_TYPE_PAD: 857 printf("PAD(%04x)", head->head.hdr_size); 858 break; 859 case HAMMER_HEAD_TYPE_DUMMY: 860 printf("DUMMY(%04x) seq=%08x", 861 head->head.hdr_size, head->head.hdr_seq); 862 break; 863 case HAMMER_HEAD_TYPE_UNDO: 864 printf("UNDO(%04x) seq=%08x " 865 "dataoff=%016jx bytes=%d", 866 head->head.hdr_size, head->head.hdr_seq, 867 (intmax_t)head->undo.undo_offset, 868 head->undo.undo_data_bytes); 869 break; 870 case HAMMER_HEAD_TYPE_REDO: 871 printf("REDO(%04x) seq=%08x flags=%08x " 872 "objid=%016jx logoff=%016jx bytes=%d", 873 head->head.hdr_size, head->head.hdr_seq, 874 head->redo.redo_flags, 875 (intmax_t)head->redo.redo_objid, 876 (intmax_t)head->redo.redo_offset, 877 head->redo.redo_data_bytes); 878 break; 879 default: 880 printf("UNKNOWN(%04x,%04x) seq=%08x", 881 head->head.hdr_type, 882 head->head.hdr_size, 883 head->head.hdr_seq); 884 break; 885 } 886 887 if (scan_offset == rootmap->first_offset) 888 printf(" >"); 889 if (scan_offset == rootmap->next_offset) 890 printf(" <"); 891 printf("\n"); 892 893 if ((head->head.hdr_size & HAMMER_HEAD_ALIGN_MASK) || 894 head->head.hdr_size == 0 || 895 head->head.hdr_size > HAMMER_UNDO_ALIGN - 896 ((u_int)scan_offset & HAMMER_UNDO_MASK)) { 897 printf("Illegal size field, skipping to " 898 "next boundary\n"); 899 scan_offset = (scan_offset + HAMMER_UNDO_MASK) & 900 ~HAMMER_UNDO_MASK64; 901 } else { 902 scan_offset += head->head.hdr_size; 903 } 904 } 905 rel_buffer(data_buffer); 906 } 907