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