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(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(const hammer2_blockref_t *, hammer2_media_data_t *,
137 static int verify_blockref(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(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);
148 static int best_zone = -1;
153 tfprintf(FILE *fp, int tab, const char *ctl, ...)
158 ret = fprintf(fp, "%*s", tab * TAB, "");
163 vfprintf(fp, ctl, va);
168 tsnprintf(char *str, size_t siz, int tab, const char *ctl, ...)
173 ret = snprintf(str, siz, "%*s", tab * TAB, "");
174 if (ret < 0 || ret >= (int)siz)
178 vsnprintf(str + ret, siz - ret, ctl, va);
183 tprintf_zone(int tab, int i, const hammer2_blockref_t *bref)
185 tfprintf(stdout, tab, "zone.%d %016jx%s\n",
186 i, (uintmax_t)bref->data_off,
187 (!ScanBest && i == best_zone) ? " (best)" : "");
191 init_root_blockref(int i, uint8_t type, hammer2_blockref_t *bref)
195 assert(type == HAMMER2_BREF_TYPE_EMPTY ||
196 type == HAMMER2_BREF_TYPE_VOLUME ||
197 type == HAMMER2_BREF_TYPE_FREEMAP);
198 memset(bref, 0, sizeof(*bref));
200 bref->data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
201 off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
203 return lseek(hammer2_get_root_volume_fd(),
204 off - hammer2_get_root_volume_offset(), SEEK_SET);
210 hammer2_blockref_t best;
213 memset(&best, 0, sizeof(best));
215 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
216 hammer2_volume_data_t voldata;
217 hammer2_blockref_t broot;
220 if (i * HAMMER2_ZONE_BYTES64 >=
221 hammer2_get_root_volume_size())
223 init_root_blockref(i, HAMMER2_BREF_TYPE_EMPTY, &broot);
224 ret = read(hammer2_get_root_volume_fd(), &voldata,
226 if (ret == HAMMER2_PBUFSIZE) {
227 if ((voldata.magic != HAMMER2_VOLUME_ID_HBO) &&
228 (voldata.magic != HAMMER2_VOLUME_ID_ABO))
230 broot.mirror_tid = voldata.mirror_tid;
231 if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
235 } else if (ret == -1) {
239 tfprintf(stderr, 1, "Failed to read volume header\n");
248 test_volume_header(void)
253 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
254 hammer2_volume_data_t voldata;
255 hammer2_blockref_t broot;
258 if (ScanBest && i != best_zone)
260 if (i * HAMMER2_ZONE_BYTES64 >=
261 hammer2_get_root_volume_size()) {
262 tfprintf(stderr, 0, "zone.%d exceeds volume size\n", i);
265 init_root_blockref(i, HAMMER2_BREF_TYPE_EMPTY, &broot);
266 ret = read(hammer2_get_root_volume_fd(), &voldata,
268 if (ret == HAMMER2_PBUFSIZE) {
269 tprintf_zone(0, i, &broot);
270 if (verify_volume_header(&voldata) == -1)
272 } else if (ret == -1) {
276 tfprintf(stderr, 1, "Failed to read volume header\n");
281 return failed ? -1 : 0;
285 test_blockref(uint8_t type)
287 struct blockref_tree droot;
291 init_delta_root(&droot);
292 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
293 hammer2_volume_data_t voldata;
294 hammer2_blockref_t broot;
297 if (ScanBest && i != best_zone)
299 if (i * HAMMER2_ZONE_BYTES64 >=
300 hammer2_get_root_volume_size()) {
301 tfprintf(stderr, 0, "zone.%d exceeds volume size\n", i);
304 init_root_blockref(i, type, &broot);
305 ret = read(hammer2_get_root_volume_fd(), &voldata,
307 if (ret == HAMMER2_PBUFSIZE) {
308 blockref_stats_t bstats;
309 init_blockref_stats(&bstats, type);
311 memset(&ds, 0, sizeof(ds));
312 tprintf_zone(0, i, &broot);
313 if (verify_blockref(&voldata, &broot, false, &bstats,
314 &droot, &ds, 0, 0) == -1)
316 print_blockref_stats(&bstats, true);
317 print_blockref_entry(&bstats.root);
318 cleanup_blockref_stats(&bstats);
319 } else if (ret == -1) {
324 tfprintf(stderr, 1, "Failed to read volume header\n");
330 cleanup_delta_root(&droot);
331 return failed ? -1 : 0;
335 test_pfs_blockref(void)
337 struct blockref_tree droot;
338 uint8_t type = HAMMER2_BREF_TYPE_VOLUME;
342 init_delta_root(&droot);
343 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
344 hammer2_volume_data_t voldata;
345 hammer2_blockref_t broot;
348 if (ScanBest && i != best_zone)
350 if (i * HAMMER2_ZONE_BYTES64 >=
351 hammer2_get_root_volume_size()) {
352 tfprintf(stderr, 0, "zone.%d exceeds volume size\n", i);
355 init_root_blockref(i, type, &broot);
356 ret = read(hammer2_get_root_volume_fd(), &voldata,
358 if (ret == HAMMER2_PBUFSIZE) {
359 struct blockref_list blist;
360 struct blockref_msg *p;
363 tprintf_zone(0, i, &broot);
365 if (init_pfs_blockref(&voldata, &broot, &blist) == -1) {
366 tfprintf(stderr, 1, "Failed to read PFS "
371 if (TAILQ_EMPTY(&blist)) {
372 tfprintf(stderr, 1, "Failed to find PFS "
377 TAILQ_FOREACH(p, &blist, entry) {
378 blockref_stats_t bstats;
380 char *f = get_inode_filename(p->msg);
383 for (j = 0; j < NumPFSNames; j++)
384 if (!strcmp(PFSNames[j], f))
398 tfprintf(stdout, 1, "%s\n", f);
400 init_blockref_stats(&bstats, type);
402 memset(&ds, 0, sizeof(ds));
403 if (verify_blockref(&voldata, &p->bref, false,
404 &bstats, &droot, &ds, 0, 0) == -1)
406 print_blockref_stats(&bstats, true);
407 print_blockref_entry(&bstats.root);
408 cleanup_blockref_stats(&bstats);
410 cleanup_pfs_blockref(&blist);
411 if (NumPFSNames && !count) {
412 tfprintf(stderr, 1, "PFS not found\n");
415 } else if (ret == -1) {
420 tfprintf(stderr, 1, "Failed to read volume header\n");
426 cleanup_delta_root(&droot);
427 return failed ? -1 : 0;
438 if (ioctl(0, TIOCGWINSZ, &ws) != -1)
440 if (columns == 0 && (cp = getenv("COLUMNS")))
443 columns = 80; /* last resort */
449 cleanup_blockref_msg(struct blockref_list *head)
451 struct blockref_msg *p;
453 while ((p = TAILQ_FIRST(head)) != NULL) {
454 TAILQ_REMOVE(head, p, entry);
458 assert(TAILQ_EMPTY(head));
462 cleanup_blockref_entry(struct blockref_tree *root)
464 struct blockref_entry *e;
466 while ((e = RB_ROOT(root)) != NULL) {
467 RB_REMOVE(blockref_tree, root, e);
468 cleanup_blockref_msg(&e->head);
471 assert(RB_EMPTY(root));
475 add_blockref_msg(struct blockref_list *head, const hammer2_blockref_t *bref,
476 const void *msg, size_t siz)
478 struct blockref_msg *m;
481 m = calloc(1, sizeof(*m));
489 TAILQ_INSERT_TAIL(head, m, entry);
493 add_blockref_entry(struct blockref_tree *root, const hammer2_blockref_t *bref,
494 const void *msg, size_t siz)
496 struct blockref_entry *e, bref_find;
498 memset(&bref_find, 0, sizeof(bref_find));
499 bref_find.data_off = bref->data_off;
500 e = RB_FIND(blockref_tree, root, &bref_find);
502 e = calloc(1, sizeof(*e));
504 TAILQ_INIT(&e->head);
505 e->data_off = bref->data_off;
508 add_blockref_msg(&e->head, bref, msg, siz);
510 RB_INSERT(blockref_tree, root, e);
514 __print_blockref(FILE *fp, int tab, const hammer2_blockref_t *bref,
517 tfprintf(fp, tab, "%016jx %-12s %016jx/%-2d%s%s\n",
518 (uintmax_t)bref->data_off,
519 hammer2_breftype_to_str(bref->type),
520 (uintmax_t)bref->key,
527 print_blockref(FILE *fp, const hammer2_blockref_t *bref, const char *msg)
529 __print_blockref(fp, 1, bref, msg);
533 print_blockref_debug(FILE *fp, int depth, int index,
534 const hammer2_blockref_t *bref, const char *msg)
540 memset(buf, 0, sizeof(buf));
541 for (i = 0; i < depth * 2; i++)
542 strlcat(buf, " ", sizeof(buf));
543 tfprintf(fp, 1, buf);
544 fprintf(fp, "%-2d %-3d ", depth, index);
545 __print_blockref(fp, 0, bref, msg);
546 } else if (DebugOpt > 0)
547 print_blockref(fp, bref, msg);
551 print_blockref_msg(const struct blockref_list *head)
553 struct blockref_msg *m;
555 TAILQ_FOREACH(m, head, entry) {
556 hammer2_blockref_t *bref = &m->bref;
557 print_blockref(stderr, bref, m->msg);
558 if (VerboseOpt > 0) {
559 hammer2_media_data_t media;
561 if (!read_media(bref, &media, &bytes))
562 print_media(stderr, 2, bref, &media, bytes);
564 tfprintf(stderr, 2, "Failed to read media\n");
570 print_blockref_entry(struct blockref_tree *root)
572 struct blockref_entry *e;
574 RB_FOREACH(e, blockref_tree, root)
575 print_blockref_msg(&e->head);
579 init_blockref_stats(blockref_stats_t *bstats, uint8_t type)
581 memset(bstats, 0, sizeof(*bstats));
582 RB_INIT(&bstats->root);
587 cleanup_blockref_stats(blockref_stats_t *bstats)
589 cleanup_blockref_entry(&bstats->root);
593 init_delta_root(struct blockref_tree *droot)
599 cleanup_delta_root(struct blockref_tree *droot)
601 cleanup_blockref_entry(droot);
605 print_blockref_stats(const blockref_stats_t *bstats, bool newline)
607 size_t siz = charsperline();
608 char *buf = calloc(1, siz);
614 snprintf(emptybuf, sizeof(emptybuf), ", %ju empty",
615 (uintmax_t)bstats->total_empty);
617 strlcpy(emptybuf, "", sizeof(emptybuf));
619 switch (bstats->type) {
620 case HAMMER2_BREF_TYPE_VOLUME:
621 tsnprintf(buf, siz, 1, "%ju blockref (%ju inode, %ju indirect, "
622 "%ju data, %ju dirent%s), %s",
623 (uintmax_t)bstats->total_blockref,
624 (uintmax_t)bstats->volume.total_inode,
625 (uintmax_t)bstats->volume.total_indirect,
626 (uintmax_t)bstats->volume.total_data,
627 (uintmax_t)bstats->volume.total_dirent,
629 sizetostr(bstats->total_bytes));
631 case HAMMER2_BREF_TYPE_FREEMAP:
632 tsnprintf(buf, siz, 1, "%ju blockref (%ju node, %ju leaf%s), "
634 (uintmax_t)bstats->total_blockref,
635 (uintmax_t)bstats->freemap.total_freemap_node,
636 (uintmax_t)bstats->freemap.total_freemap_leaf,
638 sizetostr(bstats->total_bytes));
655 verify_volume_header(const hammer2_volume_data_t *voldata)
657 hammer2_crc32_t crc0, crc1;
658 const char *p = (const char*)voldata;
660 if ((voldata->magic != HAMMER2_VOLUME_ID_HBO) &&
661 (voldata->magic != HAMMER2_VOLUME_ID_ABO)) {
662 tfprintf(stderr, 1, "Bad magic %jX\n", voldata->magic);
666 if (voldata->magic == HAMMER2_VOLUME_ID_ABO)
667 tfprintf(stderr, 1, "Reverse endian\n");
669 crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT0];
670 crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC0_OFF,
671 HAMMER2_VOLUME_ICRC0_SIZE);
673 tfprintf(stderr, 1, "Bad HAMMER2_VOL_ICRC_SECT0 CRC\n");
677 crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT1];
678 crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC1_OFF,
679 HAMMER2_VOLUME_ICRC1_SIZE);
681 tfprintf(stderr, 1, "Bad HAMMER2_VOL_ICRC_SECT1 CRC\n");
685 crc0 = voldata->icrc_volheader;
686 crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRCVH_OFF,
687 HAMMER2_VOLUME_ICRCVH_SIZE);
689 tfprintf(stderr, 1, "Bad volume header CRC\n");
697 read_media(const hammer2_blockref_t *bref, hammer2_media_data_t *media,
700 hammer2_off_t io_off, io_base;
701 size_t bytes, io_bytes, boff;
704 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
706 bytes = (size_t)1 << bytes;
708 *media_bytes = bytes;
713 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
714 io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1);
715 boff = io_off - io_base;
717 io_bytes = HAMMER2_LBUFSIZE;
718 while (io_bytes + boff < bytes)
721 if (io_bytes > sizeof(*media))
723 fd = hammer2_get_volume_fd(io_off);
724 if (lseek(fd, io_base - hammer2_get_volume_offset(io_base), SEEK_SET)
727 if (read(fd, media, io_bytes) != (ssize_t)io_bytes)
730 memmove(media, (char *)media + boff, bytes);
736 load_delta_stats(blockref_stats_t *bstats, const delta_stats_t *dstats)
738 bstats->total_blockref += dstats->total_blockref;
739 bstats->total_empty += dstats->total_empty;
740 bstats->total_bytes += dstats->total_bytes;
742 switch (bstats->type) {
743 case HAMMER2_BREF_TYPE_VOLUME:
744 bstats->volume.total_inode += dstats->volume.total_inode;
745 bstats->volume.total_indirect += dstats->volume.total_indirect;
746 bstats->volume.total_data += dstats->volume.total_data;
747 bstats->volume.total_dirent += dstats->volume.total_dirent;
749 case HAMMER2_BREF_TYPE_FREEMAP:
750 bstats->freemap.total_freemap_node +=
751 dstats->freemap.total_freemap_node;
752 bstats->freemap.total_freemap_leaf +=
753 dstats->freemap.total_freemap_leaf;
762 accumulate_delta_stats(delta_stats_t *dst, const delta_stats_t *src)
764 dst->total_blockref += src->total_blockref;
765 dst->total_empty += src->total_empty;
766 dst->total_bytes += src->total_bytes;
768 dst->volume.total_inode += src->volume.total_inode;
769 dst->volume.total_indirect += src->volume.total_indirect;
770 dst->volume.total_data += src->volume.total_data;
771 dst->volume.total_dirent += src->volume.total_dirent;
773 dst->freemap.total_freemap_node += src->freemap.total_freemap_node;
774 dst->freemap.total_freemap_leaf += src->freemap.total_freemap_leaf;
776 dst->count += src->count;
780 verify_blockref(const hammer2_volume_data_t *voldata,
781 const hammer2_blockref_t *bref, bool norecurse, blockref_stats_t *bstats,
782 struct blockref_tree *droot, delta_stats_t *dstats, int depth, int index)
784 hammer2_media_data_t media;
785 hammer2_blockref_t *bscan;
792 #ifdef HAMMER2_USE_OPENSSL
795 uint8_t digest[SHA256_DIGEST_LENGTH];
796 uint64_t digest64[SHA256_DIGEST_LENGTH/8];
799 /* only for DebugOpt > 1 */
801 print_blockref_debug(stdout, depth, index, bref, NULL);
803 if (bref->data_off) {
804 struct blockref_entry *e, bref_find;
805 memset(&bref_find, 0, sizeof(bref_find));
806 bref_find.data_off = bref->data_off;
807 e = RB_FIND(blockref_tree, droot, &bref_find);
809 struct blockref_msg *m;
810 TAILQ_FOREACH(m, &e->head, entry) {
811 delta_stats_t *ds = m->msg;
812 if (!memcmp(&m->bref, bref, sizeof(*bref))) {
813 /* delta contains cached delta */
814 accumulate_delta_stats(dstats, ds);
815 load_delta_stats(bstats, ds);
816 print_blockref_debug(stdout, depth,
817 index, &m->bref, "cache-hit");
824 bstats->total_blockref++;
825 dstats->total_blockref++;
827 switch (bref->type) {
828 case HAMMER2_BREF_TYPE_EMPTY:
830 bstats->total_empty++;
831 dstats->total_empty++;
833 bstats->total_blockref--;
834 dstats->total_blockref--;
837 case HAMMER2_BREF_TYPE_INODE:
838 bstats->volume.total_inode++;
839 dstats->volume.total_inode++;
841 case HAMMER2_BREF_TYPE_INDIRECT:
842 bstats->volume.total_indirect++;
843 dstats->volume.total_indirect++;
845 case HAMMER2_BREF_TYPE_DATA:
846 bstats->volume.total_data++;
847 dstats->volume.total_data++;
849 case HAMMER2_BREF_TYPE_DIRENT:
850 bstats->volume.total_dirent++;
851 dstats->volume.total_dirent++;
853 case HAMMER2_BREF_TYPE_FREEMAP_NODE:
854 bstats->freemap.total_freemap_node++;
855 dstats->freemap.total_freemap_node++;
857 case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
858 bstats->freemap.total_freemap_leaf++;
859 dstats->freemap.total_freemap_leaf++;
861 case HAMMER2_BREF_TYPE_VOLUME:
862 bstats->total_blockref--;
863 dstats->total_blockref--;
865 case HAMMER2_BREF_TYPE_FREEMAP:
866 bstats->total_blockref--;
867 dstats->total_blockref--;
870 snprintf(msg, sizeof(msg), "Invalid blockref type %d",
872 add_blockref_entry(&bstats->root, bref, msg, strlen(msg) + 1);
873 print_blockref_debug(stdout, depth, index, bref, msg);
878 switch (read_media(bref, &media, &bytes)) {
880 strlcpy(msg, "Bad I/O bytes", sizeof(msg));
881 add_blockref_entry(&bstats->root, bref, msg, strlen(msg) + 1);
882 print_blockref_debug(stdout, depth, index, bref, msg);
885 strlcpy(msg, "Failed to read media", sizeof(msg));
886 add_blockref_entry(&bstats->root, bref, msg, strlen(msg) + 1);
887 print_blockref_debug(stdout, depth, index, bref, msg);
893 if (bref->type != HAMMER2_BREF_TYPE_VOLUME &&
894 bref->type != HAMMER2_BREF_TYPE_FREEMAP) {
895 bstats->total_bytes += bytes;
896 dstats->total_bytes += bytes;
899 if (!CountEmpty && bref->type == HAMMER2_BREF_TYPE_EMPTY) {
901 bstats->total_bytes -= bytes;
902 dstats->total_bytes -= bytes;
905 if (!DebugOpt && QuietOpt <= 0 && (bstats->total_blockref % 100) == 0)
906 print_blockref_stats(bstats, false);
911 switch (HAMMER2_DEC_CHECK(bref->methods)) {
912 case HAMMER2_CHECK_ISCSI32:
913 cv = hammer2_icrc32(&media, bytes);
914 if (bref->check.iscsi32.value != cv) {
915 strlcpy(msg, "Bad HAMMER2_CHECK_ISCSI32", sizeof(msg));
916 add_blockref_entry(&bstats->root, bref, msg,
918 print_blockref_debug(stdout, depth, index, bref, msg);
922 case HAMMER2_CHECK_XXHASH64:
923 cv64 = XXH64(&media, bytes, XXH_HAMMER2_SEED);
924 if (bref->check.xxhash64.value != cv64) {
925 strlcpy(msg, "Bad HAMMER2_CHECK_XXHASH64", sizeof(msg));
926 add_blockref_entry(&bstats->root, bref, msg,
928 print_blockref_debug(stdout, depth, index, bref, msg);
932 case HAMMER2_CHECK_SHA192:
933 #ifdef HAMMER2_USE_OPENSSL
934 SHA256_Init(&hash_ctx);
935 SHA256_Update(&hash_ctx, &media, bytes);
936 SHA256_Final(u.digest, &hash_ctx);
937 u.digest64[2] ^= u.digest64[3];
938 if (memcmp(u.digest, bref->check.sha192.data,
939 sizeof(bref->check.sha192.data))) {
940 strlcpy(msg, "Bad HAMMER2_CHECK_SHA192", sizeof(msg));
941 add_blockref_entry(&bstats->root, bref, msg,
943 print_blockref_debug(stdout, depth, index, bref, msg);
948 case HAMMER2_CHECK_FREEMAP:
949 cv = hammer2_icrc32(&media, bytes);
950 if (bref->check.freemap.icrc32 != cv) {
951 strlcpy(msg, "Bad HAMMER2_CHECK_FREEMAP", sizeof(msg));
952 add_blockref_entry(&bstats->root, bref, msg,
954 print_blockref_debug(stdout, depth, index, bref, msg);
960 switch (bref->type) {
961 case HAMMER2_BREF_TYPE_INODE:
962 if (!(media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA)) {
963 bscan = &media.ipdata.u.blockset.blockref[0];
964 bcount = HAMMER2_SET_COUNT;
970 case HAMMER2_BREF_TYPE_INDIRECT:
971 bscan = &media.npdata[0];
972 bcount = bytes / sizeof(hammer2_blockref_t);
974 case HAMMER2_BREF_TYPE_FREEMAP_NODE:
975 bscan = &media.npdata[0];
976 bcount = bytes / sizeof(hammer2_blockref_t);
978 case HAMMER2_BREF_TYPE_VOLUME:
979 bscan = &media.voldata.sroot_blockset.blockref[0];
980 bcount = HAMMER2_SET_COUNT;
982 case HAMMER2_BREF_TYPE_FREEMAP:
983 bscan = &media.voldata.freemap_blockset.blockref[0];
984 bcount = HAMMER2_SET_COUNT;
995 * If failed, no recurse, but still verify its direct children.
996 * Beyond that is probably garbage.
998 for (i = 0; norecurse == false && i < bcount; ++i) {
1000 memset(&ds, 0, sizeof(ds));
1001 if (verify_blockref(voldata, &bscan[i], failed, bstats, droot,
1002 &ds, depth + 1, i) == -1)
1005 accumulate_delta_stats(dstats, &ds);
1012 if (bref->data_off && BlockrefCacheCount > 0 &&
1013 dstats->count >= BlockrefCacheCount) {
1015 add_blockref_entry(droot, bref, dstats, sizeof(*dstats));
1016 print_blockref_debug(stdout, depth, index, bref, "cache-add");
1023 print_pfs(const hammer2_inode_data_t *ipdata)
1025 const hammer2_inode_meta_t *meta = &ipdata->meta;
1026 char *f, *pfs_id_str = NULL;
1027 const char *type_str;
1030 f = get_inode_filename(ipdata);
1031 uuid = meta->pfs_clid;
1032 hammer2_uuid_to_str(&uuid, &pfs_id_str);
1033 if (meta->pfs_type == HAMMER2_PFSTYPE_MASTER) {
1034 if (meta->pfs_subtype == HAMMER2_PFSSUBTYPE_NONE)
1035 type_str = "MASTER";
1037 type_str = hammer2_pfssubtype_to_str(meta->pfs_subtype);
1039 type_str = hammer2_pfstype_to_str(meta->pfs_type);
1041 tfprintf(stdout, 1, "%-11s %s %s\n", type_str, pfs_id_str, f);
1048 get_inode_filename(const hammer2_inode_data_t *ipdata)
1050 char *p = malloc(HAMMER2_INODE_MAXNAME + 1);
1052 memcpy(p, ipdata->filename, sizeof(ipdata->filename));
1053 p[HAMMER2_INODE_MAXNAME] = '\0';
1059 __add_pfs_blockref(const hammer2_blockref_t *bref, struct blockref_list *blist,
1060 const hammer2_inode_data_t *ipdata)
1062 struct blockref_msg *newp, *p;
1064 newp = calloc(1, sizeof(*newp));
1066 newp->msg = calloc(1, sizeof(*ipdata));
1067 memcpy(newp->msg, ipdata, sizeof(*ipdata));
1069 p = TAILQ_FIRST(blist);
1071 char *f1 = get_inode_filename(newp->msg);
1072 char *f2 = get_inode_filename(p->msg);
1073 if (strcmp(f1, f2) <= 0) {
1074 TAILQ_INSERT_BEFORE(p, newp, entry);
1079 p = TAILQ_NEXT(p, entry);
1084 TAILQ_INSERT_TAIL(blist, newp, entry);
1088 init_pfs_blockref(const hammer2_volume_data_t *voldata,
1089 const hammer2_blockref_t *bref, struct blockref_list *blist)
1091 hammer2_media_data_t media;
1092 hammer2_inode_data_t ipdata;
1093 hammer2_blockref_t *bscan;
1097 if (read_media(bref, &media, &bytes))
1102 switch (bref->type) {
1103 case HAMMER2_BREF_TYPE_INODE:
1104 ipdata = media.ipdata;
1105 if (ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
1106 bscan = &ipdata.u.blockset.blockref[0];
1107 bcount = HAMMER2_SET_COUNT;
1111 if (ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT)
1112 __add_pfs_blockref(bref, blist, &ipdata);
1114 assert(0); /* should only see SUPROOT or PFS */
1117 case HAMMER2_BREF_TYPE_INDIRECT:
1118 bscan = &media.npdata[0];
1119 bcount = bytes / sizeof(hammer2_blockref_t);
1121 case HAMMER2_BREF_TYPE_VOLUME:
1122 bscan = &media.voldata.sroot_blockset.blockref[0];
1123 bcount = HAMMER2_SET_COUNT;
1131 for (i = 0; i < bcount; ++i)
1132 if (init_pfs_blockref(voldata, &bscan[i], blist) == -1)
1138 cleanup_pfs_blockref(struct blockref_list *blist)
1140 cleanup_blockref_msg(blist);
1144 print_media(FILE *fp, int tab, const hammer2_blockref_t *bref,
1145 const hammer2_media_data_t *media, size_t media_bytes)
1147 const hammer2_blockref_t *bscan;
1148 const hammer2_inode_data_t *ipdata;
1149 int i, bcount, namelen;
1153 switch (bref->type) {
1154 case HAMMER2_BREF_TYPE_INODE:
1155 ipdata = &media->ipdata;
1156 namelen = ipdata->meta.name_len;
1157 if (namelen > HAMMER2_INODE_MAXNAME)
1159 tfprintf(fp, tab, "filename \"%*.*s\"\n", namelen, namelen,
1161 tfprintf(fp, tab, "version %d\n", ipdata->meta.version);
1162 if ((ipdata->meta.op_flags & HAMMER2_OPFLAG_PFSROOT) ||
1163 ipdata->meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT)
1164 tfprintf(fp, tab, "pfs_subtype %d (%s)\n",
1165 ipdata->meta.pfs_subtype,
1166 hammer2_pfssubtype_to_str(ipdata->meta.pfs_subtype));
1167 tfprintf(fp, tab, "uflags 0x%08x\n", ipdata->meta.uflags);
1168 if (ipdata->meta.rmajor || ipdata->meta.rminor) {
1169 tfprintf(fp, tab, "rmajor %d\n", ipdata->meta.rmajor);
1170 tfprintf(fp, tab, "rminor %d\n", ipdata->meta.rminor);
1172 tfprintf(fp, tab, "ctime %s\n",
1173 hammer2_time64_to_str(ipdata->meta.ctime, &str));
1174 tfprintf(fp, tab, "mtime %s\n",
1175 hammer2_time64_to_str(ipdata->meta.mtime, &str));
1176 tfprintf(fp, tab, "atime %s\n",
1177 hammer2_time64_to_str(ipdata->meta.atime, &str));
1178 tfprintf(fp, tab, "btime %s\n",
1179 hammer2_time64_to_str(ipdata->meta.btime, &str));
1180 uuid = ipdata->meta.uid;
1181 tfprintf(fp, tab, "uid %s\n", hammer2_uuid_to_str(&uuid, &str));
1182 uuid = ipdata->meta.gid;
1183 tfprintf(fp, tab, "gid %s\n", hammer2_uuid_to_str(&uuid, &str));
1184 tfprintf(fp, tab, "type %s\n",
1185 hammer2_iptype_to_str(ipdata->meta.type));
1186 tfprintf(fp, tab, "op_flags 0x%02x\n", ipdata->meta.op_flags);
1187 tfprintf(fp, tab, "cap_flags 0x%04x\n", ipdata->meta.cap_flags);
1188 tfprintf(fp, tab, "mode %-7o\n", ipdata->meta.mode);
1189 tfprintf(fp, tab, "inum 0x%016jx\n", ipdata->meta.inum);
1190 tfprintf(fp, tab, "size %ju ", (uintmax_t)ipdata->meta.size);
1191 if (ipdata->meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA &&
1192 ipdata->meta.size <= HAMMER2_EMBEDDED_BYTES)
1193 printf("(embedded data)\n");
1196 tfprintf(fp, tab, "nlinks %ju\n",
1197 (uintmax_t)ipdata->meta.nlinks);
1198 tfprintf(fp, tab, "iparent 0x%016jx\n",
1199 (uintmax_t)ipdata->meta.iparent);
1200 tfprintf(fp, tab, "name_key 0x%016jx\n",
1201 (uintmax_t)ipdata->meta.name_key);
1202 tfprintf(fp, tab, "name_len %u\n", ipdata->meta.name_len);
1203 tfprintf(fp, tab, "ncopies %u\n", ipdata->meta.ncopies);
1204 tfprintf(fp, tab, "comp_algo %u\n", ipdata->meta.comp_algo);
1205 tfprintf(fp, tab, "target_type %u\n", ipdata->meta.target_type);
1206 tfprintf(fp, tab, "check_algo %u\n", ipdata->meta.check_algo);
1207 if ((ipdata->meta.op_flags & HAMMER2_OPFLAG_PFSROOT) ||
1208 ipdata->meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
1209 tfprintf(fp, tab, "pfs_nmasters %u\n",
1210 ipdata->meta.pfs_nmasters);
1211 tfprintf(fp, tab, "pfs_type %u (%s)\n",
1212 ipdata->meta.pfs_type,
1213 hammer2_pfstype_to_str(ipdata->meta.pfs_type));
1214 tfprintf(fp, tab, "pfs_inum 0x%016jx\n",
1215 (uintmax_t)ipdata->meta.pfs_inum);
1216 uuid = ipdata->meta.pfs_clid;
1217 tfprintf(fp, tab, "pfs_clid %s\n",
1218 hammer2_uuid_to_str(&uuid, &str));
1219 uuid = ipdata->meta.pfs_fsid;
1220 tfprintf(fp, tab, "pfs_fsid %s\n",
1221 hammer2_uuid_to_str(&uuid, &str));
1222 tfprintf(fp, tab, "pfs_lsnap_tid 0x%016jx\n",
1223 (uintmax_t)ipdata->meta.pfs_lsnap_tid);
1225 tfprintf(fp, tab, "data_quota %ju\n",
1226 (uintmax_t)ipdata->meta.data_quota);
1227 tfprintf(fp, tab, "data_count %ju\n",
1228 (uintmax_t)bref->embed.stats.data_count);
1229 tfprintf(fp, tab, "inode_quota %ju\n",
1230 (uintmax_t)ipdata->meta.inode_quota);
1231 tfprintf(fp, tab, "inode_count %ju\n",
1232 (uintmax_t)bref->embed.stats.inode_count);
1234 case HAMMER2_BREF_TYPE_INDIRECT:
1235 bcount = media_bytes / sizeof(hammer2_blockref_t);
1236 for (i = 0; i < bcount; ++i) {
1237 bscan = &media->npdata[i];
1238 tfprintf(fp, tab, "%3d %016jx %-12s %016jx/%-2d\n",
1239 i, (uintmax_t)bscan->data_off,
1240 hammer2_breftype_to_str(bscan->type),
1241 (uintmax_t)bscan->key,
1245 case HAMMER2_BREF_TYPE_DIRENT:
1246 if (bref->embed.dirent.namlen <= sizeof(bref->check.buf)) {
1247 tfprintf(fp, tab, "filename \"%*.*s\"\n",
1248 bref->embed.dirent.namlen,
1249 bref->embed.dirent.namlen,
1252 tfprintf(fp, tab, "filename \"%*.*s\"\n",
1253 bref->embed.dirent.namlen,
1254 bref->embed.dirent.namlen,
1257 tfprintf(fp, tab, "inum 0x%016jx\n",
1258 (uintmax_t)bref->embed.dirent.inum);
1259 tfprintf(fp, tab, "namlen %d\n",
1260 (uintmax_t)bref->embed.dirent.namlen);
1261 tfprintf(fp, tab, "type %s\n",
1262 hammer2_iptype_to_str(bref->embed.dirent.type));
1264 case HAMMER2_BREF_TYPE_FREEMAP_NODE:
1265 bcount = media_bytes / sizeof(hammer2_blockref_t);
1266 for (i = 0; i < bcount; ++i) {
1267 bscan = &media->npdata[i];
1268 tfprintf(fp, tab, "%3d %016jx %-12s %016jx/%-2d\n",
1269 i, (uintmax_t)bscan->data_off,
1270 hammer2_breftype_to_str(bscan->type),
1271 (uintmax_t)bscan->key,
1275 case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
1276 for (i = 0; i < HAMMER2_FREEMAP_COUNT; ++i) {
1277 hammer2_off_t data_off = bref->key +
1278 i * HAMMER2_FREEMAP_LEVEL0_SIZE;
1279 #if HAMMER2_BMAP_ELEMENTS != 8
1280 #error "HAMMER2_BMAP_ELEMENTS != 8"
1282 tfprintf(fp, tab, "%016jx %04d.%04x (avail=%7d) "
1283 "%016jx %016jx %016jx %016jx "
1284 "%016jx %016jx %016jx %016jx\n",
1285 data_off, i, media->bmdata[i].class,
1286 media->bmdata[i].avail,
1287 media->bmdata[i].bitmapq[0],
1288 media->bmdata[i].bitmapq[1],
1289 media->bmdata[i].bitmapq[2],
1290 media->bmdata[i].bitmapq[3],
1291 media->bmdata[i].bitmapq[4],
1292 media->bmdata[i].bitmapq[5],
1293 media->bmdata[i].bitmapq[6],
1294 media->bmdata[i].bitmapq[7]);
1305 test_hammer2(const char *devpath)
1307 bool failed = false;
1309 hammer2_init_volumes(devpath, 1);
1311 best_zone = find_best_zone();
1312 if (best_zone == -1)
1313 fprintf(stderr, "Failed to find best zone\n");
1316 if (test_pfs_blockref() == -1)
1318 goto end; /* print PFS info and exit */
1321 printf("volume header\n");
1322 if (test_volume_header() == -1) {
1328 printf("freemap\n");
1329 if (test_blockref(HAMMER2_BREF_TYPE_FREEMAP) == -1) {
1336 if (test_blockref(HAMMER2_BREF_TYPE_VOLUME) == -1) {
1342 if (test_pfs_blockref() == -1) {
1349 hammer2_cleanup_volumes();
1351 return failed ? -1 : 0;