xref: /dragonfly/sbin/hammer2/cmd_debug.c (revision 5ca0a96d)
1 /*
2  * Copyright (c) 2011-2012 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@dragonflybsd.org>
6  * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  * 3. Neither the name of The DragonFly Project nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific, prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include "hammer2.h"
37 
38 #include <openssl/sha.h>
39 
40 #define GIG	(1024LL*1024*1024)
41 
42 static int show_tab = 2;
43 static int show_depth = -1;
44 
45 static void shell_msghandler(dmsg_msg_t *msg, int unmanaged);
46 static void shell_ttymsg(dmsg_iocom_t *iocom);
47 static void CountBlocks(hammer2_bmap_data_t *bmap, int value,
48 		hammer2_off_t *accum16, hammer2_off_t *accum64);
49 
50 /************************************************************************
51  *				    SHELL				*
52  ************************************************************************/
53 
54 int
55 cmd_shell(const char *hostname)
56 {
57 	dmsg_master_service_info_t *info;
58 	pthread_t thread;
59 	int fd;
60 
61 	fd = dmsg_connect(hostname);
62 	if (fd < 0)
63 		return 1;
64 
65 	info = malloc(sizeof(*info));
66 	bzero(info, sizeof(*info));
67 	info->fd = fd;
68 	info->detachme = 0;
69 	info->usrmsg_callback = shell_msghandler;
70 	info->altmsg_callback = shell_ttymsg;
71 	info->label = strdup("debug");
72 	pthread_create(&thread, NULL, dmsg_master_service, info);
73 	pthread_join(thread, NULL);
74 
75 	return 0;
76 }
77 
78 #if 0
79 int
80 cmd_shell(const char *hostname)
81 {
82 	struct dmsg_iocom iocom;
83 	dmsg_msg_t *msg;
84 	int fd;
85 
86 	/*
87 	 * Connect to the target
88 	 */
89 	fd = dmsg_connect(hostname);
90 	if (fd < 0)
91 		return 1;
92 
93 	/*
94 	 * Initialize the session and transmit an empty DMSG_DBG_SHELL
95 	 * to cause the remote end to generate a prompt.
96 	 */
97 	dmsg_iocom_init(&iocom, fd, 0,
98 			NULL,
99 			shell_rcvmsg,
100 			hammer2_shell_parse,
101 			shell_ttymsg);
102 	fcntl(0, F_SETFL, O_NONBLOCK);
103 	printf("debug: connected\n");
104 
105 	msg = dmsg_msg_alloc(&iocom.state0, 0, DMSG_DBG_SHELL, NULL, NULL);
106 	dmsg_msg_write(msg);
107 	dmsg_iocom_core(&iocom);
108 	fprintf(stderr, "debug: disconnected\n");
109 	close(fd);
110 	return 0;
111 }
112 #endif
113 
114 /*
115  * Debug session front-end
116  *
117  * Callback from dmsg_iocom_core() when messages might be present
118  * on the socket.
119  */
120 static
121 void
122 shell_msghandler(dmsg_msg_t *msg, int unmanaged)
123 {
124 	dmsg_msg_t *nmsg;
125 
126 	switch(msg->tcmd) {
127 #if 0
128 	case DMSG_LNK_ERROR:
129 	case DMSG_LNK_ERROR | DMSGF_REPLY:
130 		/*
131 		 * One-way non-transactional LNK_ERROR messages typically
132 		 * indicate a connection failure.  Error code 0 is used by
133 		 * the debug shell to indicate no more results from last cmd.
134 		 */
135 		if (msg->any.head.error) {
136 			fprintf(stderr, "Stream failure: %s\n",
137 				dmsg_msg_str(msg));
138 		} else {
139 			write(1, "debug> ", 7);
140 		}
141 		break;
142 	case DMSG_LNK_ERROR | DMSGF_DELETE:
143 		/* ignore termination of LNK_CONN */
144 		break;
145 #endif
146 	case DMSG_DBG_SHELL:
147 		/*
148 		 * We send the commands, not accept them.
149 		 * (one-way message, not transactional)
150 		 */
151 		if (unmanaged)
152 			dmsg_msg_reply(msg, DMSG_ERR_NOSUPP);
153 		break;
154 	case DMSG_DBG_SHELL | DMSGF_REPLY:
155 		/*
156 		 * A reply from the remote is data we copy to stdout.
157 		 * (one-way message, not transactional)
158 		 */
159 		if (msg->aux_size) {
160 			msg->aux_data[msg->aux_size - 1] = 0;
161 			write(1, msg->aux_data, strlen(msg->aux_data));
162 		}
163 		break;
164 #if 1
165 	case DMSG_LNK_CONN | DMSGF_CREATE:
166 		fprintf(stderr, "Debug Shell received LNK_CONN\n");
167 		nmsg = dmsg_msg_alloc(&msg->state->iocom->state0, 0,
168 				      DMSG_DBG_SHELL,
169 				      NULL, NULL);
170 		dmsg_msg_write(nmsg);
171 		dmsg_msg_reply(msg, DMSG_ERR_NOSUPP);
172 		break;
173 	case DMSG_LNK_CONN | DMSGF_DELETE:
174 		break;
175 #endif
176 	default:
177 		/*
178 		 * Ignore any unknown messages, Terminate any unknown
179 		 * transactions with an error.
180 		 */
181 		fprintf(stderr, "Unknown message: %s\n", dmsg_msg_str(msg));
182 		if (unmanaged) {
183 			if (msg->any.head.cmd & DMSGF_CREATE)
184 				dmsg_msg_reply(msg, DMSG_ERR_NOSUPP);
185 			if (msg->any.head.cmd & DMSGF_DELETE)
186 				dmsg_msg_reply(msg, DMSG_ERR_NOSUPP);
187 		}
188 		break;
189 	}
190 }
191 
192 /*
193  * Debug session front-end
194  */
195 static
196 void
197 shell_ttymsg(dmsg_iocom_t *iocom)
198 {
199 	dmsg_state_t *pstate;
200 	dmsg_msg_t *msg;
201 	char buf[256];
202 	char *cmd;
203 	size_t len;
204 
205 	if (fgets(buf, sizeof(buf), stdin) != NULL) {
206 		if (buf[0] == '@') {
207 			pstate = dmsg_findspan(strtok(buf + 1, " \t\n"));
208 			cmd = strtok(NULL, "\n");
209 		} else {
210 			pstate = &iocom->state0;
211 			cmd = strtok(buf, "\n");
212 		}
213 		if (cmd && pstate) {
214 			len = strlen(cmd) + 1;
215 			msg = dmsg_msg_alloc(pstate, len, DMSG_DBG_SHELL,
216 					     NULL, NULL);
217 			bcopy(cmd, msg->aux_data, len);
218 			dmsg_msg_write(msg);
219 		} else if (cmd) {
220 			fprintf(stderr, "@msgid not found\n");
221 		} else {
222 			/*
223 			 * This should cause the remote end to generate
224 			 * a debug> prompt (and thus shows that there is
225 			 * connectivity).
226 			 */
227 			msg = dmsg_msg_alloc(pstate, 0, DMSG_DBG_SHELL,
228 					     NULL, NULL);
229 			dmsg_msg_write(msg);
230 		}
231 	} else if (feof(stdin)) {
232 		/*
233 		 * Set EOF flag without setting any error code for normal
234 		 * EOF.
235 		 */
236 		iocom->flags |= DMSG_IOCOMF_EOF;
237 	} else {
238 		clearerr(stdin);
239 	}
240 }
241 
242 /*
243  * Debug session back-end (on remote side)
244  */
245 static void shell_span(dmsg_msg_t *msg, char *cmdbuf);
246 static void shell_ping(dmsg_msg_t *msg, char *cmdbuf);
247 
248 void
249 hammer2_shell_parse(dmsg_msg_t *msg, int unmanaged)
250 {
251 	dmsg_iocom_t *iocom = msg->state->iocom;
252 	char *cmdbuf;
253 	char *cmdp;
254 	uint32_t cmd;
255 
256 	/*
257 	 * Filter on debug shell commands and ping responses only
258 	 */
259 	cmd = msg->any.head.cmd;
260 	if ((cmd & DMSGF_CMDSWMASK) == (DMSG_LNK_PING | DMSGF_REPLY)) {
261 		dmsg_printf(iocom, "ping reply\n");
262 		return;
263 	}
264 
265 	if ((cmd & DMSGF_PROTOS) != DMSG_PROTO_DBG) {
266 		if (unmanaged)
267 			dmsg_msg_reply(msg, DMSG_ERR_NOSUPP);
268 		return;
269 	}
270 	if ((cmd & DMSGF_CMDSWMASK) != DMSG_DBG_SHELL) {
271 		if (unmanaged)
272 			dmsg_msg_reply(msg, DMSG_ERR_NOSUPP);
273 		return;
274 	}
275 
276 	/*
277 	 * Debug shell command
278 	 */
279 	cmdbuf = msg->aux_data;
280 	cmdp = strsep(&cmdbuf, " \t");
281 
282 	if (cmdp == NULL || *cmdp == 0) {
283 		;
284 	} else if (strcmp(cmdp, "ping") == 0) {
285 		shell_ping(msg, cmdbuf);
286 	} else if (strcmp(cmdp, "span") == 0) {
287 		shell_span(msg, cmdbuf);
288 	} else if (strcmp(cmdp, "tree") == 0) {
289 		dmsg_shell_tree(iocom, cmdbuf); /* dump spanning tree */
290 	} else if (strcmp(cmdp, "help") == 0 || strcmp(cmdp, "?") == 0) {
291 		dmsg_printf(iocom, "help            Command help\n");
292 		dmsg_printf(iocom, "span <host>     Span to target host\n");
293 		dmsg_printf(iocom, "tree            Dump spanning tree\n");
294 		dmsg_printf(iocom, "@span <cmd>     Issue via circuit\n");
295 	} else {
296 		dmsg_printf(iocom, "Unrecognized command: %s\n", cmdp);
297 	}
298 	dmsg_printf(iocom, "debug> ");
299 }
300 
301 static void
302 shell_ping(dmsg_msg_t *msg, char *cmdbuf __unused)
303 {
304 	dmsg_iocom_t *iocom = msg->state->iocom;
305 	dmsg_msg_t *m2;
306 
307 	dmsg_printf(iocom, "sending ping\n");
308 	m2 = dmsg_msg_alloc(msg->state, 0, DMSG_LNK_PING, NULL, NULL);
309 	dmsg_msg_write(m2);
310 }
311 
312 static void
313 shell_span(dmsg_msg_t *msg, char *cmdbuf)
314 {
315 	dmsg_iocom_t *iocom = msg->state->iocom;
316 	dmsg_master_service_info_t *info;
317 	const char *hostname = strsep(&cmdbuf, " \t");
318 	pthread_t thread;
319 	int fd;
320 
321 	/*
322 	 * Connect to the target
323 	 */
324 	if (hostname == NULL) {
325 		fd = -1;
326 	} else {
327 		fd = dmsg_connect(hostname);
328 	}
329 
330 	/*
331 	 * Start master service
332 	 */
333 	if (fd < 0) {
334 		dmsg_printf(iocom, "Connection to %s failed\n", hostname);
335 	} else {
336 		dmsg_printf(iocom, "Connected to %s\n", hostname);
337 
338 		info = malloc(sizeof(*info));
339 		bzero(info, sizeof(*info));
340 		info->fd = fd;
341 		info->detachme = 1;
342 		info->usrmsg_callback = hammer2_shell_parse;
343 		info->label = strdup("client");
344 
345 		pthread_create(&thread, NULL, dmsg_master_service, info);
346 		/*pthread_join(thread, &res);*/
347 	}
348 }
349 
350 /************************************************************************
351  *				DEBUGSPAN				*
352  ************************************************************************
353  *
354  * Connect to the target manually (not via the cluster list embedded in
355  * a hammer2 filesystem) and initiate the SPAN protocol.
356  */
357 int
358 cmd_debugspan(const char *hostname)
359 {
360 	pthread_t thread;
361 	int fd;
362 	void *res;
363 
364 	/*
365 	 * Connect to the target
366 	 */
367 	fd = dmsg_connect(hostname);
368 	if (fd < 0)
369 		return 1;
370 
371 	printf("debugspan: connected to %s, starting CONN/SPAN\n", hostname);
372 	pthread_create(&thread, NULL,
373 		       dmsg_master_service, (void *)(intptr_t)fd);
374 	pthread_join(thread, &res);
375 	return(0);
376 }
377 
378 /************************************************************************
379  *				    SHOW				*
380  ************************************************************************/
381 
382 static void show_volhdr(hammer2_volume_data_t *voldata, int bi);
383 static void show_bref(hammer2_volume_data_t *voldata, int tab,
384 			int bi, hammer2_blockref_t *bref, int norecurse);
385 static void tabprintf(int tab, const char *ctl, ...);
386 
387 static hammer2_off_t TotalAccum16[4]; /* includes TotalAccum64 */
388 static hammer2_off_t TotalAccum64[4];
389 static hammer2_off_t TotalUnavail;
390 static hammer2_off_t TotalFreemap;
391 
392 static
393 hammer2_off_t
394 get_next_volume(hammer2_volume_data_t *voldata, hammer2_off_t volu_loff)
395 {
396 	hammer2_off_t ret = -1;
397 	int i;
398 
399 	for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
400 		hammer2_off_t tmp = voldata->volu_loff[i];
401 		if (tmp > volu_loff) {
402 			ret = tmp;
403 			break;
404 		}
405 	}
406 	return ret;
407 }
408 
409 int
410 cmd_show(const char *devpath, int which)
411 {
412 	hammer2_blockref_t broot;
413 	hammer2_blockref_t best;
414 	hammer2_media_data_t media;
415 	hammer2_media_data_t best_media;
416 	hammer2_off_t off, volu_loff, next_volu_loff = 0;
417 	int fd;
418 	int i;
419 	int best_i;
420 	char *env;
421 
422 	memset(TotalAccum16, 0, sizeof(TotalAccum16));
423 	memset(TotalAccum64, 0, sizeof(TotalAccum64));
424 	TotalUnavail = TotalFreemap = 0;
425 
426 	env = getenv("HAMMER2_SHOW_TAB");
427 	if (env != NULL) {
428 		show_tab = (int)strtol(env, NULL, 0);
429 		if (errno || show_tab < 0 || show_tab > 8)
430 			show_tab = 2;
431 	}
432 	env = getenv("HAMMER2_SHOW_DEPTH");
433 	if (env != NULL) {
434 		show_depth = (int)strtol(env, NULL, 0);
435 		if (errno || show_depth < 0)
436 			show_depth = -1;
437 	}
438 
439 	hammer2_init_volumes(devpath, 1);
440 next_volume:
441 	volu_loff = next_volu_loff;
442 	next_volu_loff = -1;
443 	printf("%s\n", hammer2_get_volume_path(volu_loff));
444 	/*
445 	 * Show the tree using the best volume header.
446 	 * -vvv will show the tree for all four volume headers.
447 	 */
448 	best_i = -1;
449 	bzero(&best, sizeof(best));
450 	bzero(&best_media, sizeof(best_media));
451 	for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
452 		bzero(&broot, sizeof(broot));
453 		broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
454 		off = broot.data_off & ~HAMMER2_OFF_MASK_RADIX;
455 		fd = hammer2_get_volume_fd(volu_loff);
456 		lseek(fd, off, SEEK_SET);
457 		if (read(fd, &media, HAMMER2_PBUFSIZE) ==
458 		    (ssize_t)HAMMER2_PBUFSIZE) {
459 			broot.mirror_tid = media.voldata.mirror_tid;
460 			if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
461 				best_i = i;
462 				best = broot;
463 				best_media = media;
464 			}
465 			printf("Volume header %d: mirror_tid=%016jx\n",
466 			       i, (intmax_t)broot.mirror_tid);
467 
468 			if (VerboseOpt >= 3) {
469 				switch(which) {
470 				case 0:
471 					broot.type = HAMMER2_BREF_TYPE_VOLUME;
472 					show_bref(&media.voldata, 0, i, &broot,
473 						  0);
474 					break;
475 				case 1:
476 					broot.type = HAMMER2_BREF_TYPE_FREEMAP;
477 					show_bref(&media.voldata, 0, i, &broot,
478 						  0);
479 					break;
480 				default:
481 					show_volhdr(&media.voldata, i);
482 					if (i == 0)
483 						next_volu_loff = get_next_volume(&media.voldata, volu_loff);
484 					break;
485 				}
486 				if (i != HAMMER2_NUM_VOLHDRS - 1)
487 					printf("\n");
488 			}
489 		}
490 	}
491 	if (next_volu_loff != (hammer2_off_t)-1) {
492 		printf("---------------------------------------------\n");
493 		goto next_volume;
494 	}
495 
496 	if (VerboseOpt < 3) {
497 		switch(which) {
498 		case 0:
499 			best.type = HAMMER2_BREF_TYPE_VOLUME;
500 			show_bref(&best_media.voldata, 0, best_i, &best, 0);
501 			break;
502 		case 1:
503 			best.type = HAMMER2_BREF_TYPE_FREEMAP;
504 			show_bref(&best_media.voldata, 0, best_i, &best, 0);
505 			break;
506 		default:
507 			show_volhdr(&best_media.voldata, best_i);
508 			next_volu_loff = get_next_volume(&best_media.voldata, volu_loff);
509 			if (next_volu_loff != (hammer2_off_t)-1) {
510 				printf("---------------------------------------------\n");
511 				goto next_volume;
512 			}
513 			break;
514 		}
515 	}
516 
517 	if (which == 1 && VerboseOpt < 3) {
518 		printf("Total unallocated storage:   %6.3fGiB (%6.3fGiB in 64KB chunks)\n",
519 		       (double)TotalAccum16[0] / GIG,
520 		       (double)TotalAccum64[0] / GIG);
521 		printf("Total possibly free storage: %6.3fGiB (%6.3fGiB in 64KB chunks)\n",
522 		       (double)TotalAccum16[2] / GIG,
523 		       (double)TotalAccum64[2] / GIG);
524 		printf("Total allocated storage:     %6.3fGiB (%6.3fGiB in 64KB chunks)\n",
525 		       (double)TotalAccum16[3] / GIG,
526 		       (double)TotalAccum64[3] / GIG);
527 		printf("Total unavailable storage:   %6.3fGiB\n",
528 		       (double)TotalUnavail / GIG);
529 		printf("Total freemap storage:       %6.3fGiB\n",
530 		       (double)TotalFreemap / GIG);
531 	}
532 	hammer2_cleanup_volumes();
533 
534 	return 0;
535 }
536 
537 static void
538 show_volhdr(hammer2_volume_data_t *voldata, int bi)
539 {
540 	uint32_t status;
541 	uint32_t i;
542 	char *str;
543 	char *name;
544 	char *buf;
545 
546 	printf("\nVolume header %d {\n", bi);
547 	printf("    magic          0x%016jx\n", (intmax_t)voldata->magic);
548 	printf("    boot_beg       0x%016jx\n", (intmax_t)voldata->boot_beg);
549 	printf("    boot_end       0x%016jx (%6.2fMB)\n",
550 	       (intmax_t)voldata->boot_end,
551 	       (double)(voldata->boot_end - voldata->boot_beg) /
552 	       (1024.0*1024.0));
553 	printf("    aux_beg        0x%016jx\n", (intmax_t)voldata->aux_beg);
554 	printf("    aux_end        0x%016jx (%6.2fMB)\n",
555 	       (intmax_t)voldata->aux_end,
556 	       (double)(voldata->aux_end - voldata->aux_beg) /
557 	       (1024.0*1024.0));
558 	printf("    volu_size      0x%016jx (%6.2fGiB)\n",
559 	       (intmax_t)voldata->volu_size,
560 	       (double)voldata->volu_size / GIG);
561 	printf("    version        %d\n", voldata->version);
562 	printf("    flags          0x%08x\n", voldata->flags);
563 	printf("    copyid         %d\n", voldata->copyid);
564 	printf("    freemap_vers   %d\n", voldata->freemap_version);
565 	printf("    peer_type      %d\n", voldata->peer_type);
566 	printf("    volu_id        %d\n", voldata->volu_id);
567 	printf("    nvolumes       %d\n", voldata->nvolumes);
568 
569 	str = NULL;
570 	hammer2_uuid_to_str(&voldata->fsid, &str);
571 	printf("    fsid           %s\n", str);
572 	free(str);
573 
574 	str = NULL;
575 	name = NULL;
576 	hammer2_uuid_to_str(&voldata->fstype, &str);
577 	printf("    fstype         %s\n", str);
578 	uuid_addr_lookup(&voldata->fstype, &name, &status);
579 	if (name == NULL)
580 		name = strdup("?");
581 	printf("                   (%s)\n", name);
582 	free(name);
583 	free(str);
584 
585 	printf("    allocator_size 0x%016jx (%6.2fGiB)\n",
586 	       voldata->allocator_size,
587 	       (double)voldata->allocator_size / GIG);
588 	printf("    allocator_free 0x%016jx (%6.2fGiB)\n",
589 	       voldata->allocator_free,
590 	       (double)voldata->allocator_free / GIG);
591 	printf("    allocator_beg  0x%016jx (%6.2fGiB)\n",
592 	       voldata->allocator_beg,
593 	       (double)voldata->allocator_beg / GIG);
594 
595 	printf("    mirror_tid     0x%016jx\n", voldata->mirror_tid);
596 	printf("    reserved0080   0x%016jx\n", voldata->reserved0080);
597 	printf("    reserved0088   0x%016jx\n", voldata->reserved0088);
598 	printf("    freemap_tid    0x%016jx\n", voldata->freemap_tid);
599 	printf("    bulkfree_tid   0x%016jx\n", voldata->bulkfree_tid);
600 	for (i = 0; i < nitems(voldata->reserved00A0); ++i) {
601 		printf("    reserved00A0/%u 0x%016jx\n",
602 		       i, voldata->reserved00A0[0]);
603 	}
604 	printf("    total_size     0x%016jx\n", voldata->total_size);
605 
606 	printf("    copyexists    ");
607 	for (i = 0; i < nitems(voldata->copyexists); ++i)
608 		printf(" 0x%02x", voldata->copyexists[i]);
609 	printf("\n");
610 
611 	/*
612 	 * NOTE: Index numbers and ICRC_SECTn definitions are not matched,
613 	 *	 the ICRC for sector 0 actually uses the last index, for
614 	 *	 example.
615 	 *
616 	 * NOTE: The whole voldata CRC does not have to match critically
617 	 *	 as certain sub-areas of the volume header have their own
618 	 *	 CRCs.
619 	 */
620 	printf("\n");
621 	for (i = 0; i < nitems(voldata->icrc_sects); ++i) {
622 		printf("    icrc_sects[%u]  ", i);
623 		switch(i) {
624 		case HAMMER2_VOL_ICRC_SECT0:
625 			printf("0x%08x/0x%08x",
626 			       hammer2_icrc32((char *)voldata +
627 					      HAMMER2_VOLUME_ICRC0_OFF,
628 					      HAMMER2_VOLUME_ICRC0_SIZE),
629 			       voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT0]);
630 			if (hammer2_icrc32((char *)voldata +
631 					   HAMMER2_VOLUME_ICRC0_OFF,
632 					   HAMMER2_VOLUME_ICRC0_SIZE) ==
633 			       voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT0]) {
634 				printf(" (OK)");
635 			} else {
636 				printf(" (FAILED)");
637 			}
638 			break;
639 		case HAMMER2_VOL_ICRC_SECT1:
640 			printf("0x%08x/0x%08x",
641 			       hammer2_icrc32((char *)voldata +
642 					      HAMMER2_VOLUME_ICRC1_OFF,
643 					      HAMMER2_VOLUME_ICRC1_SIZE),
644 			       voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT1]);
645 			if (hammer2_icrc32((char *)voldata +
646 					   HAMMER2_VOLUME_ICRC1_OFF,
647 					   HAMMER2_VOLUME_ICRC1_SIZE) ==
648 			       voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT1]) {
649 				printf(" (OK)");
650 			} else {
651 				printf(" (FAILED)");
652 			}
653 
654 			break;
655 		default:
656 			printf("0x%08x (reserved)", voldata->icrc_sects[i]);
657 			break;
658 		}
659 		printf("\n");
660 	}
661 	printf("    icrc_volhdr    0x%08x/0x%08x",
662 	       hammer2_icrc32((char *)voldata + HAMMER2_VOLUME_ICRCVH_OFF,
663 			      HAMMER2_VOLUME_ICRCVH_SIZE),
664 	       voldata->icrc_volheader);
665 	if (hammer2_icrc32((char *)voldata + HAMMER2_VOLUME_ICRCVH_OFF,
666 			   HAMMER2_VOLUME_ICRCVH_SIZE) ==
667 	    voldata->icrc_volheader) {
668 		printf(" (OK)\n");
669 	} else {
670 		printf(" (FAILED - not a critical error)\n");
671 	}
672 
673 	/*
674 	 * The super-root and freemap blocksets (not recursed)
675 	 */
676 	printf("\n");
677 	printf("    sroot_blockset {\n");
678 	for (i = 0; i < HAMMER2_SET_COUNT; ++i) {
679 		show_bref(voldata, 16, i,
680 			  &voldata->sroot_blockset.blockref[i], 2);
681 	}
682 	printf("    }\n");
683 
684 	printf("    freemap_blockset {\n");
685 	for (i = 0; i < HAMMER2_SET_COUNT; ++i) {
686 		show_bref(voldata, 16, i,
687 			  &voldata->freemap_blockset.blockref[i], 2);
688 	}
689 	printf("    }\n");
690 
691 	buf = calloc(1, sizeof(voldata->volu_loff));
692 	if (bcmp(buf, voldata->volu_loff, sizeof(voldata->volu_loff))) {
693 		printf("\n");
694 		for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
695 			hammer2_off_t loff = voldata->volu_loff[i];
696 			if (loff != (hammer2_off_t)-1)
697 				printf("    volu_loff[%d]   0x%016jx\n", i, loff);
698 		}
699 	}
700 	free(buf);
701 
702 	printf("}\n");
703 }
704 
705 static void
706 show_bref(hammer2_volume_data_t *voldata, int tab, int bi,
707 	  hammer2_blockref_t *bref, int norecurse)
708 {
709 	hammer2_media_data_t media;
710 	hammer2_blockref_t *bscan;
711 	hammer2_off_t tmp;
712 	int i, bcount, namelen, failed, obrace, fd;
713 	int type_pad;
714 	size_t bytes;
715 	const char *type_str;
716 	char *str = NULL;
717 	uint32_t cv;
718 	uint64_t cv64;
719 	static int init_tab = -1;
720 
721 	SHA256_CTX hash_ctx;
722 	union {
723 		uint8_t digest[SHA256_DIGEST_LENGTH];
724 		uint64_t digest64[SHA256_DIGEST_LENGTH/8];
725 	} u;
726 
727 	if (init_tab == -1)
728 		init_tab = tab;
729 
730 	bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
731 	if (bytes)
732 		bytes = (size_t)1 << bytes;
733 	if (bytes) {
734 		hammer2_off_t io_off;
735 		hammer2_off_t io_base;
736 		size_t io_bytes;
737 		size_t boff;
738 
739 		io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
740 		io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1);
741 		boff = io_off - io_base;
742 
743 		io_bytes = HAMMER2_LBUFSIZE;
744 		while (io_bytes + boff < bytes)
745 			io_bytes <<= 1;
746 
747 		if (io_bytes > sizeof(media)) {
748 			printf("(bad block size %zu)\n", bytes);
749 			return;
750 		}
751 		if (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1) {
752 			fd = hammer2_get_volume_fd(io_off);
753 			lseek(fd, io_base - hammer2_get_volume_offset(io_base),
754 			      SEEK_SET);
755 			if (read(fd, &media, io_bytes) != (ssize_t)io_bytes) {
756 				printf("(media read failed)\n");
757 				return;
758 			}
759 			if (boff)
760 				bcopy((char *)&media + boff, &media, bytes);
761 		}
762 	}
763 
764 	bscan = NULL;
765 	bcount = 0;
766 	namelen = 0;
767 	failed = 0;
768 	obrace = 1;
769 
770 	type_str = hammer2_breftype_to_str(bref->type);
771 	type_pad = 8 - strlen(type_str);
772 	if (type_pad < 0)
773 		type_pad = 0;
774 
775 	switch(bref->type) {
776 	case HAMMER2_BREF_TYPE_INODE:
777 		assert(bytes);
778 		if (!(media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA)) {
779 			bscan = &media.ipdata.u.blockset.blockref[0];
780 			bcount = HAMMER2_SET_COUNT;
781 		}
782 		break;
783 	case HAMMER2_BREF_TYPE_INDIRECT:
784 		assert(bytes);
785 		bscan = &media.npdata[0];
786 		bcount = bytes / sizeof(hammer2_blockref_t);
787 		break;
788 	case HAMMER2_BREF_TYPE_VOLUME:
789 		bscan = &media.voldata.sroot_blockset.blockref[0];
790 		bcount = HAMMER2_SET_COUNT;
791 		break;
792 	case HAMMER2_BREF_TYPE_FREEMAP:
793 		bscan = &media.voldata.freemap_blockset.blockref[0];
794 		bcount = HAMMER2_SET_COUNT;
795 		break;
796 	case HAMMER2_BREF_TYPE_FREEMAP_NODE:
797 		assert(bytes);
798 		bscan = &media.npdata[0];
799 		bcount = bytes / sizeof(hammer2_blockref_t);
800 		break;
801 	}
802 
803 	if (QuietOpt > 0) {
804 		tabprintf(tab,
805 			  "%s.%-3d %016jx %016jx/%-2d "
806 			  "vol=%d mir=%016jx mod=%016jx leafcnt=%d ",
807 			  type_str, bi, (intmax_t)bref->data_off,
808 			  (intmax_t)bref->key, (intmax_t)bref->keybits,
809 			  hammer2_get_volume_id(bref->data_off),
810 			  (intmax_t)bref->mirror_tid,
811 			  (intmax_t)bref->modify_tid,
812 			  bref->leaf_count);
813 	} else {
814 		tabprintf(tab, "%s.%-3d%*.*s %016jx %016jx/%-2d ",
815 			  type_str, bi, type_pad, type_pad, "",
816 			  (intmax_t)bref->data_off,
817 			  (intmax_t)bref->key, (intmax_t)bref->keybits);
818 		/*if (norecurse > 1)*/ {
819 			printf("\n");
820 			tabprintf(tab + 13, "");
821 		}
822 		printf("vol=%d mir=%016jx mod=%016jx lfcnt=%d ",
823 		       hammer2_get_volume_id(bref->data_off),
824 		       (intmax_t)bref->mirror_tid, (intmax_t)bref->modify_tid,
825 		       bref->leaf_count);
826 		if (/*norecurse > 1 && */ (bcount || bref->flags ||
827 		    bref->type == HAMMER2_BREF_TYPE_FREEMAP_NODE ||
828 		    bref->type == HAMMER2_BREF_TYPE_FREEMAP_LEAF)) {
829 			printf("\n");
830 			tabprintf(tab + 13, "");
831 		}
832 	}
833 
834 	if (bcount)
835 		printf("bcnt=%d ", bcount);
836 	if (bref->flags)
837 		printf("flags=%02x ", bref->flags);
838 	if (bref->type == HAMMER2_BREF_TYPE_FREEMAP_NODE ||
839 	    bref->type == HAMMER2_BREF_TYPE_FREEMAP_LEAF) {
840 		printf("bigmask=%08x avail=%ld ",
841 			bref->check.freemap.bigmask, bref->check.freemap.avail);
842 	}
843 
844 	/*
845 	 * Check data integrity in verbose mode, otherwise we are just doing
846 	 * a quick meta-data scan.  Meta-data integrity is always checked.
847 	 * (Also see the check above that ensures the media data is loaded,
848 	 * otherwise there's no data to check!).
849 	 *
850 	 * WARNING! bref->check state may be used for other things when
851 	 *	    bref has no data (bytes == 0).
852 	 */
853 	if (bytes &&
854 	    (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1)) {
855 		if (!(QuietOpt > 0)) {
856 			/*if (norecurse > 1)*/ {
857 				printf("\n");
858 				tabprintf(tab + 13, "");
859 			}
860 		}
861 
862 		switch(HAMMER2_DEC_CHECK(bref->methods)) {
863 		case HAMMER2_CHECK_NONE:
864 			printf("meth=%02x ", bref->methods);
865 			break;
866 		case HAMMER2_CHECK_DISABLED:
867 			printf("meth=%02x ", bref->methods);
868 			break;
869 		case HAMMER2_CHECK_ISCSI32:
870 			cv = hammer2_icrc32(&media, bytes);
871 			if (bref->check.iscsi32.value != cv) {
872 				printf("(icrc %02x:%08x/%08x failed) ",
873 				       bref->methods,
874 				       bref->check.iscsi32.value,
875 				       cv);
876 				failed = 1;
877 			} else {
878 				printf("meth=%02x iscsi32=%08x ",
879 				       bref->methods, cv);
880 			}
881 			break;
882 		case HAMMER2_CHECK_XXHASH64:
883 			cv64 = XXH64(&media, bytes, XXH_HAMMER2_SEED);
884 			if (bref->check.xxhash64.value != cv64) {
885 				printf("(xxhash64 %02x:%016jx/%016jx failed) ",
886 				       bref->methods,
887 				       bref->check.xxhash64.value,
888 				       cv64);
889 				failed = 1;
890 			} else {
891 				printf("meth=%02x xxh=%016jx ",
892 				       bref->methods, cv64);
893 			}
894 			break;
895 		case HAMMER2_CHECK_SHA192:
896 			SHA256_Init(&hash_ctx);
897 			SHA256_Update(&hash_ctx, &media, bytes);
898 			SHA256_Final(u.digest, &hash_ctx);
899 			u.digest64[2] ^= u.digest64[3];
900 			if (memcmp(u.digest, bref->check.sha192.data,
901 			    sizeof(bref->check.sha192.data))) {
902 				printf("(sha192 failed) ");
903 				failed = 1;
904 			} else {
905 				printf("meth=%02x ", bref->methods);
906 			}
907 			break;
908 		case HAMMER2_CHECK_FREEMAP:
909 			cv = hammer2_icrc32(&media, bytes);
910 			if (bref->check.freemap.icrc32 != cv) {
911 				printf("(fcrc %02x:%08x/%08x failed) ",
912 					bref->methods,
913 					bref->check.freemap.icrc32,
914 					cv);
915 				failed = 1;
916 			} else {
917 				printf("meth=%02x fcrc=%08x ",
918 					bref->methods, cv);
919 			}
920 			break;
921 		}
922 	}
923 
924 	tab += show_tab;
925 
926 	if (QuietOpt > 0) {
927 		obrace = 0;
928 		printf("\n");
929 		goto skip_data;
930 	}
931 
932 	switch(bref->type) {
933 	case HAMMER2_BREF_TYPE_EMPTY:
934 		if (norecurse)
935 			printf("\n");
936 		obrace = 0;
937 		break;
938 	case HAMMER2_BREF_TYPE_DIRENT:
939 		printf("{\n");
940 		if (bref->embed.dirent.namlen <= sizeof(bref->check.buf)) {
941 			tabprintf(tab, "filename \"%*.*s\"\n",
942 				bref->embed.dirent.namlen,
943 				bref->embed.dirent.namlen,
944 				bref->check.buf);
945 		} else {
946 			tabprintf(tab, "filename \"%*.*s\"\n",
947 				bref->embed.dirent.namlen,
948 				bref->embed.dirent.namlen,
949 				media.buf);
950 		}
951 		tabprintf(tab, "inum 0x%016jx\n",
952 			  (uintmax_t)bref->embed.dirent.inum);
953 		tabprintf(tab, "nlen %d\n", bref->embed.dirent.namlen);
954 		tabprintf(tab, "type %s\n",
955 			  hammer2_iptype_to_str(bref->embed.dirent.type));
956 		break;
957 	case HAMMER2_BREF_TYPE_INODE:
958 		printf("{\n");
959 		namelen = media.ipdata.meta.name_len;
960 		if (namelen > HAMMER2_INODE_MAXNAME)
961 			namelen = 0;
962 		tabprintf(tab, "filename \"%*.*s\"\n",
963 			  namelen, namelen, media.ipdata.filename);
964 		tabprintf(tab, "version  %d\n", media.ipdata.meta.version);
965 		if ((media.ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) ||
966 		    media.ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
967 			tabprintf(tab, "pfs_st   %d (%s)\n",
968 				  media.ipdata.meta.pfs_subtype,
969 				  hammer2_pfssubtype_to_str(media.ipdata.meta.pfs_subtype));
970 		}
971 		tabprintf(tab, "uflags   0x%08x\n",
972 			  media.ipdata.meta.uflags);
973 		if (media.ipdata.meta.rmajor || media.ipdata.meta.rminor) {
974 			tabprintf(tab, "rmajor   %d\n",
975 				  media.ipdata.meta.rmajor);
976 			tabprintf(tab, "rminor   %d\n",
977 				  media.ipdata.meta.rminor);
978 		}
979 		tabprintf(tab, "ctime    %s\n",
980 			  hammer2_time64_to_str(media.ipdata.meta.ctime, &str));
981 		tabprintf(tab, "mtime    %s\n",
982 			  hammer2_time64_to_str(media.ipdata.meta.mtime, &str));
983 		tabprintf(tab, "atime    %s\n",
984 			  hammer2_time64_to_str(media.ipdata.meta.atime, &str));
985 		tabprintf(tab, "btime    %s\n",
986 			  hammer2_time64_to_str(media.ipdata.meta.btime, &str));
987 		tabprintf(tab, "uid      %s\n",
988 			  hammer2_uuid_to_str(&media.ipdata.meta.uid, &str));
989 		tabprintf(tab, "gid      %s\n",
990 			  hammer2_uuid_to_str(&media.ipdata.meta.gid, &str));
991 		tabprintf(tab, "type     %s\n",
992 			  hammer2_iptype_to_str(media.ipdata.meta.type));
993 		tabprintf(tab, "opflgs   0x%02x\n",
994 			  media.ipdata.meta.op_flags);
995 		tabprintf(tab, "capflgs  0x%04x\n",
996 			  media.ipdata.meta.cap_flags);
997 		tabprintf(tab, "mode     %-7o\n",
998 			  media.ipdata.meta.mode);
999 		tabprintf(tab, "inum     0x%016jx\n",
1000 			  media.ipdata.meta.inum);
1001 		tabprintf(tab, "size     %ju ",
1002 			  (uintmax_t)media.ipdata.meta.size);
1003 		if (media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA &&
1004 		    media.ipdata.meta.size <= HAMMER2_EMBEDDED_BYTES)
1005 			printf("(embedded data)\n");
1006 		else
1007 			printf("\n");
1008 		tabprintf(tab, "nlinks   %ju\n",
1009 			  (uintmax_t)media.ipdata.meta.nlinks);
1010 		tabprintf(tab, "iparent  0x%016jx\n",
1011 			  (uintmax_t)media.ipdata.meta.iparent);
1012 		tabprintf(tab, "name_key 0x%016jx\n",
1013 			  (uintmax_t)media.ipdata.meta.name_key);
1014 		tabprintf(tab, "name_len %u\n",
1015 			  media.ipdata.meta.name_len);
1016 		tabprintf(tab, "ncopies  %u\n",
1017 			  media.ipdata.meta.ncopies);
1018 		tabprintf(tab, "compalg  %u\n",
1019 			  media.ipdata.meta.comp_algo);
1020 		tabprintf(tab, "target_t %u\n",
1021 			  media.ipdata.meta.target_type);
1022 		tabprintf(tab, "checkalg %u\n",
1023 			  media.ipdata.meta.check_algo);
1024 		if ((media.ipdata.meta.op_flags & HAMMER2_OPFLAG_PFSROOT) ||
1025 		    media.ipdata.meta.pfs_type == HAMMER2_PFSTYPE_SUPROOT) {
1026 			tabprintf(tab, "pfs_nmas %u\n",
1027 				  media.ipdata.meta.pfs_nmasters);
1028 			tabprintf(tab, "pfs_type %u (%s)\n",
1029 				  media.ipdata.meta.pfs_type,
1030 				  hammer2_pfstype_to_str(media.ipdata.meta.pfs_type));
1031 			tabprintf(tab, "pfs_inum 0x%016jx\n",
1032 				  (uintmax_t)media.ipdata.meta.pfs_inum);
1033 			tabprintf(tab, "pfs_clid %s\n",
1034 				  hammer2_uuid_to_str(&media.ipdata.meta.pfs_clid,
1035 						      &str));
1036 			tabprintf(tab, "pfs_fsid %s\n",
1037 				  hammer2_uuid_to_str(&media.ipdata.meta.pfs_fsid,
1038 						      &str));
1039 			tabprintf(tab, "pfs_lsnap_tid 0x%016jx\n",
1040 				  (uintmax_t)media.ipdata.meta.pfs_lsnap_tid);
1041 		}
1042 		tabprintf(tab, "data_quota  %ju\n",
1043 			  (uintmax_t)media.ipdata.meta.data_quota);
1044 		tabprintf(tab, "data_count  %ju\n",
1045 			  (uintmax_t)bref->embed.stats.data_count);
1046 		tabprintf(tab, "inode_quota %ju\n",
1047 			  (uintmax_t)media.ipdata.meta.inode_quota);
1048 		tabprintf(tab, "inode_count %ju\n",
1049 			  (uintmax_t)bref->embed.stats.inode_count);
1050 		break;
1051 	case HAMMER2_BREF_TYPE_INDIRECT:
1052 		printf("{\n");
1053 		break;
1054 	case HAMMER2_BREF_TYPE_DATA:
1055 		printf("\n");
1056 		obrace = 0;
1057 		break;
1058 	case HAMMER2_BREF_TYPE_VOLUME:
1059 		printf("mirror_tid=%016jx freemap_tid=%016jx ",
1060 			media.voldata.mirror_tid,
1061 			media.voldata.freemap_tid);
1062 		printf("{\n");
1063 		break;
1064 	case HAMMER2_BREF_TYPE_FREEMAP:
1065 		printf("mirror_tid=%016jx freemap_tid=%016jx ",
1066 			media.voldata.mirror_tid,
1067 			media.voldata.freemap_tid);
1068 		printf("{\n");
1069 		break;
1070 	case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
1071 		printf("{\n");
1072 		tmp = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
1073 		tmp &= HAMMER2_SEGMASK;
1074 		tmp /= HAMMER2_PBUFSIZE;
1075 		assert(tmp >= HAMMER2_ZONE_FREEMAP_00);
1076 		assert(tmp < HAMMER2_ZONE_FREEMAP_END);
1077 		tmp -= HAMMER2_ZONE_FREEMAP_00;
1078 		tmp /= HAMMER2_ZONE_FREEMAP_INC;
1079 		tabprintf(tab, "rotation=%d\n", (int)tmp);
1080 
1081 		for (i = 0; i < HAMMER2_FREEMAP_COUNT; ++i) {
1082 			hammer2_off_t data_off = bref->key +
1083 				i * HAMMER2_FREEMAP_LEVEL0_SIZE;
1084 #if HAMMER2_BMAP_ELEMENTS != 8
1085 #error "cmd_debug.c: HAMMER2_BMAP_ELEMENTS expected to be 8"
1086 #endif
1087 			tabprintf(tab + 4, "%016jx %04d.%04x (avail=%7d) "
1088 				  "%016jx %016jx %016jx %016jx "
1089 				  "%016jx %016jx %016jx %016jx\n",
1090 				  data_off, i, media.bmdata[i].class,
1091 				  media.bmdata[i].avail,
1092 				  media.bmdata[i].bitmapq[0],
1093 				  media.bmdata[i].bitmapq[1],
1094 				  media.bmdata[i].bitmapq[2],
1095 				  media.bmdata[i].bitmapq[3],
1096 				  media.bmdata[i].bitmapq[4],
1097 				  media.bmdata[i].bitmapq[5],
1098 				  media.bmdata[i].bitmapq[6],
1099 				  media.bmdata[i].bitmapq[7]);
1100 		}
1101 		tabprintf(tab, "}\n");
1102 		break;
1103 	case HAMMER2_BREF_TYPE_FREEMAP_NODE:
1104 		printf("{\n");
1105 		tmp = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
1106 		tmp &= HAMMER2_SEGMASK;
1107 		tmp /= HAMMER2_PBUFSIZE;
1108 		assert(tmp >= HAMMER2_ZONE_FREEMAP_00);
1109 		assert(tmp < HAMMER2_ZONE_FREEMAP_END);
1110 		tmp -= HAMMER2_ZONE_FREEMAP_00;
1111 		tmp /= HAMMER2_ZONE_FREEMAP_INC;
1112 		tabprintf(tab, "rotation=%d\n", (int)tmp);
1113 		break;
1114 	default:
1115 		printf("\n");
1116 		obrace = 0;
1117 		break;
1118 	}
1119 	if (str)
1120 		free(str);
1121 
1122 skip_data:
1123 	/*
1124 	 * Update statistics.
1125 	 */
1126 	switch(bref->type) {
1127 	case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
1128 		for (i = 0; i < HAMMER2_FREEMAP_COUNT; ++i) {
1129 			hammer2_off_t data_off = bref->key +
1130 				i * HAMMER2_FREEMAP_LEVEL0_SIZE;
1131 			if (data_off >= voldata->aux_end &&
1132 			    data_off < hammer2_get_total_size()) {
1133 				int j;
1134 				for (j = 0; j < 4; ++j)
1135 					CountBlocks(&media.bmdata[i], j,
1136 						    &TotalAccum16[j],
1137 						    &TotalAccum64[j]);
1138 			} else
1139 				TotalUnavail += HAMMER2_FREEMAP_LEVEL0_SIZE;
1140 		}
1141 		TotalFreemap += HAMMER2_FREEMAP_LEVEL1_SIZE;
1142 		break;
1143 	default:
1144 		break;
1145 	}
1146 
1147 	/*
1148 	 * Recurse if norecurse == 0.  If the CRC failed, pass norecurse = 1.
1149 	 * That is, if an indirect or inode fails we still try to list its
1150 	 * direct children to help with debugging, but go no further than
1151 	 * that because they are probably garbage.
1152 	 */
1153 	if (show_depth == -1 || ((tab - init_tab) / show_tab) < show_depth) {
1154 		for (i = 0; norecurse == 0 && i < bcount; ++i) {
1155 			if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) {
1156 				show_bref(voldata, tab, i, &bscan[i],
1157 				    failed);
1158 			}
1159 		}
1160 	}
1161 	tab -= show_tab;
1162 	if (obrace) {
1163 		if (bref->type == HAMMER2_BREF_TYPE_INODE)
1164 			tabprintf(tab, "} (%s.%d, \"%*.*s\")\n",
1165 				  type_str, bi, namelen, namelen,
1166 				  media.ipdata.filename);
1167 		else
1168 			tabprintf(tab, "} (%s.%d)\n", type_str, bi);
1169 	}
1170 }
1171 
1172 static
1173 void
1174 CountBlocks(hammer2_bmap_data_t *bmap, int value,
1175 	    hammer2_off_t *accum16, hammer2_off_t *accum64)
1176 {
1177 	int i, j, bits;
1178 	hammer2_bitmap_t value16, value64;
1179 
1180 	bits = (int)sizeof(hammer2_bitmap_t) * 8;
1181 	assert(bits == 64);
1182 
1183 	value16 = value;
1184 	assert(value16 < 4);
1185 	value64 = (value16 << 6) | (value16 << 4) | (value16 << 2) | value16;
1186 	assert(value64 < 256);
1187 
1188 	for (i = 0; i < HAMMER2_BMAP_ELEMENTS; ++i) {
1189 		hammer2_bitmap_t bm = bmap->bitmapq[i];
1190 		hammer2_bitmap_t bm_save = bm;
1191 		hammer2_bitmap_t mask;
1192 
1193 		mask = 0x03; /* 2 bits per 16KB */
1194 		for (j = 0; j < bits; j += 2) {
1195 			if ((bm & mask) == value16)
1196 				*accum16 += 16384;
1197 			bm >>= 2;
1198 		}
1199 
1200 		bm = bm_save;
1201 		mask = 0xFF; /* 8 bits per 64KB chunk */
1202 		for (j = 0; j < bits; j += 8) {
1203 			if ((bm & mask) == value64)
1204 				*accum64 += 65536;
1205 			bm >>= 8;
1206 		}
1207 	}
1208 }
1209 
1210 int
1211 cmd_hash(int ac, const char **av)
1212 {
1213 	int i;
1214 
1215 	for (i = 0; i < ac; ++i) {
1216 		printf("%016jx %s\n",
1217 		       dirhash((const unsigned char*)av[i], strlen(av[i])),
1218 		       av[i]);
1219 	}
1220 	return(0);
1221 }
1222 
1223 int
1224 cmd_dhash(int ac, const char **av)
1225 {
1226 	char buf[1024];		/* 1K extended directory record */
1227 	uint64_t hash;
1228 	int i;
1229 
1230 	for (i = 0; i < ac; ++i) {
1231 		bzero(buf, sizeof(buf));
1232 		snprintf(buf, sizeof(buf), "%s", av[i]);
1233 		hash = XXH64(buf, sizeof(buf), XXH_HAMMER2_SEED);
1234 		printf("%016jx %s\n", hash, av[i]);
1235 	}
1236 	return(0);
1237 }
1238 
1239 int
1240 cmd_dumpchain(const char *path, u_int flags)
1241 {
1242 	int dummy = (int)flags;
1243 	int ecode = 0;
1244 	int fd;
1245 
1246 	fd = open(path, O_RDONLY);
1247 	if (fd >= 0) {
1248 		if (ioctl(fd, HAMMER2IOC_DEBUG_DUMP, &dummy) < 0) {
1249 			fprintf(stderr, "%s: %s\n", path, strerror(errno));
1250 			ecode = 1;
1251 		}
1252 		close(fd);
1253 	} else {
1254 		fprintf(stderr, "unable to open %s\n", path);
1255 		ecode = 1;
1256 	}
1257 	return ecode;
1258 }
1259 
1260 static
1261 void
1262 tabprintf(int tab, const char *ctl, ...)
1263 {
1264 	va_list va;
1265 
1266 	printf("%*.*s", tab, tab, "");
1267 	va_start(va, ctl);
1268 	vprintf(ctl, va);
1269 	va_end(va);
1270 }
1271