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