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