1 /* 2 * Copyright (c) 2019 Tomohiro Kusumi <tkusumi@netbsd.org> 3 * Copyright (c) 2019 The DragonFly Project 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to The DragonFly Project 7 * by Matthew Dillon <dillon@dragonflybsd.org> 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in 17 * the documentation and/or other materials provided with the 18 * distribution. 19 * 3. Neither the name of The DragonFly Project nor the names of its 20 * contributors may be used to endorse or promote products derived 21 * from this software without specific, prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 31 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #include <sys/types.h> 38 #include <sys/stat.h> 39 #include <sys/tree.h> 40 #include <sys/queue.h> 41 #include <sys/ttycom.h> 42 #include <sys/diskslice.h> 43 #include <unistd.h> 44 #include <fcntl.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <stdarg.h> 48 #include <stdbool.h> 49 #include <stdint.h> 50 #include <string.h> 51 #include <assert.h> 52 53 #ifdef HAMMER2_USE_OPENSSL 54 #include <openssl/sha.h> 55 #endif 56 57 #include <vfs/hammer2/hammer2_disk.h> 58 #include <vfs/hammer2/hammer2_xxhash.h> 59 60 #include "hammer2_subs.h" 61 #include "fsck_hammer2.h" 62 63 struct blockref_msg { 64 TAILQ_ENTRY(blockref_msg) entry; 65 hammer2_blockref_t bref; 66 void *msg; 67 }; 68 69 TAILQ_HEAD(blockref_list, blockref_msg); 70 71 struct blockref_entry { 72 RB_ENTRY(blockref_entry) entry; 73 hammer2_off_t data_off; 74 struct blockref_list head; 75 }; 76 77 static int 78 blockref_cmp(struct blockref_entry *b1, struct blockref_entry *b2) 79 { 80 if (b1->data_off < b2->data_off) 81 return -1; 82 if (b1->data_off > b2->data_off) 83 return 1; 84 return 0; 85 } 86 87 RB_HEAD(blockref_tree, blockref_entry); 88 RB_PROTOTYPE(blockref_tree, blockref_entry, entry, blockref_cmp); 89 RB_GENERATE(blockref_tree, blockref_entry, entry, blockref_cmp); 90 91 typedef struct { 92 struct blockref_tree root; 93 uint8_t type; /* HAMMER2_BREF_TYPE_VOLUME or FREEMAP */ 94 uint64_t total_blockref; 95 uint64_t total_empty; 96 uint64_t total_bytes; 97 union { 98 /* use volume or freemap depending on type value */ 99 struct { 100 uint64_t total_inode; 101 uint64_t total_indirect; 102 uint64_t total_data; 103 uint64_t total_dirent; 104 } volume; 105 struct { 106 uint64_t total_freemap_node; 107 uint64_t total_freemap_leaf; 108 } freemap; 109 }; 110 } blockref_stats_t; 111 112 typedef struct { 113 uint64_t total_blockref; 114 uint64_t total_empty; 115 uint64_t total_bytes; 116 struct { 117 uint64_t total_inode; 118 uint64_t total_indirect; 119 uint64_t total_data; 120 uint64_t total_dirent; 121 } volume; 122 struct { 123 uint64_t total_freemap_node; 124 uint64_t total_freemap_leaf; 125 } freemap; 126 long count; 127 } delta_stats_t; 128 129 static void print_blockref_entry(struct blockref_tree *); 130 static void init_blockref_stats(blockref_stats_t *, uint8_t); 131 static void cleanup_blockref_stats(blockref_stats_t *); 132 static void init_delta_root(struct blockref_tree *); 133 static void cleanup_delta_root(struct blockref_tree *); 134 static void print_blockref_stats(const blockref_stats_t *, bool); 135 static int verify_volume_header(const hammer2_volume_data_t *); 136 static int read_media(const hammer2_blockref_t *, hammer2_media_data_t *, 137 size_t *); 138 static int verify_blockref(const hammer2_volume_data_t *, 139 const hammer2_blockref_t *, bool, blockref_stats_t *, 140 struct blockref_tree *, delta_stats_t *, int, int); 141 static void print_pfs(const hammer2_inode_data_t *); 142 static char *get_inode_filename(const hammer2_inode_data_t *); 143 static int init_pfs_blockref(const hammer2_volume_data_t *, 144 const hammer2_blockref_t *, struct blockref_list *); 145 static void cleanup_pfs_blockref(struct blockref_list *); 146 static void print_media(FILE *, int, const hammer2_blockref_t *, 147 const hammer2_media_data_t *, size_t); 148 149 static int best_zone = -1; 150 151 #define TAB 8 152 153 static void 154 tfprintf(FILE *fp, int tab, const char *ctl, ...) 155 { 156 va_list va; 157 int ret; 158 159 ret = fprintf(fp, "%*s", tab * TAB, ""); 160 if (ret < 0) 161 return; 162 163 va_start(va, ctl); 164 vfprintf(fp, ctl, va); 165 va_end(va); 166 } 167 168 static void 169 tsnprintf(char *str, size_t siz, int tab, const char *ctl, ...) 170 { 171 va_list va; 172 int ret; 173 174 ret = snprintf(str, siz, "%*s", tab * TAB, ""); 175 if (ret < 0 || ret >= (int)siz) 176 return; 177 178 va_start(va, ctl); 179 vsnprintf(str + ret, siz - ret, ctl, va); 180 va_end(va); 181 } 182 183 static void 184 tprintf_zone(int tab, int i, const hammer2_blockref_t *bref) 185 { 186 tfprintf(stdout, tab, "zone.%d %016jx%s\n", 187 i, (uintmax_t)bref->data_off, 188 (!ScanBest && i == best_zone) ? " (best)" : ""); 189 } 190 191 static int 192 init_root_blockref(int i, uint8_t type, hammer2_blockref_t *bref) 193 { 194 hammer2_off_t off; 195 196 assert(type == HAMMER2_BREF_TYPE_EMPTY || 197 type == HAMMER2_BREF_TYPE_VOLUME || 198 type == HAMMER2_BREF_TYPE_FREEMAP); 199 memset(bref, 0, sizeof(*bref)); 200 bref->type = type; 201 bref->data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX; 202 off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; 203 204 return lseek(hammer2_get_root_volume_fd(), 205 off - hammer2_get_root_volume_offset(), SEEK_SET); 206 } 207 208 static int 209 find_best_zone(void) 210 { 211 hammer2_blockref_t best; 212 int i, best_i = -1; 213 214 memset(&best, 0, sizeof(best)); 215 216 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) { 217 hammer2_volume_data_t voldata; 218 hammer2_blockref_t broot; 219 ssize_t ret; 220 221 if (i * HAMMER2_ZONE_BYTES64 >= 222 hammer2_get_root_volume_size()) 223 break; 224 init_root_blockref(i, HAMMER2_BREF_TYPE_EMPTY, &broot); 225 ret = read(hammer2_get_root_volume_fd(), &voldata, 226 HAMMER2_VOLUME_BYTES); 227 if (ret == HAMMER2_VOLUME_BYTES) { 228 if ((voldata.magic != HAMMER2_VOLUME_ID_HBO) && 229 (voldata.magic != HAMMER2_VOLUME_ID_ABO)) 230 continue; 231 broot.mirror_tid = voldata.mirror_tid; 232 if (best_i < 0 || best.mirror_tid < broot.mirror_tid) { 233 best_i = i; 234 best = broot; 235 } 236 } else if (ret == -1) { 237 perror("read"); 238 return -1; 239 } else { 240 tfprintf(stderr, 1, "Failed to read volume header\n"); 241 return -1; 242 } 243 } 244 245 return best_i; 246 } 247 248 static int 249 test_volume_header(void) 250 { 251 bool failed = false; 252 int i; 253 254 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) { 255 hammer2_volume_data_t voldata; 256 hammer2_blockref_t broot; 257 ssize_t ret; 258 259 if (ScanBest && i != best_zone) 260 continue; 261 if (i * HAMMER2_ZONE_BYTES64 >= 262 hammer2_get_root_volume_size()) { 263 tfprintf(stderr, 0, "zone.%d exceeds volume size\n", i); 264 break; 265 } 266 init_root_blockref(i, HAMMER2_BREF_TYPE_EMPTY, &broot); 267 ret = read(hammer2_get_root_volume_fd(), &voldata, 268 HAMMER2_VOLUME_BYTES); 269 if (ret == HAMMER2_VOLUME_BYTES) { 270 tprintf_zone(0, i, &broot); 271 if (verify_volume_header(&voldata) == -1) 272 failed = true; 273 } else if (ret == -1) { 274 perror("read"); 275 return -1; 276 } else { 277 tfprintf(stderr, 1, "Failed to read volume header\n"); 278 return -1; 279 } 280 } 281 282 return failed ? -1 : 0; 283 } 284 285 static int 286 test_blockref(uint8_t type) 287 { 288 struct blockref_tree droot; 289 bool failed = false; 290 int i; 291 292 init_delta_root(&droot); 293 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) { 294 hammer2_volume_data_t voldata; 295 hammer2_blockref_t broot; 296 ssize_t ret; 297 298 if (ScanBest && i != best_zone) 299 continue; 300 if (i * HAMMER2_ZONE_BYTES64 >= 301 hammer2_get_root_volume_size()) { 302 tfprintf(stderr, 0, "zone.%d exceeds volume size\n", i); 303 break; 304 } 305 init_root_blockref(i, type, &broot); 306 ret = read(hammer2_get_root_volume_fd(), &voldata, 307 HAMMER2_VOLUME_BYTES); 308 if (ret == HAMMER2_VOLUME_BYTES) { 309 blockref_stats_t bstats; 310 init_blockref_stats(&bstats, type); 311 delta_stats_t ds; 312 memset(&ds, 0, sizeof(ds)); 313 tprintf_zone(0, i, &broot); 314 if (verify_blockref(&voldata, &broot, false, &bstats, 315 &droot, &ds, 0, 0) == -1) 316 failed = true; 317 print_blockref_stats(&bstats, true); 318 print_blockref_entry(&bstats.root); 319 cleanup_blockref_stats(&bstats); 320 } else if (ret == -1) { 321 perror("read"); 322 failed = true; 323 goto end; 324 } else { 325 tfprintf(stderr, 1, "Failed to read volume header\n"); 326 failed = true; 327 goto end; 328 } 329 } 330 end: 331 cleanup_delta_root(&droot); 332 return failed ? -1 : 0; 333 } 334 335 static int 336 test_pfs_blockref(void) 337 { 338 struct blockref_tree droot; 339 uint8_t type = HAMMER2_BREF_TYPE_VOLUME; 340 bool failed = false; 341 int i; 342 343 init_delta_root(&droot); 344 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) { 345 hammer2_volume_data_t voldata; 346 hammer2_blockref_t broot; 347 ssize_t ret; 348 349 if (ScanBest && i != best_zone) 350 continue; 351 if (i * HAMMER2_ZONE_BYTES64 >= 352 hammer2_get_root_volume_size()) { 353 tfprintf(stderr, 0, "zone.%d exceeds volume size\n", i); 354 break; 355 } 356 init_root_blockref(i, type, &broot); 357 ret = read(hammer2_get_root_volume_fd(), &voldata, 358 HAMMER2_VOLUME_BYTES); 359 if (ret == HAMMER2_VOLUME_BYTES) { 360 struct blockref_list blist; 361 struct blockref_msg *p; 362 int count = 0; 363 364 tprintf_zone(0, i, &broot); 365 TAILQ_INIT(&blist); 366 if (init_pfs_blockref(&voldata, &broot, &blist) == -1) { 367 tfprintf(stderr, 1, "Failed to read PFS " 368 "blockref\n"); 369 failed = true; 370 continue; 371 } 372 if (TAILQ_EMPTY(&blist)) { 373 tfprintf(stderr, 1, "Failed to find PFS " 374 "blockref\n"); 375 failed = true; 376 continue; 377 } 378 TAILQ_FOREACH(p, &blist, entry) { 379 blockref_stats_t bstats; 380 bool found = false; 381 char *f = get_inode_filename(p->msg); 382 if (NumPFSNames) { 383 int j; 384 for (j = 0; j < NumPFSNames; j++) 385 if (!strcmp(PFSNames[j], f)) 386 found = true; 387 } else 388 found = true; 389 if (!found) { 390 free(f); 391 continue; 392 } 393 count++; 394 if (PrintPFS) { 395 print_pfs(p->msg); 396 free(f); 397 continue; 398 } 399 tfprintf(stdout, 1, "%s\n", f); 400 free(f); 401 init_blockref_stats(&bstats, type); 402 delta_stats_t ds; 403 memset(&ds, 0, sizeof(ds)); 404 if (verify_blockref(&voldata, &p->bref, false, 405 &bstats, &droot, &ds, 0, 0) == -1) 406 failed = true; 407 print_blockref_stats(&bstats, true); 408 print_blockref_entry(&bstats.root); 409 cleanup_blockref_stats(&bstats); 410 } 411 cleanup_pfs_blockref(&blist); 412 if (NumPFSNames && !count) { 413 tfprintf(stderr, 1, "PFS not found\n"); 414 failed = true; 415 } 416 } else if (ret == -1) { 417 perror("read"); 418 failed = true; 419 goto end; 420 } else { 421 tfprintf(stderr, 1, "Failed to read volume header\n"); 422 failed = true; 423 goto end; 424 } 425 } 426 end: 427 cleanup_delta_root(&droot); 428 return failed ? -1 : 0; 429 } 430 431 static int 432 charsperline(void) 433 { 434 int columns; 435 char *cp; 436 struct winsize ws; 437 438 columns = 0; 439 if (ioctl(0, TIOCGWINSZ, &ws) != -1) 440 columns = ws.ws_col; 441 if (columns == 0 && (cp = getenv("COLUMNS"))) 442 columns = atoi(cp); 443 if (columns == 0) 444 columns = 80; /* last resort */ 445 446 return columns; 447 } 448 449 static void 450 cleanup_blockref_msg(struct blockref_list *head) 451 { 452 struct blockref_msg *p; 453 454 while ((p = TAILQ_FIRST(head)) != NULL) { 455 TAILQ_REMOVE(head, p, entry); 456 free(p->msg); 457 free(p); 458 } 459 assert(TAILQ_EMPTY(head)); 460 } 461 462 static void 463 cleanup_blockref_entry(struct blockref_tree *root) 464 { 465 struct blockref_entry *e; 466 467 while ((e = RB_ROOT(root)) != NULL) { 468 RB_REMOVE(blockref_tree, root, e); 469 cleanup_blockref_msg(&e->head); 470 free(e); 471 } 472 assert(RB_EMPTY(root)); 473 } 474 475 static void 476 add_blockref_msg(struct blockref_list *head, const hammer2_blockref_t *bref, 477 const void *msg, size_t siz) 478 { 479 struct blockref_msg *m; 480 void *p; 481 482 m = calloc(1, sizeof(*m)); 483 assert(m); 484 m->bref = *bref; 485 p = calloc(1, siz); 486 assert(p); 487 memcpy(p, msg, siz); 488 m->msg = p; 489 490 TAILQ_INSERT_TAIL(head, m, entry); 491 } 492 493 static void 494 add_blockref_entry(struct blockref_tree *root, const hammer2_blockref_t *bref, 495 const void *msg, size_t siz) 496 { 497 struct blockref_entry *e, bref_find; 498 499 memset(&bref_find, 0, sizeof(bref_find)); 500 bref_find.data_off = bref->data_off; 501 e = RB_FIND(blockref_tree, root, &bref_find); 502 if (!e) { 503 e = calloc(1, sizeof(*e)); 504 assert(e); 505 TAILQ_INIT(&e->head); 506 e->data_off = bref->data_off; 507 } 508 509 add_blockref_msg(&e->head, bref, msg, siz); 510 511 RB_INSERT(blockref_tree, root, e); 512 } 513 514 static void 515 __print_blockref(FILE *fp, int tab, const hammer2_blockref_t *bref, 516 const char *msg) 517 { 518 tfprintf(fp, tab, "%016jx %-12s %016jx/%-2d%s%s\n", 519 (uintmax_t)bref->data_off, 520 hammer2_breftype_to_str(bref->type), 521 (uintmax_t)bref->key, 522 bref->keybits, 523 msg ? " " : "", 524 msg ? msg : ""); 525 } 526 527 static void 528 print_blockref(FILE *fp, const hammer2_blockref_t *bref, const char *msg) 529 { 530 __print_blockref(fp, 1, bref, msg); 531 } 532 533 static void 534 print_blockref_debug(FILE *fp, int depth, int index, 535 const hammer2_blockref_t *bref, const char *msg) 536 { 537 if (DebugOpt > 1) { 538 char buf[256]; 539 int i; 540 541 memset(buf, 0, sizeof(buf)); 542 for (i = 0; i < depth * 2; i++) 543 strlcat(buf, " ", sizeof(buf)); 544 tfprintf(fp, 1, buf); 545 fprintf(fp, "%-2d %-3d ", depth, index); 546 __print_blockref(fp, 0, bref, msg); 547 } else if (DebugOpt > 0) 548 print_blockref(fp, bref, msg); 549 } 550 551 static void 552 print_blockref_msg(const struct blockref_list *head) 553 { 554 struct blockref_msg *m; 555 556 TAILQ_FOREACH(m, head, entry) { 557 hammer2_blockref_t *bref = &m->bref; 558 print_blockref(stderr, bref, m->msg); 559 if (VerboseOpt > 0) { 560 hammer2_media_data_t media; 561 size_t bytes; 562 if (!read_media(bref, &media, &bytes)) 563 print_media(stderr, 2, bref, &media, bytes); 564 else 565 tfprintf(stderr, 2, "Failed to read media\n"); 566 } 567 } 568 } 569 570 static void 571 print_blockref_entry(struct blockref_tree *root) 572 { 573 struct blockref_entry *e; 574 575 RB_FOREACH(e, blockref_tree, root) 576 print_blockref_msg(&e->head); 577 } 578 579 static void 580 init_blockref_stats(blockref_stats_t *bstats, uint8_t type) 581 { 582 memset(bstats, 0, sizeof(*bstats)); 583 RB_INIT(&bstats->root); 584 bstats->type = type; 585 } 586 587 static void 588 cleanup_blockref_stats(blockref_stats_t *bstats) 589 { 590 cleanup_blockref_entry(&bstats->root); 591 } 592 593 static void 594 init_delta_root(struct blockref_tree *droot) 595 { 596 RB_INIT(droot); 597 } 598 599 static void 600 cleanup_delta_root(struct blockref_tree *droot) 601 { 602 cleanup_blockref_entry(droot); 603 } 604 605 static void 606 print_blockref_stats(const blockref_stats_t *bstats, bool newline) 607 { 608 size_t siz = charsperline(); 609 char *buf = calloc(1, siz); 610 char emptybuf[128]; 611 612 assert(buf); 613 614 if (CountEmpty) 615 snprintf(emptybuf, sizeof(emptybuf), ", %ju empty", 616 (uintmax_t)bstats->total_empty); 617 else 618 strlcpy(emptybuf, "", sizeof(emptybuf)); 619 620 switch (bstats->type) { 621 case HAMMER2_BREF_TYPE_VOLUME: 622 tsnprintf(buf, siz, 1, "%ju blockref (%ju inode, %ju indirect, " 623 "%ju data, %ju dirent%s), %s", 624 (uintmax_t)bstats->total_blockref, 625 (uintmax_t)bstats->volume.total_inode, 626 (uintmax_t)bstats->volume.total_indirect, 627 (uintmax_t)bstats->volume.total_data, 628 (uintmax_t)bstats->volume.total_dirent, 629 emptybuf, 630 sizetostr(bstats->total_bytes)); 631 break; 632 case HAMMER2_BREF_TYPE_FREEMAP: 633 tsnprintf(buf, siz, 1, "%ju blockref (%ju node, %ju leaf%s), " 634 "%s", 635 (uintmax_t)bstats->total_blockref, 636 (uintmax_t)bstats->freemap.total_freemap_node, 637 (uintmax_t)bstats->freemap.total_freemap_leaf, 638 emptybuf, 639 sizetostr(bstats->total_bytes)); 640 break; 641 default: 642 assert(0); 643 break; 644 } 645 646 if (newline) { 647 printf("%s\n", buf); 648 } else { 649 printf("%s\r", buf); 650 fflush(stdout); 651 } 652 free(buf); 653 } 654 655 static int 656 verify_volume_header(const hammer2_volume_data_t *voldata) 657 { 658 hammer2_crc32_t crc0, crc1; 659 const char *p = (const char*)voldata; 660 661 if ((voldata->magic != HAMMER2_VOLUME_ID_HBO) && 662 (voldata->magic != HAMMER2_VOLUME_ID_ABO)) { 663 tfprintf(stderr, 1, "Bad magic %jX\n", voldata->magic); 664 return -1; 665 } 666 667 if (voldata->magic == HAMMER2_VOLUME_ID_ABO) 668 tfprintf(stderr, 1, "Reverse endian\n"); 669 670 crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT0]; 671 crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC0_OFF, 672 HAMMER2_VOLUME_ICRC0_SIZE); 673 if (crc0 != crc1) { 674 tfprintf(stderr, 1, "Bad HAMMER2_VOL_ICRC_SECT0 CRC\n"); 675 return -1; 676 } 677 678 crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT1]; 679 crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC1_OFF, 680 HAMMER2_VOLUME_ICRC1_SIZE); 681 if (crc0 != crc1) { 682 tfprintf(stderr, 1, "Bad HAMMER2_VOL_ICRC_SECT1 CRC\n"); 683 return -1; 684 } 685 686 crc0 = voldata->icrc_volheader; 687 crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRCVH_OFF, 688 HAMMER2_VOLUME_ICRCVH_SIZE); 689 if (crc0 != crc1) { 690 tfprintf(stderr, 1, "Bad volume header CRC\n"); 691 return -1; 692 } 693 694 return 0; 695 } 696 697 static int 698 read_media(const hammer2_blockref_t *bref, hammer2_media_data_t *media, 699 size_t *media_bytes) 700 { 701 hammer2_off_t io_off, io_base; 702 size_t bytes, io_bytes, boff; 703 int fd; 704 705 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX); 706 if (bytes) 707 bytes = (size_t)1 << bytes; 708 if (media_bytes) 709 *media_bytes = bytes; 710 711 if (!bytes) 712 return 0; 713 714 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; 715 io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1); 716 boff = io_off - io_base; 717 718 io_bytes = HAMMER2_LBUFSIZE; 719 while (io_bytes + boff < bytes) 720 io_bytes <<= 1; 721 722 if (io_bytes > sizeof(*media)) 723 return -1; 724 fd = hammer2_get_volume_fd(io_off); 725 if (lseek(fd, io_base - hammer2_get_volume_offset(io_base), SEEK_SET) 726 == -1) 727 return -2; 728 if (read(fd, media, io_bytes) != (ssize_t)io_bytes) 729 return -2; 730 if (boff) 731 memmove(media, (char *)media + boff, bytes); 732 733 return 0; 734 } 735 736 static void 737 load_delta_stats(blockref_stats_t *bstats, const delta_stats_t *dstats) 738 { 739 bstats->total_blockref += dstats->total_blockref; 740 bstats->total_empty += dstats->total_empty; 741 bstats->total_bytes += dstats->total_bytes; 742 743 switch (bstats->type) { 744 case HAMMER2_BREF_TYPE_VOLUME: 745 bstats->volume.total_inode += dstats->volume.total_inode; 746 bstats->volume.total_indirect += dstats->volume.total_indirect; 747 bstats->volume.total_data += dstats->volume.total_data; 748 bstats->volume.total_dirent += dstats->volume.total_dirent; 749 break; 750 case HAMMER2_BREF_TYPE_FREEMAP: 751 bstats->freemap.total_freemap_node += 752 dstats->freemap.total_freemap_node; 753 bstats->freemap.total_freemap_leaf += 754 dstats->freemap.total_freemap_leaf; 755 break; 756 default: 757 assert(0); 758 break; 759 } 760 } 761 762 static void 763 accumulate_delta_stats(delta_stats_t *dst, const delta_stats_t *src) 764 { 765 dst->total_blockref += src->total_blockref; 766 dst->total_empty += src->total_empty; 767 dst->total_bytes += src->total_bytes; 768 769 dst->volume.total_inode += src->volume.total_inode; 770 dst->volume.total_indirect += src->volume.total_indirect; 771 dst->volume.total_data += src->volume.total_data; 772 dst->volume.total_dirent += src->volume.total_dirent; 773 774 dst->freemap.total_freemap_node += src->freemap.total_freemap_node; 775 dst->freemap.total_freemap_leaf += src->freemap.total_freemap_leaf; 776 777 dst->count += src->count; 778 } 779 780 static int 781 verify_blockref(const hammer2_volume_data_t *voldata, 782 const hammer2_blockref_t *bref, bool norecurse, blockref_stats_t *bstats, 783 struct blockref_tree *droot, delta_stats_t *dstats, int depth, int index) 784 { 785 hammer2_media_data_t media; 786 hammer2_blockref_t *bscan; 787 int i, bcount; 788 bool failed = false; 789 size_t bytes; 790 uint32_t cv; 791 uint64_t cv64; 792 char msg[256]; 793 #ifdef HAMMER2_USE_OPENSSL 794 SHA256_CTX hash_ctx; 795 union { 796 uint8_t digest[SHA256_DIGEST_LENGTH]; 797 uint64_t digest64[SHA256_DIGEST_LENGTH/8]; 798 } u; 799 #endif 800 /* only for DebugOpt > 1 */ 801 if (DebugOpt > 1) 802 print_blockref_debug(stdout, depth, index, bref, NULL); 803 804 if (bref->data_off) { 805 struct blockref_entry *e, bref_find; 806 memset(&bref_find, 0, sizeof(bref_find)); 807 bref_find.data_off = bref->data_off; 808 e = RB_FIND(blockref_tree, droot, &bref_find); 809 if (e) { 810 struct blockref_msg *m; 811 TAILQ_FOREACH(m, &e->head, entry) { 812 delta_stats_t *ds = m->msg; 813 if (!memcmp(&m->bref, bref, sizeof(*bref))) { 814 /* delta contains cached delta */ 815 accumulate_delta_stats(dstats, ds); 816 load_delta_stats(bstats, ds); 817 print_blockref_debug(stdout, depth, 818 index, &m->bref, "cache-hit"); 819 return 0; 820 } 821 } 822 } 823 } 824 825 bstats->total_blockref++; 826 dstats->total_blockref++; 827 828 switch (bref->type) { 829 case HAMMER2_BREF_TYPE_EMPTY: 830 if (CountEmpty) { 831 bstats->total_empty++; 832 dstats->total_empty++; 833 } else { 834 bstats->total_blockref--; 835 dstats->total_blockref--; 836 } 837 break; 838 case HAMMER2_BREF_TYPE_INODE: 839 bstats->volume.total_inode++; 840 dstats->volume.total_inode++; 841 break; 842 case HAMMER2_BREF_TYPE_INDIRECT: 843 bstats->volume.total_indirect++; 844 dstats->volume.total_indirect++; 845 break; 846 case HAMMER2_BREF_TYPE_DATA: 847 bstats->volume.total_data++; 848 dstats->volume.total_data++; 849 break; 850 case HAMMER2_BREF_TYPE_DIRENT: 851 bstats->volume.total_dirent++; 852 dstats->volume.total_dirent++; 853 break; 854 case HAMMER2_BREF_TYPE_FREEMAP_NODE: 855 bstats->freemap.total_freemap_node++; 856 dstats->freemap.total_freemap_node++; 857 break; 858 case HAMMER2_BREF_TYPE_FREEMAP_LEAF: 859 bstats->freemap.total_freemap_leaf++; 860 dstats->freemap.total_freemap_leaf++; 861 break; 862 case HAMMER2_BREF_TYPE_VOLUME: 863 bstats->total_blockref--; 864 dstats->total_blockref--; 865 break; 866 case HAMMER2_BREF_TYPE_FREEMAP: 867 bstats->total_blockref--; 868 dstats->total_blockref--; 869 break; 870 default: 871 snprintf(msg, sizeof(msg), "Invalid blockref type %d", 872 bref->type); 873 add_blockref_entry(&bstats->root, bref, msg, strlen(msg) + 1); 874 print_blockref_debug(stdout, depth, index, bref, msg); 875 failed = true; 876 break; 877 } 878 879 switch (read_media(bref, &media, &bytes)) { 880 case -1: 881 strlcpy(msg, "Bad I/O bytes", sizeof(msg)); 882 add_blockref_entry(&bstats->root, bref, msg, strlen(msg) + 1); 883 print_blockref_debug(stdout, depth, index, bref, msg); 884 return -1; 885 case -2: 886 strlcpy(msg, "Failed to read media", sizeof(msg)); 887 add_blockref_entry(&bstats->root, bref, msg, strlen(msg) + 1); 888 print_blockref_debug(stdout, depth, index, bref, msg); 889 return -1; 890 default: 891 break; 892 } 893 894 if (bref->type != HAMMER2_BREF_TYPE_VOLUME && 895 bref->type != HAMMER2_BREF_TYPE_FREEMAP) { 896 bstats->total_bytes += bytes; 897 dstats->total_bytes += bytes; 898 } 899 900 if (!CountEmpty && bref->type == HAMMER2_BREF_TYPE_EMPTY) { 901 assert(bytes == 0); 902 bstats->total_bytes -= bytes; 903 dstats->total_bytes -= bytes; 904 } 905 906 if (!DebugOpt && QuietOpt <= 0 && (bstats->total_blockref % 100) == 0) 907 print_blockref_stats(bstats, false); 908 909 if (!bytes) 910 goto end; 911 912 switch (HAMMER2_DEC_CHECK(bref->methods)) { 913 case HAMMER2_CHECK_ISCSI32: 914 cv = hammer2_icrc32(&media, bytes); 915 if (bref->check.iscsi32.value != cv) { 916 strlcpy(msg, "Bad HAMMER2_CHECK_ISCSI32", sizeof(msg)); 917 add_blockref_entry(&bstats->root, bref, msg, 918 strlen(msg) + 1); 919 print_blockref_debug(stdout, depth, index, bref, msg); 920 failed = true; 921 } 922 break; 923 case HAMMER2_CHECK_XXHASH64: 924 cv64 = XXH64(&media, bytes, XXH_HAMMER2_SEED); 925 if (bref->check.xxhash64.value != cv64) { 926 strlcpy(msg, "Bad HAMMER2_CHECK_XXHASH64", sizeof(msg)); 927 add_blockref_entry(&bstats->root, bref, msg, 928 strlen(msg) + 1); 929 print_blockref_debug(stdout, depth, index, bref, msg); 930 failed = true; 931 } 932 break; 933 case HAMMER2_CHECK_SHA192: 934 #ifdef HAMMER2_USE_OPENSSL 935 SHA256_Init(&hash_ctx); 936 SHA256_Update(&hash_ctx, &media, bytes); 937 SHA256_Final(u.digest, &hash_ctx); 938 u.digest64[2] ^= u.digest64[3]; 939 if (memcmp(u.digest, bref->check.sha192.data, 940 sizeof(bref->check.sha192.data))) { 941 strlcpy(msg, "Bad HAMMER2_CHECK_SHA192", sizeof(msg)); 942 add_blockref_entry(&bstats->root, bref, msg, 943 strlen(msg) + 1); 944 print_blockref_debug(stdout, depth, index, bref, msg); 945 failed = true; 946 } 947 #endif 948 break; 949 case HAMMER2_CHECK_FREEMAP: 950 cv = hammer2_icrc32(&media, bytes); 951 if (bref->check.freemap.icrc32 != cv) { 952 strlcpy(msg, "Bad HAMMER2_CHECK_FREEMAP", sizeof(msg)); 953 add_blockref_entry(&bstats->root, bref, msg, 954 strlen(msg) + 1); 955 print_blockref_debug(stdout, depth, index, bref, msg); 956 failed = true; 957 } 958 break; 959 } 960 961 switch (bref->type) { 962 case HAMMER2_BREF_TYPE_INODE: 963 if (!(media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA)) { 964 bscan = &media.ipdata.u.blockset.blockref[0]; 965 bcount = HAMMER2_SET_COUNT; 966 } else { 967 bscan = NULL; 968 bcount = 0; 969 } 970 break; 971 case HAMMER2_BREF_TYPE_INDIRECT: 972 bscan = &media.npdata[0]; 973 bcount = bytes / sizeof(hammer2_blockref_t); 974 break; 975 case HAMMER2_BREF_TYPE_FREEMAP_NODE: 976 bscan = &media.npdata[0]; 977 bcount = bytes / sizeof(hammer2_blockref_t); 978 break; 979 case HAMMER2_BREF_TYPE_VOLUME: 980 bscan = &media.voldata.sroot_blockset.blockref[0]; 981 bcount = HAMMER2_SET_COUNT; 982 break; 983 case HAMMER2_BREF_TYPE_FREEMAP: 984 bscan = &media.voldata.freemap_blockset.blockref[0]; 985 bcount = HAMMER2_SET_COUNT; 986 break; 987 default: 988 bscan = NULL; 989 bcount = 0; 990 break; 991 } 992 993 if (ForceOpt) 994 norecurse = false; 995 /* 996 * If failed, no recurse, but still verify its direct children. 997 * Beyond that is probably garbage. 998 */ 999 for (i = 0; norecurse == false && i < bcount; ++i) { 1000 delta_stats_t ds; 1001 memset(&ds, 0, sizeof(ds)); 1002 if (verify_blockref(voldata, &bscan[i], failed, bstats, droot, 1003 &ds, depth + 1, i) == -1) 1004 return -1; 1005 if (!failed) 1006 accumulate_delta_stats(dstats, &ds); 1007 } 1008 end: 1009 if (failed) 1010 return -1; 1011 1012 dstats->count++; 1013 if (bref->data_off && BlockrefCacheCount > 0 && 1014 dstats->count >= BlockrefCacheCount) { 1015 assert(bytes); 1016 add_blockref_entry(droot, bref, dstats, sizeof(*dstats)); 1017 print_blockref_debug(stdout, depth, index, bref, "cache-add"); 1018 } 1019 1020 return 0; 1021 } 1022 1023 static void 1024 print_pfs(const hammer2_inode_data_t *ipdata) 1025 { 1026 const hammer2_inode_meta_t *meta = &ipdata->meta; 1027 char *f, *pfs_id_str = NULL; 1028 const char *type_str; 1029 uuid_t uuid; 1030 1031 f = get_inode_filename(ipdata); 1032 uuid = meta->pfs_clid; 1033 hammer2_uuid_to_str(&uuid, &pfs_id_str); 1034 if (meta->pfs_type == HAMMER2_PFSTYPE_MASTER) { 1035 if (meta->pfs_subtype == HAMMER2_PFSSUBTYPE_NONE) 1036 type_str = "MASTER"; 1037 else 1038 type_str = hammer2_pfssubtype_to_str(meta->pfs_subtype); 1039 } else { 1040 type_str = hammer2_pfstype_to_str(meta->pfs_type); 1041 } 1042 tfprintf(stdout, 1, "%-11s %s %s\n", type_str, pfs_id_str, f); 1043 1044 free(f); 1045 free(pfs_id_str); 1046 } 1047 1048 static char* 1049 get_inode_filename(const hammer2_inode_data_t *ipdata) 1050 { 1051 char *p = malloc(HAMMER2_INODE_MAXNAME + 1); 1052 1053 memcpy(p, ipdata->filename, sizeof(ipdata->filename)); 1054 p[HAMMER2_INODE_MAXNAME] = '\0'; 1055 1056 return p; 1057 } 1058 1059 static void 1060 __add_pfs_blockref(const hammer2_blockref_t *bref, struct blockref_list *blist, 1061 const hammer2_inode_data_t *ipdata) 1062 { 1063 struct blockref_msg *newp, *p; 1064 1065 newp = calloc(1, sizeof(*newp)); 1066 newp->bref = *bref; 1067 newp->msg = calloc(1, sizeof(*ipdata)); 1068 memcpy(newp->msg, ipdata, sizeof(*ipdata)); 1069 1070 p = TAILQ_FIRST(blist); 1071 while (p) { 1072 char *f1 = get_inode_filename(newp->msg); 1073 char *f2 = get_inode_filename(p->msg); 1074 if (strcmp(f1, f2) <= 0) { 1075 TAILQ_INSERT_BEFORE(p, newp, entry); 1076 free(f1); 1077 free(f2); 1078 break; 1079 } 1080 p = TAILQ_NEXT(p, entry); 1081 free(f1); 1082 free(f2); 1083 } 1084 if (!p) 1085 TAILQ_INSERT_TAIL(blist, newp, entry); 1086 } 1087 1088 static int 1089 init_pfs_blockref(const hammer2_volume_data_t *voldata, 1090 const hammer2_blockref_t *bref, struct blockref_list *blist) 1091 { 1092 hammer2_media_data_t media; 1093 hammer2_inode_data_t ipdata; 1094 hammer2_blockref_t *bscan; 1095 int i, bcount; 1096 size_t bytes; 1097 1098 if (read_media(bref, &media, &bytes)) 1099 return -1; 1100 if (!bytes) 1101 return 0; 1102 1103 switch (bref->type) { 1104 case HAMMER2_BREF_TYPE_INODE: 1105 ipdata = media.ipdata; 1106 if (ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) { 1107 bscan = &ipdata.u.blockset.blockref[0]; 1108 bcount = HAMMER2_SET_COUNT; 1109 } else { 1110 bscan = NULL; 1111 bcount = 0; 1112 if (ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) 1113 __add_pfs_blockref(bref, blist, &ipdata); 1114 else 1115 assert(0); /* should only see SUPROOT or PFS */ 1116 } 1117 break; 1118 case HAMMER2_BREF_TYPE_INDIRECT: 1119 bscan = &media.npdata[0]; 1120 bcount = bytes / sizeof(hammer2_blockref_t); 1121 break; 1122 case HAMMER2_BREF_TYPE_VOLUME: 1123 bscan = &media.voldata.sroot_blockset.blockref[0]; 1124 bcount = HAMMER2_SET_COUNT; 1125 break; 1126 default: 1127 bscan = NULL; 1128 bcount = 0; 1129 break; 1130 } 1131 1132 for (i = 0; i < bcount; ++i) 1133 if (init_pfs_blockref(voldata, &bscan[i], blist) == -1) 1134 return -1; 1135 return 0; 1136 } 1137 1138 static void 1139 cleanup_pfs_blockref(struct blockref_list *blist) 1140 { 1141 cleanup_blockref_msg(blist); 1142 } 1143 1144 static void 1145 print_media(FILE *fp, int tab, const hammer2_blockref_t *bref, 1146 const hammer2_media_data_t *media, size_t media_bytes) 1147 { 1148 const hammer2_blockref_t *bscan; 1149 const hammer2_inode_data_t *ipdata; 1150 int i, bcount, namelen; 1151 char *str = NULL; 1152 uuid_t uuid; 1153 1154 switch (bref->type) { 1155 case HAMMER2_BREF_TYPE_INODE: 1156 ipdata = &media->ipdata; 1157 namelen = ipdata->meta.name_len; 1158 if (namelen > HAMMER2_INODE_MAXNAME) 1159 namelen = 0; 1160 tfprintf(fp, tab, "filename \"%*.*s\"\n", namelen, namelen, 1161 ipdata->filename); 1162 tfprintf(fp, tab, "version %d\n", ipdata->meta.version); 1163 if ((ipdata->meta.op_flags & HAMMER2_OPFLAG_PFSROOT) || 1164 ipdata->meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) 1165 tfprintf(fp, tab, "pfs_subtype %d (%s)\n", 1166 ipdata->meta.pfs_subtype, 1167 hammer2_pfssubtype_to_str(ipdata->meta.pfs_subtype)); 1168 tfprintf(fp, tab, "uflags 0x%08x\n", ipdata->meta.uflags); 1169 if (ipdata->meta.rmajor || ipdata->meta.rminor) { 1170 tfprintf(fp, tab, "rmajor %d\n", ipdata->meta.rmajor); 1171 tfprintf(fp, tab, "rminor %d\n", ipdata->meta.rminor); 1172 } 1173 tfprintf(fp, tab, "ctime %s\n", 1174 hammer2_time64_to_str(ipdata->meta.ctime, &str)); 1175 tfprintf(fp, tab, "mtime %s\n", 1176 hammer2_time64_to_str(ipdata->meta.mtime, &str)); 1177 tfprintf(fp, tab, "atime %s\n", 1178 hammer2_time64_to_str(ipdata->meta.atime, &str)); 1179 tfprintf(fp, tab, "btime %s\n", 1180 hammer2_time64_to_str(ipdata->meta.btime, &str)); 1181 uuid = ipdata->meta.uid; 1182 tfprintf(fp, tab, "uid %s\n", hammer2_uuid_to_str(&uuid, &str)); 1183 uuid = ipdata->meta.gid; 1184 tfprintf(fp, tab, "gid %s\n", hammer2_uuid_to_str(&uuid, &str)); 1185 tfprintf(fp, tab, "type %s\n", 1186 hammer2_iptype_to_str(ipdata->meta.type)); 1187 tfprintf(fp, tab, "op_flags 0x%02x\n", ipdata->meta.op_flags); 1188 tfprintf(fp, tab, "cap_flags 0x%04x\n", ipdata->meta.cap_flags); 1189 tfprintf(fp, tab, "mode %-7o\n", ipdata->meta.mode); 1190 tfprintf(fp, tab, "inum 0x%016jx\n", ipdata->meta.inum); 1191 tfprintf(fp, tab, "size %ju ", (uintmax_t)ipdata->meta.size); 1192 if (ipdata->meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA && 1193 ipdata->meta.size <= HAMMER2_EMBEDDED_BYTES) 1194 printf("(embedded data)\n"); 1195 else 1196 printf("\n"); 1197 tfprintf(fp, tab, "nlinks %ju\n", 1198 (uintmax_t)ipdata->meta.nlinks); 1199 tfprintf(fp, tab, "iparent 0x%016jx\n", 1200 (uintmax_t)ipdata->meta.iparent); 1201 tfprintf(fp, tab, "name_key 0x%016jx\n", 1202 (uintmax_t)ipdata->meta.name_key); 1203 tfprintf(fp, tab, "name_len %u\n", ipdata->meta.name_len); 1204 tfprintf(fp, tab, "ncopies %u\n", ipdata->meta.ncopies); 1205 tfprintf(fp, tab, "comp_algo %u\n", ipdata->meta.comp_algo); 1206 tfprintf(fp, tab, "check_algo %u\n", ipdata->meta.check_algo); 1207 if ((ipdata->meta.op_flags & HAMMER2_OPFLAG_PFSROOT) || 1208 ipdata->meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) { 1209 tfprintf(fp, tab, "pfs_nmasters %u\n", 1210 ipdata->meta.pfs_nmasters); 1211 tfprintf(fp, tab, "pfs_type %u (%s)\n", 1212 ipdata->meta.pfs_type, 1213 hammer2_pfstype_to_str(ipdata->meta.pfs_type)); 1214 tfprintf(fp, tab, "pfs_inum 0x%016jx\n", 1215 (uintmax_t)ipdata->meta.pfs_inum); 1216 uuid = ipdata->meta.pfs_clid; 1217 tfprintf(fp, tab, "pfs_clid %s\n", 1218 hammer2_uuid_to_str(&uuid, &str)); 1219 uuid = ipdata->meta.pfs_fsid; 1220 tfprintf(fp, tab, "pfs_fsid %s\n", 1221 hammer2_uuid_to_str(&uuid, &str)); 1222 tfprintf(fp, tab, "pfs_lsnap_tid 0x%016jx\n", 1223 (uintmax_t)ipdata->meta.pfs_lsnap_tid); 1224 } 1225 tfprintf(fp, tab, "data_quota %ju\n", 1226 (uintmax_t)ipdata->meta.data_quota); 1227 tfprintf(fp, tab, "data_count %ju\n", 1228 (uintmax_t)bref->embed.stats.data_count); 1229 tfprintf(fp, tab, "inode_quota %ju\n", 1230 (uintmax_t)ipdata->meta.inode_quota); 1231 tfprintf(fp, tab, "inode_count %ju\n", 1232 (uintmax_t)bref->embed.stats.inode_count); 1233 break; 1234 case HAMMER2_BREF_TYPE_INDIRECT: 1235 bcount = media_bytes / sizeof(hammer2_blockref_t); 1236 for (i = 0; i < bcount; ++i) { 1237 bscan = &media->npdata[i]; 1238 tfprintf(fp, tab, "%3d %016jx %-12s %016jx/%-2d\n", 1239 i, (uintmax_t)bscan->data_off, 1240 hammer2_breftype_to_str(bscan->type), 1241 (uintmax_t)bscan->key, 1242 bscan->keybits); 1243 } 1244 break; 1245 case HAMMER2_BREF_TYPE_DIRENT: 1246 if (bref->embed.dirent.namlen <= sizeof(bref->check.buf)) { 1247 tfprintf(fp, tab, "filename \"%*.*s\"\n", 1248 bref->embed.dirent.namlen, 1249 bref->embed.dirent.namlen, 1250 bref->check.buf); 1251 } else { 1252 tfprintf(fp, tab, "filename \"%*.*s\"\n", 1253 bref->embed.dirent.namlen, 1254 bref->embed.dirent.namlen, 1255 media->buf); 1256 } 1257 tfprintf(fp, tab, "inum 0x%016jx\n", 1258 (uintmax_t)bref->embed.dirent.inum); 1259 tfprintf(fp, tab, "namlen %d\n", 1260 (uintmax_t)bref->embed.dirent.namlen); 1261 tfprintf(fp, tab, "type %s\n", 1262 hammer2_iptype_to_str(bref->embed.dirent.type)); 1263 break; 1264 case HAMMER2_BREF_TYPE_FREEMAP_NODE: 1265 bcount = media_bytes / sizeof(hammer2_blockref_t); 1266 for (i = 0; i < bcount; ++i) { 1267 bscan = &media->npdata[i]; 1268 tfprintf(fp, tab, "%3d %016jx %-12s %016jx/%-2d\n", 1269 i, (uintmax_t)bscan->data_off, 1270 hammer2_breftype_to_str(bscan->type), 1271 (uintmax_t)bscan->key, 1272 bscan->keybits); 1273 } 1274 break; 1275 case HAMMER2_BREF_TYPE_FREEMAP_LEAF: 1276 for (i = 0; i < HAMMER2_FREEMAP_COUNT; ++i) { 1277 hammer2_off_t data_off = bref->key + 1278 i * HAMMER2_FREEMAP_LEVEL0_SIZE; 1279 #if HAMMER2_BMAP_ELEMENTS != 8 1280 #error "HAMMER2_BMAP_ELEMENTS != 8" 1281 #endif 1282 tfprintf(fp, tab, "%016jx %04d.%04x (avail=%7d) " 1283 "%016jx %016jx %016jx %016jx " 1284 "%016jx %016jx %016jx %016jx\n", 1285 data_off, i, media->bmdata[i].class, 1286 media->bmdata[i].avail, 1287 media->bmdata[i].bitmapq[0], 1288 media->bmdata[i].bitmapq[1], 1289 media->bmdata[i].bitmapq[2], 1290 media->bmdata[i].bitmapq[3], 1291 media->bmdata[i].bitmapq[4], 1292 media->bmdata[i].bitmapq[5], 1293 media->bmdata[i].bitmapq[6], 1294 media->bmdata[i].bitmapq[7]); 1295 } 1296 break; 1297 default: 1298 break; 1299 } 1300 if (str) 1301 free(str); 1302 } 1303 1304 int 1305 test_hammer2(const char *devpath) 1306 { 1307 bool failed = false; 1308 1309 hammer2_init_volumes(devpath, 1); 1310 1311 best_zone = find_best_zone(); 1312 if (best_zone == -1) 1313 fprintf(stderr, "Failed to find best zone\n"); 1314 1315 if (PrintPFS) { 1316 if (test_pfs_blockref() == -1) 1317 failed = true; 1318 goto end; /* print PFS info and exit */ 1319 } 1320 1321 printf("volume header\n"); 1322 if (test_volume_header() == -1) { 1323 failed = true; 1324 if (!ForceOpt) 1325 goto end; 1326 } 1327 1328 printf("freemap\n"); 1329 if (test_blockref(HAMMER2_BREF_TYPE_FREEMAP) == -1) { 1330 failed = true; 1331 if (!ForceOpt) 1332 goto end; 1333 } 1334 printf("volume\n"); 1335 if (!ScanPFS) { 1336 if (test_blockref(HAMMER2_BREF_TYPE_VOLUME) == -1) { 1337 failed = true; 1338 if (!ForceOpt) 1339 goto end; 1340 } 1341 } else { 1342 if (test_pfs_blockref() == -1) { 1343 failed = true; 1344 if (!ForceOpt) 1345 goto end; 1346 } 1347 } 1348 end: 1349 hammer2_cleanup_volumes(); 1350 1351 return failed ? -1 : 0; 1352 } 1353