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