xref: /dragonfly/sbin/fsck_hammer2/test.c (revision e436662cfa66ce7f6db187ffb059eea82ab0d1e5)
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 <stdint.h>
50 #include <string.h>
51 #include <assert.h>
52 
53 #ifdef HAMMER2_USE_OPENSSL
54 #include <openssl/sha.h>
55 #endif
56 
57 #include <vfs/hammer2/hammer2_disk.h>
58 #include <vfs/hammer2/hammer2_xxhash.h>
59 
60 #include "hammer2_subs.h"
61 #include "fsck_hammer2.h"
62 
63 struct blockref_msg {
64           TAILQ_ENTRY(blockref_msg) entry;
65           hammer2_blockref_t bref;
66           void *msg;
67 };
68 
69 TAILQ_HEAD(blockref_list, blockref_msg);
70 
71 struct blockref_entry {
72           RB_ENTRY(blockref_entry) entry;
73           hammer2_off_t data_off;
74           struct blockref_list head;
75 };
76 
77 static int
blockref_cmp(struct blockref_entry * b1,struct blockref_entry * b2)78 blockref_cmp(struct blockref_entry *b1, struct blockref_entry *b2)
79 {
80           if (b1->data_off < b2->data_off)
81                     return -1;
82           if (b1->data_off > b2->data_off)
83                     return 1;
84           return 0;
85 }
86 
87 RB_HEAD(blockref_tree, blockref_entry);
88 RB_PROTOTYPE(blockref_tree, blockref_entry, entry, blockref_cmp);
89 RB_GENERATE(blockref_tree, blockref_entry, entry, blockref_cmp);
90 
91 typedef struct {
92           struct blockref_tree root;
93           uint8_t type; /* HAMMER2_BREF_TYPE_VOLUME or FREEMAP */
94           uint64_t total_blockref;
95           uint64_t total_empty;
96           uint64_t total_bytes;
97           union {
98                     /* use volume or freemap depending on type value */
99                     struct {
100                               uint64_t total_inode;
101                               uint64_t total_indirect;
102                               uint64_t total_data;
103                               uint64_t total_dirent;
104                     } volume;
105                     struct {
106                               uint64_t total_freemap_node;
107                               uint64_t total_freemap_leaf;
108                     } freemap;
109           };
110 } blockref_stats_t;
111 
112 typedef struct {
113           uint64_t total_blockref;
114           uint64_t total_empty;
115           uint64_t total_bytes;
116           struct {
117                     uint64_t total_inode;
118                     uint64_t total_indirect;
119                     uint64_t total_data;
120                     uint64_t total_dirent;
121           } volume;
122           struct {
123                     uint64_t total_freemap_node;
124                     uint64_t total_freemap_leaf;
125           } freemap;
126           long count;
127 } delta_stats_t;
128 
129 static void print_blockref_entry(struct blockref_tree *);
130 static void init_blockref_stats(blockref_stats_t *, uint8_t);
131 static void cleanup_blockref_stats(blockref_stats_t *);
132 static void init_delta_root(struct blockref_tree *);
133 static void cleanup_delta_root(struct blockref_tree *);
134 static void print_blockref_stats(const blockref_stats_t *, bool);
135 static int verify_volume_header(const hammer2_volume_data_t *);
136 static int read_media(const hammer2_blockref_t *, hammer2_media_data_t *,
137     size_t *);
138 static int verify_blockref(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_blockref_t *,
143     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
tfprintf(FILE * fp,int tab,const char * ctl,...)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
tsnprintf(char * str,size_t siz,int tab,const char * ctl,...)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
tprintf_zone(int tab,int i,const hammer2_blockref_t * bref)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
init_root_blockref(int i,uint8_t type,hammer2_blockref_t * bref)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
find_best_zone(void)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_VOLUME_BYTES);
226                     if (ret == HAMMER2_VOLUME_BYTES) {
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
test_volume_header(void)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_VOLUME_BYTES);
268                     if (ret == HAMMER2_VOLUME_BYTES) {
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
test_blockref(uint8_t type)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_blockref_t broot;
294 
295                     if (ScanBest && i != best_zone)
296                               continue;
297                     if (i * HAMMER2_ZONE_BYTES64 >=
298                         hammer2_get_root_volume_size()) {
299                               tfprintf(stderr, 0, "zone.%d exceeds volume size\n", i);
300                               break;
301                     }
302                     init_root_blockref(i, type, &broot);
303                     blockref_stats_t bstats;
304                     init_blockref_stats(&bstats, type);
305                     delta_stats_t ds;
306                     memset(&ds, 0, sizeof(ds));
307                     tprintf_zone(0, i, &broot);
308                     if (verify_blockref(&broot, false, &bstats, &droot, &ds, 0, 0)
309                         == -1)
310                               failed = true;
311                     print_blockref_stats(&bstats, true);
312                     print_blockref_entry(&bstats.root);
313                     cleanup_blockref_stats(&bstats);
314           }
315           cleanup_delta_root(&droot);
316           return failed ? -1 : 0;
317 }
318 
319 static int
test_pfs_blockref(void)320 test_pfs_blockref(void)
321 {
322           struct blockref_tree droot;
323           uint8_t type = HAMMER2_BREF_TYPE_VOLUME;
324           bool failed = false;
325           int i;
326 
327           init_delta_root(&droot);
328           for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
329                     hammer2_blockref_t broot;
330                     struct blockref_list blist;
331                     struct blockref_msg *p;
332                     int count = 0;
333 
334                     if (ScanBest && i != best_zone)
335                               continue;
336                     if (i * HAMMER2_ZONE_BYTES64 >=
337                         hammer2_get_root_volume_size()) {
338                               tfprintf(stderr, 0, "zone.%d exceeds volume size\n", i);
339                               break;
340                     }
341                     init_root_blockref(i, type, &broot);
342                     tprintf_zone(0, i, &broot);
343                     TAILQ_INIT(&blist);
344                     if (init_pfs_blockref(&broot, &blist) == -1) {
345                               tfprintf(stderr, 1, "Failed to read PFS blockref\n");
346                               failed = true;
347                               continue;
348                     }
349                     if (TAILQ_EMPTY(&blist)) {
350                               tfprintf(stderr, 1, "Failed to find PFS blockref\n");
351                               failed = true;
352                               continue;
353                     }
354                     TAILQ_FOREACH(p, &blist, entry) {
355                               blockref_stats_t bstats;
356                               bool found = false;
357                               char *f = get_inode_filename(p->msg);
358                               if (NumPFSNames) {
359                                         int j;
360                                         for (j = 0; j < NumPFSNames; j++)
361                                                   if (!strcmp(PFSNames[j], f))
362                                                             found = true;
363                               } else
364                                         found = true;
365                               if (!found) {
366                                         free(f);
367                                         continue;
368                               }
369                               count++;
370                               if (PrintPFS) {
371                                         print_pfs(p->msg);
372                                         free(f);
373                                         continue;
374                               }
375                               tfprintf(stdout, 1, "%s\n", f);
376                               free(f);
377                               init_blockref_stats(&bstats, type);
378                               delta_stats_t ds;
379                               memset(&ds, 0, sizeof(ds));
380                               if (verify_blockref(&p->bref, false, &bstats, &droot,
381                                   &ds, 0, 0) == -1)
382                                         failed = true;
383                               print_blockref_stats(&bstats, true);
384                               print_blockref_entry(&bstats.root);
385                               cleanup_blockref_stats(&bstats);
386                     }
387                     cleanup_pfs_blockref(&blist);
388                     if (NumPFSNames && !count) {
389                               tfprintf(stderr, 1, "PFS not found\n");
390                               failed = true;
391                     }
392           }
393           cleanup_delta_root(&droot);
394           return failed ? -1 : 0;
395 }
396 
397 static int
charsperline(void)398 charsperline(void)
399 {
400           int columns;
401           char *cp;
402           struct winsize ws;
403 
404           columns = 0;
405           if (ioctl(0, TIOCGWINSZ, &ws) != -1)
406                     columns = ws.ws_col;
407           if (columns == 0 && (cp = getenv("COLUMNS")))
408                     columns = atoi(cp);
409           if (columns == 0)
410                     columns = 80;       /* last resort */
411 
412           return columns;
413 }
414 
415 static void
cleanup_blockref_msg(struct blockref_list * head)416 cleanup_blockref_msg(struct blockref_list *head)
417 {
418           struct blockref_msg *p;
419 
420           while ((p = TAILQ_FIRST(head)) != NULL) {
421                     TAILQ_REMOVE(head, p, entry);
422                     free(p->msg);
423                     free(p);
424           }
425           assert(TAILQ_EMPTY(head));
426 }
427 
428 static void
cleanup_blockref_entry(struct blockref_tree * root)429 cleanup_blockref_entry(struct blockref_tree *root)
430 {
431           struct blockref_entry *e;
432 
433           while ((e = RB_ROOT(root)) != NULL) {
434                     RB_REMOVE(blockref_tree, root, e);
435                     cleanup_blockref_msg(&e->head);
436                     free(e);
437           }
438           assert(RB_EMPTY(root));
439 }
440 
441 static void
add_blockref_msg(struct blockref_list * head,const hammer2_blockref_t * bref,const void * msg,size_t siz)442 add_blockref_msg(struct blockref_list *head, const hammer2_blockref_t *bref,
443     const void *msg, size_t siz)
444 {
445           struct blockref_msg *m;
446           void *p;
447 
448           m = calloc(1, sizeof(*m));
449           assert(m);
450           m->bref = *bref;
451           p = calloc(1, siz);
452           assert(p);
453           memcpy(p, msg, siz);
454           m->msg = p;
455 
456           TAILQ_INSERT_TAIL(head, m, entry);
457 }
458 
459 static void
add_blockref_entry(struct blockref_tree * root,const hammer2_blockref_t * bref,const void * msg,size_t siz)460 add_blockref_entry(struct blockref_tree *root, const hammer2_blockref_t *bref,
461     const void *msg, size_t siz)
462 {
463           struct blockref_entry *e, bref_find;
464 
465           memset(&bref_find, 0, sizeof(bref_find));
466           bref_find.data_off = bref->data_off;
467           e = RB_FIND(blockref_tree, root, &bref_find);
468           if (!e) {
469                     e = calloc(1, sizeof(*e));
470                     assert(e);
471                     TAILQ_INIT(&e->head);
472                     e->data_off = bref->data_off;
473           }
474 
475           add_blockref_msg(&e->head, bref, msg, siz);
476 
477           RB_INSERT(blockref_tree, root, e);
478 }
479 
480 static void
__print_blockref(FILE * fp,int tab,const hammer2_blockref_t * bref,const char * msg)481 __print_blockref(FILE *fp, int tab, const hammer2_blockref_t *bref,
482     const char *msg)
483 {
484           tfprintf(fp, tab, "%016jx %-12s %016jx/%-2d%s%s\n",
485               (uintmax_t)bref->data_off,
486               hammer2_breftype_to_str(bref->type),
487               (uintmax_t)bref->key,
488               bref->keybits,
489               msg ? " " : "",
490               msg ? msg : "");
491 }
492 
493 static void
print_blockref(FILE * fp,const hammer2_blockref_t * bref,const char * msg)494 print_blockref(FILE *fp, const hammer2_blockref_t *bref, const char *msg)
495 {
496           __print_blockref(fp, 1, bref, msg);
497 }
498 
499 static void
print_blockref_debug(FILE * fp,int depth,int index,const hammer2_blockref_t * bref,const char * msg)500 print_blockref_debug(FILE *fp, int depth, int index,
501     const hammer2_blockref_t *bref, const char *msg)
502 {
503           if (DebugOpt > 1) {
504                     char buf[256];
505                     int i;
506 
507                     memset(buf, 0, sizeof(buf));
508                     for (i = 0; i < depth * 2; i++)
509                               strlcat(buf, " ", sizeof(buf));
510                     tfprintf(fp, 1, buf);
511                     fprintf(fp, "%-2d %-3d ", depth, index);
512                     __print_blockref(fp, 0, bref, msg);
513           } else if (DebugOpt > 0)
514                     print_blockref(fp, bref, msg);
515 }
516 
517 static void
print_blockref_msg(const struct blockref_list * head)518 print_blockref_msg(const struct blockref_list *head)
519 {
520           struct blockref_msg *m;
521 
522           TAILQ_FOREACH(m, head, entry) {
523                     hammer2_blockref_t *bref = &m->bref;
524                     print_blockref(stderr, bref, m->msg);
525                     if (VerboseOpt > 0) {
526                               hammer2_media_data_t media;
527                               size_t bytes;
528                               if (!read_media(bref, &media, &bytes))
529                                         print_media(stderr, 2, bref, &media, bytes);
530                               else
531                                         tfprintf(stderr, 2, "Failed to read media\n");
532                     }
533           }
534 }
535 
536 static void
print_blockref_entry(struct blockref_tree * root)537 print_blockref_entry(struct blockref_tree *root)
538 {
539           struct blockref_entry *e;
540 
541           RB_FOREACH(e, blockref_tree, root)
542                     print_blockref_msg(&e->head);
543 }
544 
545 static void
init_blockref_stats(blockref_stats_t * bstats,uint8_t type)546 init_blockref_stats(blockref_stats_t *bstats, uint8_t type)
547 {
548           memset(bstats, 0, sizeof(*bstats));
549           RB_INIT(&bstats->root);
550           bstats->type = type;
551 }
552 
553 static void
cleanup_blockref_stats(blockref_stats_t * bstats)554 cleanup_blockref_stats(blockref_stats_t *bstats)
555 {
556           cleanup_blockref_entry(&bstats->root);
557 }
558 
559 static void
init_delta_root(struct blockref_tree * droot)560 init_delta_root(struct blockref_tree *droot)
561 {
562           RB_INIT(droot);
563 }
564 
565 static void
cleanup_delta_root(struct blockref_tree * droot)566 cleanup_delta_root(struct blockref_tree *droot)
567 {
568           cleanup_blockref_entry(droot);
569 }
570 
571 static void
print_blockref_stats(const blockref_stats_t * bstats,bool newline)572 print_blockref_stats(const blockref_stats_t *bstats, bool newline)
573 {
574           size_t siz = charsperline();
575           char *buf = calloc(1, siz);
576           char emptybuf[128];
577 
578           assert(buf);
579 
580           if (CountEmpty)
581                     snprintf(emptybuf, sizeof(emptybuf), ", %ju empty",
582                         (uintmax_t)bstats->total_empty);
583           else
584                     strlcpy(emptybuf, "", sizeof(emptybuf));
585 
586           switch (bstats->type) {
587           case HAMMER2_BREF_TYPE_VOLUME:
588                     tsnprintf(buf, siz, 1, "%ju blockref (%ju inode, %ju indirect, "
589                         "%ju data, %ju dirent%s), %s",
590                         (uintmax_t)bstats->total_blockref,
591                         (uintmax_t)bstats->volume.total_inode,
592                         (uintmax_t)bstats->volume.total_indirect,
593                         (uintmax_t)bstats->volume.total_data,
594                         (uintmax_t)bstats->volume.total_dirent,
595                         emptybuf,
596                         sizetostr(bstats->total_bytes));
597                     break;
598           case HAMMER2_BREF_TYPE_FREEMAP:
599                     tsnprintf(buf, siz, 1, "%ju blockref (%ju node, %ju leaf%s), "
600                         "%s",
601                         (uintmax_t)bstats->total_blockref,
602                         (uintmax_t)bstats->freemap.total_freemap_node,
603                         (uintmax_t)bstats->freemap.total_freemap_leaf,
604                         emptybuf,
605                         sizetostr(bstats->total_bytes));
606                     break;
607           default:
608                     assert(0);
609                     break;
610           }
611 
612           if (newline) {
613                     printf("%s\n", buf);
614           } else {
615                     printf("%s\r", buf);
616                     fflush(stdout);
617           }
618           free(buf);
619 }
620 
621 static int
verify_volume_header(const hammer2_volume_data_t * voldata)622 verify_volume_header(const hammer2_volume_data_t *voldata)
623 {
624           hammer2_crc32_t crc0, crc1;
625           const char *p = (const char*)voldata;
626 
627           if ((voldata->magic != HAMMER2_VOLUME_ID_HBO) &&
628               (voldata->magic != HAMMER2_VOLUME_ID_ABO)) {
629                     tfprintf(stderr, 1, "Bad magic %jX\n", voldata->magic);
630                     return -1;
631           }
632 
633           if (voldata->magic == HAMMER2_VOLUME_ID_ABO)
634                     tfprintf(stderr, 1, "Reverse endian\n");
635 
636           crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT0];
637           crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC0_OFF,
638               HAMMER2_VOLUME_ICRC0_SIZE);
639           if (crc0 != crc1) {
640                     tfprintf(stderr, 1, "Bad HAMMER2_VOL_ICRC_SECT0 CRC\n");
641                     return -1;
642           }
643 
644           crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT1];
645           crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC1_OFF,
646               HAMMER2_VOLUME_ICRC1_SIZE);
647           if (crc0 != crc1) {
648                     tfprintf(stderr, 1, "Bad HAMMER2_VOL_ICRC_SECT1 CRC\n");
649                     return -1;
650           }
651 
652           crc0 = voldata->icrc_volheader;
653           crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRCVH_OFF,
654               HAMMER2_VOLUME_ICRCVH_SIZE);
655           if (crc0 != crc1) {
656                     tfprintf(stderr, 1, "Bad volume header CRC\n");
657                     return -1;
658           }
659 
660           return 0;
661 }
662 
663 static int
read_media(const hammer2_blockref_t * bref,hammer2_media_data_t * media,size_t * media_bytes)664 read_media(const hammer2_blockref_t *bref, hammer2_media_data_t *media,
665     size_t *media_bytes)
666 {
667           hammer2_off_t io_off, io_base;
668           size_t bytes, io_bytes, boff;
669           int fd;
670 
671           bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
672           if (bytes)
673                     bytes = (size_t)1 << bytes;
674           if (media_bytes)
675                     *media_bytes = bytes;
676 
677           if (!bytes)
678                     return 0;
679 
680           io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
681           io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1);
682           boff = io_off - io_base;
683 
684           io_bytes = HAMMER2_LBUFSIZE;
685           while (io_bytes + boff < bytes)
686                     io_bytes <<= 1;
687 
688           if (io_bytes > sizeof(*media))
689                     return -1;
690           fd = hammer2_get_volume_fd(io_off);
691           if (lseek(fd, io_base - hammer2_get_volume_offset(io_base), SEEK_SET)
692               == -1)
693                     return -2;
694           if (read(fd, media, io_bytes) != (ssize_t)io_bytes)
695                     return -2;
696           if (boff)
697                     memmove(media, (char *)media + boff, bytes);
698 
699           return 0;
700 }
701 
702 static void
load_delta_stats(blockref_stats_t * bstats,const delta_stats_t * dstats)703 load_delta_stats(blockref_stats_t *bstats, const delta_stats_t *dstats)
704 {
705           bstats->total_blockref += dstats->total_blockref;
706           bstats->total_empty += dstats->total_empty;
707           bstats->total_bytes += dstats->total_bytes;
708 
709           switch (bstats->type) {
710           case HAMMER2_BREF_TYPE_VOLUME:
711                     bstats->volume.total_inode += dstats->volume.total_inode;
712                     bstats->volume.total_indirect += dstats->volume.total_indirect;
713                     bstats->volume.total_data += dstats->volume.total_data;
714                     bstats->volume.total_dirent += dstats->volume.total_dirent;
715                     break;
716           case HAMMER2_BREF_TYPE_FREEMAP:
717                     bstats->freemap.total_freemap_node +=
718                         dstats->freemap.total_freemap_node;
719                     bstats->freemap.total_freemap_leaf +=
720                         dstats->freemap.total_freemap_leaf;
721                     break;
722           default:
723                     assert(0);
724                     break;
725           }
726 }
727 
728 static void
accumulate_delta_stats(delta_stats_t * dst,const delta_stats_t * src)729 accumulate_delta_stats(delta_stats_t *dst, const delta_stats_t *src)
730 {
731           dst->total_blockref += src->total_blockref;
732           dst->total_empty += src->total_empty;
733           dst->total_bytes += src->total_bytes;
734 
735           dst->volume.total_inode += src->volume.total_inode;
736           dst->volume.total_indirect += src->volume.total_indirect;
737           dst->volume.total_data += src->volume.total_data;
738           dst->volume.total_dirent += src->volume.total_dirent;
739 
740           dst->freemap.total_freemap_node += src->freemap.total_freemap_node;
741           dst->freemap.total_freemap_leaf += src->freemap.total_freemap_leaf;
742 
743           dst->count += src->count;
744 }
745 
746 static int
verify_blockref(const hammer2_blockref_t * bref,bool norecurse,blockref_stats_t * bstats,struct blockref_tree * droot,delta_stats_t * dstats,int depth,int index)747 verify_blockref(const hammer2_blockref_t *bref, bool norecurse,
748     blockref_stats_t *bstats, struct blockref_tree *droot,
749     delta_stats_t *dstats, int depth, int index)
750 {
751           hammer2_media_data_t media;
752           hammer2_blockref_t *bscan;
753           int i, bcount;
754           bool failed = false;
755           size_t bytes;
756           uint32_t cv;
757           uint64_t cv64;
758           char msg[256];
759 #ifdef HAMMER2_USE_OPENSSL
760           SHA256_CTX hash_ctx;
761           union {
762                     uint8_t digest[SHA256_DIGEST_LENGTH];
763                     uint64_t digest64[SHA256_DIGEST_LENGTH/8];
764           } u;
765 #endif
766           /* only for DebugOpt > 1 */
767           if (DebugOpt > 1)
768                     print_blockref_debug(stdout, depth, index, bref, NULL);
769 
770           if (bref->data_off) {
771                     struct blockref_entry *e, bref_find;
772                     memset(&bref_find, 0, sizeof(bref_find));
773                     bref_find.data_off = bref->data_off;
774                     e = RB_FIND(blockref_tree, droot, &bref_find);
775                     if (e) {
776                               struct blockref_msg *m;
777                               TAILQ_FOREACH(m, &e->head, entry) {
778                                         delta_stats_t *ds = m->msg;
779                                         if (!memcmp(&m->bref, bref, sizeof(*bref))) {
780                                                   /* delta contains cached delta */
781                                                   accumulate_delta_stats(dstats, ds);
782                                                   load_delta_stats(bstats, ds);
783                                                   print_blockref_debug(stdout, depth,
784                                                       index, &m->bref, "cache-hit");
785                                                   return 0;
786                                         }
787                               }
788                     }
789           }
790 
791           bstats->total_blockref++;
792           dstats->total_blockref++;
793 
794           switch (bref->type) {
795           case HAMMER2_BREF_TYPE_EMPTY:
796                     if (CountEmpty) {
797                               bstats->total_empty++;
798                               dstats->total_empty++;
799                     } else {
800                               bstats->total_blockref--;
801                               dstats->total_blockref--;
802                     }
803                     break;
804           case HAMMER2_BREF_TYPE_INODE:
805                     bstats->volume.total_inode++;
806                     dstats->volume.total_inode++;
807                     break;
808           case HAMMER2_BREF_TYPE_INDIRECT:
809                     bstats->volume.total_indirect++;
810                     dstats->volume.total_indirect++;
811                     break;
812           case HAMMER2_BREF_TYPE_DATA:
813                     bstats->volume.total_data++;
814                     dstats->volume.total_data++;
815                     break;
816           case HAMMER2_BREF_TYPE_DIRENT:
817                     bstats->volume.total_dirent++;
818                     dstats->volume.total_dirent++;
819                     break;
820           case HAMMER2_BREF_TYPE_FREEMAP_NODE:
821                     bstats->freemap.total_freemap_node++;
822                     dstats->freemap.total_freemap_node++;
823                     break;
824           case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
825                     bstats->freemap.total_freemap_leaf++;
826                     dstats->freemap.total_freemap_leaf++;
827                     break;
828           case HAMMER2_BREF_TYPE_VOLUME:
829                     bstats->total_blockref--;
830                     dstats->total_blockref--;
831                     break;
832           case HAMMER2_BREF_TYPE_FREEMAP:
833                     bstats->total_blockref--;
834                     dstats->total_blockref--;
835                     break;
836           default:
837                     snprintf(msg, sizeof(msg), "Invalid blockref type %d",
838                         bref->type);
839                     add_blockref_entry(&bstats->root, bref, msg, strlen(msg) + 1);
840                     print_blockref_debug(stdout, depth, index, bref, msg);
841                     failed = true;
842                     break;
843           }
844 
845           switch (read_media(bref, &media, &bytes)) {
846           case -1:
847                     strlcpy(msg, "Bad I/O bytes", sizeof(msg));
848                     add_blockref_entry(&bstats->root, bref, msg, strlen(msg) + 1);
849                     print_blockref_debug(stdout, depth, index, bref, msg);
850                     return -1;
851           case -2:
852                     strlcpy(msg, "Failed to read media", sizeof(msg));
853                     add_blockref_entry(&bstats->root, bref, msg, strlen(msg) + 1);
854                     print_blockref_debug(stdout, depth, index, bref, msg);
855                     return -1;
856           default:
857                     break;
858           }
859 
860           if (bref->type != HAMMER2_BREF_TYPE_VOLUME &&
861               bref->type != HAMMER2_BREF_TYPE_FREEMAP) {
862                     bstats->total_bytes += bytes;
863                     dstats->total_bytes += bytes;
864           }
865 
866           if (!CountEmpty && bref->type == HAMMER2_BREF_TYPE_EMPTY) {
867                     assert(bytes == 0);
868                     bstats->total_bytes -= bytes;
869                     dstats->total_bytes -= bytes;
870           }
871 
872           if (!DebugOpt && QuietOpt <= 0 && (bstats->total_blockref % 100) == 0)
873                     print_blockref_stats(bstats, false);
874 
875           if (!bytes)
876                     goto end;
877 
878           switch (HAMMER2_DEC_CHECK(bref->methods)) {
879           case HAMMER2_CHECK_ISCSI32:
880                     cv = hammer2_icrc32(&media, bytes);
881                     if (bref->check.iscsi32.value != cv) {
882                               strlcpy(msg, "Bad HAMMER2_CHECK_ISCSI32", sizeof(msg));
883                               add_blockref_entry(&bstats->root, bref, msg,
884                                   strlen(msg) + 1);
885                               print_blockref_debug(stdout, depth, index, bref, msg);
886                               failed = true;
887                     }
888                     break;
889           case HAMMER2_CHECK_XXHASH64:
890                     cv64 = XXH64(&media, bytes, XXH_HAMMER2_SEED);
891                     if (bref->check.xxhash64.value != cv64) {
892                               strlcpy(msg, "Bad HAMMER2_CHECK_XXHASH64", sizeof(msg));
893                               add_blockref_entry(&bstats->root, bref, msg,
894                                   strlen(msg) + 1);
895                               print_blockref_debug(stdout, depth, index, bref, msg);
896                               failed = true;
897                     }
898                     break;
899           case HAMMER2_CHECK_SHA192:
900 #ifdef HAMMER2_USE_OPENSSL
901                     SHA256_Init(&hash_ctx);
902                     SHA256_Update(&hash_ctx, &media, bytes);
903                     SHA256_Final(u.digest, &hash_ctx);
904                     u.digest64[2] ^= u.digest64[3];
905                     if (memcmp(u.digest, bref->check.sha192.data,
906                         sizeof(bref->check.sha192.data))) {
907                               strlcpy(msg, "Bad HAMMER2_CHECK_SHA192", sizeof(msg));
908                               add_blockref_entry(&bstats->root, bref, msg,
909                                   strlen(msg) + 1);
910                               print_blockref_debug(stdout, depth, index, bref, msg);
911                               failed = true;
912                     }
913 #endif
914                     break;
915           case HAMMER2_CHECK_FREEMAP:
916                     cv = hammer2_icrc32(&media, bytes);
917                     if (bref->check.freemap.icrc32 != cv) {
918                               strlcpy(msg, "Bad HAMMER2_CHECK_FREEMAP", sizeof(msg));
919                               add_blockref_entry(&bstats->root, bref, msg,
920                                   strlen(msg) + 1);
921                               print_blockref_debug(stdout, depth, index, bref, msg);
922                               failed = true;
923                     }
924                     break;
925           }
926 
927           switch (bref->type) {
928           case HAMMER2_BREF_TYPE_INODE:
929                     if (!(media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA)) {
930                               bscan = &media.ipdata.u.blockset.blockref[0];
931                               bcount = HAMMER2_SET_COUNT;
932                     } else {
933                               bscan = NULL;
934                               bcount = 0;
935                     }
936                     break;
937           case HAMMER2_BREF_TYPE_INDIRECT:
938                     bscan = &media.npdata[0];
939                     bcount = bytes / sizeof(hammer2_blockref_t);
940                     break;
941           case HAMMER2_BREF_TYPE_FREEMAP_NODE:
942                     bscan = &media.npdata[0];
943                     bcount = bytes / sizeof(hammer2_blockref_t);
944                     break;
945           case HAMMER2_BREF_TYPE_VOLUME:
946                     bscan = &media.voldata.sroot_blockset.blockref[0];
947                     bcount = HAMMER2_SET_COUNT;
948                     break;
949           case HAMMER2_BREF_TYPE_FREEMAP:
950                     bscan = &media.voldata.freemap_blockset.blockref[0];
951                     bcount = HAMMER2_SET_COUNT;
952                     break;
953           default:
954                     bscan = NULL;
955                     bcount = 0;
956                     break;
957           }
958 
959           if (ForceOpt)
960                     norecurse = false;
961           /*
962            * If failed, no recurse, but still verify its direct children.
963            * Beyond that is probably garbage.
964            */
965           for (i = 0; norecurse == false && i < bcount; ++i) {
966                     delta_stats_t ds;
967                     memset(&ds, 0, sizeof(ds));
968                     if (verify_blockref(&bscan[i], failed, bstats, droot, &ds,
969                         depth + 1, i) == -1)
970                               return -1;
971                     if (!failed)
972                               accumulate_delta_stats(dstats, &ds);
973           }
974 end:
975           if (failed)
976                     return -1;
977 
978           dstats->count++;
979           if (bref->data_off && BlockrefCacheCount > 0 &&
980               dstats->count >= BlockrefCacheCount) {
981                     assert(bytes);
982                     add_blockref_entry(droot, bref, dstats, sizeof(*dstats));
983                     print_blockref_debug(stdout, depth, index, bref, "cache-add");
984           }
985 
986           return 0;
987 }
988 
989 static void
print_pfs(const hammer2_inode_data_t * ipdata)990 print_pfs(const hammer2_inode_data_t *ipdata)
991 {
992           const hammer2_inode_meta_t *meta = &ipdata->meta;
993           char *f, *pfs_id_str = NULL;
994           const char *type_str;
995           uuid_t uuid;
996 
997           f = get_inode_filename(ipdata);
998           uuid = meta->pfs_clid;
999           hammer2_uuid_to_str(&uuid, &pfs_id_str);
1000           if (meta->pfs_type == HAMMER2_PFSTYPE_MASTER) {
1001                     if (meta->pfs_subtype == HAMMER2_PFSSUBTYPE_NONE)
1002                               type_str = "MASTER";
1003                     else
1004                               type_str = hammer2_pfssubtype_to_str(meta->pfs_subtype);
1005           } else {
1006                     type_str = hammer2_pfstype_to_str(meta->pfs_type);
1007           }
1008           tfprintf(stdout, 1, "%-11s %s %s\n", type_str, pfs_id_str, f);
1009 
1010           free(f);
1011           free(pfs_id_str);
1012 }
1013 
1014 static char*
get_inode_filename(const hammer2_inode_data_t * ipdata)1015 get_inode_filename(const hammer2_inode_data_t *ipdata)
1016 {
1017           char *p = malloc(HAMMER2_INODE_MAXNAME + 1);
1018 
1019           memcpy(p, ipdata->filename, sizeof(ipdata->filename));
1020           p[HAMMER2_INODE_MAXNAME] = '\0';
1021 
1022           return p;
1023 }
1024 
1025 static void
__add_pfs_blockref(const hammer2_blockref_t * bref,struct blockref_list * blist,const hammer2_inode_data_t * ipdata)1026 __add_pfs_blockref(const hammer2_blockref_t *bref, struct blockref_list *blist,
1027     const hammer2_inode_data_t *ipdata)
1028 {
1029           struct blockref_msg *newp, *p;
1030 
1031           newp = calloc(1, sizeof(*newp));
1032           newp->bref = *bref;
1033           newp->msg = calloc(1, sizeof(*ipdata));
1034           memcpy(newp->msg, ipdata, sizeof(*ipdata));
1035 
1036           p = TAILQ_FIRST(blist);
1037           while (p) {
1038                     char *f1 = get_inode_filename(newp->msg);
1039                     char *f2 = get_inode_filename(p->msg);
1040                     if (strcmp(f1, f2) <= 0) {
1041                               TAILQ_INSERT_BEFORE(p, newp, entry);
1042                               free(f1);
1043                               free(f2);
1044                               break;
1045                     }
1046                     p = TAILQ_NEXT(p, entry);
1047                     free(f1);
1048                     free(f2);
1049           }
1050           if (!p)
1051                     TAILQ_INSERT_TAIL(blist, newp, entry);
1052 }
1053 
1054 static int
init_pfs_blockref(const hammer2_blockref_t * bref,struct blockref_list * blist)1055 init_pfs_blockref(const hammer2_blockref_t *bref, struct blockref_list *blist)
1056 {
1057           hammer2_media_data_t media;
1058           hammer2_inode_data_t ipdata;
1059           hammer2_blockref_t *bscan;
1060           int i, bcount;
1061           size_t bytes;
1062 
1063           if (read_media(bref, &media, &bytes))
1064                     return -1;
1065           if (!bytes)
1066                     return 0;
1067 
1068           switch (bref->type) {
1069           case HAMMER2_BREF_TYPE_INODE:
1070                     ipdata = media.ipdata;
1071                     if (ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
1072                               bscan = &ipdata.u.blockset.blockref[0];
1073                               bcount = HAMMER2_SET_COUNT;
1074                     } else {
1075                               bscan = NULL;
1076                               bcount = 0;
1077                               if (ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT)
1078                                         __add_pfs_blockref(bref, blist, &ipdata);
1079                               else
1080                                         assert(0); /* should only see SUPROOT or PFS */
1081                     }
1082                     break;
1083           case HAMMER2_BREF_TYPE_INDIRECT:
1084                     bscan = &media.npdata[0];
1085                     bcount = bytes / sizeof(hammer2_blockref_t);
1086                     break;
1087           case HAMMER2_BREF_TYPE_VOLUME:
1088                     bscan = &media.voldata.sroot_blockset.blockref[0];
1089                     bcount = HAMMER2_SET_COUNT;
1090                     break;
1091           default:
1092                     bscan = NULL;
1093                     bcount = 0;
1094                     break;
1095           }
1096 
1097           for (i = 0; i < bcount; ++i)
1098                     if (init_pfs_blockref(&bscan[i], blist) == -1)
1099                               return -1;
1100           return 0;
1101 }
1102 
1103 static void
cleanup_pfs_blockref(struct blockref_list * blist)1104 cleanup_pfs_blockref(struct blockref_list *blist)
1105 {
1106           cleanup_blockref_msg(blist);
1107 }
1108 
1109 static void
print_media(FILE * fp,int tab,const hammer2_blockref_t * bref,const hammer2_media_data_t * media,size_t media_bytes)1110 print_media(FILE *fp, int tab, const hammer2_blockref_t *bref,
1111     const hammer2_media_data_t *media, size_t media_bytes)
1112 {
1113           const hammer2_blockref_t *bscan;
1114           const hammer2_inode_data_t *ipdata;
1115           int i, bcount, namelen;
1116           char *str = NULL;
1117           uuid_t uuid;
1118 
1119           switch (bref->type) {
1120           case HAMMER2_BREF_TYPE_INODE:
1121                     ipdata = &media->ipdata;
1122                     namelen = ipdata->meta.name_len;
1123                     if (namelen > HAMMER2_INODE_MAXNAME)
1124                               namelen = 0;
1125                     tfprintf(fp, tab, "filename \"%*.*s\"\n", namelen, namelen,
1126                         ipdata->filename);
1127                     tfprintf(fp, tab, "version %d\n", ipdata->meta.version);
1128                     if ((ipdata->meta.op_flags & HAMMER2_OPFLAG_PFSROOT) ||
1129                         ipdata->meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT)
1130                               tfprintf(fp, tab, "pfs_subtype %d (%s)\n",
1131                                   ipdata->meta.pfs_subtype,
1132                                   hammer2_pfssubtype_to_str(ipdata->meta.pfs_subtype));
1133                     tfprintf(fp, tab, "uflags 0x%08x\n", ipdata->meta.uflags);
1134                     if (ipdata->meta.rmajor || ipdata->meta.rminor) {
1135                               tfprintf(fp, tab, "rmajor %d\n", ipdata->meta.rmajor);
1136                               tfprintf(fp, tab, "rminor %d\n", ipdata->meta.rminor);
1137                     }
1138                     tfprintf(fp, tab, "ctime %s\n",
1139                         hammer2_time64_to_str(ipdata->meta.ctime, &str));
1140                     tfprintf(fp, tab, "mtime %s\n",
1141                         hammer2_time64_to_str(ipdata->meta.mtime, &str));
1142                     tfprintf(fp, tab, "atime %s\n",
1143                         hammer2_time64_to_str(ipdata->meta.atime, &str));
1144                     tfprintf(fp, tab, "btime %s\n",
1145                         hammer2_time64_to_str(ipdata->meta.btime, &str));
1146                     uuid = ipdata->meta.uid;
1147                     tfprintf(fp, tab, "uid %s\n", hammer2_uuid_to_str(&uuid, &str));
1148                     uuid = ipdata->meta.gid;
1149                     tfprintf(fp, tab, "gid %s\n", hammer2_uuid_to_str(&uuid, &str));
1150                     tfprintf(fp, tab, "type %s\n",
1151                         hammer2_iptype_to_str(ipdata->meta.type));
1152                     tfprintf(fp, tab, "op_flags 0x%02x\n", ipdata->meta.op_flags);
1153                     tfprintf(fp, tab, "cap_flags 0x%04x\n", ipdata->meta.cap_flags);
1154                     tfprintf(fp, tab, "mode %-7o\n", ipdata->meta.mode);
1155                     tfprintf(fp, tab, "inum 0x%016jx\n", ipdata->meta.inum);
1156                     tfprintf(fp, tab, "size %ju ", (uintmax_t)ipdata->meta.size);
1157                     if (ipdata->meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA &&
1158                         ipdata->meta.size <= HAMMER2_EMBEDDED_BYTES)
1159                               fprintf(fp, "(embedded data)\n");
1160                     else
1161                               fprintf(fp, "\n");
1162                     tfprintf(fp, tab, "nlinks %ju\n",
1163                         (uintmax_t)ipdata->meta.nlinks);
1164                     tfprintf(fp, tab, "iparent 0x%016jx\n",
1165                         (uintmax_t)ipdata->meta.iparent);
1166                     tfprintf(fp, tab, "name_key 0x%016jx\n",
1167                         (uintmax_t)ipdata->meta.name_key);
1168                     tfprintf(fp, tab, "name_len %u\n", ipdata->meta.name_len);
1169                     tfprintf(fp, tab, "ncopies %u\n", ipdata->meta.ncopies);
1170                     tfprintf(fp, tab, "comp_algo %s\n",
1171                         hammer2_compmode_to_str(ipdata->meta.comp_algo));
1172                     tfprintf(fp, tab, "check_algo %s\n",
1173                         hammer2_checkmode_to_str(ipdata->meta.check_algo));
1174                     if ((ipdata->meta.op_flags & HAMMER2_OPFLAG_PFSROOT) ||
1175                         ipdata->meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
1176                               tfprintf(fp, tab, "pfs_nmasters %u\n",
1177                                   ipdata->meta.pfs_nmasters);
1178                               tfprintf(fp, tab, "pfs_type %u (%s)\n",
1179                                   ipdata->meta.pfs_type,
1180                                   hammer2_pfstype_to_str(ipdata->meta.pfs_type));
1181                               tfprintf(fp, tab, "pfs_inum 0x%016jx\n",
1182                                   (uintmax_t)ipdata->meta.pfs_inum);
1183                               uuid = ipdata->meta.pfs_clid;
1184                               tfprintf(fp, tab, "pfs_clid %s\n",
1185                                   hammer2_uuid_to_str(&uuid, &str));
1186                               uuid = ipdata->meta.pfs_fsid;
1187                               tfprintf(fp, tab, "pfs_fsid %s\n",
1188                                   hammer2_uuid_to_str(&uuid, &str));
1189                               tfprintf(fp, tab, "pfs_lsnap_tid 0x%016jx\n",
1190                                   (uintmax_t)ipdata->meta.pfs_lsnap_tid);
1191                     }
1192                     tfprintf(fp, tab, "data_quota %ju\n",
1193                         (uintmax_t)ipdata->meta.data_quota);
1194                     tfprintf(fp, tab, "data_count %ju\n",
1195                         (uintmax_t)bref->embed.stats.data_count);
1196                     tfprintf(fp, tab, "inode_quota %ju\n",
1197                         (uintmax_t)ipdata->meta.inode_quota);
1198                     tfprintf(fp, tab, "inode_count %ju\n",
1199                         (uintmax_t)bref->embed.stats.inode_count);
1200                     break;
1201           case HAMMER2_BREF_TYPE_INDIRECT:
1202                     bcount = media_bytes / sizeof(hammer2_blockref_t);
1203                     for (i = 0; i < bcount; ++i) {
1204                               bscan = &media->npdata[i];
1205                               tfprintf(fp, tab, "%3d %016jx %-12s %016jx/%-2d\n",
1206                                   i, (uintmax_t)bscan->data_off,
1207                                   hammer2_breftype_to_str(bscan->type),
1208                                   (uintmax_t)bscan->key,
1209                                   bscan->keybits);
1210                     }
1211                     break;
1212           case HAMMER2_BREF_TYPE_DIRENT:
1213                     if (bref->embed.dirent.namlen <= sizeof(bref->check.buf)) {
1214                               tfprintf(fp, tab, "filename \"%*.*s\"\n",
1215                                   bref->embed.dirent.namlen,
1216                                   bref->embed.dirent.namlen,
1217                                   bref->check.buf);
1218                     } else {
1219                               tfprintf(fp, tab, "filename \"%*.*s\"\n",
1220                                   bref->embed.dirent.namlen,
1221                                   bref->embed.dirent.namlen,
1222                                   media->buf);
1223                     }
1224                     tfprintf(fp, tab, "inum 0x%016jx\n",
1225                         (uintmax_t)bref->embed.dirent.inum);
1226                     tfprintf(fp, tab, "namlen %d\n",
1227                         (uintmax_t)bref->embed.dirent.namlen);
1228                     tfprintf(fp, tab, "type %s\n",
1229                         hammer2_iptype_to_str(bref->embed.dirent.type));
1230                     break;
1231           case HAMMER2_BREF_TYPE_FREEMAP_NODE:
1232                     bcount = media_bytes / sizeof(hammer2_blockref_t);
1233                     for (i = 0; i < bcount; ++i) {
1234                               bscan = &media->npdata[i];
1235                               tfprintf(fp, tab, "%3d %016jx %-12s %016jx/%-2d\n",
1236                                   i, (uintmax_t)bscan->data_off,
1237                                   hammer2_breftype_to_str(bscan->type),
1238                                   (uintmax_t)bscan->key,
1239                                   bscan->keybits);
1240                     }
1241                     break;
1242           case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
1243                     for (i = 0; i < HAMMER2_FREEMAP_COUNT; ++i) {
1244                               hammer2_off_t data_off = bref->key +
1245                                         i * HAMMER2_FREEMAP_LEVEL0_SIZE;
1246 #if HAMMER2_BMAP_ELEMENTS != 8
1247 #error "HAMMER2_BMAP_ELEMENTS != 8"
1248 #endif
1249                               tfprintf(fp, tab, "%016jx %04d.%04x (avail=%7d) "
1250                                   "%016jx %016jx %016jx %016jx "
1251                                   "%016jx %016jx %016jx %016jx\n",
1252                                   data_off, i, media->bmdata[i].class,
1253                                   media->bmdata[i].avail,
1254                                   media->bmdata[i].bitmapq[0],
1255                                   media->bmdata[i].bitmapq[1],
1256                                   media->bmdata[i].bitmapq[2],
1257                                   media->bmdata[i].bitmapq[3],
1258                                   media->bmdata[i].bitmapq[4],
1259                                   media->bmdata[i].bitmapq[5],
1260                                   media->bmdata[i].bitmapq[6],
1261                                   media->bmdata[i].bitmapq[7]);
1262                     }
1263                     break;
1264           default:
1265                     break;
1266           }
1267           if (str)
1268                     free(str);
1269 }
1270 
1271 int
test_hammer2(const char * devpath)1272 test_hammer2(const char *devpath)
1273 {
1274           bool failed = false;
1275 
1276           hammer2_init_volumes(devpath, 1);
1277 
1278           best_zone = find_best_zone();
1279           if (best_zone == -1)
1280                     fprintf(stderr, "Failed to find best zone\n");
1281 
1282           if (PrintPFS) {
1283                     if (test_pfs_blockref() == -1)
1284                               failed = true;
1285                     goto end; /* print PFS info and exit */
1286           }
1287 
1288           printf("volume header\n");
1289           if (test_volume_header() == -1) {
1290                     failed = true;
1291                     if (!ForceOpt)
1292                               goto end;
1293           }
1294 
1295           printf("freemap\n");
1296           if (test_blockref(HAMMER2_BREF_TYPE_FREEMAP) == -1) {
1297                     failed = true;
1298                     if (!ForceOpt)
1299                               goto end;
1300           }
1301           printf("volume\n");
1302           if (!ScanPFS) {
1303                     if (test_blockref(HAMMER2_BREF_TYPE_VOLUME) == -1) {
1304                               failed = true;
1305                               if (!ForceOpt)
1306                                         goto end;
1307                     }
1308           } else {
1309                     if (test_pfs_blockref() == -1) {
1310                               failed = true;
1311                               if (!ForceOpt)
1312                                         goto end;
1313                     }
1314           }
1315 end:
1316           hammer2_cleanup_volumes();
1317 
1318           return failed ? -1 : 0;
1319 }
1320