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