xref: /dragonfly/sbin/fsck_hammer2/test.c (revision b3f6432c)
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 *, int, int);
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, 0, 0) == -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, 0, 0) == -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 __inline void
476 __print_blockref(FILE *fp, int tab, const hammer2_blockref_t *bref,
477     const char *msg)
478 {
479 	tfprintf(fp, tab, "%016jx %-12s %016jx/%-2d%s%s\n",
480 	    (uintmax_t)bref->data_off,
481 	    hammer2_breftype_to_str(bref->type),
482 	    (uintmax_t)bref->key,
483 	    bref->keybits,
484 	    msg ? " " : "",
485 	    msg ? msg : "");
486 }
487 
488 static void
489 print_blockref(FILE *fp, const hammer2_blockref_t *bref, const char *msg)
490 {
491 	__print_blockref(fp, 1, bref, msg);
492 }
493 
494 static void
495 print_blockref_verbose(FILE *fp, int depth, int index,
496     const hammer2_blockref_t *bref, const char *msg)
497 {
498 	if (DebugOpt > 1) {
499 		char buf[256];
500 		int i;
501 
502 		memset(buf, 0, sizeof(buf));
503 		for (i = 0; i < depth * 2; i++)
504 			strlcat(buf, " ", sizeof(buf));
505 		tfprintf(fp, 1, buf);
506 		fprintf(fp, "%-2d %-3d ", depth, index);
507 		__print_blockref(fp, 0, bref, msg);
508 	} else
509 		print_blockref(fp, bref, msg);
510 }
511 
512 static void
513 print_blockref_msg(int fd, struct blockref_list *head)
514 {
515 	struct blockref_msg *m;
516 
517 	TAILQ_FOREACH(m, head, entry) {
518 		hammer2_blockref_t *bref = &m->bref;
519 		print_blockref(stderr, bref, m->msg);
520 		if (fd != -1 && VerboseOpt > 0) {
521 			hammer2_media_data_t media;
522 			size_t bytes;
523 			if (!read_media(fd, bref, &media, &bytes))
524 				print_media(stderr, 2, bref, &media, bytes);
525 			else
526 				tfprintf(stderr, 2, "Failed to read media\n");
527 		}
528 	}
529 }
530 
531 static void
532 print_blockref_entry(int fd, struct blockref_tree *root)
533 {
534 	struct blockref_entry *e;
535 
536 	RB_FOREACH(e, blockref_tree, root)
537 		print_blockref_msg(fd, &e->head);
538 }
539 
540 static void
541 init_blockref_stats(blockref_stats_t *bstats, uint8_t type)
542 {
543 	memset(bstats, 0, sizeof(*bstats));
544 	RB_INIT(&bstats->root);
545 	bstats->type = type;
546 }
547 
548 static void
549 cleanup_blockref_stats(blockref_stats_t *bstats)
550 {
551 	cleanup_blockref_entry(&bstats->root);
552 }
553 
554 static void
555 init_delta_root(struct blockref_tree *droot)
556 {
557 	RB_INIT(droot);
558 }
559 
560 static void
561 cleanup_delta_root(struct blockref_tree *droot)
562 {
563 	cleanup_blockref_entry(droot);
564 }
565 
566 static void
567 print_blockref_stats(const blockref_stats_t *bstats, bool newline)
568 {
569 	size_t siz = charsperline();
570 	char *buf = calloc(1, siz);
571 	char emptybuf[128];
572 
573 	assert(buf);
574 
575 	if (CountEmpty)
576 		snprintf(emptybuf, sizeof(emptybuf), ", %ju empty",
577 		    (uintmax_t)bstats->total_empty);
578 	else
579 		strlcpy(emptybuf, "", sizeof(emptybuf));
580 
581 	switch (bstats->type) {
582 	case HAMMER2_BREF_TYPE_VOLUME:
583 		tsnprintf(buf, siz, 1, "%ju blockref (%ju inode, %ju indirect, "
584 		    "%ju data, %ju dirent%s), %s",
585 		    (uintmax_t)bstats->total_blockref,
586 		    (uintmax_t)bstats->volume.total_inode,
587 		    (uintmax_t)bstats->volume.total_indirect,
588 		    (uintmax_t)bstats->volume.total_data,
589 		    (uintmax_t)bstats->volume.total_dirent,
590 		    emptybuf,
591 		    sizetostr(bstats->total_bytes));
592 		break;
593 	case HAMMER2_BREF_TYPE_FREEMAP:
594 		tsnprintf(buf, siz, 1, "%ju blockref (%ju node, %ju leaf%s), "
595 		    "%s",
596 		    (uintmax_t)bstats->total_blockref,
597 		    (uintmax_t)bstats->freemap.total_freemap_node,
598 		    (uintmax_t)bstats->freemap.total_freemap_leaf,
599 		    emptybuf,
600 		    sizetostr(bstats->total_bytes));
601 		break;
602 	default:
603 		assert(0);
604 		break;
605 	}
606 
607 	if (newline) {
608 		printf("%s\n", buf);
609 	} else {
610 		printf("%s\r", buf);
611 		fflush(stdout);
612 	}
613 	free(buf);
614 }
615 
616 static int
617 verify_volume_header(const hammer2_volume_data_t *voldata)
618 {
619 	hammer2_crc32_t crc0, crc1;
620 	const char *p = (const char*)voldata;
621 
622 	if ((voldata->magic != HAMMER2_VOLUME_ID_HBO) &&
623 	    (voldata->magic != HAMMER2_VOLUME_ID_ABO)) {
624 		tfprintf(stderr, 1, "Bad magic %jX\n", voldata->magic);
625 		return -1;
626 	}
627 
628 	if (voldata->magic == HAMMER2_VOLUME_ID_ABO)
629 		tfprintf(stderr, 1, "Reverse endian\n");
630 
631 	crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT0];
632 	crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC0_OFF,
633 	    HAMMER2_VOLUME_ICRC0_SIZE);
634 	if (crc0 != crc1) {
635 		tfprintf(stderr, 1, "Bad HAMMER2_VOL_ICRC_SECT0 CRC\n");
636 		return -1;
637 	}
638 
639 	crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT1];
640 	crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC1_OFF,
641 	    HAMMER2_VOLUME_ICRC1_SIZE);
642 	if (crc0 != crc1) {
643 		tfprintf(stderr, 1, "Bad HAMMER2_VOL_ICRC_SECT1 CRC\n");
644 		return -1;
645 	}
646 
647 	crc0 = voldata->icrc_volheader;
648 	crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRCVH_OFF,
649 	    HAMMER2_VOLUME_ICRCVH_SIZE);
650 	if (crc0 != crc1) {
651 		tfprintf(stderr, 1, "Bad volume header CRC\n");
652 		return -1;
653 	}
654 
655 	return 0;
656 }
657 
658 static int
659 read_media(int fd, const hammer2_blockref_t *bref, hammer2_media_data_t *media,
660     size_t *media_bytes)
661 {
662 	hammer2_off_t io_off, io_base;
663 	size_t bytes, io_bytes, boff;
664 
665 	bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
666 	if (bytes)
667 		bytes = (size_t)1 << bytes;
668 	if (media_bytes)
669 		*media_bytes = bytes;
670 
671 	if (!bytes)
672 		return 0;
673 
674 	io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
675 	io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1);
676 	boff = io_off - io_base;
677 
678 	io_bytes = HAMMER2_MINIOSIZE;
679 	while (io_bytes + boff < bytes)
680 		io_bytes <<= 1;
681 
682 	if (io_bytes > sizeof(*media))
683 		return -1;
684 	if (lseek(fd, io_base, SEEK_SET) == -1)
685 		return -2;
686 	if (read(fd, media, io_bytes) != (ssize_t)io_bytes)
687 		return -2;
688 	if (boff)
689 		memmove(media, (char *)media + boff, bytes);
690 
691 	return 0;
692 }
693 
694 static void
695 load_delta_stats(blockref_stats_t *bstats, const delta_stats_t *dstats)
696 {
697 	bstats->total_blockref += dstats->total_blockref;
698 	bstats->total_empty += dstats->total_empty;
699 	bstats->total_bytes += dstats->total_bytes;
700 
701 	switch (bstats->type) {
702 	case HAMMER2_BREF_TYPE_VOLUME:
703 		bstats->volume.total_inode += dstats->volume.total_inode;
704 		bstats->volume.total_indirect += dstats->volume.total_indirect;
705 		bstats->volume.total_data += dstats->volume.total_data;
706 		bstats->volume.total_dirent += dstats->volume.total_dirent;
707 		break;
708 	case HAMMER2_BREF_TYPE_FREEMAP:
709 		bstats->freemap.total_freemap_node +=
710 		    dstats->freemap.total_freemap_node;
711 		bstats->freemap.total_freemap_leaf +=
712 		    dstats->freemap.total_freemap_leaf;
713 		break;
714 	default:
715 		assert(0);
716 		break;
717 	}
718 }
719 
720 static void
721 accumulate_delta_stats(delta_stats_t *dst, const delta_stats_t *src)
722 {
723 	dst->total_blockref += src->total_blockref;
724 	dst->total_empty += src->total_empty;
725 	dst->total_bytes += src->total_bytes;
726 
727 	dst->volume.total_inode += src->volume.total_inode;
728 	dst->volume.total_indirect += src->volume.total_indirect;
729 	dst->volume.total_data += src->volume.total_data;
730 	dst->volume.total_dirent += src->volume.total_dirent;
731 
732 	dst->freemap.total_freemap_node += src->freemap.total_freemap_node;
733 	dst->freemap.total_freemap_leaf += src->freemap.total_freemap_leaf;
734 
735 	dst->count += src->count;
736 }
737 
738 static int
739 verify_blockref(int fd, const hammer2_volume_data_t *voldata,
740     const hammer2_blockref_t *bref, bool norecurse, blockref_stats_t *bstats,
741     struct blockref_tree *droot, delta_stats_t *dstats, int depth, int index)
742 {
743 	hammer2_media_data_t media;
744 	hammer2_blockref_t *bscan;
745 	int i, bcount;
746 	bool failed = false;
747 	size_t bytes;
748 	uint32_t cv;
749 	uint64_t cv64;
750 	char msg[256];
751 
752 	SHA256_CTX hash_ctx;
753 	union {
754 		uint8_t digest[SHA256_DIGEST_LENGTH];
755 		uint64_t digest64[SHA256_DIGEST_LENGTH/8];
756 	} u;
757 
758 	if (DebugOpt > 1)
759 		print_blockref_verbose(stdout, depth, index, bref, NULL);
760 
761 	if (bref->data_off) {
762 		struct blockref_entry *e;
763 		e = RB_LOOKUP(blockref_tree, droot, bref->data_off);
764 		if (e) {
765 			struct blockref_msg *m;
766 			TAILQ_FOREACH(m, &e->head, entry) {
767 				delta_stats_t *ds = m->msg;
768 				if (!memcmp(&m->bref, bref, sizeof(*bref))) {
769 					if (DebugOpt)
770 						print_blockref_verbose(stdout,
771 						    depth, index, &m->bref,
772 						    "cache-hit");
773 					/* delta contains cached delta */
774 					accumulate_delta_stats(dstats, ds);
775 					load_delta_stats(bstats, ds);
776 					return 0;
777 				}
778 			}
779 		}
780 	}
781 
782 	bstats->total_blockref++;
783 	dstats->total_blockref++;
784 
785 	switch (bref->type) {
786 	case HAMMER2_BREF_TYPE_EMPTY:
787 		if (CountEmpty) {
788 			bstats->total_empty++;
789 			dstats->total_empty++;
790 		} else {
791 			bstats->total_blockref--;
792 			dstats->total_blockref--;
793 		}
794 		break;
795 	case HAMMER2_BREF_TYPE_INODE:
796 		bstats->volume.total_inode++;
797 		dstats->volume.total_inode++;
798 		break;
799 	case HAMMER2_BREF_TYPE_INDIRECT:
800 		bstats->volume.total_indirect++;
801 		dstats->volume.total_indirect++;
802 		break;
803 	case HAMMER2_BREF_TYPE_DATA:
804 		bstats->volume.total_data++;
805 		dstats->volume.total_data++;
806 		break;
807 	case HAMMER2_BREF_TYPE_DIRENT:
808 		bstats->volume.total_dirent++;
809 		dstats->volume.total_dirent++;
810 		break;
811 	case HAMMER2_BREF_TYPE_FREEMAP_NODE:
812 		bstats->freemap.total_freemap_node++;
813 		dstats->freemap.total_freemap_node++;
814 		break;
815 	case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
816 		bstats->freemap.total_freemap_leaf++;
817 		dstats->freemap.total_freemap_leaf++;
818 		break;
819 	case HAMMER2_BREF_TYPE_VOLUME:
820 		bstats->total_blockref--;
821 		dstats->total_blockref--;
822 		break;
823 	case HAMMER2_BREF_TYPE_FREEMAP:
824 		bstats->total_blockref--;
825 		dstats->total_blockref--;
826 		break;
827 	default:
828 		snprintf(msg, sizeof(msg), "Invalid blockref type %d",
829 		    bref->type);
830 		add_blockref_entry(&bstats->root, bref, msg, strlen(msg) + 1);
831 		failed = true;
832 		break;
833 	}
834 
835 	switch (read_media(fd, bref, &media, &bytes)) {
836 	case -1:
837 		strlcpy(msg, "Bad I/O bytes", sizeof(msg));
838 		add_blockref_entry(&bstats->root, bref, msg, strlen(msg) + 1);
839 		return -1;
840 	case -2:
841 		strlcpy(msg, "Failed to read media", sizeof(msg));
842 		add_blockref_entry(&bstats->root, bref, msg, strlen(msg) + 1);
843 		return -1;
844 	default:
845 		break;
846 	}
847 
848 	if (bref->type != HAMMER2_BREF_TYPE_VOLUME &&
849 	    bref->type != HAMMER2_BREF_TYPE_FREEMAP) {
850 		bstats->total_bytes += bytes;
851 		dstats->total_bytes += bytes;
852 	}
853 
854 	if (!CountEmpty && bref->type == HAMMER2_BREF_TYPE_EMPTY) {
855 		assert(bytes == 0);
856 		bstats->total_bytes -= bytes;
857 		dstats->total_bytes -= bytes;
858 	}
859 
860 	if (!DebugOpt && QuietOpt <= 0 && (bstats->total_blockref % 100) == 0)
861 		print_blockref_stats(bstats, false);
862 
863 	if (!bytes)
864 		goto end;
865 
866 	switch (HAMMER2_DEC_CHECK(bref->methods)) {
867 	case HAMMER2_CHECK_ISCSI32:
868 		cv = hammer2_icrc32(&media, bytes);
869 		if (bref->check.iscsi32.value != cv) {
870 			strlcpy(msg, "Bad HAMMER2_CHECK_ISCSI32", sizeof(msg));
871 			add_blockref_entry(&bstats->root, bref, msg,
872 			    strlen(msg) + 1);
873 			failed = true;
874 		}
875 		break;
876 	case HAMMER2_CHECK_XXHASH64:
877 		cv64 = XXH64(&media, bytes, XXH_HAMMER2_SEED);
878 		if (bref->check.xxhash64.value != cv64) {
879 			strlcpy(msg, "Bad HAMMER2_CHECK_XXHASH64", sizeof(msg));
880 			add_blockref_entry(&bstats->root, bref, msg,
881 			    strlen(msg) + 1);
882 			failed = true;
883 		}
884 		break;
885 	case HAMMER2_CHECK_SHA192:
886 		SHA256_Init(&hash_ctx);
887 		SHA256_Update(&hash_ctx, &media, bytes);
888 		SHA256_Final(u.digest, &hash_ctx);
889 		u.digest64[2] ^= u.digest64[3];
890 		if (memcmp(u.digest, bref->check.sha192.data,
891 		    sizeof(bref->check.sha192.data))) {
892 			strlcpy(msg, "Bad HAMMER2_CHECK_SHA192", sizeof(msg));
893 			add_blockref_entry(&bstats->root, bref, msg,
894 			    strlen(msg) + 1);
895 			failed = true;
896 		}
897 		break;
898 	case HAMMER2_CHECK_FREEMAP:
899 		cv = hammer2_icrc32(&media, bytes);
900 		if (bref->check.freemap.icrc32 != cv) {
901 			strlcpy(msg, "Bad HAMMER2_CHECK_FREEMAP", sizeof(msg));
902 			add_blockref_entry(&bstats->root, bref, msg,
903 			    strlen(msg) + 1);
904 			failed = true;
905 		}
906 		break;
907 	}
908 
909 	switch (bref->type) {
910 	case HAMMER2_BREF_TYPE_INODE:
911 		if (!(media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA)) {
912 			bscan = &media.ipdata.u.blockset.blockref[0];
913 			bcount = HAMMER2_SET_COUNT;
914 		} else {
915 			bscan = NULL;
916 			bcount = 0;
917 		}
918 		break;
919 	case HAMMER2_BREF_TYPE_INDIRECT:
920 		bscan = &media.npdata[0];
921 		bcount = bytes / sizeof(hammer2_blockref_t);
922 		break;
923 	case HAMMER2_BREF_TYPE_FREEMAP_NODE:
924 		bscan = &media.npdata[0];
925 		bcount = bytes / sizeof(hammer2_blockref_t);
926 		break;
927 	case HAMMER2_BREF_TYPE_VOLUME:
928 		bscan = &media.voldata.sroot_blockset.blockref[0];
929 		bcount = HAMMER2_SET_COUNT;
930 		break;
931 	case HAMMER2_BREF_TYPE_FREEMAP:
932 		bscan = &media.voldata.freemap_blockset.blockref[0];
933 		bcount = HAMMER2_SET_COUNT;
934 		break;
935 	default:
936 		bscan = NULL;
937 		bcount = 0;
938 		break;
939 	}
940 
941 	if (ForceOpt)
942 		norecurse = false;
943 	/*
944 	 * If failed, no recurse, but still verify its direct children.
945 	 * Beyond that is probably garbage.
946 	 */
947 	for (i = 0; norecurse == false && i < bcount; ++i) {
948 		delta_stats_t ds;
949 		memset(&ds, 0, sizeof(ds));
950 		if (verify_blockref(fd, voldata, &bscan[i], failed, bstats,
951 		    droot, &ds, depth + 1, i) == -1)
952 			return -1;
953 		if (!failed)
954 			accumulate_delta_stats(dstats, &ds);
955 	}
956 end:
957 	if (failed)
958 		return -1;
959 
960 	dstats->count++;
961 	if (bref->data_off && BlockrefCacheCount > 0 &&
962 	    dstats->count >= BlockrefCacheCount) {
963 		assert(bytes);
964 		if (DebugOpt)
965 			print_blockref_verbose(stdout, depth, index, bref,
966 			    "cache-add");
967 		add_blockref_entry(droot, bref, dstats, sizeof(*dstats));
968 	}
969 
970 	return 0;
971 }
972 
973 static int
974 init_pfs_blockref(int fd, const hammer2_volume_data_t *voldata,
975     const hammer2_blockref_t *bref, struct blockref_list *blist)
976 {
977 	hammer2_media_data_t media;
978 	hammer2_inode_data_t ipdata;
979 	hammer2_blockref_t *bscan;
980 	int i, bcount;
981 	size_t bytes;
982 
983 	if (read_media(fd, bref, &media, &bytes))
984 		return -1;
985 	if (!bytes)
986 		return 0;
987 
988 	switch (bref->type) {
989 	case HAMMER2_BREF_TYPE_INODE:
990 		ipdata = media.ipdata;
991 		if (ipdata.meta.pfs_type & HAMMER2_PFSTYPE_SUPROOT) {
992 			bscan = &ipdata.u.blockset.blockref[0];
993 			bcount = HAMMER2_SET_COUNT;
994 		} else {
995 			bscan = NULL;
996 			bcount = 0;
997 			if (ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) {
998 				struct blockref_msg *newp, *p;
999 				newp = calloc(1, sizeof(*newp));
1000 				assert(newp);
1001 				newp->bref = *bref;
1002 				newp->msg = calloc(1,
1003 				    sizeof(ipdata.filename) + 1);
1004 				memcpy(newp->msg, ipdata.filename,
1005 				    sizeof(ipdata.filename));
1006 				p = TAILQ_FIRST(blist);
1007 				while (p) {
1008 					if (strcmp(newp->msg, p->msg) <= 0) {
1009 						TAILQ_INSERT_BEFORE(p, newp,
1010 						    entry);
1011 						break;
1012 					}
1013 					p = TAILQ_NEXT(p, entry);
1014 				}
1015 				if (!p)
1016 					TAILQ_INSERT_TAIL(blist, newp, entry);
1017 			} else
1018 				assert(0); /* should only see SUPROOT or PFS */
1019 		}
1020 		break;
1021 	case HAMMER2_BREF_TYPE_INDIRECT:
1022 		bscan = &media.npdata[0];
1023 		bcount = bytes / sizeof(hammer2_blockref_t);
1024 		break;
1025 	case HAMMER2_BREF_TYPE_VOLUME:
1026 		bscan = &media.voldata.sroot_blockset.blockref[0];
1027 		bcount = HAMMER2_SET_COUNT;
1028 		break;
1029 	default:
1030 		bscan = NULL;
1031 		bcount = 0;
1032 		break;
1033 	}
1034 
1035 	for (i = 0; i < bcount; ++i)
1036 		if (init_pfs_blockref(fd, voldata, &bscan[i], blist) == -1)
1037 			return -1;
1038 	return 0;
1039 }
1040 
1041 static void
1042 cleanup_pfs_blockref(struct blockref_list *blist)
1043 {
1044 	cleanup_blockref_msg(blist);
1045 }
1046 
1047 static void
1048 print_media(FILE *fp, int tab, const hammer2_blockref_t *bref,
1049     hammer2_media_data_t *media, size_t media_bytes)
1050 {
1051 	hammer2_blockref_t *bscan;
1052 	hammer2_inode_data_t *ipdata;
1053 	int i, bcount, namelen;
1054 	char *str = NULL;
1055 
1056 	switch (bref->type) {
1057 	case HAMMER2_BREF_TYPE_INODE:
1058 		ipdata = &media->ipdata;
1059 		namelen = ipdata->meta.name_len;
1060 		if (namelen > HAMMER2_INODE_MAXNAME)
1061 			namelen = 0;
1062 		tfprintf(fp, tab, "filename \"%*.*s\"\n", namelen, namelen,
1063 		    ipdata->filename);
1064 		tfprintf(fp, tab, "version %d\n", ipdata->meta.version);
1065 		tfprintf(fp, tab, "pfs_subtype %d\n", ipdata->meta.pfs_subtype);
1066 		tfprintf(fp, tab, "uflags 0x%08x\n", ipdata->meta.uflags);
1067 		if (ipdata->meta.rmajor || ipdata->meta.rminor) {
1068 			tfprintf(fp, tab, "rmajor %d\n", ipdata->meta.rmajor);
1069 			tfprintf(fp, tab, "rminor %d\n", ipdata->meta.rminor);
1070 		}
1071 		tfprintf(fp, tab, "ctime %s\n",
1072 		    hammer2_time64_to_str(ipdata->meta.ctime, &str));
1073 		tfprintf(fp, tab, "mtime %s\n",
1074 		    hammer2_time64_to_str(ipdata->meta.mtime, &str));
1075 		tfprintf(fp, tab, "atime %s\n",
1076 		    hammer2_time64_to_str(ipdata->meta.atime, &str));
1077 		tfprintf(fp, tab, "btime %s\n",
1078 		    hammer2_time64_to_str(ipdata->meta.btime, &str));
1079 		tfprintf(fp, tab, "uid %s\n",
1080 		    hammer2_uuid_to_str(&ipdata->meta.uid, &str));
1081 		tfprintf(fp, tab, "gid %s\n",
1082 		    hammer2_uuid_to_str(&ipdata->meta.gid, &str));
1083 		tfprintf(fp, tab, "type %s\n",
1084 		    hammer2_iptype_to_str(ipdata->meta.type));
1085 		tfprintf(fp, tab, "op_flags 0x%02x\n", ipdata->meta.op_flags);
1086 		tfprintf(fp, tab, "cap_flags 0x%04x\n", ipdata->meta.cap_flags);
1087 		tfprintf(fp, tab, "mode %-7o\n", ipdata->meta.mode);
1088 		tfprintf(fp, tab, "inum 0x%016jx\n", ipdata->meta.inum);
1089 		tfprintf(fp, tab, "size %ju ", (uintmax_t)ipdata->meta.size);
1090 		if (ipdata->meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA &&
1091 		    ipdata->meta.size <= HAMMER2_EMBEDDED_BYTES)
1092 			printf("(embedded data)\n");
1093 		else
1094 			printf("\n");
1095 		tfprintf(fp, tab, "nlinks %ju\n",
1096 		    (uintmax_t)ipdata->meta.nlinks);
1097 		tfprintf(fp, tab, "iparent 0x%016jx\n",
1098 		    (uintmax_t)ipdata->meta.iparent);
1099 		tfprintf(fp, tab, "name_key 0x%016jx\n",
1100 		    (uintmax_t)ipdata->meta.name_key);
1101 		tfprintf(fp, tab, "name_len %u\n", ipdata->meta.name_len);
1102 		tfprintf(fp, tab, "ncopies %u\n", ipdata->meta.ncopies);
1103 		tfprintf(fp, tab, "comp_algo %u\n", ipdata->meta.comp_algo);
1104 		tfprintf(fp, tab, "target_type %u\n", ipdata->meta.target_type);
1105 		tfprintf(fp, tab, "check_algo %u\n", ipdata->meta.check_algo);
1106 		if ((ipdata->meta.op_flags & HAMMER2_OPFLAG_PFSROOT) ||
1107 		    ipdata->meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
1108 			tfprintf(fp, tab, "pfs_nmasters %u\n",
1109 			    ipdata->meta.pfs_nmasters);
1110 			tfprintf(fp, tab, "pfs_type %u (%s)\n",
1111 			    ipdata->meta.pfs_type,
1112 			    hammer2_pfstype_to_str(ipdata->meta.pfs_type));
1113 			tfprintf(fp, tab, "pfs_inum 0x%016jx\n",
1114 			    (uintmax_t)ipdata->meta.pfs_inum);
1115 			tfprintf(fp, tab, "pfs_clid %s\n",
1116 			    hammer2_uuid_to_str(&ipdata->meta.pfs_clid, &str));
1117 			tfprintf(fp, tab, "pfs_fsid %s\n",
1118 			    hammer2_uuid_to_str(&ipdata->meta.pfs_fsid, &str));
1119 			tfprintf(fp, tab, "pfs_lsnap_tid 0x%016jx\n",
1120 			    (uintmax_t)ipdata->meta.pfs_lsnap_tid);
1121 		}
1122 		tfprintf(fp, tab, "data_quota %ju\n",
1123 		    (uintmax_t)ipdata->meta.data_quota);
1124 		tfprintf(fp, tab, "data_count %ju\n",
1125 		    (uintmax_t)bref->embed.stats.data_count);
1126 		tfprintf(fp, tab, "inode_quota %ju\n",
1127 		    (uintmax_t)ipdata->meta.inode_quota);
1128 		tfprintf(fp, tab, "inode_count %ju\n",
1129 		    (uintmax_t)bref->embed.stats.inode_count);
1130 		break;
1131 	case HAMMER2_BREF_TYPE_INDIRECT:
1132 		bcount = media_bytes / sizeof(hammer2_blockref_t);
1133 		for (i = 0; i < bcount; ++i) {
1134 			bscan = &media->npdata[i];
1135 			tfprintf(fp, tab, "%3d %016jx %-12s %016jx/%-2d\n",
1136 			    i, (uintmax_t)bscan->data_off,
1137 			    hammer2_breftype_to_str(bscan->type),
1138 			    (uintmax_t)bscan->key,
1139 			    bscan->keybits);
1140 		}
1141 		break;
1142 	case HAMMER2_BREF_TYPE_DIRENT:
1143 		if (bref->embed.dirent.namlen <= sizeof(bref->check.buf)) {
1144 			tfprintf(fp, tab, "filename \"%*.*s\"\n",
1145 			    bref->embed.dirent.namlen,
1146 			    bref->embed.dirent.namlen,
1147 			    bref->check.buf);
1148 		} else {
1149 			tfprintf(fp, tab, "filename \"%*.*s\"\n",
1150 			    bref->embed.dirent.namlen,
1151 			    bref->embed.dirent.namlen,
1152 			    media->buf);
1153 		}
1154 		tfprintf(fp, tab, "inum 0x%016jx\n",
1155 		    (uintmax_t)bref->embed.dirent.inum);
1156 		tfprintf(fp, tab, "namlen %d\n",
1157 		    (uintmax_t)bref->embed.dirent.namlen);
1158 		tfprintf(fp, tab, "type %s\n",
1159 		    hammer2_iptype_to_str(bref->embed.dirent.type));
1160 		break;
1161 	case HAMMER2_BREF_TYPE_FREEMAP_NODE:
1162 		bcount = media_bytes / sizeof(hammer2_blockref_t);
1163 		for (i = 0; i < bcount; ++i) {
1164 			bscan = &media->npdata[i];
1165 			tfprintf(fp, tab, "%3d %016jx %-12s %016jx/%-2d\n",
1166 			    i, (uintmax_t)bscan->data_off,
1167 			    hammer2_breftype_to_str(bscan->type),
1168 			    (uintmax_t)bscan->key,
1169 			    bscan->keybits);
1170 		}
1171 		break;
1172 	case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
1173 		for (i = 0; i < HAMMER2_FREEMAP_COUNT; ++i) {
1174 			hammer2_off_t data_off = bref->key +
1175 				i * HAMMER2_FREEMAP_LEVEL0_SIZE;
1176 #if HAMMER2_BMAP_ELEMENTS != 8
1177 #error "HAMMER2_BMAP_ELEMENTS != 8"
1178 #endif
1179 			tfprintf(fp, tab, "%016jx %04d.%04x (avail=%7d) "
1180 			    "%016jx %016jx %016jx %016jx "
1181 			    "%016jx %016jx %016jx %016jx\n",
1182 			    data_off, i, media->bmdata[i].class,
1183 			    media->bmdata[i].avail,
1184 			    media->bmdata[i].bitmapq[0],
1185 			    media->bmdata[i].bitmapq[1],
1186 			    media->bmdata[i].bitmapq[2],
1187 			    media->bmdata[i].bitmapq[3],
1188 			    media->bmdata[i].bitmapq[4],
1189 			    media->bmdata[i].bitmapq[5],
1190 			    media->bmdata[i].bitmapq[6],
1191 			    media->bmdata[i].bitmapq[7]);
1192 		}
1193 		break;
1194 	default:
1195 		break;
1196 	}
1197 	if (str)
1198 		free(str);
1199 }
1200 
1201 int
1202 test_hammer2(const char *devpath)
1203 {
1204 	struct stat st;
1205 	bool failed = false;
1206 	int fd;
1207 
1208 	fd = open(devpath, O_RDONLY);
1209 	if (fd == -1) {
1210 		perror("open");
1211 		return -1;
1212 	}
1213 
1214 	if (fstat(fd, &st) == -1) {
1215 		perror("fstat");
1216 		failed = true;
1217 		goto end;
1218 	}
1219 	if (!S_ISCHR(st.st_mode)) {
1220 		fprintf(stderr, "%s is not a block device\n", devpath);
1221 		failed = true;
1222 		goto end;
1223 	}
1224 
1225 	best_zone = find_best_zone(fd);
1226 	if (best_zone == -1)
1227 		fprintf(stderr, "Failed to find best zone\n");
1228 
1229 	printf("volume header\n");
1230 	if (test_volume_header(fd) == -1) {
1231 		failed = true;
1232 		if (!ForceOpt)
1233 			goto end;
1234 	}
1235 
1236 	printf("freemap\n");
1237 	if (test_blockref(fd, HAMMER2_BREF_TYPE_FREEMAP) == -1) {
1238 		failed = true;
1239 		if (!ForceOpt)
1240 			goto end;
1241 	}
1242 	printf("volume\n");
1243 	if (!ScanPFS) {
1244 		if (test_blockref(fd, HAMMER2_BREF_TYPE_VOLUME) == -1) {
1245 			failed = true;
1246 			if (!ForceOpt)
1247 				goto end;
1248 		}
1249 	} else {
1250 		if (test_pfs_blockref(fd) == -1) {
1251 			failed = true;
1252 			if (!ForceOpt)
1253 				goto end;
1254 		}
1255 	}
1256 end:
1257 	close(fd);
1258 
1259 	return failed ? -1 : 0;
1260 }
1261