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