4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
43 #include <sys/mntent.h>
44 #include <sys/mnttab.h>
45 #include <sys/mount.h>
53 libzfs_handle_t *g_zfs;
55 static FILE *mnttab_file;
57 static int zfs_do_clone(int argc, char **argv);
58 static int zfs_do_create(int argc, char **argv);
59 static int zfs_do_destroy(int argc, char **argv);
60 static int zfs_do_get(int argc, char **argv);
61 static int zfs_do_inherit(int argc, char **argv);
62 static int zfs_do_list(int argc, char **argv);
63 static int zfs_do_mount(int argc, char **argv);
64 static int zfs_do_rename(int argc, char **argv);
65 static int zfs_do_rollback(int argc, char **argv);
66 static int zfs_do_set(int argc, char **argv);
67 static int zfs_do_snapshot(int argc, char **argv);
68 static int zfs_do_unmount(int argc, char **argv);
69 static int zfs_do_share(int argc, char **argv);
70 static int zfs_do_unshare(int argc, char **argv);
71 static int zfs_do_send(int argc, char **argv);
72 static int zfs_do_receive(int argc, char **argv);
73 static int zfs_do_promote(int argc, char **argv);
74 static int zfs_do_jail(int argc, char **argv);
75 static int zfs_do_unjail(int argc, char **argv);
78 * These libumem hooks provide a reasonable set of defaults for the allocator's
79 * debugging facilities.
82 _umem_debug_init(void)
84 return ("default,verbose"); /* $UMEM_DEBUG setting */
88 _umem_logging_init(void)
90 return ("fail,contents"); /* $UMEM_LOGGING setting */
115 typedef struct zfs_command {
117 int (*func)(int argc, char **argv);
122 * Master command table. Each ZFS command has a name, associated function, and
123 * usage message. The usage messages need to be internationalized, so we have
124 * to have a function to return the usage message based on a command index.
126 * These commands are organized according to how they are displayed in the usage
127 * message. An empty command (one with a NULL name) indicates an empty line in
128 * the generic usage message.
130 static zfs_command_t command_table[] = {
131 { "create", zfs_do_create, HELP_CREATE },
132 { "destroy", zfs_do_destroy, HELP_DESTROY },
134 { "snapshot", zfs_do_snapshot, HELP_SNAPSHOT },
135 { "rollback", zfs_do_rollback, HELP_ROLLBACK },
136 { "clone", zfs_do_clone, HELP_CLONE },
137 { "promote", zfs_do_promote, HELP_PROMOTE },
138 { "rename", zfs_do_rename, HELP_RENAME },
140 { "list", zfs_do_list, HELP_LIST },
142 { "set", zfs_do_set, HELP_SET },
143 { "get", zfs_do_get, HELP_GET },
144 { "inherit", zfs_do_inherit, HELP_INHERIT },
146 { "mount", zfs_do_mount, HELP_MOUNT },
148 { "unmount", zfs_do_unmount, HELP_UNMOUNT },
150 { "share", zfs_do_share, HELP_SHARE },
152 { "unshare", zfs_do_unshare, HELP_UNSHARE },
154 { "send", zfs_do_send, HELP_SEND },
155 { "receive", zfs_do_receive, HELP_RECEIVE },
157 { "jail", zfs_do_jail, HELP_JAIL },
158 { "unjail", zfs_do_unjail, HELP_UNJAIL },
161 #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0]))
163 zfs_command_t *current_command;
166 get_usage(zfs_help_t idx)
170 return (gettext("\tclone <snapshot> <filesystem|volume>\n"));
172 return (gettext("\tcreate [[-o property=value] ... ] "
174 "\tcreate [-s] [-b blocksize] [[-o property=value] ...]\n"
175 "\t -V <size> <volume>\n"));
177 return (gettext("\tdestroy [-rRf] "
178 "<filesystem|volume|snapshot>\n"));
180 return (gettext("\tget [-rHp] [-o field[,field]...] "
181 "[-s source[,source]...]\n"
182 "\t <all | property[,property]...> "
183 "[filesystem|volume|snapshot] ...\n"));
185 return (gettext("\tinherit [-r] <property> "
186 "<filesystem|volume> ...\n"));
188 return (gettext("\tjail <jailid> <filesystem>\n"));
190 return (gettext("\tunjail <jailid> <filesystem>\n"));
192 return (gettext("\tlist [-rH] [-o property[,property]...] "
193 "[-t type[,type]...]\n"
194 "\t [-s property [-s property]...]"
195 " [-S property [-S property]...]\n"
196 "\t [filesystem|volume|snapshot] ...\n"));
198 return (gettext("\tmount\n"
199 "\tmount [-o opts] [-O] -a\n"
200 "\tmount [-o opts] [-O] <filesystem>\n"));
202 return (gettext("\tpromote <clone filesystem>\n"));
204 return (gettext("\treceive [-vnF] <filesystem|volume|"
206 "\treceive [-vnF] -d <filesystem>\n"));
208 return (gettext("\trename <filesystem|volume|snapshot> "
209 "<filesystem|volume|snapshot>\n"
210 "\trename -r <snapshot> <snapshot>"));
212 return (gettext("\trollback [-rRf] <snapshot>\n"));
214 return (gettext("\tsend [-i <snapshot>] <snapshot>\n"));
216 return (gettext("\tset <property=value> "
217 "<filesystem|volume> ...\n"));
219 return (gettext("\tshare -a\n"
220 "\tshare <filesystem>\n"));
222 return (gettext("\tsnapshot [-r] "
223 "<filesystem@name|volume@name>\n"));
225 return (gettext("\tunmount [-f] -a\n"
226 "\tunmount [-f] <filesystem|mountpoint>\n"));
228 return (gettext("\tunshare [-f] -a\n"
229 "\tunshare [-f] <filesystem|mountpoint>\n"));
237 * Utility function to guarantee malloc() success.
240 safe_malloc(size_t size)
244 if ((data = calloc(1, size)) == NULL) {
245 (void) fprintf(stderr, "internal error: out of memory\n");
253 * Callback routinue that will print out information for each of the
257 usage_prop_cb(zfs_prop_t prop, void *cb)
261 (void) fprintf(fp, "\t%-13s ", zfs_prop_to_name(prop));
263 if (zfs_prop_readonly(prop))
264 (void) fprintf(fp, " NO ");
266 (void) fprintf(fp, " YES ");
268 if (zfs_prop_inheritable(prop))
269 (void) fprintf(fp, " YES ");
271 (void) fprintf(fp, " NO ");
273 if (zfs_prop_values(prop) == NULL)
274 (void) fprintf(fp, "-\n");
276 (void) fprintf(fp, "%s\n", zfs_prop_values(prop));
278 return (ZFS_PROP_CONT);
282 * Display usage message. If we're inside a command, display only the usage for
283 * that command. Otherwise, iterate over the entire command table and display
284 * a complete usage message.
287 usage(boolean_t requested)
290 boolean_t show_properties = B_FALSE;
291 FILE *fp = requested ? stdout : stderr;
293 if (current_command == NULL) {
295 (void) fprintf(fp, gettext("usage: zfs command args ...\n"));
297 gettext("where 'command' is one of the following:\n\n"));
299 for (i = 0; i < NCOMMAND; i++) {
300 if (command_table[i].name == NULL)
301 (void) fprintf(fp, "\n");
303 (void) fprintf(fp, "%s",
304 get_usage(command_table[i].usage));
307 (void) fprintf(fp, gettext("\nEach dataset is of the form: "
308 "pool/[dataset/]*dataset[@name]\n"));
310 (void) fprintf(fp, gettext("usage:\n"));
311 (void) fprintf(fp, "%s", get_usage(current_command->usage));
314 if (current_command != NULL &&
315 (strcmp(current_command->name, "set") == 0 ||
316 strcmp(current_command->name, "get") == 0 ||
317 strcmp(current_command->name, "inherit") == 0 ||
318 strcmp(current_command->name, "list") == 0))
319 show_properties = B_TRUE;
321 if (show_properties) {
324 gettext("\nThe following properties are supported:\n"));
326 (void) fprintf(fp, "\n\t%-13s %s %s %s\n\n",
327 "PROPERTY", "EDIT", "INHERIT", "VALUES");
329 /* Iterate over all properties */
330 (void) zfs_prop_iter(usage_prop_cb, fp, B_FALSE);
332 (void) fprintf(fp, gettext("\nSizes are specified in bytes "
333 "with standard units such as K, M, G, etc.\n"));
334 (void) fprintf(fp, gettext("\n\nUser-defined properties can "
335 "be specified by using a name containing a colon (:).\n"));
339 * "zfs set|get" must not be localised this is the
340 * command name and arguments.
343 gettext("\nFor the property list, run: zfs set|get\n"));
347 * See comments at end of main().
349 if (getenv("ZFS_ABORT") != NULL) {
350 (void) printf("dumping core by request\n");
354 exit(requested ? 0 : 2);
358 * zfs clone <fs, snap, vol> fs
360 * Given an existing dataset, create a writable copy whose initial contents
361 * are the same as the source. The newly created dataset maintains a
362 * dependency on the original; the original cannot be destroyed so long as
366 zfs_do_clone(int argc, char **argv)
372 if (argc > 1 && argv[1][0] == '-') {
373 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
378 /* check number of arguments */
380 (void) fprintf(stderr, gettext("missing source dataset "
385 (void) fprintf(stderr, gettext("missing target dataset "
390 (void) fprintf(stderr, gettext("too many arguments\n"));
394 /* open the source dataset */
395 if ((zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_SNAPSHOT)) == NULL)
399 ret = zfs_clone(zhp, argv[2], NULL);
401 /* create the mountpoint if necessary */
403 zfs_handle_t *clone = zfs_open(g_zfs, argv[2], ZFS_TYPE_ANY);
405 if ((ret = zfs_mount(clone, NULL, 0)) == 0)
406 ret = zfs_share(clone);
409 zpool_log_history(g_zfs, argc, argv, argv[2], B_FALSE, B_FALSE);
414 return (ret == 0 ? 0 : 1);
418 * zfs create [-o prop=value] ... fs
419 * zfs create [-s] [-b blocksize] [-o prop=value] ... -V vol size
421 * Create a new dataset. This command can be used to create filesystems
422 * and volumes. Snapshot creation is handled by 'zfs snapshot'.
423 * For volumes, the user must specify a size to be used.
425 * The '-s' flag applies only to volumes, and indicates that we should not try
426 * to set the reservation for this volume. By default we set a reservation
427 * equal to the size for any volume.
430 zfs_do_create(int argc, char **argv)
432 zfs_type_t type = ZFS_TYPE_FILESYSTEM;
433 zfs_handle_t *zhp = NULL;
436 boolean_t noreserve = B_FALSE;
438 nvlist_t *props = NULL;
441 char *propval = NULL;
444 if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) {
445 (void) fprintf(stderr, gettext("internal error: "
451 while ((c = getopt(argc, argv, ":V:b:so:")) != -1) {
454 type = ZFS_TYPE_VOLUME;
455 if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {
456 (void) fprintf(stderr, gettext("bad volume "
457 "size '%s': %s\n"), optarg,
458 libzfs_error_description(g_zfs));
462 if (nvlist_add_uint64(props,
463 zfs_prop_to_name(ZFS_PROP_VOLSIZE),
465 (void) fprintf(stderr, gettext("internal "
466 "error: out of memory\n"));
472 if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {
473 (void) fprintf(stderr, gettext("bad volume "
474 "block size '%s': %s\n"), optarg,
475 libzfs_error_description(g_zfs));
479 if (nvlist_add_uint64(props,
480 zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
482 (void) fprintf(stderr, gettext("internal "
483 "error: out of memory\n"));
489 if ((propval = strchr(propname, '=')) == NULL) {
490 (void) fprintf(stderr, gettext("missing "
491 "'=' for -o option\n"));
496 if (nvlist_lookup_string(props, propname,
498 (void) fprintf(stderr, gettext("property '%s' "
499 "specified multiple times\n"), propname);
502 if (nvlist_add_string(props, propname, propval) != 0) {
503 (void) fprintf(stderr, gettext("internal "
504 "error: out of memory\n"));
512 (void) fprintf(stderr, gettext("missing size "
517 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
523 if (noreserve && type != ZFS_TYPE_VOLUME) {
524 (void) fprintf(stderr, gettext("'-s' can only be used when "
525 "creating a volume\n"));
532 /* check number of arguments */
534 (void) fprintf(stderr, gettext("missing %s argument\n"),
535 zfs_type_to_name(type));
539 (void) fprintf(stderr, gettext("too many arguments\n"));
543 if (type == ZFS_TYPE_VOLUME && !noreserve &&
544 nvlist_lookup_string(props, zfs_prop_to_name(ZFS_PROP_RESERVATION),
546 if (nvlist_add_uint64(props,
547 zfs_prop_to_name(ZFS_PROP_RESERVATION),
549 (void) fprintf(stderr, gettext("internal "
550 "error: out of memory\n"));
557 if (zfs_create(g_zfs, argv[0], type, props) != 0)
561 *(propval - 1) = '=';
562 zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0],
565 if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL)
569 * Mount and/or share the new filesystem as appropriate. We provide a
570 * verbose error message to let the user know that their filesystem was
571 * in fact created, even if we failed to mount or share it.
573 if (zfs_mount(zhp, NULL, 0) != 0) {
574 (void) fprintf(stderr, gettext("filesystem successfully "
575 "created, but not mounted\n"));
577 } else if (zfs_share(zhp) != 0) {
578 (void) fprintf(stderr, gettext("filesystem successfully "
579 "created, but not shared\n"));
597 * zfs destroy [-rf] <fs, snap, vol>
599 * -r Recursively destroy all children
600 * -R Recursively destroy all dependents, including clones
601 * -f Force unmounting of any dependents
603 * Destroys the given dataset. By default, it will unmount any filesystems,
604 * and refuse to destroy a dataset that has any dependents. A dependent can
605 * either be a child, or a clone of a child.
607 typedef struct destroy_cbdata {
614 boolean_t cb_closezhp;
615 zfs_handle_t *cb_target;
620 * Check for any dependents based on the '-r' or '-R' flags.
623 destroy_check_dependent(zfs_handle_t *zhp, void *data)
625 destroy_cbdata_t *cbp = data;
626 const char *tname = zfs_get_name(cbp->cb_target);
627 const char *name = zfs_get_name(zhp);
629 if (strncmp(tname, name, strlen(tname)) == 0 &&
630 (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) {
632 * This is a direct descendant, not a clone somewhere else in
639 (void) fprintf(stderr, gettext("cannot destroy '%s': "
640 "%s has children\n"),
641 zfs_get_name(cbp->cb_target),
642 zfs_type_to_name(zfs_get_type(cbp->cb_target)));
643 (void) fprintf(stderr, gettext("use '-r' to destroy "
644 "the following datasets:\n"));
645 cbp->cb_first = B_FALSE;
649 (void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
652 * This is a clone. We only want to report this if the '-r'
653 * wasn't specified, or the target is a snapshot.
655 if (!cbp->cb_recurse &&
656 zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT)
660 (void) fprintf(stderr, gettext("cannot destroy '%s': "
661 "%s has dependent clones\n"),
662 zfs_get_name(cbp->cb_target),
663 zfs_type_to_name(zfs_get_type(cbp->cb_target)));
664 (void) fprintf(stderr, gettext("use '-R' to destroy "
665 "the following datasets:\n"));
666 cbp->cb_first = B_FALSE;
670 (void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
679 destroy_callback(zfs_handle_t *zhp, void *data)
681 destroy_cbdata_t *cbp = data;
684 * Ignore pools (which we've already flagged as an error before getting
687 if (strchr(zfs_get_name(zhp), '/') == NULL &&
688 zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
694 * Bail out on the first error.
696 if (zfs_unmount(zhp, NULL, cbp->cb_force ? MS_FORCE : 0) != 0 ||
697 zfs_destroy(zhp) != 0) {
707 destroy_snap_clones(zfs_handle_t *zhp, void *arg)
709 destroy_cbdata_t *cbp = arg;
710 char thissnap[MAXPATHLEN];
712 boolean_t closezhp = cbp->cb_closezhp;
715 (void) snprintf(thissnap, sizeof (thissnap),
716 "%s@%s", zfs_get_name(zhp), cbp->cb_snapname);
718 libzfs_print_on_error(g_zfs, B_FALSE);
719 szhp = zfs_open(g_zfs, thissnap, ZFS_TYPE_SNAPSHOT);
720 libzfs_print_on_error(g_zfs, B_TRUE);
723 * Destroy any clones of this snapshot
725 if (zfs_iter_dependents(szhp, B_FALSE, destroy_callback,
735 cbp->cb_closezhp = B_TRUE;
736 rv = zfs_iter_filesystems(zhp, destroy_snap_clones, arg);
743 zfs_do_destroy(int argc, char **argv)
745 destroy_cbdata_t cb = { 0 };
751 while ((c = getopt(argc, argv, "frR")) != -1) {
765 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
774 /* check number of arguments */
776 (void) fprintf(stderr, gettext("missing path argument\n"));
780 (void) fprintf(stderr, gettext("too many arguments\n"));
785 * If we are doing recursive destroy of a snapshot, then the
786 * named snapshot may not exist. Go straight to libzfs.
788 if (cb.cb_recurse && (cp = strchr(argv[0], '@'))) {
792 if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL)
797 if (cb.cb_doclones) {
799 if (destroy_snap_clones(zhp, &cb) != 0) {
805 ret = zfs_destroy_snaps(zhp, cp);
808 (void) fprintf(stderr,
809 gettext("no snapshots destroyed\n"));
811 zpool_log_history(g_zfs, argc + optind, argv - optind,
812 argv[0], B_FALSE, B_FALSE);
818 /* Open the given dataset */
819 if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL)
825 * Perform an explicit check for pools before going any further.
827 if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL &&
828 zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
829 (void) fprintf(stderr, gettext("cannot destroy '%s': "
830 "operation does not apply to pools\n"),
832 (void) fprintf(stderr, gettext("use 'zfs destroy -r "
833 "%s' to destroy all datasets in the pool\n"),
835 (void) fprintf(stderr, gettext("use 'zpool destroy %s' "
836 "to destroy the pool itself\n"), zfs_get_name(zhp));
842 * Check for any dependents and/or clones.
844 cb.cb_first = B_TRUE;
845 if (!cb.cb_doclones &&
846 zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent,
854 zfs_iter_dependents(zhp, B_FALSE, destroy_callback, &cb) != 0) {
860 * Do the real thing. The callback will close the handle regardless of
861 * whether it succeeds or not.
863 if (destroy_callback(zhp, &cb) != 0)
866 zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0],
873 * zfs get [-rHp] [-o field[,field]...] [-s source[,source]...]
874 * < all | property[,property]... > < fs | snap | vol > ...
876 * -r recurse over any child datasets
877 * -H scripted mode. Headers are stripped, and fields are separated
878 * by tabs instead of spaces.
879 * -o Set of fields to display. One of "name,property,value,source".
880 * Default is all four.
881 * -s Set of sources to allow. One of
882 * "local,default,inherited,temporary,none". Default is all
884 * -p Display values in parsable (literal) format.
886 * Prints properties for the given datasets. The user can control which
887 * columns to display as well as which property types to allow.
891 * Invoked to display the properties for a single dataset.
894 get_callback(zfs_handle_t *zhp, void *data)
896 char buf[ZFS_MAXPROPLEN];
897 zfs_source_t sourcetype;
898 char source[ZFS_MAXNAMELEN];
899 libzfs_get_cbdata_t *cbp = data;
900 nvlist_t *userprop = zfs_get_user_props(zhp);
901 zfs_proplist_t *pl = cbp->cb_proplist;
906 for (; pl != NULL; pl = pl->pl_next) {
908 * Skip the special fake placeholder. This will also skip over
909 * the name property when 'all' is specified.
911 if (pl->pl_prop == ZFS_PROP_NAME &&
912 pl == cbp->cb_proplist)
915 if (pl->pl_prop != ZFS_PROP_INVAL) {
916 if (zfs_prop_get(zhp, pl->pl_prop, buf,
917 sizeof (buf), &sourcetype, source,
919 cbp->cb_literal) != 0) {
922 if (!zfs_prop_valid_for_type(pl->pl_prop,
924 (void) fprintf(stderr,
925 gettext("No such property '%s'\n"),
926 zfs_prop_to_name(pl->pl_prop));
929 sourcetype = ZFS_SRC_NONE;
930 (void) strlcpy(buf, "-", sizeof (buf));
933 libzfs_print_one_property(zfs_get_name(zhp), cbp,
934 zfs_prop_to_name(pl->pl_prop),
935 buf, sourcetype, source);
937 if (nvlist_lookup_nvlist(userprop,
938 pl->pl_user_prop, &propval) != 0) {
941 sourcetype = ZFS_SRC_NONE;
944 verify(nvlist_lookup_string(propval,
945 ZFS_PROP_VALUE, &strval) == 0);
946 verify(nvlist_lookup_string(propval,
947 ZFS_PROP_SOURCE, &sourceval) == 0);
949 if (strcmp(sourceval,
950 zfs_get_name(zhp)) == 0) {
951 sourcetype = ZFS_SRC_LOCAL;
953 sourcetype = ZFS_SRC_INHERITED;
954 (void) strlcpy(source,
955 sourceval, sizeof (source));
959 libzfs_print_one_property(zfs_get_name(zhp), cbp,
960 pl->pl_user_prop, strval, sourcetype,
969 zfs_do_get(int argc, char **argv)
971 libzfs_get_cbdata_t cb = { 0 };
972 boolean_t recurse = B_FALSE;
974 char *value, *fields;
976 zfs_proplist_t fake_name = { 0 };
979 * Set up default columns and sources.
981 cb.cb_sources = ZFS_SRC_ALL;
982 cb.cb_columns[0] = GET_COL_NAME;
983 cb.cb_columns[1] = GET_COL_PROPERTY;
984 cb.cb_columns[2] = GET_COL_VALUE;
985 cb.cb_columns[3] = GET_COL_SOURCE;
988 while ((c = getopt(argc, argv, ":o:s:rHp")) != -1) {
991 cb.cb_literal = B_TRUE;
997 cb.cb_scripted = B_TRUE;
1000 (void) fprintf(stderr, gettext("missing argument for "
1001 "'%c' option\n"), optopt);
1006 * Process the set of columns to display. We zero out
1007 * the structure to give us a blank slate.
1009 bzero(&cb.cb_columns, sizeof (cb.cb_columns));
1011 while (*optarg != '\0') {
1012 static char *col_subopts[] =
1013 { "name", "property", "value", "source",
1017 (void) fprintf(stderr, gettext("too "
1018 "many fields given to -o "
1023 switch (getsubopt(&optarg, col_subopts,
1026 cb.cb_columns[i++] = GET_COL_NAME;
1029 cb.cb_columns[i++] = GET_COL_PROPERTY;
1032 cb.cb_columns[i++] = GET_COL_VALUE;
1035 cb.cb_columns[i++] = GET_COL_SOURCE;
1038 (void) fprintf(stderr,
1039 gettext("invalid column name "
1048 while (*optarg != '\0') {
1049 static char *source_subopts[] = {
1050 "local", "default", "inherited",
1051 "temporary", "none", NULL };
1053 switch (getsubopt(&optarg, source_subopts,
1056 cb.cb_sources |= ZFS_SRC_LOCAL;
1059 cb.cb_sources |= ZFS_SRC_DEFAULT;
1062 cb.cb_sources |= ZFS_SRC_INHERITED;
1065 cb.cb_sources |= ZFS_SRC_TEMPORARY;
1068 cb.cb_sources |= ZFS_SRC_NONE;
1071 (void) fprintf(stderr,
1072 gettext("invalid source "
1080 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
1090 (void) fprintf(stderr, gettext("missing property "
1097 if (zfs_get_proplist(g_zfs, fields, &cb.cb_proplist) != 0)
1104 * As part of zfs_expand_proplist(), we keep track of the maximum column
1105 * width for each property. For the 'NAME' (and 'SOURCE') columns, we
1106 * need to know the maximum name length. However, the user likely did
1107 * not specify 'name' as one of the properties to fetch, so we need to
1108 * make sure we always include at least this property for
1109 * print_get_headers() to work properly.
1111 if (cb.cb_proplist != NULL) {
1112 fake_name.pl_prop = ZFS_PROP_NAME;
1113 fake_name.pl_width = strlen(gettext("NAME"));
1114 fake_name.pl_next = cb.cb_proplist;
1115 cb.cb_proplist = &fake_name;
1118 cb.cb_first = B_TRUE;
1120 /* run for each object */
1121 ret = zfs_for_each(argc, argv, recurse, ZFS_TYPE_ANY, NULL,
1122 &cb.cb_proplist, get_callback, &cb, B_FALSE);
1124 if (cb.cb_proplist == &fake_name)
1125 zfs_free_proplist(fake_name.pl_next);
1127 zfs_free_proplist(cb.cb_proplist);
1133 * inherit [-r] <property> <fs|vol> ...
1135 * -r Recurse over all children
1137 * For each dataset specified on the command line, inherit the given property
1138 * from its parent. Inheriting a property at the pool level will cause it to
1139 * use the default value. The '-r' flag will recurse over all children, and is
1140 * useful for setting a property on a hierarchy-wide basis, regardless of any
1141 * local modifications for each dataset.
1143 typedef struct inherit_cbdata {
1145 boolean_t cb_any_successful;
1149 inherit_callback(zfs_handle_t *zhp, void *data)
1151 inherit_cbdata_t *cbp = data;
1154 ret = zfs_prop_inherit(zhp, cbp->cb_propname);
1156 cbp->cb_any_successful = B_TRUE;
1161 zfs_do_inherit(int argc, char **argv)
1163 boolean_t recurse = B_FALSE;
1166 inherit_cbdata_t cb;
1170 while ((c = getopt(argc, argv, "r")) != -1) {
1177 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
1186 /* check number of arguments */
1188 (void) fprintf(stderr, gettext("missing property argument\n"));
1192 (void) fprintf(stderr, gettext("missing dataset argument\n"));
1196 cb.cb_propname = argv[0];
1200 if ((prop = zfs_name_to_prop(cb.cb_propname)) != ZFS_PROP_INVAL) {
1201 if (zfs_prop_readonly(prop)) {
1202 (void) fprintf(stderr, gettext(
1203 "%s property is read-only\n"),
1207 if (!zfs_prop_inheritable(prop)) {
1208 (void) fprintf(stderr, gettext("'%s' property cannot "
1209 "be inherited\n"), cb.cb_propname);
1210 if (prop == ZFS_PROP_QUOTA ||
1211 prop == ZFS_PROP_RESERVATION)
1212 (void) fprintf(stderr, gettext("use 'zfs set "
1213 "%s=none' to clear\n"), cb.cb_propname);
1216 } else if (!zfs_prop_user(cb.cb_propname)) {
1217 (void) fprintf(stderr, gettext(
1218 "invalid property '%s'\n"),
1223 cb.cb_any_successful = B_FALSE;
1225 ret = zfs_for_each(argc, argv, recurse,
1226 ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, NULL,
1227 inherit_callback, &cb, B_FALSE);
1229 if (cb.cb_any_successful) {
1230 zpool_log_history(g_zfs, argc + optind + 1, argv - optind - 1,
1231 argv[0], B_FALSE, B_FALSE);
1238 * list [-rH] [-o property[,property]...] [-t type[,type]...]
1239 * [-s property [-s property]...] [-S property [-S property]...]
1242 * -r Recurse over all children
1243 * -H Scripted mode; elide headers and separate colums by tabs
1244 * -o Control which fields to display.
1245 * -t Control which object types to display.
1246 * -s Specify sort columns, descending order.
1247 * -S Specify sort columns, ascending order.
1249 * When given no arguments, lists all filesystems in the system.
1250 * Otherwise, list the specified datasets, optionally recursing down them if
1251 * '-r' is specified.
1253 typedef struct list_cbdata {
1255 boolean_t cb_scripted;
1256 zfs_proplist_t *cb_proplist;
1260 * Given a list of columns to display, output appropriate headers for each one.
1263 print_header(zfs_proplist_t *pl)
1265 char headerbuf[ZFS_MAXPROPLEN];
1268 boolean_t first = B_TRUE;
1269 boolean_t right_justify;
1271 for (; pl != NULL; pl = pl->pl_next) {
1278 right_justify = B_FALSE;
1279 if (pl->pl_prop != ZFS_PROP_INVAL) {
1280 header = zfs_prop_column_name(pl->pl_prop);
1281 right_justify = zfs_prop_align_right(pl->pl_prop);
1283 for (i = 0; pl->pl_user_prop[i] != '\0'; i++)
1284 headerbuf[i] = toupper(pl->pl_user_prop[i]);
1285 headerbuf[i] = '\0';
1289 if (pl->pl_next == NULL && !right_justify)
1290 (void) printf("%s", header);
1291 else if (right_justify)
1292 (void) printf("%*s", pl->pl_width, header);
1294 (void) printf("%-*s", pl->pl_width, header);
1297 (void) printf("\n");
1301 * Given a dataset and a list of fields, print out all the properties according
1302 * to the described layout.
1305 print_dataset(zfs_handle_t *zhp, zfs_proplist_t *pl, int scripted)
1307 boolean_t first = B_TRUE;
1308 char property[ZFS_MAXPROPLEN];
1309 nvlist_t *userprops = zfs_get_user_props(zhp);
1312 boolean_t right_justify;
1315 for (; pl != NULL; pl = pl->pl_next) {
1318 (void) printf("\t");
1325 right_justify = B_FALSE;
1326 if (pl->pl_prop != ZFS_PROP_INVAL) {
1327 if (zfs_prop_get(zhp, pl->pl_prop, property,
1328 sizeof (property), NULL, NULL, 0, B_FALSE) != 0)
1333 right_justify = zfs_prop_align_right(pl->pl_prop);
1335 if (nvlist_lookup_nvlist(userprops,
1336 pl->pl_user_prop, &propval) != 0)
1339 verify(nvlist_lookup_string(propval,
1340 ZFS_PROP_VALUE, &propstr) == 0);
1343 width = pl->pl_width;
1346 * If this is being called in scripted mode, or if this is the
1347 * last column and it is left-justified, don't include a width
1350 if (scripted || (pl->pl_next == NULL && !right_justify))
1351 (void) printf("%s", propstr);
1352 else if (right_justify)
1353 (void) printf("%*s", width, propstr);
1355 (void) printf("%-*s", width, propstr);
1358 (void) printf("\n");
1362 * Generic callback function to list a dataset or snapshot.
1365 list_callback(zfs_handle_t *zhp, void *data)
1367 list_cbdata_t *cbp = data;
1369 if (cbp->cb_first) {
1370 if (!cbp->cb_scripted)
1371 print_header(cbp->cb_proplist);
1372 cbp->cb_first = B_FALSE;
1375 print_dataset(zhp, cbp->cb_proplist, cbp->cb_scripted);
1381 zfs_do_list(int argc, char **argv)
1384 boolean_t recurse = B_FALSE;
1385 boolean_t scripted = B_FALSE;
1386 static char default_fields[] =
1387 "name,used,available,referenced,mountpoint";
1388 int types = ZFS_TYPE_ANY;
1389 char *fields = NULL;
1390 char *basic_fields = default_fields;
1391 list_cbdata_t cb = { 0 };
1394 char *type_subopts[] = { "filesystem", "volume", "snapshot", NULL };
1395 zfs_sort_column_t *sortcol = NULL;
1398 while ((c = getopt(argc, argv, ":o:rt:Hs:S:")) != -1) {
1410 if (zfs_add_sort_column(&sortcol, optarg,
1412 (void) fprintf(stderr,
1413 gettext("invalid property '%s'\n"), optarg);
1418 if (zfs_add_sort_column(&sortcol, optarg,
1420 (void) fprintf(stderr,
1421 gettext("invalid property '%s'\n"), optarg);
1427 while (*optarg != '\0') {
1428 switch (getsubopt(&optarg, type_subopts,
1431 types |= ZFS_TYPE_FILESYSTEM;
1434 types |= ZFS_TYPE_VOLUME;
1437 types |= ZFS_TYPE_SNAPSHOT;
1440 (void) fprintf(stderr,
1441 gettext("invalid type '%s'\n"),
1448 (void) fprintf(stderr, gettext("missing argument for "
1449 "'%c' option\n"), optopt);
1453 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
1463 fields = basic_fields;
1466 * If the user specifies '-o all', the zfs_get_proplist() doesn't
1467 * normally include the name of the dataset. For 'zfs list', we always
1468 * want this property to be first.
1470 if (zfs_get_proplist(g_zfs, fields, &cb.cb_proplist) != 0)
1473 cb.cb_scripted = scripted;
1474 cb.cb_first = B_TRUE;
1476 ret = zfs_for_each(argc, argv, recurse, types, sortcol, &cb.cb_proplist,
1477 list_callback, &cb, B_TRUE);
1479 zfs_free_proplist(cb.cb_proplist);
1480 zfs_free_sort_columns(sortcol);
1482 if (ret == 0 && cb.cb_first)
1483 (void) printf(gettext("no datasets available\n"));
1489 * zfs rename [-r] <fs | snap | vol> <fs | snap | vol>
1491 * Renames the given dataset to another of the same type.
1495 zfs_do_rename(int argc, char **argv)
1503 while ((c = getopt(argc, argv, "r")) != -1) {
1510 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
1519 /* check number of arguments */
1521 (void) fprintf(stderr, gettext("missing source dataset "
1526 (void) fprintf(stderr, gettext("missing target dataset "
1531 (void) fprintf(stderr, gettext("too many arguments\n"));
1535 if (recurse && strchr(argv[0], '@') == 0) {
1536 (void) fprintf(stderr, gettext("source dataset for recursive "
1537 "rename must be a snapshot\n"));
1541 if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL)
1544 ret = (zfs_rename(zhp, argv[1], recurse) != 0);
1547 zpool_log_history(g_zfs, argc + optind, argv - optind, argv[1],
1557 * Promotes the given clone fs to be the parent
1561 zfs_do_promote(int argc, char **argv)
1567 if (argc > 1 && argv[1][0] == '-') {
1568 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
1573 /* check number of arguments */
1575 (void) fprintf(stderr, gettext("missing clone filesystem"
1580 (void) fprintf(stderr, gettext("too many arguments\n"));
1584 zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
1588 ret = (zfs_promote(zhp) != 0);
1591 zpool_log_history(g_zfs, argc, argv, argv[1], B_FALSE, B_FALSE);
1598 * zfs rollback [-rfR] <snapshot>
1600 * -r Delete any intervening snapshots before doing rollback
1601 * -R Delete any snapshots and their clones
1602 * -f Force unmount filesystems, even if they are in use.
1604 * Given a filesystem, rollback to a specific snapshot, discarding any changes
1605 * since then and making it the active dataset. If more recent snapshots exist,
1606 * the command will complain unless the '-r' flag is given.
1608 typedef struct rollback_cbdata {
1614 boolean_t cb_recurse;
1615 boolean_t cb_dependent;
1616 } rollback_cbdata_t;
1619 * Report any snapshots more recent than the one specified. Used when '-r' is
1620 * not specified. We reuse this same callback for the snapshot dependents - if
1621 * 'cb_dependent' is set, then this is a dependent and we should report it
1622 * without checking the transaction group.
1625 rollback_check(zfs_handle_t *zhp, void *data)
1627 rollback_cbdata_t *cbp = data;
1629 if (cbp->cb_doclones) {
1634 if (!cbp->cb_dependent) {
1635 if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 &&
1636 zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
1637 zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
1640 if (cbp->cb_first && !cbp->cb_recurse) {
1641 (void) fprintf(stderr, gettext("cannot "
1642 "rollback to '%s': more recent snapshots "
1645 (void) fprintf(stderr, gettext("use '-r' to "
1646 "force deletion of the following "
1652 if (cbp->cb_recurse) {
1653 cbp->cb_dependent = B_TRUE;
1654 if (zfs_iter_dependents(zhp, B_TRUE,
1655 rollback_check, cbp) != 0) {
1659 cbp->cb_dependent = B_FALSE;
1661 (void) fprintf(stderr, "%s\n",
1666 if (cbp->cb_first && cbp->cb_recurse) {
1667 (void) fprintf(stderr, gettext("cannot rollback to "
1668 "'%s': clones of previous snapshots exist\n"),
1670 (void) fprintf(stderr, gettext("use '-R' to "
1671 "force deletion of the following clones and "
1677 (void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
1685 zfs_do_rollback(int argc, char **argv)
1689 rollback_cbdata_t cb = { 0 };
1690 zfs_handle_t *zhp, *snap;
1691 char parentname[ZFS_MAXNAMELEN];
1696 while ((c = getopt(argc, argv, "rfR")) != -1) {
1709 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
1718 /* check number of arguments */
1720 (void) fprintf(stderr, gettext("missing dataset argument\n"));
1724 (void) fprintf(stderr, gettext("too many arguments\n"));
1728 /* open the snapshot */
1729 if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL)
1732 /* open the parent dataset */
1733 (void) strlcpy(parentname, argv[0], sizeof (parentname));
1734 verify((delim = strrchr(parentname, '@')) != NULL);
1736 if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_ANY)) == NULL) {
1742 * Check for more recent snapshots and/or clones based on the presence
1745 cb.cb_target = argv[0];
1746 cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
1747 cb.cb_first = B_TRUE;
1749 if ((ret = zfs_iter_children(zhp, rollback_check, &cb)) != 0)
1752 if ((ret = cb.cb_error) != 0)
1756 * Rollback parent to the given snapshot.
1758 ret = zfs_rollback(zhp, snap, force);
1761 zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0],
1776 * zfs set property=value { fs | snap | vol } ...
1778 * Sets the given property for all datasets specified on the command line.
1780 typedef struct set_cbdata {
1783 boolean_t cb_any_successful;
1787 set_callback(zfs_handle_t *zhp, void *data)
1789 set_cbdata_t *cbp = data;
1791 if (zfs_prop_set(zhp, cbp->cb_propname, cbp->cb_value) != 0) {
1792 switch (libzfs_errno(g_zfs)) {
1793 case EZFS_MOUNTFAILED:
1794 (void) fprintf(stderr, gettext("property may be set "
1795 "but unable to remount filesystem\n"));
1797 case EZFS_SHARENFSFAILED:
1798 (void) fprintf(stderr, gettext("property may be set "
1799 "but unable to reshare filesystem\n"));
1804 cbp->cb_any_successful = B_TRUE;
1809 zfs_do_set(int argc, char **argv)
1814 /* check for options */
1815 if (argc > 1 && argv[1][0] == '-') {
1816 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
1821 /* check number of arguments */
1823 (void) fprintf(stderr, gettext("missing property=value "
1828 (void) fprintf(stderr, gettext("missing dataset name\n"));
1832 /* validate property=value argument */
1833 cb.cb_propname = argv[1];
1834 if ((cb.cb_value = strchr(cb.cb_propname, '=')) == NULL) {
1835 (void) fprintf(stderr, gettext("missing value in "
1836 "property=value argument\n"));
1840 *cb.cb_value = '\0';
1842 cb.cb_any_successful = B_FALSE;
1844 if (*cb.cb_propname == '\0') {
1845 (void) fprintf(stderr,
1846 gettext("missing property in property=value argument\n"));
1850 ret = zfs_for_each(argc - 2, argv + 2, B_FALSE,
1851 ZFS_TYPE_ANY, NULL, NULL, set_callback, &cb, B_FALSE);
1853 if (cb.cb_any_successful) {
1854 *(cb.cb_value - 1) = '=';
1855 zpool_log_history(g_zfs, argc, argv, argv[2], B_FALSE, B_FALSE);
1862 * zfs snapshot [-r] <fs@snap>
1864 * Creates a snapshot with the given name. While functionally equivalent to
1865 * 'zfs create', it is a separate command to diffferentiate intent.
1868 zfs_do_snapshot(int argc, char **argv)
1870 int recursive = B_FALSE;
1875 while ((c = getopt(argc, argv, ":r")) != -1) {
1881 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
1890 /* check number of arguments */
1892 (void) fprintf(stderr, gettext("missing snapshot argument\n"));
1896 (void) fprintf(stderr, gettext("too many arguments\n"));
1900 ret = zfs_snapshot(g_zfs, argv[0], recursive);
1901 if (ret && recursive)
1902 (void) fprintf(stderr, gettext("no snapshots were created\n"));
1904 zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0],
1911 * zfs send [-i <@snap>] <fs@snap>
1913 * Send a backup stream to stdout.
1916 zfs_do_send(int argc, char **argv)
1918 char *fromname = NULL;
1924 while ((c = getopt(argc, argv, ":i:")) != -1) {
1932 (void) fprintf(stderr, gettext("missing argument for "
1933 "'%c' option\n"), optopt);
1937 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
1946 /* check number of arguments */
1948 (void) fprintf(stderr, gettext("missing snapshot argument\n"));
1952 (void) fprintf(stderr, gettext("too many arguments\n"));
1956 if (isatty(STDOUT_FILENO)) {
1957 (void) fprintf(stderr,
1958 gettext("Error: Stream can not be written to a terminal.\n"
1959 "You must redirect standard output.\n"));
1963 if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL)
1967 * If they specified the full path to the snapshot, chop off
1968 * everything except the short name of the snapshot.
1970 if (fromname && (cp = strchr(fromname, '@')) != NULL) {
1971 if (cp != fromname &&
1972 strncmp(argv[0], fromname, cp - fromname + 1)) {
1973 (void) fprintf(stderr,
1974 gettext("incremental source must be "
1975 "in same filesystem\n"));
1979 if (strchr(fromname, '@') || strchr(fromname, '/')) {
1980 (void) fprintf(stderr,
1981 gettext("invalid incremental source\n"));
1986 err = zfs_send(zhp, fromname, STDOUT_FILENO);
1993 * zfs receive <fs@snap>
1995 * Restore a backup stream from stdin.
1998 zfs_do_receive(int argc, char **argv)
2001 boolean_t isprefix = B_FALSE;
2002 boolean_t dryrun = B_FALSE;
2003 boolean_t verbose = B_FALSE;
2004 boolean_t force = B_FALSE;
2007 while ((c = getopt(argc, argv, ":dnvF")) != -1) {
2022 (void) fprintf(stderr, gettext("missing argument for "
2023 "'%c' option\n"), optopt);
2027 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
2036 /* check number of arguments */
2038 (void) fprintf(stderr, gettext("missing snapshot argument\n"));
2042 (void) fprintf(stderr, gettext("too many arguments\n"));
2046 if (isatty(STDIN_FILENO)) {
2047 (void) fprintf(stderr,
2048 gettext("Error: Backup stream can not be read "
2049 "from a terminal.\n"
2050 "You must redirect standard input.\n"));
2054 err = zfs_receive(g_zfs, argv[0], isprefix, verbose, dryrun, force,
2058 zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0],
2065 typedef struct get_all_cbdata {
2066 zfs_handle_t **cb_handles;
2073 get_one_dataset(zfs_handle_t *zhp, void *data)
2075 get_all_cbdata_t *cbp = data;
2076 zfs_type_t type = zfs_get_type(zhp);
2079 * Interate over any nested datasets.
2081 if (type == ZFS_TYPE_FILESYSTEM &&
2082 zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) {
2088 * Skip any datasets whose type does not match.
2090 if ((type & cbp->cb_types) == 0) {
2095 if (cbp->cb_alloc == cbp->cb_used) {
2096 zfs_handle_t **handles;
2098 if (cbp->cb_alloc == 0)
2103 handles = safe_malloc(cbp->cb_alloc * sizeof (void *));
2105 if (cbp->cb_handles) {
2106 bcopy(cbp->cb_handles, handles,
2107 cbp->cb_used * sizeof (void *));
2108 free(cbp->cb_handles);
2111 cbp->cb_handles = handles;
2114 cbp->cb_handles[cbp->cb_used++] = zhp;
2120 get_all_datasets(uint_t types, zfs_handle_t ***dslist, size_t *count)
2122 get_all_cbdata_t cb = { 0 };
2123 cb.cb_types = types;
2125 (void) zfs_iter_root(g_zfs, get_one_dataset, &cb);
2127 *dslist = cb.cb_handles;
2128 *count = cb.cb_used;
2132 dataset_cmp(const void *a, const void *b)
2134 zfs_handle_t **za = (zfs_handle_t **)a;
2135 zfs_handle_t **zb = (zfs_handle_t **)b;
2136 char mounta[MAXPATHLEN];
2137 char mountb[MAXPATHLEN];
2138 boolean_t gota, gotb;
2140 if ((gota = (zfs_get_type(*za) == ZFS_TYPE_FILESYSTEM)) != 0)
2141 verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta,
2142 sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
2143 if ((gotb = (zfs_get_type(*zb) == ZFS_TYPE_FILESYSTEM)) != 0)
2144 verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb,
2145 sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
2148 return (strcmp(mounta, mountb));
2155 return (strcmp(zfs_get_name(a), zfs_get_name(b)));
2159 * Generic callback for sharing or mounting filesystems. Because the code is so
2160 * similar, we have a common function with an extra parameter to determine which
2161 * mode we are using.
2163 #define OP_SHARE 0x1
2164 #define OP_MOUNT 0x2
2167 * Share or mount a dataset.
2170 share_mount_one(zfs_handle_t *zhp, int op, int flags, boolean_t explicit,
2171 const char *options)
2173 char mountpoint[ZFS_MAXPROPLEN];
2174 char shareopts[ZFS_MAXPROPLEN];
2175 const char *cmdname = op == OP_SHARE ? "share" : "mount";
2177 uint64_t zoned, canmount;
2178 zfs_type_t type = zfs_get_type(zhp);
2180 assert(type & (ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME));
2182 if (type == ZFS_TYPE_FILESYSTEM) {
2184 * Check to make sure we can mount/share this dataset. If we
2185 * are in the global zone and the filesystem is exported to a
2186 * local zone, or if we are in a local zone and the
2187 * filesystem is not exported, then it is an error.
2189 zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
2191 if (zoned && getzoneid() == GLOBAL_ZONEID) {
2195 (void) fprintf(stderr, gettext("cannot %s '%s': "
2196 "dataset is exported to a local zone\n"), cmdname,
2200 } else if (!zoned && getzoneid() != GLOBAL_ZONEID) {
2204 (void) fprintf(stderr, gettext("cannot %s '%s': "
2205 "permission denied\n"), cmdname,
2211 * Ignore any filesystems which don't apply to us. This
2212 * includes those with a legacy mountpoint, or those with
2213 * legacy share options.
2215 verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2216 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0);
2217 verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts,
2218 sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0);
2219 canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);
2221 if (op == OP_SHARE && strcmp(shareopts, "off") == 0) {
2225 (void) fprintf(stderr, gettext("cannot share '%s': "
2226 "legacy share\n"), zfs_get_name(zhp));
2227 (void) fprintf(stderr, gettext("use share(1M) to "
2228 "share this filesystem\n"));
2233 * We cannot share or mount legacy filesystems. If the
2234 * shareopts is non-legacy but the mountpoint is legacy, we
2235 * treat it as a legacy share.
2237 if (strcmp(mountpoint, "legacy") == 0) {
2241 (void) fprintf(stderr, gettext("cannot %s '%s': "
2242 "legacy mountpoint\n"), cmdname, zfs_get_name(zhp));
2243 (void) fprintf(stderr, gettext("use %s to "
2244 "%s this filesystem\n"), op == OP_SHARE ?
2245 "share(1M)" : "mount(1M)", cmdname);
2249 if (strcmp(mountpoint, "none") == 0) {
2253 (void) fprintf(stderr, gettext("cannot %s '%s': no "
2254 "mountpoint set\n"), cmdname, zfs_get_name(zhp));
2262 (void) fprintf(stderr, gettext("cannot %s '%s': "
2263 "'canmount' property is set to 'off'\n"), cmdname,
2269 * At this point, we have verified that the mountpoint and/or
2270 * shareopts are appropriate for auto management. If the
2271 * filesystem is already mounted or shared, return (failing
2272 * for explicit requests); otherwise mount or share the
2277 if (zfs_is_shared_nfs(zhp, NULL)) {
2281 (void) fprintf(stderr, gettext("cannot share "
2282 "'%s': filesystem already shared\n"),
2287 if (!zfs_is_mounted(zhp, NULL) &&
2288 zfs_mount(zhp, NULL, 0) != 0)
2291 if (zfs_share_nfs(zhp) != 0)
2296 if (options == NULL)
2297 mnt.mnt_mntopts = "";
2299 mnt.mnt_mntopts = (char *)options;
2301 if (!hasmntopt(&mnt, MNTOPT_REMOUNT) &&
2302 zfs_is_mounted(zhp, NULL)) {
2306 (void) fprintf(stderr, gettext("cannot mount "
2307 "'%s': filesystem already mounted\n"),
2312 if (zfs_mount(zhp, options, flags) != 0)
2317 assert(op == OP_SHARE);
2320 * Ignore any volumes that aren't shared.
2322 verify(zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, shareopts,
2323 sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0);
2325 if (strcmp(shareopts, "off") == 0) {
2329 (void) fprintf(stderr, gettext("cannot share '%s': "
2330 "'shareiscsi' property not set\n"),
2332 (void) fprintf(stderr, gettext("set 'shareiscsi' "
2333 "property or use iscsitadm(1M) to share this "
2338 if (zfs_is_shared_iscsi(zhp)) {
2342 (void) fprintf(stderr, gettext("cannot share "
2343 "'%s': volume already shared\n"),
2348 if (zfs_share_iscsi(zhp) != 0)
2356 share_mount(int op, int argc, char **argv)
2360 const char *options = NULL;
2361 int types, flags = 0;
2364 while ((c = getopt(argc, argv, op == OP_MOUNT ? ":ao:O" : "a"))
2374 warnx("no overlay mounts support on FreeBSD, ignoring");
2377 (void) fprintf(stderr, gettext("missing argument for "
2378 "'%c' option\n"), optopt);
2382 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
2391 /* check number of arguments */
2393 zfs_handle_t **dslist = NULL;
2394 size_t i, count = 0;
2396 if (op == OP_MOUNT) {
2397 types = ZFS_TYPE_FILESYSTEM;
2398 } else if (argc > 0) {
2399 if (strcmp(argv[0], "nfs") == 0) {
2400 types = ZFS_TYPE_FILESYSTEM;
2401 } else if (strcmp(argv[0], "iscsi") == 0) {
2402 types = ZFS_TYPE_VOLUME;
2404 (void) fprintf(stderr, gettext("share type "
2405 "must be 'nfs' or 'iscsi'\n"));
2412 types = ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;
2416 (void) fprintf(stderr, gettext("too many arguments\n"));
2420 get_all_datasets(types, &dslist, &count);
2425 qsort(dslist, count, sizeof (void *), dataset_cmp);
2427 for (i = 0; i < count; i++) {
2428 if (share_mount_one(dslist[i], op, flags, B_FALSE,
2431 zfs_close(dslist[i]);
2435 } else if (argc == 0) {
2439 if (op == OP_SHARE) {
2440 (void) fprintf(stderr, gettext("missing filesystem "
2446 * When mount is given no arguments, go through /etc/mnttab and
2447 * display any active ZFS mounts. We hide any snapshots, since
2448 * they are controlled automatically.
2450 if ((n = getmntinfo(&sfs, MNT_WAIT)) == 0) {
2451 fprintf(stderr, "getmntinfo(): %s\n", strerror(errno));
2454 for (i = 0; i < n; i++) {
2455 if (strcmp(sfs[i].f_fstypename, MNTTYPE_ZFS) != 0 ||
2456 strchr(sfs[i].f_mntfromname, '@') != NULL)
2459 (void) printf("%-30s %s\n", sfs[i].f_mntfromname,
2460 sfs[i].f_mntonname);
2466 types = ZFS_TYPE_FILESYSTEM;
2468 types |= ZFS_TYPE_VOLUME;
2471 (void) fprintf(stderr,
2472 gettext("too many arguments\n"));
2476 if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL) {
2479 ret = share_mount_one(zhp, op, flags, B_TRUE,
2489 * zfs mount -a [nfs | iscsi]
2490 * zfs mount filesystem
2492 * Mount all filesystems, or mount the given filesystem.
2495 zfs_do_mount(int argc, char **argv)
2497 return (share_mount(OP_MOUNT, argc, argv));
2501 * zfs share -a [nfs | iscsi]
2502 * zfs share filesystem
2504 * Share all filesystems, or share the given filesystem.
2507 zfs_do_share(int argc, char **argv)
2509 return (share_mount(OP_SHARE, argc, argv));
2512 typedef struct unshare_unmount_node {
2513 zfs_handle_t *un_zhp;
2515 uu_avl_node_t un_avlnode;
2516 } unshare_unmount_node_t;
2520 unshare_unmount_compare(const void *larg, const void *rarg, void *unused)
2522 const unshare_unmount_node_t *l = larg;
2523 const unshare_unmount_node_t *r = rarg;
2525 return (strcmp(l->un_mountp, r->un_mountp));
2529 * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an
2530 * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem,
2531 * and unmount it appropriately.
2534 unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
2538 struct mnttab search = { 0 }, entry;
2539 const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount";
2540 char property[ZFS_MAXPROPLEN];
2543 * Search for the given (major,minor) pair in the mount table.
2545 search.mnt_mountp = path;
2546 rewind(mnttab_file);
2547 if (getmntany(mnttab_file, &entry, &search) != 0) {
2548 (void) fprintf(stderr, gettext("cannot %s '%s': not "
2549 "currently mounted\n"), cmdname, path);
2553 if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) {
2554 (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS "
2555 "filesystem\n"), cmdname, path);
2559 if ((zhp = zfs_open(g_zfs, entry.mnt_special,
2560 ZFS_TYPE_FILESYSTEM)) == NULL)
2563 verify(zfs_prop_get(zhp, op == OP_SHARE ?
2564 ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property,
2565 sizeof (property), NULL, NULL, 0, B_FALSE) == 0);
2567 if (op == OP_SHARE) {
2568 if (strcmp(property, "off") == 0) {
2569 (void) fprintf(stderr, gettext("cannot unshare "
2570 "'%s': legacy share\n"), path);
2571 (void) fprintf(stderr, gettext("use "
2572 "unshare(1M) to unshare this filesystem\n"));
2574 } else if (!zfs_is_shared_nfs(zhp, NULL)) {
2575 (void) fprintf(stderr, gettext("cannot unshare '%s': "
2576 "not currently shared\n"), path);
2579 ret = zfs_unshareall_nfs(zhp);
2583 ret = zfs_unmount(zhp, NULL, flags);
2584 } else if (strcmp(property, "legacy") == 0) {
2585 (void) fprintf(stderr, gettext("cannot unmount "
2586 "'%s': legacy mountpoint\n"),
2588 (void) fprintf(stderr, gettext("use umount(1M) "
2589 "to unmount this filesystem\n"));
2592 ret = zfs_unmountall(zhp, flags);
2602 * Generic callback for unsharing or unmounting a filesystem.
2605 unshare_unmount(int op, int argc, char **argv)
2612 char property[ZFS_MAXPROPLEN];
2615 while ((c = getopt(argc, argv, op == OP_SHARE ? "a" : "af")) != -1) {
2624 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
2635 * We could make use of zfs_for_each() to walk all datasets in
2636 * the system, but this would be very inefficient, especially
2637 * since we would have to linearly search /etc/mnttab for each
2638 * one. Instead, do one pass through /etc/mnttab looking for
2639 * zfs entries and call zfs_unmount() for each one.
2641 * Things get a little tricky if the administrator has created
2642 * mountpoints beneath other ZFS filesystems. In this case, we
2643 * have to unmount the deepest filesystems first. To accomplish
2644 * this, we place all the mountpoints in an AVL tree sorted by
2645 * the special type (dataset name), and walk the result in
2646 * reverse to make sure to get any snapshots first.
2648 uu_avl_pool_t *pool;
2650 unshare_unmount_node_t *node;
2652 uu_avl_walk_t *walk;
2657 (void) fprintf(stderr, gettext("too many arguments\n"));
2661 if ((pool = uu_avl_pool_create("unmount_pool",
2662 sizeof (unshare_unmount_node_t),
2663 offsetof(unshare_unmount_node_t, un_avlnode),
2664 unshare_unmount_compare,
2665 UU_DEFAULT)) == NULL) {
2666 (void) fprintf(stderr, gettext("internal error: "
2667 "out of memory\n"));
2671 if ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL) {
2672 (void) fprintf(stderr, gettext("internal error: "
2673 "out of memory\n"));
2677 if ((n = getmntinfo(&sfs, MNT_WAIT)) == 0) {
2678 (void) fprintf(stderr, gettext("internal error: "
2679 "getmntinfo() failed\n"));
2682 for (i = 0; i < n; i++) {
2684 /* ignore non-ZFS entries */
2685 if (strcmp(sfs[i].f_fstypename, MNTTYPE_ZFS) != 0)
2688 /* ignore snapshots */
2689 if (strchr(sfs[i].f_mntfromname, '@') != NULL)
2692 if ((zhp = zfs_open(g_zfs, sfs[i].f_mntfromname,
2693 ZFS_TYPE_FILESYSTEM)) == NULL) {
2698 verify(zfs_prop_get(zhp, op == OP_SHARE ?
2699 ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT,
2700 property, sizeof (property), NULL, NULL,
2703 /* Ignore legacy mounts and shares */
2704 if ((op == OP_SHARE &&
2705 strcmp(property, "off") == 0) ||
2707 strcmp(property, "legacy") == 0)) {
2712 node = safe_malloc(sizeof (unshare_unmount_node_t));
2715 if ((node->un_mountp = strdup(sfs[i].f_mntonname)) ==
2717 (void) fprintf(stderr, gettext("internal error:"
2718 " out of memory\n"));
2722 uu_avl_node_init(node, &node->un_avlnode, pool);
2724 if (uu_avl_find(tree, node, NULL, &idx) == NULL) {
2725 uu_avl_insert(tree, node, idx);
2727 zfs_close(node->un_zhp);
2728 free(node->un_mountp);
2734 * Walk the AVL tree in reverse, unmounting each filesystem and
2735 * removing it from the AVL tree in the process.
2737 if ((walk = uu_avl_walk_start(tree,
2738 UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL) {
2739 (void) fprintf(stderr,
2740 gettext("internal error: out of memory"));
2744 while ((node = uu_avl_walk_next(walk)) != NULL) {
2745 uu_avl_remove(tree, node);
2749 if (zfs_unshare_nfs(node->un_zhp,
2750 node->un_mountp) != 0)
2755 if (zfs_unmount(node->un_zhp,
2756 node->un_mountp, flags) != 0)
2761 zfs_close(node->un_zhp);
2762 free(node->un_mountp);
2766 uu_avl_walk_end(walk);
2767 uu_avl_destroy(tree);
2768 uu_avl_pool_destroy(pool);
2770 if (op == OP_SHARE) {
2772 * Finally, unshare any volumes shared via iSCSI.
2774 zfs_handle_t **dslist = NULL;
2775 size_t i, count = 0;
2777 get_all_datasets(ZFS_TYPE_VOLUME, &dslist, &count);
2780 qsort(dslist, count, sizeof (void *),
2783 for (i = 0; i < count; i++) {
2784 if (zfs_unshare_iscsi(dslist[i]) != 0)
2786 zfs_close(dslist[i]);
2795 (void) fprintf(stderr,
2796 gettext("missing filesystem argument\n"));
2798 (void) fprintf(stderr,
2799 gettext("too many arguments\n"));
2804 * We have an argument, but it may be a full path or a ZFS
2805 * filesystem. Pass full paths off to unmount_path() (shared by
2806 * manual_unmount), otherwise open the filesystem and pass to
2809 if (argv[0][0] == '/')
2810 return (unshare_unmount_path(op, argv[0],
2813 types = ZFS_TYPE_FILESYSTEM;
2815 types |= ZFS_TYPE_VOLUME;
2817 if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL)
2820 if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
2821 verify(zfs_prop_get(zhp, op == OP_SHARE ?
2822 ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property,
2823 sizeof (property), NULL, NULL, 0, B_FALSE) == 0);
2827 if (strcmp(property, "off") == 0) {
2828 (void) fprintf(stderr, gettext("cannot "
2829 "unshare '%s': legacy share\n"),
2831 (void) fprintf(stderr, gettext("use "
2832 "unshare(1M) to unshare this "
2835 } else if (!zfs_is_shared_nfs(zhp, NULL)) {
2836 (void) fprintf(stderr, gettext("cannot "
2837 "unshare '%s': not currently "
2838 "shared\n"), zfs_get_name(zhp));
2840 } else if (zfs_unshareall_nfs(zhp) != 0) {
2846 if (strcmp(property, "legacy") == 0) {
2847 (void) fprintf(stderr, gettext("cannot "
2848 "unmount '%s': legacy "
2849 "mountpoint\n"), zfs_get_name(zhp));
2850 (void) fprintf(stderr, gettext("use "
2851 "umount(1M) to unmount this "
2854 } else if (!zfs_is_mounted(zhp, NULL)) {
2855 (void) fprintf(stderr, gettext("cannot "
2856 "unmount '%s': not currently "
2860 } else if (zfs_unmountall(zhp, flags) != 0) {
2866 assert(op == OP_SHARE);
2868 verify(zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, property,
2869 sizeof (property), NULL, NULL, 0, B_FALSE) == 0);
2871 if (strcmp(property, "off") == 0) {
2872 (void) fprintf(stderr, gettext("cannot unshare "
2873 "'%s': 'shareiscsi' property not set\n"),
2875 (void) fprintf(stderr, gettext("set "
2876 "'shareiscsi' property or use "
2877 "iscsitadm(1M) to share this volume\n"));
2879 } else if (!zfs_is_shared_iscsi(zhp)) {
2880 (void) fprintf(stderr, gettext("cannot "
2881 "unshare '%s': not currently shared\n"),
2884 } else if (zfs_unshare_iscsi(zhp) != 0) {
2897 * zfs unmount filesystem
2899 * Unmount all filesystems, or a specific ZFS filesystem.
2902 zfs_do_unmount(int argc, char **argv)
2904 return (unshare_unmount(OP_MOUNT, argc, argv));
2909 * zfs unshare filesystem
2911 * Unshare all filesystems, or a specific ZFS filesystem.
2914 zfs_do_unshare(int argc, char **argv)
2916 return (unshare_unmount(OP_SHARE, argc, argv));
2920 * Attach/detach the given dataset to/from the given jail
2924 do_jail(int argc, char **argv, int attach)
2929 /* check number of arguments */
2931 (void) fprintf(stderr, gettext("missing argument(s)\n"));
2935 (void) fprintf(stderr, gettext("too many arguments\n"));
2939 jailid = atoi(argv[1]);
2941 (void) fprintf(stderr, gettext("invalid jailid\n"));
2945 zhp = zfs_open(g_zfs, argv[2], ZFS_TYPE_FILESYSTEM);
2949 ret = (zfs_jail(zhp, jailid, attach) != 0);
2952 zpool_log_history(g_zfs, argc, argv, argv[2], B_FALSE, B_FALSE);
2959 * zfs jail jailid filesystem
2961 * Attach the given dataset to the given jail
2965 zfs_do_jail(int argc, char **argv)
2968 return (do_jail(argc, argv, 1));
2972 * zfs unjail jailid filesystem
2974 * Detach the given dataset from the given jail
2978 zfs_do_unjail(int argc, char **argv)
2981 return (do_jail(argc, argv, 0));
2985 * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is
2986 * 'legacy'. Otherwise, complain that use should be using 'zfs mount'.
2989 manual_mount(int argc, char **argv)
2992 char mountpoint[ZFS_MAXPROPLEN];
2993 char mntopts[MNT_LINE_MAX] = { '\0' };
2997 char *dataset, *path;
3000 while ((c = getopt(argc, argv, ":mo:O")) != -1) {
3003 (void) strlcpy(mntopts, optarg, sizeof (mntopts));
3006 #if 0 /* FreeBSD: No support for MS_OVERLAY. */
3007 flags |= MS_OVERLAY;
3011 #if 0 /* FreeBSD: No support for MS_NOMNTTAB. */
3012 flags |= MS_NOMNTTAB;
3016 (void) fprintf(stderr, gettext("missing argument for "
3017 "'%c' option\n"), optopt);
3021 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3023 (void) fprintf(stderr, gettext("usage: mount [-o opts] "
3032 /* check that we only have two arguments */
3035 (void) fprintf(stderr, gettext("missing dataset "
3038 (void) fprintf(stderr,
3039 gettext("missing mountpoint argument\n"));
3041 (void) fprintf(stderr, gettext("too many arguments\n"));
3042 (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n");
3049 /* try to open the dataset */
3050 if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL)
3053 (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
3054 sizeof (mountpoint), NULL, NULL, 0, B_FALSE);
3056 /* check for legacy mountpoint and complain appropriately */
3058 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
3059 if (zmount(dataset, path, flags, MNTTYPE_ZFS,
3060 NULL, 0, mntopts, sizeof (mntopts)) != 0) {
3061 (void) fprintf(stderr, gettext("mount failed: %s\n"),
3066 (void) fprintf(stderr, gettext("filesystem '%s' cannot be "
3067 "mounted using 'mount -F zfs'\n"), dataset);
3068 (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' "
3069 "instead.\n"), path);
3070 (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' "
3071 "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n"));
3072 (void) fprintf(stderr, gettext("See zfs(1M) for more "
3081 * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow
3082 * unmounts of non-legacy filesystems, as this is the dominant administrative
3086 manual_unmount(int argc, char **argv)
3092 while ((c = getopt(argc, argv, "f")) != -1) {
3098 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3100 (void) fprintf(stderr, gettext("usage: unmount [-f] "
3109 /* check arguments */
3112 (void) fprintf(stderr, gettext("missing path "
3115 (void) fprintf(stderr, gettext("too many arguments\n"));
3116 (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n"));
3120 return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE));
3124 volcheck(zpool_handle_t *zhp, void *data)
3126 boolean_t isinit = *((boolean_t *)data);
3129 return (zpool_create_zvol_links(zhp));
3131 return (zpool_remove_zvol_links(zhp));
3135 * Iterate over all pools in the system and either create or destroy /dev/zvol
3136 * links, depending on the value of 'isinit'.
3139 do_volcheck(boolean_t isinit)
3141 return (zpool_iter(g_zfs, volcheck, &isinit) ? 1 : 0);
3145 main(int argc, char **argv)
3152 (void) setlocale(LC_ALL, "");
3153 (void) textdomain(TEXT_DOMAIN);
3157 if ((g_zfs = libzfs_init()) == NULL) {
3158 (void) fprintf(stderr, gettext("internal error: failed to "
3159 "initialize ZFS library\n"));
3163 libzfs_print_on_error(g_zfs, B_TRUE);
3165 if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) {
3166 (void) fprintf(stderr, gettext("internal error: unable to "
3167 "open %s\n"), MNTTAB);
3172 * This command also doubles as the /etc/fs mount and unmount program.
3173 * Determine if we should take this behavior based on argv[0].
3175 progname = basename(argv[0]);
3176 if (strcmp(progname, "mount") == 0) {
3177 ret = manual_mount(argc, argv);
3178 } else if (strcmp(progname, "umount") == 0) {
3179 ret = manual_unmount(argc, argv);
3182 * Make sure the user has specified some command.
3185 (void) fprintf(stderr, gettext("missing command\n"));
3192 * The 'umount' command is an alias for 'unmount'
3194 if (strcmp(cmdname, "umount") == 0)
3195 cmdname = "unmount";
3198 * The 'recv' command is an alias for 'receive'
3200 if (strcmp(cmdname, "recv") == 0)
3201 cmdname = "receive";
3206 if (strcmp(cmdname, "-?") == 0)
3210 * 'volinit' and 'volfini' do not appear in the usage message,
3211 * so we have to special case them here.
3213 if (strcmp(cmdname, "volinit") == 0)
3214 return (do_volcheck(B_TRUE));
3215 else if (strcmp(cmdname, "volfini") == 0)
3216 return (do_volcheck(B_FALSE));
3219 * Run the appropriate command.
3221 for (i = 0; i < NCOMMAND; i++) {
3222 if (command_table[i].name == NULL)
3225 if (strcmp(cmdname, command_table[i].name) == 0) {
3226 current_command = &command_table[i];
3227 ret = command_table[i].func(argc - 1, argv + 1);
3232 if (i == NCOMMAND) {
3233 (void) fprintf(stderr, gettext("unrecognized "
3234 "command '%s'\n"), cmdname);
3239 (void) fclose(mnttab_file);
3244 * The 'ZFS_ABORT' environment variable causes us to dump core on exit
3245 * for the purposes of running ::findleaks.
3247 if (getenv("ZFS_ABORT") != NULL) {
3248 (void) printf("dumping core by request\n");