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