sbin/fsck_hammer2: Allow -l to take multiple PFS names
[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 <unistd.h>
43 #include <fcntl.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <stdarg.h>
47 #include <stdbool.h>
48 #include <string.h>
49 #include <assert.h>
50
51 #include <openssl/sha.h>
52
53 #include <vfs/hammer2/hammer2_disk.h>
54 #include <vfs/hammer2/hammer2_xxhash.h>
55
56 #include "hammer2_subs.h"
57 #include "fsck_hammer2.h"
58
59 struct blockref_msg {
60         TAILQ_ENTRY(blockref_msg) entry;
61         hammer2_blockref_t bref;
62         char *msg;
63 };
64
65 TAILQ_HEAD(blockref_list, blockref_msg);
66
67 struct blockref_entry {
68         RB_ENTRY(blockref_entry) entry;
69         hammer2_off_t data_off;
70         struct blockref_list head;
71 };
72
73 static int
74 blockref_cmp(struct blockref_entry *b1, struct blockref_entry *b2)
75 {
76         if (b1->data_off < b2->data_off)
77                 return -1;
78         if (b1->data_off > b2->data_off)
79                 return 1;
80         return 0;
81 }
82
83 RB_HEAD(blockref_tree, blockref_entry);
84 RB_PROTOTYPE2(blockref_tree, blockref_entry, entry, blockref_cmp,
85     hammer2_off_t);
86 RB_GENERATE2(blockref_tree, blockref_entry, entry, blockref_cmp, hammer2_off_t,
87     data_off);
88
89 typedef struct {
90         struct blockref_tree root;
91         uint8_t type; /* HAMMER2_BREF_TYPE_VOLUME or FREEMAP */
92         uint64_t total_blockref;
93         uint64_t total_empty;
94         uint64_t total_invalid;
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 static void print_blockref_entry(int, struct blockref_tree *);
112 static void init_blockref_stats(blockref_stats_t *, uint8_t);
113 static void cleanup_blockref_stats(blockref_stats_t *);
114 static void print_blockref_stats(const blockref_stats_t *, bool);
115 static int verify_volume_header(const hammer2_volume_data_t *);
116 static int read_media(int, const hammer2_blockref_t *, hammer2_media_data_t *,
117     size_t *);
118 static int verify_blockref(int, const hammer2_volume_data_t *,
119     const hammer2_blockref_t *, bool, blockref_stats_t *);
120 static int init_pfs_blockref(int, const hammer2_volume_data_t *,
121     const hammer2_blockref_t *, struct blockref_list *);
122 static void cleanup_pfs_blockref(struct blockref_list *);
123 static void print_media(FILE *, int, const hammer2_blockref_t *,
124     hammer2_media_data_t *, size_t);
125
126 static int best_zone = -1;
127
128 #define TAB 8
129
130 static void
131 tfprintf(FILE *fp, int tab, const char *ctl, ...)
132 {
133         va_list va;
134         int ret;
135
136         ret = fprintf(fp, "%*s", tab * TAB, "");
137         if (ret < 0)
138                 return;
139
140         va_start(va, ctl);
141         vfprintf(fp, ctl, va);
142         va_end(va);
143 }
144
145 static void
146 tsnprintf(char *str, size_t siz, int tab, const char *ctl, ...)
147 {
148         va_list va;
149         int ret;
150
151         ret = snprintf(str, siz, "%*s", tab * TAB, "");
152         if (ret < 0 || ret >= (int)siz)
153                 return;
154
155         va_start(va, ctl);
156         vsnprintf(str + ret, siz, ctl, va);
157         va_end(va);
158 }
159
160 static void
161 tprintf_zone(int tab, int i, const hammer2_blockref_t *bref)
162 {
163         tfprintf(stdout, tab, "zone.%d %016jx%s\n",
164             i, (uintmax_t)bref->data_off,
165             (!ScanBest && i == best_zone) ? " (best)" : "");
166 }
167
168 static int
169 init_root_blockref(int fd, int i, uint8_t type, hammer2_blockref_t *bref)
170 {
171         assert(type == HAMMER2_BREF_TYPE_EMPTY ||
172                 type == HAMMER2_BREF_TYPE_VOLUME ||
173                 type == HAMMER2_BREF_TYPE_FREEMAP);
174         memset(bref, 0, sizeof(*bref));
175         bref->type = type;
176         bref->data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
177
178         return lseek(fd, bref->data_off & ~HAMMER2_OFF_MASK_RADIX, SEEK_SET);
179 }
180
181 static int
182 find_best_zone(int fd)
183 {
184         hammer2_blockref_t best;
185         int i, best_i = -1;
186
187         memset(&best, 0, sizeof(best));
188
189         for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
190                 hammer2_volume_data_t voldata;
191                 hammer2_blockref_t broot;
192                 ssize_t ret;
193
194                 init_root_blockref(fd, i, HAMMER2_BREF_TYPE_EMPTY, &broot);
195                 ret = read(fd, &voldata, HAMMER2_PBUFSIZE);
196                 if (ret == HAMMER2_PBUFSIZE) {
197                         if ((voldata.magic != HAMMER2_VOLUME_ID_HBO) &&
198                             (voldata.magic != HAMMER2_VOLUME_ID_ABO))
199                                 continue;
200                         broot.mirror_tid = voldata.mirror_tid;
201                         if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
202                                 best_i = i;
203                                 best = broot;
204                         }
205                 } else if (ret == -1) {
206                         perror("read");
207                         return -1;
208                 } else {
209                         tfprintf(stderr, 1, "Failed to read volume header\n");
210                         return -1;
211                 }
212         }
213
214         return best_i;
215 }
216
217 static int
218 test_volume_header(int fd)
219 {
220         bool failed = false;
221         int i;
222
223         for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
224                 hammer2_volume_data_t voldata;
225                 hammer2_blockref_t broot;
226                 ssize_t ret;
227
228                 if (ScanBest && i != best_zone)
229                         continue;
230                 init_root_blockref(fd, i, HAMMER2_BREF_TYPE_EMPTY, &broot);
231                 ret = read(fd, &voldata, HAMMER2_PBUFSIZE);
232                 if (ret == HAMMER2_PBUFSIZE) {
233                         tprintf_zone(0, i, &broot);
234                         if (verify_volume_header(&voldata) == -1)
235                                 failed = true;
236                 } else if (ret == -1) {
237                         perror("read");
238                         return -1;
239                 } else {
240                         tfprintf(stderr, 1, "Failed to read volume header\n");
241                         return -1;
242                 }
243         }
244
245         return failed ? -1 : 0;
246 }
247
248 static int
249 test_blockref(int fd, uint8_t type)
250 {
251         bool failed = false;
252         int i;
253
254         for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
255                 hammer2_volume_data_t voldata;
256                 hammer2_blockref_t broot;
257                 ssize_t ret;
258
259                 if (ScanBest && i != best_zone)
260                         continue;
261                 init_root_blockref(fd, i, type, &broot);
262                 ret = read(fd, &voldata, HAMMER2_PBUFSIZE);
263                 if (ret == HAMMER2_PBUFSIZE) {
264                         blockref_stats_t bstats;
265                         init_blockref_stats(&bstats, type);
266                         tprintf_zone(0, i, &broot);
267                         if (verify_blockref(fd, &voldata, &broot, false,
268                             &bstats) == -1)
269                                 failed = true;
270                         print_blockref_stats(&bstats, true);
271                         print_blockref_entry(fd, &bstats.root);
272                         cleanup_blockref_stats(&bstats);
273                 } else if (ret == -1) {
274                         perror("read");
275                         return -1;
276                 } else {
277                         tfprintf(stderr, 1, "Failed to read volume header\n");
278                         return -1;
279                 }
280         }
281
282         return failed ? -1 : 0;
283 }
284
285 static int
286 test_pfs_blockref(int fd)
287 {
288         uint8_t type = HAMMER2_BREF_TYPE_VOLUME;
289         bool failed = false;
290         int i;
291
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                 init_root_blockref(fd, i, type, &broot);
300                 ret = read(fd, &voldata, HAMMER2_PBUFSIZE);
301                 if (ret == HAMMER2_PBUFSIZE) {
302                         struct blockref_list blist;
303                         struct blockref_msg *p;
304                         int count = 0;
305
306                         tprintf_zone(0, i, &broot);
307                         TAILQ_INIT(&blist);
308                         if (init_pfs_blockref(fd, &voldata, &broot, &blist) ==
309                             -1) {
310                                 tfprintf(stderr, 1, "Failed to read PFS "
311                                     "blockref\n");
312                                 failed = true;
313                                 continue;
314                         }
315                         if (TAILQ_EMPTY(&blist)) {
316                                 tfprintf(stderr, 1, "Failed to find PFS "
317                                     "blockref\n");
318                                 failed = true;
319                                 continue;
320                         }
321                         TAILQ_FOREACH(p, &blist, entry) {
322                                 blockref_stats_t bstats;
323                                 bool found = false;
324                                 if (NumPFSNames) {
325                                         int j;
326                                         for (j = 0; j < NumPFSNames; j++)
327                                                 if (!strcmp(PFSNames[j],
328                                                     p->msg))
329                                                         found = true;
330                                 } else
331                                         found = true;
332                                 if (!found)
333                                         continue;
334                                 count++;
335                                 tfprintf(stdout, 1, "%s\n", p->msg);
336                                 init_blockref_stats(&bstats, type);
337                                 if (verify_blockref(fd, &voldata, &p->bref,
338                                     false, &bstats) == -1)
339                                         failed = true;
340                                 print_blockref_stats(&bstats, true);
341                                 print_blockref_entry(fd, &bstats.root);
342                                 cleanup_blockref_stats(&bstats);
343                         }
344                         cleanup_pfs_blockref(&blist);
345                         if (NumPFSNames && !count) {
346                                 tfprintf(stderr, 1, "PFS not found\n");
347                                 failed = true;
348                         }
349                 } else if (ret == -1) {
350                         perror("read");
351                         return -1;
352                 } else {
353                         tfprintf(stderr, 1, "Failed to read volume header\n");
354                         return -1;
355                 }
356         }
357
358         return failed ? -1 : 0;
359 }
360
361 static int
362 charsperline(void)
363 {
364         int columns;
365         char *cp;
366         struct winsize ws;
367
368         columns = 0;
369         if (ioctl(0, TIOCGWINSZ, &ws) != -1)
370                 columns = ws.ws_col;
371         if (columns == 0 && (cp = getenv("COLUMNS")))
372                 columns = atoi(cp);
373         if (columns == 0)
374                 columns = 80;   /* last resort */
375
376         return columns;
377 }
378
379 static void
380 cleanup_blockref_msg(struct blockref_list *head)
381 {
382         struct blockref_msg *p;
383
384         while ((p = TAILQ_FIRST(head)) != NULL) {
385                 TAILQ_REMOVE(head, p, entry);
386                 free(p->msg);
387                 free(p);
388         }
389         assert(TAILQ_EMPTY(head));
390 }
391
392 static void
393 cleanup_blockref_entry(struct blockref_tree *root)
394 {
395         struct blockref_entry *e;
396
397         while ((e = RB_ROOT(root)) != NULL) {
398                 RB_REMOVE(blockref_tree, root, e);
399                 cleanup_blockref_msg(&e->head);
400                 free(e);
401         }
402         assert(RB_EMPTY(root));
403 }
404
405 static void
406 add_blockref_msg(struct blockref_list *head, const hammer2_blockref_t *bref,
407     const char *msg)
408 {
409         struct blockref_msg *m;
410
411         m = calloc(1, sizeof(*m));
412         assert(m);
413         m->bref = *bref;
414         m->msg = strdup(msg);
415
416         TAILQ_INSERT_TAIL(head, m, entry);
417 }
418
419 static void
420 add_blockref_entry(struct blockref_tree *root, const hammer2_blockref_t *bref,
421     const char *msg)
422 {
423         struct blockref_entry *e;
424
425         e = RB_LOOKUP(blockref_tree, root, bref->data_off);
426         if (!e) {
427                 e = calloc(1, sizeof(*e));
428                 assert(e);
429                 TAILQ_INIT(&e->head);
430                 e->data_off = bref->data_off;
431         }
432
433         add_blockref_msg(&e->head, bref, msg);
434
435         RB_INSERT(blockref_tree, root, e);
436 }
437
438 static void
439 print_blockref_msg(int fd, struct blockref_list *head)
440 {
441         struct blockref_msg *m;
442
443         TAILQ_FOREACH(m, head, entry) {
444                 hammer2_blockref_t *bref = &m->bref;
445                 hammer2_media_data_t media;
446
447                 tfprintf(stderr, 1, "%016jx %-12s %016jx/%-2d \"%s\"\n",
448                     (uintmax_t)bref->data_off,
449                     hammer2_breftype_to_str(bref->type),
450                     (uintmax_t)bref->key,
451                     bref->keybits,
452                     m->msg);
453
454                 if (fd != -1 && VerboseOpt > 0) {
455                         size_t bytes;
456                         if (!read_media(fd, bref, &media, &bytes))
457                                 print_media(stderr, 2, bref, &media, bytes);
458                         else
459                                 tfprintf(stderr, 2, "Failed to read media\n");
460                 }
461         }
462 }
463
464 static void
465 print_blockref_entry(int fd, struct blockref_tree *root)
466 {
467         struct blockref_entry *e;
468
469         RB_FOREACH(e, blockref_tree, root)
470                 print_blockref_msg(fd, &e->head);
471 }
472
473 static void
474 init_blockref_stats(blockref_stats_t *bstats, uint8_t type)
475 {
476         memset(bstats, 0, sizeof(*bstats));
477         RB_INIT(&bstats->root);
478         bstats->type = type;
479 }
480
481 static void
482 cleanup_blockref_stats(blockref_stats_t *bstats)
483 {
484         cleanup_blockref_entry(&bstats->root);
485 }
486
487 static void
488 print_blockref_stats(const blockref_stats_t *bstats, bool newline)
489 {
490         size_t siz = charsperline();
491         char *buf = calloc(1, siz);
492         char emptybuf[128];
493
494         assert(buf);
495
496         if (CountEmpty)
497                 snprintf(emptybuf, sizeof(emptybuf), ", %ju empty",
498                     (uintmax_t)bstats->total_empty);
499         else
500                 strlcpy(emptybuf, "", sizeof(emptybuf));
501
502         switch (bstats->type) {
503         case HAMMER2_BREF_TYPE_VOLUME:
504                 tsnprintf(buf, siz, 1, "%ju blockref (%ju inode, %ju indirect, "
505                     "%ju data, %ju dirent%s), %s",
506                     (uintmax_t)bstats->total_blockref,
507                     (uintmax_t)bstats->volume.total_inode,
508                     (uintmax_t)bstats->volume.total_indirect,
509                     (uintmax_t)bstats->volume.total_data,
510                     (uintmax_t)bstats->volume.total_dirent,
511                     emptybuf,
512                     sizetostr(bstats->total_bytes));
513                 break;
514         case HAMMER2_BREF_TYPE_FREEMAP:
515                 tsnprintf(buf, siz, 1, "%ju blockref (%ju node, %ju leaf%s), "
516                     "%s",
517                     (uintmax_t)bstats->total_blockref,
518                     (uintmax_t)bstats->freemap.total_freemap_node,
519                     (uintmax_t)bstats->freemap.total_freemap_leaf,
520                     emptybuf,
521                     sizetostr(bstats->total_bytes));
522                 break;
523         default:
524                 assert(0);
525                 break;
526         }
527
528         if (newline) {
529                 printf("%s\n", buf);
530         } else {
531                 printf("%s\r", buf);
532                 fflush(stdout);
533         }
534         free(buf);
535 }
536
537 static int
538 verify_volume_header(const hammer2_volume_data_t *voldata)
539 {
540         hammer2_crc32_t crc0, crc1;
541         const char *p = (const char*)voldata;
542
543         if ((voldata->magic != HAMMER2_VOLUME_ID_HBO) &&
544             (voldata->magic != HAMMER2_VOLUME_ID_ABO)) {
545                 tfprintf(stderr, 1, "Bad magic %jX\n", voldata->magic);
546                 return -1;
547         }
548
549         if (voldata->magic == HAMMER2_VOLUME_ID_ABO)
550                 tfprintf(stderr, 1, "Reverse endian\n");
551
552         crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT0];
553         crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC0_OFF,
554             HAMMER2_VOLUME_ICRC0_SIZE);
555         if (crc0 != crc1) {
556                 tfprintf(stderr, 1, "Bad HAMMER2_VOL_ICRC_SECT0 CRC\n");
557                 return -1;
558         }
559
560         crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT1];
561         crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC1_OFF,
562             HAMMER2_VOLUME_ICRC1_SIZE);
563         if (crc0 != crc1) {
564                 tfprintf(stderr, 1, "Bad HAMMER2_VOL_ICRC_SECT1 CRC\n");
565                 return -1;
566         }
567
568         crc0 = voldata->icrc_volheader;
569         crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRCVH_OFF,
570             HAMMER2_VOLUME_ICRCVH_SIZE);
571         if (crc0 != crc1) {
572                 tfprintf(stderr, 1, "Bad volume header CRC\n");
573                 return -1;
574         }
575
576         return 0;
577 }
578
579 static int
580 read_media(int fd, const hammer2_blockref_t *bref, hammer2_media_data_t *media,
581     size_t *media_bytes)
582 {
583         hammer2_off_t io_off, io_base;
584         size_t bytes, io_bytes, boff;
585
586         bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
587         if (bytes)
588                 bytes = (size_t)1 << bytes;
589         if (media_bytes)
590                 *media_bytes = bytes;
591
592         if (!bytes)
593                 return 0;
594
595         io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
596         io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1);
597         boff = io_off - io_base;
598
599         io_bytes = HAMMER2_MINIOSIZE;
600         while (io_bytes + boff < bytes)
601                 io_bytes <<= 1;
602
603         if (io_bytes > sizeof(*media))
604                 return -1;
605         if (lseek(fd, io_base, SEEK_SET) == -1)
606                 return -2;
607         if (read(fd, media, io_bytes) != (ssize_t)io_bytes)
608                 return -2;
609         if (boff)
610                 memmove(media, (char *)media + boff, bytes);
611
612         return 0;
613 }
614
615 static int
616 verify_blockref(int fd, const hammer2_volume_data_t *voldata,
617     const hammer2_blockref_t *bref, bool norecurse, blockref_stats_t *bstats)
618 {
619         hammer2_media_data_t media;
620         hammer2_blockref_t *bscan;
621         int i, bcount;
622         bool failed = false;
623         size_t bytes;
624         uint32_t cv;
625         uint64_t cv64;
626         char msg[256];
627
628         SHA256_CTX hash_ctx;
629         union {
630                 uint8_t digest[SHA256_DIGEST_LENGTH];
631                 uint64_t digest64[SHA256_DIGEST_LENGTH/8];
632         } u;
633
634         bstats->total_blockref++;
635
636         switch (bref->type) {
637         case HAMMER2_BREF_TYPE_EMPTY:
638                 if (CountEmpty)
639                         bstats->total_empty++;
640                 else
641                         bstats->total_blockref--;
642                 break;
643         case HAMMER2_BREF_TYPE_INODE:
644                 bstats->volume.total_inode++;
645                 break;
646         case HAMMER2_BREF_TYPE_INDIRECT:
647                 bstats->volume.total_indirect++;
648                 break;
649         case HAMMER2_BREF_TYPE_DATA:
650                 bstats->volume.total_data++;
651                 break;
652         case HAMMER2_BREF_TYPE_DIRENT:
653                 bstats->volume.total_dirent++;
654                 break;
655         case HAMMER2_BREF_TYPE_FREEMAP_NODE:
656                 bstats->freemap.total_freemap_node++;
657                 break;
658         case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
659                 bstats->freemap.total_freemap_leaf++;
660                 break;
661         case HAMMER2_BREF_TYPE_VOLUME:
662                 bstats->total_blockref--;
663                 break;
664         case HAMMER2_BREF_TYPE_FREEMAP:
665                 bstats->total_blockref--;
666                 break;
667         default:
668                 bstats->total_invalid++;
669                 snprintf(msg, sizeof(msg), "Invalid blockref type %d",
670                     bref->type);
671                 add_blockref_entry(&bstats->root, bref, msg);
672                 failed = true;
673                 break;
674         }
675
676         switch (read_media(fd, bref, &media, &bytes)) {
677         case -1:
678                 add_blockref_entry(&bstats->root, bref, "Bad I/O bytes");
679                 return -1;
680         case -2:
681                 add_blockref_entry(&bstats->root, bref, "Failed to read media");
682                 return -1;
683         default:
684                 break;
685         }
686
687         if (bref->type != HAMMER2_BREF_TYPE_VOLUME &&
688             bref->type != HAMMER2_BREF_TYPE_FREEMAP)
689                 bstats->total_bytes += bytes;
690
691         if (!CountEmpty && bref->type == HAMMER2_BREF_TYPE_EMPTY) {
692                 assert(bytes == 0);
693                 bstats->total_bytes -= bytes;
694         }
695
696         if (QuietOpt <= 0 && (bstats->total_blockref % 100) == 0)
697                 print_blockref_stats(bstats, false);
698
699         if (!bytes)
700                 return 0;
701
702         switch (HAMMER2_DEC_CHECK(bref->methods)) {
703         case HAMMER2_CHECK_ISCSI32:
704                 cv = hammer2_icrc32(&media, bytes);
705                 if (bref->check.iscsi32.value != cv) {
706                         add_blockref_entry(&bstats->root, bref,
707                             "Bad HAMMER2_CHECK_ISCSI32");
708                         failed = true;
709                 }
710                 break;
711         case HAMMER2_CHECK_XXHASH64:
712                 cv64 = XXH64(&media, bytes, XXH_HAMMER2_SEED);
713                 if (bref->check.xxhash64.value != cv64) {
714                         add_blockref_entry(&bstats->root, bref,
715                             "Bad HAMMER2_CHECK_XXHASH64");
716                         failed = true;
717                 }
718                 break;
719         case HAMMER2_CHECK_SHA192:
720                 SHA256_Init(&hash_ctx);
721                 SHA256_Update(&hash_ctx, &media, bytes);
722                 SHA256_Final(u.digest, &hash_ctx);
723                 u.digest64[2] ^= u.digest64[3];
724                 if (memcmp(u.digest, bref->check.sha192.data,
725                     sizeof(bref->check.sha192.data))) {
726                         add_blockref_entry(&bstats->root, bref,
727                             "Bad HAMMER2_CHECK_SHA192");
728                         failed = true;
729                 }
730                 break;
731         case HAMMER2_CHECK_FREEMAP:
732                 cv = hammer2_icrc32(&media, bytes);
733                 if (bref->check.freemap.icrc32 != cv) {
734                         add_blockref_entry(&bstats->root, bref,
735                             "Bad HAMMER2_CHECK_FREEMAP");
736                         failed = true;
737                 }
738                 break;
739         }
740
741         switch (bref->type) {
742         case HAMMER2_BREF_TYPE_INODE:
743                 if (!(media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA)) {
744                         bscan = &media.ipdata.u.blockset.blockref[0];
745                         bcount = HAMMER2_SET_COUNT;
746                 } else {
747                         bscan = NULL;
748                         bcount = 0;
749                 }
750                 break;
751         case HAMMER2_BREF_TYPE_INDIRECT:
752                 bscan = &media.npdata[0];
753                 bcount = bytes / sizeof(hammer2_blockref_t);
754                 break;
755         case HAMMER2_BREF_TYPE_FREEMAP_NODE:
756                 bscan = &media.npdata[0];
757                 bcount = bytes / sizeof(hammer2_blockref_t);
758                 break;
759         case HAMMER2_BREF_TYPE_VOLUME:
760                 bscan = &media.voldata.sroot_blockset.blockref[0];
761                 bcount = HAMMER2_SET_COUNT;
762                 break;
763         case HAMMER2_BREF_TYPE_FREEMAP:
764                 bscan = &media.voldata.freemap_blockset.blockref[0];
765                 bcount = HAMMER2_SET_COUNT;
766                 break;
767         default:
768                 bscan = NULL;
769                 bcount = 0;
770                 break;
771         }
772
773         if (ForceOpt)
774                 norecurse = false;
775         /*
776          * If failed, no recurse, but still verify its direct children.
777          * Beyond that is probably garbage.
778          */
779         for (i = 0; norecurse == false && i < bcount; ++i)
780                 if (verify_blockref(fd, voldata, &bscan[i], failed, bstats)
781                     == -1)
782                         return -1;
783         return failed ? -1 : 0;
784 }
785
786 static int
787 init_pfs_blockref(int fd, const hammer2_volume_data_t *voldata,
788     const hammer2_blockref_t *bref, struct blockref_list *blist)
789 {
790         hammer2_media_data_t media;
791         hammer2_inode_data_t ipdata;
792         hammer2_blockref_t *bscan;
793         int i, bcount;
794         size_t bytes;
795
796         if (read_media(fd, bref, &media, &bytes))
797                 return -1;
798         if (!bytes)
799                 return 0;
800
801         switch (bref->type) {
802         case HAMMER2_BREF_TYPE_INODE:
803                 ipdata = media.ipdata;
804                 if (ipdata.meta.pfs_type & HAMMER2_PFSTYPE_SUPROOT) {
805                         bscan = &ipdata.u.blockset.blockref[0];
806                         bcount = HAMMER2_SET_COUNT;
807                 } else {
808                         bscan = NULL;
809                         bcount = 0;
810                         if (ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) {
811                                 struct blockref_msg *p;
812                                 p = calloc(1, sizeof(*p));
813                                 assert(p);
814                                 p->bref = *bref;
815                                 p->msg = strdup(ipdata.filename);
816                                 TAILQ_INSERT_TAIL(blist, p, entry);
817                         } else
818                                 assert(0); /* should only see SUPROOT or PFS */
819                 }
820                 break;
821         case HAMMER2_BREF_TYPE_INDIRECT:
822                 bscan = &media.npdata[0];
823                 bcount = bytes / sizeof(hammer2_blockref_t);
824                 break;
825         case HAMMER2_BREF_TYPE_VOLUME:
826                 bscan = &media.voldata.sroot_blockset.blockref[0];
827                 bcount = HAMMER2_SET_COUNT;
828                 break;
829         default:
830                 bscan = NULL;
831                 bcount = 0;
832                 break;
833         }
834
835         for (i = 0; i < bcount; ++i)
836                 if (init_pfs_blockref(fd, voldata, &bscan[i], blist) == -1)
837                         return -1;
838         return 0;
839 }
840
841 static void
842 cleanup_pfs_blockref(struct blockref_list *blist)
843 {
844         cleanup_blockref_msg(blist);
845 }
846
847 static void
848 print_media(FILE *fp, int tab, const hammer2_blockref_t *bref,
849     hammer2_media_data_t *media, size_t media_bytes)
850 {
851         hammer2_blockref_t *bscan;
852         hammer2_inode_data_t *ipdata;
853         int i, bcount, namelen;
854         char *str = NULL;
855
856         switch (bref->type) {
857         case HAMMER2_BREF_TYPE_INODE:
858                 ipdata = &media->ipdata;
859                 namelen = ipdata->meta.name_len;
860                 if (namelen > HAMMER2_INODE_MAXNAME)
861                         namelen = 0;
862                 tfprintf(fp, tab, "filename \"%*.*s\"\n", namelen, namelen,
863                     ipdata->filename);
864                 tfprintf(fp, tab, "version %d\n", ipdata->meta.version);
865                 tfprintf(fp, tab, "pfs_subtype %d\n", ipdata->meta.pfs_subtype);
866                 tfprintf(fp, tab, "uflags 0x%08x\n", ipdata->meta.uflags);
867                 if (ipdata->meta.rmajor || ipdata->meta.rminor) {
868                         tfprintf(fp, tab, "rmajor %d\n", ipdata->meta.rmajor);
869                         tfprintf(fp, tab, "rminor %d\n", ipdata->meta.rminor);
870                 }
871                 tfprintf(fp, tab, "ctime %s\n",
872                     hammer2_time64_to_str(ipdata->meta.ctime, &str));
873                 tfprintf(fp, tab, "mtime %s\n",
874                     hammer2_time64_to_str(ipdata->meta.mtime, &str));
875                 tfprintf(fp, tab, "atime %s\n",
876                     hammer2_time64_to_str(ipdata->meta.atime, &str));
877                 tfprintf(fp, tab, "btime %s\n",
878                     hammer2_time64_to_str(ipdata->meta.btime, &str));
879                 tfprintf(fp, tab, "uid %s\n",
880                     hammer2_uuid_to_str(&ipdata->meta.uid, &str));
881                 tfprintf(fp, tab, "gid %s\n",
882                     hammer2_uuid_to_str(&ipdata->meta.gid, &str));
883                 tfprintf(fp, tab, "type %s\n",
884                     hammer2_iptype_to_str(ipdata->meta.type));
885                 tfprintf(fp, tab, "op_flags 0x%02x\n", ipdata->meta.op_flags);
886                 tfprintf(fp, tab, "cap_flags 0x%04x\n", ipdata->meta.cap_flags);
887                 tfprintf(fp, tab, "mode %-7o\n", ipdata->meta.mode);
888                 tfprintf(fp, tab, "inum 0x%016jx\n", ipdata->meta.inum);
889                 tfprintf(fp, tab, "size %ju ", (uintmax_t)ipdata->meta.size);
890                 if (ipdata->meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA &&
891                     ipdata->meta.size <= HAMMER2_EMBEDDED_BYTES)
892                         printf("(embedded data)\n");
893                 else
894                         printf("\n");
895                 tfprintf(fp, tab, "nlinks %ju\n",
896                     (uintmax_t)ipdata->meta.nlinks);
897                 tfprintf(fp, tab, "iparent 0x%016jx\n",
898                     (uintmax_t)ipdata->meta.iparent);
899                 tfprintf(fp, tab, "name_key 0x%016jx\n",
900                     (uintmax_t)ipdata->meta.name_key);
901                 tfprintf(fp, tab, "name_len %u\n", ipdata->meta.name_len);
902                 tfprintf(fp, tab, "ncopies %u\n", ipdata->meta.ncopies);
903                 tfprintf(fp, tab, "comp_algo %u\n", ipdata->meta.comp_algo);
904                 tfprintf(fp, tab, "target_type %u\n", ipdata->meta.target_type);
905                 tfprintf(fp, tab, "check_algo %u\n", ipdata->meta.check_algo);
906                 if ((ipdata->meta.op_flags & HAMMER2_OPFLAG_PFSROOT) ||
907                     ipdata->meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
908                         tfprintf(fp, tab, "pfs_nmasters %u\n",
909                             ipdata->meta.pfs_nmasters);
910                         tfprintf(fp, tab, "pfs_type %u (%s)\n",
911                             ipdata->meta.pfs_type,
912                             hammer2_pfstype_to_str(ipdata->meta.pfs_type));
913                         tfprintf(fp, tab, "pfs_inum 0x%016jx\n",
914                             (uintmax_t)ipdata->meta.pfs_inum);
915                         tfprintf(fp, tab, "pfs_clid %s\n",
916                             hammer2_uuid_to_str(&ipdata->meta.pfs_clid, &str));
917                         tfprintf(fp, tab, "pfs_fsid %s\n",
918                             hammer2_uuid_to_str(&ipdata->meta.pfs_fsid, &str));
919                         tfprintf(fp, tab, "pfs_lsnap_tid 0x%016jx\n",
920                             (uintmax_t)ipdata->meta.pfs_lsnap_tid);
921                 }
922                 tfprintf(fp, tab, "data_quota %ju\n",
923                     (uintmax_t)ipdata->meta.data_quota);
924                 tfprintf(fp, tab, "data_count %ju\n",
925                     (uintmax_t)bref->embed.stats.data_count);
926                 tfprintf(fp, tab, "inode_quota %ju\n",
927                     (uintmax_t)ipdata->meta.inode_quota);
928                 tfprintf(fp, tab, "inode_count %ju\n",
929                     (uintmax_t)bref->embed.stats.inode_count);
930                 break;
931         case HAMMER2_BREF_TYPE_INDIRECT:
932                 bcount = media_bytes / sizeof(hammer2_blockref_t);
933                 for (i = 0; i < bcount; ++i) {
934                         bscan = &media->npdata[i];
935                         tfprintf(fp, tab, "%3d %016jx %-12s %016jx/%-2d\n",
936                             i, (uintmax_t)bscan->data_off,
937                             hammer2_breftype_to_str(bscan->type),
938                             (uintmax_t)bscan->key,
939                             bscan->keybits);
940                 }
941                 break;
942         case HAMMER2_BREF_TYPE_DIRENT:
943                 if (bref->embed.dirent.namlen <= sizeof(bref->check.buf)) {
944                         tfprintf(fp, tab, "filename \"%*.*s\"\n",
945                             bref->embed.dirent.namlen,
946                             bref->embed.dirent.namlen,
947                             bref->check.buf);
948                 } else {
949                         tfprintf(fp, tab, "filename \"%*.*s\"\n",
950                             bref->embed.dirent.namlen,
951                             bref->embed.dirent.namlen,
952                             media->buf);
953                 }
954                 tfprintf(fp, tab, "inum 0x%016jx\n",
955                     (uintmax_t)bref->embed.dirent.inum);
956                 tfprintf(fp, tab, "namlen %d\n",
957                     (uintmax_t)bref->embed.dirent.namlen);
958                 tfprintf(fp, tab, "type %s\n",
959                     hammer2_iptype_to_str(bref->embed.dirent.type));
960                 break;
961         case HAMMER2_BREF_TYPE_FREEMAP_NODE:
962                 bcount = media_bytes / sizeof(hammer2_blockref_t);
963                 for (i = 0; i < bcount; ++i) {
964                         bscan = &media->npdata[i];
965                         tfprintf(fp, tab, "%3d %016jx %-12s %016jx/%-2d\n",
966                             i, (uintmax_t)bscan->data_off,
967                             hammer2_breftype_to_str(bscan->type),
968                             (uintmax_t)bscan->key,
969                             bscan->keybits);
970                 }
971                 break;
972         case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
973                 for (i = 0; i < HAMMER2_FREEMAP_COUNT; ++i) {
974                         hammer2_off_t data_off = bref->key +
975                                 i * HAMMER2_FREEMAP_LEVEL0_SIZE;
976 #if HAMMER2_BMAP_ELEMENTS != 8
977 #error "HAMMER2_BMAP_ELEMENTS != 8"
978 #endif
979                         tfprintf(fp, tab, "%016jx %04d.%04x (avail=%7d) "
980                             "%016jx %016jx %016jx %016jx "
981                             "%016jx %016jx %016jx %016jx\n",
982                             data_off, i, media->bmdata[i].class,
983                             media->bmdata[i].avail,
984                             media->bmdata[i].bitmapq[0],
985                             media->bmdata[i].bitmapq[1],
986                             media->bmdata[i].bitmapq[2],
987                             media->bmdata[i].bitmapq[3],
988                             media->bmdata[i].bitmapq[4],
989                             media->bmdata[i].bitmapq[5],
990                             media->bmdata[i].bitmapq[6],
991                             media->bmdata[i].bitmapq[7]);
992                 }
993                 break;
994         default:
995                 break;
996         }
997         if (str)
998                 free(str);
999 }
1000
1001 int
1002 test_hammer2(const char *devpath)
1003 {
1004         struct stat st;
1005         bool failed = false;
1006         int fd;
1007
1008         fd = open(devpath, O_RDONLY);
1009         if (fd == -1) {
1010                 perror("open");
1011                 return -1;
1012         }
1013
1014         if (fstat(fd, &st) == -1) {
1015                 perror("fstat");
1016                 failed = true;
1017                 goto end;
1018         }
1019         if (!S_ISCHR(st.st_mode)) {
1020                 fprintf(stderr, "%s is not a block device\n", devpath);
1021                 failed = true;
1022                 goto end;
1023         }
1024
1025         best_zone = find_best_zone(fd);
1026         if (best_zone == -1)
1027                 fprintf(stderr, "Failed to find best zone\n");
1028
1029         printf("volume header\n");
1030         if (test_volume_header(fd) == -1) {
1031                 failed = true;
1032                 if (!ForceOpt)
1033                         goto end;
1034         }
1035
1036         printf("freemap\n");
1037         if (test_blockref(fd, HAMMER2_BREF_TYPE_FREEMAP) == -1) {
1038                 failed = true;
1039                 if (!ForceOpt)
1040                         goto end;
1041         }
1042         printf("volume\n");
1043         if (!ScanPFS) {
1044                 if (test_blockref(fd, HAMMER2_BREF_TYPE_VOLUME) == -1) {
1045                         failed = true;
1046                         if (!ForceOpt)
1047                                 goto end;
1048                 }
1049         } else {
1050                 if (test_pfs_blockref(fd) == -1) {
1051                         failed = true;
1052                         if (!ForceOpt)
1053                                 goto end;
1054                 }
1055         }
1056 end:
1057         close(fd);
1058
1059         return failed ? -1 : 0;
1060 }