xref: /dragonfly/sbin/hammer/cmd_show.c (revision c93b565c)
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 	u_int16_t	rec_type;
48 	int64_t		key;
49 	hammer_tid_t	create_tid;
50 	int		limit;   /* # of fields to test */
51 	int		filter;  /* filter type (default -1) */
52 } *btree_search_t;
53 
54 static void print_btree_node(hammer_off_t node_offset, btree_search_t search,
55 			int depth, hammer_tid_t mirror_tid,
56 			hammer_base_elm_t left_bound,
57 			hammer_base_elm_t right_bound);
58 static const char *check_data_crc(hammer_btree_elm_t elm);
59 static void print_record(hammer_btree_elm_t elm);
60 static void print_btree_elm(hammer_btree_elm_t elm, int i, u_int8_t type,
61 			int flags, const char *label, const char *ext);
62 static int get_elm_flags(hammer_node_ondisk_t node, hammer_off_t node_offset,
63 			hammer_btree_elm_t elm, u_int8_t btype,
64 			hammer_base_elm_t left_bound,
65 			hammer_base_elm_t right_bound);
66 static void print_bigblock_fill(hammer_off_t offset);
67 static int init_btree_search(const char *arg, int filter,
68 			btree_search_t search);
69 static int test_btree_search(hammer_btree_elm_t elm, btree_search_t search);
70 
71 void
72 hammer_cmd_show(hammer_off_t node_offset, const char *arg,
73 		int filter, int depth,
74 		hammer_base_elm_t left_bound, hammer_base_elm_t right_bound)
75 {
76 	struct volume_info *volume;
77 	struct btree_search search;
78 	btree_search_t searchp = NULL;
79 	int zone;
80 
81 	AssertOnFailure = 0;
82 
83 	if (node_offset == (hammer_off_t)-1) {
84 		volume = get_volume(RootVolNo);
85 		node_offset = volume->ondisk->vol0_btree_root;
86 		if (QuietOpt < 3) {
87 			printf("Volume header\trecords=%jd next_tid=%016jx\n",
88 			       (intmax_t)volume->ondisk->vol0_stat_records,
89 			       (uintmax_t)volume->ondisk->vol0_next_tid);
90 			printf("\t\tbufoffset=%016jx\n",
91 			       (uintmax_t)volume->ondisk->vol_buf_beg);
92 			for (zone = 0; zone < HAMMER_MAX_ZONES; ++zone) {
93 				printf("\t\tzone %d\tnext_offset=%016jx\n",
94 					zone,
95 					(uintmax_t)volume->ondisk->vol0_blockmap[zone].next_offset
96 				);
97 			}
98 		}
99 		rel_volume(volume);
100 	}
101 
102 	printf("show %016jx", (uintmax_t)node_offset);
103 	if (arg) {
104 		assert(init_btree_search(arg, filter, &search) != -1);
105 		if (search.limit >= 1)
106 			printf(" lo %08x obj_id %016jx",
107 				search.lo, (uintmax_t)search.obj_id);
108 		if (search.limit >= 3)
109 			printf(" rec_type %02x", search.rec_type);
110 		if (search.limit >= 4)
111 			printf(" key %016jx", (uintmax_t)search.key);
112 		if (search.limit == 5)
113 			printf(" create_tid %016jx\n",
114 				(uintmax_t)search.create_tid);
115 		searchp = &search;
116 	}
117 	printf(" depth %d\n", depth);
118 	print_btree_node(node_offset, searchp, depth, HAMMER_MAX_TID,
119 			 left_bound, right_bound);
120 
121 	AssertOnFailure = 1;
122 }
123 
124 static void
125 print_btree_node(hammer_off_t node_offset, btree_search_t search,
126 		int depth, hammer_tid_t mirror_tid,
127 		hammer_base_elm_t left_bound, hammer_base_elm_t right_bound)
128 {
129 	struct buffer_info *buffer = NULL;
130 	hammer_node_ondisk_t node;
131 	hammer_btree_elm_t elm;
132 	int i;
133 	int flags;
134 	int maxcount;
135 	char badc;
136 	char badm;
137 	const char *ext;
138 
139 	node = get_node(node_offset, &buffer);
140 
141 	if (node == NULL) {
142 		printf("BI   NODE %016jx (IO ERROR)\n",
143 		       (uintmax_t)node_offset);
144 		return;
145 	}
146 
147 	if (crc32(&node->crc + 1, HAMMER_BTREE_CRCSIZE) == node->crc)
148 		badc = ' ';
149 	else
150 		badc = 'B';
151 
152 	if (node->mirror_tid <= mirror_tid) {
153 		badm = ' ';
154 	} else {
155 		badm = 'M';
156 		badc = 'B';
157 	}
158 
159 	printf("%c%c   NODE %016jx cnt=%02d p=%016jx "
160 	       "type=%c depth=%d",
161 	       badc,
162 	       badm,
163 	       (uintmax_t)node_offset, node->count,
164 	       (uintmax_t)node->parent,
165 	       (node->type ? node->type : '?'), depth);
166 	printf(" mirror %016jx", (uintmax_t)node->mirror_tid);
167 	if (QuietOpt < 3) {
168 		printf(" fill=");
169 		print_bigblock_fill(node_offset);
170 	}
171 	printf(" {\n");
172 
173 	maxcount = (node->type == HAMMER_BTREE_TYPE_INTERNAL) ?
174 		   HAMMER_BTREE_INT_ELMS : HAMMER_BTREE_LEAF_ELMS;
175 
176 	for (i = 0; i < node->count && i < maxcount; ++i) {
177 		elm = &node->elms[i];
178 
179 		if (node->type != HAMMER_BTREE_TYPE_INTERNAL) {
180 			ext = NULL;
181 			if (search && test_btree_search(elm, search) == 0)
182 				ext = " *";
183 		} else if (search) {
184 			ext = " *";
185 			if (test_btree_search(elm, search) > 0 ||
186 			    test_btree_search(elm + 1, search) < 0)
187 				ext = NULL;
188 		} else {
189 			ext = NULL;
190 		}
191 
192 		flags = get_elm_flags(node, node_offset,
193 					elm, elm->base.btype,
194 					left_bound, right_bound);
195 		print_btree_elm(elm, i, node->type, flags, "ELM", ext);
196 	}
197 	if (node->type == HAMMER_BTREE_TYPE_INTERNAL) {
198 		elm = &node->elms[i];
199 
200 		flags = get_elm_flags(node, node_offset,
201 					elm, 'I',
202 					left_bound, right_bound);
203 		print_btree_elm(elm, i, node->type, flags, "RBN", NULL);
204 	}
205 	printf("     }\n");
206 
207 	for (i = 0; i < node->count; ++i) {
208 		elm = &node->elms[i];
209 
210 		switch(node->type) {
211 		case HAMMER_BTREE_TYPE_INTERNAL:
212 			if (search && search->filter) {
213 				if (test_btree_search(elm, search) > 0 ||
214 				    test_btree_search(elm + 1, search) < 0)
215 					break;
216 			}
217 			if (elm->internal.subtree_offset) {
218 				print_btree_node(elm->internal.subtree_offset,
219 						 search, depth + 1,
220 						 elm->internal.mirror_tid,
221 						 &elm[0].base, &elm[1].base);
222 				/*
223 				 * Cause show to do normal iteration after
224 				 * seeking to the lo:objid:rectype:key:tid
225 				 * by default
226 				 */
227 				if (search && search->filter == -1)  /* default */
228 					search->filter = 0;
229 			}
230 			break;
231 		default:
232 			break;
233 		}
234 	}
235 	rel_buffer(buffer);
236 }
237 
238 static __inline
239 int
240 is_root_btree_beg(u_int8_t type, int i, hammer_btree_elm_t elm)
241 {
242 	return (type == HAMMER_BTREE_TYPE_INTERNAL &&
243 		i == 0 &&
244 		elm->base.localization == 0 &&
245 		elm->base.obj_id == (int64_t)-0x8000000000000000LL &&
246 		elm->base.key == (int64_t)-0x8000000000000000LL &&
247 		elm->base.create_tid == 1 &&
248 		elm->base.delete_tid == 1 &&
249 		elm->base.rec_type == 0 &&
250 		elm->base.obj_type == 0 &&
251 		elm->base.btype != 0);  /* depends on the original node */
252 }
253 
254 static __inline
255 int
256 is_root_btree_end(u_int8_t type, int i, hammer_btree_elm_t elm)
257 {
258 	return (type == HAMMER_BTREE_TYPE_INTERNAL &&
259 		i != 0 &&
260 		elm->base.localization == 0xFFFFFFFFU &&
261 		elm->base.obj_id == 0x7FFFFFFFFFFFFFFFLL &&
262 		elm->base.key == 0x7FFFFFFFFFFFFFFFLL &&
263 		elm->base.create_tid == 0xFFFFFFFFFFFFFFFFULL &&
264 		elm->base.delete_tid == 0 &&
265 		elm->base.rec_type == 0xFFFFU &&
266 		elm->base.obj_type == 0 &&
267 		elm->base.btype == 0);  /* not initialized */
268 }
269 
270 static
271 void
272 print_btree_elm(hammer_btree_elm_t elm, int i, u_int8_t type,
273 		int flags, const char *label, const char *ext)
274 {
275 	char flagstr[8] = { 0, '-', '-', '-', '-', '-', '-', 0 };
276 	char deleted;
277 	char rootelm;
278 
279 	flagstr[0] = flags ? 'B' : 'G';
280 	if (flags & FLAG_TOOFARLEFT)
281 		flagstr[2] = 'L';
282 	if (flags & FLAG_TOOFARRIGHT)
283 		flagstr[3] = 'R';
284 	if (flags & FLAG_BADTYPE)
285 		flagstr[4] = 'T';
286 	if (flags & FLAG_BADCHILDPARENT)
287 		flagstr[5] = 'C';
288 	if (flags & FLAG_BADMIRRORTID)
289 		flagstr[6] = 'M';
290 
291 	/*
292 	 * Check if elm is derived from root split
293 	 */
294 	if (is_root_btree_beg(type, i, elm))
295 		rootelm = '>';
296 	else if (is_root_btree_end(type, i, elm))
297 		rootelm = '<';
298 	else
299 		rootelm = ' ';
300 
301 	if (elm->base.delete_tid)
302 		deleted = 'd';
303 	else
304 		deleted = ' ';
305 
306 	printf("%s\t%s %2d %c ",
307 	       flagstr, label, i,
308 	       (elm->base.btype ? elm->base.btype : '?'));
309 	printf("lo=%08x obj=%016jx rt=%02x key=%016jx ot=%02x\n",
310 	       elm->base.localization,
311 	       (uintmax_t)elm->base.obj_id,
312 	       elm->base.rec_type,
313 	       (uintmax_t)elm->base.key,
314 	       elm->base.obj_type);
315 	printf("\t       %c tids %016jx:%016jx ",
316 	       (rootelm == ' ' ? deleted : rootelm),
317 	       (uintmax_t)elm->base.create_tid,
318 	       (uintmax_t)elm->base.delete_tid);
319 
320 	switch(type) {
321 	case HAMMER_BTREE_TYPE_INTERNAL:
322 		printf("suboff=%016jx",
323 		       (uintmax_t)elm->internal.subtree_offset);
324 		if (QuietOpt < 3) {
325 			printf(" mirror %016jx",
326 			       (uintmax_t)elm->internal.mirror_tid);
327 		}
328 		if (ext)
329 			printf(" %s", ext);
330 		break;
331 	case HAMMER_BTREE_TYPE_LEAF:
332 		if (ext)
333 			printf(" %s", ext);
334 		switch(elm->base.btype) {
335 		case HAMMER_BTREE_TYPE_RECORD:
336 			if (QuietOpt < 3)
337 				printf("\n%s\t         ", check_data_crc(elm));
338 			else
339 				printf("\n\t         ");
340 			printf("dataoff=%016jx/%d",
341 			       (uintmax_t)elm->leaf.data_offset,
342 			       elm->leaf.data_len);
343 			if (QuietOpt < 3) {
344 				printf(" crc=%04x", elm->leaf.data_crc);
345 				printf("\n\t         fills=");
346 				print_bigblock_fill(elm->leaf.data_offset);
347 			}
348 			if (QuietOpt < 2)
349 				print_record(elm);
350 			break;
351 		}
352 		break;
353 	default:
354 		break;
355 	}
356 	printf("\n");
357 }
358 
359 static
360 int
361 get_elm_flags(hammer_node_ondisk_t node, hammer_off_t node_offset,
362 		hammer_btree_elm_t elm, u_int8_t btype,
363 		hammer_base_elm_t left_bound, hammer_base_elm_t right_bound)
364 {
365 	int flags = 0;
366 
367 	switch(node->type) {
368 	case HAMMER_BTREE_TYPE_INTERNAL:
369 		if (elm->internal.subtree_offset) {
370 			struct buffer_info *buffer = NULL;
371 			hammer_node_ondisk_t subnode;
372 
373 			subnode = get_node(elm->internal.subtree_offset,
374 					   &buffer);
375 			if (subnode == NULL)
376 				flags |= FLAG_BADCHILDPARENT;
377 			else if (subnode->parent != node_offset)
378 				flags |= FLAG_BADCHILDPARENT;
379 			rel_buffer(buffer);
380 		}
381 		if (elm->internal.mirror_tid > node->mirror_tid)
382 			flags |= FLAG_BADMIRRORTID;
383 
384 		switch(btype) {
385 		case HAMMER_BTREE_TYPE_INTERNAL:
386 			if (left_bound == NULL || right_bound == NULL)
387 				break;
388 			if (hammer_btree_cmp(&elm->base, left_bound) < 0)
389 				flags |= FLAG_TOOFARLEFT;
390 			if (hammer_btree_cmp(&elm->base, right_bound) > 0)
391 				flags |= FLAG_TOOFARRIGHT;
392 			break;
393 		case HAMMER_BTREE_TYPE_LEAF:
394 			if (left_bound == NULL || right_bound == NULL)
395 				break;
396 			if (hammer_btree_cmp(&elm->base, left_bound) < 0)
397 				flags |= FLAG_TOOFARLEFT;
398 			if (hammer_btree_cmp(&elm->base, right_bound) >= 0)
399 				flags |= FLAG_TOOFARRIGHT;
400 			break;
401 		default:
402 			flags |= FLAG_BADTYPE;
403 			break;
404 		}
405 		break;
406 	case HAMMER_BTREE_TYPE_LEAF:
407 		if (node->mirror_tid == 0 &&
408 		    !(node->parent == 0 && node->count == 2)) {
409 			flags |= FLAG_BADMIRRORTID;
410 		}
411 		if (elm->base.create_tid && node->mirror_tid &&
412 		    elm->base.create_tid > node->mirror_tid) {
413 			flags |= FLAG_BADMIRRORTID;
414 		}
415 		if (elm->base.delete_tid && node->mirror_tid &&
416 		    elm->base.delete_tid > node->mirror_tid) {
417 			flags |= FLAG_BADMIRRORTID;
418 		}
419 		switch(btype) {
420 		case HAMMER_BTREE_TYPE_RECORD:
421 			if (left_bound == NULL || right_bound == NULL)
422 				break;
423 			if (hammer_btree_cmp(&elm->base, left_bound) < 0)
424 				flags |= FLAG_TOOFARLEFT;
425 			if (hammer_btree_cmp(&elm->base, right_bound) >= 0)
426 				flags |= FLAG_TOOFARRIGHT;
427 			break;
428 		default:
429 			flags |= FLAG_BADTYPE;
430 			break;
431 		}
432 		break;
433 	default:
434 		flags |= FLAG_BADTYPE;
435 		break;
436 	}
437 	return(flags);
438 }
439 
440 static
441 void
442 print_bigblock_fill(hammer_off_t offset)
443 {
444 	struct hammer_blockmap_layer1 layer1;
445 	struct hammer_blockmap_layer2 layer2;
446 	int fill;
447 	int error;
448 
449 	blockmap_lookup(offset, &layer1, &layer2, &error);
450 	if (error) {
451 		printf("z%d:%lld=BADZ",
452 			HAMMER_ZONE_DECODE(offset),
453 			(offset & ~HAMMER_OFF_ZONE_MASK) /
454 			    HAMMER_BIGBLOCK_SIZE
455 		);
456 	} else {
457 		fill = layer2.bytes_free * 100 / HAMMER_BIGBLOCK_SIZE;
458 		fill = 100 - fill;
459 
460 		printf("z%d:%lld=%d%%",
461 			HAMMER_ZONE_DECODE(offset),
462 			(offset & ~HAMMER_OFF_ZONE_MASK) /
463 			    HAMMER_BIGBLOCK_SIZE,
464 			fill
465 		);
466 	}
467 }
468 
469 /*
470  * Check the generic crc on a data element.  Inodes record types are
471  * special in that some of their fields are not CRCed.
472  *
473  * Also check that the zone is valid.
474  */
475 static
476 const char *
477 check_data_crc(hammer_btree_elm_t elm)
478 {
479 	struct buffer_info *data_buffer;
480 	hammer_off_t data_offset;
481 	int32_t data_len;
482 	int32_t len;
483 	u_int32_t crc;
484 	int error;
485 	char *ptr;
486 
487 	data_offset = elm->leaf.data_offset;
488 	data_len = elm->leaf.data_len;
489 	data_buffer = NULL;
490 	if (data_offset == 0 || data_len == 0)
491 		return("Z");
492 
493 	crc = 0;
494 	error = 0;
495 	while (data_len) {
496 		blockmap_lookup(data_offset, NULL, NULL, &error);
497 		if (error)
498 			break;
499 
500 		ptr = get_buffer_data(data_offset, &data_buffer, 0);
501 		len = HAMMER_BUFSIZE - ((int)data_offset & HAMMER_BUFMASK);
502 		if (len > data_len)
503 			len = (int)data_len;
504 		if (elm->leaf.base.rec_type == HAMMER_RECTYPE_INODE &&
505 		    data_len == sizeof(struct hammer_inode_data)) {
506 			crc = crc32_ext(ptr, HAMMER_INODE_CRCSIZE, crc);
507 		} else {
508 			crc = crc32_ext(ptr, len, crc);
509 		}
510 		data_len -= len;
511 		data_offset += len;
512 	}
513 	rel_buffer(data_buffer);
514 	if (error) {
515 		switch (error) {	/* bad offset */
516 		case -1:
517 			return("BO-ZL");
518 		case -2:
519 			return("BO-ZG");
520 		case -3:
521 			return("BO-RV");
522 		case -4:
523 			return("BO-AO");
524 		case -5:
525 			return("BO-DE");
526 		case -6:
527 			return("BO-L1");
528 		case -7:
529 			return("BO-LU");
530 		case -8:
531 			return("BO-L2");
532 		case -9:
533 			return("BO-LZ");
534 		default:
535 			return("BO-??");
536 		}
537 	}
538 	if (crc == elm->leaf.data_crc)
539 		return("");
540 	return("BX");			/* bad crc */
541 }
542 
543 static
544 void
545 print_config(char *cfgtxt)
546 {
547 	char *token;
548 
549 	printf("\n%17stext=\"\n", "");
550 	while((token = strsep(&cfgtxt, "\r\n")) != NULL) {
551 		printf("%17s  %s\n", "", token);
552 	}
553 	printf("%17s\"", "");
554 }
555 
556 static
557 void
558 print_record(hammer_btree_elm_t elm)
559 {
560 	struct buffer_info *data_buffer;
561 	hammer_off_t data_offset;
562 	int32_t data_len;
563 	hammer_data_ondisk_t data;
564 	u_int32_t status;
565 	char *str1 = NULL;
566 	char *str2 = NULL;
567 
568 	data_offset = elm->leaf.data_offset;
569 	data_len = elm->leaf.data_len;
570 	data_buffer = NULL;
571 
572 	if (data_offset)
573 		data = get_buffer_data(data_offset, &data_buffer, 0);
574 	else
575 		data = NULL;
576 
577 	switch(elm->leaf.base.rec_type) {
578 	case HAMMER_RECTYPE_UNKNOWN:
579 		printf("\n%17s", "");
580 		printf("unknown");
581 		break;
582 	case HAMMER_RECTYPE_INODE:
583 		printf("\n%17s", "");
584 		printf("size=%jd nlinks=%jd",
585 		       (intmax_t)data->inode.size,
586 		       (intmax_t)data->inode.nlinks);
587 		if (QuietOpt < 1) {
588 			printf(" mode=%05o uflags=%08x\n",
589 				data->inode.mode,
590 				data->inode.uflags);
591 			printf("%17s", "");
592 			printf("ctime=%016jx pobjid=%016jx obj_type=%d\n",
593 				(uintmax_t)data->inode.ctime,
594 				(uintmax_t)data->inode.parent_obj_id,
595 				data->inode.obj_type);
596 			printf("%17s", "");
597 			printf("mtime=%016jx", (uintmax_t)data->inode.mtime);
598 			printf(" caps=%02x", data->inode.cap_flags);
599 		}
600 		break;
601 	case HAMMER_RECTYPE_DIRENTRY:
602 		printf("\n%17s", "");
603 		data_len -= HAMMER_ENTRY_NAME_OFF;
604 		printf("dir-entry ino=%016jx lo=%08x name=\"%*.*s\"",
605 		       (uintmax_t)data->entry.obj_id,
606 		       data->entry.localization,
607 		       data_len, data_len, data->entry.name);
608 		break;
609 	case HAMMER_RECTYPE_FIX:
610 		switch(elm->leaf.base.key) {
611 		case HAMMER_FIXKEY_SYMLINK:
612 			data_len -= HAMMER_SYMLINK_NAME_OFF;
613 			printf("\n%17s", "");
614 			printf("symlink=\"%*.*s\"", data_len, data_len,
615 				data->symlink.name);
616 			break;
617 		default:
618 			break;
619 		}
620 		break;
621 	case HAMMER_RECTYPE_PFS:
622 		printf("\n%17s", "");
623 		printf("sync_beg_tid=%016jx sync_end_tid=%016jx\n",
624 			(intmax_t)data->pfsd.sync_beg_tid,
625 			(intmax_t)data->pfsd.sync_end_tid);
626 		uuid_to_string(&data->pfsd.shared_uuid, &str1, &status);
627 		uuid_to_string(&data->pfsd.unique_uuid, &str2, &status);
628 		printf("%17s", "");
629 		printf("shared_uuid=%s\n", str1);
630 		printf("%17s", "");
631 		printf("unique_uuid=%s\n", str2);
632 		printf("%17s", "");
633 		printf("mirror_flags=%08x label=\"%s\"",
634 			data->pfsd.mirror_flags, data->pfsd.label);
635 		if (data->pfsd.snapshots[0])
636 			printf(" snapshots=\"%s\"", data->pfsd.snapshots);
637 		free(str1);
638 		free(str2);
639 		break;
640 	case HAMMER_RECTYPE_SNAPSHOT:
641 		printf("\n%17s", "");
642 		printf("tid=%016jx label=\"%s\"",
643 			(intmax_t)data->snap.tid, data->snap.label);
644 		break;
645 	case HAMMER_RECTYPE_CONFIG:
646 		if (VerboseOpt > 2) {
647 			print_config(data->config.text);
648 		}
649 		break;
650 	case HAMMER_RECTYPE_DATA:
651 		if (VerboseOpt > 3) {
652 			printf("\n");
653 			hexdump(data, data_len, "\t\t  ", 0);
654 		}
655 		break;
656 	case HAMMER_RECTYPE_EXT:
657 	case HAMMER_RECTYPE_DB:
658 		if (VerboseOpt > 2) {
659 			printf("\n");
660 			hexdump(data, data_len, "\t\t  ", 0);
661 		}
662 		break;
663 	default:
664 		break;
665 	}
666 	rel_buffer(data_buffer);
667 }
668 
669 static __inline
670 unsigned long
671 _strtoul(const char *p, int base)
672 {
673 	unsigned long retval;
674 
675 	errno = 0;  /* clear */
676 	retval = strtoul(p, NULL, base);
677 	if (errno == ERANGE && retval == ULONG_MAX)
678 		err(1, "strtoul");
679 	return retval;
680 }
681 
682 static __inline
683 unsigned long long
684 _strtoull(const char *p, int base)
685 {
686 	unsigned long long retval;
687 
688 	errno = 0;  /* clear */
689 	retval = strtoull(p, NULL, base);
690 	if (errno == ERANGE && retval == ULLONG_MAX)
691 		err(1, "strtoull");
692 	return retval;
693 }
694 
695 static int
696 init_btree_search(const char *arg, int filter, btree_search_t search)
697 {
698 	char *s, *p;
699 	int i = 0;
700 
701 	search->lo = 0;
702 	search->obj_id = (int64_t)HAMMER_MIN_OBJID;
703 	search->rec_type = HAMMER_RECTYPE_LOWEST;
704 	search->key = 0;
705 	search->create_tid = 0;
706 	search->limit = 0;
707 	search->filter = filter;
708 
709 	s = strdup(arg);
710 	if (s == NULL)
711 		return(-1);
712 
713 	while ((p = s) != NULL) {
714 		if ((s = strchr(s, ':')) != NULL)
715 			*s++ = 0;
716 		if (++i == 1) {
717 			search->lo = _strtoul(p, 16);
718 		} else if (i == 2) {
719 			search->obj_id = _strtoull(p, 16);
720 		} else if (i == 3) {
721 			search->rec_type = _strtoul(p, 16);
722 		} else if (i == 4) {
723 			search->key = _strtoull(p, 16);
724 		} else if (i == 5) {
725 			search->create_tid = _strtoull(p, 16);
726 			break;
727 		}
728 	}
729 	search->limit = i;
730 	free(s);
731 	return(i);
732 }
733 
734 static int
735 test_btree_search(hammer_btree_elm_t elm, btree_search_t search)
736 {
737 	hammer_base_elm_t base = &elm->base;
738 	assert(search);
739 
740 	if (base->localization < search->lo)
741 		return(-1);
742 	if (base->localization > search->lo)
743 		return(1);
744 	/* fall through */
745 
746 	if (base->obj_id < search->obj_id)
747 		return(-2);
748 	if (base->obj_id > search->obj_id)
749 		return(2);
750 	if (search->limit == 2)
751 		return(0);  /* ignore below */
752 
753 	if (base->rec_type < search->rec_type)
754 		return(-3);
755 	if (base->rec_type > search->rec_type)
756 		return(3);
757 	if (search->limit == 3)
758 		return(0);  /* ignore below */
759 
760 	if (base->key < search->key)
761 		return(-4);
762 	if (base->key > search->key)
763 		return(4);
764 	if (search->limit == 4)
765 		return(0);  /* ignore below */
766 
767 	if (base->create_tid == 0) {
768 		if (search->create_tid == 0)
769 			return(0);
770 		return(5);
771 	}
772 	if (search->create_tid == 0)
773 		return(-5);
774 	if (base->create_tid < search->create_tid)
775 		return(-5);
776 	if (base->create_tid > search->create_tid)
777 		return(5);
778 	return(0);
779 }
780 
781 /*
782  * Dump the UNDO FIFO
783  */
784 void
785 hammer_cmd_show_undo(void)
786 {
787 	struct volume_info *volume;
788 	hammer_blockmap_t rootmap;
789 	hammer_off_t scan_offset;
790 	hammer_fifo_any_t head;
791 	struct buffer_info *data_buffer = NULL;
792 	int64_t bytes;
793 
794 	volume = get_volume(RootVolNo);
795 	rootmap = &volume->ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX];
796 	if (rootmap->first_offset <= rootmap->next_offset)
797 		bytes = rootmap->next_offset - rootmap->first_offset;
798 	else
799 		bytes = rootmap->alloc_offset - rootmap->first_offset +
800 			(rootmap->next_offset & HAMMER_OFF_LONG_MASK);
801 
802 	printf("Volume header UNDO %016jx-%016jx/%016jx\n",
803 		(intmax_t)rootmap->first_offset,
804 		(intmax_t)rootmap->next_offset,
805 		(intmax_t)rootmap->alloc_offset);
806 	printf("UNDO map is %jdMB\n",
807 		(intmax_t)((rootmap->alloc_offset & HAMMER_OFF_LONG_MASK) /
808 			   (1024 * 1024)));
809 	printf("UNDO being used is %jdB\n", (intmax_t)bytes);
810 
811 	scan_offset = HAMMER_ZONE_ENCODE(HAMMER_ZONE_UNDO_INDEX, 0);
812 	while (scan_offset < rootmap->alloc_offset) {
813 		head = get_buffer_data(scan_offset, &data_buffer, 0);
814 		printf("%016jx ", scan_offset);
815 
816 		switch(head->head.hdr_type) {
817 		case HAMMER_HEAD_TYPE_PAD:
818 			printf("PAD(%04x)", head->head.hdr_size);
819 			break;
820 		case HAMMER_HEAD_TYPE_DUMMY:
821 			printf("DUMMY(%04x) seq=%08x",
822 				head->head.hdr_size, head->head.hdr_seq);
823 			break;
824 		case HAMMER_HEAD_TYPE_UNDO:
825 			printf("UNDO(%04x) seq=%08x "
826 			       "dataoff=%016jx bytes=%d",
827 				head->head.hdr_size, head->head.hdr_seq,
828 				(intmax_t)head->undo.undo_offset,
829 				head->undo.undo_data_bytes);
830 			break;
831 		case HAMMER_HEAD_TYPE_REDO:
832 			printf("REDO(%04x) seq=%08x flags=%08x "
833 			       "objid=%016jx logoff=%016jx bytes=%d",
834 				head->head.hdr_size, head->head.hdr_seq,
835 				head->redo.redo_flags,
836 				(intmax_t)head->redo.redo_objid,
837 				(intmax_t)head->redo.redo_offset,
838 				head->redo.redo_data_bytes);
839 			break;
840 		default:
841 			printf("UNKNOWN(%04x,%04x) seq=%08x",
842 				head->head.hdr_type,
843 				head->head.hdr_size,
844 				head->head.hdr_seq);
845 			break;
846 		}
847 
848 		if (scan_offset == rootmap->first_offset)
849 			printf(" >");
850 		if (scan_offset == rootmap->next_offset)
851 			printf(" <");
852 		printf("\n");
853 
854 		if ((head->head.hdr_size & HAMMER_HEAD_ALIGN_MASK) ||
855 		    head->head.hdr_size == 0 ||
856 		    head->head.hdr_size > HAMMER_UNDO_ALIGN -
857 				    ((u_int)scan_offset & HAMMER_UNDO_MASK)) {
858 			printf("Illegal size field, skipping to "
859 			       "next boundary\n");
860 			scan_offset = (scan_offset + HAMMER_UNDO_MASK) &
861 					~HAMMER_UNDO_MASK64;
862 		} else {
863 			scan_offset += head->head.hdr_size;
864 		}
865 	}
866 	rel_buffer(data_buffer);
867 }
868