2 * Copyright (c) 2019 Tomohiro Kusumi <tkusumi@netbsd.org>
3 * Copyright (c) 2019 The DragonFly Project
6 * This code is derived from software contributed to The DragonFly Project
7 * by Matthew Dillon <dillon@dragonflybsd.org>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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
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.
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
37 #include <sys/types.h>
40 #include <sys/queue.h>
41 #include <sys/ttycom.h>
42 #include <sys/diskslice.h>
52 #ifdef HAMMER2_USE_OPENSSL
53 #include <openssl/sha.h>
56 #include <vfs/hammer2/hammer2_disk.h>
57 #include <vfs/hammer2/hammer2_xxhash.h>
59 #include "hammer2_subs.h"
60 #include "fsck_hammer2.h"
63 TAILQ_ENTRY(blockref_msg) entry;
64 hammer2_blockref_t bref;
68 TAILQ_HEAD(blockref_list, blockref_msg);
70 struct blockref_entry {
71 RB_ENTRY(blockref_entry) entry;
72 hammer2_off_t data_off;
73 struct blockref_list head;
77 blockref_cmp(struct blockref_entry *b1, struct blockref_entry *b2)
79 if (b1->data_off < b2->data_off)
81 if (b1->data_off > b2->data_off)
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);
91 struct blockref_tree root;
92 uint8_t type; /* HAMMER2_BREF_TYPE_VOLUME or FREEMAP */
93 uint64_t total_blockref;
97 /* use volume or freemap depending on type value */
100 uint64_t total_indirect;
102 uint64_t total_dirent;
105 uint64_t total_freemap_node;
106 uint64_t total_freemap_leaf;
112 uint64_t total_blockref;
113 uint64_t total_empty;
114 uint64_t total_bytes;
116 uint64_t total_inode;
117 uint64_t total_indirect;
119 uint64_t total_dirent;
122 uint64_t total_freemap_node;
123 uint64_t total_freemap_leaf;
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 *,
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);
146 static hammer2_off_t volume_size;
147 static int best_zone = -1;
152 tfprintf(FILE *fp, int tab, const char *ctl, ...)
157 ret = fprintf(fp, "%*s", tab * TAB, "");
162 vfprintf(fp, ctl, va);
167 tsnprintf(char *str, size_t siz, int tab, const char *ctl, ...)
172 ret = snprintf(str, siz, "%*s", tab * TAB, "");
173 if (ret < 0 || ret >= (int)siz)
177 vsnprintf(str + ret, siz - ret, ctl, va);
182 tprintf_zone(int tab, int i, const hammer2_blockref_t *bref)
184 tfprintf(stdout, tab, "zone.%d %016jx%s\n",
185 i, (uintmax_t)bref->data_off,
186 (!ScanBest && i == best_zone) ? " (best)" : "");
190 init_root_blockref(int fd, int i, uint8_t type, hammer2_blockref_t *bref)
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));
197 bref->data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
199 return lseek(fd, bref->data_off & ~HAMMER2_OFF_MASK_RADIX, SEEK_SET);
203 find_best_zone(int fd)
205 hammer2_blockref_t best;
208 memset(&best, 0, sizeof(best));
210 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
211 hammer2_volume_data_t voldata;
212 hammer2_blockref_t broot;
215 if (i * HAMMER2_ZONE_BYTES64 >= volume_size) {
216 tfprintf(stderr, 0, "zone.%d exceeds volume size\n", i);
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))
225 broot.mirror_tid = voldata.mirror_tid;
226 if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
230 } else if (ret == -1) {
234 tfprintf(stderr, 1, "Failed to read volume header\n");
243 test_volume_header(int fd)
248 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
249 hammer2_volume_data_t voldata;
250 hammer2_blockref_t broot;
253 if (ScanBest && i != best_zone)
255 if (i * HAMMER2_ZONE_BYTES64 >= volume_size) {
256 tfprintf(stderr, 0, "zone.%d exceeds volume size\n", i);
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)
265 } else if (ret == -1) {
269 tfprintf(stderr, 1, "Failed to read volume header\n");
274 return failed ? -1 : 0;
278 test_blockref(int fd, uint8_t type)
280 struct blockref_tree droot;
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;
290 if (ScanBest && i != best_zone)
292 if (i * HAMMER2_ZONE_BYTES64 >= volume_size) {
293 tfprintf(stderr, 0, "zone.%d exceeds volume size\n", i);
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);
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)
307 print_blockref_stats(&bstats, true);
308 print_blockref_entry(fd, &bstats.root);
309 cleanup_blockref_stats(&bstats);
310 } else if (ret == -1) {
315 tfprintf(stderr, 1, "Failed to read volume header\n");
321 cleanup_delta_root(&droot);
322 return failed ? -1 : 0;
326 test_pfs_blockref(int fd)
328 struct blockref_tree droot;
329 uint8_t type = HAMMER2_BREF_TYPE_VOLUME;
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;
339 if (ScanBest && i != best_zone)
341 if (i * HAMMER2_ZONE_BYTES64 >= volume_size) {
342 tfprintf(stderr, 0, "zone.%d exceeds volume size\n", i);
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;
352 tprintf_zone(0, i, &broot);
354 if (init_pfs_blockref(fd, &voldata, &broot, &blist) ==
356 tfprintf(stderr, 1, "Failed to read PFS "
361 if (TAILQ_EMPTY(&blist)) {
362 tfprintf(stderr, 1, "Failed to find PFS "
367 TAILQ_FOREACH(p, &blist, entry) {
368 blockref_stats_t bstats;
372 for (j = 0; j < NumPFSNames; j++)
373 if (!strcmp(PFSNames[j],
381 tfprintf(stdout, 1, "%s\n", p->msg);
382 init_blockref_stats(&bstats, type);
384 memset(&ds, 0, sizeof(ds));
385 if (verify_blockref(fd, &voldata, &p->bref,
386 false, &bstats, &droot, &ds, 0, 0) == -1)
388 print_blockref_stats(&bstats, true);
389 print_blockref_entry(fd, &bstats.root);
390 cleanup_blockref_stats(&bstats);
392 cleanup_pfs_blockref(&blist);
393 if (NumPFSNames && !count) {
394 tfprintf(stderr, 1, "PFS not found\n");
397 } else if (ret == -1) {
402 tfprintf(stderr, 1, "Failed to read volume header\n");
408 cleanup_delta_root(&droot);
409 return failed ? -1 : 0;
420 if (ioctl(0, TIOCGWINSZ, &ws) != -1)
422 if (columns == 0 && (cp = getenv("COLUMNS")))
425 columns = 80; /* last resort */
431 cleanup_blockref_msg(struct blockref_list *head)
433 struct blockref_msg *p;
435 while ((p = TAILQ_FIRST(head)) != NULL) {
436 TAILQ_REMOVE(head, p, entry);
440 assert(TAILQ_EMPTY(head));
444 cleanup_blockref_entry(struct blockref_tree *root)
446 struct blockref_entry *e;
448 while ((e = RB_ROOT(root)) != NULL) {
449 RB_REMOVE(blockref_tree, root, e);
450 cleanup_blockref_msg(&e->head);
453 assert(RB_EMPTY(root));
457 add_blockref_msg(struct blockref_list *head, const hammer2_blockref_t *bref,
458 const void *msg, size_t siz)
460 struct blockref_msg *m;
463 m = calloc(1, sizeof(*m));
471 TAILQ_INSERT_TAIL(head, m, entry);
475 add_blockref_entry(struct blockref_tree *root, const hammer2_blockref_t *bref,
476 const void *msg, size_t siz)
478 struct blockref_entry *e, bref_find;
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);
484 e = calloc(1, sizeof(*e));
486 TAILQ_INIT(&e->head);
487 e->data_off = bref->data_off;
490 add_blockref_msg(&e->head, bref, msg, siz);
492 RB_INSERT(blockref_tree, root, e);
496 __print_blockref(FILE *fp, int tab, const hammer2_blockref_t *bref,
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,
509 print_blockref(FILE *fp, const hammer2_blockref_t *bref, const char *msg)
511 __print_blockref(fp, 1, bref, msg);
515 print_blockref_debug(FILE *fp, int depth, int index,
516 const hammer2_blockref_t *bref, const char *msg)
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);
533 print_blockref_msg(int fd, struct blockref_list *head)
535 struct blockref_msg *m;
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;
543 if (!read_media(fd, bref, &media, &bytes))
544 print_media(stderr, 2, bref, &media, bytes);
546 tfprintf(stderr, 2, "Failed to read media\n");
552 print_blockref_entry(int fd, struct blockref_tree *root)
554 struct blockref_entry *e;
556 RB_FOREACH(e, blockref_tree, root)
557 print_blockref_msg(fd, &e->head);
561 init_blockref_stats(blockref_stats_t *bstats, uint8_t type)
563 memset(bstats, 0, sizeof(*bstats));
564 RB_INIT(&bstats->root);
569 cleanup_blockref_stats(blockref_stats_t *bstats)
571 cleanup_blockref_entry(&bstats->root);
575 init_delta_root(struct blockref_tree *droot)
581 cleanup_delta_root(struct blockref_tree *droot)
583 cleanup_blockref_entry(droot);
587 print_blockref_stats(const blockref_stats_t *bstats, bool newline)
589 size_t siz = charsperline();
590 char *buf = calloc(1, siz);
596 snprintf(emptybuf, sizeof(emptybuf), ", %ju empty",
597 (uintmax_t)bstats->total_empty);
599 strlcpy(emptybuf, "", sizeof(emptybuf));
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,
611 sizetostr(bstats->total_bytes));
613 case HAMMER2_BREF_TYPE_FREEMAP:
614 tsnprintf(buf, siz, 1, "%ju blockref (%ju node, %ju leaf%s), "
616 (uintmax_t)bstats->total_blockref,
617 (uintmax_t)bstats->freemap.total_freemap_node,
618 (uintmax_t)bstats->freemap.total_freemap_leaf,
620 sizetostr(bstats->total_bytes));
637 verify_volume_header(const hammer2_volume_data_t *voldata)
639 hammer2_crc32_t crc0, crc1;
640 const char *p = (const char*)voldata;
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);
648 if (voldata->magic == HAMMER2_VOLUME_ID_ABO)
649 tfprintf(stderr, 1, "Reverse endian\n");
651 crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT0];
652 crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC0_OFF,
653 HAMMER2_VOLUME_ICRC0_SIZE);
655 tfprintf(stderr, 1, "Bad HAMMER2_VOL_ICRC_SECT0 CRC\n");
659 crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT1];
660 crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC1_OFF,
661 HAMMER2_VOLUME_ICRC1_SIZE);
663 tfprintf(stderr, 1, "Bad HAMMER2_VOL_ICRC_SECT1 CRC\n");
667 crc0 = voldata->icrc_volheader;
668 crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRCVH_OFF,
669 HAMMER2_VOLUME_ICRCVH_SIZE);
671 tfprintf(stderr, 1, "Bad volume header CRC\n");
679 read_media(int fd, const hammer2_blockref_t *bref, hammer2_media_data_t *media,
682 hammer2_off_t io_off, io_base;
683 size_t bytes, io_bytes, boff;
685 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
687 bytes = (size_t)1 << bytes;
689 *media_bytes = bytes;
694 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
695 io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1);
696 boff = io_off - io_base;
698 io_bytes = HAMMER2_LBUFSIZE;
699 while (io_bytes + boff < bytes)
702 if (io_bytes > sizeof(*media))
704 if (lseek(fd, io_base, SEEK_SET) == -1)
706 if (read(fd, media, io_bytes) != (ssize_t)io_bytes)
709 memmove(media, (char *)media + boff, bytes);
715 load_delta_stats(blockref_stats_t *bstats, const delta_stats_t *dstats)
717 bstats->total_blockref += dstats->total_blockref;
718 bstats->total_empty += dstats->total_empty;
719 bstats->total_bytes += dstats->total_bytes;
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;
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;
741 accumulate_delta_stats(delta_stats_t *dst, const delta_stats_t *src)
743 dst->total_blockref += src->total_blockref;
744 dst->total_empty += src->total_empty;
745 dst->total_bytes += src->total_bytes;
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;
752 dst->freemap.total_freemap_node += src->freemap.total_freemap_node;
753 dst->freemap.total_freemap_leaf += src->freemap.total_freemap_leaf;
755 dst->count += src->count;
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)
763 hammer2_media_data_t media;
764 hammer2_blockref_t *bscan;
771 #ifdef HAMMER2_USE_OPENSSL
774 uint8_t digest[SHA256_DIGEST_LENGTH];
775 uint64_t digest64[SHA256_DIGEST_LENGTH/8];
778 /* only for DebugOpt > 1 */
780 print_blockref_debug(stdout, depth, index, bref, NULL);
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);
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");
803 bstats->total_blockref++;
804 dstats->total_blockref++;
806 switch (bref->type) {
807 case HAMMER2_BREF_TYPE_EMPTY:
809 bstats->total_empty++;
810 dstats->total_empty++;
812 bstats->total_blockref--;
813 dstats->total_blockref--;
816 case HAMMER2_BREF_TYPE_INODE:
817 bstats->volume.total_inode++;
818 dstats->volume.total_inode++;
820 case HAMMER2_BREF_TYPE_INDIRECT:
821 bstats->volume.total_indirect++;
822 dstats->volume.total_indirect++;
824 case HAMMER2_BREF_TYPE_DATA:
825 bstats->volume.total_data++;
826 dstats->volume.total_data++;
828 case HAMMER2_BREF_TYPE_DIRENT:
829 bstats->volume.total_dirent++;
830 dstats->volume.total_dirent++;
832 case HAMMER2_BREF_TYPE_FREEMAP_NODE:
833 bstats->freemap.total_freemap_node++;
834 dstats->freemap.total_freemap_node++;
836 case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
837 bstats->freemap.total_freemap_leaf++;
838 dstats->freemap.total_freemap_leaf++;
840 case HAMMER2_BREF_TYPE_VOLUME:
841 bstats->total_blockref--;
842 dstats->total_blockref--;
844 case HAMMER2_BREF_TYPE_FREEMAP:
845 bstats->total_blockref--;
846 dstats->total_blockref--;
849 snprintf(msg, sizeof(msg), "Invalid blockref type %d",
851 add_blockref_entry(&bstats->root, bref, msg, strlen(msg) + 1);
852 print_blockref_debug(stdout, depth, index, bref, msg);
857 switch (read_media(fd, bref, &media, &bytes)) {
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);
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);
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;
878 if (!CountEmpty && bref->type == HAMMER2_BREF_TYPE_EMPTY) {
880 bstats->total_bytes -= bytes;
881 dstats->total_bytes -= bytes;
884 if (!DebugOpt && QuietOpt <= 0 && (bstats->total_blockref % 100) == 0)
885 print_blockref_stats(bstats, false);
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,
897 print_blockref_debug(stdout, depth, index, bref, msg);
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,
907 print_blockref_debug(stdout, depth, index, bref, msg);
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,
922 print_blockref_debug(stdout, depth, index, bref, msg);
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,
933 print_blockref_debug(stdout, depth, index, bref, msg);
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;
949 case HAMMER2_BREF_TYPE_INDIRECT:
950 bscan = &media.npdata[0];
951 bcount = bytes / sizeof(hammer2_blockref_t);
953 case HAMMER2_BREF_TYPE_FREEMAP_NODE:
954 bscan = &media.npdata[0];
955 bcount = bytes / sizeof(hammer2_blockref_t);
957 case HAMMER2_BREF_TYPE_VOLUME:
958 bscan = &media.voldata.sroot_blockset.blockref[0];
959 bcount = HAMMER2_SET_COUNT;
961 case HAMMER2_BREF_TYPE_FREEMAP:
962 bscan = &media.voldata.freemap_blockset.blockref[0];
963 bcount = HAMMER2_SET_COUNT;
974 * If failed, no recurse, but still verify its direct children.
975 * Beyond that is probably garbage.
977 for (i = 0; norecurse == false && i < bcount; ++i) {
979 memset(&ds, 0, sizeof(ds));
980 if (verify_blockref(fd, voldata, &bscan[i], failed, bstats,
981 droot, &ds, depth + 1, i) == -1)
984 accumulate_delta_stats(dstats, &ds);
991 if (bref->data_off && BlockrefCacheCount > 0 &&
992 dstats->count >= BlockrefCacheCount) {
994 add_blockref_entry(droot, bref, dstats, sizeof(*dstats));
995 print_blockref_debug(stdout, depth, index, bref, "cache-add");
1002 init_pfs_blockref(int fd, const hammer2_volume_data_t *voldata,
1003 const hammer2_blockref_t *bref, struct blockref_list *blist)
1005 hammer2_media_data_t media;
1006 hammer2_inode_data_t ipdata;
1007 hammer2_blockref_t *bscan;
1011 if (read_media(fd, bref, &media, &bytes))
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;
1025 if (ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) {
1026 struct blockref_msg *newp, *p;
1027 newp = calloc(1, sizeof(*newp));
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);
1036 if (strcmp(newp->msg, p->msg) <= 0) {
1037 TAILQ_INSERT_BEFORE(p, newp,
1041 p = TAILQ_NEXT(p, entry);
1044 TAILQ_INSERT_TAIL(blist, newp, entry);
1046 assert(0); /* should only see SUPROOT or PFS */
1049 case HAMMER2_BREF_TYPE_INDIRECT:
1050 bscan = &media.npdata[0];
1051 bcount = bytes / sizeof(hammer2_blockref_t);
1053 case HAMMER2_BREF_TYPE_VOLUME:
1054 bscan = &media.voldata.sroot_blockset.blockref[0];
1055 bcount = HAMMER2_SET_COUNT;
1063 for (i = 0; i < bcount; ++i)
1064 if (init_pfs_blockref(fd, voldata, &bscan[i], blist) == -1)
1070 cleanup_pfs_blockref(struct blockref_list *blist)
1072 cleanup_blockref_msg(blist);
1076 print_media(FILE *fp, int tab, const hammer2_blockref_t *bref,
1077 hammer2_media_data_t *media, size_t media_bytes)
1079 hammer2_blockref_t *bscan;
1080 hammer2_inode_data_t *ipdata;
1081 int i, bcount, namelen;
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)
1090 tfprintf(fp, tab, "filename \"%*.*s\"\n", namelen, namelen,
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);
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");
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);
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);
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,
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,
1177 tfprintf(fp, tab, "filename \"%*.*s\"\n",
1178 bref->embed.dirent.namlen,
1179 bref->embed.dirent.namlen,
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));
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,
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"
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]);
1229 static hammer2_off_t
1230 check_volume(int fd)
1232 struct partinfo pinfo;
1235 if (ioctl(fd, DIOCGPART, &pinfo) < 0) {
1237 if (fstat(fd, &st) < 0) {
1241 if (!S_ISREG(st.st_mode)) {
1242 fprintf(stderr, "Unsupported file type\n");
1247 if (pinfo.reserved_blocks) {
1248 fprintf(stderr, "HAMMER2 cannot be placed in a "
1249 "partition which overlaps the disklabel or MBR\n");
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);
1258 size = pinfo.media_size;
1264 test_hammer2(const char *devpath)
1267 bool failed = false;
1270 fd = open(devpath, O_RDONLY);
1276 if (fstat(fd, &st) == -1) {
1281 if (!S_ISCHR(st.st_mode) && !S_ISREG(st.st_mode)) {
1282 fprintf(stderr, "Unsupported file type\n");
1287 volume_size = check_volume(fd);
1288 if (volume_size == (hammer2_off_t)-1) {
1289 fprintf(stderr, "Failed to find volume size\n");
1293 volume_size &= ~HAMMER2_VOLUME_ALIGNMASK64;
1295 best_zone = find_best_zone(fd);
1296 if (best_zone == -1)
1297 fprintf(stderr, "Failed to find best zone\n");
1299 printf("volume header\n");
1300 if (test_volume_header(fd) == -1) {
1306 printf("freemap\n");
1307 if (test_blockref(fd, HAMMER2_BREF_TYPE_FREEMAP) == -1) {
1314 if (test_blockref(fd, HAMMER2_BREF_TYPE_VOLUME) == -1) {
1320 if (test_pfs_blockref(fd) == -1) {
1329 return failed ? -1 : 0;