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