1 /*
2 * Copyright (c) 2019 Tomohiro Kusumi <tkusumi@netbsd.org>
3 * Copyright (c) 2019 The DragonFly Project
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to The DragonFly Project
7 * by Matthew Dillon <dillon@dragonflybsd.org>
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
19 * 3. Neither the name of The DragonFly Project nor the names of its
20 * contributors may be used to endorse or promote products derived
21 * from this software without specific, prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/tree.h>
40 #include <sys/queue.h>
41 #include <sys/ttycom.h>
42 #include <sys/diskslice.h>
43 #include <unistd.h>
44 #include <fcntl.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <stdarg.h>
48 #include <stdbool.h>
49 #include <stdint.h>
50 #include <string.h>
51 #include <assert.h>
52
53 #ifdef HAMMER2_USE_OPENSSL
54 #include <openssl/sha.h>
55 #endif
56
57 #include <vfs/hammer2/hammer2_disk.h>
58 #include <vfs/hammer2/hammer2_xxhash.h>
59
60 #include "hammer2_subs.h"
61 #include "fsck_hammer2.h"
62
63 struct blockref_msg {
64 TAILQ_ENTRY(blockref_msg) entry;
65 hammer2_blockref_t bref;
66 void *msg;
67 };
68
69 TAILQ_HEAD(blockref_list, blockref_msg);
70
71 struct blockref_entry {
72 RB_ENTRY(blockref_entry) entry;
73 hammer2_off_t data_off;
74 struct blockref_list head;
75 };
76
77 static int
blockref_cmp(struct blockref_entry * b1,struct blockref_entry * b2)78 blockref_cmp(struct blockref_entry *b1, struct blockref_entry *b2)
79 {
80 if (b1->data_off < b2->data_off)
81 return -1;
82 if (b1->data_off > b2->data_off)
83 return 1;
84 return 0;
85 }
86
87 RB_HEAD(blockref_tree, blockref_entry);
88 RB_PROTOTYPE(blockref_tree, blockref_entry, entry, blockref_cmp);
89 RB_GENERATE(blockref_tree, blockref_entry, entry, blockref_cmp);
90
91 typedef struct {
92 struct blockref_tree root;
93 uint8_t type; /* HAMMER2_BREF_TYPE_VOLUME or FREEMAP */
94 uint64_t total_blockref;
95 uint64_t total_empty;
96 uint64_t total_bytes;
97 union {
98 /* use volume or freemap depending on type value */
99 struct {
100 uint64_t total_inode;
101 uint64_t total_indirect;
102 uint64_t total_data;
103 uint64_t total_dirent;
104 } volume;
105 struct {
106 uint64_t total_freemap_node;
107 uint64_t total_freemap_leaf;
108 } freemap;
109 };
110 } blockref_stats_t;
111
112 typedef struct {
113 uint64_t total_blockref;
114 uint64_t total_empty;
115 uint64_t total_bytes;
116 struct {
117 uint64_t total_inode;
118 uint64_t total_indirect;
119 uint64_t total_data;
120 uint64_t total_dirent;
121 } volume;
122 struct {
123 uint64_t total_freemap_node;
124 uint64_t total_freemap_leaf;
125 } freemap;
126 long count;
127 } delta_stats_t;
128
129 static void print_blockref_entry(struct blockref_tree *);
130 static void init_blockref_stats(blockref_stats_t *, uint8_t);
131 static void cleanup_blockref_stats(blockref_stats_t *);
132 static void init_delta_root(struct blockref_tree *);
133 static void cleanup_delta_root(struct blockref_tree *);
134 static void print_blockref_stats(const blockref_stats_t *, bool);
135 static int verify_volume_header(const hammer2_volume_data_t *);
136 static int read_media(const hammer2_blockref_t *, hammer2_media_data_t *,
137 size_t *);
138 static int verify_blockref(const hammer2_volume_data_t *,
139 const hammer2_blockref_t *, bool, blockref_stats_t *,
140 struct blockref_tree *, delta_stats_t *, int, int);
141 static void print_pfs(const hammer2_inode_data_t *);
142 static char *get_inode_filename(const hammer2_inode_data_t *);
143 static int init_pfs_blockref(const hammer2_volume_data_t *,
144 const hammer2_blockref_t *, struct blockref_list *);
145 static void cleanup_pfs_blockref(struct blockref_list *);
146 static void print_media(FILE *, int, const hammer2_blockref_t *,
147 const hammer2_media_data_t *, size_t);
148
149 static int best_zone = -1;
150
151 #define TAB 8
152
153 static void
tfprintf(FILE * fp,int tab,const char * ctl,...)154 tfprintf(FILE *fp, int tab, const char *ctl, ...)
155 {
156 va_list va;
157 int ret;
158
159 ret = fprintf(fp, "%*s", tab * TAB, "");
160 if (ret < 0)
161 return;
162
163 va_start(va, ctl);
164 vfprintf(fp, ctl, va);
165 va_end(va);
166 }
167
168 static void
tsnprintf(char * str,size_t siz,int tab,const char * ctl,...)169 tsnprintf(char *str, size_t siz, int tab, const char *ctl, ...)
170 {
171 va_list va;
172 int ret;
173
174 ret = snprintf(str, siz, "%*s", tab * TAB, "");
175 if (ret < 0 || ret >= (int)siz)
176 return;
177
178 va_start(va, ctl);
179 vsnprintf(str + ret, siz - ret, ctl, va);
180 va_end(va);
181 }
182
183 static void
tprintf_zone(int tab,int i,const hammer2_blockref_t * bref)184 tprintf_zone(int tab, int i, const hammer2_blockref_t *bref)
185 {
186 tfprintf(stdout, tab, "zone.%d %016jx%s\n",
187 i, (uintmax_t)bref->data_off,
188 (!ScanBest && i == best_zone) ? " (best)" : "");
189 }
190
191 static int
init_root_blockref(int i,uint8_t type,hammer2_blockref_t * bref)192 init_root_blockref(int i, uint8_t type, hammer2_blockref_t *bref)
193 {
194 hammer2_off_t off;
195
196 assert(type == HAMMER2_BREF_TYPE_EMPTY ||
197 type == HAMMER2_BREF_TYPE_VOLUME ||
198 type == HAMMER2_BREF_TYPE_FREEMAP);
199 memset(bref, 0, sizeof(*bref));
200 bref->type = type;
201 bref->data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
202 off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
203
204 return lseek(hammer2_get_root_volume_fd(),
205 off - hammer2_get_root_volume_offset(), SEEK_SET);
206 }
207
208 static int
find_best_zone(void)209 find_best_zone(void)
210 {
211 hammer2_blockref_t best;
212 int i, best_i = -1;
213
214 memset(&best, 0, sizeof(best));
215
216 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
217 hammer2_volume_data_t voldata;
218 hammer2_blockref_t broot;
219 ssize_t ret;
220
221 if (i * HAMMER2_ZONE_BYTES64 >=
222 hammer2_get_root_volume_size())
223 break;
224 init_root_blockref(i, HAMMER2_BREF_TYPE_EMPTY, &broot);
225 ret = read(hammer2_get_root_volume_fd(), &voldata,
226 HAMMER2_VOLUME_BYTES);
227 if (ret == HAMMER2_VOLUME_BYTES) {
228 if ((voldata.magic != HAMMER2_VOLUME_ID_HBO) &&
229 (voldata.magic != HAMMER2_VOLUME_ID_ABO))
230 continue;
231 broot.mirror_tid = voldata.mirror_tid;
232 if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
233 best_i = i;
234 best = broot;
235 }
236 } else if (ret == -1) {
237 perror("read");
238 return -1;
239 } else {
240 tfprintf(stderr, 1, "Failed to read volume header\n");
241 return -1;
242 }
243 }
244
245 return best_i;
246 }
247
248 static int
test_volume_header(void)249 test_volume_header(void)
250 {
251 bool failed = false;
252 int i;
253
254 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
255 hammer2_volume_data_t voldata;
256 hammer2_blockref_t broot;
257 ssize_t ret;
258
259 if (ScanBest && i != best_zone)
260 continue;
261 if (i * HAMMER2_ZONE_BYTES64 >=
262 hammer2_get_root_volume_size()) {
263 tfprintf(stderr, 0, "zone.%d exceeds volume size\n", i);
264 break;
265 }
266 init_root_blockref(i, HAMMER2_BREF_TYPE_EMPTY, &broot);
267 ret = read(hammer2_get_root_volume_fd(), &voldata,
268 HAMMER2_VOLUME_BYTES);
269 if (ret == HAMMER2_VOLUME_BYTES) {
270 tprintf_zone(0, i, &broot);
271 if (verify_volume_header(&voldata) == -1)
272 failed = true;
273 } else if (ret == -1) {
274 perror("read");
275 return -1;
276 } else {
277 tfprintf(stderr, 1, "Failed to read volume header\n");
278 return -1;
279 }
280 }
281
282 return failed ? -1 : 0;
283 }
284
285 static int
test_blockref(uint8_t type)286 test_blockref(uint8_t type)
287 {
288 struct blockref_tree droot;
289 bool failed = false;
290 int i;
291
292 init_delta_root(&droot);
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 if (i * HAMMER2_ZONE_BYTES64 >=
301 hammer2_get_root_volume_size()) {
302 tfprintf(stderr, 0, "zone.%d exceeds volume size\n", i);
303 break;
304 }
305 init_root_blockref(i, type, &broot);
306 ret = read(hammer2_get_root_volume_fd(), &voldata,
307 HAMMER2_VOLUME_BYTES);
308 if (ret == HAMMER2_VOLUME_BYTES) {
309 blockref_stats_t bstats;
310 init_blockref_stats(&bstats, type);
311 delta_stats_t ds;
312 memset(&ds, 0, sizeof(ds));
313 tprintf_zone(0, i, &broot);
314 if (verify_blockref(&voldata, &broot, false, &bstats,
315 &droot, &ds, 0, 0) == -1)
316 failed = true;
317 print_blockref_stats(&bstats, true);
318 print_blockref_entry(&bstats.root);
319 cleanup_blockref_stats(&bstats);
320 } else if (ret == -1) {
321 perror("read");
322 failed = true;
323 goto end;
324 } else {
325 tfprintf(stderr, 1, "Failed to read volume header\n");
326 failed = true;
327 goto end;
328 }
329 }
330 end:
331 cleanup_delta_root(&droot);
332 return failed ? -1 : 0;
333 }
334
335 static int
test_pfs_blockref(void)336 test_pfs_blockref(void)
337 {
338 struct blockref_tree droot;
339 uint8_t type = HAMMER2_BREF_TYPE_VOLUME;
340 bool failed = false;
341 int i;
342
343 init_delta_root(&droot);
344 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
345 hammer2_volume_data_t voldata;
346 hammer2_blockref_t broot;
347 ssize_t ret;
348
349 if (ScanBest && i != best_zone)
350 continue;
351 if (i * HAMMER2_ZONE_BYTES64 >=
352 hammer2_get_root_volume_size()) {
353 tfprintf(stderr, 0, "zone.%d exceeds volume size\n", i);
354 break;
355 }
356 init_root_blockref(i, type, &broot);
357 ret = read(hammer2_get_root_volume_fd(), &voldata,
358 HAMMER2_VOLUME_BYTES);
359 if (ret == HAMMER2_VOLUME_BYTES) {
360 struct blockref_list blist;
361 struct blockref_msg *p;
362 int count = 0;
363
364 tprintf_zone(0, i, &broot);
365 TAILQ_INIT(&blist);
366 if (init_pfs_blockref(&voldata, &broot, &blist) == -1) {
367 tfprintf(stderr, 1, "Failed to read PFS "
368 "blockref\n");
369 failed = true;
370 continue;
371 }
372 if (TAILQ_EMPTY(&blist)) {
373 tfprintf(stderr, 1, "Failed to find PFS "
374 "blockref\n");
375 failed = true;
376 continue;
377 }
378 TAILQ_FOREACH(p, &blist, entry) {
379 blockref_stats_t bstats;
380 bool found = false;
381 char *f = get_inode_filename(p->msg);
382 if (NumPFSNames) {
383 int j;
384 for (j = 0; j < NumPFSNames; j++)
385 if (!strcmp(PFSNames[j], f))
386 found = true;
387 } else
388 found = true;
389 if (!found) {
390 free(f);
391 continue;
392 }
393 count++;
394 if (PrintPFS) {
395 print_pfs(p->msg);
396 free(f);
397 continue;
398 }
399 tfprintf(stdout, 1, "%s\n", f);
400 free(f);
401 init_blockref_stats(&bstats, type);
402 delta_stats_t ds;
403 memset(&ds, 0, sizeof(ds));
404 if (verify_blockref(&voldata, &p->bref, false,
405 &bstats, &droot, &ds, 0, 0) == -1)
406 failed = true;
407 print_blockref_stats(&bstats, true);
408 print_blockref_entry(&bstats.root);
409 cleanup_blockref_stats(&bstats);
410 }
411 cleanup_pfs_blockref(&blist);
412 if (NumPFSNames && !count) {
413 tfprintf(stderr, 1, "PFS not found\n");
414 failed = true;
415 }
416 } else if (ret == -1) {
417 perror("read");
418 failed = true;
419 goto end;
420 } else {
421 tfprintf(stderr, 1, "Failed to read volume header\n");
422 failed = true;
423 goto end;
424 }
425 }
426 end:
427 cleanup_delta_root(&droot);
428 return failed ? -1 : 0;
429 }
430
431 static int
charsperline(void)432 charsperline(void)
433 {
434 int columns;
435 char *cp;
436 struct winsize ws;
437
438 columns = 0;
439 if (ioctl(0, TIOCGWINSZ, &ws) != -1)
440 columns = ws.ws_col;
441 if (columns == 0 && (cp = getenv("COLUMNS")))
442 columns = atoi(cp);
443 if (columns == 0)
444 columns = 80; /* last resort */
445
446 return columns;
447 }
448
449 static void
cleanup_blockref_msg(struct blockref_list * head)450 cleanup_blockref_msg(struct blockref_list *head)
451 {
452 struct blockref_msg *p;
453
454 while ((p = TAILQ_FIRST(head)) != NULL) {
455 TAILQ_REMOVE(head, p, entry);
456 free(p->msg);
457 free(p);
458 }
459 assert(TAILQ_EMPTY(head));
460 }
461
462 static void
cleanup_blockref_entry(struct blockref_tree * root)463 cleanup_blockref_entry(struct blockref_tree *root)
464 {
465 struct blockref_entry *e;
466
467 while ((e = RB_ROOT(root)) != NULL) {
468 RB_REMOVE(blockref_tree, root, e);
469 cleanup_blockref_msg(&e->head);
470 free(e);
471 }
472 assert(RB_EMPTY(root));
473 }
474
475 static void
add_blockref_msg(struct blockref_list * head,const hammer2_blockref_t * bref,const void * msg,size_t siz)476 add_blockref_msg(struct blockref_list *head, const hammer2_blockref_t *bref,
477 const void *msg, size_t siz)
478 {
479 struct blockref_msg *m;
480 void *p;
481
482 m = calloc(1, sizeof(*m));
483 assert(m);
484 m->bref = *bref;
485 p = calloc(1, siz);
486 assert(p);
487 memcpy(p, msg, siz);
488 m->msg = p;
489
490 TAILQ_INSERT_TAIL(head, m, entry);
491 }
492
493 static void
add_blockref_entry(struct blockref_tree * root,const hammer2_blockref_t * bref,const void * msg,size_t siz)494 add_blockref_entry(struct blockref_tree *root, const hammer2_blockref_t *bref,
495 const void *msg, size_t siz)
496 {
497 struct blockref_entry *e, bref_find;
498
499 memset(&bref_find, 0, sizeof(bref_find));
500 bref_find.data_off = bref->data_off;
501 e = RB_FIND(blockref_tree, root, &bref_find);
502 if (!e) {
503 e = calloc(1, sizeof(*e));
504 assert(e);
505 TAILQ_INIT(&e->head);
506 e->data_off = bref->data_off;
507 }
508
509 add_blockref_msg(&e->head, bref, msg, siz);
510
511 RB_INSERT(blockref_tree, root, e);
512 }
513
514 static void
__print_blockref(FILE * fp,int tab,const hammer2_blockref_t * bref,const char * msg)515 __print_blockref(FILE *fp, int tab, const hammer2_blockref_t *bref,
516 const char *msg)
517 {
518 tfprintf(fp, tab, "%016jx %-12s %016jx/%-2d%s%s\n",
519 (uintmax_t)bref->data_off,
520 hammer2_breftype_to_str(bref->type),
521 (uintmax_t)bref->key,
522 bref->keybits,
523 msg ? " " : "",
524 msg ? msg : "");
525 }
526
527 static void
print_blockref(FILE * fp,const hammer2_blockref_t * bref,const char * msg)528 print_blockref(FILE *fp, const hammer2_blockref_t *bref, const char *msg)
529 {
530 __print_blockref(fp, 1, bref, msg);
531 }
532
533 static void
print_blockref_debug(FILE * fp,int depth,int index,const hammer2_blockref_t * bref,const char * msg)534 print_blockref_debug(FILE *fp, int depth, int index,
535 const hammer2_blockref_t *bref, const char *msg)
536 {
537 if (DebugOpt > 1) {
538 char buf[256];
539 int i;
540
541 memset(buf, 0, sizeof(buf));
542 for (i = 0; i < depth * 2; i++)
543 strlcat(buf, " ", sizeof(buf));
544 tfprintf(fp, 1, buf);
545 fprintf(fp, "%-2d %-3d ", depth, index);
546 __print_blockref(fp, 0, bref, msg);
547 } else if (DebugOpt > 0)
548 print_blockref(fp, bref, msg);
549 }
550
551 static void
print_blockref_msg(const struct blockref_list * head)552 print_blockref_msg(const struct blockref_list *head)
553 {
554 struct blockref_msg *m;
555
556 TAILQ_FOREACH(m, head, entry) {
557 hammer2_blockref_t *bref = &m->bref;
558 print_blockref(stderr, bref, m->msg);
559 if (VerboseOpt > 0) {
560 hammer2_media_data_t media;
561 size_t bytes;
562 if (!read_media(bref, &media, &bytes))
563 print_media(stderr, 2, bref, &media, bytes);
564 else
565 tfprintf(stderr, 2, "Failed to read media\n");
566 }
567 }
568 }
569
570 static void
print_blockref_entry(struct blockref_tree * root)571 print_blockref_entry(struct blockref_tree *root)
572 {
573 struct blockref_entry *e;
574
575 RB_FOREACH(e, blockref_tree, root)
576 print_blockref_msg(&e->head);
577 }
578
579 static void
init_blockref_stats(blockref_stats_t * bstats,uint8_t type)580 init_blockref_stats(blockref_stats_t *bstats, uint8_t type)
581 {
582 memset(bstats, 0, sizeof(*bstats));
583 RB_INIT(&bstats->root);
584 bstats->type = type;
585 }
586
587 static void
cleanup_blockref_stats(blockref_stats_t * bstats)588 cleanup_blockref_stats(blockref_stats_t *bstats)
589 {
590 cleanup_blockref_entry(&bstats->root);
591 }
592
593 static void
init_delta_root(struct blockref_tree * droot)594 init_delta_root(struct blockref_tree *droot)
595 {
596 RB_INIT(droot);
597 }
598
599 static void
cleanup_delta_root(struct blockref_tree * droot)600 cleanup_delta_root(struct blockref_tree *droot)
601 {
602 cleanup_blockref_entry(droot);
603 }
604
605 static void
print_blockref_stats(const blockref_stats_t * bstats,bool newline)606 print_blockref_stats(const blockref_stats_t *bstats, bool newline)
607 {
608 size_t siz = charsperline();
609 char *buf = calloc(1, siz);
610 char emptybuf[128];
611
612 assert(buf);
613
614 if (CountEmpty)
615 snprintf(emptybuf, sizeof(emptybuf), ", %ju empty",
616 (uintmax_t)bstats->total_empty);
617 else
618 strlcpy(emptybuf, "", sizeof(emptybuf));
619
620 switch (bstats->type) {
621 case HAMMER2_BREF_TYPE_VOLUME:
622 tsnprintf(buf, siz, 1, "%ju blockref (%ju inode, %ju indirect, "
623 "%ju data, %ju dirent%s), %s",
624 (uintmax_t)bstats->total_blockref,
625 (uintmax_t)bstats->volume.total_inode,
626 (uintmax_t)bstats->volume.total_indirect,
627 (uintmax_t)bstats->volume.total_data,
628 (uintmax_t)bstats->volume.total_dirent,
629 emptybuf,
630 sizetostr(bstats->total_bytes));
631 break;
632 case HAMMER2_BREF_TYPE_FREEMAP:
633 tsnprintf(buf, siz, 1, "%ju blockref (%ju node, %ju leaf%s), "
634 "%s",
635 (uintmax_t)bstats->total_blockref,
636 (uintmax_t)bstats->freemap.total_freemap_node,
637 (uintmax_t)bstats->freemap.total_freemap_leaf,
638 emptybuf,
639 sizetostr(bstats->total_bytes));
640 break;
641 default:
642 assert(0);
643 break;
644 }
645
646 if (newline) {
647 printf("%s\n", buf);
648 } else {
649 printf("%s\r", buf);
650 fflush(stdout);
651 }
652 free(buf);
653 }
654
655 static int
verify_volume_header(const hammer2_volume_data_t * voldata)656 verify_volume_header(const hammer2_volume_data_t *voldata)
657 {
658 hammer2_crc32_t crc0, crc1;
659 const char *p = (const char*)voldata;
660
661 if ((voldata->magic != HAMMER2_VOLUME_ID_HBO) &&
662 (voldata->magic != HAMMER2_VOLUME_ID_ABO)) {
663 tfprintf(stderr, 1, "Bad magic %jX\n", voldata->magic);
664 return -1;
665 }
666
667 if (voldata->magic == HAMMER2_VOLUME_ID_ABO)
668 tfprintf(stderr, 1, "Reverse endian\n");
669
670 crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT0];
671 crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC0_OFF,
672 HAMMER2_VOLUME_ICRC0_SIZE);
673 if (crc0 != crc1) {
674 tfprintf(stderr, 1, "Bad HAMMER2_VOL_ICRC_SECT0 CRC\n");
675 return -1;
676 }
677
678 crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT1];
679 crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC1_OFF,
680 HAMMER2_VOLUME_ICRC1_SIZE);
681 if (crc0 != crc1) {
682 tfprintf(stderr, 1, "Bad HAMMER2_VOL_ICRC_SECT1 CRC\n");
683 return -1;
684 }
685
686 crc0 = voldata->icrc_volheader;
687 crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRCVH_OFF,
688 HAMMER2_VOLUME_ICRCVH_SIZE);
689 if (crc0 != crc1) {
690 tfprintf(stderr, 1, "Bad volume header CRC\n");
691 return -1;
692 }
693
694 return 0;
695 }
696
697 static int
read_media(const hammer2_blockref_t * bref,hammer2_media_data_t * media,size_t * media_bytes)698 read_media(const hammer2_blockref_t *bref, hammer2_media_data_t *media,
699 size_t *media_bytes)
700 {
701 hammer2_off_t io_off, io_base;
702 size_t bytes, io_bytes, boff;
703 int fd;
704
705 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
706 if (bytes)
707 bytes = (size_t)1 << bytes;
708 if (media_bytes)
709 *media_bytes = bytes;
710
711 if (!bytes)
712 return 0;
713
714 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
715 io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1);
716 boff = io_off - io_base;
717
718 io_bytes = HAMMER2_LBUFSIZE;
719 while (io_bytes + boff < bytes)
720 io_bytes <<= 1;
721
722 if (io_bytes > sizeof(*media))
723 return -1;
724 fd = hammer2_get_volume_fd(io_off);
725 if (lseek(fd, io_base - hammer2_get_volume_offset(io_base), SEEK_SET)
726 == -1)
727 return -2;
728 if (read(fd, media, io_bytes) != (ssize_t)io_bytes)
729 return -2;
730 if (boff)
731 memmove(media, (char *)media + boff, bytes);
732
733 return 0;
734 }
735
736 static void
load_delta_stats(blockref_stats_t * bstats,const delta_stats_t * dstats)737 load_delta_stats(blockref_stats_t *bstats, const delta_stats_t *dstats)
738 {
739 bstats->total_blockref += dstats->total_blockref;
740 bstats->total_empty += dstats->total_empty;
741 bstats->total_bytes += dstats->total_bytes;
742
743 switch (bstats->type) {
744 case HAMMER2_BREF_TYPE_VOLUME:
745 bstats->volume.total_inode += dstats->volume.total_inode;
746 bstats->volume.total_indirect += dstats->volume.total_indirect;
747 bstats->volume.total_data += dstats->volume.total_data;
748 bstats->volume.total_dirent += dstats->volume.total_dirent;
749 break;
750 case HAMMER2_BREF_TYPE_FREEMAP:
751 bstats->freemap.total_freemap_node +=
752 dstats->freemap.total_freemap_node;
753 bstats->freemap.total_freemap_leaf +=
754 dstats->freemap.total_freemap_leaf;
755 break;
756 default:
757 assert(0);
758 break;
759 }
760 }
761
762 static void
accumulate_delta_stats(delta_stats_t * dst,const delta_stats_t * src)763 accumulate_delta_stats(delta_stats_t *dst, const delta_stats_t *src)
764 {
765 dst->total_blockref += src->total_blockref;
766 dst->total_empty += src->total_empty;
767 dst->total_bytes += src->total_bytes;
768
769 dst->volume.total_inode += src->volume.total_inode;
770 dst->volume.total_indirect += src->volume.total_indirect;
771 dst->volume.total_data += src->volume.total_data;
772 dst->volume.total_dirent += src->volume.total_dirent;
773
774 dst->freemap.total_freemap_node += src->freemap.total_freemap_node;
775 dst->freemap.total_freemap_leaf += src->freemap.total_freemap_leaf;
776
777 dst->count += src->count;
778 }
779
780 static int
verify_blockref(const hammer2_volume_data_t * voldata,const hammer2_blockref_t * bref,bool norecurse,blockref_stats_t * bstats,struct blockref_tree * droot,delta_stats_t * dstats,int depth,int index)781 verify_blockref(const hammer2_volume_data_t *voldata,
782 const hammer2_blockref_t *bref, bool norecurse, blockref_stats_t *bstats,
783 struct blockref_tree *droot, delta_stats_t *dstats, int depth, int index)
784 {
785 hammer2_media_data_t media;
786 hammer2_blockref_t *bscan;
787 int i, bcount;
788 bool failed = false;
789 size_t bytes;
790 uint32_t cv;
791 uint64_t cv64;
792 char msg[256];
793 #ifdef HAMMER2_USE_OPENSSL
794 SHA256_CTX hash_ctx;
795 union {
796 uint8_t digest[SHA256_DIGEST_LENGTH];
797 uint64_t digest64[SHA256_DIGEST_LENGTH/8];
798 } u;
799 #endif
800 /* only for DebugOpt > 1 */
801 if (DebugOpt > 1)
802 print_blockref_debug(stdout, depth, index, bref, NULL);
803
804 if (bref->data_off) {
805 struct blockref_entry *e, bref_find;
806 memset(&bref_find, 0, sizeof(bref_find));
807 bref_find.data_off = bref->data_off;
808 e = RB_FIND(blockref_tree, droot, &bref_find);
809 if (e) {
810 struct blockref_msg *m;
811 TAILQ_FOREACH(m, &e->head, entry) {
812 delta_stats_t *ds = m->msg;
813 if (!memcmp(&m->bref, bref, sizeof(*bref))) {
814 /* delta contains cached delta */
815 accumulate_delta_stats(dstats, ds);
816 load_delta_stats(bstats, ds);
817 print_blockref_debug(stdout, depth,
818 index, &m->bref, "cache-hit");
819 return 0;
820 }
821 }
822 }
823 }
824
825 bstats->total_blockref++;
826 dstats->total_blockref++;
827
828 switch (bref->type) {
829 case HAMMER2_BREF_TYPE_EMPTY:
830 if (CountEmpty) {
831 bstats->total_empty++;
832 dstats->total_empty++;
833 } else {
834 bstats->total_blockref--;
835 dstats->total_blockref--;
836 }
837 break;
838 case HAMMER2_BREF_TYPE_INODE:
839 bstats->volume.total_inode++;
840 dstats->volume.total_inode++;
841 break;
842 case HAMMER2_BREF_TYPE_INDIRECT:
843 bstats->volume.total_indirect++;
844 dstats->volume.total_indirect++;
845 break;
846 case HAMMER2_BREF_TYPE_DATA:
847 bstats->volume.total_data++;
848 dstats->volume.total_data++;
849 break;
850 case HAMMER2_BREF_TYPE_DIRENT:
851 bstats->volume.total_dirent++;
852 dstats->volume.total_dirent++;
853 break;
854 case HAMMER2_BREF_TYPE_FREEMAP_NODE:
855 bstats->freemap.total_freemap_node++;
856 dstats->freemap.total_freemap_node++;
857 break;
858 case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
859 bstats->freemap.total_freemap_leaf++;
860 dstats->freemap.total_freemap_leaf++;
861 break;
862 case HAMMER2_BREF_TYPE_VOLUME:
863 bstats->total_blockref--;
864 dstats->total_blockref--;
865 break;
866 case HAMMER2_BREF_TYPE_FREEMAP:
867 bstats->total_blockref--;
868 dstats->total_blockref--;
869 break;
870 default:
871 snprintf(msg, sizeof(msg), "Invalid blockref type %d",
872 bref->type);
873 add_blockref_entry(&bstats->root, bref, msg, strlen(msg) + 1);
874 print_blockref_debug(stdout, depth, index, bref, msg);
875 failed = true;
876 break;
877 }
878
879 switch (read_media(bref, &media, &bytes)) {
880 case -1:
881 strlcpy(msg, "Bad I/O bytes", sizeof(msg));
882 add_blockref_entry(&bstats->root, bref, msg, strlen(msg) + 1);
883 print_blockref_debug(stdout, depth, index, bref, msg);
884 return -1;
885 case -2:
886 strlcpy(msg, "Failed to read media", sizeof(msg));
887 add_blockref_entry(&bstats->root, bref, msg, strlen(msg) + 1);
888 print_blockref_debug(stdout, depth, index, bref, msg);
889 return -1;
890 default:
891 break;
892 }
893
894 if (bref->type != HAMMER2_BREF_TYPE_VOLUME &&
895 bref->type != HAMMER2_BREF_TYPE_FREEMAP) {
896 bstats->total_bytes += bytes;
897 dstats->total_bytes += bytes;
898 }
899
900 if (!CountEmpty && bref->type == HAMMER2_BREF_TYPE_EMPTY) {
901 assert(bytes == 0);
902 bstats->total_bytes -= bytes;
903 dstats->total_bytes -= bytes;
904 }
905
906 if (!DebugOpt && QuietOpt <= 0 && (bstats->total_blockref % 100) == 0)
907 print_blockref_stats(bstats, false);
908
909 if (!bytes)
910 goto end;
911
912 switch (HAMMER2_DEC_CHECK(bref->methods)) {
913 case HAMMER2_CHECK_ISCSI32:
914 cv = hammer2_icrc32(&media, bytes);
915 if (bref->check.iscsi32.value != cv) {
916 strlcpy(msg, "Bad HAMMER2_CHECK_ISCSI32", sizeof(msg));
917 add_blockref_entry(&bstats->root, bref, msg,
918 strlen(msg) + 1);
919 print_blockref_debug(stdout, depth, index, bref, msg);
920 failed = true;
921 }
922 break;
923 case HAMMER2_CHECK_XXHASH64:
924 cv64 = XXH64(&media, bytes, XXH_HAMMER2_SEED);
925 if (bref->check.xxhash64.value != cv64) {
926 strlcpy(msg, "Bad HAMMER2_CHECK_XXHASH64", sizeof(msg));
927 add_blockref_entry(&bstats->root, bref, msg,
928 strlen(msg) + 1);
929 print_blockref_debug(stdout, depth, index, bref, msg);
930 failed = true;
931 }
932 break;
933 case HAMMER2_CHECK_SHA192:
934 #ifdef HAMMER2_USE_OPENSSL
935 SHA256_Init(&hash_ctx);
936 SHA256_Update(&hash_ctx, &media, bytes);
937 SHA256_Final(u.digest, &hash_ctx);
938 u.digest64[2] ^= u.digest64[3];
939 if (memcmp(u.digest, bref->check.sha192.data,
940 sizeof(bref->check.sha192.data))) {
941 strlcpy(msg, "Bad HAMMER2_CHECK_SHA192", sizeof(msg));
942 add_blockref_entry(&bstats->root, bref, msg,
943 strlen(msg) + 1);
944 print_blockref_debug(stdout, depth, index, bref, msg);
945 failed = true;
946 }
947 #endif
948 break;
949 case HAMMER2_CHECK_FREEMAP:
950 cv = hammer2_icrc32(&media, bytes);
951 if (bref->check.freemap.icrc32 != cv) {
952 strlcpy(msg, "Bad HAMMER2_CHECK_FREEMAP", sizeof(msg));
953 add_blockref_entry(&bstats->root, bref, msg,
954 strlen(msg) + 1);
955 print_blockref_debug(stdout, depth, index, bref, msg);
956 failed = true;
957 }
958 break;
959 }
960
961 switch (bref->type) {
962 case HAMMER2_BREF_TYPE_INODE:
963 if (!(media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA)) {
964 bscan = &media.ipdata.u.blockset.blockref[0];
965 bcount = HAMMER2_SET_COUNT;
966 } else {
967 bscan = NULL;
968 bcount = 0;
969 }
970 break;
971 case HAMMER2_BREF_TYPE_INDIRECT:
972 bscan = &media.npdata[0];
973 bcount = bytes / sizeof(hammer2_blockref_t);
974 break;
975 case HAMMER2_BREF_TYPE_FREEMAP_NODE:
976 bscan = &media.npdata[0];
977 bcount = bytes / sizeof(hammer2_blockref_t);
978 break;
979 case HAMMER2_BREF_TYPE_VOLUME:
980 bscan = &media.voldata.sroot_blockset.blockref[0];
981 bcount = HAMMER2_SET_COUNT;
982 break;
983 case HAMMER2_BREF_TYPE_FREEMAP:
984 bscan = &media.voldata.freemap_blockset.blockref[0];
985 bcount = HAMMER2_SET_COUNT;
986 break;
987 default:
988 bscan = NULL;
989 bcount = 0;
990 break;
991 }
992
993 if (ForceOpt)
994 norecurse = false;
995 /*
996 * If failed, no recurse, but still verify its direct children.
997 * Beyond that is probably garbage.
998 */
999 for (i = 0; norecurse == false && i < bcount; ++i) {
1000 delta_stats_t ds;
1001 memset(&ds, 0, sizeof(ds));
1002 if (verify_blockref(voldata, &bscan[i], failed, bstats, droot,
1003 &ds, depth + 1, i) == -1)
1004 return -1;
1005 if (!failed)
1006 accumulate_delta_stats(dstats, &ds);
1007 }
1008 end:
1009 if (failed)
1010 return -1;
1011
1012 dstats->count++;
1013 if (bref->data_off && BlockrefCacheCount > 0 &&
1014 dstats->count >= BlockrefCacheCount) {
1015 assert(bytes);
1016 add_blockref_entry(droot, bref, dstats, sizeof(*dstats));
1017 print_blockref_debug(stdout, depth, index, bref, "cache-add");
1018 }
1019
1020 return 0;
1021 }
1022
1023 static void
print_pfs(const hammer2_inode_data_t * ipdata)1024 print_pfs(const hammer2_inode_data_t *ipdata)
1025 {
1026 const hammer2_inode_meta_t *meta = &ipdata->meta;
1027 char *f, *pfs_id_str = NULL;
1028 const char *type_str;
1029 uuid_t uuid;
1030
1031 f = get_inode_filename(ipdata);
1032 uuid = meta->pfs_clid;
1033 hammer2_uuid_to_str(&uuid, &pfs_id_str);
1034 if (meta->pfs_type == HAMMER2_PFSTYPE_MASTER) {
1035 if (meta->pfs_subtype == HAMMER2_PFSSUBTYPE_NONE)
1036 type_str = "MASTER";
1037 else
1038 type_str = hammer2_pfssubtype_to_str(meta->pfs_subtype);
1039 } else {
1040 type_str = hammer2_pfstype_to_str(meta->pfs_type);
1041 }
1042 tfprintf(stdout, 1, "%-11s %s %s\n", type_str, pfs_id_str, f);
1043
1044 free(f);
1045 free(pfs_id_str);
1046 }
1047
1048 static char*
get_inode_filename(const hammer2_inode_data_t * ipdata)1049 get_inode_filename(const hammer2_inode_data_t *ipdata)
1050 {
1051 char *p = malloc(HAMMER2_INODE_MAXNAME + 1);
1052
1053 memcpy(p, ipdata->filename, sizeof(ipdata->filename));
1054 p[HAMMER2_INODE_MAXNAME] = '\0';
1055
1056 return p;
1057 }
1058
1059 static void
__add_pfs_blockref(const hammer2_blockref_t * bref,struct blockref_list * blist,const hammer2_inode_data_t * ipdata)1060 __add_pfs_blockref(const hammer2_blockref_t *bref, struct blockref_list *blist,
1061 const hammer2_inode_data_t *ipdata)
1062 {
1063 struct blockref_msg *newp, *p;
1064
1065 newp = calloc(1, sizeof(*newp));
1066 newp->bref = *bref;
1067 newp->msg = calloc(1, sizeof(*ipdata));
1068 memcpy(newp->msg, ipdata, sizeof(*ipdata));
1069
1070 p = TAILQ_FIRST(blist);
1071 while (p) {
1072 char *f1 = get_inode_filename(newp->msg);
1073 char *f2 = get_inode_filename(p->msg);
1074 if (strcmp(f1, f2) <= 0) {
1075 TAILQ_INSERT_BEFORE(p, newp, entry);
1076 free(f1);
1077 free(f2);
1078 break;
1079 }
1080 p = TAILQ_NEXT(p, entry);
1081 free(f1);
1082 free(f2);
1083 }
1084 if (!p)
1085 TAILQ_INSERT_TAIL(blist, newp, entry);
1086 }
1087
1088 static int
init_pfs_blockref(const hammer2_volume_data_t * voldata,const hammer2_blockref_t * bref,struct blockref_list * blist)1089 init_pfs_blockref(const hammer2_volume_data_t *voldata,
1090 const hammer2_blockref_t *bref, struct blockref_list *blist)
1091 {
1092 hammer2_media_data_t media;
1093 hammer2_inode_data_t ipdata;
1094 hammer2_blockref_t *bscan;
1095 int i, bcount;
1096 size_t bytes;
1097
1098 if (read_media(bref, &media, &bytes))
1099 return -1;
1100 if (!bytes)
1101 return 0;
1102
1103 switch (bref->type) {
1104 case HAMMER2_BREF_TYPE_INODE:
1105 ipdata = media.ipdata;
1106 if (ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
1107 bscan = &ipdata.u.blockset.blockref[0];
1108 bcount = HAMMER2_SET_COUNT;
1109 } else {
1110 bscan = NULL;
1111 bcount = 0;
1112 if (ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT)
1113 __add_pfs_blockref(bref, blist, &ipdata);
1114 else
1115 assert(0); /* should only see SUPROOT or PFS */
1116 }
1117 break;
1118 case HAMMER2_BREF_TYPE_INDIRECT:
1119 bscan = &media.npdata[0];
1120 bcount = bytes / sizeof(hammer2_blockref_t);
1121 break;
1122 case HAMMER2_BREF_TYPE_VOLUME:
1123 bscan = &media.voldata.sroot_blockset.blockref[0];
1124 bcount = HAMMER2_SET_COUNT;
1125 break;
1126 default:
1127 bscan = NULL;
1128 bcount = 0;
1129 break;
1130 }
1131
1132 for (i = 0; i < bcount; ++i)
1133 if (init_pfs_blockref(voldata, &bscan[i], blist) == -1)
1134 return -1;
1135 return 0;
1136 }
1137
1138 static void
cleanup_pfs_blockref(struct blockref_list * blist)1139 cleanup_pfs_blockref(struct blockref_list *blist)
1140 {
1141 cleanup_blockref_msg(blist);
1142 }
1143
1144 static void
print_media(FILE * fp,int tab,const hammer2_blockref_t * bref,const hammer2_media_data_t * media,size_t media_bytes)1145 print_media(FILE *fp, int tab, const hammer2_blockref_t *bref,
1146 const hammer2_media_data_t *media, size_t media_bytes)
1147 {
1148 const hammer2_blockref_t *bscan;
1149 const hammer2_inode_data_t *ipdata;
1150 int i, bcount, namelen;
1151 char *str = NULL;
1152 uuid_t uuid;
1153
1154 switch (bref->type) {
1155 case HAMMER2_BREF_TYPE_INODE:
1156 ipdata = &media->ipdata;
1157 namelen = ipdata->meta.name_len;
1158 if (namelen > HAMMER2_INODE_MAXNAME)
1159 namelen = 0;
1160 tfprintf(fp, tab, "filename \"%*.*s\"\n", namelen, namelen,
1161 ipdata->filename);
1162 tfprintf(fp, tab, "version %d\n", ipdata->meta.version);
1163 if ((ipdata->meta.op_flags & HAMMER2_OPFLAG_PFSROOT) ||
1164 ipdata->meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT)
1165 tfprintf(fp, tab, "pfs_subtype %d (%s)\n",
1166 ipdata->meta.pfs_subtype,
1167 hammer2_pfssubtype_to_str(ipdata->meta.pfs_subtype));
1168 tfprintf(fp, tab, "uflags 0x%08x\n", ipdata->meta.uflags);
1169 if (ipdata->meta.rmajor || ipdata->meta.rminor) {
1170 tfprintf(fp, tab, "rmajor %d\n", ipdata->meta.rmajor);
1171 tfprintf(fp, tab, "rminor %d\n", ipdata->meta.rminor);
1172 }
1173 tfprintf(fp, tab, "ctime %s\n",
1174 hammer2_time64_to_str(ipdata->meta.ctime, &str));
1175 tfprintf(fp, tab, "mtime %s\n",
1176 hammer2_time64_to_str(ipdata->meta.mtime, &str));
1177 tfprintf(fp, tab, "atime %s\n",
1178 hammer2_time64_to_str(ipdata->meta.atime, &str));
1179 tfprintf(fp, tab, "btime %s\n",
1180 hammer2_time64_to_str(ipdata->meta.btime, &str));
1181 uuid = ipdata->meta.uid;
1182 tfprintf(fp, tab, "uid %s\n", hammer2_uuid_to_str(&uuid, &str));
1183 uuid = ipdata->meta.gid;
1184 tfprintf(fp, tab, "gid %s\n", hammer2_uuid_to_str(&uuid, &str));
1185 tfprintf(fp, tab, "type %s\n",
1186 hammer2_iptype_to_str(ipdata->meta.type));
1187 tfprintf(fp, tab, "op_flags 0x%02x\n", ipdata->meta.op_flags);
1188 tfprintf(fp, tab, "cap_flags 0x%04x\n", ipdata->meta.cap_flags);
1189 tfprintf(fp, tab, "mode %-7o\n", ipdata->meta.mode);
1190 tfprintf(fp, tab, "inum 0x%016jx\n", ipdata->meta.inum);
1191 tfprintf(fp, tab, "size %ju ", (uintmax_t)ipdata->meta.size);
1192 if (ipdata->meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA &&
1193 ipdata->meta.size <= HAMMER2_EMBEDDED_BYTES)
1194 printf("(embedded data)\n");
1195 else
1196 printf("\n");
1197 tfprintf(fp, tab, "nlinks %ju\n",
1198 (uintmax_t)ipdata->meta.nlinks);
1199 tfprintf(fp, tab, "iparent 0x%016jx\n",
1200 (uintmax_t)ipdata->meta.iparent);
1201 tfprintf(fp, tab, "name_key 0x%016jx\n",
1202 (uintmax_t)ipdata->meta.name_key);
1203 tfprintf(fp, tab, "name_len %u\n", ipdata->meta.name_len);
1204 tfprintf(fp, tab, "ncopies %u\n", ipdata->meta.ncopies);
1205 tfprintf(fp, tab, "comp_algo %u\n", ipdata->meta.comp_algo);
1206 tfprintf(fp, tab, "target_type %u\n", ipdata->meta.target_type);
1207 tfprintf(fp, tab, "check_algo %u\n", ipdata->meta.check_algo);
1208 if ((ipdata->meta.op_flags & HAMMER2_OPFLAG_PFSROOT) ||
1209 ipdata->meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
1210 tfprintf(fp, tab, "pfs_nmasters %u\n",
1211 ipdata->meta.pfs_nmasters);
1212 tfprintf(fp, tab, "pfs_type %u (%s)\n",
1213 ipdata->meta.pfs_type,
1214 hammer2_pfstype_to_str(ipdata->meta.pfs_type));
1215 tfprintf(fp, tab, "pfs_inum 0x%016jx\n",
1216 (uintmax_t)ipdata->meta.pfs_inum);
1217 uuid = ipdata->meta.pfs_clid;
1218 tfprintf(fp, tab, "pfs_clid %s\n",
1219 hammer2_uuid_to_str(&uuid, &str));
1220 uuid = ipdata->meta.pfs_fsid;
1221 tfprintf(fp, tab, "pfs_fsid %s\n",
1222 hammer2_uuid_to_str(&uuid, &str));
1223 tfprintf(fp, tab, "pfs_lsnap_tid 0x%016jx\n",
1224 (uintmax_t)ipdata->meta.pfs_lsnap_tid);
1225 }
1226 tfprintf(fp, tab, "data_quota %ju\n",
1227 (uintmax_t)ipdata->meta.data_quota);
1228 tfprintf(fp, tab, "data_count %ju\n",
1229 (uintmax_t)bref->embed.stats.data_count);
1230 tfprintf(fp, tab, "inode_quota %ju\n",
1231 (uintmax_t)ipdata->meta.inode_quota);
1232 tfprintf(fp, tab, "inode_count %ju\n",
1233 (uintmax_t)bref->embed.stats.inode_count);
1234 break;
1235 case HAMMER2_BREF_TYPE_INDIRECT:
1236 bcount = media_bytes / sizeof(hammer2_blockref_t);
1237 for (i = 0; i < bcount; ++i) {
1238 bscan = &media->npdata[i];
1239 tfprintf(fp, tab, "%3d %016jx %-12s %016jx/%-2d\n",
1240 i, (uintmax_t)bscan->data_off,
1241 hammer2_breftype_to_str(bscan->type),
1242 (uintmax_t)bscan->key,
1243 bscan->keybits);
1244 }
1245 break;
1246 case HAMMER2_BREF_TYPE_DIRENT:
1247 if (bref->embed.dirent.namlen <= sizeof(bref->check.buf)) {
1248 tfprintf(fp, tab, "filename \"%*.*s\"\n",
1249 bref->embed.dirent.namlen,
1250 bref->embed.dirent.namlen,
1251 bref->check.buf);
1252 } else {
1253 tfprintf(fp, tab, "filename \"%*.*s\"\n",
1254 bref->embed.dirent.namlen,
1255 bref->embed.dirent.namlen,
1256 media->buf);
1257 }
1258 tfprintf(fp, tab, "inum 0x%016jx\n",
1259 (uintmax_t)bref->embed.dirent.inum);
1260 tfprintf(fp, tab, "namlen %d\n",
1261 (uintmax_t)bref->embed.dirent.namlen);
1262 tfprintf(fp, tab, "type %s\n",
1263 hammer2_iptype_to_str(bref->embed.dirent.type));
1264 break;
1265 case HAMMER2_BREF_TYPE_FREEMAP_NODE:
1266 bcount = media_bytes / sizeof(hammer2_blockref_t);
1267 for (i = 0; i < bcount; ++i) {
1268 bscan = &media->npdata[i];
1269 tfprintf(fp, tab, "%3d %016jx %-12s %016jx/%-2d\n",
1270 i, (uintmax_t)bscan->data_off,
1271 hammer2_breftype_to_str(bscan->type),
1272 (uintmax_t)bscan->key,
1273 bscan->keybits);
1274 }
1275 break;
1276 case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
1277 for (i = 0; i < HAMMER2_FREEMAP_COUNT; ++i) {
1278 hammer2_off_t data_off = bref->key +
1279 i * HAMMER2_FREEMAP_LEVEL0_SIZE;
1280 #if HAMMER2_BMAP_ELEMENTS != 8
1281 #error "HAMMER2_BMAP_ELEMENTS != 8"
1282 #endif
1283 tfprintf(fp, tab, "%016jx %04d.%04x (avail=%7d) "
1284 "%016jx %016jx %016jx %016jx "
1285 "%016jx %016jx %016jx %016jx\n",
1286 data_off, i, media->bmdata[i].class,
1287 media->bmdata[i].avail,
1288 media->bmdata[i].bitmapq[0],
1289 media->bmdata[i].bitmapq[1],
1290 media->bmdata[i].bitmapq[2],
1291 media->bmdata[i].bitmapq[3],
1292 media->bmdata[i].bitmapq[4],
1293 media->bmdata[i].bitmapq[5],
1294 media->bmdata[i].bitmapq[6],
1295 media->bmdata[i].bitmapq[7]);
1296 }
1297 break;
1298 default:
1299 break;
1300 }
1301 if (str)
1302 free(str);
1303 }
1304
1305 int
test_hammer2(const char * devpath)1306 test_hammer2(const char *devpath)
1307 {
1308 bool failed = false;
1309
1310 hammer2_init_volumes(devpath, 1);
1311
1312 best_zone = find_best_zone();
1313 if (best_zone == -1)
1314 fprintf(stderr, "Failed to find best zone\n");
1315
1316 if (PrintPFS) {
1317 if (test_pfs_blockref() == -1)
1318 failed = true;
1319 goto end; /* print PFS info and exit */
1320 }
1321
1322 printf("volume header\n");
1323 if (test_volume_header() == -1) {
1324 failed = true;
1325 if (!ForceOpt)
1326 goto end;
1327 }
1328
1329 printf("freemap\n");
1330 if (test_blockref(HAMMER2_BREF_TYPE_FREEMAP) == -1) {
1331 failed = true;
1332 if (!ForceOpt)
1333 goto end;
1334 }
1335 printf("volume\n");
1336 if (!ScanPFS) {
1337 if (test_blockref(HAMMER2_BREF_TYPE_VOLUME) == -1) {
1338 failed = true;
1339 if (!ForceOpt)
1340 goto end;
1341 }
1342 } else {
1343 if (test_pfs_blockref() == -1) {
1344 failed = true;
1345 if (!ForceOpt)
1346 goto end;
1347 }
1348 }
1349 end:
1350 hammer2_cleanup_volumes();
1351
1352 return failed ? -1 : 0;
1353 }
1354