1 /* 2 * Copyright (c) 2011-2012 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@dragonflybsd.org> 6 * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 3. Neither the name of The DragonFly Project nor the names of its 19 * contributors may be used to endorse or promote products derived 20 * from this software without specific, prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 32 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include "hammer2.h" 37 38 #include <openssl/sha.h> 39 40 #define GIG (1024LL*1024*1024) 41 42 static int show_tab = 2; 43 44 static void shell_msghandler(dmsg_msg_t *msg, int unmanaged); 45 static void shell_ttymsg(dmsg_iocom_t *iocom); 46 static void CountBlocks(hammer2_bmap_data_t *bmap, int value, 47 hammer2_off_t *accum16, hammer2_off_t *accum64); 48 49 /************************************************************************ 50 * SHELL * 51 ************************************************************************/ 52 53 int 54 cmd_shell(const char *hostname) 55 { 56 dmsg_master_service_info_t *info; 57 pthread_t thread; 58 int fd; 59 60 fd = dmsg_connect(hostname); 61 if (fd < 0) 62 return 1; 63 64 info = malloc(sizeof(*info)); 65 bzero(info, sizeof(*info)); 66 info->fd = fd; 67 info->detachme = 0; 68 info->usrmsg_callback = shell_msghandler; 69 info->altmsg_callback = shell_ttymsg; 70 info->label = strdup("debug"); 71 pthread_create(&thread, NULL, dmsg_master_service, info); 72 pthread_join(thread, NULL); 73 74 return 0; 75 } 76 77 #if 0 78 int 79 cmd_shell(const char *hostname) 80 { 81 struct dmsg_iocom iocom; 82 dmsg_msg_t *msg; 83 int fd; 84 85 /* 86 * Connect to the target 87 */ 88 fd = dmsg_connect(hostname); 89 if (fd < 0) 90 return 1; 91 92 /* 93 * Initialize the session and transmit an empty DMSG_DBG_SHELL 94 * to cause the remote end to generate a prompt. 95 */ 96 dmsg_iocom_init(&iocom, fd, 0, 97 NULL, 98 shell_rcvmsg, 99 hammer2_shell_parse, 100 shell_ttymsg); 101 fcntl(0, F_SETFL, O_NONBLOCK); 102 printf("debug: connected\n"); 103 104 msg = dmsg_msg_alloc(&iocom.state0, 0, DMSG_DBG_SHELL, NULL, NULL); 105 dmsg_msg_write(msg); 106 dmsg_iocom_core(&iocom); 107 fprintf(stderr, "debug: disconnected\n"); 108 close(fd); 109 return 0; 110 } 111 #endif 112 113 /* 114 * Debug session front-end 115 * 116 * Callback from dmsg_iocom_core() when messages might be present 117 * on the socket. 118 */ 119 static 120 void 121 shell_msghandler(dmsg_msg_t *msg, int unmanaged) 122 { 123 dmsg_msg_t *nmsg; 124 125 switch(msg->tcmd) { 126 #if 0 127 case DMSG_LNK_ERROR: 128 case DMSG_LNK_ERROR | DMSGF_REPLY: 129 /* 130 * One-way non-transactional LNK_ERROR messages typically 131 * indicate a connection failure. Error code 0 is used by 132 * the debug shell to indicate no more results from last cmd. 133 */ 134 if (msg->any.head.error) { 135 fprintf(stderr, "Stream failure: %s\n", 136 dmsg_msg_str(msg)); 137 } else { 138 write(1, "debug> ", 7); 139 } 140 break; 141 case DMSG_LNK_ERROR | DMSGF_DELETE: 142 /* ignore termination of LNK_CONN */ 143 break; 144 #endif 145 case DMSG_DBG_SHELL: 146 /* 147 * We send the commands, not accept them. 148 * (one-way message, not transactional) 149 */ 150 if (unmanaged) 151 dmsg_msg_reply(msg, DMSG_ERR_NOSUPP); 152 break; 153 case DMSG_DBG_SHELL | DMSGF_REPLY: 154 /* 155 * A reply from the remote is data we copy to stdout. 156 * (one-way message, not transactional) 157 */ 158 if (msg->aux_size) { 159 msg->aux_data[msg->aux_size - 1] = 0; 160 write(1, msg->aux_data, strlen(msg->aux_data)); 161 } 162 break; 163 #if 1 164 case DMSG_LNK_CONN | DMSGF_CREATE: 165 fprintf(stderr, "Debug Shell received LNK_CONN\n"); 166 nmsg = dmsg_msg_alloc(&msg->state->iocom->state0, 0, 167 DMSG_DBG_SHELL, 168 NULL, NULL); 169 dmsg_msg_write(nmsg); 170 dmsg_msg_reply(msg, DMSG_ERR_NOSUPP); 171 break; 172 case DMSG_LNK_CONN | DMSGF_DELETE: 173 break; 174 #endif 175 default: 176 /* 177 * Ignore any unknown messages, Terminate any unknown 178 * transactions with an error. 179 */ 180 fprintf(stderr, "Unknown message: %s\n", dmsg_msg_str(msg)); 181 if (unmanaged) { 182 if (msg->any.head.cmd & DMSGF_CREATE) 183 dmsg_msg_reply(msg, DMSG_ERR_NOSUPP); 184 if (msg->any.head.cmd & DMSGF_DELETE) 185 dmsg_msg_reply(msg, DMSG_ERR_NOSUPP); 186 } 187 break; 188 } 189 } 190 191 /* 192 * Debug session front-end 193 */ 194 static 195 void 196 shell_ttymsg(dmsg_iocom_t *iocom) 197 { 198 dmsg_state_t *pstate; 199 dmsg_msg_t *msg; 200 char buf[256]; 201 char *cmd; 202 size_t len; 203 204 if (fgets(buf, sizeof(buf), stdin) != NULL) { 205 if (buf[0] == '@') { 206 pstate = dmsg_findspan(strtok(buf + 1, " \t\n")); 207 cmd = strtok(NULL, "\n"); 208 } else { 209 pstate = &iocom->state0; 210 cmd = strtok(buf, "\n"); 211 } 212 if (cmd && pstate) { 213 len = strlen(cmd) + 1; 214 msg = dmsg_msg_alloc(pstate, len, DMSG_DBG_SHELL, 215 NULL, NULL); 216 bcopy(cmd, msg->aux_data, len); 217 dmsg_msg_write(msg); 218 } else if (cmd) { 219 fprintf(stderr, "@msgid not found\n"); 220 } else { 221 /* 222 * This should cause the remote end to generate 223 * a debug> prompt (and thus shows that there is 224 * connectivity). 225 */ 226 msg = dmsg_msg_alloc(pstate, 0, DMSG_DBG_SHELL, 227 NULL, NULL); 228 dmsg_msg_write(msg); 229 } 230 } else if (feof(stdin)) { 231 /* 232 * Set EOF flag without setting any error code for normal 233 * EOF. 234 */ 235 iocom->flags |= DMSG_IOCOMF_EOF; 236 } else { 237 clearerr(stdin); 238 } 239 } 240 241 /* 242 * Debug session back-end (on remote side) 243 */ 244 static void shell_span(dmsg_msg_t *msg, char *cmdbuf); 245 static void shell_ping(dmsg_msg_t *msg, char *cmdbuf); 246 247 void 248 hammer2_shell_parse(dmsg_msg_t *msg, int unmanaged) 249 { 250 dmsg_iocom_t *iocom = msg->state->iocom; 251 char *cmdbuf; 252 char *cmdp; 253 uint32_t cmd; 254 255 /* 256 * Filter on debug shell commands and ping responses only 257 */ 258 cmd = msg->any.head.cmd; 259 if ((cmd & DMSGF_CMDSWMASK) == (DMSG_LNK_PING | DMSGF_REPLY)) { 260 dmsg_printf(iocom, "ping reply\n"); 261 return; 262 } 263 264 if ((cmd & DMSGF_PROTOS) != DMSG_PROTO_DBG) { 265 if (unmanaged) 266 dmsg_msg_reply(msg, DMSG_ERR_NOSUPP); 267 return; 268 } 269 if ((cmd & DMSGF_CMDSWMASK) != DMSG_DBG_SHELL) { 270 if (unmanaged) 271 dmsg_msg_reply(msg, DMSG_ERR_NOSUPP); 272 return; 273 } 274 275 /* 276 * Debug shell command 277 */ 278 cmdbuf = msg->aux_data; 279 cmdp = strsep(&cmdbuf, " \t"); 280 281 if (cmdp == NULL || *cmdp == 0) { 282 ; 283 } else if (strcmp(cmdp, "ping") == 0) { 284 shell_ping(msg, cmdbuf); 285 } else if (strcmp(cmdp, "span") == 0) { 286 shell_span(msg, cmdbuf); 287 } else if (strcmp(cmdp, "tree") == 0) { 288 dmsg_shell_tree(iocom, cmdbuf); /* dump spanning tree */ 289 } else if (strcmp(cmdp, "help") == 0 || strcmp(cmdp, "?") == 0) { 290 dmsg_printf(iocom, "help Command help\n"); 291 dmsg_printf(iocom, "span <host> Span to target host\n"); 292 dmsg_printf(iocom, "tree Dump spanning tree\n"); 293 dmsg_printf(iocom, "@span <cmd> Issue via circuit\n"); 294 } else { 295 dmsg_printf(iocom, "Unrecognized command: %s\n", cmdp); 296 } 297 dmsg_printf(iocom, "debug> "); 298 } 299 300 static void 301 shell_ping(dmsg_msg_t *msg, char *cmdbuf __unused) 302 { 303 dmsg_iocom_t *iocom = msg->state->iocom; 304 dmsg_msg_t *m2; 305 306 dmsg_printf(iocom, "sending ping\n"); 307 m2 = dmsg_msg_alloc(msg->state, 0, DMSG_LNK_PING, NULL, NULL); 308 dmsg_msg_write(m2); 309 } 310 311 static void 312 shell_span(dmsg_msg_t *msg, char *cmdbuf) 313 { 314 dmsg_iocom_t *iocom = msg->state->iocom; 315 dmsg_master_service_info_t *info; 316 const char *hostname = strsep(&cmdbuf, " \t"); 317 pthread_t thread; 318 int fd; 319 320 /* 321 * Connect to the target 322 */ 323 if (hostname == NULL) { 324 fd = -1; 325 } else { 326 fd = dmsg_connect(hostname); 327 } 328 329 /* 330 * Start master service 331 */ 332 if (fd < 0) { 333 dmsg_printf(iocom, "Connection to %s failed\n", hostname); 334 } else { 335 dmsg_printf(iocom, "Connected to %s\n", hostname); 336 337 info = malloc(sizeof(*info)); 338 bzero(info, sizeof(*info)); 339 info->fd = fd; 340 info->detachme = 1; 341 info->usrmsg_callback = hammer2_shell_parse; 342 info->label = strdup("client"); 343 344 pthread_create(&thread, NULL, dmsg_master_service, info); 345 /*pthread_join(thread, &res);*/ 346 } 347 } 348 349 /************************************************************************ 350 * DEBUGSPAN * 351 ************************************************************************ 352 * 353 * Connect to the target manually (not via the cluster list embedded in 354 * a hammer2 filesystem) and initiate the SPAN protocol. 355 */ 356 int 357 cmd_debugspan(const char *hostname) 358 { 359 pthread_t thread; 360 int fd; 361 void *res; 362 363 /* 364 * Connect to the target 365 */ 366 fd = dmsg_connect(hostname); 367 if (fd < 0) 368 return 1; 369 370 printf("debugspan: connected to %s, starting CONN/SPAN\n", hostname); 371 pthread_create(&thread, NULL, 372 dmsg_master_service, (void *)(intptr_t)fd); 373 pthread_join(thread, &res); 374 return(0); 375 } 376 377 /************************************************************************ 378 * SHOW * 379 ************************************************************************/ 380 381 static void show_volhdr(hammer2_volume_data_t *voldata, int fd, int bi); 382 static void show_bref(hammer2_volume_data_t *voldata, int fd, int tab, 383 int bi, hammer2_blockref_t *bref, int norecurse); 384 static void tabprintf(int tab, const char *ctl, ...); 385 386 static hammer2_off_t TotalAccum16[4]; /* includes TotalAccum64 */ 387 static hammer2_off_t TotalAccum64[4]; 388 static hammer2_off_t TotalUnavail; 389 static hammer2_off_t TotalFreemap; 390 391 int 392 cmd_show(const char *devpath, int which) 393 { 394 hammer2_blockref_t broot; 395 hammer2_blockref_t best; 396 hammer2_media_data_t media; 397 hammer2_media_data_t best_media; 398 int fd; 399 int i; 400 int best_i; 401 char *env; 402 403 memset(TotalAccum16, 0, sizeof(TotalAccum16)); 404 memset(TotalAccum64, 0, sizeof(TotalAccum64)); 405 TotalUnavail = TotalFreemap = 0; 406 407 env = getenv("HAMMER2_SHOW_TAB"); 408 if (env != NULL) { 409 show_tab = (int)strtol(env, NULL, 0); 410 if (errno || show_tab < 0 || show_tab > 8) 411 show_tab = 2; 412 } 413 414 fd = open(devpath, O_RDONLY); 415 if (fd < 0) { 416 perror("open"); 417 return 1; 418 } 419 420 /* 421 * Show the tree using the best volume header. 422 * -vvv will show the tree for all four volume headers. 423 */ 424 best_i = -1; 425 bzero(&best, sizeof(best)); 426 bzero(&best_media, sizeof(best_media)); 427 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) { 428 bzero(&broot, sizeof(broot)); 429 broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX; 430 lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, SEEK_SET); 431 if (read(fd, &media, HAMMER2_PBUFSIZE) == 432 (ssize_t)HAMMER2_PBUFSIZE) { 433 broot.mirror_tid = media.voldata.mirror_tid; 434 if (best_i < 0 || best.mirror_tid < broot.mirror_tid) { 435 best_i = i; 436 best = broot; 437 best_media = media; 438 } 439 printf("Volume header %d: mirror_tid=%016jx\n", 440 i, (intmax_t)broot.mirror_tid); 441 442 if (VerboseOpt >= 3) { 443 switch(which) { 444 case 0: 445 broot.type = HAMMER2_BREF_TYPE_VOLUME; 446 show_bref(&media.voldata, fd, 0, 447 i, &broot, 0); 448 break; 449 case 1: 450 broot.type = HAMMER2_BREF_TYPE_FREEMAP; 451 show_bref(&media.voldata, fd, 0, 452 i, &broot, 0); 453 break; 454 default: 455 show_volhdr(&media.voldata, fd, i); 456 break; 457 } 458 printf("\n"); 459 } 460 } 461 } 462 if (VerboseOpt < 3) { 463 switch(which) { 464 case 0: 465 best.type = HAMMER2_BREF_TYPE_VOLUME; 466 show_bref(&best_media.voldata, fd, 0, best_i, &best, 0); 467 break; 468 case 1: 469 best.type = HAMMER2_BREF_TYPE_FREEMAP; 470 show_bref(&best_media.voldata, fd, 0, best_i, &best, 0); 471 break; 472 default: 473 show_volhdr(&best_media.voldata, fd, best_i); 474 break; 475 } 476 } 477 close(fd); 478 479 if (which == 1 && VerboseOpt < 3) { 480 printf("Total unallocated storage: %6.3fGiB (%6.3fGiB in 64KB chunks)\n", 481 (double)TotalAccum16[0] / GIG, 482 (double)TotalAccum64[0] / GIG); 483 printf("Total possibly free storage: %6.3fGiB (%6.3fGiB in 64KB chunks)\n", 484 (double)TotalAccum16[2] / GIG, 485 (double)TotalAccum64[2] / GIG); 486 printf("Total allocated storage: %6.3fGiB (%6.3fGiB in 64KB chunks)\n", 487 (double)TotalAccum16[3] / GIG, 488 (double)TotalAccum64[3] / GIG); 489 printf("Total unavailable storage: %6.3fGiB\n", 490 (double)TotalUnavail / GIG); 491 printf("Total freemap storage: %6.3fGiB\n", 492 (double)TotalFreemap / GIG); 493 } 494 495 return 0; 496 } 497 498 static void 499 show_volhdr(hammer2_volume_data_t *voldata, int fd, int bi) 500 { 501 uint32_t status; 502 uint32_t i; 503 char *str; 504 char *name; 505 506 printf("\nVolume header %d {\n", bi); 507 printf(" magic 0x%016jx\n", (intmax_t)voldata->magic); 508 printf(" boot_beg 0x%016jx\n", (intmax_t)voldata->boot_beg); 509 printf(" boot_end 0x%016jx (%6.2fMB)\n", 510 (intmax_t)voldata->boot_end, 511 (double)(voldata->boot_end - voldata->boot_beg) / 512 (1024.0*1024.0)); 513 printf(" aux_beg 0x%016jx\n", (intmax_t)voldata->aux_beg); 514 printf(" aux_end 0x%016jx (%6.2fMB)\n", 515 (intmax_t)voldata->aux_end, 516 (double)(voldata->aux_end - voldata->aux_beg) / 517 (1024.0*1024.0)); 518 printf(" volu_size 0x%016jx (%6.2fGiB)\n", 519 (intmax_t)voldata->volu_size, 520 (double)voldata->volu_size / GIG); 521 printf(" version %d\n", voldata->version); 522 printf(" flags 0x%08x\n", voldata->flags); 523 printf(" copyid %d\n", voldata->copyid); 524 printf(" freemap_vers %d\n", voldata->freemap_version); 525 printf(" peer_type %d\n", voldata->peer_type); 526 527 str = NULL; 528 hammer2_uuid_to_str(&voldata->fsid, &str); 529 printf(" fsid %s\n", str); 530 free(str); 531 532 str = NULL; 533 name = NULL; 534 hammer2_uuid_to_str(&voldata->fstype, &str); 535 printf(" fstype %s\n", str); 536 uuid_addr_lookup(&voldata->fstype, &name, &status); 537 if (name == NULL) 538 name = strdup("?"); 539 printf(" (%s)\n", name); 540 free(name); 541 free(str); 542 543 printf(" allocator_size 0x%016jx (%6.2fGiB)\n", 544 voldata->allocator_size, 545 (double)voldata->allocator_size / GIG); 546 printf(" allocator_free 0x%016jx (%6.2fGiB)\n", 547 voldata->allocator_free, 548 (double)voldata->allocator_free / GIG); 549 printf(" allocator_beg 0x%016jx (%6.2fGiB)\n", 550 voldata->allocator_beg, 551 (double)voldata->allocator_beg / GIG); 552 553 printf(" mirror_tid 0x%016jx\n", voldata->mirror_tid); 554 printf(" reserved0080 0x%016jx\n", voldata->reserved0080); 555 printf(" reserved0088 0x%016jx\n", voldata->reserved0088); 556 printf(" freemap_tid 0x%016jx\n", voldata->freemap_tid); 557 for (i = 0; i < nitems(voldata->reserved00A0); ++i) { 558 printf(" reserved00A0/%u 0x%016jx\n", 559 i, voldata->reserved00A0[0]); 560 } 561 562 printf(" copyexists "); 563 for (i = 0; i < nitems(voldata->copyexists); ++i) 564 printf(" 0x%02x", voldata->copyexists[i]); 565 printf("\n"); 566 567 /* 568 * NOTE: Index numbers and ICRC_SECTn definitions are not matched, 569 * the ICRC for sector 0 actually uses the last index, for 570 * example. 571 * 572 * NOTE: The whole voldata CRC does not have to match critically 573 * as certain sub-areas of the volume header have their own 574 * CRCs. 575 */ 576 printf("\n"); 577 for (i = 0; i < nitems(voldata->icrc_sects); ++i) { 578 printf(" icrc_sects[%u] ", i); 579 switch(i) { 580 case HAMMER2_VOL_ICRC_SECT0: 581 printf("0x%08x/0x%08x", 582 hammer2_icrc32((char *)voldata + 583 HAMMER2_VOLUME_ICRC0_OFF, 584 HAMMER2_VOLUME_ICRC0_SIZE), 585 voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT0]); 586 if (hammer2_icrc32((char *)voldata + 587 HAMMER2_VOLUME_ICRC0_OFF, 588 HAMMER2_VOLUME_ICRC0_SIZE) == 589 voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT0]) { 590 printf(" (OK)"); 591 } else { 592 printf(" (FAILED)"); 593 } 594 break; 595 case HAMMER2_VOL_ICRC_SECT1: 596 printf("0x%08x/0x%08x", 597 hammer2_icrc32((char *)voldata + 598 HAMMER2_VOLUME_ICRC1_OFF, 599 HAMMER2_VOLUME_ICRC1_SIZE), 600 voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT1]); 601 if (hammer2_icrc32((char *)voldata + 602 HAMMER2_VOLUME_ICRC1_OFF, 603 HAMMER2_VOLUME_ICRC1_SIZE) == 604 voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT1]) { 605 printf(" (OK)"); 606 } else { 607 printf(" (FAILED)"); 608 } 609 610 break; 611 default: 612 printf("0x%08x (reserved)", voldata->icrc_sects[i]); 613 break; 614 } 615 printf("\n"); 616 } 617 printf(" icrc_volhdr 0x%08x/0x%08x", 618 hammer2_icrc32((char *)voldata + HAMMER2_VOLUME_ICRCVH_OFF, 619 HAMMER2_VOLUME_ICRCVH_SIZE), 620 voldata->icrc_volheader); 621 if (hammer2_icrc32((char *)voldata + HAMMER2_VOLUME_ICRCVH_OFF, 622 HAMMER2_VOLUME_ICRCVH_SIZE) == 623 voldata->icrc_volheader) { 624 printf(" (OK)\n"); 625 } else { 626 printf(" (FAILED - not a critical error)\n"); 627 } 628 629 /* 630 * The super-root and freemap blocksets (not recursed) 631 */ 632 printf("\n"); 633 printf(" sroot_blockset {\n"); 634 for (i = 0; i < HAMMER2_SET_COUNT; ++i) { 635 show_bref(voldata, fd, 16, i, 636 &voldata->sroot_blockset.blockref[i], 2); 637 printf("\n"); 638 } 639 printf(" }\n"); 640 641 printf(" freemap_blockset {\n"); 642 for (i = 0; i < HAMMER2_SET_COUNT; ++i) { 643 show_bref(voldata, fd, 16, i, 644 &voldata->freemap_blockset.blockref[i], 2); 645 printf("\n"); 646 } 647 printf(" }\n"); 648 649 printf("}\n"); 650 } 651 652 static void 653 show_bref(hammer2_volume_data_t *voldata, int fd, int tab, 654 int bi, hammer2_blockref_t *bref, int norecurse) 655 { 656 hammer2_media_data_t media; 657 hammer2_blockref_t *bscan; 658 hammer2_off_t tmp; 659 int i, bcount, namelen, failed, obrace; 660 int type_pad; 661 size_t bytes; 662 const char *type_str; 663 char *str = NULL; 664 uint32_t cv; 665 uint64_t cv64; 666 667 SHA256_CTX hash_ctx; 668 union { 669 uint8_t digest[SHA256_DIGEST_LENGTH]; 670 uint64_t digest64[SHA256_DIGEST_LENGTH/8]; 671 } u; 672 673 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX); 674 if (bytes) 675 bytes = (size_t)1 << bytes; 676 if (bytes) { 677 hammer2_off_t io_off; 678 hammer2_off_t io_base; 679 size_t io_bytes; 680 size_t boff; 681 682 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; 683 io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1); 684 boff = io_off - io_base; 685 686 io_bytes = HAMMER2_MINIOSIZE; 687 while (io_bytes + boff < bytes) 688 io_bytes <<= 1; 689 690 if (io_bytes > sizeof(media)) { 691 printf("(bad block size %zu)\n", bytes); 692 return; 693 } 694 if (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1) { 695 lseek(fd, io_base, SEEK_SET); 696 if (read(fd, &media, io_bytes) != (ssize_t)io_bytes) { 697 printf("(media read failed)\n"); 698 return; 699 } 700 if (boff) 701 bcopy((char *)&media + boff, &media, bytes); 702 } 703 } 704 705 bscan = NULL; 706 bcount = 0; 707 namelen = 0; 708 failed = 0; 709 obrace = 1; 710 711 type_str = hammer2_breftype_to_str(bref->type); 712 type_pad = 8 - strlen(type_str); 713 if (type_pad < 0) 714 type_pad = 0; 715 716 switch(bref->type) { 717 case HAMMER2_BREF_TYPE_INODE: 718 assert(bytes); 719 if (!(media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA)) { 720 bscan = &media.ipdata.u.blockset.blockref[0]; 721 bcount = HAMMER2_SET_COUNT; 722 } 723 break; 724 case HAMMER2_BREF_TYPE_INDIRECT: 725 assert(bytes); 726 bscan = &media.npdata[0]; 727 bcount = bytes / sizeof(hammer2_blockref_t); 728 break; 729 case HAMMER2_BREF_TYPE_VOLUME: 730 bscan = &media.voldata.sroot_blockset.blockref[0]; 731 bcount = HAMMER2_SET_COUNT; 732 break; 733 case HAMMER2_BREF_TYPE_FREEMAP: 734 bscan = &media.voldata.freemap_blockset.blockref[0]; 735 bcount = HAMMER2_SET_COUNT; 736 break; 737 case HAMMER2_BREF_TYPE_FREEMAP_NODE: 738 assert(bytes); 739 bscan = &media.npdata[0]; 740 bcount = bytes / sizeof(hammer2_blockref_t); 741 break; 742 } 743 744 if (QuietOpt > 0) { 745 tabprintf(tab, 746 "%s.%-3d %016jx %016jx/%-2d " 747 "mir=%016jx mod=%016jx leafcnt=%d ", 748 type_str, bi, (intmax_t)bref->data_off, 749 (intmax_t)bref->key, (intmax_t)bref->keybits, 750 (intmax_t)bref->mirror_tid, 751 (intmax_t)bref->modify_tid, 752 bref->leaf_count); 753 tab += show_tab; 754 } else { 755 tabprintf(tab, "%s.%-3d%*.*s 0x%016jx 0x%016jx/%-2d ", 756 type_str, bi, type_pad, type_pad, "", 757 (intmax_t)bref->data_off, 758 (intmax_t)bref->key, (intmax_t)bref->keybits); 759 /*if (norecurse > 1)*/ { 760 printf("\n"); 761 tabprintf(tab + 13, ""); 762 } 763 printf("mir=%016jx mod=%016jx lfcnt=%d ", 764 (intmax_t)bref->mirror_tid, (intmax_t)bref->modify_tid, 765 bref->leaf_count); 766 if (/*norecurse > 1 && */ (bcount || bref->flags || 767 bref->type == HAMMER2_BREF_TYPE_FREEMAP_NODE || 768 bref->type == HAMMER2_BREF_TYPE_FREEMAP_LEAF)) { 769 printf("\n"); 770 tabprintf(tab + 13, ""); 771 } 772 } 773 774 if (bcount) 775 printf("bcnt=%d ", bcount); 776 if (bref->flags) 777 printf("flags=%02x ", bref->flags); 778 if (bref->type == HAMMER2_BREF_TYPE_FREEMAP_NODE || 779 bref->type == HAMMER2_BREF_TYPE_FREEMAP_LEAF) { 780 printf("bigmask=%08x avail=%ld ", 781 bref->check.freemap.bigmask, bref->check.freemap.avail); 782 } 783 784 /* 785 * Check data integrity in verbose mode, otherwise we are just doing 786 * a quick meta-data scan. Meta-data integrity is always checked. 787 * (Also see the check above that ensures the media data is loaded, 788 * otherwise there's no data to check!). 789 * 790 * WARNING! bref->check state may be used for other things when 791 * bref has no data (bytes == 0). 792 */ 793 if (bytes && 794 (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1)) { 795 if (!(QuietOpt > 0)) { 796 /*if (norecurse > 1)*/ { 797 printf("\n"); 798 tabprintf(tab + 13, ""); 799 } 800 } 801 802 switch(HAMMER2_DEC_CHECK(bref->methods)) { 803 case HAMMER2_CHECK_NONE: 804 printf("meth=%02x ", bref->methods); 805 break; 806 case HAMMER2_CHECK_DISABLED: 807 printf("meth=%02x ", bref->methods); 808 break; 809 case HAMMER2_CHECK_ISCSI32: 810 cv = hammer2_icrc32(&media, bytes); 811 if (bref->check.iscsi32.value != cv) { 812 printf("(icrc %02x:%08x/%08x failed) ", 813 bref->methods, 814 bref->check.iscsi32.value, 815 cv); 816 failed = 1; 817 } else { 818 printf("meth=%02x iscsi32=%08x ", 819 bref->methods, cv); 820 } 821 break; 822 case HAMMER2_CHECK_XXHASH64: 823 cv64 = XXH64(&media, bytes, XXH_HAMMER2_SEED); 824 if (bref->check.xxhash64.value != cv64) { 825 printf("(xxhash64 %02x:%016jx/%016jx failed) ", 826 bref->methods, 827 bref->check.xxhash64.value, 828 cv64); 829 failed = 1; 830 } else { 831 printf("meth=%02x xxh=%016jx ", 832 bref->methods, cv64); 833 } 834 break; 835 case HAMMER2_CHECK_SHA192: 836 SHA256_Init(&hash_ctx); 837 SHA256_Update(&hash_ctx, &media, bytes); 838 SHA256_Final(u.digest, &hash_ctx); 839 u.digest64[2] ^= u.digest64[3]; 840 if (memcmp(u.digest, bref->check.sha192.data, 841 sizeof(bref->check.sha192.data))) { 842 printf("(sha192 failed) "); 843 failed = 1; 844 } else { 845 printf("meth=%02x ", bref->methods); 846 } 847 break; 848 case HAMMER2_CHECK_FREEMAP: 849 cv = hammer2_icrc32(&media, bytes); 850 if (bref->check.freemap.icrc32 != cv) { 851 printf("(fcrc %02x:%08x/%08x failed) ", 852 bref->methods, 853 bref->check.freemap.icrc32, 854 cv); 855 failed = 1; 856 } else { 857 printf("meth=%02x fcrc=%08x ", 858 bref->methods, cv); 859 } 860 break; 861 } 862 } 863 864 tab += show_tab; 865 866 if (QuietOpt > 0) { 867 obrace = 0; 868 printf("\n"); 869 goto skip_data; 870 } 871 872 switch(bref->type) { 873 case HAMMER2_BREF_TYPE_EMPTY: 874 obrace = 0; 875 break; 876 case HAMMER2_BREF_TYPE_DIRENT: 877 printf("{\n"); 878 if (bref->embed.dirent.namlen <= sizeof(bref->check.buf)) { 879 tabprintf(tab, "filename \"%*.*s\"\n", 880 bref->embed.dirent.namlen, 881 bref->embed.dirent.namlen, 882 bref->check.buf); 883 } else { 884 tabprintf(tab, "filename \"%*.*s\"\n", 885 bref->embed.dirent.namlen, 886 bref->embed.dirent.namlen, 887 media.buf); 888 } 889 tabprintf(tab, "inum 0x%016jx\n", 890 (uintmax_t)bref->embed.dirent.inum); 891 tabprintf(tab, "nlen %d\n", bref->embed.dirent.namlen); 892 tabprintf(tab, "type %s\n", 893 hammer2_iptype_to_str(bref->embed.dirent.type)); 894 break; 895 case HAMMER2_BREF_TYPE_INODE: 896 printf("{\n"); 897 namelen = media.ipdata.meta.name_len; 898 if (namelen > HAMMER2_INODE_MAXNAME) 899 namelen = 0; 900 tabprintf(tab, "filename \"%*.*s\"\n", 901 namelen, namelen, media.ipdata.filename); 902 tabprintf(tab, "version %d\n", media.ipdata.meta.version); 903 tabprintf(tab, "pfs_st %d\n", media.ipdata.meta.pfs_subtype); 904 tabprintf(tab, "uflags 0x%08x\n", 905 media.ipdata.meta.uflags); 906 if (media.ipdata.meta.rmajor || media.ipdata.meta.rminor) { 907 tabprintf(tab, "rmajor %d\n", 908 media.ipdata.meta.rmajor); 909 tabprintf(tab, "rminor %d\n", 910 media.ipdata.meta.rminor); 911 } 912 tabprintf(tab, "ctime %s\n", 913 hammer2_time64_to_str(media.ipdata.meta.ctime, &str)); 914 tabprintf(tab, "mtime %s\n", 915 hammer2_time64_to_str(media.ipdata.meta.mtime, &str)); 916 tabprintf(tab, "atime %s\n", 917 hammer2_time64_to_str(media.ipdata.meta.atime, &str)); 918 tabprintf(tab, "btime %s\n", 919 hammer2_time64_to_str(media.ipdata.meta.btime, &str)); 920 tabprintf(tab, "uid %s\n", 921 hammer2_uuid_to_str(&media.ipdata.meta.uid, &str)); 922 tabprintf(tab, "gid %s\n", 923 hammer2_uuid_to_str(&media.ipdata.meta.gid, &str)); 924 tabprintf(tab, "type %s\n", 925 hammer2_iptype_to_str(media.ipdata.meta.type)); 926 tabprintf(tab, "opflgs 0x%02x\n", 927 media.ipdata.meta.op_flags); 928 tabprintf(tab, "capflgs 0x%04x\n", 929 media.ipdata.meta.cap_flags); 930 tabprintf(tab, "mode %-7o\n", 931 media.ipdata.meta.mode); 932 tabprintf(tab, "inum 0x%016jx\n", 933 media.ipdata.meta.inum); 934 tabprintf(tab, "size %ju ", 935 (uintmax_t)media.ipdata.meta.size); 936 if (media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA && 937 media.ipdata.meta.size <= HAMMER2_EMBEDDED_BYTES) 938 printf("(embedded data)\n"); 939 else 940 printf("\n"); 941 tabprintf(tab, "nlinks %ju\n", 942 (uintmax_t)media.ipdata.meta.nlinks); 943 tabprintf(tab, "iparent 0x%016jx\n", 944 (uintmax_t)media.ipdata.meta.iparent); 945 tabprintf(tab, "name_key 0x%016jx\n", 946 (uintmax_t)media.ipdata.meta.name_key); 947 tabprintf(tab, "name_len %u\n", 948 media.ipdata.meta.name_len); 949 tabprintf(tab, "ncopies %u\n", 950 media.ipdata.meta.ncopies); 951 tabprintf(tab, "compalg %u\n", 952 media.ipdata.meta.comp_algo); 953 tabprintf(tab, "target_t %u\n", 954 media.ipdata.meta.target_type); 955 tabprintf(tab, "checkalg %u\n", 956 media.ipdata.meta.check_algo); 957 if ((media.ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) || 958 media.ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) { 959 tabprintf(tab, "pfs_nmas %u\n", 960 media.ipdata.meta.pfs_nmasters); 961 tabprintf(tab, "pfs_type %u (%s)\n", 962 media.ipdata.meta.pfs_type, 963 hammer2_pfstype_to_str(media.ipdata.meta.pfs_type)); 964 tabprintf(tab, "pfs_inum 0x%016jx\n", 965 (uintmax_t)media.ipdata.meta.pfs_inum); 966 tabprintf(tab, "pfs_clid %s\n", 967 hammer2_uuid_to_str(&media.ipdata.meta.pfs_clid, 968 &str)); 969 tabprintf(tab, "pfs_fsid %s\n", 970 hammer2_uuid_to_str(&media.ipdata.meta.pfs_fsid, 971 &str)); 972 tabprintf(tab, "pfs_lsnap_tid 0x%016jx\n", 973 (uintmax_t)media.ipdata.meta.pfs_lsnap_tid); 974 } 975 tabprintf(tab, "data_quota %ju\n", 976 (uintmax_t)media.ipdata.meta.data_quota); 977 tabprintf(tab, "data_count %ju\n", 978 (uintmax_t)bref->embed.stats.data_count); 979 tabprintf(tab, "inode_quota %ju\n", 980 (uintmax_t)media.ipdata.meta.inode_quota); 981 tabprintf(tab, "inode_count %ju\n", 982 (uintmax_t)bref->embed.stats.inode_count); 983 break; 984 case HAMMER2_BREF_TYPE_INDIRECT: 985 printf("{\n"); 986 break; 987 case HAMMER2_BREF_TYPE_DATA: 988 printf("\n"); 989 obrace = 0; 990 break; 991 case HAMMER2_BREF_TYPE_VOLUME: 992 printf("mirror_tid=%016jx freemap_tid=%016jx ", 993 media.voldata.mirror_tid, 994 media.voldata.freemap_tid); 995 printf("{\n"); 996 break; 997 case HAMMER2_BREF_TYPE_FREEMAP: 998 printf("mirror_tid=%016jx freemap_tid=%016jx ", 999 media.voldata.mirror_tid, 1000 media.voldata.freemap_tid); 1001 printf("{\n"); 1002 break; 1003 case HAMMER2_BREF_TYPE_FREEMAP_LEAF: 1004 printf("{\n"); 1005 tmp = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; 1006 tmp &= HAMMER2_SEGMASK; 1007 tmp /= HAMMER2_PBUFSIZE; 1008 assert(tmp >= HAMMER2_ZONE_FREEMAP_00); 1009 assert(tmp < HAMMER2_ZONE_FREEMAP_END); 1010 tmp -= HAMMER2_ZONE_FREEMAP_00; 1011 tmp /= HAMMER2_ZONE_FREEMAP_INC; 1012 tabprintf(tab, "rotation=%d\n", (int)tmp); 1013 1014 for (i = 0; i < HAMMER2_FREEMAP_COUNT; ++i) { 1015 hammer2_off_t data_off = bref->key + 1016 i * HAMMER2_FREEMAP_LEVEL0_SIZE; 1017 #if HAMMER2_BMAP_ELEMENTS != 8 1018 #error "cmd_debug.c: HAMMER2_BMAP_ELEMENTS expected to be 8" 1019 #endif 1020 tabprintf(tab + 4, "%016jx %04d.%04x (avail=%7d) " 1021 "%016jx %016jx %016jx %016jx " 1022 "%016jx %016jx %016jx %016jx\n", 1023 data_off, i, media.bmdata[i].class, 1024 media.bmdata[i].avail, 1025 media.bmdata[i].bitmapq[0], 1026 media.bmdata[i].bitmapq[1], 1027 media.bmdata[i].bitmapq[2], 1028 media.bmdata[i].bitmapq[3], 1029 media.bmdata[i].bitmapq[4], 1030 media.bmdata[i].bitmapq[5], 1031 media.bmdata[i].bitmapq[6], 1032 media.bmdata[i].bitmapq[7]); 1033 } 1034 tabprintf(tab, "}\n"); 1035 break; 1036 case HAMMER2_BREF_TYPE_FREEMAP_NODE: 1037 printf("{\n"); 1038 tmp = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; 1039 tmp &= HAMMER2_SEGMASK; 1040 tmp /= HAMMER2_PBUFSIZE; 1041 assert(tmp >= HAMMER2_ZONE_FREEMAP_00); 1042 assert(tmp < HAMMER2_ZONE_FREEMAP_END); 1043 tmp -= HAMMER2_ZONE_FREEMAP_00; 1044 tmp /= HAMMER2_ZONE_FREEMAP_INC; 1045 tabprintf(tab, "rotation=%d\n", (int)tmp); 1046 break; 1047 default: 1048 printf("\n"); 1049 obrace = 0; 1050 break; 1051 } 1052 if (str) 1053 free(str); 1054 1055 skip_data: 1056 /* 1057 * Update statistics. 1058 */ 1059 switch(bref->type) { 1060 case HAMMER2_BREF_TYPE_FREEMAP_LEAF: 1061 for (i = 0; i < HAMMER2_FREEMAP_COUNT; ++i) { 1062 hammer2_off_t data_off = bref->key + 1063 i * HAMMER2_FREEMAP_LEVEL0_SIZE; 1064 if (data_off >= voldata->aux_end && 1065 data_off < voldata->volu_size) { 1066 int j; 1067 for (j = 0; j < 4; ++j) 1068 CountBlocks(&media.bmdata[i], j, 1069 &TotalAccum16[j], 1070 &TotalAccum64[j]); 1071 } else 1072 TotalUnavail += HAMMER2_FREEMAP_LEVEL0_SIZE; 1073 } 1074 TotalFreemap += HAMMER2_FREEMAP_LEVEL1_SIZE; 1075 break; 1076 default: 1077 break; 1078 } 1079 1080 /* 1081 * Recurse if norecurse == 0. If the CRC failed, pass norecurse = 1. 1082 * That is, if an indirect or inode fails we still try to list its 1083 * direct children to help with debugging, but go no further than 1084 * that because they are probably garbage. 1085 */ 1086 for (i = 0; norecurse == 0 && i < bcount; ++i) { 1087 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) { 1088 show_bref(voldata, fd, tab, i, &bscan[i], failed); 1089 } 1090 } 1091 tab -= show_tab; 1092 if (obrace) { 1093 if (bref->type == HAMMER2_BREF_TYPE_INODE) 1094 tabprintf(tab, "} (%s.%d, \"%*.*s\")\n", 1095 type_str, bi, namelen, namelen, 1096 media.ipdata.filename); 1097 else 1098 tabprintf(tab, "} (%s.%d)\n", type_str, bi); 1099 } 1100 } 1101 1102 static 1103 void 1104 CountBlocks(hammer2_bmap_data_t *bmap, int value, 1105 hammer2_off_t *accum16, hammer2_off_t *accum64) 1106 { 1107 int i, j, bits; 1108 hammer2_bitmap_t value16, value64; 1109 1110 bits = (int)sizeof(hammer2_bitmap_t) * 8; 1111 assert(bits == 64); 1112 1113 value16 = value; 1114 assert(value16 < 4); 1115 value64 = (value16 << 6) | (value16 << 4) | (value16 << 2) | value16; 1116 assert(value64 < 256); 1117 1118 for (i = 0; i < HAMMER2_BMAP_ELEMENTS; ++i) { 1119 hammer2_bitmap_t bm = bmap->bitmapq[i]; 1120 hammer2_bitmap_t bm_save = bm; 1121 hammer2_bitmap_t mask; 1122 1123 mask = 0x03; /* 2 bits per 16KB */ 1124 for (j = 0; j < bits; j += 2) { 1125 if ((bm & mask) == value16) 1126 *accum16 += 16384; 1127 bm >>= 2; 1128 } 1129 1130 bm = bm_save; 1131 mask = 0xFF; /* 8 bits per 64KB chunk */ 1132 for (j = 0; j < bits; j += 8) { 1133 if ((bm & mask) == value64) 1134 *accum64 += 65536; 1135 bm >>= 8; 1136 } 1137 } 1138 } 1139 1140 int 1141 cmd_hash(int ac, const char **av) 1142 { 1143 int i; 1144 1145 for (i = 0; i < ac; ++i) { 1146 printf("%016jx %s\n", 1147 dirhash((const unsigned char*)av[i], strlen(av[i])), 1148 av[i]); 1149 } 1150 return(0); 1151 } 1152 1153 int 1154 cmd_dhash(int ac, const char **av) 1155 { 1156 char buf[1024]; /* 1K extended directory record */ 1157 uint64_t hash; 1158 int i; 1159 1160 for (i = 0; i < ac; ++i) { 1161 bzero(buf, sizeof(buf)); 1162 snprintf(buf, sizeof(buf), "%s", av[i]); 1163 hash = XXH64(buf, sizeof(buf), XXH_HAMMER2_SEED); 1164 printf("%016jx %s\n", hash, av[i]); 1165 } 1166 return(0); 1167 } 1168 1169 int 1170 cmd_dumpchain(const char *path, u_int flags) 1171 { 1172 int dummy = (int)flags; 1173 int ecode = 0; 1174 int fd; 1175 1176 fd = open(path, O_RDONLY); 1177 if (fd >= 0) { 1178 if (ioctl(fd, HAMMER2IOC_DEBUG_DUMP, &dummy) < 0) { 1179 fprintf(stderr, "%s: %s\n", path, strerror(errno)); 1180 ecode = 1; 1181 } 1182 close(fd); 1183 } else { 1184 fprintf(stderr, "unable to open %s\n", path); 1185 ecode = 1; 1186 } 1187 return ecode; 1188 } 1189 1190 1191 static 1192 void 1193 tabprintf(int tab, const char *ctl, ...) 1194 { 1195 va_list va; 1196 1197 printf("%*.*s", tab, tab, ""); 1198 va_start(va, ctl); 1199 vprintf(ctl, va); 1200 va_end(va); 1201 } 1202