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;
94 uint64_t total_invalid;
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;
111 static void init_blockref_stats(blockref_stats_t *, uint8_t);
112 static void cleanup_blockref_stats(blockref_stats_t *);
113 static void print_blockref_stats(const blockref_stats_t *, bool);
114 static int verify_volume_header(const hammer2_volume_data_t *);
115 static int verify_blockref(int, const hammer2_volume_data_t *,
116 const hammer2_blockref_t *, bool, blockref_stats_t *);
117 static int init_pfs_blockref(int, const hammer2_volume_data_t *,
118 const hammer2_blockref_t *, struct blockref_list *);
119 static void cleanup_pfs_blockref(struct blockref_list *);
121 static int best_zone = -1;
126 tfprintf(FILE *fp, int tab, const char *ctl, ...)
131 fprintf(fp, "%*s", tab, "");
135 vfprintf(fp, ctl, va);
140 find_best_zone(int fd)
142 hammer2_blockref_t best;
145 memset(&best, 0, sizeof(best));
147 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
148 hammer2_volume_data_t voldata;
149 hammer2_blockref_t broot;
152 memset(&broot, 0, sizeof(broot));
153 broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
154 lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, SEEK_SET);
156 ret = read(fd, &voldata, HAMMER2_PBUFSIZE);
157 if (ret == HAMMER2_PBUFSIZE) {
158 if ((voldata.magic != HAMMER2_VOLUME_ID_HBO) &&
159 (voldata.magic != HAMMER2_VOLUME_ID_ABO))
161 broot.mirror_tid = voldata.mirror_tid;
162 if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
166 } else if (ret == -1) {
170 tfprintf(stderr, 1, "Failed to read volume header\n");
179 test_volume_header(int fd)
184 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
185 hammer2_volume_data_t voldata;
186 hammer2_blockref_t broot;
189 memset(&broot, 0, sizeof(broot));
190 broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
191 lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, SEEK_SET);
193 ret = read(fd, &voldata, HAMMER2_PBUFSIZE);
194 if (ret == HAMMER2_PBUFSIZE) {
195 broot.mirror_tid = voldata.mirror_tid;
197 printf("zone.%d %016jx%s\n",
198 i, (uintmax_t)broot.data_off,
199 (i == best_zone) ? " (best)" : "");
200 if (verify_volume_header(&voldata) == -1)
202 } else if (ret == -1) {
206 tfprintf(stderr, 1, "Failed to read volume header\n");
211 return failed ? -1 : 0;
215 test_blockref(int fd, uint8_t type)
220 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
221 hammer2_volume_data_t voldata;
222 hammer2_blockref_t broot;
225 memset(&broot, 0, sizeof(broot));
227 broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
228 lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, SEEK_SET);
230 ret = read(fd, &voldata, HAMMER2_PBUFSIZE);
231 if (ret == HAMMER2_PBUFSIZE) {
232 blockref_stats_t bstats;
233 struct blockref_entry *e;
234 broot.mirror_tid = voldata.mirror_tid;
235 init_blockref_stats(&bstats, type);
237 printf("zone.%d %016jx%s\n",
238 i, (uintmax_t)broot.data_off,
239 (i == best_zone) ? " (best)" : "");
240 if (verify_blockref(fd, &voldata, &broot, false,
243 print_blockref_stats(&bstats, true);
245 RB_FOREACH(e, blockref_tree, &bstats.root) {
246 struct blockref_msg *m;
247 TAILQ_FOREACH(m, &e->head, entry) {
248 tfprintf(stderr, 1, "%016jx %3d "
249 "%016jx/%-2d \"%s\"\n",
250 (uintmax_t)e->data_off,
252 (uintmax_t)m->bref.key,
257 cleanup_blockref_stats(&bstats);
258 } else if (ret == -1) {
262 tfprintf(stderr, 1, "Failed to read volume header\n");
267 return failed ? -1 : 0;
271 test_pfs_blockref(int fd, const char *name)
273 uint8_t type = HAMMER2_BREF_TYPE_VOLUME;
277 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
278 hammer2_volume_data_t voldata;
279 hammer2_blockref_t broot;
282 memset(&broot, 0, sizeof(broot));
284 broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
285 lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, SEEK_SET);
287 ret = read(fd, &voldata, HAMMER2_PBUFSIZE);
288 if (ret == HAMMER2_PBUFSIZE) {
289 struct blockref_list blist;
290 struct blockref_msg *p;
292 broot.mirror_tid = voldata.mirror_tid;
294 printf("zone.%d %016jx%s\n",
295 i, (uintmax_t)broot.data_off,
296 (i == best_zone) ? " (best)" : "");
299 if (init_pfs_blockref(fd, &voldata, &broot, &blist) ==
301 tfprintf(stderr, 1, "Failed to read PFS "
306 if (TAILQ_EMPTY(&blist)) {
307 tfprintf(stderr, 1, "Failed to find PFS "
313 TAILQ_FOREACH(p, &blist, entry) {
314 blockref_stats_t bstats;
315 struct blockref_entry *e;
316 if (name && strcmp(name, p->msg))
319 tfprintf(stdout, 1, "%s\n", p->msg);
320 init_blockref_stats(&bstats, type);
321 if (verify_blockref(fd, &voldata, &p->bref,
322 false, &bstats) == -1)
324 print_blockref_stats(&bstats, true);
326 RB_FOREACH(e, blockref_tree, &bstats.root) {
327 struct blockref_msg *m;
328 TAILQ_FOREACH(m, &e->head, entry) {
329 tfprintf(stderr, 1, "%016jx %3d "
330 "%016jx/%-2d \"%s\"\n",
331 (uintmax_t)e->data_off,
333 (uintmax_t)m->bref.key,
338 cleanup_blockref_stats(&bstats);
340 cleanup_pfs_blockref(&blist);
341 if (name && !count) {
342 tfprintf(stderr, 1, "PFS \"%s\" not found\n",
346 } else if (ret == -1) {
350 tfprintf(stderr, 1, "Failed to read volume header\n");
355 return failed ? -1 : 0;
366 if (ioctl(0, TIOCGWINSZ, &ws) != -1)
368 if (columns == 0 && (cp = getenv("COLUMNS")))
371 columns = 80; /* last resort */
377 add_blockref_entry(blockref_stats_t *bstats, const hammer2_blockref_t *bref,
380 struct blockref_entry *e;
381 struct blockref_msg *m;
383 e = RB_LOOKUP(blockref_tree, &bstats->root, bref->data_off);
385 e = calloc(1, sizeof(*e));
387 TAILQ_INIT(&e->head);
390 m = calloc(1, sizeof(*m));
393 m->msg = strdup(msg);
395 e->data_off = bref->data_off;
396 TAILQ_INSERT_TAIL(&e->head, m, entry);
397 RB_INSERT(blockref_tree, &bstats->root, e);
401 init_blockref_stats(blockref_stats_t *bstats, uint8_t type)
403 memset(bstats, 0, sizeof(*bstats));
405 RB_INIT(&bstats->root);
409 cleanup_blockref_stats(blockref_stats_t *bstats)
411 struct blockref_entry *e;
413 while ((e = RB_ROOT(&bstats->root)) != NULL) {
414 struct blockref_msg *m;
415 RB_REMOVE(blockref_tree, &bstats->root, e);
416 while ((m = TAILQ_FIRST(&e->head)) != NULL) {
417 TAILQ_REMOVE(&e->head, m, entry);
421 assert(TAILQ_EMPTY(&e->head));
424 assert(RB_EMPTY(&bstats->root));
428 print_blockref_stats(const blockref_stats_t *bstats, bool newline)
430 size_t siz = charsperline();
431 char *buf = calloc(1, siz);
435 switch (bstats->type) {
436 case HAMMER2_BREF_TYPE_VOLUME:
437 snprintf(buf, siz, "%*s%ju blockref (%ju inode, %ju indirect, "
438 "%ju data, %ju dirent, %ju empty), %s",
440 (uintmax_t)bstats->total_blockref,
441 (uintmax_t)bstats->volume.total_inode,
442 (uintmax_t)bstats->volume.total_indirect,
443 (uintmax_t)bstats->volume.total_data,
444 (uintmax_t)bstats->volume.total_dirent,
445 (uintmax_t)bstats->total_empty,
446 sizetostr(bstats->total_bytes));
448 case HAMMER2_BREF_TYPE_FREEMAP:
449 snprintf(buf, siz, "%*s%ju blockref (%ju node, %ju leaf, "
452 (uintmax_t)bstats->total_blockref,
453 (uintmax_t)bstats->freemap.total_freemap_node,
454 (uintmax_t)bstats->freemap.total_freemap_leaf,
455 (uintmax_t)bstats->total_empty,
456 sizetostr(bstats->total_bytes));
473 verify_volume_header(const hammer2_volume_data_t *voldata)
475 hammer2_crc32_t crc0, crc, bcrc0, bcrc;
476 const char *p = (const char*)voldata;
478 if ((voldata->magic != HAMMER2_VOLUME_ID_HBO) &&
479 (voldata->magic != HAMMER2_VOLUME_ID_ABO)) {
480 tfprintf(stderr, 1, "Bad magic %jX\n", voldata->magic);
484 if (voldata->magic == HAMMER2_VOLUME_ID_ABO)
485 tfprintf(stderr, 1, "Reverse endian\n");
487 crc = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT0];
488 crc0 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC0_OFF,
489 HAMMER2_VOLUME_ICRC0_SIZE);
491 tfprintf(stderr, 1, "Bad HAMMER2_VOL_ICRC_SECT0 CRC\n");
495 bcrc = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT1];
496 bcrc0 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC1_OFF,
497 HAMMER2_VOLUME_ICRC1_SIZE);
499 tfprintf(stderr, 1, "Bad HAMMER2_VOL_ICRC_SECT1 CRC\n");
507 read_media(int fd, const hammer2_blockref_t *bref, hammer2_media_data_t *media,
510 hammer2_off_t io_off, io_base;
511 size_t bytes, io_bytes, boff;
513 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
515 bytes = (size_t)1 << bytes;
516 *media_bytes = bytes;
521 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
522 io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1);
523 boff = io_off - io_base;
525 io_bytes = HAMMER2_MINIOSIZE;
526 while (io_bytes + boff < bytes)
529 if (io_bytes > sizeof(*media))
531 lseek(fd, io_base, SEEK_SET);
532 if (read(fd, media, io_bytes) != (ssize_t)io_bytes)
535 memcpy(media, (char *)media + boff, bytes);
541 verify_blockref(int fd, const hammer2_volume_data_t *voldata,
542 const hammer2_blockref_t *bref, bool norecurse, blockref_stats_t *bstats)
544 hammer2_media_data_t media;
545 hammer2_blockref_t *bscan;
555 uint8_t digest[SHA256_DIGEST_LENGTH];
556 uint64_t digest64[SHA256_DIGEST_LENGTH/8];
559 bstats->total_blockref++;
561 switch (bref->type) {
562 case HAMMER2_BREF_TYPE_EMPTY:
563 bstats->total_empty++;
565 case HAMMER2_BREF_TYPE_INODE:
566 bstats->volume.total_inode++;
568 case HAMMER2_BREF_TYPE_INDIRECT:
569 bstats->volume.total_indirect++;
571 case HAMMER2_BREF_TYPE_DATA:
572 bstats->volume.total_data++;
574 case HAMMER2_BREF_TYPE_DIRENT:
575 bstats->volume.total_dirent++;
577 case HAMMER2_BREF_TYPE_FREEMAP_NODE:
578 bstats->freemap.total_freemap_node++;
580 case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
581 bstats->freemap.total_freemap_leaf++;
583 case HAMMER2_BREF_TYPE_VOLUME:
584 bstats->total_blockref--;
586 case HAMMER2_BREF_TYPE_FREEMAP:
587 bstats->total_blockref--;
590 bstats->total_invalid++;
591 snprintf(msg, sizeof(msg),
592 "Invalid blockref type %d", bref->type);
593 add_blockref_entry(bstats, bref, msg);
598 switch (read_media(fd, bref, &media, &bytes)) {
600 add_blockref_entry(bstats, bref, "Bad I/O bytes");
603 add_blockref_entry(bstats, bref, "Failed to read media");
609 if (bref->type != HAMMER2_BREF_TYPE_VOLUME &&
610 bref->type != HAMMER2_BREF_TYPE_FREEMAP)
611 bstats->total_bytes += bytes;
613 if ((bstats->total_blockref % 100) == 0)
614 print_blockref_stats(bstats, false);
617 switch (HAMMER2_DEC_CHECK(bref->methods)) {
618 case HAMMER2_CHECK_ISCSI32:
619 cv = hammer2_icrc32(&media, bytes);
620 if (bref->check.iscsi32.value != cv) {
621 add_blockref_entry(bstats, bref,
622 "Bad HAMMER2_CHECK_ISCSI32");
626 case HAMMER2_CHECK_XXHASH64:
627 cv64 = XXH64(&media, bytes, XXH_HAMMER2_SEED);
628 if (bref->check.xxhash64.value != cv64) {
629 add_blockref_entry(bstats, bref,
630 "Bad HAMMER2_CHECK_XXHASH64");
634 case HAMMER2_CHECK_SHA192:
635 SHA256_Init(&hash_ctx);
636 SHA256_Update(&hash_ctx, &media, bytes);
637 SHA256_Final(u.digest, &hash_ctx);
638 u.digest64[2] ^= u.digest64[3];
639 if (memcmp(u.digest, bref->check.sha192.data,
640 sizeof(bref->check.sha192.data))) {
641 add_blockref_entry(bstats, bref,
642 "Bad HAMMER2_CHECK_SHA192");
646 case HAMMER2_CHECK_FREEMAP:
647 cv = hammer2_icrc32(&media, bytes);
648 if (bref->check.freemap.icrc32 != cv) {
649 add_blockref_entry(bstats, bref,
650 "Bad HAMMER2_CHECK_FREEMAP");
657 switch (bref->type) {
658 case HAMMER2_BREF_TYPE_INODE:
659 if (!(media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA)) {
660 bscan = &media.ipdata.u.blockset.blockref[0];
661 bcount = HAMMER2_SET_COUNT;
667 case HAMMER2_BREF_TYPE_INDIRECT:
668 bscan = &media.npdata[0];
669 bcount = bytes / sizeof(hammer2_blockref_t);
671 case HAMMER2_BREF_TYPE_FREEMAP_NODE:
672 bscan = &media.npdata[0];
673 bcount = bytes / sizeof(hammer2_blockref_t);
675 case HAMMER2_BREF_TYPE_VOLUME:
676 bscan = &media.voldata.sroot_blockset.blockref[0];
677 bcount = HAMMER2_SET_COUNT;
679 case HAMMER2_BREF_TYPE_FREEMAP:
680 bscan = &media.voldata.freemap_blockset.blockref[0];
681 bcount = HAMMER2_SET_COUNT;
692 * If failed, no recurse, but still verify its direct children.
693 * Beyond that is probably garbage.
695 for (i = 0; norecurse == 0 && i < bcount; ++i)
696 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY)
697 if (verify_blockref(fd, voldata, &bscan[i], failed,
700 return failed ? -1 : 0;
704 init_pfs_blockref(int fd, const hammer2_volume_data_t *voldata,
705 const hammer2_blockref_t *bref, struct blockref_list *blist)
707 hammer2_media_data_t media;
708 hammer2_inode_data_t ipdata;
709 hammer2_blockref_t *bscan;
713 if (read_media(fd, bref, &media, &bytes))
718 switch (bref->type) {
719 case HAMMER2_BREF_TYPE_INODE:
720 ipdata = media.ipdata;
721 if (ipdata.meta.pfs_type & HAMMER2_PFSTYPE_SUPROOT) {
722 bscan = &ipdata.u.blockset.blockref[0];
723 bcount = HAMMER2_SET_COUNT;
727 if (ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) {
728 struct blockref_msg *p;
729 p = calloc(1, sizeof(*p));
732 p->msg = strdup(ipdata.filename);
733 TAILQ_INSERT_TAIL(blist, p, entry);
735 assert(0); /* should only see SUPROOT or PFS */
738 case HAMMER2_BREF_TYPE_INDIRECT:
739 bscan = &media.npdata[0];
740 bcount = bytes / sizeof(hammer2_blockref_t);
742 case HAMMER2_BREF_TYPE_VOLUME:
743 bscan = &media.voldata.sroot_blockset.blockref[0];
744 bcount = HAMMER2_SET_COUNT;
752 for (i = 0; i < bcount; ++i)
753 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY)
754 if (init_pfs_blockref(fd, voldata, &bscan[i], blist)
761 cleanup_pfs_blockref(struct blockref_list *blist)
763 struct blockref_msg *p;
765 while ((p = TAILQ_FIRST(blist)) != NULL) {
766 TAILQ_REMOVE(blist, p, entry);
770 assert(TAILQ_EMPTY(blist));
774 test_hammer2(const char *devpath)
780 fd = open(devpath, O_RDONLY);
786 if (fstat(fd, &st) == -1) {
791 if (!S_ISCHR(st.st_mode)) {
792 fprintf(stderr, "%s is not a block device\n", devpath);
797 best_zone = find_best_zone(fd);
799 fprintf(stderr, "Failed to find best zone\n");
801 printf("volume header\n");
802 if (test_volume_header(fd) == -1) {
809 if (test_blockref(fd, HAMMER2_BREF_TYPE_FREEMAP) == -1) {
816 if (test_blockref(fd, HAMMER2_BREF_TYPE_VOLUME) == -1) {
822 if (test_pfs_blockref(fd, PFSName) == -1) {
831 return failed ? -1 : 0;