Merge branch 'vendor/TCPDUMP'
[dragonfly.git] / sbin / fsck_hammer2 / test.c
1 /*
2  * Copyright (c) 2019 Tomohiro Kusumi <tkusumi@netbsd.org>
3  * Copyright (c) 2019 The DragonFly Project
4  * All rights reserved.
5  *
6  * This code is derived from software contributed to The DragonFly Project
7  * by Matthew Dillon <dillon@dragonflybsd.org>
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  * 3. Neither the name of The DragonFly Project nor the names of its
20  *    contributors may be used to endorse or promote products derived
21  *    from this software without specific, prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
27  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/tree.h>
40 #include <sys/queue.h>
41 #include <sys/ttycom.h>
42 #include <sys/diskslice.h>
43 #include <unistd.h>
44 #include <fcntl.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <stdarg.h>
48 #include <stdbool.h>
49 #include <string.h>
50 #include <assert.h>
51
52 #ifdef HAMMER2_USE_OPENSSL
53 #include <openssl/sha.h>
54 #endif
55
56 #include <vfs/hammer2/hammer2_disk.h>
57 #include <vfs/hammer2/hammer2_xxhash.h>
58
59 #include "hammer2_subs.h"
60 #include "fsck_hammer2.h"
61
62 struct blockref_msg {
63         TAILQ_ENTRY(blockref_msg) entry;
64         hammer2_blockref_t bref;
65         void *msg;
66 };
67
68 TAILQ_HEAD(blockref_list, blockref_msg);
69
70 struct blockref_entry {
71         RB_ENTRY(blockref_entry) entry;
72         hammer2_off_t data_off;
73         struct blockref_list head;
74 };
75
76 static int
77 blockref_cmp(struct blockref_entry *b1, struct blockref_entry *b2)
78 {
79         if (b1->data_off < b2->data_off)
80                 return -1;
81         if (b1->data_off > b2->data_off)
82                 return 1;
83         return 0;
84 }
85
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);
89
90 typedef struct {
91         struct blockref_tree root;
92         uint8_t type; /* HAMMER2_BREF_TYPE_VOLUME or FREEMAP */
93         uint64_t total_blockref;
94         uint64_t total_empty;
95         uint64_t total_bytes;
96         union {
97                 /* use volume or freemap depending on type value */
98                 struct {
99                         uint64_t total_inode;
100                         uint64_t total_indirect;
101                         uint64_t total_data;
102                         uint64_t total_dirent;
103                 } volume;
104                 struct {
105                         uint64_t total_freemap_node;
106                         uint64_t total_freemap_leaf;
107                 } freemap;
108         };
109 } blockref_stats_t;
110
111 typedef struct {
112         uint64_t total_blockref;
113         uint64_t total_empty;
114         uint64_t total_bytes;
115         struct {
116                 uint64_t total_inode;
117                 uint64_t total_indirect;
118                 uint64_t total_data;
119                 uint64_t total_dirent;
120         } volume;
121         struct {
122                 uint64_t total_freemap_node;
123                 uint64_t total_freemap_leaf;
124         } freemap;
125         long count;
126 } delta_stats_t;
127
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 *,
136     size_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);
147
148 static int best_zone = -1;
149
150 #define TAB 8
151
152 static void
153 tfprintf(FILE *fp, int tab, const char *ctl, ...)
154 {
155         va_list va;
156         int ret;
157
158         ret = fprintf(fp, "%*s", tab * TAB, "");
159         if (ret < 0)
160                 return;
161
162         va_start(va, ctl);
163         vfprintf(fp, ctl, va);
164         va_end(va);
165 }
166
167 static void
168 tsnprintf(char *str, size_t siz, int tab, const char *ctl, ...)
169 {
170         va_list va;
171         int ret;
172
173         ret = snprintf(str, siz, "%*s", tab * TAB, "");
174         if (ret < 0 || ret >= (int)siz)
175                 return;
176
177         va_start(va, ctl);
178         vsnprintf(str + ret, siz - ret, ctl, va);
179         va_end(va);
180 }
181
182 static void
183 tprintf_zone(int tab, int i, const hammer2_blockref_t *bref)
184 {
185         tfprintf(stdout, tab, "zone.%d %016jx%s\n",
186             i, (uintmax_t)bref->data_off,
187             (!ScanBest && i == best_zone) ? " (best)" : "");
188 }
189
190 static int
191 init_root_blockref(int i, uint8_t type, hammer2_blockref_t *bref)
192 {
193         hammer2_off_t off;
194
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));
199         bref->type = type;
200         bref->data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
201         off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
202
203         return lseek(hammer2_get_root_volume_fd(),
204             off - hammer2_get_root_volume_offset(), SEEK_SET);
205 }
206
207 static int
208 find_best_zone(void)
209 {
210         hammer2_blockref_t best;
211         int i, best_i = -1;
212
213         memset(&best, 0, sizeof(best));
214
215         for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
216                 hammer2_volume_data_t voldata;
217                 hammer2_blockref_t broot;
218                 ssize_t ret;
219
220                 if (i * HAMMER2_ZONE_BYTES64 >=
221                     hammer2_get_root_volume_size())
222                         break;
223                 init_root_blockref(i, HAMMER2_BREF_TYPE_EMPTY, &broot);
224                 ret = read(hammer2_get_root_volume_fd(), &voldata,
225                     HAMMER2_PBUFSIZE);
226                 if (ret == HAMMER2_PBUFSIZE) {
227                         if ((voldata.magic != HAMMER2_VOLUME_ID_HBO) &&
228                             (voldata.magic != HAMMER2_VOLUME_ID_ABO))
229                                 continue;
230                         broot.mirror_tid = voldata.mirror_tid;
231                         if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
232                                 best_i = i;
233                                 best = broot;
234                         }
235                 } else if (ret == -1) {
236                         perror("read");
237                         return -1;
238                 } else {
239                         tfprintf(stderr, 1, "Failed to read volume header\n");
240                         return -1;
241                 }
242         }
243
244         return best_i;
245 }
246
247 static int
248 test_volume_header(void)
249 {
250         bool failed = false;
251         int i;
252
253         for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
254                 hammer2_volume_data_t voldata;
255                 hammer2_blockref_t broot;
256                 ssize_t ret;
257
258                 if (ScanBest && i != best_zone)
259                         continue;
260                 if (i * HAMMER2_ZONE_BYTES64 >=
261                     hammer2_get_root_volume_size()) {
262                         tfprintf(stderr, 0, "zone.%d exceeds volume size\n", i);
263                         break;
264                 }
265                 init_root_blockref(i, HAMMER2_BREF_TYPE_EMPTY, &broot);
266                 ret = read(hammer2_get_root_volume_fd(), &voldata,
267                     HAMMER2_PBUFSIZE);
268                 if (ret == HAMMER2_PBUFSIZE) {
269                         tprintf_zone(0, i, &broot);
270                         if (verify_volume_header(&voldata) == -1)
271                                 failed = true;
272                 } else if (ret == -1) {
273                         perror("read");
274                         return -1;
275                 } else {
276                         tfprintf(stderr, 1, "Failed to read volume header\n");
277                         return -1;
278                 }
279         }
280
281         return failed ? -1 : 0;
282 }
283
284 static int
285 test_blockref(uint8_t type)
286 {
287         struct blockref_tree droot;
288         bool failed = false;
289         int i;
290
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;
295                 ssize_t ret;
296
297                 if (ScanBest && i != best_zone)
298                         continue;
299                 if (i * HAMMER2_ZONE_BYTES64 >=
300                     hammer2_get_root_volume_size()) {
301                         tfprintf(stderr, 0, "zone.%d exceeds volume size\n", i);
302                         break;
303                 }
304                 init_root_blockref(i, type, &broot);
305                 ret = read(hammer2_get_root_volume_fd(), &voldata,
306                     HAMMER2_PBUFSIZE);
307                 if (ret == HAMMER2_PBUFSIZE) {
308                         blockref_stats_t bstats;
309                         init_blockref_stats(&bstats, type);
310                         delta_stats_t ds;
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)
315                                 failed = true;
316                         print_blockref_stats(&bstats, true);
317                         print_blockref_entry(&bstats.root);
318                         cleanup_blockref_stats(&bstats);
319                 } else if (ret == -1) {
320                         perror("read");
321                         failed = true;
322                         goto end;
323                 } else {
324                         tfprintf(stderr, 1, "Failed to read volume header\n");
325                         failed = true;
326                         goto end;
327                 }
328         }
329 end:
330         cleanup_delta_root(&droot);
331         return failed ? -1 : 0;
332 }
333
334 static int
335 test_pfs_blockref(void)
336 {
337         struct blockref_tree droot;
338         uint8_t type = HAMMER2_BREF_TYPE_VOLUME;
339         bool failed = false;
340         int i;
341
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;
346                 ssize_t ret;
347
348                 if (ScanBest && i != best_zone)
349                         continue;
350                 if (i * HAMMER2_ZONE_BYTES64 >=
351                     hammer2_get_root_volume_size()) {
352                         tfprintf(stderr, 0, "zone.%d exceeds volume size\n", i);
353                         break;
354                 }
355                 init_root_blockref(i, type, &broot);
356                 ret = read(hammer2_get_root_volume_fd(), &voldata,
357                     HAMMER2_PBUFSIZE);
358                 if (ret == HAMMER2_PBUFSIZE) {
359                         struct blockref_list blist;
360                         struct blockref_msg *p;
361                         int count = 0;
362
363                         tprintf_zone(0, i, &broot);
364                         TAILQ_INIT(&blist);
365                         if (init_pfs_blockref(&voldata, &broot, &blist) == -1) {
366                                 tfprintf(stderr, 1, "Failed to read PFS "
367                                     "blockref\n");
368                                 failed = true;
369                                 continue;
370                         }
371                         if (TAILQ_EMPTY(&blist)) {
372                                 tfprintf(stderr, 1, "Failed to find PFS "
373                                     "blockref\n");
374                                 failed = true;
375                                 continue;
376                         }
377                         TAILQ_FOREACH(p, &blist, entry) {
378                                 blockref_stats_t bstats;
379                                 bool found = false;
380                                 char *f = get_inode_filename(p->msg);
381                                 if (NumPFSNames) {
382                                         int j;
383                                         for (j = 0; j < NumPFSNames; j++)
384                                                 if (!strcmp(PFSNames[j], f))
385                                                         found = true;
386                                 } else
387                                         found = true;
388                                 if (!found) {
389                                         free(f);
390                                         continue;
391                                 }
392                                 count++;
393                                 if (PrintPFS) {
394                                         print_pfs(p->msg);
395                                         free(f);
396                                         continue;
397                                 }
398                                 tfprintf(stdout, 1, "%s\n", f);
399                                 free(f);
400                                 init_blockref_stats(&bstats, type);
401                                 delta_stats_t ds;
402                                 memset(&ds, 0, sizeof(ds));
403                                 if (verify_blockref(&voldata, &p->bref, false,
404                                     &bstats, &droot, &ds, 0, 0) == -1)
405                                         failed = true;
406                                 print_blockref_stats(&bstats, true);
407                                 print_blockref_entry(&bstats.root);
408                                 cleanup_blockref_stats(&bstats);
409                         }
410                         cleanup_pfs_blockref(&blist);
411                         if (NumPFSNames && !count) {
412                                 tfprintf(stderr, 1, "PFS not found\n");
413                                 failed = true;
414                         }
415                 } else if (ret == -1) {
416                         perror("read");
417                         failed = true;
418                         goto end;
419                 } else {
420                         tfprintf(stderr, 1, "Failed to read volume header\n");
421                         failed = true;
422                         goto end;
423                 }
424         }
425 end:
426         cleanup_delta_root(&droot);
427         return failed ? -1 : 0;
428 }
429
430 static int
431 charsperline(void)
432 {
433         int columns;
434         char *cp;
435         struct winsize ws;
436
437         columns = 0;
438         if (ioctl(0, TIOCGWINSZ, &ws) != -1)
439                 columns = ws.ws_col;
440         if (columns == 0 && (cp = getenv("COLUMNS")))
441                 columns = atoi(cp);
442         if (columns == 0)
443                 columns = 80;   /* last resort */
444
445         return columns;
446 }
447
448 static void
449 cleanup_blockref_msg(struct blockref_list *head)
450 {
451         struct blockref_msg *p;
452
453         while ((p = TAILQ_FIRST(head)) != NULL) {
454                 TAILQ_REMOVE(head, p, entry);
455                 free(p->msg);
456                 free(p);
457         }
458         assert(TAILQ_EMPTY(head));
459 }
460
461 static void
462 cleanup_blockref_entry(struct blockref_tree *root)
463 {
464         struct blockref_entry *e;
465
466         while ((e = RB_ROOT(root)) != NULL) {
467                 RB_REMOVE(blockref_tree, root, e);
468                 cleanup_blockref_msg(&e->head);
469                 free(e);
470         }
471         assert(RB_EMPTY(root));
472 }
473
474 static void
475 add_blockref_msg(struct blockref_list *head, const hammer2_blockref_t *bref,
476     const void *msg, size_t siz)
477 {
478         struct blockref_msg *m;
479         void *p;
480
481         m = calloc(1, sizeof(*m));
482         assert(m);
483         m->bref = *bref;
484         p = calloc(1, siz);
485         assert(p);
486         memcpy(p, msg, siz);
487         m->msg = p;
488
489         TAILQ_INSERT_TAIL(head, m, entry);
490 }
491
492 static void
493 add_blockref_entry(struct blockref_tree *root, const hammer2_blockref_t *bref,
494     const void *msg, size_t siz)
495 {
496         struct blockref_entry *e, bref_find;
497
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);
501         if (!e) {
502                 e = calloc(1, sizeof(*e));
503                 assert(e);
504                 TAILQ_INIT(&e->head);
505                 e->data_off = bref->data_off;
506         }
507
508         add_blockref_msg(&e->head, bref, msg, siz);
509
510         RB_INSERT(blockref_tree, root, e);
511 }
512
513 static void
514 __print_blockref(FILE *fp, int tab, const hammer2_blockref_t *bref,
515     const char *msg)
516 {
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,
521             bref->keybits,
522             msg ? " " : "",
523             msg ? msg : "");
524 }
525
526 static void
527 print_blockref(FILE *fp, const hammer2_blockref_t *bref, const char *msg)
528 {
529         __print_blockref(fp, 1, bref, msg);
530 }
531
532 static void
533 print_blockref_debug(FILE *fp, int depth, int index,
534     const hammer2_blockref_t *bref, const char *msg)
535 {
536         if (DebugOpt > 1) {
537                 char buf[256];
538                 int i;
539
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);
548 }
549
550 static void
551 print_blockref_msg(const struct blockref_list *head)
552 {
553         struct blockref_msg *m;
554
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;
560                         size_t bytes;
561                         if (!read_media(bref, &media, &bytes))
562                                 print_media(stderr, 2, bref, &media, bytes);
563                         else
564                                 tfprintf(stderr, 2, "Failed to read media\n");
565                 }
566         }
567 }
568
569 static void
570 print_blockref_entry(struct blockref_tree *root)
571 {
572         struct blockref_entry *e;
573
574         RB_FOREACH(e, blockref_tree, root)
575                 print_blockref_msg(&e->head);
576 }
577
578 static void
579 init_blockref_stats(blockref_stats_t *bstats, uint8_t type)
580 {
581         memset(bstats, 0, sizeof(*bstats));
582         RB_INIT(&bstats->root);
583         bstats->type = type;
584 }
585
586 static void
587 cleanup_blockref_stats(blockref_stats_t *bstats)
588 {
589         cleanup_blockref_entry(&bstats->root);
590 }
591
592 static void
593 init_delta_root(struct blockref_tree *droot)
594 {
595         RB_INIT(droot);
596 }
597
598 static void
599 cleanup_delta_root(struct blockref_tree *droot)
600 {
601         cleanup_blockref_entry(droot);
602 }
603
604 static void
605 print_blockref_stats(const blockref_stats_t *bstats, bool newline)
606 {
607         size_t siz = charsperline();
608         char *buf = calloc(1, siz);
609         char emptybuf[128];
610
611         assert(buf);
612
613         if (CountEmpty)
614                 snprintf(emptybuf, sizeof(emptybuf), ", %ju empty",
615                     (uintmax_t)bstats->total_empty);
616         else
617                 strlcpy(emptybuf, "", sizeof(emptybuf));
618
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,
628                     emptybuf,
629                     sizetostr(bstats->total_bytes));
630                 break;
631         case HAMMER2_BREF_TYPE_FREEMAP:
632                 tsnprintf(buf, siz, 1, "%ju blockref (%ju node, %ju leaf%s), "
633                     "%s",
634                     (uintmax_t)bstats->total_blockref,
635                     (uintmax_t)bstats->freemap.total_freemap_node,
636                     (uintmax_t)bstats->freemap.total_freemap_leaf,
637                     emptybuf,
638                     sizetostr(bstats->total_bytes));
639                 break;
640         default:
641                 assert(0);
642                 break;
643         }
644
645         if (newline) {
646                 printf("%s\n", buf);
647         } else {
648                 printf("%s\r", buf);
649                 fflush(stdout);
650         }
651         free(buf);
652 }
653
654 static int
655 verify_volume_header(const hammer2_volume_data_t *voldata)
656 {
657         hammer2_crc32_t crc0, crc1;
658         const char *p = (const char*)voldata;
659
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);
663                 return -1;
664         }
665
666         if (voldata->magic == HAMMER2_VOLUME_ID_ABO)
667                 tfprintf(stderr, 1, "Reverse endian\n");
668
669         crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT0];
670         crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC0_OFF,
671             HAMMER2_VOLUME_ICRC0_SIZE);
672         if (crc0 != crc1) {
673                 tfprintf(stderr, 1, "Bad HAMMER2_VOL_ICRC_SECT0 CRC\n");
674                 return -1;
675         }
676
677         crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT1];
678         crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC1_OFF,
679             HAMMER2_VOLUME_ICRC1_SIZE);
680         if (crc0 != crc1) {
681                 tfprintf(stderr, 1, "Bad HAMMER2_VOL_ICRC_SECT1 CRC\n");
682                 return -1;
683         }
684
685         crc0 = voldata->icrc_volheader;
686         crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRCVH_OFF,
687             HAMMER2_VOLUME_ICRCVH_SIZE);
688         if (crc0 != crc1) {
689                 tfprintf(stderr, 1, "Bad volume header CRC\n");
690                 return -1;
691         }
692
693         return 0;
694 }
695
696 static int
697 read_media(const hammer2_blockref_t *bref, hammer2_media_data_t *media,
698     size_t *media_bytes)
699 {
700         hammer2_off_t io_off, io_base;
701         size_t bytes, io_bytes, boff;
702         int fd;
703
704         bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
705         if (bytes)
706                 bytes = (size_t)1 << bytes;
707         if (media_bytes)
708                 *media_bytes = bytes;
709
710         if (!bytes)
711                 return 0;
712
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;
716
717         io_bytes = HAMMER2_LBUFSIZE;
718         while (io_bytes + boff < bytes)
719                 io_bytes <<= 1;
720
721         if (io_bytes > sizeof(*media))
722                 return -1;
723         fd = hammer2_get_volume_fd(io_off);
724         if (lseek(fd, io_base - hammer2_get_volume_offset(io_base), SEEK_SET)
725             == -1)
726                 return -2;
727         if (read(fd, media, io_bytes) != (ssize_t)io_bytes)
728                 return -2;
729         if (boff)
730                 memmove(media, (char *)media + boff, bytes);
731
732         return 0;
733 }
734
735 static void
736 load_delta_stats(blockref_stats_t *bstats, const delta_stats_t *dstats)
737 {
738         bstats->total_blockref += dstats->total_blockref;
739         bstats->total_empty += dstats->total_empty;
740         bstats->total_bytes += dstats->total_bytes;
741
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;
748                 break;
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;
754                 break;
755         default:
756                 assert(0);
757                 break;
758         }
759 }
760
761 static void
762 accumulate_delta_stats(delta_stats_t *dst, const delta_stats_t *src)
763 {
764         dst->total_blockref += src->total_blockref;
765         dst->total_empty += src->total_empty;
766         dst->total_bytes += src->total_bytes;
767
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;
772
773         dst->freemap.total_freemap_node += src->freemap.total_freemap_node;
774         dst->freemap.total_freemap_leaf += src->freemap.total_freemap_leaf;
775
776         dst->count += src->count;
777 }
778
779 static int
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)
783 {
784         hammer2_media_data_t media;
785         hammer2_blockref_t *bscan;
786         int i, bcount;
787         bool failed = false;
788         size_t bytes;
789         uint32_t cv;
790         uint64_t cv64;
791         char msg[256];
792 #ifdef HAMMER2_USE_OPENSSL
793         SHA256_CTX hash_ctx;
794         union {
795                 uint8_t digest[SHA256_DIGEST_LENGTH];
796                 uint64_t digest64[SHA256_DIGEST_LENGTH/8];
797         } u;
798 #endif
799         /* only for DebugOpt > 1 */
800         if (DebugOpt > 1)
801                 print_blockref_debug(stdout, depth, index, bref, NULL);
802
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);
808                 if (e) {
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");
818                                         return 0;
819                                 }
820                         }
821                 }
822         }
823
824         bstats->total_blockref++;
825         dstats->total_blockref++;
826
827         switch (bref->type) {
828         case HAMMER2_BREF_TYPE_EMPTY:
829                 if (CountEmpty) {
830                         bstats->total_empty++;
831                         dstats->total_empty++;
832                 } else {
833                         bstats->total_blockref--;
834                         dstats->total_blockref--;
835                 }
836                 break;
837         case HAMMER2_BREF_TYPE_INODE:
838                 bstats->volume.total_inode++;
839                 dstats->volume.total_inode++;
840                 break;
841         case HAMMER2_BREF_TYPE_INDIRECT:
842                 bstats->volume.total_indirect++;
843                 dstats->volume.total_indirect++;
844                 break;
845         case HAMMER2_BREF_TYPE_DATA:
846                 bstats->volume.total_data++;
847                 dstats->volume.total_data++;
848                 break;
849         case HAMMER2_BREF_TYPE_DIRENT:
850                 bstats->volume.total_dirent++;
851                 dstats->volume.total_dirent++;
852                 break;
853         case HAMMER2_BREF_TYPE_FREEMAP_NODE:
854                 bstats->freemap.total_freemap_node++;
855                 dstats->freemap.total_freemap_node++;
856                 break;
857         case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
858                 bstats->freemap.total_freemap_leaf++;
859                 dstats->freemap.total_freemap_leaf++;
860                 break;
861         case HAMMER2_BREF_TYPE_VOLUME:
862                 bstats->total_blockref--;
863                 dstats->total_blockref--;
864                 break;
865         case HAMMER2_BREF_TYPE_FREEMAP:
866                 bstats->total_blockref--;
867                 dstats->total_blockref--;
868                 break;
869         default:
870                 snprintf(msg, sizeof(msg), "Invalid blockref type %d",
871                     bref->type);
872                 add_blockref_entry(&bstats->root, bref, msg, strlen(msg) + 1);
873                 print_blockref_debug(stdout, depth, index, bref, msg);
874                 failed = true;
875                 break;
876         }
877
878         switch (read_media(bref, &media, &bytes)) {
879         case -1:
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);
883                 return -1;
884         case -2:
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);
888                 return -1;
889         default:
890                 break;
891         }
892
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;
897         }
898
899         if (!CountEmpty && bref->type == HAMMER2_BREF_TYPE_EMPTY) {
900                 assert(bytes == 0);
901                 bstats->total_bytes -= bytes;
902                 dstats->total_bytes -= bytes;
903         }
904
905         if (!DebugOpt && QuietOpt <= 0 && (bstats->total_blockref % 100) == 0)
906                 print_blockref_stats(bstats, false);
907
908         if (!bytes)
909                 goto end;
910
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,
917                             strlen(msg) + 1);
918                         print_blockref_debug(stdout, depth, index, bref, msg);
919                         failed = true;
920                 }
921                 break;
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,
927                             strlen(msg) + 1);
928                         print_blockref_debug(stdout, depth, index, bref, msg);
929                         failed = true;
930                 }
931                 break;
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,
942                             strlen(msg) + 1);
943                         print_blockref_debug(stdout, depth, index, bref, msg);
944                         failed = true;
945                 }
946 #endif
947                 break;
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,
953                             strlen(msg) + 1);
954                         print_blockref_debug(stdout, depth, index, bref, msg);
955                         failed = true;
956                 }
957                 break;
958         }
959
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;
965                 } else {
966                         bscan = NULL;
967                         bcount = 0;
968                 }
969                 break;
970         case HAMMER2_BREF_TYPE_INDIRECT:
971                 bscan = &media.npdata[0];
972                 bcount = bytes / sizeof(hammer2_blockref_t);
973                 break;
974         case HAMMER2_BREF_TYPE_FREEMAP_NODE:
975                 bscan = &media.npdata[0];
976                 bcount = bytes / sizeof(hammer2_blockref_t);
977                 break;
978         case HAMMER2_BREF_TYPE_VOLUME:
979                 bscan = &media.voldata.sroot_blockset.blockref[0];
980                 bcount = HAMMER2_SET_COUNT;
981                 break;
982         case HAMMER2_BREF_TYPE_FREEMAP:
983                 bscan = &media.voldata.freemap_blockset.blockref[0];
984                 bcount = HAMMER2_SET_COUNT;
985                 break;
986         default:
987                 bscan = NULL;
988                 bcount = 0;
989                 break;
990         }
991
992         if (ForceOpt)
993                 norecurse = false;
994         /*
995          * If failed, no recurse, but still verify its direct children.
996          * Beyond that is probably garbage.
997          */
998         for (i = 0; norecurse == false && i < bcount; ++i) {
999                 delta_stats_t ds;
1000                 memset(&ds, 0, sizeof(ds));
1001                 if (verify_blockref(voldata, &bscan[i], failed, bstats, droot,
1002                     &ds, depth + 1, i) == -1)
1003                         return -1;
1004                 if (!failed)
1005                         accumulate_delta_stats(dstats, &ds);
1006         }
1007 end:
1008         if (failed)
1009                 return -1;
1010
1011         dstats->count++;
1012         if (bref->data_off && BlockrefCacheCount > 0 &&
1013             dstats->count >= BlockrefCacheCount) {
1014                 assert(bytes);
1015                 add_blockref_entry(droot, bref, dstats, sizeof(*dstats));
1016                 print_blockref_debug(stdout, depth, index, bref, "cache-add");
1017         }
1018
1019         return 0;
1020 }
1021
1022 static void
1023 print_pfs(const hammer2_inode_data_t *ipdata)
1024 {
1025         const hammer2_inode_meta_t *meta = &ipdata->meta;
1026         char *f, *pfs_id_str = NULL;
1027         const char *type_str;
1028         uuid_t uuid;
1029
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";
1036                 else
1037                         type_str = hammer2_pfssubtype_to_str(meta->pfs_subtype);
1038         } else {
1039                 type_str = hammer2_pfstype_to_str(meta->pfs_type);
1040         }
1041         tfprintf(stdout, 1, "%-11s %s %s\n", type_str, pfs_id_str, f);
1042
1043         free(f);
1044         free(pfs_id_str);
1045 }
1046
1047 static char*
1048 get_inode_filename(const hammer2_inode_data_t *ipdata)
1049 {
1050         char *p = malloc(HAMMER2_INODE_MAXNAME + 1);
1051
1052         memcpy(p, ipdata->filename, sizeof(ipdata->filename));
1053         p[HAMMER2_INODE_MAXNAME] = '\0';
1054
1055         return p;
1056 }
1057
1058 static void
1059 __add_pfs_blockref(const hammer2_blockref_t *bref, struct blockref_list *blist,
1060     const hammer2_inode_data_t *ipdata)
1061 {
1062         struct blockref_msg *newp, *p;
1063
1064         newp = calloc(1, sizeof(*newp));
1065         newp->bref = *bref;
1066         newp->msg = calloc(1, sizeof(*ipdata));
1067         memcpy(newp->msg, ipdata, sizeof(*ipdata));
1068
1069         p = TAILQ_FIRST(blist);
1070         while (p) {
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);
1075                         free(f1);
1076                         free(f2);
1077                         break;
1078                 }
1079                 p = TAILQ_NEXT(p, entry);
1080                 free(f1);
1081                 free(f2);
1082         }
1083         if (!p)
1084                 TAILQ_INSERT_TAIL(blist, newp, entry);
1085 }
1086
1087 static int
1088 init_pfs_blockref(const hammer2_volume_data_t *voldata,
1089     const hammer2_blockref_t *bref, struct blockref_list *blist)
1090 {
1091         hammer2_media_data_t media;
1092         hammer2_inode_data_t ipdata;
1093         hammer2_blockref_t *bscan;
1094         int i, bcount;
1095         size_t bytes;
1096
1097         if (read_media(bref, &media, &bytes))
1098                 return -1;
1099         if (!bytes)
1100                 return 0;
1101
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;
1108                 } else {
1109                         bscan = NULL;
1110                         bcount = 0;
1111                         if (ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT)
1112                                 __add_pfs_blockref(bref, blist, &ipdata);
1113                         else
1114                                 assert(0); /* should only see SUPROOT or PFS */
1115                 }
1116                 break;
1117         case HAMMER2_BREF_TYPE_INDIRECT:
1118                 bscan = &media.npdata[0];
1119                 bcount = bytes / sizeof(hammer2_blockref_t);
1120                 break;
1121         case HAMMER2_BREF_TYPE_VOLUME:
1122                 bscan = &media.voldata.sroot_blockset.blockref[0];
1123                 bcount = HAMMER2_SET_COUNT;
1124                 break;
1125         default:
1126                 bscan = NULL;
1127                 bcount = 0;
1128                 break;
1129         }
1130
1131         for (i = 0; i < bcount; ++i)
1132                 if (init_pfs_blockref(voldata, &bscan[i], blist) == -1)
1133                         return -1;
1134         return 0;
1135 }
1136
1137 static void
1138 cleanup_pfs_blockref(struct blockref_list *blist)
1139 {
1140         cleanup_blockref_msg(blist);
1141 }
1142
1143 static void
1144 print_media(FILE *fp, int tab, const hammer2_blockref_t *bref,
1145     const hammer2_media_data_t *media, size_t media_bytes)
1146 {
1147         const hammer2_blockref_t *bscan;
1148         const hammer2_inode_data_t *ipdata;
1149         int i, bcount, namelen;
1150         char *str = NULL;
1151         uuid_t uuid;
1152
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)
1158                         namelen = 0;
1159                 tfprintf(fp, tab, "filename \"%*.*s\"\n", namelen, namelen,
1160                     ipdata->filename);
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);
1171                 }
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");
1194                 else
1195                         printf("\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);
1224                 }
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);
1233                 break;
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,
1242                             bscan->keybits);
1243                 }
1244                 break;
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,
1250                             bref->check.buf);
1251                 } else {
1252                         tfprintf(fp, tab, "filename \"%*.*s\"\n",
1253                             bref->embed.dirent.namlen,
1254                             bref->embed.dirent.namlen,
1255                             media->buf);
1256                 }
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));
1263                 break;
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,
1272                             bscan->keybits);
1273                 }
1274                 break;
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"
1281 #endif
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]);
1295                 }
1296                 break;
1297         default:
1298                 break;
1299         }
1300         if (str)
1301                 free(str);
1302 }
1303
1304 int
1305 test_hammer2(const char *devpath)
1306 {
1307         bool failed = false;
1308
1309         hammer2_init_volumes(devpath, 1);
1310
1311         best_zone = find_best_zone();
1312         if (best_zone == -1)
1313                 fprintf(stderr, "Failed to find best zone\n");
1314
1315         if (PrintPFS) {
1316                 if (test_pfs_blockref() == -1)
1317                         failed = true;
1318                 goto end; /* print PFS info and exit */
1319         }
1320
1321         printf("volume header\n");
1322         if (test_volume_header() == -1) {
1323                 failed = true;
1324                 if (!ForceOpt)
1325                         goto end;
1326         }
1327
1328         printf("freemap\n");
1329         if (test_blockref(HAMMER2_BREF_TYPE_FREEMAP) == -1) {
1330                 failed = true;
1331                 if (!ForceOpt)
1332                         goto end;
1333         }
1334         printf("volume\n");
1335         if (!ScanPFS) {
1336                 if (test_blockref(HAMMER2_BREF_TYPE_VOLUME) == -1) {
1337                         failed = true;
1338                         if (!ForceOpt)
1339                                 goto end;
1340                 }
1341         } else {
1342                 if (test_pfs_blockref() == -1) {
1343                         failed = true;
1344                         if (!ForceOpt)
1345                                 goto end;
1346                 }
1347         }
1348 end:
1349         hammer2_cleanup_volumes();
1350
1351         return failed ? -1 : 0;
1352 }