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