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