nrelease - fix/improve livecd
[dragonfly.git] / sbin / hammer2 / main.c
1 /*
2  * Copyright (c) 2011-2019 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 int DebugOpt;
39 int VerboseOpt;
40 int QuietOpt;
41 int ForceOpt;
42 int RecurseOpt;
43 int NormalExit = 1;     /* if set to 0 main() has to pthread_exit() */
44 size_t MemOpt;
45
46 static void usage(int code);
47
48 int
49 main(int ac, char **av)
50 {
51         char *sel_path = NULL;
52         const char *uuid_str = NULL;
53         const char *arg;
54         char *opt;
55         int pfs_type = HAMMER2_PFSTYPE_NONE;
56         int all_opt = 0;
57         int ecode = 0;
58         int ch;
59
60         srandomdev();
61         signal(SIGPIPE, SIG_IGN);
62         dmsg_crypto_setup();
63
64         /*
65          * Core options
66          */
67         while ((ch = getopt(ac, av, "adfm:rs:t:u:vq")) != -1) {
68                 switch(ch) {
69                 case 'a':
70                         all_opt = 1;
71                         break;
72                 case 'd':
73                         if (DebugOpt)
74                                 ++DMsgDebugOpt;
75                         DebugOpt = 1;
76                         break;
77                 case 'f':
78                         ForceOpt = 1;
79                         break;
80                 case 'm':
81                         MemOpt = strtoul(optarg, &opt, 0);
82                         switch(*opt) {
83                         case 'g':
84                         case 'G':
85                                 MemOpt *= 1024;
86                                 /* FALLTHROUGH */
87                         case 'm':
88                         case 'M':
89                                 MemOpt *= 1024;
90                                 /* FALLTHROUGH */
91                         case 'k':
92                         case 'K':
93                                 MemOpt *= 1024;
94                                 break;
95                         case 0:
96                                 break;
97                         default:
98                                 fprintf(stderr, "-m: Unrecognized suffix\n");
99                                 usage(1);
100                                 break;
101                         }
102                         break;
103                 case 'r':
104                         RecurseOpt = 1;
105                         break;
106                 case 's':
107                         sel_path = strdup(optarg);
108                         break;
109                 case 't':
110                         /*
111                          * set node type for pfs-create
112                          */
113                         if (strcasecmp(optarg, "CACHE") == 0) {
114                                 pfs_type = HAMMER2_PFSTYPE_CACHE;
115                         } else if (strcasecmp(optarg, "DUMMY") == 0) {
116                                 pfs_type = HAMMER2_PFSTYPE_DUMMY;
117                         } else if (strcasecmp(optarg, "SLAVE") == 0) {
118                                 pfs_type = HAMMER2_PFSTYPE_SLAVE;
119                         } else if (strcasecmp(optarg, "SOFT_SLAVE") == 0) {
120                                 pfs_type = HAMMER2_PFSTYPE_SOFT_SLAVE;
121                         } else if (strcasecmp(optarg, "SOFT_MASTER") == 0) {
122                                 pfs_type = HAMMER2_PFSTYPE_SOFT_MASTER;
123                         } else if (strcasecmp(optarg, "MASTER") == 0) {
124                                 pfs_type = HAMMER2_PFSTYPE_MASTER;
125                         } else {
126                                 fprintf(stderr, "-t: Unrecognized node type\n");
127                                 usage(1);
128                         }
129                         break;
130                 case 'u':
131                         /*
132                          * set uuid for pfs-create, else one will be generated
133                          * (required for all except the MASTER node_type)
134                          */
135                         uuid_str = optarg;
136                         break;
137                 case 'v':
138                         if (QuietOpt)
139                                 --QuietOpt;
140                         else
141                                 ++VerboseOpt;
142                         break;
143                 case 'q':
144                         if (VerboseOpt)
145                                 --VerboseOpt;
146                         else
147                                 ++QuietOpt;
148                         break;
149                 default:
150                         fprintf(stderr, "Unknown option: %c\n", ch);
151                         usage(1);
152                         /* not reached */
153                         break;
154                 }
155         }
156
157         /*
158          * Adjust, then process the command
159          */
160         ac -= optind;
161         av += optind;
162         if (ac < 1) {
163                 fprintf(stderr, "Missing command\n");
164                 usage(1);
165                 /* not reached */
166         }
167
168         if (strcmp(av[0], "connect") == 0) {
169                 /*
170                  * Add cluster connection
171                  */
172                 if (ac < 2) {
173                         fprintf(stderr, "connect: missing argument\n");
174                         usage(1);
175                 }
176                 ecode = cmd_remote_connect(sel_path, av[1]);
177         } else if (strcmp(av[0], "dumpchain") == 0) {
178                 if (ac < 2)
179                         ecode = cmd_dumpchain(".", (u_int)-1);
180                 else if (ac < 3)
181                         ecode = cmd_dumpchain(av[1], (u_int)-1);
182                 else
183                         ecode = cmd_dumpchain(av[1],
184                                               (u_int)strtoul(av[2], NULL, 0));
185         } else if (strcmp(av[0], "debugspan") == 0) {
186                 /*
187                  * Debug connection to the target hammer2 service and run
188                  * the CONN/SPAN protocol.
189                  */
190                 if (ac < 2) {
191                         fprintf(stderr, "debugspan: requires hostname\n");
192                         usage(1);
193                 }
194                 ecode = cmd_debugspan(av[1]);
195         } else if (strcmp(av[0], "disconnect") == 0) {
196                 /*
197                  * Remove cluster connection
198                  */
199                 if (ac < 2) {
200                         fprintf(stderr, "disconnect: missing argument\n");
201                         usage(1);
202                 }
203                 ecode = cmd_remote_disconnect(sel_path, av[1]);
204         } else if (strcmp(av[0], "destroy") == 0) {
205                 if (ac < 2) {
206                         fprintf(stderr,
207                                 "destroy: specify one or more paths to "
208                                 "destroy\n");
209                         usage(1);
210                 }
211                 ecode = cmd_destroy_path(ac - 1, (const char **)(void *)&av[1]);
212         } else if (strcmp(av[0], "destroy-inum") == 0) {
213                 if (ac < 2) {
214                         fprintf(stderr,
215                                 "destroy-inum: specify one or more inode "
216                                 "numbers to destroy\n");
217                         usage(1);
218                 }
219                 ecode = cmd_destroy_inum(sel_path, ac - 1,
220                                          (const char **)(void *)&av[1]);
221         } else if (strcmp(av[0], "emergency-mode-enable") == 0) {
222                 ecode = cmd_emergency_mode(sel_path, 1, ac - 1,
223                                          (const char **)(void *)&av[1]);
224         } else if (strcmp(av[0], "emergency-mode-disable") == 0) {
225                 ecode = cmd_emergency_mode(sel_path, 0, ac - 1,
226                                          (const char **)(void *)&av[1]);
227         } else if (strcmp(av[0], "growfs") == 0) {
228                 ecode = cmd_growfs(sel_path, ac - 1,
229                                          (const char **)(void *)&av[1]);
230         } else if (strcmp(av[0], "hash") == 0) {
231                 ecode = cmd_hash(ac - 1, (const char **)(void *)&av[1]);
232         } else if (strcmp(av[0], "dhash") == 0) {
233                 ecode = cmd_dhash(ac - 1, (const char **)(void *)&av[1]);
234         } else if (strcmp(av[0], "info") == 0) {
235                 ecode = cmd_info(ac - 1, (const char **)(void *)&av[1]);
236         } else if (strcmp(av[0], "mountall") == 0) {
237                 ecode = cmd_mountall(ac - 1, (const char **)(void *)&av[1]);
238         } else if (strcmp(av[0], "status") == 0) {
239                 /*
240                  * Get status of PFS and its connections (-a for all PFSs)
241                  */
242                 if (ac < 2) {
243                         ecode = cmd_remote_status(sel_path, all_opt);
244                 } else {
245                         int i;
246                         for (i = 1; i < ac; ++i)
247                                 ecode = cmd_remote_status(av[i], all_opt);
248                 }
249         } else if (strcmp(av[0], "pfs-clid") == 0) {
250                 /*
251                  * Print cluster id (uuid) for specific PFS
252                  */
253                 if (ac < 2) {
254                         fprintf(stderr, "pfs-clid: requires name\n");
255                         usage(1);
256                 }
257                 ecode = cmd_pfs_getid(sel_path, av[1], 0);
258         } else if (strcmp(av[0], "pfs-fsid") == 0) {
259                 /*
260                  * Print private id (uuid) for specific PFS
261                  */
262                 if (ac < 2) {
263                         fprintf(stderr, "pfs-fsid: requires name\n");
264                         usage(1);
265                 }
266                 ecode = cmd_pfs_getid(sel_path, av[1], 1);
267         } else if (strcmp(av[0], "pfs-list") == 0) {
268                 /*
269                  * List all PFSs
270                  */
271                 if (ac >= 2) {
272                         ecode = cmd_pfs_list(ac - 1,
273                                              (char **)(void *)&av[1]);
274                 } else {
275                         ecode = cmd_pfs_list(1, &sel_path);
276                 }
277         } else if (strcmp(av[0], "pfs-create") == 0) {
278                 /*
279                  * Create new PFS using pfs_type
280                  */
281                 if (ac < 2) {
282                         fprintf(stderr, "pfs-create: requires name\n");
283                         usage(1);
284                 }
285                 ecode = cmd_pfs_create(sel_path, av[1], pfs_type, uuid_str);
286         } else if (strcmp(av[0], "pfs-delete") == 0) {
287                 /*
288                  * Delete a PFS by name
289                  */
290                 if (ac < 2) {
291                         fprintf(stderr, "pfs-delete: requires name\n");
292                         usage(1);
293                 }
294                 ecode = cmd_pfs_delete(sel_path, av, ac);
295         } else if (strcmp(av[0], "recover") == 0 ||
296                    strcmp(av[0], "recover-relaxed") == 0 ||
297                    strcmp(av[0], "recover-file") == 0)
298         {
299                 /*
300                  * Recover a relative path (unanchored match), absolute path,
301                  * specific file, or directory sub-tree.  File restorals are
302                  * fully validated.
303                  */
304                 if (ac != 4) {
305                         fprintf(stderr, "recover device [/]path destdir\n");
306                         usage(1);
307                 } else {
308                         int strict = (strcmp(av[0], "recover-relaxed") != 0);
309                         int isafile = (strcmp(av[0], "recover-file") == 0);
310                         cmd_recover(av[1], av[2], av[3], strict, isafile);
311                 }
312         } else if (strcmp(av[0], "snapshot") == 0 ||
313                    strcmp(av[0], "snapshot-debug") == 0) {
314                 /*
315                  * Create snapshot with optional pfs-type and optional
316                  * label override.
317                  */
318                 uint32_t flags = 0;
319
320                 if (strcmp(av[0], "snapshot-debug") == 0)
321                         flags = HAMMER2_PFSFLAGS_NOSYNC;
322
323                 if (ac > 3) {
324                         fprintf(stderr, "%s: too many arguments\n", av[0]);
325                         usage(1);
326                 }
327                 switch(ac) {
328                 case 1:
329                         ecode = cmd_pfs_snapshot(sel_path, NULL, NULL, flags);
330                         break;
331                 case 2:
332                         ecode = cmd_pfs_snapshot(sel_path, av[1], NULL, flags);
333                         break;
334                 case 3:
335                         ecode = cmd_pfs_snapshot(sel_path, av[1], av[2], flags);
336                         break;
337                 }
338         } else if (strcmp(av[0], "service") == 0) {
339                 /*
340                  * Start the service daemon.  This daemon accepts
341                  * connections from local and remote clients, handles
342                  * the security handshake, and manages the core messaging
343                  * protocol.
344                  */
345                 ecode = cmd_service();
346         } else if (strcmp(av[0], "stat") == 0) {
347                 ecode = cmd_stat(ac - 1, (const char **)(void *)&av[1]);
348         } else if (strcmp(av[0], "leaf") == 0) {
349                 /*
350                  * Start the management daemon for a specific PFS.
351                  *
352                  * This will typically connect to the local master node
353                  * daemon, register the PFS, and then pass its side of
354                  * the socket descriptor to the kernel HAMMER2 VFS via an
355                  * ioctl().  The process and/or thread context remains in the
356                  * kernel until the PFS is unmounted or the connection is
357                  * lost, then returns from the ioctl.
358                  *
359                  * It is possible to connect directly to a remote master node
360                  * instead of the local master node in situations where
361                  * encryption is not desired or no local master node is
362                  * desired.  This is not recommended because it represents
363                  * a single point of failure for the PFS's communications.
364                  *
365                  * Direct kernel<->kernel communication between HAMMER2 VFSs
366                  * is theoretically possible for directly-connected
367                  * registrations (i.e. where the spanning tree is degenerate),
368                  * but not recommended.  We specifically try to reduce the
369                  * complexity of the HAMMER2 VFS kernel code.
370                  */
371                 ecode = cmd_leaf(sel_path);
372         } else if (strcmp(av[0], "shell") == 0) {
373                 /*
374                  * Connect to the command line monitor in the hammer2 master
375                  * node for the machine using HAMMER2_DBG_SHELL messages.
376                  */
377                 ecode = cmd_shell((ac < 2) ? NULL : av[1]);
378         } else if (strcmp(av[0], "rsainit") == 0) {
379                 /*
380                  * Initialize a RSA keypair.  If no target directory is
381                  * specified we default to "/etc/hammer2".
382                  */
383                 arg = (ac < 2) ? HAMMER2_DEFAULT_DIR : av[1];
384                 ecode = cmd_rsainit(arg);
385         } else if (strcmp(av[0], "rsaenc") == 0) {
386                 /*
387                  * Encrypt the input symmetrically by running it through
388                  * the specified public and/or private key files.
389                  *
390                  * If no key files are specified data is encoded using
391                  * "/etc/hammer2/rsa.pub".
392                  *
393                  * WARNING: no padding is added, data stream must contain
394                  *          random padding for this to be secure.
395                  *
396                  * Used for debugging only
397                  */
398                 if (ac == 1) {
399                         const char *rsapath = HAMMER2_DEFAULT_DIR "/rsa.pub";
400                         ecode = cmd_rsaenc(&rsapath, 1);
401                 } else {
402                         ecode = cmd_rsaenc((const char **)(void *)&av[1],
403                                            ac - 1);
404                 }
405         } else if (strcmp(av[0], "rsadec") == 0) {
406                 /*
407                  * Decrypt the input symmetrically by running it through
408                  * the specified public and/or private key files.
409                  *
410                  * If no key files are specified data is decoded using
411                  * "/etc/hammer2/rsa.prv".
412                  *
413                  * WARNING: no padding is added, data stream must contain
414                  *          random padding for this to be secure.
415                  *
416                  * Used for debugging only
417                  */
418                 if (ac == 1) {
419                         const char *rsapath = HAMMER2_DEFAULT_DIR "/rsa.prv";
420                         ecode = cmd_rsadec(&rsapath, 1);
421                 } else {
422                         ecode = cmd_rsadec((const char **)(void *)&av[1],
423                                            ac - 1);
424                 }
425         } else if (strcmp(av[0], "show") == 0) {
426                 /*
427                  * Raw dump of filesystem.  Use -v to check all crc's, and
428                  * -vv to dump bulk file data.
429                  */
430                 if (ac != 2) {
431                         fprintf(stderr, "show: requires device path\n");
432                         usage(1);
433                 } else {
434                         cmd_show(av[1], 0);
435                 }
436         } else if (strcmp(av[0], "freemap") == 0) {
437                 /*
438                  * Raw dump of freemap.  Use -v to check all crc's, and
439                  * -vv to dump bulk file data.
440                  */
441                 if (ac != 2) {
442                         fprintf(stderr, "freemap: requires device path\n");
443                         usage(1);
444                 } else {
445                         cmd_show(av[1], 1);
446                 }
447         } else if (strcmp(av[0], "volhdr") == 0) {
448                 /*
449                  * Dump the volume header.
450                  */
451                 if (ac != 2) {
452                         fprintf(stderr, "volhdr: requires device path\n");
453                         usage(1);
454                 } else {
455                         cmd_show(av[1], 2);
456                 }
457         } else if (strcmp(av[0], "volume-list") == 0) {
458                 /*
459                  * List all volumes
460                  */
461                 if (ac >= 2) {
462                         ecode = cmd_volume_list(ac - 1,
463                                              (char **)(void *)&av[1]);
464                 } else {
465                         ecode = cmd_volume_list(1, &sel_path);
466                 }
467         } else if (strcmp(av[0], "setcomp") == 0) {
468                 if (ac < 3) {
469                         /*
470                          * Missing compression method and at least one
471                          * path.
472                          */
473                         fprintf(stderr,
474                                 "setcomp: requires compression method and "
475                                 "directory/file path\n");
476                         usage(1);
477                 } else {
478                         /*
479                          * Multiple paths may be specified
480                          */
481                         ecode = cmd_setcomp(av[1], &av[2]);
482                 }
483         } else if (strcmp(av[0], "setcheck") == 0) {
484                 if (ac < 3) {
485                         /*
486                          * Missing compression method and at least one
487                          * path.
488                          */
489                         fprintf(stderr,
490                                 "setcheck: requires check code method and "
491                                 "directory/file path\n");
492                         usage(1);
493                 } else {
494                         /*
495                          * Multiple paths may be specified
496                          */
497                         ecode = cmd_setcheck(av[1], &av[2]);
498                 }
499         } else if (strcmp(av[0], "clrcheck") == 0) {
500                 ecode = cmd_setcheck("none", &av[1]);
501         } else if (strcmp(av[0], "setcrc32") == 0) {
502                 ecode = cmd_setcheck("crc32", &av[1]);
503         } else if (strcmp(av[0], "setxxhash64") == 0) {
504                 ecode = cmd_setcheck("xxhash64", &av[1]);
505         } else if (strcmp(av[0], "setsha192") == 0) {
506                 ecode = cmd_setcheck("sha192", &av[1]);
507         } else if (strcmp(av[0], "printinode") == 0) {
508                 if (ac != 2) {
509                         fprintf(stderr,
510                                 "printinode: requires directory/file path\n");
511                         usage(1);
512                 } else {
513                         print_inode(av[1]);
514                 }
515         } else if (strcmp(av[0], "bulkfree") == 0) {
516                 if (ac != 2) {
517                         fprintf(stderr, "bulkfree: requires path to mount\n");
518                         usage(1);
519                 } else {
520                         ecode = cmd_bulkfree(av[1]);
521                 }
522 #if 0
523         } else if (strcmp(av[0], "bulkfree-async") == 0) {
524                 if (ac != 2) {
525                         fprintf(stderr,
526                                 "bulkfree-async: requires path to mount\n");
527                         usage(1);
528                 } else {
529                         ecode = cmd_bulkfree_async(av[1]);
530                 }
531 #endif
532         } else if (strcmp(av[0], "cleanup") == 0) {
533                 ecode = cmd_cleanup(av[1]);     /* can be NULL */
534         } else {
535                 fprintf(stderr, "Unrecognized command: %s\n", av[0]);
536                 usage(1);
537         }
538
539         /*
540          * In DebugMode we may wind up starting several pthreads in the
541          * original process, in which case we have to let them run and
542          * not actually exit.
543          */
544         if (NormalExit) {
545                 return (ecode);
546         } else {
547                 pthread_exit(NULL);
548                 _exit(2);       /* NOT REACHED */
549         }
550 }
551
552 static
553 void
554 usage(int code)
555 {
556         fprintf(stderr,
557                 "hammer2 [options] command [argument ...]\n"
558                 "    -s path            Select filesystem\n"
559                 "    -t type            PFS type for pfs-create\n"
560                 "    -u uuid            uuid for pfs-create\n"
561                 "    -m mem[k,m,g]      buffer memory (bulkfree)\n"
562                 "\n"
563                 "    cleanup [<path>]                  "
564                         "Run cleanup passes\n"
565                 "    connect <target>                  "
566                         "Add cluster link\n"
567                 "    destroy <path>...                 "
568                         "Destroy directory entries (only use if inode bad)\n"
569                 "    destroy-inum <inum>...            "
570                         "Destroy inodes (only use if inode bad)\n"
571                 "    disconnect <target>               "
572                         "Del cluster link\n"
573                 "    emergency-mode-enable <target>    "
574                         "Enable emergency operations mode on filesystem\n"
575                 "                                      "
576                         "THIS IS A VERY DANGEROUS MODE\n"
577                 "    emergency-mode-disable <target>   "
578                         "Disable emergency operations mode on filesystem\n"
579                 "    info [<devpath>...]               "
580                         "Info on all offline or online H2 partitions\n"
581                 "    mountall [<devpath>...]           "
582                         "Mount @LOCAL for all H2 partitions\n"
583                 "    status [<path>...]                "
584                         "Report active cluster status\n"
585                 "    hash [<filename>...]              "
586                         "Print directory hash (key) for name\n"
587                 "    dhash [<filename>...]             "
588                         "Print data hash for long directory entry\n"
589                 "    pfs-list [<path>...]              "
590                         "List PFSs\n"
591                 "    pfs-clid <label>                  "
592                         "Print cluster id for specific PFS\n"
593                 "    pfs-fsid <label>                  "
594                         "Print private id for specific PFS\n"
595                 "    pfs-create <label>                "
596                         "Create a PFS\n"
597                 "    pfs-delete <label>                "
598                         "Destroy a PFS\n"
599                 "    recover <devpath> <path> <destdir> "
600                         "Recover deleted or corrupt files or trees\n"
601                 "    recover-relaxed <devpath> <path> <destdir> "
602                         "Recover deleted or corrupt files or trees\n"
603                 "    recover-file <devpath> <path> <destdir> "
604                         "Recover, target is explicitly a regular file\n"
605                 "    snapshot <path> [<label>]         "
606                         "Snapshot a PFS or directory\n"
607                 "    snapshot-debug <path> [<label>]   "
608                         "Snapshot without filesystem sync\n"
609                 "    service                           "
610                         "Start service daemon\n"
611                 "    stat [<path>...]                  "
612                         "Return inode quota & config\n"
613                 "    leaf                              "
614                         "Start pfs leaf daemon\n"
615                 "    shell [<host>]                    "
616                         "Connect to debug shell\n"
617                 "    debugspan <target>                "
618                         "Connect to target, run CONN/SPAN\n"
619                 "    growfs [<path...]                 "
620                         "Grow a filesystem into resized partition\n"
621                 "    rsainit [<path>]                  "
622                         "Initialize rsa fields\n"
623                 "    show <devpath>                    "
624                         "Raw hammer2 media dump for topology\n"
625                 "    freemap <devpath>                 "
626                         "Raw hammer2 media dump for freemap\n"
627                 "    volhdr <devpath>                  "
628                         "Raw hammer2 media dump for the volume header(s)\n"
629                 "    volume-list [<path>...]           "
630                         "List volumes\n"
631                 "    setcomp <comp[:level]> <path>...  "
632                         "Set comp algo {none, autozero, lz4, zlib} & level\n"
633                 "    setcheck <check> <path>...        "
634                         "Set check algo {none, crc32, xxhash64, sha192}\n"
635                 "    clrcheck [<path>...]              "
636                         "Clear check code override\n"
637                 "    setcrc32 [<path>...]              "
638                         "Set check algo to crc32\n"
639                 "    setxxhash64 [<path>...]           "
640                         "Set check algo to xxhash64\n"
641                 "    setsha192 [<path>...]             "
642                         "Set check algo to sha192\n"
643                 "    bulkfree <path>                   "
644                         "Run bulkfree pass\n"
645                 "    printinode <path>                 "
646                         "Dump inode\n"
647                 "    dumpchain [<path> [<chnflags>]]   "
648                         "Dump in-memory chain topology (ONFLUSH flag is 0x200)\n"
649 #if 0
650                 "    bulkfree-async path               "
651                         "Run bulkfree pass asynchronously\n"
652 #endif
653         );
654         exit(code);
655 }