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