Merge branch 'vendor/LIBPCAP'
[dragonfly.git] / sbin / hammer2 / cmd_debug.c
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 #define SHOW_TAB        2
39
40 static void shell_msghandler(dmsg_msg_t *msg, int unmanaged);
41 static void shell_ttymsg(dmsg_iocom_t *iocom);
42
43 /************************************************************************
44  *                                  SHELL                               *
45  ************************************************************************/
46
47 int
48 cmd_shell(const char *hostname)
49 {
50         dmsg_master_service_info_t *info;
51         pthread_t thread;
52         int fd;
53
54         fd = dmsg_connect(hostname);
55         if (fd < 0)
56                 return 1;
57
58         info = malloc(sizeof(*info));
59         bzero(info, sizeof(*info));
60         info->fd = fd;
61         info->detachme = 0;
62         info->usrmsg_callback = shell_msghandler;
63         info->altmsg_callback = shell_ttymsg;
64         info->node_handler = NULL;
65         info->label = strdup("debug");
66         pthread_create(&thread, NULL, dmsg_master_service, info);
67         pthread_join(thread, NULL);
68
69         return 0;
70 }
71
72 #if 0
73 int
74 cmd_shell(const char *hostname)
75 {
76         struct dmsg_iocom iocom;
77         dmsg_msg_t *msg;
78         int fd;
79
80         /*
81          * Connect to the target
82          */
83         fd = dmsg_connect(hostname);
84         if (fd < 0)
85                 return 1;
86
87         /*
88          * Initialize the session and transmit an empty DMSG_DBG_SHELL
89          * to cause the remote end to generate a prompt.
90          */
91         dmsg_iocom_init(&iocom, fd, 0,
92                         NULL,
93                         shell_rcvmsg,
94                         hammer2_shell_parse,
95                         shell_ttymsg);
96         fcntl(0, F_SETFL, O_NONBLOCK);
97         printf("debug: connected\n");
98
99         msg = dmsg_msg_alloc(&iocom.state0, 0, DMSG_DBG_SHELL, NULL, NULL);
100         dmsg_msg_write(msg);
101         dmsg_iocom_core(&iocom);
102         fprintf(stderr, "debug: disconnected\n");
103         close(fd);
104         return 0;
105 }
106 #endif
107
108 /*
109  * Debug session front-end
110  *
111  * Callback from dmsg_iocom_core() when messages might be present
112  * on the socket.
113  */
114 static
115 void
116 shell_msghandler(dmsg_msg_t *msg, int unmanaged)
117 {
118         dmsg_msg_t *nmsg;
119
120         switch(msg->tcmd) {
121 #if 0
122         case DMSG_LNK_ERROR:
123         case DMSG_LNK_ERROR | DMSGF_REPLY:
124                 /*
125                  * One-way non-transactional LNK_ERROR messages typically
126                  * indicate a connection failure.  Error code 0 is used by
127                  * the debug shell to indicate no more results from last cmd.
128                  */
129                 if (msg->any.head.error) {
130                         fprintf(stderr, "Stream failure: %s\n",
131                                 dmsg_msg_str(msg));
132                 } else {
133                         write(1, "debug> ", 7);
134                 }
135                 break;
136         case DMSG_LNK_ERROR | DMSGF_DELETE:
137                 /* ignore termination of LNK_CONN */
138                 break;
139 #endif
140         case DMSG_DBG_SHELL:
141                 /*
142                  * We send the commands, not accept them.
143                  * (one-way message, not transactional)
144                  */
145                 if (unmanaged)
146                         dmsg_msg_reply(msg, DMSG_ERR_NOSUPP);
147                 break;
148         case DMSG_DBG_SHELL | DMSGF_REPLY:
149                 /*
150                  * A reply from the remote is data we copy to stdout.
151                  * (one-way message, not transactional)
152                  */
153                 if (msg->aux_size) {
154                         msg->aux_data[msg->aux_size - 1] = 0;
155                         write(1, msg->aux_data, strlen(msg->aux_data));
156                 }
157                 break;
158 #if 1
159         case DMSG_LNK_CONN | DMSGF_CREATE:
160                 fprintf(stderr, "Debug Shell received LNK_CONN\n");
161                 nmsg = dmsg_msg_alloc(&msg->state->iocom->state0, 0,
162                                       DMSG_DBG_SHELL,
163                                       NULL, NULL);
164                 dmsg_msg_write(nmsg);
165                 dmsg_msg_reply(msg, DMSG_ERR_NOSUPP);
166                 break;
167         case DMSG_LNK_CONN | DMSGF_DELETE:
168                 break;
169 #endif
170         default:
171                 /*
172                  * Ignore any unknown messages, Terminate any unknown
173                  * transactions with an error.
174                  */
175                 fprintf(stderr, "Unknown message: %s\n", dmsg_msg_str(msg));
176                 if (unmanaged) {
177                         if (msg->any.head.cmd & DMSGF_CREATE)
178                                 dmsg_msg_reply(msg, DMSG_ERR_NOSUPP);
179                         if (msg->any.head.cmd & DMSGF_DELETE)
180                                 dmsg_msg_reply(msg, DMSG_ERR_NOSUPP);
181                 }
182                 break;
183         }
184 }
185
186 /*
187  * Debug session front-end
188  */
189 static
190 void
191 shell_ttymsg(dmsg_iocom_t *iocom)
192 {
193         dmsg_state_t *pstate;
194         dmsg_msg_t *msg;
195         char buf[256];
196         char *cmd;
197         size_t len;
198
199         if (fgets(buf, sizeof(buf), stdin) != NULL) {
200                 if (buf[0] == '@') {
201                         pstate = dmsg_findspan(strtok(buf + 1, " \t\n"));
202                         cmd = strtok(NULL, "\n");
203                 } else {
204                         pstate = &iocom->state0;
205                         cmd = strtok(buf, "\n");
206                 }
207                 if (cmd && pstate) {
208                         len = strlen(cmd) + 1;
209                         msg = dmsg_msg_alloc(pstate, len, DMSG_DBG_SHELL,
210                                              NULL, NULL);
211                         bcopy(cmd, msg->aux_data, len);
212                         dmsg_msg_write(msg);
213                 } else if (cmd) {
214                         fprintf(stderr, "@msgid not found\n");
215                 } else {
216                         /*
217                          * This should cause the remote end to generate
218                          * a debug> prompt (and thus shows that there is
219                          * connectivity).
220                          */
221                         msg = dmsg_msg_alloc(pstate, 0, DMSG_DBG_SHELL,
222                                              NULL, NULL);
223                         dmsg_msg_write(msg);
224                 }
225         } else if (feof(stdin)) {
226                 /*
227                  * Set EOF flag without setting any error code for normal
228                  * EOF.
229                  */
230                 iocom->flags |= DMSG_IOCOMF_EOF;
231         } else {
232                 clearerr(stdin);
233         }
234 }
235
236 /*
237  * Debug session back-end (on remote side)
238  */
239 static void shell_span(dmsg_msg_t *msg, char *cmdbuf);
240
241 void
242 hammer2_shell_parse(dmsg_msg_t *msg, int unmanaged)
243 {
244         dmsg_iocom_t *iocom = msg->state->iocom;
245         char *cmdbuf;
246         char *cmdp;
247         uint32_t cmd;
248
249         /*
250          * Filter on debug shell commands only
251          */
252         cmd = msg->any.head.cmd;
253         if ((cmd & DMSGF_PROTOS) != DMSG_PROTO_DBG) {
254                 if (unmanaged)
255                         dmsg_msg_reply(msg, DMSG_ERR_NOSUPP);
256                 return;
257         }
258         if ((cmd & DMSGF_CMDSWMASK) != DMSG_DBG_SHELL) {
259                 if (unmanaged)
260                         dmsg_msg_reply(msg, DMSG_ERR_NOSUPP);
261                 return;
262         }
263
264         /*
265          * Debug shell command
266          */
267         cmdbuf = msg->aux_data;
268         cmdp = strsep(&cmdbuf, " \t");
269
270         if (cmdp == NULL || *cmdp == 0) {
271                 ;
272         } else if (strcmp(cmdp, "span") == 0) {
273                 shell_span(msg, cmdbuf);
274         } else if (strcmp(cmdp, "tree") == 0) {
275                 dmsg_shell_tree(iocom, cmdbuf); /* dump spanning tree */
276         } else if (strcmp(cmdp, "help") == 0 || strcmp(cmdp, "?") == 0) {
277                 dmsg_printf(iocom, "help            Command help\n");
278                 dmsg_printf(iocom, "span <host>     Span to target host\n");
279                 dmsg_printf(iocom, "tree            Dump spanning tree\n");
280                 dmsg_printf(iocom, "@span <cmd>     Issue via circuit\n");
281         } else {
282                 dmsg_printf(iocom, "Unrecognized command: %s\n", cmdp);
283         }
284         dmsg_printf(iocom, "debug> ");
285 }
286
287 static void
288 shell_span(dmsg_msg_t *msg, char *cmdbuf)
289 {
290         dmsg_iocom_t *iocom = msg->state->iocom;
291         dmsg_master_service_info_t *info;
292         const char *hostname = strsep(&cmdbuf, " \t");
293         pthread_t thread;
294         int fd;
295
296         /*
297          * Connect to the target
298          */
299         if (hostname == NULL) {
300                 fd = -1;
301         } else {
302                 fd = dmsg_connect(hostname);
303         }
304
305         /*
306          * Start master service
307          */
308         if (fd < 0) {
309                 dmsg_printf(iocom, "Connection to %s failed\n", hostname);
310         } else {
311                 dmsg_printf(iocom, "Connected to %s\n", hostname);
312
313                 info = malloc(sizeof(*info));
314                 bzero(info, sizeof(*info));
315                 info->fd = fd;
316                 info->detachme = 1;
317                 info->usrmsg_callback = hammer2_shell_parse;
318                 info->label = strdup("client");
319
320                 pthread_create(&thread, NULL, dmsg_master_service, info);
321                 /*pthread_join(thread, &res);*/
322         }
323 }
324
325 /************************************************************************
326  *                              DEBUGSPAN                               *
327  ************************************************************************
328  *
329  * Connect to the target manually (not via the cluster list embedded in
330  * a hammer2 filesystem) and initiate the SPAN protocol.
331  */
332 int
333 cmd_debugspan(const char *hostname)
334 {
335         pthread_t thread;
336         int fd;
337         void *res;
338
339         /*
340          * Connect to the target
341          */
342         fd = dmsg_connect(hostname);
343         if (fd < 0)
344                 return 1;
345
346         printf("debugspan: connected to %s, starting CONN/SPAN\n", hostname);
347         pthread_create(&thread, NULL,
348                        dmsg_master_service, (void *)(intptr_t)fd);
349         pthread_join(thread, &res);
350         return(0);
351 }
352
353 /************************************************************************
354  *                                  SHOW                                *
355  ************************************************************************/
356
357 static void show_bref(int fd, int tab, int bi, hammer2_blockref_t *bref,
358                         int dofreemap);
359 static void tabprintf(int tab, const char *ctl, ...);
360
361 int
362 cmd_show(const char *devpath, int dofreemap)
363 {
364         hammer2_blockref_t broot;
365         hammer2_blockref_t best;
366         hammer2_media_data_t media;
367         int fd;
368         int i;
369         int best_i;
370
371         fd = open(devpath, O_RDONLY);
372         if (fd < 0) {
373                 perror("open");
374                 return 1;
375         }
376
377         /*
378          * Show the tree using the best volume header.
379          * -vvv will show the tree for all four volume headers.
380          */
381         best_i = -1;
382         bzero(&best, sizeof(best));
383         for (i = 0; i < 4; ++i) {
384                 bzero(&broot, sizeof(broot));
385                 broot.type = HAMMER2_BREF_TYPE_VOLUME;
386                 broot.data_off = (i * HAMMER2_ZONE_BYTES64) |
387                                  HAMMER2_PBUFRADIX;
388                 lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, 0);
389                 if (read(fd, &media, HAMMER2_PBUFSIZE) ==
390                     (ssize_t)HAMMER2_PBUFSIZE) {
391                         broot.mirror_tid = media.voldata.mirror_tid;
392                         if (best_i < 0 || best.mirror_tid < broot.mirror_tid) {
393                                 best_i = i;
394                                 best = broot;
395                         }
396                         if (VerboseOpt >= 3)
397                                 show_bref(fd, 0, i, &broot, dofreemap);
398                 }
399         }
400         if (VerboseOpt < 3)
401                 show_bref(fd, 0, best_i, &best, dofreemap);
402         close(fd);
403
404         return 0;
405 }
406
407 static void
408 show_bref(int fd, int tab, int bi, hammer2_blockref_t *bref, int dofreemap)
409 {
410         hammer2_media_data_t media;
411         hammer2_blockref_t *bscan;
412         int bcount;
413         int i;
414         int didnl;
415         int namelen;
416         int obrace = 1;
417         size_t bytes;
418         const char *type_str;
419         char *str = NULL;
420
421         switch(bref->type) {
422         case HAMMER2_BREF_TYPE_EMPTY:
423                 type_str = "empty";
424                 break;
425         case HAMMER2_BREF_TYPE_INODE:
426                 type_str = "inode";
427                 break;
428         case HAMMER2_BREF_TYPE_INDIRECT:
429                 type_str = "indblk";
430                 break;
431         case HAMMER2_BREF_TYPE_DATA:
432                 type_str = "data";
433                 break;
434         case HAMMER2_BREF_TYPE_VOLUME:
435                 type_str = "volume";
436                 break;
437         case HAMMER2_BREF_TYPE_FREEMAP:
438                 type_str = "freemap";
439                 break;
440         case HAMMER2_BREF_TYPE_FREEMAP_NODE:
441                 type_str = "fmapnode";
442                 break;
443         case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
444                 type_str = "fbitmap";
445                 break;
446         default:
447                 type_str = "unknown";
448                 break;
449         }
450
451         tabprintf(tab, "%s.%-3d %016jx %016jx/%-2d mir=%016jx mod=%016jx ",
452                type_str, bi, (intmax_t)bref->data_off,
453                (intmax_t)bref->key, (intmax_t)bref->keybits,
454                (intmax_t)bref->mirror_tid, (intmax_t)bref->modify_tid);
455         tab += SHOW_TAB;
456
457         bytes = (size_t)1 << (bref->data_off & HAMMER2_OFF_MASK_RADIX);
458
459         {
460                 hammer2_off_t io_off;
461                 hammer2_off_t io_base;
462                 size_t io_bytes;
463                 size_t boff;
464
465                 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
466                 io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1);
467                 io_bytes = bytes;
468                 boff = io_off - io_base;
469
470                 io_bytes = HAMMER2_MINIOSIZE;
471                 while (io_bytes + boff < bytes)
472                         io_bytes <<= 1;
473
474                 if (io_bytes > sizeof(media)) {
475                         printf("(bad block size %zd)\n", bytes);
476                         return;
477                 }
478                 if (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1) {
479                         lseek(fd, io_base, 0);
480                         if (read(fd, &media, io_bytes) != (ssize_t)io_bytes) {
481                                 printf("(media read failed)\n");
482                                 return;
483                         }
484                         if (boff)
485                                 bcopy((char *)&media + boff, &media, bytes);
486                 }
487         }
488
489         bscan = NULL;
490         bcount = 0;
491         didnl = 1;
492         namelen = 0;
493
494         switch(bref->type) {
495         case HAMMER2_BREF_TYPE_EMPTY:
496                 obrace = 0;
497                 break;
498         case HAMMER2_BREF_TYPE_INODE:
499                 printf("{\n");
500                 if (media.ipdata.op_flags & HAMMER2_OPFLAG_DIRECTDATA) {
501                         /* no blockrefs */
502                 } else {
503                         bscan = &media.ipdata.u.blockset.blockref[0];
504                         bcount = HAMMER2_SET_COUNT;
505                 }
506                 namelen = media.ipdata.name_len;
507                 if (namelen > HAMMER2_INODE_MAXNAME)
508                         namelen = 0;
509                 tabprintf(tab, "filename \"%*.*s\"\n",
510                           namelen, namelen, media.ipdata.filename);
511                 tabprintf(tab, "version  %d\n", media.ipdata.version);
512                 tabprintf(tab, "uflags   0x%08x\n",
513                           media.ipdata.uflags);
514                 if (media.ipdata.rmajor || media.ipdata.rminor) {
515                         tabprintf(tab, "rmajor   %d\n",
516                                   media.ipdata.rmajor);
517                         tabprintf(tab, "rminor   %d\n",
518                                   media.ipdata.rminor);
519                 }
520                 tabprintf(tab, "ctime    %s\n",
521                           hammer2_time64_to_str(media.ipdata.ctime, &str));
522                 tabprintf(tab, "mtime    %s\n",
523                           hammer2_time64_to_str(media.ipdata.mtime, &str));
524                 tabprintf(tab, "atime    %s\n",
525                           hammer2_time64_to_str(media.ipdata.atime, &str));
526                 tabprintf(tab, "btime    %s\n",
527                           hammer2_time64_to_str(media.ipdata.btime, &str));
528                 tabprintf(tab, "uid      %s\n",
529                           hammer2_uuid_to_str(&media.ipdata.uid, &str));
530                 tabprintf(tab, "gid      %s\n",
531                           hammer2_uuid_to_str(&media.ipdata.gid, &str));
532                 if (media.ipdata.type == HAMMER2_OBJTYPE_HARDLINK)
533                         tabprintf(tab, "type     %s (%s)\n",
534                               hammer2_iptype_to_str(media.ipdata.type),
535                               hammer2_iptype_to_str(media.ipdata.target_type));
536                 else
537                         tabprintf(tab, "type     %s\n",
538                                   hammer2_iptype_to_str(media.ipdata.type));
539                 tabprintf(tab, "opflgs   0x%02x\n",
540                           media.ipdata.op_flags);
541                 tabprintf(tab, "capflgs  0x%04x\n",
542                           media.ipdata.cap_flags);
543                 tabprintf(tab, "mode     %-7o\n",
544                           media.ipdata.mode);
545                 tabprintf(tab, "inum     0x%016jx\n",
546                           media.ipdata.inum);
547                 tabprintf(tab, "size     %ju\n",
548                           (uintmax_t)media.ipdata.size);
549                 tabprintf(tab, "nlinks   %ju\n",
550                           (uintmax_t)media.ipdata.nlinks);
551                 tabprintf(tab, "iparent  0x%016jx\n",
552                           (uintmax_t)media.ipdata.iparent);
553                 tabprintf(tab, "name_key 0x%016jx\n",
554                           (uintmax_t)media.ipdata.name_key);
555                 tabprintf(tab, "name_len %u\n",
556                           media.ipdata.name_len);
557                 tabprintf(tab, "ncopies  %u\n",
558                           media.ipdata.ncopies);
559                 tabprintf(tab, "compalg  %u\n",
560                           media.ipdata.comp_algo);
561                 if (media.ipdata.op_flags & HAMMER2_OPFLAG_PFSROOT) {
562                         tabprintf(tab, "pfs_type %u (%s)\n",
563                                   media.ipdata.pfs_type,
564                                   hammer2_pfstype_to_str(media.ipdata.pfs_type));
565                         tabprintf(tab, "pfs_inum 0x%016jx\n",
566                                   (uintmax_t)media.ipdata.pfs_inum);
567                         tabprintf(tab, "pfs_clid %s\n",
568                                   hammer2_uuid_to_str(&media.ipdata.pfs_clid,
569                                                       &str));
570                         tabprintf(tab, "pfs_fsid %s\n",
571                                   hammer2_uuid_to_str(&media.ipdata.pfs_fsid,
572                                                       &str));
573                 }
574                 tabprintf(tab, "data_quota  %ju\n",
575                           (uintmax_t)media.ipdata.data_quota);
576                 tabprintf(tab, "data_count  %ju\n",
577                           (uintmax_t)media.ipdata.data_count);
578                 tabprintf(tab, "inode_quota %ju\n",
579                           (uintmax_t)media.ipdata.inode_quota);
580                 tabprintf(tab, "inode_count %ju\n",
581                           (uintmax_t)media.ipdata.inode_count);
582                 tabprintf(tab, "attr_tid    0x%016jx\n",
583                           (uintmax_t)media.ipdata.attr_tid);
584                 if (media.ipdata.type == HAMMER2_OBJTYPE_DIRECTORY) {
585                         tabprintf(tab, "dirent_tid  %016jx\n",
586                                   (uintmax_t)media.ipdata.dirent_tid);
587                 }
588                 break;
589         case HAMMER2_BREF_TYPE_INDIRECT:
590                 bscan = &media.npdata[0];
591                 bcount = bytes / sizeof(hammer2_blockref_t);
592                 didnl = 1;
593                 printf("{\n");
594                 break;
595         case HAMMER2_BREF_TYPE_DATA:
596                 if (VerboseOpt >= 2) {
597                         printf("{\n");
598                 } else {
599                         printf("\n");
600                         obrace = 0;
601                 }
602                 break;
603         case HAMMER2_BREF_TYPE_VOLUME:
604                 printf("mirror_tid=%016jx freemap_tid=%016jx ",
605                         media.voldata.mirror_tid,
606                         media.voldata.freemap_tid);
607                 if (dofreemap) {
608                         bscan = &media.voldata.freemap_blockset.blockref[0];
609                         bcount = HAMMER2_SET_COUNT;
610                 } else {
611                         bscan = &media.voldata.sroot_blockset.blockref[0];
612                         bcount = HAMMER2_SET_COUNT;
613                 }
614                 printf("{\n");
615                 break;
616         case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
617                 printf("{\n");
618                 for (i = 0; i < HAMMER2_FREEMAP_COUNT; ++i) {
619                         if (media.bmdata[i].class == 0 &&
620                             media.bmdata[i].avail == 0) {
621                                 continue;
622                         }
623                         tabprintf(tab + 4, "%04d.%04x (avail=%5d) "
624                                   "%08x %08x %08x %08x %08x %08x %08x %08x\n",
625                                   i, media.bmdata[i].class,
626                                   media.bmdata[i].avail,
627                                   media.bmdata[i].bitmap[0],
628                                   media.bmdata[i].bitmap[1],
629                                   media.bmdata[i].bitmap[2],
630                                   media.bmdata[i].bitmap[3],
631                                   media.bmdata[i].bitmap[4],
632                                   media.bmdata[i].bitmap[5],
633                                   media.bmdata[i].bitmap[6],
634                                   media.bmdata[i].bitmap[7]);
635                 }
636                 tabprintf(tab, "}\n");
637                 break;
638         case HAMMER2_BREF_TYPE_FREEMAP_NODE:
639                 printf("{\n");
640                 bscan = &media.npdata[0];
641                 bcount = bytes / sizeof(hammer2_blockref_t);
642                 break;
643         default:
644                 printf("\n");
645                 obrace = 0;
646                 break;
647         }
648         if (str)
649                 free(str);
650         for (i = 0; i < bcount; ++i) {
651                 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) {
652                         if (didnl == 0) {
653                                 printf("\n");
654                                 didnl = 1;
655                         }
656                         show_bref(fd, tab, i, &bscan[i], dofreemap);
657                 }
658         }
659         tab -= SHOW_TAB;
660         if (obrace) {
661                 if (bref->type == HAMMER2_BREF_TYPE_INODE)
662                         tabprintf(tab, "} (%s.%d, \"%*.*s\")\n",
663                                   type_str, bi,
664                                   namelen, namelen, media.ipdata.filename);
665                 else
666                         tabprintf(tab, "} (%s.%d)\n", type_str,bi);
667         }
668 }
669
670 int
671 cmd_hash(int ac, const char **av)
672 {
673         int i;
674
675         for (i = 0; i < ac; ++i) {
676                 printf("%016jx %s\n", dirhash(av[i], strlen(av[i])), av[i]);
677         }
678         return(0);
679 }
680
681 int
682 cmd_chaindump(const char *path)
683 {
684         int dummy = 0;
685         int fd;
686
687         fd = open(path, O_RDONLY);
688         if (fd >= 0) {
689                 ioctl(fd, HAMMER2IOC_DEBUG_DUMP, &dummy);
690                 close(fd);
691         } else {
692                 fprintf(stderr, "unable to open %s\n", path);
693         }
694         return 0;
695 }
696
697
698 static
699 void
700 tabprintf(int tab, const char *ctl, ...)
701 {
702         va_list va;
703
704         printf("%*.*s", tab, tab, "");
705         va_start(va, ctl);
706         vprintf(ctl, va);
707         va_end(va);
708 }