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 static int show_tab = 2; 41 42 static void shell_msghandler(dmsg_msg_t *msg, int unmanaged); 43 static void shell_ttymsg(dmsg_iocom_t *iocom); 44 static void CountBlocks(hammer2_bmap_data_t *bmap, int value, 45 hammer2_off_t *accum16, hammer2_off_t *accum64); 46 47 /************************************************************************ 48 * SHELL * 49 ************************************************************************/ 50 51 int 52 cmd_shell(const char *hostname) 53 { 54 dmsg_master_service_info_t *info; 55 pthread_t thread; 56 int fd; 57 58 fd = dmsg_connect(hostname); 59 if (fd < 0) 60 return 1; 61 62 info = malloc(sizeof(*info)); 63 bzero(info, sizeof(*info)); 64 info->fd = fd; 65 info->detachme = 0; 66 info->usrmsg_callback = shell_msghandler; 67 info->altmsg_callback = shell_ttymsg; 68 info->label = strdup("debug"); 69 pthread_create(&thread, NULL, dmsg_master_service, info); 70 pthread_join(thread, NULL); 71 72 return 0; 73 } 74 75 #if 0 76 int 77 cmd_shell(const char *hostname) 78 { 79 struct dmsg_iocom iocom; 80 dmsg_msg_t *msg; 81 int fd; 82 83 /* 84 * Connect to the target 85 */ 86 fd = dmsg_connect(hostname); 87 if (fd < 0) 88 return 1; 89 90 /* 91 * Initialize the session and transmit an empty DMSG_DBG_SHELL 92 * to cause the remote end to generate a prompt. 93 */ 94 dmsg_iocom_init(&iocom, fd, 0, 95 NULL, 96 shell_rcvmsg, 97 hammer2_shell_parse, 98 shell_ttymsg); 99 fcntl(0, F_SETFL, O_NONBLOCK); 100 printf("debug: connected\n"); 101 102 msg = dmsg_msg_alloc(&iocom.state0, 0, DMSG_DBG_SHELL, NULL, NULL); 103 dmsg_msg_write(msg); 104 dmsg_iocom_core(&iocom); 105 fprintf(stderr, "debug: disconnected\n"); 106 close(fd); 107 return 0; 108 } 109 #endif 110 111 /* 112 * Debug session front-end 113 * 114 * Callback from dmsg_iocom_core() when messages might be present 115 * on the socket. 116 */ 117 static 118 void 119 shell_msghandler(dmsg_msg_t *msg, int unmanaged) 120 { 121 dmsg_msg_t *nmsg; 122 123 switch(msg->tcmd) { 124 #if 0 125 case DMSG_LNK_ERROR: 126 case DMSG_LNK_ERROR | DMSGF_REPLY: 127 /* 128 * One-way non-transactional LNK_ERROR messages typically 129 * indicate a connection failure. Error code 0 is used by 130 * the debug shell to indicate no more results from last cmd. 131 */ 132 if (msg->any.head.error) { 133 fprintf(stderr, "Stream failure: %s\n", 134 dmsg_msg_str(msg)); 135 } else { 136 write(1, "debug> ", 7); 137 } 138 break; 139 case DMSG_LNK_ERROR | DMSGF_DELETE: 140 /* ignore termination of LNK_CONN */ 141 break; 142 #endif 143 case DMSG_DBG_SHELL: 144 /* 145 * We send the commands, not accept them. 146 * (one-way message, not transactional) 147 */ 148 if (unmanaged) 149 dmsg_msg_reply(msg, DMSG_ERR_NOSUPP); 150 break; 151 case DMSG_DBG_SHELL | DMSGF_REPLY: 152 /* 153 * A reply from the remote is data we copy to stdout. 154 * (one-way message, not transactional) 155 */ 156 if (msg->aux_size) { 157 msg->aux_data[msg->aux_size - 1] = 0; 158 write(1, msg->aux_data, strlen(msg->aux_data)); 159 } 160 break; 161 #if 1 162 case DMSG_LNK_CONN | DMSGF_CREATE: 163 fprintf(stderr, "Debug Shell received LNK_CONN\n"); 164 nmsg = dmsg_msg_alloc(&msg->state->iocom->state0, 0, 165 DMSG_DBG_SHELL, 166 NULL, NULL); 167 dmsg_msg_write(nmsg); 168 dmsg_msg_reply(msg, DMSG_ERR_NOSUPP); 169 break; 170 case DMSG_LNK_CONN | DMSGF_DELETE: 171 break; 172 #endif 173 default: 174 /* 175 * Ignore any unknown messages, Terminate any unknown 176 * transactions with an error. 177 */ 178 fprintf(stderr, "Unknown message: %s\n", dmsg_msg_str(msg)); 179 if (unmanaged) { 180 if (msg->any.head.cmd & DMSGF_CREATE) 181 dmsg_msg_reply(msg, DMSG_ERR_NOSUPP); 182 if (msg->any.head.cmd & DMSGF_DELETE) 183 dmsg_msg_reply(msg, DMSG_ERR_NOSUPP); 184 } 185 break; 186 } 187 } 188 189 /* 190 * Debug session front-end 191 */ 192 static 193 void 194 shell_ttymsg(dmsg_iocom_t *iocom) 195 { 196 dmsg_state_t *pstate; 197 dmsg_msg_t *msg; 198 char buf[256]; 199 char *cmd; 200 size_t len; 201 202 if (fgets(buf, sizeof(buf), stdin) != NULL) { 203 if (buf[0] == '@') { 204 pstate = dmsg_findspan(strtok(buf + 1, " \t\n")); 205 cmd = strtok(NULL, "\n"); 206 } else { 207 pstate = &iocom->state0; 208 cmd = strtok(buf, "\n"); 209 } 210 if (cmd && pstate) { 211 len = strlen(cmd) + 1; 212 msg = dmsg_msg_alloc(pstate, len, DMSG_DBG_SHELL, 213 NULL, NULL); 214 bcopy(cmd, msg->aux_data, len); 215 dmsg_msg_write(msg); 216 } else if (cmd) { 217 fprintf(stderr, "@msgid not found\n"); 218 } else { 219 /* 220 * This should cause the remote end to generate 221 * a debug> prompt (and thus shows that there is 222 * connectivity). 223 */ 224 msg = dmsg_msg_alloc(pstate, 0, DMSG_DBG_SHELL, 225 NULL, NULL); 226 dmsg_msg_write(msg); 227 } 228 } else if (feof(stdin)) { 229 /* 230 * Set EOF flag without setting any error code for normal 231 * EOF. 232 */ 233 iocom->flags |= DMSG_IOCOMF_EOF; 234 } else { 235 clearerr(stdin); 236 } 237 } 238 239 /* 240 * Debug session back-end (on remote side) 241 */ 242 static void shell_span(dmsg_msg_t *msg, char *cmdbuf); 243 static void shell_ping(dmsg_msg_t *msg, char *cmdbuf); 244 245 void 246 hammer2_shell_parse(dmsg_msg_t *msg, int unmanaged) 247 { 248 dmsg_iocom_t *iocom = msg->state->iocom; 249 char *cmdbuf; 250 char *cmdp; 251 uint32_t cmd; 252 253 /* 254 * Filter on debug shell commands and ping responses only 255 */ 256 cmd = msg->any.head.cmd; 257 if ((cmd & DMSGF_CMDSWMASK) == (DMSG_LNK_PING | DMSGF_REPLY)) { 258 dmsg_printf(iocom, "ping reply\n"); 259 return; 260 } 261 262 if ((cmd & DMSGF_PROTOS) != DMSG_PROTO_DBG) { 263 if (unmanaged) 264 dmsg_msg_reply(msg, DMSG_ERR_NOSUPP); 265 return; 266 } 267 if ((cmd & DMSGF_CMDSWMASK) != DMSG_DBG_SHELL) { 268 if (unmanaged) 269 dmsg_msg_reply(msg, DMSG_ERR_NOSUPP); 270 return; 271 } 272 273 /* 274 * Debug shell command 275 */ 276 cmdbuf = msg->aux_data; 277 cmdp = strsep(&cmdbuf, " \t"); 278 279 if (cmdp == NULL || *cmdp == 0) { 280 ; 281 } else if (strcmp(cmdp, "ping") == 0) { 282 shell_ping(msg, cmdbuf); 283 } else if (strcmp(cmdp, "span") == 0) { 284 shell_span(msg, cmdbuf); 285 } else if (strcmp(cmdp, "tree") == 0) { 286 dmsg_shell_tree(iocom, cmdbuf); /* dump spanning tree */ 287 } else if (strcmp(cmdp, "help") == 0 || strcmp(cmdp, "?") == 0) { 288 dmsg_printf(iocom, "help Command help\n"); 289 dmsg_printf(iocom, "span <host> Span to target host\n"); 290 dmsg_printf(iocom, "tree Dump spanning tree\n"); 291 dmsg_printf(iocom, "@span <cmd> Issue via circuit\n"); 292 } else { 293 dmsg_printf(iocom, "Unrecognized command: %s\n", cmdp); 294 } 295 dmsg_printf(iocom, "debug> "); 296 } 297 298 static void 299 shell_ping(dmsg_msg_t *msg, char *cmdbuf __unused) 300 { 301 dmsg_iocom_t *iocom = msg->state->iocom; 302 dmsg_msg_t *m2; 303 304 dmsg_printf(iocom, "sending ping\n"); 305 m2 = dmsg_msg_alloc(msg->state, 0, DMSG_LNK_PING, NULL, NULL); 306 dmsg_msg_write(m2); 307 } 308 309 static void 310 shell_span(dmsg_msg_t *msg, char *cmdbuf) 311 { 312 dmsg_iocom_t *iocom = msg->state->iocom; 313 dmsg_master_service_info_t *info; 314 const char *hostname = strsep(&cmdbuf, " \t"); 315 pthread_t thread; 316 int fd; 317 318 /* 319 * Connect to the target 320 */ 321 if (hostname == NULL) { 322 fd = -1; 323 } else { 324 fd = dmsg_connect(hostname); 325 } 326 327 /* 328 * Start master service 329 */ 330 if (fd < 0) { 331 dmsg_printf(iocom, "Connection to %s failed\n", hostname); 332 } else { 333 dmsg_printf(iocom, "Connected to %s\n", hostname); 334 335 info = malloc(sizeof(*info)); 336 bzero(info, sizeof(*info)); 337 info->fd = fd; 338 info->detachme = 1; 339 info->usrmsg_callback = hammer2_shell_parse; 340 info->label = strdup("client"); 341 342 pthread_create(&thread, NULL, dmsg_master_service, info); 343 /*pthread_join(thread, &res);*/ 344 } 345 } 346 347 /************************************************************************ 348 * DEBUGSPAN * 349 ************************************************************************ 350 * 351 * Connect to the target manually (not via the cluster list embedded in 352 * a hammer2 filesystem) and initiate the SPAN protocol. 353 */ 354 int 355 cmd_debugspan(const char *hostname) 356 { 357 pthread_t thread; 358 int fd; 359 void *res; 360 361 /* 362 * Connect to the target 363 */ 364 fd = dmsg_connect(hostname); 365 if (fd < 0) 366 return 1; 367 368 printf("debugspan: connected to %s, starting CONN/SPAN\n", hostname); 369 pthread_create(&thread, NULL, 370 dmsg_master_service, (void *)(intptr_t)fd); 371 pthread_join(thread, &res); 372 return(0); 373 } 374 375 /************************************************************************ 376 * SHOW * 377 ************************************************************************/ 378 379 static void show_bref(hammer2_volume_data_t *voldata, int fd, int tab, 380 int bi, hammer2_blockref_t *bref, int norecurse); 381 static void tabprintf(int tab, const char *ctl, ...); 382 383 static hammer2_off_t TotalAccum16[4]; /* includes TotalAccum64 */ 384 static hammer2_off_t TotalAccum64[4]; 385 static hammer2_off_t TotalUnavail; 386 static hammer2_off_t TotalFreemap; 387 388 int 389 cmd_show(const char *devpath, int dofreemap) 390 { 391 hammer2_blockref_t broot; 392 hammer2_blockref_t best; 393 hammer2_media_data_t media; 394 int fd; 395 int i; 396 int best_i; 397 char *env; 398 399 memset(TotalAccum16, 0, sizeof(TotalAccum16)); 400 memset(TotalAccum64, 0, sizeof(TotalAccum64)); 401 TotalUnavail = TotalFreemap = 0; 402 403 env = getenv("HAMMER2_SHOW_TAB"); 404 if (env != NULL) { 405 show_tab = (int)strtol(env, NULL, 0); 406 if (errno || show_tab < 0 || show_tab > 8) 407 show_tab = 2; 408 } 409 410 fd = open(devpath, O_RDONLY); 411 if (fd < 0) { 412 perror("open"); 413 return 1; 414 } 415 416 /* 417 * Show the tree using the best volume header. 418 * -vvv will show the tree for all four volume headers. 419 */ 420 best_i = -1; 421 bzero(&best, sizeof(best)); 422 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) { 423 bzero(&broot, sizeof(broot)); 424 broot.type = dofreemap ? 425 HAMMER2_BREF_TYPE_FREEMAP : HAMMER2_BREF_TYPE_VOLUME; 426 broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX; 427 lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, SEEK_SET); 428 if (read(fd, &media, HAMMER2_PBUFSIZE) == 429 (ssize_t)HAMMER2_PBUFSIZE) { 430 broot.mirror_tid = media.voldata.mirror_tid; 431 if (best_i < 0 || best.mirror_tid < broot.mirror_tid) { 432 best_i = i; 433 best = broot; 434 } 435 if (VerboseOpt >= 3) 436 show_bref(&media.voldata, fd, 0, i, &broot, 0); 437 } 438 } 439 if (VerboseOpt < 3) 440 show_bref(&media.voldata, fd, 0, best_i, &best, 0); 441 close(fd); 442 443 #define GIG (1024LL*1024*1024) 444 if (dofreemap && VerboseOpt < 3) { 445 printf("Total unallocated storage: %6.3fGB (%6.3fGB in 64KB chunks)\n", 446 (double)TotalAccum16[0] / GIG, 447 (double)TotalAccum64[0] / GIG); 448 printf("Total possibly free storage: %6.3fGB (%6.3fGB in 64KB chunks)\n", 449 (double)TotalAccum16[2] / GIG, 450 (double)TotalAccum64[2] / GIG); 451 printf("Total allocated storage: %6.3fGB (%6.3fGB in 64KB chunks)\n", 452 (double)TotalAccum16[3] / GIG, 453 (double)TotalAccum64[3] / GIG); 454 printf("Total unavailable storage: %6.3fGB\n", 455 (double)TotalUnavail / GIG); 456 printf("Total freemap storage: %6.3fGB\n", 457 (double)TotalFreemap / GIG); 458 } 459 460 return 0; 461 } 462 463 static void 464 show_bref(hammer2_volume_data_t *voldata, int fd, int tab, 465 int bi, hammer2_blockref_t *bref, int norecurse) 466 { 467 hammer2_media_data_t media; 468 hammer2_blockref_t *bscan; 469 hammer2_off_t tmp; 470 int i, bcount, namelen, failed, obrace; 471 size_t bytes; 472 const char *type_str; 473 char *str = NULL; 474 uint32_t cv; 475 uint64_t cv64; 476 477 SHA256_CTX hash_ctx; 478 union { 479 uint8_t digest[SHA256_DIGEST_LENGTH]; 480 uint64_t digest64[SHA256_DIGEST_LENGTH/8]; 481 } u; 482 483 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX); 484 if (bytes) 485 bytes = (size_t)1 << bytes; 486 if (bytes) { 487 hammer2_off_t io_off; 488 hammer2_off_t io_base; 489 size_t io_bytes; 490 size_t boff; 491 492 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; 493 io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1); 494 boff = io_off - io_base; 495 496 io_bytes = HAMMER2_MINIOSIZE; 497 while (io_bytes + boff < bytes) 498 io_bytes <<= 1; 499 500 if (io_bytes > sizeof(media)) { 501 printf("(bad block size %zd)\n", bytes); 502 return; 503 } 504 if (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1) { 505 lseek(fd, io_base, SEEK_SET); 506 if (read(fd, &media, io_bytes) != (ssize_t)io_bytes) { 507 printf("(media read failed)\n"); 508 return; 509 } 510 if (boff) 511 bcopy((char *)&media + boff, &media, bytes); 512 } 513 } 514 515 bscan = NULL; 516 bcount = 0; 517 namelen = 0; 518 failed = 0; 519 obrace = 1; 520 521 switch(bref->type) { 522 case HAMMER2_BREF_TYPE_EMPTY: 523 type_str = "empty"; 524 break; 525 case HAMMER2_BREF_TYPE_DIRENT: 526 type_str = "dirent"; 527 break; 528 case HAMMER2_BREF_TYPE_INODE: 529 type_str = "inode"; 530 break; 531 case HAMMER2_BREF_TYPE_INDIRECT: 532 type_str = "indblk"; 533 break; 534 case HAMMER2_BREF_TYPE_DATA: 535 type_str = "data"; 536 break; 537 case HAMMER2_BREF_TYPE_VOLUME: 538 type_str = "volume"; 539 break; 540 case HAMMER2_BREF_TYPE_FREEMAP: 541 type_str = "freemap"; 542 break; 543 case HAMMER2_BREF_TYPE_FREEMAP_NODE: 544 type_str = "fmapnode"; 545 break; 546 case HAMMER2_BREF_TYPE_FREEMAP_LEAF: 547 type_str = "fbitmap"; 548 break; 549 default: 550 type_str = "unknown"; 551 break; 552 } 553 554 switch(bref->type) { 555 case HAMMER2_BREF_TYPE_INODE: 556 if (!(media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA)) { 557 bscan = &media.ipdata.u.blockset.blockref[0]; 558 bcount = HAMMER2_SET_COUNT; 559 } 560 break; 561 case HAMMER2_BREF_TYPE_INDIRECT: 562 bscan = &media.npdata[0]; 563 bcount = bytes / sizeof(hammer2_blockref_t); 564 break; 565 case HAMMER2_BREF_TYPE_VOLUME: 566 bscan = &media.voldata.sroot_blockset.blockref[0]; 567 bcount = HAMMER2_SET_COUNT; 568 break; 569 case HAMMER2_BREF_TYPE_FREEMAP: 570 bscan = &media.voldata.freemap_blockset.blockref[0]; 571 bcount = HAMMER2_SET_COUNT; 572 break; 573 case HAMMER2_BREF_TYPE_FREEMAP_NODE: 574 bscan = &media.npdata[0]; 575 bcount = bytes / sizeof(hammer2_blockref_t); 576 break; 577 } 578 579 tabprintf(tab, 580 "%s.%-3d %016jx %016jx/%-2d " 581 "mir=%016jx mod=%016jx leafcnt=%d ", 582 type_str, bi, (intmax_t)bref->data_off, 583 (intmax_t)bref->key, (intmax_t)bref->keybits, 584 (intmax_t)bref->mirror_tid, (intmax_t)bref->modify_tid, 585 bref->leaf_count); 586 tab += show_tab; 587 if (bcount) 588 printf("bcnt=%d ", bcount); 589 if (bref->flags) 590 printf("flags=%02x ", bref->flags); 591 if (bref->type == HAMMER2_BREF_TYPE_FREEMAP_NODE || 592 bref->type == HAMMER2_BREF_TYPE_FREEMAP_LEAF) { 593 printf("bigmask=%08x avail=%ld ", 594 bref->check.freemap.bigmask, bref->check.freemap.avail); 595 } 596 597 /* 598 * Check data integrity in verbose mode, otherwise we are just doing 599 * a quick meta-data scan. Meta-data integrity is always checked. 600 * (Also see the check above that ensures the media data is loaded, 601 * otherwise there's no data to check!). 602 * 603 * WARNING! bref->check state may be used for other things when 604 * bref has no data (bytes == 0). 605 */ 606 if (bytes && 607 (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1)) { 608 switch(HAMMER2_DEC_CHECK(bref->methods)) { 609 case HAMMER2_CHECK_NONE: 610 printf("(meth %02x) ", bref->methods); 611 break; 612 case HAMMER2_CHECK_DISABLED: 613 printf("(meth %02x) ", bref->methods); 614 break; 615 case HAMMER2_CHECK_ISCSI32: 616 cv = hammer2_icrc32(&media, bytes); 617 if (bref->check.iscsi32.value != cv) { 618 printf("(icrc %02x:%08x/%08x failed) ", 619 bref->methods, 620 bref->check.iscsi32.value, 621 cv); 622 failed = 1; 623 } else { 624 printf("(meth %02x, iscsi32=%08x) ", 625 bref->methods, cv); 626 } 627 break; 628 case HAMMER2_CHECK_XXHASH64: 629 cv64 = XXH64(&media, bytes, XXH_HAMMER2_SEED); 630 if (bref->check.xxhash64.value != cv64) { 631 printf("(xxhash64 %02x:%016jx/%016jx failed) ", 632 bref->methods, 633 bref->check.xxhash64.value, 634 cv64); 635 failed = 1; 636 } else { 637 printf("(meth %02x, xxh=%016jx) ", 638 bref->methods, cv64); 639 } 640 break; 641 case HAMMER2_CHECK_SHA192: 642 SHA256_Init(&hash_ctx); 643 SHA256_Update(&hash_ctx, &media, bytes); 644 SHA256_Final(u.digest, &hash_ctx); 645 u.digest64[2] ^= u.digest64[3]; 646 if (memcmp(u.digest, bref->check.sha192.data, 647 sizeof(bref->check.sha192.data))) { 648 printf("(sha192 failed) "); 649 failed = 1; 650 } else { 651 printf("(meth %02x) ", bref->methods); 652 } 653 break; 654 case HAMMER2_CHECK_FREEMAP: 655 cv = hammer2_icrc32(&media, bytes); 656 if (bref->check.freemap.icrc32 != cv) { 657 printf("(fcrc %02x:%08x/%08x failed) ", 658 bref->methods, 659 bref->check.freemap.icrc32, 660 cv); 661 failed = 1; 662 } else { 663 printf("(meth %02x, fcrc=%08x) ", 664 bref->methods, cv); 665 } 666 break; 667 } 668 } 669 670 if (QuietOpt > 0) { 671 obrace = 0; 672 printf("\n"); 673 goto skip_data; 674 } 675 676 switch(bref->type) { 677 case HAMMER2_BREF_TYPE_EMPTY: 678 obrace = 0; 679 break; 680 case HAMMER2_BREF_TYPE_DIRENT: 681 printf("{\n"); 682 if (bref->embed.dirent.namlen <= sizeof(bref->check.buf)) { 683 tabprintf(tab, "filename \"%*.*s\"\n", 684 bref->embed.dirent.namlen, 685 bref->embed.dirent.namlen, 686 bref->check.buf); 687 } else { 688 tabprintf(tab, "filename \"%*.*s\"\n", 689 bref->embed.dirent.namlen, 690 bref->embed.dirent.namlen, 691 media.buf); 692 } 693 tabprintf(tab, "inum 0x%016jx\n", 694 (uintmax_t)bref->embed.dirent.inum); 695 tabprintf(tab, "type %s\n", 696 hammer2_iptype_to_str(bref->embed.dirent.type)); 697 break; 698 case HAMMER2_BREF_TYPE_INODE: 699 printf("{\n"); 700 namelen = media.ipdata.meta.name_len; 701 if (namelen > HAMMER2_INODE_MAXNAME) 702 namelen = 0; 703 tabprintf(tab, "filename \"%*.*s\"\n", 704 namelen, namelen, media.ipdata.filename); 705 tabprintf(tab, "version %d\n", media.ipdata.meta.version); 706 tabprintf(tab, "pfs_st %d\n", media.ipdata.meta.pfs_subtype); 707 tabprintf(tab, "uflags 0x%08x\n", 708 media.ipdata.meta.uflags); 709 if (media.ipdata.meta.rmajor || media.ipdata.meta.rminor) { 710 tabprintf(tab, "rmajor %d\n", 711 media.ipdata.meta.rmajor); 712 tabprintf(tab, "rminor %d\n", 713 media.ipdata.meta.rminor); 714 } 715 tabprintf(tab, "ctime %s\n", 716 hammer2_time64_to_str(media.ipdata.meta.ctime, &str)); 717 tabprintf(tab, "mtime %s\n", 718 hammer2_time64_to_str(media.ipdata.meta.mtime, &str)); 719 tabprintf(tab, "atime %s\n", 720 hammer2_time64_to_str(media.ipdata.meta.atime, &str)); 721 tabprintf(tab, "btime %s\n", 722 hammer2_time64_to_str(media.ipdata.meta.btime, &str)); 723 tabprintf(tab, "uid %s\n", 724 hammer2_uuid_to_str(&media.ipdata.meta.uid, &str)); 725 tabprintf(tab, "gid %s\n", 726 hammer2_uuid_to_str(&media.ipdata.meta.gid, &str)); 727 tabprintf(tab, "type %s\n", 728 hammer2_iptype_to_str(media.ipdata.meta.type)); 729 tabprintf(tab, "opflgs 0x%02x\n", 730 media.ipdata.meta.op_flags); 731 tabprintf(tab, "capflgs 0x%04x\n", 732 media.ipdata.meta.cap_flags); 733 tabprintf(tab, "mode %-7o\n", 734 media.ipdata.meta.mode); 735 tabprintf(tab, "inum 0x%016jx\n", 736 media.ipdata.meta.inum); 737 tabprintf(tab, "size %ju ", 738 (uintmax_t)media.ipdata.meta.size); 739 if (media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA && 740 media.ipdata.meta.size <= HAMMER2_EMBEDDED_BYTES) 741 printf("(embedded data)\n"); 742 else 743 printf("\n"); 744 tabprintf(tab, "nlinks %ju\n", 745 (uintmax_t)media.ipdata.meta.nlinks); 746 tabprintf(tab, "iparent 0x%016jx\n", 747 (uintmax_t)media.ipdata.meta.iparent); 748 tabprintf(tab, "name_key 0x%016jx\n", 749 (uintmax_t)media.ipdata.meta.name_key); 750 tabprintf(tab, "name_len %u\n", 751 media.ipdata.meta.name_len); 752 tabprintf(tab, "ncopies %u\n", 753 media.ipdata.meta.ncopies); 754 tabprintf(tab, "compalg %u\n", 755 media.ipdata.meta.comp_algo); 756 tabprintf(tab, "target_t %u\n", 757 media.ipdata.meta.target_type); 758 tabprintf(tab, "checkalg %u\n", 759 media.ipdata.meta.check_algo); 760 if ((media.ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) || 761 media.ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) { 762 tabprintf(tab, "pfs_nmas %u\n", 763 media.ipdata.meta.pfs_nmasters); 764 tabprintf(tab, "pfs_type %u (%s)\n", 765 media.ipdata.meta.pfs_type, 766 hammer2_pfstype_to_str(media.ipdata.meta.pfs_type)); 767 tabprintf(tab, "pfs_inum 0x%016jx\n", 768 (uintmax_t)media.ipdata.meta.pfs_inum); 769 tabprintf(tab, "pfs_clid %s\n", 770 hammer2_uuid_to_str(&media.ipdata.meta.pfs_clid, 771 &str)); 772 tabprintf(tab, "pfs_fsid %s\n", 773 hammer2_uuid_to_str(&media.ipdata.meta.pfs_fsid, 774 &str)); 775 tabprintf(tab, "pfs_lsnap_tid 0x%016jx\n", 776 (uintmax_t)media.ipdata.meta.pfs_lsnap_tid); 777 } 778 tabprintf(tab, "data_quota %ju\n", 779 (uintmax_t)media.ipdata.meta.data_quota); 780 tabprintf(tab, "data_count %ju\n", 781 (uintmax_t)bref->embed.stats.data_count); 782 tabprintf(tab, "inode_quota %ju\n", 783 (uintmax_t)media.ipdata.meta.inode_quota); 784 tabprintf(tab, "inode_count %ju\n", 785 (uintmax_t)bref->embed.stats.inode_count); 786 break; 787 case HAMMER2_BREF_TYPE_INDIRECT: 788 printf("{\n"); 789 break; 790 case HAMMER2_BREF_TYPE_DATA: 791 printf("\n"); 792 obrace = 0; 793 break; 794 case HAMMER2_BREF_TYPE_VOLUME: 795 printf("mirror_tid=%016jx freemap_tid=%016jx ", 796 media.voldata.mirror_tid, 797 media.voldata.freemap_tid); 798 printf("{\n"); 799 break; 800 case HAMMER2_BREF_TYPE_FREEMAP: 801 printf("mirror_tid=%016jx freemap_tid=%016jx ", 802 media.voldata.mirror_tid, 803 media.voldata.freemap_tid); 804 printf("{\n"); 805 break; 806 case HAMMER2_BREF_TYPE_FREEMAP_LEAF: 807 printf("{\n"); 808 tmp = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; 809 tmp &= HAMMER2_SEGMASK; 810 tmp /= HAMMER2_PBUFSIZE; 811 assert(tmp >= HAMMER2_ZONE_FREEMAP_00); 812 assert(tmp < HAMMER2_ZONE_FREEMAP_END); 813 tmp -= HAMMER2_ZONE_FREEMAP_00; 814 tmp /= HAMMER2_ZONE_FREEMAP_INC; 815 tabprintf(tab, "rotation=%d\n", (int)tmp); 816 817 for (i = 0; i < HAMMER2_FREEMAP_COUNT; ++i) { 818 hammer2_off_t data_off = bref->key + 819 i * HAMMER2_FREEMAP_LEVEL0_SIZE; 820 #if HAMMER2_BMAP_ELEMENTS != 8 821 #error "cmd_debug.c: HAMMER2_BMAP_ELEMENTS expected to be 8" 822 #endif 823 tabprintf(tab + 4, "%016jx %04d.%04x (avail=%7d) " 824 "%016jx %016jx %016jx %016jx " 825 "%016jx %016jx %016jx %016jx\n", 826 data_off, i, media.bmdata[i].class, 827 media.bmdata[i].avail, 828 media.bmdata[i].bitmapq[0], 829 media.bmdata[i].bitmapq[1], 830 media.bmdata[i].bitmapq[2], 831 media.bmdata[i].bitmapq[3], 832 media.bmdata[i].bitmapq[4], 833 media.bmdata[i].bitmapq[5], 834 media.bmdata[i].bitmapq[6], 835 media.bmdata[i].bitmapq[7]); 836 } 837 tabprintf(tab, "}\n"); 838 break; 839 case HAMMER2_BREF_TYPE_FREEMAP_NODE: 840 printf("{\n"); 841 tmp = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; 842 tmp &= HAMMER2_SEGMASK; 843 tmp /= HAMMER2_PBUFSIZE; 844 assert(tmp >= HAMMER2_ZONE_FREEMAP_00); 845 assert(tmp < HAMMER2_ZONE_FREEMAP_END); 846 tmp -= HAMMER2_ZONE_FREEMAP_00; 847 tmp /= HAMMER2_ZONE_FREEMAP_INC; 848 tabprintf(tab, "rotation=%d\n", (int)tmp); 849 break; 850 default: 851 printf("\n"); 852 obrace = 0; 853 break; 854 } 855 if (str) 856 free(str); 857 858 skip_data: 859 /* 860 * Update statistics. 861 */ 862 switch(bref->type) { 863 case HAMMER2_BREF_TYPE_FREEMAP_LEAF: 864 for (i = 0; i < HAMMER2_FREEMAP_COUNT; ++i) { 865 hammer2_off_t data_off = bref->key + 866 i * HAMMER2_FREEMAP_LEVEL0_SIZE; 867 if (data_off >= voldata->aux_end && 868 data_off < voldata->volu_size) { 869 int j; 870 for (j = 0; j < 4; ++j) 871 CountBlocks(&media.bmdata[i], j, 872 &TotalAccum16[j], 873 &TotalAccum64[j]); 874 } else 875 TotalUnavail += HAMMER2_FREEMAP_LEVEL0_SIZE; 876 } 877 TotalFreemap += HAMMER2_FREEMAP_LEVEL1_SIZE; 878 break; 879 default: 880 break; 881 } 882 883 /* 884 * Recurse if norecurse == 0. If the CRC failed, pass norecurse = 1. 885 * That is, if an indirect or inode fails we still try to list its 886 * direct children to help with debugging, but go no further than 887 * that because they are probably garbage. 888 */ 889 for (i = 0; norecurse == 0 && i < bcount; ++i) { 890 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) { 891 show_bref(voldata, fd, tab, i, &bscan[i], failed); 892 } 893 } 894 tab -= show_tab; 895 if (obrace) { 896 if (bref->type == HAMMER2_BREF_TYPE_INODE) 897 tabprintf(tab, "} (%s.%d, \"%*.*s\")\n", 898 type_str, bi, namelen, namelen, 899 media.ipdata.filename); 900 else 901 tabprintf(tab, "} (%s.%d)\n", type_str, bi); 902 } 903 } 904 905 static 906 void 907 CountBlocks(hammer2_bmap_data_t *bmap, int value, 908 hammer2_off_t *accum16, hammer2_off_t *accum64) 909 { 910 int i, j, bits; 911 hammer2_bitmap_t value16, value64; 912 913 bits = (int)sizeof(hammer2_bitmap_t) * 8; 914 assert(bits == 64); 915 916 value16 = value; 917 assert(value16 < 4); 918 value64 = (value16 << 6) | (value16 << 4) | (value16 << 2) | value16; 919 assert(value64 < 256); 920 921 for (i = 0; i < HAMMER2_BMAP_ELEMENTS; ++i) { 922 hammer2_bitmap_t bm = bmap->bitmapq[i]; 923 hammer2_bitmap_t bm_save = bm; 924 hammer2_bitmap_t mask; 925 926 mask = 0x03; /* 2 bits per 16KB */ 927 for (j = 0; j < bits; j += 2) { 928 if ((bm & mask) == value16) 929 *accum16 += 16384; 930 bm >>= 2; 931 } 932 933 bm = bm_save; 934 mask = 0xFF; /* 8 bits per 64KB chunk */ 935 for (j = 0; j < bits; j += 8) { 936 if ((bm & mask) == value64) 937 *accum64 += 65536; 938 bm >>= 8; 939 } 940 } 941 } 942 943 int 944 cmd_hash(int ac, const char **av) 945 { 946 int i; 947 948 for (i = 0; i < ac; ++i) { 949 printf("%016jx %s\n", 950 dirhash((const unsigned char*)av[i], strlen(av[i])), 951 av[i]); 952 } 953 return(0); 954 } 955 956 int 957 cmd_dhash(int ac, const char **av) 958 { 959 char buf[1024]; /* 1K extended directory record */ 960 uint64_t hash; 961 int i; 962 963 for (i = 0; i < ac; ++i) { 964 bzero(buf, sizeof(buf)); 965 snprintf(buf, sizeof(buf), "%s", av[i]); 966 hash = XXH64(buf, sizeof(buf), XXH_HAMMER2_SEED); 967 printf("%016jx %s\n", hash, av[i]); 968 } 969 return(0); 970 } 971 972 int 973 cmd_dumpchain(const char *path, u_int flags) 974 { 975 int dummy = (int)flags; 976 int fd; 977 978 fd = open(path, O_RDONLY); 979 if (fd >= 0) { 980 ioctl(fd, HAMMER2IOC_DEBUG_DUMP, &dummy); 981 close(fd); 982 } else { 983 fprintf(stderr, "unable to open %s\n", path); 984 } 985 return 0; 986 } 987 988 989 static 990 void 991 tabprintf(int tab, const char *ctl, ...) 992 { 993 va_list va; 994 995 printf("%*.*s", tab, tab, ""); 996 va_start(va, ctl); 997 vprintf(ctl, va); 998 va_end(va); 999 } 1000