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>
51 #include <openssl/sha.h>
53 #include <vfs/hammer2/hammer2_disk.h>
54 #include <vfs/hammer2/hammer2_xxhash.h>
56 #include "hammer2_subs.h"
57 #include "fsck_hammer2.h"
60 TAILQ_ENTRY(blockref_msg) entry;
61 hammer2_blockref_t bref;
65 TAILQ_HEAD(blockref_list, blockref_msg);
67 struct blockref_entry {
68 RB_ENTRY(blockref_entry) entry;
69 hammer2_off_t data_off;
70 struct blockref_list head;
74 blockref_cmp(struct blockref_entry *b1, struct blockref_entry *b2)
76 if (b1->data_off < b2->data_off)
78 if (b1->data_off > b2->data_off)
83 RB_HEAD(blockref_tree, blockref_entry);
84 RB_PROTOTYPE2(blockref_tree, blockref_entry, entry, blockref_cmp,
86 RB_GENERATE2(blockref_tree, blockref_entry, entry, blockref_cmp, hammer2_off_t,
90 struct blockref_tree root;
91 uint8_t type; /* HAMMER2_BREF_TYPE_VOLUME or FREEMAP */
92 uint64_t total_blockref;
96 /* use volume or freemap depending on type value */
99 uint64_t total_indirect;
101 uint64_t total_dirent;
104 uint64_t total_freemap_node;
105 uint64_t total_freemap_leaf;
111 uint64_t total_blockref;
112 uint64_t total_empty;
113 uint64_t total_bytes;
115 uint64_t total_inode;
116 uint64_t total_indirect;
118 uint64_t total_dirent;
121 uint64_t total_freemap_node;
122 uint64_t total_freemap_leaf;
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 *,
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 *);
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);
145 static int best_zone = -1;
150 tfprintf(FILE *fp, int tab, const char *ctl, ...)
155 ret = fprintf(fp, "%*s", tab * TAB, "");
160 vfprintf(fp, ctl, va);
165 tsnprintf(char *str, size_t siz, int tab, const char *ctl, ...)
170 ret = snprintf(str, siz, "%*s", tab * TAB, "");
171 if (ret < 0 || ret >= (int)siz)
175 vsnprintf(str + ret, siz, ctl, va);
180 tprintf_zone(int tab, int i, const hammer2_blockref_t *bref)
182 tfprintf(stdout, tab, "zone.%d %016jx%s\n",
183 i, (uintmax_t)bref->data_off,
184 (!ScanBest && i == best_zone) ? " (best)" : "");
188 init_root_blockref(int fd, int i, uint8_t type, hammer2_blockref_t *bref)
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));
195 bref->data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
197 return lseek(fd, bref->data_off & ~HAMMER2_OFF_MASK_RADIX, SEEK_SET);
201 find_best_zone(int fd)
203 hammer2_blockref_t best;
206 memset(&best, 0, sizeof(best));
208 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
209 hammer2_volume_data_t voldata;
210 hammer2_blockref_t broot;
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))
219 broot.mirror_tid = voldata.mirror_tid;
220 if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
224 } else if (ret == -1) {
228 tfprintf(stderr, 1, "Failed to read volume header\n");
237 test_volume_header(int fd)
242 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
243 hammer2_volume_data_t voldata;
244 hammer2_blockref_t broot;
247 if (ScanBest && i != best_zone)
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)
255 } else if (ret == -1) {
259 tfprintf(stderr, 1, "Failed to read volume header\n");
264 return failed ? -1 : 0;
268 test_blockref(int fd, uint8_t type)
270 struct blockref_tree droot;
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;
280 if (ScanBest && i != best_zone)
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);
288 memset(&ds, 0, sizeof(ds));
289 tprintf_zone(0, i, &broot);
290 if (verify_blockref(fd, &voldata, &broot, false,
291 &bstats, &droot, &ds) == -1)
293 print_blockref_stats(&bstats, true);
294 print_blockref_entry(fd, &bstats.root);
295 cleanup_blockref_stats(&bstats);
296 } else if (ret == -1) {
301 tfprintf(stderr, 1, "Failed to read volume header\n");
307 cleanup_delta_root(&droot);
308 return failed ? -1 : 0;
312 test_pfs_blockref(int fd)
314 struct blockref_tree droot;
315 uint8_t type = HAMMER2_BREF_TYPE_VOLUME;
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;
325 if (ScanBest && i != best_zone)
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;
334 tprintf_zone(0, i, &broot);
336 if (init_pfs_blockref(fd, &voldata, &broot, &blist) ==
338 tfprintf(stderr, 1, "Failed to read PFS "
343 if (TAILQ_EMPTY(&blist)) {
344 tfprintf(stderr, 1, "Failed to find PFS "
349 TAILQ_FOREACH(p, &blist, entry) {
350 blockref_stats_t bstats;
354 for (j = 0; j < NumPFSNames; j++)
355 if (!strcmp(PFSNames[j],
363 tfprintf(stdout, 1, "%s\n", p->msg);
364 init_blockref_stats(&bstats, type);
366 memset(&ds, 0, sizeof(ds));
367 if (verify_blockref(fd, &voldata, &p->bref,
368 false, &bstats, &droot, &ds) == -1)
370 print_blockref_stats(&bstats, true);
371 print_blockref_entry(fd, &bstats.root);
372 cleanup_blockref_stats(&bstats);
374 cleanup_pfs_blockref(&blist);
375 if (NumPFSNames && !count) {
376 tfprintf(stderr, 1, "PFS not found\n");
379 } else if (ret == -1) {
384 tfprintf(stderr, 1, "Failed to read volume header\n");
390 cleanup_delta_root(&droot);
391 return failed ? -1 : 0;
402 if (ioctl(0, TIOCGWINSZ, &ws) != -1)
404 if (columns == 0 && (cp = getenv("COLUMNS")))
407 columns = 80; /* last resort */
413 cleanup_blockref_msg(struct blockref_list *head)
415 struct blockref_msg *p;
417 while ((p = TAILQ_FIRST(head)) != NULL) {
418 TAILQ_REMOVE(head, p, entry);
422 assert(TAILQ_EMPTY(head));
426 cleanup_blockref_entry(struct blockref_tree *root)
428 struct blockref_entry *e;
430 while ((e = RB_ROOT(root)) != NULL) {
431 RB_REMOVE(blockref_tree, root, e);
432 cleanup_blockref_msg(&e->head);
435 assert(RB_EMPTY(root));
439 add_blockref_msg(struct blockref_list *head, const hammer2_blockref_t *bref,
440 const void *msg, size_t siz)
442 struct blockref_msg *m;
445 m = calloc(1, sizeof(*m));
453 TAILQ_INSERT_TAIL(head, m, entry);
457 add_blockref_entry(struct blockref_tree *root, const hammer2_blockref_t *bref,
458 const void *msg, size_t siz)
460 struct blockref_entry *e;
462 e = RB_LOOKUP(blockref_tree, root, bref->data_off);
464 e = calloc(1, sizeof(*e));
466 TAILQ_INIT(&e->head);
467 e->data_off = bref->data_off;
470 add_blockref_msg(&e->head, bref, msg, siz);
472 RB_INSERT(blockref_tree, root, e);
476 print_blockref(FILE *fp, const hammer2_blockref_t *bref, const char *msg)
478 tfprintf(fp, 1, "%016jx %-12s %016jx/%-2d \"%s\"\n",
479 (uintmax_t)bref->data_off,
480 hammer2_breftype_to_str(bref->type),
481 (uintmax_t)bref->key,
487 print_blockref_msg(int fd, struct blockref_list *head)
489 struct blockref_msg *m;
491 TAILQ_FOREACH(m, head, entry) {
492 hammer2_blockref_t *bref = &m->bref;
493 print_blockref(stderr, bref, m->msg);
494 if (fd != -1 && VerboseOpt > 0) {
495 hammer2_media_data_t media;
497 if (!read_media(fd, bref, &media, &bytes))
498 print_media(stderr, 2, bref, &media, bytes);
500 tfprintf(stderr, 2, "Failed to read media\n");
506 print_blockref_entry(int fd, struct blockref_tree *root)
508 struct blockref_entry *e;
510 RB_FOREACH(e, blockref_tree, root)
511 print_blockref_msg(fd, &e->head);
515 init_blockref_stats(blockref_stats_t *bstats, uint8_t type)
517 memset(bstats, 0, sizeof(*bstats));
518 RB_INIT(&bstats->root);
523 cleanup_blockref_stats(blockref_stats_t *bstats)
525 cleanup_blockref_entry(&bstats->root);
529 init_delta_root(struct blockref_tree *droot)
535 cleanup_delta_root(struct blockref_tree *droot)
537 cleanup_blockref_entry(droot);
541 print_blockref_stats(const blockref_stats_t *bstats, bool newline)
543 size_t siz = charsperline();
544 char *buf = calloc(1, siz);
550 snprintf(emptybuf, sizeof(emptybuf), ", %ju empty",
551 (uintmax_t)bstats->total_empty);
553 strlcpy(emptybuf, "", sizeof(emptybuf));
555 switch (bstats->type) {
556 case HAMMER2_BREF_TYPE_VOLUME:
557 tsnprintf(buf, siz, 1, "%ju blockref (%ju inode, %ju indirect, "
558 "%ju data, %ju dirent%s), %s",
559 (uintmax_t)bstats->total_blockref,
560 (uintmax_t)bstats->volume.total_inode,
561 (uintmax_t)bstats->volume.total_indirect,
562 (uintmax_t)bstats->volume.total_data,
563 (uintmax_t)bstats->volume.total_dirent,
565 sizetostr(bstats->total_bytes));
567 case HAMMER2_BREF_TYPE_FREEMAP:
568 tsnprintf(buf, siz, 1, "%ju blockref (%ju node, %ju leaf%s), "
570 (uintmax_t)bstats->total_blockref,
571 (uintmax_t)bstats->freemap.total_freemap_node,
572 (uintmax_t)bstats->freemap.total_freemap_leaf,
574 sizetostr(bstats->total_bytes));
591 verify_volume_header(const hammer2_volume_data_t *voldata)
593 hammer2_crc32_t crc0, crc1;
594 const char *p = (const char*)voldata;
596 if ((voldata->magic != HAMMER2_VOLUME_ID_HBO) &&
597 (voldata->magic != HAMMER2_VOLUME_ID_ABO)) {
598 tfprintf(stderr, 1, "Bad magic %jX\n", voldata->magic);
602 if (voldata->magic == HAMMER2_VOLUME_ID_ABO)
603 tfprintf(stderr, 1, "Reverse endian\n");
605 crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT0];
606 crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC0_OFF,
607 HAMMER2_VOLUME_ICRC0_SIZE);
609 tfprintf(stderr, 1, "Bad HAMMER2_VOL_ICRC_SECT0 CRC\n");
613 crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT1];
614 crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC1_OFF,
615 HAMMER2_VOLUME_ICRC1_SIZE);
617 tfprintf(stderr, 1, "Bad HAMMER2_VOL_ICRC_SECT1 CRC\n");
621 crc0 = voldata->icrc_volheader;
622 crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRCVH_OFF,
623 HAMMER2_VOLUME_ICRCVH_SIZE);
625 tfprintf(stderr, 1, "Bad volume header CRC\n");
633 read_media(int fd, const hammer2_blockref_t *bref, hammer2_media_data_t *media,
636 hammer2_off_t io_off, io_base;
637 size_t bytes, io_bytes, boff;
639 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
641 bytes = (size_t)1 << bytes;
643 *media_bytes = bytes;
648 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
649 io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1);
650 boff = io_off - io_base;
652 io_bytes = HAMMER2_MINIOSIZE;
653 while (io_bytes + boff < bytes)
656 if (io_bytes > sizeof(*media))
658 if (lseek(fd, io_base, SEEK_SET) == -1)
660 if (read(fd, media, io_bytes) != (ssize_t)io_bytes)
663 memmove(media, (char *)media + boff, bytes);
669 load_delta_stats(blockref_stats_t *bstats, const delta_stats_t *dstats)
671 bstats->total_blockref += dstats->total_blockref;
672 bstats->total_empty += dstats->total_empty;
673 bstats->total_bytes += dstats->total_bytes;
675 switch (bstats->type) {
676 case HAMMER2_BREF_TYPE_VOLUME:
677 bstats->volume.total_inode += dstats->volume.total_inode;
678 bstats->volume.total_indirect += dstats->volume.total_indirect;
679 bstats->volume.total_data += dstats->volume.total_data;
680 bstats->volume.total_dirent += dstats->volume.total_dirent;
682 case HAMMER2_BREF_TYPE_FREEMAP:
683 bstats->freemap.total_freemap_node +=
684 dstats->freemap.total_freemap_node;
685 bstats->freemap.total_freemap_leaf +=
686 dstats->freemap.total_freemap_leaf;
695 accumulate_delta_stats(delta_stats_t *dst, const delta_stats_t *src)
697 dst->total_blockref += src->total_blockref;
698 dst->total_empty += src->total_empty;
699 dst->total_bytes += src->total_bytes;
701 dst->volume.total_inode += src->volume.total_inode;
702 dst->volume.total_indirect += src->volume.total_indirect;
703 dst->volume.total_data += src->volume.total_data;
704 dst->volume.total_dirent += src->volume.total_dirent;
706 dst->freemap.total_freemap_node += src->freemap.total_freemap_node;
707 dst->freemap.total_freemap_leaf += src->freemap.total_freemap_leaf;
709 dst->count += src->count;
713 verify_blockref(int fd, const hammer2_volume_data_t *voldata,
714 const hammer2_blockref_t *bref, bool norecurse, blockref_stats_t *bstats,
715 struct blockref_tree *droot, delta_stats_t *dstats)
717 hammer2_media_data_t media;
718 hammer2_blockref_t *bscan;
728 uint8_t digest[SHA256_DIGEST_LENGTH];
729 uint64_t digest64[SHA256_DIGEST_LENGTH/8];
732 if (bref->data_off) {
733 struct blockref_entry *e;
734 e = RB_LOOKUP(blockref_tree, droot, bref->data_off);
736 struct blockref_msg *m;
737 TAILQ_FOREACH(m, &e->head, entry) {
738 delta_stats_t *ds = m->msg;
739 if (!memcmp(&m->bref, bref, sizeof(*bref))) {
741 print_blockref(stdout, &m->bref,
743 /* delta contains cached delta */
744 accumulate_delta_stats(dstats, ds);
745 load_delta_stats(bstats, ds);
752 bstats->total_blockref++;
753 dstats->total_blockref++;
755 switch (bref->type) {
756 case HAMMER2_BREF_TYPE_EMPTY:
758 bstats->total_empty++;
759 dstats->total_empty++;
761 bstats->total_blockref--;
762 dstats->total_blockref--;
765 case HAMMER2_BREF_TYPE_INODE:
766 bstats->volume.total_inode++;
767 dstats->volume.total_inode++;
769 case HAMMER2_BREF_TYPE_INDIRECT:
770 bstats->volume.total_indirect++;
771 dstats->volume.total_indirect++;
773 case HAMMER2_BREF_TYPE_DATA:
774 bstats->volume.total_data++;
775 dstats->volume.total_data++;
777 case HAMMER2_BREF_TYPE_DIRENT:
778 bstats->volume.total_dirent++;
779 dstats->volume.total_dirent++;
781 case HAMMER2_BREF_TYPE_FREEMAP_NODE:
782 bstats->freemap.total_freemap_node++;
783 dstats->freemap.total_freemap_node++;
785 case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
786 bstats->freemap.total_freemap_leaf++;
787 dstats->freemap.total_freemap_leaf++;
789 case HAMMER2_BREF_TYPE_VOLUME:
790 bstats->total_blockref--;
791 dstats->total_blockref--;
793 case HAMMER2_BREF_TYPE_FREEMAP:
794 bstats->total_blockref--;
795 dstats->total_blockref--;
798 snprintf(msg, sizeof(msg), "Invalid blockref type %d",
800 add_blockref_entry(&bstats->root, bref, msg, strlen(msg) + 1);
805 switch (read_media(fd, bref, &media, &bytes)) {
807 strlcpy(msg, "Bad I/O bytes", sizeof(msg));
808 add_blockref_entry(&bstats->root, bref, msg, strlen(msg) + 1);
811 strlcpy(msg, "Failed to read media", sizeof(msg));
812 add_blockref_entry(&bstats->root, bref, msg, strlen(msg) + 1);
818 if (bref->type != HAMMER2_BREF_TYPE_VOLUME &&
819 bref->type != HAMMER2_BREF_TYPE_FREEMAP) {
820 bstats->total_bytes += bytes;
821 dstats->total_bytes += bytes;
824 if (!CountEmpty && bref->type == HAMMER2_BREF_TYPE_EMPTY) {
826 bstats->total_bytes -= bytes;
827 dstats->total_bytes -= bytes;
830 if (!DebugOpt && QuietOpt <= 0 && (bstats->total_blockref % 100) == 0)
831 print_blockref_stats(bstats, false);
836 switch (HAMMER2_DEC_CHECK(bref->methods)) {
837 case HAMMER2_CHECK_ISCSI32:
838 cv = hammer2_icrc32(&media, bytes);
839 if (bref->check.iscsi32.value != cv) {
840 strlcpy(msg, "Bad HAMMER2_CHECK_ISCSI32", sizeof(msg));
841 add_blockref_entry(&bstats->root, bref, msg,
846 case HAMMER2_CHECK_XXHASH64:
847 cv64 = XXH64(&media, bytes, XXH_HAMMER2_SEED);
848 if (bref->check.xxhash64.value != cv64) {
849 strlcpy(msg, "Bad HAMMER2_CHECK_XXHASH64", sizeof(msg));
850 add_blockref_entry(&bstats->root, bref, msg,
855 case HAMMER2_CHECK_SHA192:
856 SHA256_Init(&hash_ctx);
857 SHA256_Update(&hash_ctx, &media, bytes);
858 SHA256_Final(u.digest, &hash_ctx);
859 u.digest64[2] ^= u.digest64[3];
860 if (memcmp(u.digest, bref->check.sha192.data,
861 sizeof(bref->check.sha192.data))) {
862 strlcpy(msg, "Bad HAMMER2_CHECK_SHA192", sizeof(msg));
863 add_blockref_entry(&bstats->root, bref, msg,
868 case HAMMER2_CHECK_FREEMAP:
869 cv = hammer2_icrc32(&media, bytes);
870 if (bref->check.freemap.icrc32 != cv) {
871 strlcpy(msg, "Bad HAMMER2_CHECK_FREEMAP", sizeof(msg));
872 add_blockref_entry(&bstats->root, bref, msg,
879 switch (bref->type) {
880 case HAMMER2_BREF_TYPE_INODE:
881 if (!(media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA)) {
882 bscan = &media.ipdata.u.blockset.blockref[0];
883 bcount = HAMMER2_SET_COUNT;
889 case HAMMER2_BREF_TYPE_INDIRECT:
890 bscan = &media.npdata[0];
891 bcount = bytes / sizeof(hammer2_blockref_t);
893 case HAMMER2_BREF_TYPE_FREEMAP_NODE:
894 bscan = &media.npdata[0];
895 bcount = bytes / sizeof(hammer2_blockref_t);
897 case HAMMER2_BREF_TYPE_VOLUME:
898 bscan = &media.voldata.sroot_blockset.blockref[0];
899 bcount = HAMMER2_SET_COUNT;
901 case HAMMER2_BREF_TYPE_FREEMAP:
902 bscan = &media.voldata.freemap_blockset.blockref[0];
903 bcount = HAMMER2_SET_COUNT;
914 * If failed, no recurse, but still verify its direct children.
915 * Beyond that is probably garbage.
917 for (i = 0; norecurse == false && i < bcount; ++i) {
919 memset(&ds, 0, sizeof(ds));
920 if (verify_blockref(fd, voldata, &bscan[i], failed, bstats,
924 accumulate_delta_stats(dstats, &ds);
931 if (bref->data_off && BlockrefCacheCount > 0 &&
932 dstats->count >= BlockrefCacheCount) {
934 add_blockref_entry(droot, bref, dstats, sizeof(*dstats));
941 init_pfs_blockref(int fd, const hammer2_volume_data_t *voldata,
942 const hammer2_blockref_t *bref, struct blockref_list *blist)
944 hammer2_media_data_t media;
945 hammer2_inode_data_t ipdata;
946 hammer2_blockref_t *bscan;
950 if (read_media(fd, bref, &media, &bytes))
955 switch (bref->type) {
956 case HAMMER2_BREF_TYPE_INODE:
957 ipdata = media.ipdata;
958 if (ipdata.meta.pfs_type & HAMMER2_PFSTYPE_SUPROOT) {
959 bscan = &ipdata.u.blockset.blockref[0];
960 bcount = HAMMER2_SET_COUNT;
964 if (ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) {
965 struct blockref_msg *newp, *p;
966 newp = calloc(1, sizeof(*newp));
969 newp->msg = calloc(1,
970 sizeof(ipdata.filename) + 1);
971 memcpy(newp->msg, ipdata.filename,
972 sizeof(ipdata.filename));
973 p = TAILQ_FIRST(blist);
975 if (strcmp(newp->msg, p->msg) <= 0) {
976 TAILQ_INSERT_BEFORE(p, newp,
980 p = TAILQ_NEXT(p, entry);
983 TAILQ_INSERT_TAIL(blist, newp, entry);
985 assert(0); /* should only see SUPROOT or PFS */
988 case HAMMER2_BREF_TYPE_INDIRECT:
989 bscan = &media.npdata[0];
990 bcount = bytes / sizeof(hammer2_blockref_t);
992 case HAMMER2_BREF_TYPE_VOLUME:
993 bscan = &media.voldata.sroot_blockset.blockref[0];
994 bcount = HAMMER2_SET_COUNT;
1002 for (i = 0; i < bcount; ++i)
1003 if (init_pfs_blockref(fd, voldata, &bscan[i], blist) == -1)
1009 cleanup_pfs_blockref(struct blockref_list *blist)
1011 cleanup_blockref_msg(blist);
1015 print_media(FILE *fp, int tab, const hammer2_blockref_t *bref,
1016 hammer2_media_data_t *media, size_t media_bytes)
1018 hammer2_blockref_t *bscan;
1019 hammer2_inode_data_t *ipdata;
1020 int i, bcount, namelen;
1023 switch (bref->type) {
1024 case HAMMER2_BREF_TYPE_INODE:
1025 ipdata = &media->ipdata;
1026 namelen = ipdata->meta.name_len;
1027 if (namelen > HAMMER2_INODE_MAXNAME)
1029 tfprintf(fp, tab, "filename \"%*.*s\"\n", namelen, namelen,
1031 tfprintf(fp, tab, "version %d\n", ipdata->meta.version);
1032 tfprintf(fp, tab, "pfs_subtype %d\n", ipdata->meta.pfs_subtype);
1033 tfprintf(fp, tab, "uflags 0x%08x\n", ipdata->meta.uflags);
1034 if (ipdata->meta.rmajor || ipdata->meta.rminor) {
1035 tfprintf(fp, tab, "rmajor %d\n", ipdata->meta.rmajor);
1036 tfprintf(fp, tab, "rminor %d\n", ipdata->meta.rminor);
1038 tfprintf(fp, tab, "ctime %s\n",
1039 hammer2_time64_to_str(ipdata->meta.ctime, &str));
1040 tfprintf(fp, tab, "mtime %s\n",
1041 hammer2_time64_to_str(ipdata->meta.mtime, &str));
1042 tfprintf(fp, tab, "atime %s\n",
1043 hammer2_time64_to_str(ipdata->meta.atime, &str));
1044 tfprintf(fp, tab, "btime %s\n",
1045 hammer2_time64_to_str(ipdata->meta.btime, &str));
1046 tfprintf(fp, tab, "uid %s\n",
1047 hammer2_uuid_to_str(&ipdata->meta.uid, &str));
1048 tfprintf(fp, tab, "gid %s\n",
1049 hammer2_uuid_to_str(&ipdata->meta.gid, &str));
1050 tfprintf(fp, tab, "type %s\n",
1051 hammer2_iptype_to_str(ipdata->meta.type));
1052 tfprintf(fp, tab, "op_flags 0x%02x\n", ipdata->meta.op_flags);
1053 tfprintf(fp, tab, "cap_flags 0x%04x\n", ipdata->meta.cap_flags);
1054 tfprintf(fp, tab, "mode %-7o\n", ipdata->meta.mode);
1055 tfprintf(fp, tab, "inum 0x%016jx\n", ipdata->meta.inum);
1056 tfprintf(fp, tab, "size %ju ", (uintmax_t)ipdata->meta.size);
1057 if (ipdata->meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA &&
1058 ipdata->meta.size <= HAMMER2_EMBEDDED_BYTES)
1059 printf("(embedded data)\n");
1062 tfprintf(fp, tab, "nlinks %ju\n",
1063 (uintmax_t)ipdata->meta.nlinks);
1064 tfprintf(fp, tab, "iparent 0x%016jx\n",
1065 (uintmax_t)ipdata->meta.iparent);
1066 tfprintf(fp, tab, "name_key 0x%016jx\n",
1067 (uintmax_t)ipdata->meta.name_key);
1068 tfprintf(fp, tab, "name_len %u\n", ipdata->meta.name_len);
1069 tfprintf(fp, tab, "ncopies %u\n", ipdata->meta.ncopies);
1070 tfprintf(fp, tab, "comp_algo %u\n", ipdata->meta.comp_algo);
1071 tfprintf(fp, tab, "target_type %u\n", ipdata->meta.target_type);
1072 tfprintf(fp, tab, "check_algo %u\n", ipdata->meta.check_algo);
1073 if ((ipdata->meta.op_flags & HAMMER2_OPFLAG_PFSROOT) ||
1074 ipdata->meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
1075 tfprintf(fp, tab, "pfs_nmasters %u\n",
1076 ipdata->meta.pfs_nmasters);
1077 tfprintf(fp, tab, "pfs_type %u (%s)\n",
1078 ipdata->meta.pfs_type,
1079 hammer2_pfstype_to_str(ipdata->meta.pfs_type));
1080 tfprintf(fp, tab, "pfs_inum 0x%016jx\n",
1081 (uintmax_t)ipdata->meta.pfs_inum);
1082 tfprintf(fp, tab, "pfs_clid %s\n",
1083 hammer2_uuid_to_str(&ipdata->meta.pfs_clid, &str));
1084 tfprintf(fp, tab, "pfs_fsid %s\n",
1085 hammer2_uuid_to_str(&ipdata->meta.pfs_fsid, &str));
1086 tfprintf(fp, tab, "pfs_lsnap_tid 0x%016jx\n",
1087 (uintmax_t)ipdata->meta.pfs_lsnap_tid);
1089 tfprintf(fp, tab, "data_quota %ju\n",
1090 (uintmax_t)ipdata->meta.data_quota);
1091 tfprintf(fp, tab, "data_count %ju\n",
1092 (uintmax_t)bref->embed.stats.data_count);
1093 tfprintf(fp, tab, "inode_quota %ju\n",
1094 (uintmax_t)ipdata->meta.inode_quota);
1095 tfprintf(fp, tab, "inode_count %ju\n",
1096 (uintmax_t)bref->embed.stats.inode_count);
1098 case HAMMER2_BREF_TYPE_INDIRECT:
1099 bcount = media_bytes / sizeof(hammer2_blockref_t);
1100 for (i = 0; i < bcount; ++i) {
1101 bscan = &media->npdata[i];
1102 tfprintf(fp, tab, "%3d %016jx %-12s %016jx/%-2d\n",
1103 i, (uintmax_t)bscan->data_off,
1104 hammer2_breftype_to_str(bscan->type),
1105 (uintmax_t)bscan->key,
1109 case HAMMER2_BREF_TYPE_DIRENT:
1110 if (bref->embed.dirent.namlen <= sizeof(bref->check.buf)) {
1111 tfprintf(fp, tab, "filename \"%*.*s\"\n",
1112 bref->embed.dirent.namlen,
1113 bref->embed.dirent.namlen,
1116 tfprintf(fp, tab, "filename \"%*.*s\"\n",
1117 bref->embed.dirent.namlen,
1118 bref->embed.dirent.namlen,
1121 tfprintf(fp, tab, "inum 0x%016jx\n",
1122 (uintmax_t)bref->embed.dirent.inum);
1123 tfprintf(fp, tab, "namlen %d\n",
1124 (uintmax_t)bref->embed.dirent.namlen);
1125 tfprintf(fp, tab, "type %s\n",
1126 hammer2_iptype_to_str(bref->embed.dirent.type));
1128 case HAMMER2_BREF_TYPE_FREEMAP_NODE:
1129 bcount = media_bytes / sizeof(hammer2_blockref_t);
1130 for (i = 0; i < bcount; ++i) {
1131 bscan = &media->npdata[i];
1132 tfprintf(fp, tab, "%3d %016jx %-12s %016jx/%-2d\n",
1133 i, (uintmax_t)bscan->data_off,
1134 hammer2_breftype_to_str(bscan->type),
1135 (uintmax_t)bscan->key,
1139 case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
1140 for (i = 0; i < HAMMER2_FREEMAP_COUNT; ++i) {
1141 hammer2_off_t data_off = bref->key +
1142 i * HAMMER2_FREEMAP_LEVEL0_SIZE;
1143 #if HAMMER2_BMAP_ELEMENTS != 8
1144 #error "HAMMER2_BMAP_ELEMENTS != 8"
1146 tfprintf(fp, tab, "%016jx %04d.%04x (avail=%7d) "
1147 "%016jx %016jx %016jx %016jx "
1148 "%016jx %016jx %016jx %016jx\n",
1149 data_off, i, media->bmdata[i].class,
1150 media->bmdata[i].avail,
1151 media->bmdata[i].bitmapq[0],
1152 media->bmdata[i].bitmapq[1],
1153 media->bmdata[i].bitmapq[2],
1154 media->bmdata[i].bitmapq[3],
1155 media->bmdata[i].bitmapq[4],
1156 media->bmdata[i].bitmapq[5],
1157 media->bmdata[i].bitmapq[6],
1158 media->bmdata[i].bitmapq[7]);
1169 test_hammer2(const char *devpath)
1172 bool failed = false;
1175 fd = open(devpath, O_RDONLY);
1181 if (fstat(fd, &st) == -1) {
1186 if (!S_ISCHR(st.st_mode)) {
1187 fprintf(stderr, "%s is not a block device\n", devpath);
1192 best_zone = find_best_zone(fd);
1193 if (best_zone == -1)
1194 fprintf(stderr, "Failed to find best zone\n");
1196 printf("volume header\n");
1197 if (test_volume_header(fd) == -1) {
1203 printf("freemap\n");
1204 if (test_blockref(fd, HAMMER2_BREF_TYPE_FREEMAP) == -1) {
1211 if (test_blockref(fd, HAMMER2_BREF_TYPE_VOLUME) == -1) {
1217 if (test_pfs_blockref(fd) == -1) {
1226 return failed ? -1 : 0;