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