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