xref: /dflybsd-src/sbin/hammer/cmd_show.c (revision 03a6e5dbbde92f66033ecc870b16d286b432c52a)
1 /*
2  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include "hammer.h"
36 #include <libutil.h>
37 
38 #define FLAG_TOOFARLEFT		0x0001
39 #define FLAG_TOOFARRIGHT	0x0002
40 #define FLAG_BADTYPE		0x0004
41 #define FLAG_BADCHILDPARENT	0x0008
42 #define FLAG_BADMIRRORTID	0x0010
43 
44 typedef struct btree_search {
45 	u_int32_t	lo;
46 	int64_t		obj_id;
47 } *btree_search_t;
48 
49 static void print_btree_node(hammer_off_t node_offset, btree_search_t search,
50 			int depth, hammer_tid_t mirror_tid,
51 			hammer_base_elm_t left_bound,
52 			hammer_base_elm_t right_bound);
53 static const char *check_data_crc(hammer_btree_elm_t elm);
54 static void print_record(hammer_btree_elm_t elm);
55 static void print_btree_elm(hammer_btree_elm_t elm, int i, u_int8_t type,
56 			int flags, const char *label, const char *ext);
57 static int get_elm_flags(hammer_node_ondisk_t node, hammer_off_t node_offset,
58 			hammer_btree_elm_t elm, u_int8_t btype,
59 			hammer_base_elm_t left_bound,
60 			hammer_base_elm_t right_bound);
61 static void print_bigblock_fill(hammer_off_t offset);
62 
63 void
64 hammer_cmd_show(hammer_off_t node_offset, u_int32_t lo, int64_t obj_id,
65 		int depth,
66 		hammer_base_elm_t left_bound, hammer_base_elm_t right_bound)
67 {
68 	struct volume_info *volume;
69 	struct btree_search search;
70 	btree_search_t searchp;
71 	int zone;
72 
73 	AssertOnFailure = 0;
74 
75 	if (node_offset == (hammer_off_t)-1) {
76 		volume = get_volume(RootVolNo);
77 		node_offset = volume->ondisk->vol0_btree_root;
78 		if (QuietOpt < 3) {
79 			printf("Volume header\trecords=%jd next_tid=%016jx\n",
80 			       (intmax_t)volume->ondisk->vol0_stat_records,
81 			       (uintmax_t)volume->ondisk->vol0_next_tid);
82 			printf("\t\tbufoffset=%016jx\n",
83 			       (uintmax_t)volume->ondisk->vol_buf_beg);
84 			for (zone = 0; zone < HAMMER_MAX_ZONES; ++zone) {
85 				printf("\t\tzone %d\tnext_offset=%016jx\n",
86 					zone,
87 					(uintmax_t)volume->ondisk->vol0_blockmap[zone].next_offset
88 				);
89 			}
90 		}
91 		rel_volume(volume);
92 	}
93 
94 	if (lo == 0 && obj_id == (int64_t)HAMMER_MIN_OBJID) {
95 		searchp = NULL;
96 		printf("show %016jx depth %d\n", (uintmax_t)node_offset, depth);
97 	} else {
98 		search.lo = lo;
99 		search.obj_id = obj_id;
100 		searchp = &search;
101 		printf("show %016jx lo %08x obj_id %016jx depth %d\n",
102 			(uintmax_t)node_offset, lo, (uintmax_t)obj_id, depth);
103 	}
104 	print_btree_node(node_offset, searchp, depth, HAMMER_MAX_TID,
105 			 left_bound, right_bound);
106 
107 	AssertOnFailure = 1;
108 }
109 
110 static void
111 print_btree_node(hammer_off_t node_offset, btree_search_t search,
112 		int depth, hammer_tid_t mirror_tid,
113 		hammer_base_elm_t left_bound, hammer_base_elm_t right_bound)
114 {
115 	struct buffer_info *buffer = NULL;
116 	hammer_node_ondisk_t node;
117 	hammer_btree_elm_t elm;
118 	int i;
119 	int flags;
120 	int maxcount;
121 	char badc;
122 	char badm;
123 	const char *ext;
124 
125 	node = get_node(node_offset, &buffer);
126 
127 	if (node == NULL) {
128 		printf("BI   NODE %016jx (IO ERROR)\n",
129 		       (uintmax_t)node_offset);
130 		return;
131 	}
132 
133 	if (crc32(&node->crc + 1, HAMMER_BTREE_CRCSIZE) == node->crc)
134 		badc = ' ';
135 	else
136 		badc = 'B';
137 
138 	if (node->mirror_tid <= mirror_tid) {
139 		badm = ' ';
140 	} else {
141 		badm = 'M';
142 		badc = 'B';
143 	}
144 
145 	printf("%c%c   NODE %016jx cnt=%02d p=%016jx "
146 	       "type=%c depth=%d",
147 	       badc,
148 	       badm,
149 	       (uintmax_t)node_offset, node->count,
150 	       (uintmax_t)node->parent,
151 	       (node->type ? node->type : '?'), depth);
152 	printf(" mirror %016jx", (uintmax_t)node->mirror_tid);
153 	if (QuietOpt < 3) {
154 		printf(" fill=");
155 		print_bigblock_fill(node_offset);
156 	}
157 	printf(" {\n");
158 
159 	maxcount = (node->type == HAMMER_BTREE_TYPE_INTERNAL) ?
160 		   HAMMER_BTREE_INT_ELMS : HAMMER_BTREE_LEAF_ELMS;
161 
162 	for (i = 0; i < node->count && i < maxcount; ++i) {
163 		elm = &node->elms[i];
164 
165 		if (node->type != HAMMER_BTREE_TYPE_INTERNAL) {
166 			ext = NULL;
167 			if (search &&
168 			    elm->base.localization == search->lo &&
169 			     elm->base.obj_id == search->obj_id) {
170 				ext = " *";
171 			}
172 		} else if (search) {
173 			ext = " *";
174 			if (elm->base.localization > search->lo ||
175 			    (elm->base.localization == search->lo &&
176 			     elm->base.obj_id > search->obj_id)) {
177 				ext = NULL;
178 			}
179 			if (elm[1].base.localization < search->lo ||
180 			    (elm[1].base.localization == search->lo &&
181 			     elm[1].base.obj_id < search->obj_id)) {
182 				ext = NULL;
183 			}
184 		} else {
185 			ext = NULL;
186 		}
187 
188 		flags = get_elm_flags(node, node_offset,
189 					elm, elm->base.btype,
190 					left_bound, right_bound);
191 		print_btree_elm(elm, i, node->type, flags, "ELM", ext);
192 	}
193 	if (node->type == HAMMER_BTREE_TYPE_INTERNAL) {
194 		elm = &node->elms[i];
195 
196 		flags = get_elm_flags(node, node_offset,
197 					elm, 'I',
198 					left_bound, right_bound);
199 		print_btree_elm(elm, i, node->type, flags, "RBN", NULL);
200 	}
201 	printf("     }\n");
202 
203 	for (i = 0; i < node->count; ++i) {
204 		elm = &node->elms[i];
205 
206 		switch(node->type) {
207 		case HAMMER_BTREE_TYPE_INTERNAL:
208 			if (search) {
209 				if (elm->base.localization > search->lo ||
210 				    (elm->base.localization == search->lo &&
211 				     elm->base.obj_id > search->obj_id)) {
212 					break;
213 				}
214 				if (elm[1].base.localization < search->lo ||
215 				    (elm[1].base.localization == search->lo &&
216 				     elm[1].base.obj_id < search->obj_id)) {
217 					break;
218 				}
219 			}
220 			if (elm->internal.subtree_offset) {
221 				print_btree_node(elm->internal.subtree_offset,
222 						 search, depth + 1,
223 						 elm->internal.mirror_tid,
224 						 &elm[0].base, &elm[1].base);
225 				/*
226 				 * Cause show to iterate after seeking to
227 				 * the lo:objid
228 				 */
229 				search = NULL;
230 			}
231 			break;
232 		default:
233 			break;
234 		}
235 	}
236 	rel_buffer(buffer);
237 }
238 
239 static __inline
240 int
241 is_root_btree_beg(u_int8_t type, int i, hammer_btree_elm_t elm)
242 {
243 	return (type == HAMMER_BTREE_TYPE_INTERNAL &&
244 		i == 0 &&
245 		elm->base.localization == 0 &&
246 		elm->base.obj_id == (int64_t)-0x8000000000000000LL &&
247 		elm->base.key == (int64_t)-0x8000000000000000LL &&
248 		elm->base.create_tid == 1 &&
249 		elm->base.delete_tid == 1 &&
250 		elm->base.rec_type == 0 &&
251 		elm->base.obj_type == 0 &&
252 		elm->base.btype != 0);  /* depends on the original node */
253 }
254 
255 static __inline
256 int
257 is_root_btree_end(u_int8_t type, int i, hammer_btree_elm_t elm)
258 {
259 	return (type == HAMMER_BTREE_TYPE_INTERNAL &&
260 		i != 0 &&
261 		elm->base.localization == 0xFFFFFFFFU &&
262 		elm->base.obj_id == 0x7FFFFFFFFFFFFFFFLL &&
263 		elm->base.key == 0x7FFFFFFFFFFFFFFFLL &&
264 		elm->base.create_tid == 0xFFFFFFFFFFFFFFFFULL &&
265 		elm->base.delete_tid == 0 &&
266 		elm->base.rec_type == 0xFFFFU &&
267 		elm->base.obj_type == 0 &&
268 		elm->base.btype == 0);  /* not initialized */
269 }
270 
271 static
272 void
273 print_btree_elm(hammer_btree_elm_t elm, int i, u_int8_t type,
274 		int flags, const char *label, const char *ext)
275 {
276 	char flagstr[8] = { 0, '-', '-', '-', '-', '-', '-', 0 };
277 	char deleted;
278 	char rootelm;
279 
280 	flagstr[0] = flags ? 'B' : 'G';
281 	if (flags & FLAG_TOOFARLEFT)
282 		flagstr[2] = 'L';
283 	if (flags & FLAG_TOOFARRIGHT)
284 		flagstr[3] = 'R';
285 	if (flags & FLAG_BADTYPE)
286 		flagstr[4] = 'T';
287 	if (flags & FLAG_BADCHILDPARENT)
288 		flagstr[5] = 'C';
289 	if (flags & FLAG_BADMIRRORTID)
290 		flagstr[6] = 'M';
291 
292 	/*
293 	 * Check if elm is derived from root split
294 	 */
295 	if (is_root_btree_beg(type, i, elm))
296 		rootelm = '>';
297 	else if (is_root_btree_end(type, i, elm))
298 		rootelm = '<';
299 	else
300 		rootelm = ' ';
301 
302 	if (elm->base.delete_tid)
303 		deleted = 'd';
304 	else
305 		deleted = ' ';
306 
307 	printf("%s\t%s %2d %c ",
308 	       flagstr, label, i,
309 	       (elm->base.btype ? elm->base.btype : '?'));
310 	printf("lo=%08x obj=%016jx rt=%02x key=%016jx ot=%02x\n",
311 	       elm->base.localization,
312 	       (uintmax_t)elm->base.obj_id,
313 	       elm->base.rec_type,
314 	       (uintmax_t)elm->base.key,
315 	       elm->base.obj_type);
316 	printf("\t       %c tids %016jx:%016jx ",
317 	       (rootelm == ' ' ? deleted : rootelm),
318 	       (uintmax_t)elm->base.create_tid,
319 	       (uintmax_t)elm->base.delete_tid);
320 
321 	switch(type) {
322 	case HAMMER_BTREE_TYPE_INTERNAL:
323 		printf("suboff=%016jx",
324 		       (uintmax_t)elm->internal.subtree_offset);
325 		if (QuietOpt < 3) {
326 			printf(" mirror %016jx",
327 			       (uintmax_t)elm->internal.mirror_tid);
328 		}
329 		if (ext)
330 			printf(" %s", ext);
331 		break;
332 	case HAMMER_BTREE_TYPE_LEAF:
333 		if (ext)
334 			printf(" %s", ext);
335 		switch(elm->base.btype) {
336 		case HAMMER_BTREE_TYPE_RECORD:
337 			if (QuietOpt < 3)
338 				printf("\n%s\t         ", check_data_crc(elm));
339 			else
340 				printf("\n\t         ");
341 			printf("dataoff=%016jx/%d",
342 			       (uintmax_t)elm->leaf.data_offset,
343 			       elm->leaf.data_len);
344 			if (QuietOpt < 3) {
345 				printf(" crc=%04x", elm->leaf.data_crc);
346 				printf("\n\t         fills=");
347 				print_bigblock_fill(elm->leaf.data_offset);
348 			}
349 			if (QuietOpt < 2)
350 				print_record(elm);
351 			break;
352 		}
353 		break;
354 	default:
355 		break;
356 	}
357 	printf("\n");
358 }
359 
360 static
361 int
362 get_elm_flags(hammer_node_ondisk_t node, hammer_off_t node_offset,
363 		hammer_btree_elm_t elm, u_int8_t btype,
364 		hammer_base_elm_t left_bound, hammer_base_elm_t right_bound)
365 {
366 	int flags = 0;
367 
368 	switch(node->type) {
369 	case HAMMER_BTREE_TYPE_INTERNAL:
370 		if (elm->internal.subtree_offset) {
371 			struct buffer_info *buffer = NULL;
372 			hammer_node_ondisk_t subnode;
373 
374 			subnode = get_node(elm->internal.subtree_offset,
375 					   &buffer);
376 			if (subnode == NULL)
377 				flags |= FLAG_BADCHILDPARENT;
378 			else if (subnode->parent != node_offset)
379 				flags |= FLAG_BADCHILDPARENT;
380 			rel_buffer(buffer);
381 		}
382 		if (elm->internal.mirror_tid > node->mirror_tid)
383 			flags |= FLAG_BADMIRRORTID;
384 
385 		switch(btype) {
386 		case HAMMER_BTREE_TYPE_INTERNAL:
387 			if (left_bound == NULL || right_bound == NULL)
388 				break;
389 			if (hammer_btree_cmp(&elm->base, left_bound) < 0)
390 				flags |= FLAG_TOOFARLEFT;
391 			if (hammer_btree_cmp(&elm->base, right_bound) > 0)
392 				flags |= FLAG_TOOFARRIGHT;
393 			break;
394 		case HAMMER_BTREE_TYPE_LEAF:
395 			if (left_bound == NULL || right_bound == NULL)
396 				break;
397 			if (hammer_btree_cmp(&elm->base, left_bound) < 0)
398 				flags |= FLAG_TOOFARLEFT;
399 			if (hammer_btree_cmp(&elm->base, right_bound) >= 0)
400 				flags |= FLAG_TOOFARRIGHT;
401 			break;
402 		default:
403 			flags |= FLAG_BADTYPE;
404 			break;
405 		}
406 		break;
407 	case HAMMER_BTREE_TYPE_LEAF:
408 		if (node->mirror_tid == 0 &&
409 		    !(node->parent == 0 && node->count == 2)) {
410 			flags |= FLAG_BADMIRRORTID;
411 		}
412 		if (elm->base.create_tid && node->mirror_tid &&
413 		    elm->base.create_tid > node->mirror_tid) {
414 			flags |= FLAG_BADMIRRORTID;
415 		}
416 		if (elm->base.delete_tid && node->mirror_tid &&
417 		    elm->base.delete_tid > node->mirror_tid) {
418 			flags |= FLAG_BADMIRRORTID;
419 		}
420 		switch(btype) {
421 		case HAMMER_BTREE_TYPE_RECORD:
422 			if (left_bound == NULL || right_bound == NULL)
423 				break;
424 			if (hammer_btree_cmp(&elm->base, left_bound) < 0)
425 				flags |= FLAG_TOOFARLEFT;
426 			if (hammer_btree_cmp(&elm->base, right_bound) >= 0)
427 				flags |= FLAG_TOOFARRIGHT;
428 			break;
429 		default:
430 			flags |= FLAG_BADTYPE;
431 			break;
432 		}
433 		break;
434 	default:
435 		flags |= FLAG_BADTYPE;
436 		break;
437 	}
438 	return(flags);
439 }
440 
441 static
442 void
443 print_bigblock_fill(hammer_off_t offset)
444 {
445 	struct hammer_blockmap_layer1 layer1;
446 	struct hammer_blockmap_layer2 layer2;
447 	int fill;
448 	int error;
449 
450 	blockmap_lookup(offset, &layer1, &layer2, &error);
451 	if (error) {
452 		printf("z%d:%lld=BADZ",
453 			HAMMER_ZONE_DECODE(offset),
454 			(offset & ~HAMMER_OFF_ZONE_MASK) /
455 			    HAMMER_BIGBLOCK_SIZE
456 		);
457 	} else {
458 		fill = layer2.bytes_free * 100 / HAMMER_BIGBLOCK_SIZE;
459 		fill = 100 - fill;
460 
461 		printf("z%d:%lld=%d%%",
462 			HAMMER_ZONE_DECODE(offset),
463 			(offset & ~HAMMER_OFF_ZONE_MASK) /
464 			    HAMMER_BIGBLOCK_SIZE,
465 			fill
466 		);
467 	}
468 }
469 
470 /*
471  * Check the generic crc on a data element.  Inodes record types are
472  * special in that some of their fields are not CRCed.
473  *
474  * Also check that the zone is valid.
475  */
476 static
477 const char *
478 check_data_crc(hammer_btree_elm_t elm)
479 {
480 	struct buffer_info *data_buffer;
481 	hammer_off_t data_offset;
482 	int32_t data_len;
483 	int32_t len;
484 	u_int32_t crc;
485 	int error;
486 	char *ptr;
487 
488 	data_offset = elm->leaf.data_offset;
489 	data_len = elm->leaf.data_len;
490 	data_buffer = NULL;
491 	if (data_offset == 0 || data_len == 0)
492 		return("Z");
493 
494 	crc = 0;
495 	error = 0;
496 	while (data_len) {
497 		blockmap_lookup(data_offset, NULL, NULL, &error);
498 		if (error)
499 			break;
500 
501 		ptr = get_buffer_data(data_offset, &data_buffer, 0);
502 		len = HAMMER_BUFSIZE - ((int)data_offset & HAMMER_BUFMASK);
503 		if (len > data_len)
504 			len = (int)data_len;
505 		if (elm->leaf.base.rec_type == HAMMER_RECTYPE_INODE &&
506 		    data_len == sizeof(struct hammer_inode_data)) {
507 			crc = crc32_ext(ptr, HAMMER_INODE_CRCSIZE, crc);
508 		} else {
509 			crc = crc32_ext(ptr, len, crc);
510 		}
511 		data_len -= len;
512 		data_offset += len;
513 	}
514 	if (data_buffer)
515 		rel_buffer(data_buffer);
516 	if (error)
517 		return("BO");		/* bad offset */
518 	if (crc == elm->leaf.data_crc)
519 		return("");
520 	return("BX");			/* bad crc */
521 }
522 
523 static
524 void
525 print_config(char *cfgtxt)
526 {
527 	char *token;
528 
529 	printf("\n%17stext=\"\n", "");
530 	while((token = strsep(&cfgtxt, "\r\n")) != NULL) {
531 		printf("%17s  %s\n", "", token);
532 	}
533 	printf("%17s\"", "");
534 }
535 
536 static
537 void
538 print_record(hammer_btree_elm_t elm)
539 {
540 	struct buffer_info *data_buffer;
541 	hammer_off_t data_offset;
542 	int32_t data_len;
543 	hammer_data_ondisk_t data;
544 	u_int32_t status;
545 	char *str1 = NULL;
546 	char *str2 = NULL;
547 
548 	data_offset = elm->leaf.data_offset;
549 	data_len = elm->leaf.data_len;
550 	data_buffer = NULL;
551 
552 	if (data_offset)
553 		data = get_buffer_data(data_offset, &data_buffer, 0);
554 	else
555 		data = NULL;
556 
557 	switch(elm->leaf.base.rec_type) {
558 	case HAMMER_RECTYPE_UNKNOWN:
559 		printf("\n%17s", "");
560 		printf("unknown");
561 		break;
562 	case HAMMER_RECTYPE_INODE:
563 		printf("\n%17s", "");
564 		printf("size=%jd nlinks=%jd",
565 		       (intmax_t)data->inode.size,
566 		       (intmax_t)data->inode.nlinks);
567 		if (QuietOpt < 1) {
568 			printf(" mode=%05o uflags=%08x\n",
569 				data->inode.mode,
570 				data->inode.uflags);
571 			printf("%17s", "");
572 			printf("ctime=%016jx pobjid=%016jx obj_type=%d\n",
573 				(uintmax_t)data->inode.ctime,
574 				(uintmax_t)data->inode.parent_obj_id,
575 				data->inode.obj_type);
576 			printf("%17s", "");
577 			printf("mtime=%016jx", (uintmax_t)data->inode.mtime);
578 			printf(" caps=%02x", data->inode.cap_flags);
579 		}
580 		break;
581 	case HAMMER_RECTYPE_DIRENTRY:
582 		printf("\n%17s", "");
583 		data_len -= HAMMER_ENTRY_NAME_OFF;
584 		printf("dir-entry ino=%016jx lo=%08x name=\"%*.*s\"",
585 		       (uintmax_t)data->entry.obj_id,
586 		       data->entry.localization,
587 		       data_len, data_len, data->entry.name);
588 		break;
589 	case HAMMER_RECTYPE_FIX:
590 		switch(elm->leaf.base.key) {
591 		case HAMMER_FIXKEY_SYMLINK:
592 			data_len -= HAMMER_SYMLINK_NAME_OFF;
593 			printf("\n%17s", "");
594 			printf("symlink=\"%*.*s\"", data_len, data_len,
595 				data->symlink.name);
596 			break;
597 		default:
598 			break;
599 		}
600 		break;
601 	case HAMMER_RECTYPE_PFS:
602 		printf("\n%17s", "");
603 		printf("sync_beg_tid=%016jx sync_end_tid=%016jx\n",
604 			(intmax_t)data->pfsd.sync_beg_tid,
605 			(intmax_t)data->pfsd.sync_end_tid);
606 		uuid_to_string(&data->pfsd.shared_uuid, &str1, &status);
607 		uuid_to_string(&data->pfsd.unique_uuid, &str2, &status);
608 		printf("%17s", "");
609 		printf("shared_uuid=%s\n", str1);
610 		printf("%17s", "");
611 		printf("unique_uuid=%s\n", str2);
612 		printf("%17s", "");
613 		printf("mirror_flags=%08x label=\"%s\"",
614 			data->pfsd.mirror_flags, data->pfsd.label);
615 		if (data->pfsd.snapshots[0])
616 			printf(" snapshots=\"%s\"", data->pfsd.snapshots);
617 		free(str1);
618 		free(str2);
619 		break;
620 	case HAMMER_RECTYPE_SNAPSHOT:
621 		printf("\n%17s", "");
622 		printf("tid=%016jx label=\"%s\"",
623 			(intmax_t)data->snap.tid, data->snap.label);
624 		break;
625 	case HAMMER_RECTYPE_CONFIG:
626 		if (VerboseOpt > 2) {
627 			print_config(data->config.text);
628 		}
629 		break;
630 	case HAMMER_RECTYPE_DATA:
631 		if (VerboseOpt > 3) {
632 			printf("\n");
633 			hexdump(data, data_len, "\t\t  ", 0);
634 		}
635 		break;
636 	case HAMMER_RECTYPE_EXT:
637 	case HAMMER_RECTYPE_DB:
638 		if (VerboseOpt > 2) {
639 			printf("\n");
640 			hexdump(data, data_len, "\t\t  ", 0);
641 		}
642 		break;
643 	default:
644 		break;
645 	}
646 	if (data_buffer)
647 		rel_buffer(data_buffer);
648 }
649 
650 /*
651  * Dump the UNDO FIFO
652  */
653 void
654 hammer_cmd_show_undo(void)
655 {
656 	struct volume_info *volume;
657 	hammer_blockmap_t rootmap;
658 	hammer_off_t scan_offset;
659 	hammer_fifo_any_t head;
660 	struct buffer_info *data_buffer = NULL;
661 
662 	volume = get_volume(RootVolNo);
663 	rootmap = &volume->ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX];
664 	printf("Volume header UNDO %016jx-%016jx/%016jx\n",
665 		(intmax_t)rootmap->first_offset,
666 		(intmax_t)rootmap->next_offset,
667 		(intmax_t)rootmap->alloc_offset);
668 	printf("Undo map is %jdMB\n",
669 		(intmax_t)((rootmap->alloc_offset & HAMMER_OFF_LONG_MASK) /
670 			   (1024 * 1024)));
671 	scan_offset = HAMMER_ZONE_ENCODE(HAMMER_ZONE_UNDO_INDEX, 0);
672 	while (scan_offset < rootmap->alloc_offset) {
673 		head = get_buffer_data(scan_offset, &data_buffer, 0);
674 		printf("%016jx ", scan_offset);
675 
676 		switch(head->head.hdr_type) {
677 		case HAMMER_HEAD_TYPE_PAD:
678 			printf("PAD(%04x)\n", head->head.hdr_size);
679 			break;
680 		case HAMMER_HEAD_TYPE_DUMMY:
681 			printf("DUMMY(%04x) seq=%08x\n",
682 				head->head.hdr_size, head->head.hdr_seq);
683 			break;
684 		case HAMMER_HEAD_TYPE_UNDO:
685 			printf("UNDO(%04x) seq=%08x "
686 			       "dataoff=%016jx bytes=%d\n",
687 				head->head.hdr_size, head->head.hdr_seq,
688 				(intmax_t)head->undo.undo_offset,
689 				head->undo.undo_data_bytes);
690 			break;
691 		case HAMMER_HEAD_TYPE_REDO:
692 			printf("REDO(%04x) seq=%08x flags=%08x "
693 			       "objid=%016jx logoff=%016jx bytes=%d\n",
694 				head->head.hdr_size, head->head.hdr_seq,
695 				head->redo.redo_flags,
696 				(intmax_t)head->redo.redo_objid,
697 				(intmax_t)head->redo.redo_offset,
698 				head->redo.redo_data_bytes);
699 			break;
700 		default:
701 			printf("UNKNOWN(%04x,%04x) seq=%08x\n",
702 				head->head.hdr_type,
703 				head->head.hdr_size,
704 				head->head.hdr_seq);
705 			break;
706 		}
707 		if ((head->head.hdr_size & HAMMER_HEAD_ALIGN_MASK) ||
708 		    head->head.hdr_size == 0 ||
709 		    head->head.hdr_size > HAMMER_UNDO_ALIGN -
710 				    ((u_int)scan_offset & HAMMER_UNDO_MASK)) {
711 			printf("Illegal size field, skipping to "
712 			       "next boundary\n");
713 			scan_offset = (scan_offset + HAMMER_UNDO_MASK) &
714 					~HAMMER_UNDO_MASK64;
715 		} else {
716 			scan_offset += head->head.hdr_size;
717 		}
718 	}
719 	if (data_buffer)
720 		rel_buffer(data_buffer);
721 }
722