1/*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2012 by Delphix. All rights reserved.
25 * Copyright 2012 Milan Jurik. All rights reserved.
26 * Copyright (c) 2012, Joyent, Inc. All rights reserved.
27 * Copyright (c) 2011-2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
28 * All rights reserved.
29 * Copyright (c) 2012 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
30 * Copyright (c) 2013 Steven Hartland.  All rights reserved.
31 * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
32 */
33
34#include <assert.h>
35#include <ctype.h>
36#include <errno.h>
37#include <libgen.h>
38#include <libintl.h>
39#include <libuutil.h>
40#include <libnvpair.h>
41#include <locale.h>
42#include <stddef.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <strings.h>
46#include <unistd.h>
47#include <fcntl.h>
48#include <zone.h>
49#include <grp.h>
50#include <pwd.h>
51#include <signal.h>
52#include <sys/list.h>
53#include <sys/mntent.h>
54#include <sys/mnttab.h>
55#include <sys/mount.h>
56#include <sys/stat.h>
57#include <sys/fs/zfs.h>
58#include <sys/types.h>
59#include <time.h>
60
61#include <libzfs.h>
62#include <libzfs_core.h>
63#include <zfs_prop.h>
64#include <zfs_deleg.h>
65#include <libuutil.h>
66#ifdef sun
67#include <aclutils.h>
68#include <directory.h>
69#endif
70
71#include "zfs_iter.h"
72#include "zfs_util.h"
73#include "zfs_comutil.h"
74
75libzfs_handle_t *g_zfs;
76
77static FILE *mnttab_file;
78static char history_str[HIS_MAX_RECORD_LEN];
79static boolean_t log_history = B_TRUE;
80
81static int zfs_do_clone(int argc, char **argv);
82static int zfs_do_create(int argc, char **argv);
83static int zfs_do_destroy(int argc, char **argv);
84static int zfs_do_get(int argc, char **argv);
85static int zfs_do_inherit(int argc, char **argv);
86static int zfs_do_list(int argc, char **argv);
87static int zfs_do_mount(int argc, char **argv);
88static int zfs_do_rename(int argc, char **argv);
89static int zfs_do_rollback(int argc, char **argv);
90static int zfs_do_set(int argc, char **argv);
91static int zfs_do_upgrade(int argc, char **argv);
92static int zfs_do_snapshot(int argc, char **argv);
93static int zfs_do_unmount(int argc, char **argv);
94static int zfs_do_share(int argc, char **argv);
95static int zfs_do_unshare(int argc, char **argv);
96static int zfs_do_send(int argc, char **argv);
97static int zfs_do_receive(int argc, char **argv);
98static int zfs_do_promote(int argc, char **argv);
99static int zfs_do_userspace(int argc, char **argv);
100static int zfs_do_allow(int argc, char **argv);
101static int zfs_do_unallow(int argc, char **argv);
102static int zfs_do_hold(int argc, char **argv);
103static int zfs_do_holds(int argc, char **argv);
104static int zfs_do_release(int argc, char **argv);
105static int zfs_do_diff(int argc, char **argv);
106static int zfs_do_jail(int argc, char **argv);
107static int zfs_do_unjail(int argc, char **argv);
108static int zfs_do_bookmark(int argc, char **argv);
109
110/*
111 * Enable a reasonable set of defaults for libumem debugging on DEBUG builds.
112 */
113
114#ifdef DEBUG
115const char *
116_umem_debug_init(void)
117{
118	return ("default,verbose"); /* $UMEM_DEBUG setting */
119}
120
121const char *
122_umem_logging_init(void)
123{
124	return ("fail,contents"); /* $UMEM_LOGGING setting */
125}
126#endif
127
128typedef enum {
129	HELP_CLONE,
130	HELP_CREATE,
131	HELP_DESTROY,
132	HELP_GET,
133	HELP_INHERIT,
134	HELP_UPGRADE,
135	HELP_JAIL,
136	HELP_UNJAIL,
137	HELP_LIST,
138	HELP_MOUNT,
139	HELP_PROMOTE,
140	HELP_RECEIVE,
141	HELP_RENAME,
142	HELP_ROLLBACK,
143	HELP_SEND,
144	HELP_SET,
145	HELP_SHARE,
146	HELP_SNAPSHOT,
147	HELP_UNMOUNT,
148	HELP_UNSHARE,
149	HELP_ALLOW,
150	HELP_UNALLOW,
151	HELP_USERSPACE,
152	HELP_GROUPSPACE,
153	HELP_HOLD,
154	HELP_HOLDS,
155	HELP_RELEASE,
156	HELP_DIFF,
157	HELP_BOOKMARK,
158} zfs_help_t;
159
160typedef struct zfs_command {
161	const char	*name;
162	int		(*func)(int argc, char **argv);
163	zfs_help_t	usage;
164} zfs_command_t;
165
166/*
167 * Master command table.  Each ZFS command has a name, associated function, and
168 * usage message.  The usage messages need to be internationalized, so we have
169 * to have a function to return the usage message based on a command index.
170 *
171 * These commands are organized according to how they are displayed in the usage
172 * message.  An empty command (one with a NULL name) indicates an empty line in
173 * the generic usage message.
174 */
175static zfs_command_t command_table[] = {
176	{ "create",	zfs_do_create,		HELP_CREATE		},
177	{ "destroy",	zfs_do_destroy,		HELP_DESTROY		},
178	{ NULL },
179	{ "snapshot",	zfs_do_snapshot,	HELP_SNAPSHOT		},
180	{ "rollback",	zfs_do_rollback,	HELP_ROLLBACK		},
181	{ "clone",	zfs_do_clone,		HELP_CLONE		},
182	{ "promote",	zfs_do_promote,		HELP_PROMOTE		},
183	{ "rename",	zfs_do_rename,		HELP_RENAME		},
184	{ "bookmark",	zfs_do_bookmark,	HELP_BOOKMARK		},
185	{ NULL },
186	{ "list",	zfs_do_list,		HELP_LIST		},
187	{ NULL },
188	{ "set",	zfs_do_set,		HELP_SET		},
189	{ "get",	zfs_do_get,		HELP_GET		},
190	{ "inherit",	zfs_do_inherit,		HELP_INHERIT		},
191	{ "upgrade",	zfs_do_upgrade,		HELP_UPGRADE		},
192	{ "userspace",	zfs_do_userspace,	HELP_USERSPACE		},
193	{ "groupspace",	zfs_do_userspace,	HELP_GROUPSPACE		},
194	{ NULL },
195	{ "mount",	zfs_do_mount,		HELP_MOUNT		},
196	{ "unmount",	zfs_do_unmount,		HELP_UNMOUNT		},
197	{ "share",	zfs_do_share,		HELP_SHARE		},
198	{ "unshare",	zfs_do_unshare,		HELP_UNSHARE		},
199	{ NULL },
200	{ "send",	zfs_do_send,		HELP_SEND		},
201	{ "receive",	zfs_do_receive,		HELP_RECEIVE		},
202	{ NULL },
203	{ "allow",	zfs_do_allow,		HELP_ALLOW		},
204	{ NULL },
205	{ "unallow",	zfs_do_unallow,		HELP_UNALLOW		},
206	{ NULL },
207	{ "hold",	zfs_do_hold,		HELP_HOLD		},
208	{ "holds",	zfs_do_holds,		HELP_HOLDS		},
209	{ "release",	zfs_do_release,		HELP_RELEASE		},
210	{ "diff",	zfs_do_diff,		HELP_DIFF		},
211	{ NULL },
212	{ "jail",	zfs_do_jail,		HELP_JAIL		},
213	{ "unjail",	zfs_do_unjail,		HELP_UNJAIL		},
214};
215
216#define	NCOMMAND	(sizeof (command_table) / sizeof (command_table[0]))
217
218zfs_command_t *current_command;
219
220static const char *
221get_usage(zfs_help_t idx)
222{
223	switch (idx) {
224	case HELP_CLONE:
225		return (gettext("\tclone [-p] [-o property=value] ... "
226		    "<snapshot> <filesystem|volume>\n"));
227	case HELP_CREATE:
228		return (gettext("\tcreate [-p] [-o property=value] ... "
229		    "<filesystem>\n"
230		    "\tcreate [-ps] [-b blocksize] [-o property=value] ... "
231		    "-V <size> <volume>\n"));
232	case HELP_DESTROY:
233		return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n"
234		    "\tdestroy [-dnpRrv] "
235		    "<filesystem|volume>@<snap>[%<snap>][,...]\n"
236		    "\tdestroy <filesystem|volume>#<bookmark>\n"));
237	case HELP_GET:
238		return (gettext("\tget [-rHp] [-d max] "
239		    "[-o \"all\" | field[,...]]\n"
240		    "\t    [-t type[,...]] [-s source[,...]]\n"
241		    "\t    <\"all\" | property[,...]> "
242		    "[filesystem|volume|snapshot] ...\n"));
243	case HELP_INHERIT:
244		return (gettext("\tinherit [-rS] <property> "
245		    "<filesystem|volume|snapshot> ...\n"));
246	case HELP_UPGRADE:
247		return (gettext("\tupgrade [-v]\n"
248		    "\tupgrade [-r] [-V version] <-a | filesystem ...>\n"));
249	case HELP_JAIL:
250		return (gettext("\tjail <jailid|jailname> <filesystem>\n"));
251	case HELP_UNJAIL:
252		return (gettext("\tunjail <jailid|jailname> <filesystem>\n"));
253	case HELP_LIST:
254		return (gettext("\tlist [-Hp] [-r|-d max] [-o property[,...]] "
255		    "[-s property]...\n\t    [-S property]... [-t type[,...]] "
256		    "[filesystem|volume|snapshot] ...\n"));
257	case HELP_MOUNT:
258		return (gettext("\tmount\n"
259		    "\tmount [-vO] [-o opts] <-a | filesystem>\n"));
260	case HELP_PROMOTE:
261		return (gettext("\tpromote <clone-filesystem>\n"));
262	case HELP_RECEIVE:
263		return (gettext("\treceive|recv [-vnFu] <filesystem|volume|"
264		"snapshot>\n"
265		"\treceive|recv [-vnFu] [-d | -e] <filesystem>\n"));
266	case HELP_RENAME:
267		return (gettext("\trename [-f] <filesystem|volume|snapshot> "
268		    "<filesystem|volume|snapshot>\n"
269		    "\trename [-f] -p <filesystem|volume> <filesystem|volume>\n"
270		    "\trename -r <snapshot> <snapshot>\n"
271		    "\trename -u [-p] <filesystem> <filesystem>"));
272	case HELP_ROLLBACK:
273		return (gettext("\trollback [-rRf] <snapshot>\n"));
274	case HELP_SEND:
275		return (gettext("\tsend [-DnPpRv] [-[iI] snapshot] "
276		    "<snapshot>\n"
277		    "\tsend [-i snapshot|bookmark] "
278		    "<filesystem|volume|snapshot>\n"));
279	case HELP_SET:
280		return (gettext("\tset <property=value> "
281		    "<filesystem|volume|snapshot> ...\n"));
282	case HELP_SHARE:
283		return (gettext("\tshare <-a | filesystem>\n"));
284	case HELP_SNAPSHOT:
285		return (gettext("\tsnapshot|snap [-r] [-o property=value] ... "
286		    "<filesystem|volume>@<snap> ...\n"));
287	case HELP_UNMOUNT:
288		return (gettext("\tunmount|umount [-f] "
289		    "<-a | filesystem|mountpoint>\n"));
290	case HELP_UNSHARE:
291		return (gettext("\tunshare "
292		    "<-a | filesystem|mountpoint>\n"));
293	case HELP_ALLOW:
294		return (gettext("\tallow <filesystem|volume>\n"
295		    "\tallow [-ldug] "
296		    "<\"everyone\"|user|group>[,...] <perm|@setname>[,...]\n"
297		    "\t    <filesystem|volume>\n"
298		    "\tallow [-ld] -e <perm|@setname>[,...] "
299		    "<filesystem|volume>\n"
300		    "\tallow -c <perm|@setname>[,...] <filesystem|volume>\n"
301		    "\tallow -s @setname <perm|@setname>[,...] "
302		    "<filesystem|volume>\n"));
303	case HELP_UNALLOW:
304		return (gettext("\tunallow [-rldug] "
305		    "<\"everyone\"|user|group>[,...]\n"
306		    "\t    [<perm|@setname>[,...]] <filesystem|volume>\n"
307		    "\tunallow [-rld] -e [<perm|@setname>[,...]] "
308		    "<filesystem|volume>\n"
309		    "\tunallow [-r] -c [<perm|@setname>[,...]] "
310		    "<filesystem|volume>\n"
311		    "\tunallow [-r] -s @setname [<perm|@setname>[,...]] "
312		    "<filesystem|volume>\n"));
313	case HELP_USERSPACE:
314		return (gettext("\tuserspace [-Hinp] [-o field[,...]] "
315		    "[-s field] ...\n"
316		    "\t    [-S field] ... [-t type[,...]] "
317		    "<filesystem|snapshot>\n"));
318	case HELP_GROUPSPACE:
319		return (gettext("\tgroupspace [-Hinp] [-o field[,...]] "
320		    "[-s field] ...\n"
321		    "\t    [-S field] ... [-t type[,...]] "
322		    "<filesystem|snapshot>\n"));
323	case HELP_HOLD:
324		return (gettext("\thold [-r] <tag> <snapshot> ...\n"));
325	case HELP_HOLDS:
326		return (gettext("\tholds [-r] <snapshot> ...\n"));
327	case HELP_RELEASE:
328		return (gettext("\trelease [-r] <tag> <snapshot> ...\n"));
329	case HELP_DIFF:
330		return (gettext("\tdiff [-FHt] <snapshot> "
331		    "[snapshot|filesystem]\n"));
332	case HELP_BOOKMARK:
333		return (gettext("\tbookmark <snapshot> <bookmark>\n"));
334	}
335
336	abort();
337	/* NOTREACHED */
338}
339
340void
341nomem(void)
342{
343	(void) fprintf(stderr, gettext("internal error: out of memory\n"));
344	exit(1);
345}
346
347/*
348 * Utility function to guarantee malloc() success.
349 */
350
351void *
352safe_malloc(size_t size)
353{
354	void *data;
355
356	if ((data = calloc(1, size)) == NULL)
357		nomem();
358
359	return (data);
360}
361
362static char *
363safe_strdup(char *str)
364{
365	char *dupstr = strdup(str);
366
367	if (dupstr == NULL)
368		nomem();
369
370	return (dupstr);
371}
372
373/*
374 * Callback routine that will print out information for each of
375 * the properties.
376 */
377static int
378usage_prop_cb(int prop, void *cb)
379{
380	FILE *fp = cb;
381
382	(void) fprintf(fp, "\t%-15s ", zfs_prop_to_name(prop));
383
384	if (zfs_prop_readonly(prop))
385		(void) fprintf(fp, " NO    ");
386	else
387		(void) fprintf(fp, "YES    ");
388
389	if (zfs_prop_inheritable(prop))
390		(void) fprintf(fp, "  YES   ");
391	else
392		(void) fprintf(fp, "   NO   ");
393
394	if (zfs_prop_values(prop) == NULL)
395		(void) fprintf(fp, "-\n");
396	else
397		(void) fprintf(fp, "%s\n", zfs_prop_values(prop));
398
399	return (ZPROP_CONT);
400}
401
402/*
403 * Display usage message.  If we're inside a command, display only the usage for
404 * that command.  Otherwise, iterate over the entire command table and display
405 * a complete usage message.
406 */
407static void
408usage(boolean_t requested)
409{
410	int i;
411	boolean_t show_properties = B_FALSE;
412	FILE *fp = requested ? stdout : stderr;
413
414	if (current_command == NULL) {
415
416		(void) fprintf(fp, gettext("usage: zfs command args ...\n"));
417		(void) fprintf(fp,
418		    gettext("where 'command' is one of the following:\n\n"));
419
420		for (i = 0; i < NCOMMAND; i++) {
421			if (command_table[i].name == NULL)
422				(void) fprintf(fp, "\n");
423			else
424				(void) fprintf(fp, "%s",
425				    get_usage(command_table[i].usage));
426		}
427
428		(void) fprintf(fp, gettext("\nEach dataset is of the form: "
429		    "pool/[dataset/]*dataset[@name]\n"));
430	} else {
431		(void) fprintf(fp, gettext("usage:\n"));
432		(void) fprintf(fp, "%s", get_usage(current_command->usage));
433	}
434
435	if (current_command != NULL &&
436	    (strcmp(current_command->name, "set") == 0 ||
437	    strcmp(current_command->name, "get") == 0 ||
438	    strcmp(current_command->name, "inherit") == 0 ||
439	    strcmp(current_command->name, "list") == 0))
440		show_properties = B_TRUE;
441
442	if (show_properties) {
443		(void) fprintf(fp,
444		    gettext("\nThe following properties are supported:\n"));
445
446		(void) fprintf(fp, "\n\t%-14s %s  %s   %s\n\n",
447		    "PROPERTY", "EDIT", "INHERIT", "VALUES");
448
449		/* Iterate over all properties */
450		(void) zprop_iter(usage_prop_cb, fp, B_FALSE, B_TRUE,
451		    ZFS_TYPE_DATASET);
452
453		(void) fprintf(fp, "\t%-15s ", "userused@...");
454		(void) fprintf(fp, " NO       NO   <size>\n");
455		(void) fprintf(fp, "\t%-15s ", "groupused@...");
456		(void) fprintf(fp, " NO       NO   <size>\n");
457		(void) fprintf(fp, "\t%-15s ", "userquota@...");
458		(void) fprintf(fp, "YES       NO   <size> | none\n");
459		(void) fprintf(fp, "\t%-15s ", "groupquota@...");
460		(void) fprintf(fp, "YES       NO   <size> | none\n");
461		(void) fprintf(fp, "\t%-15s ", "written@<snap>");
462		(void) fprintf(fp, " NO       NO   <size>\n");
463
464		(void) fprintf(fp, gettext("\nSizes are specified in bytes "
465		    "with standard units such as K, M, G, etc.\n"));
466		(void) fprintf(fp, gettext("\nUser-defined properties can "
467		    "be specified by using a name containing a colon (:).\n"));
468		(void) fprintf(fp, gettext("\nThe {user|group}{used|quota}@ "
469		    "properties must be appended with\n"
470		    "a user or group specifier of one of these forms:\n"
471		    "    POSIX name      (eg: \"matt\")\n"
472		    "    POSIX id        (eg: \"126829\")\n"
473		    "    SMB name@domain (eg: \"matt@sun\")\n"
474		    "    SMB SID         (eg: \"S-1-234-567-89\")\n"));
475	} else {
476		(void) fprintf(fp,
477		    gettext("\nFor the property list, run: %s\n"),
478		    "zfs set|get");
479		(void) fprintf(fp,
480		    gettext("\nFor the delegated permission list, run: %s\n"),
481		    "zfs allow|unallow");
482	}
483
484	/*
485	 * See comments at end of main().
486	 */
487	if (getenv("ZFS_ABORT") != NULL) {
488		(void) printf("dumping core by request\n");
489		abort();
490	}
491
492	exit(requested ? 0 : 2);
493}
494
495static int
496parseprop(nvlist_t *props, char *propname)
497{
498	char *propval, *strval;
499
500	if ((propval = strchr(propname, '=')) == NULL) {
501		(void) fprintf(stderr, gettext("missing "
502		    "'=' for -o option\n"));
503		return (-1);
504	}
505	*propval = '\0';
506	propval++;
507	if (nvlist_lookup_string(props, propname, &strval) == 0) {
508		(void) fprintf(stderr, gettext("property '%s' "
509		    "specified multiple times\n"), propname);
510		return (-1);
511	}
512	if (nvlist_add_string(props, propname, propval) != 0)
513		nomem();
514	return (0);
515}
516
517static int
518parse_depth(char *opt, int *flags)
519{
520	char *tmp;
521	int depth;
522
523	depth = (int)strtol(opt, &tmp, 0);
524	if (*tmp) {
525		(void) fprintf(stderr,
526		    gettext("%s is not an integer\n"), opt);
527		usage(B_FALSE);
528	}
529	if (depth < 0) {
530		(void) fprintf(stderr,
531		    gettext("Depth can not be negative.\n"));
532		usage(B_FALSE);
533	}
534	*flags |= (ZFS_ITER_DEPTH_LIMIT|ZFS_ITER_RECURSE);
535	return (depth);
536}
537
538#define	PROGRESS_DELAY 2		/* seconds */
539
540static char *pt_reverse = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
541static time_t pt_begin;
542static char *pt_header = NULL;
543static boolean_t pt_shown;
544
545static void
546start_progress_timer(void)
547{
548	pt_begin = time(NULL) + PROGRESS_DELAY;
549	pt_shown = B_FALSE;
550}
551
552static void
553set_progress_header(char *header)
554{
555	assert(pt_header == NULL);
556	pt_header = safe_strdup(header);
557	if (pt_shown) {
558		(void) printf("%s: ", header);
559		(void) fflush(stdout);
560	}
561}
562
563static void
564update_progress(char *update)
565{
566	if (!pt_shown && time(NULL) > pt_begin) {
567		int len = strlen(update);
568
569		(void) printf("%s: %s%*.*s", pt_header, update, len, len,
570		    pt_reverse);
571		(void) fflush(stdout);
572		pt_shown = B_TRUE;
573	} else if (pt_shown) {
574		int len = strlen(update);
575
576		(void) printf("%s%*.*s", update, len, len, pt_reverse);
577		(void) fflush(stdout);
578	}
579}
580
581static void
582finish_progress(char *done)
583{
584	if (pt_shown) {
585		(void) printf("%s\n", done);
586		(void) fflush(stdout);
587	}
588	free(pt_header);
589	pt_header = NULL;
590}
591/*
592 * zfs clone [-p] [-o prop=value] ... <snap> <fs | vol>
593 *
594 * Given an existing dataset, create a writable copy whose initial contents
595 * are the same as the source.  The newly created dataset maintains a
596 * dependency on the original; the original cannot be destroyed so long as
597 * the clone exists.
598 *
599 * The '-p' flag creates all the non-existing ancestors of the target first.
600 */
601static int
602zfs_do_clone(int argc, char **argv)
603{
604	zfs_handle_t *zhp = NULL;
605	boolean_t parents = B_FALSE;
606	nvlist_t *props;
607	int ret = 0;
608	int c;
609
610	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
611		nomem();
612
613	/* check options */
614	while ((c = getopt(argc, argv, "o:p")) != -1) {
615		switch (c) {
616		case 'o':
617			if (parseprop(props, optarg))
618				return (1);
619			break;
620		case 'p':
621			parents = B_TRUE;
622			break;
623		case '?':
624			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
625			    optopt);
626			goto usage;
627		}
628	}
629
630	argc -= optind;
631	argv += optind;
632
633	/* check number of arguments */
634	if (argc < 1) {
635		(void) fprintf(stderr, gettext("missing source dataset "
636		    "argument\n"));
637		goto usage;
638	}
639	if (argc < 2) {
640		(void) fprintf(stderr, gettext("missing target dataset "
641		    "argument\n"));
642		goto usage;
643	}
644	if (argc > 2) {
645		(void) fprintf(stderr, gettext("too many arguments\n"));
646		goto usage;
647	}
648
649	/* open the source dataset */
650	if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL)
651		return (1);
652
653	if (parents && zfs_name_valid(argv[1], ZFS_TYPE_FILESYSTEM |
654	    ZFS_TYPE_VOLUME)) {
655		/*
656		 * Now create the ancestors of the target dataset.  If the
657		 * target already exists and '-p' option was used we should not
658		 * complain.
659		 */
660		if (zfs_dataset_exists(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM |
661		    ZFS_TYPE_VOLUME))
662			return (0);
663		if (zfs_create_ancestors(g_zfs, argv[1]) != 0)
664			return (1);
665	}
666
667	/* pass to libzfs */
668	ret = zfs_clone(zhp, argv[1], props);
669
670	/* create the mountpoint if necessary */
671	if (ret == 0) {
672		zfs_handle_t *clone;
673
674		clone = zfs_open(g_zfs, argv[1], ZFS_TYPE_DATASET);
675		if (clone != NULL) {
676			if (zfs_get_type(clone) != ZFS_TYPE_VOLUME)
677				if ((ret = zfs_mount(clone, NULL, 0)) == 0)
678					ret = zfs_share(clone);
679			zfs_close(clone);
680		}
681	}
682
683	zfs_close(zhp);
684	nvlist_free(props);
685
686	return (!!ret);
687
688usage:
689	if (zhp)
690		zfs_close(zhp);
691	nvlist_free(props);
692	usage(B_FALSE);
693	return (-1);
694}
695
696/*
697 * zfs create [-p] [-o prop=value] ... fs
698 * zfs create [-ps] [-b blocksize] [-o prop=value] ... -V vol size
699 *
700 * Create a new dataset.  This command can be used to create filesystems
701 * and volumes.  Snapshot creation is handled by 'zfs snapshot'.
702 * For volumes, the user must specify a size to be used.
703 *
704 * The '-s' flag applies only to volumes, and indicates that we should not try
705 * to set the reservation for this volume.  By default we set a reservation
706 * equal to the size for any volume.  For pools with SPA_VERSION >=
707 * SPA_VERSION_REFRESERVATION, we set a refreservation instead.
708 *
709 * The '-p' flag creates all the non-existing ancestors of the target first.
710 */
711static int
712zfs_do_create(int argc, char **argv)
713{
714	zfs_type_t type = ZFS_TYPE_FILESYSTEM;
715	zfs_handle_t *zhp = NULL;
716	uint64_t volsize;
717	int c;
718	boolean_t noreserve = B_FALSE;
719	boolean_t bflag = B_FALSE;
720	boolean_t parents = B_FALSE;
721	int ret = 1;
722	nvlist_t *props;
723	uint64_t intval;
724	int canmount = ZFS_CANMOUNT_OFF;
725
726	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
727		nomem();
728
729	/* check options */
730	while ((c = getopt(argc, argv, ":V:b:so:p")) != -1) {
731		switch (c) {
732		case 'V':
733			type = ZFS_TYPE_VOLUME;
734			if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {
735				(void) fprintf(stderr, gettext("bad volume "
736				    "size '%s': %s\n"), optarg,
737				    libzfs_error_description(g_zfs));
738				goto error;
739			}
740
741			if (nvlist_add_uint64(props,
742			    zfs_prop_to_name(ZFS_PROP_VOLSIZE), intval) != 0)
743				nomem();
744			volsize = intval;
745			break;
746		case 'p':
747			parents = B_TRUE;
748			break;
749		case 'b':
750			bflag = B_TRUE;
751			if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {
752				(void) fprintf(stderr, gettext("bad volume "
753				    "block size '%s': %s\n"), optarg,
754				    libzfs_error_description(g_zfs));
755				goto error;
756			}
757
758			if (nvlist_add_uint64(props,
759			    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
760			    intval) != 0)
761				nomem();
762			break;
763		case 'o':
764			if (parseprop(props, optarg))
765				goto error;
766			break;
767		case 's':
768			noreserve = B_TRUE;
769			break;
770		case ':':
771			(void) fprintf(stderr, gettext("missing size "
772			    "argument\n"));
773			goto badusage;
774		case '?':
775			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
776			    optopt);
777			goto badusage;
778		}
779	}
780
781	if ((bflag || noreserve) && type != ZFS_TYPE_VOLUME) {
782		(void) fprintf(stderr, gettext("'-s' and '-b' can only be "
783		    "used when creating a volume\n"));
784		goto badusage;
785	}
786
787	argc -= optind;
788	argv += optind;
789
790	/* check number of arguments */
791	if (argc == 0) {
792		(void) fprintf(stderr, gettext("missing %s argument\n"),
793		    zfs_type_to_name(type));
794		goto badusage;
795	}
796	if (argc > 1) {
797		(void) fprintf(stderr, gettext("too many arguments\n"));
798		goto badusage;
799	}
800
801	if (type == ZFS_TYPE_VOLUME && !noreserve) {
802		zpool_handle_t *zpool_handle;
803		uint64_t spa_version;
804		char *p;
805		zfs_prop_t resv_prop;
806		char *strval;
807
808		if (p = strchr(argv[0], '/'))
809			*p = '\0';
810		zpool_handle = zpool_open(g_zfs, argv[0]);
811		if (p != NULL)
812			*p = '/';
813		if (zpool_handle == NULL)
814			goto error;
815		spa_version = zpool_get_prop_int(zpool_handle,
816		    ZPOOL_PROP_VERSION, NULL);
817		zpool_close(zpool_handle);
818		if (spa_version >= SPA_VERSION_REFRESERVATION)
819			resv_prop = ZFS_PROP_REFRESERVATION;
820		else
821			resv_prop = ZFS_PROP_RESERVATION;
822		volsize = zvol_volsize_to_reservation(volsize, props);
823
824		if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop),
825		    &strval) != 0) {
826			if (nvlist_add_uint64(props,
827			    zfs_prop_to_name(resv_prop), volsize) != 0) {
828				nvlist_free(props);
829				nomem();
830			}
831		}
832	}
833
834	if (parents && zfs_name_valid(argv[0], type)) {
835		/*
836		 * Now create the ancestors of target dataset.  If the target
837		 * already exists and '-p' option was used we should not
838		 * complain.
839		 */
840		if (zfs_dataset_exists(g_zfs, argv[0], type)) {
841			ret = 0;
842			goto error;
843		}
844		if (zfs_create_ancestors(g_zfs, argv[0]) != 0)
845			goto error;
846	}
847
848	/* pass to libzfs */
849	if (zfs_create(g_zfs, argv[0], type, props) != 0)
850		goto error;
851
852	if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL)
853		goto error;
854
855	ret = 0;
856	/*
857	 * if the user doesn't want the dataset automatically mounted,
858	 * then skip the mount/share step
859	 */
860	if (zfs_prop_valid_for_type(ZFS_PROP_CANMOUNT, type))
861		canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);
862
863	/*
864	 * Mount and/or share the new filesystem as appropriate.  We provide a
865	 * verbose error message to let the user know that their filesystem was
866	 * in fact created, even if we failed to mount or share it.
867	 */
868	if (canmount == ZFS_CANMOUNT_ON) {
869		if (zfs_mount(zhp, NULL, 0) != 0) {
870			(void) fprintf(stderr, gettext("filesystem "
871			    "successfully created, but not mounted\n"));
872			ret = 1;
873		} else if (zfs_share(zhp) != 0) {
874			(void) fprintf(stderr, gettext("filesystem "
875			    "successfully created, but not shared\n"));
876			ret = 1;
877		}
878	}
879
880error:
881	if (zhp)
882		zfs_close(zhp);
883	nvlist_free(props);
884	return (ret);
885badusage:
886	nvlist_free(props);
887	usage(B_FALSE);
888	return (2);
889}
890
891/*
892 * zfs destroy [-rRf] <fs, vol>
893 * zfs destroy [-rRd] <snap>
894 *
895 *	-r	Recursively destroy all children
896 *	-R	Recursively destroy all dependents, including clones
897 *	-f	Force unmounting of any dependents
898 *	-d	If we can't destroy now, mark for deferred destruction
899 *
900 * Destroys the given dataset.  By default, it will unmount any filesystems,
901 * and refuse to destroy a dataset that has any dependents.  A dependent can
902 * either be a child, or a clone of a child.
903 */
904typedef struct destroy_cbdata {
905	boolean_t	cb_first;
906	boolean_t	cb_force;
907	boolean_t	cb_recurse;
908	boolean_t	cb_error;
909	boolean_t	cb_doclones;
910	zfs_handle_t	*cb_target;
911	boolean_t	cb_defer_destroy;
912	boolean_t	cb_verbose;
913	boolean_t	cb_parsable;
914	boolean_t	cb_dryrun;
915	nvlist_t	*cb_nvl;
916	nvlist_t	*cb_batchedsnaps;
917
918	/* first snap in contiguous run */
919	char		*cb_firstsnap;
920	/* previous snap in contiguous run */
921	char		*cb_prevsnap;
922	int64_t		cb_snapused;
923	char		*cb_snapspec;
924	char		*cb_bookmark;
925} destroy_cbdata_t;
926
927/*
928 * Check for any dependents based on the '-r' or '-R' flags.
929 */
930static int
931destroy_check_dependent(zfs_handle_t *zhp, void *data)
932{
933	destroy_cbdata_t *cbp = data;
934	const char *tname = zfs_get_name(cbp->cb_target);
935	const char *name = zfs_get_name(zhp);
936
937	if (strncmp(tname, name, strlen(tname)) == 0 &&
938	    (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) {
939		/*
940		 * This is a direct descendant, not a clone somewhere else in
941		 * the hierarchy.
942		 */
943		if (cbp->cb_recurse)
944			goto out;
945
946		if (cbp->cb_first) {
947			(void) fprintf(stderr, gettext("cannot destroy '%s': "
948			    "%s has children\n"),
949			    zfs_get_name(cbp->cb_target),
950			    zfs_type_to_name(zfs_get_type(cbp->cb_target)));
951			(void) fprintf(stderr, gettext("use '-r' to destroy "
952			    "the following datasets:\n"));
953			cbp->cb_first = B_FALSE;
954			cbp->cb_error = B_TRUE;
955		}
956
957		(void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
958	} else {
959		/*
960		 * This is a clone.  We only want to report this if the '-r'
961		 * wasn't specified, or the target is a snapshot.
962		 */
963		if (!cbp->cb_recurse &&
964		    zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT)
965			goto out;
966
967		if (cbp->cb_first) {
968			(void) fprintf(stderr, gettext("cannot destroy '%s': "
969			    "%s has dependent clones\n"),
970			    zfs_get_name(cbp->cb_target),
971			    zfs_type_to_name(zfs_get_type(cbp->cb_target)));
972			(void) fprintf(stderr, gettext("use '-R' to destroy "
973			    "the following datasets:\n"));
974			cbp->cb_first = B_FALSE;
975			cbp->cb_error = B_TRUE;
976			cbp->cb_dryrun = B_TRUE;
977		}
978
979		(void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
980	}
981
982out:
983	zfs_close(zhp);
984	return (0);
985}
986
987static int
988destroy_callback(zfs_handle_t *zhp, void *data)
989{
990	destroy_cbdata_t *cb = data;
991	const char *name = zfs_get_name(zhp);
992
993	if (cb->cb_verbose) {
994		if (cb->cb_parsable) {
995			(void) printf("destroy\t%s\n", name);
996		} else if (cb->cb_dryrun) {
997			(void) printf(gettext("would destroy %s\n"),
998			    name);
999		} else {
1000			(void) printf(gettext("will destroy %s\n"),
1001			    name);
1002		}
1003	}
1004
1005	/*
1006	 * Ignore pools (which we've already flagged as an error before getting
1007	 * here).
1008	 */
1009	if (strchr(zfs_get_name(zhp), '/') == NULL &&
1010	    zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
1011		zfs_close(zhp);
1012		return (0);
1013	}
1014	if (cb->cb_dryrun) {
1015		zfs_close(zhp);
1016		return (0);
1017	}
1018
1019	/*
1020	 * We batch up all contiguous snapshots (even of different
1021	 * filesystems) and destroy them with one ioctl.  We can't
1022	 * simply do all snap deletions and then all fs deletions,
1023	 * because we must delete a clone before its origin.
1024	 */
1025	if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) {
1026		fnvlist_add_boolean(cb->cb_batchedsnaps, name);
1027	} else {
1028		int error = zfs_destroy_snaps_nvl(g_zfs,
1029		    cb->cb_batchedsnaps, B_FALSE);
1030		fnvlist_free(cb->cb_batchedsnaps);
1031		cb->cb_batchedsnaps = fnvlist_alloc();
1032
1033		if (error != 0 ||
1034		    zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 ||
1035		    zfs_destroy(zhp, cb->cb_defer_destroy) != 0) {
1036			zfs_close(zhp);
1037			return (-1);
1038		}
1039	}
1040
1041	zfs_close(zhp);
1042	return (0);
1043}
1044
1045static int
1046destroy_print_cb(zfs_handle_t *zhp, void *arg)
1047{
1048	destroy_cbdata_t *cb = arg;
1049	const char *name = zfs_get_name(zhp);
1050	int err = 0;
1051
1052	if (nvlist_exists(cb->cb_nvl, name)) {
1053		if (cb->cb_firstsnap == NULL)
1054			cb->cb_firstsnap = strdup(name);
1055		if (cb->cb_prevsnap != NULL)
1056			free(cb->cb_prevsnap);
1057		/* this snap continues the current range */
1058		cb->cb_prevsnap = strdup(name);
1059		if (cb->cb_firstsnap == NULL || cb->cb_prevsnap == NULL)
1060			nomem();
1061		if (cb->cb_verbose) {
1062			if (cb->cb_parsable) {
1063				(void) printf("destroy\t%s\n", name);
1064			} else if (cb->cb_dryrun) {
1065				(void) printf(gettext("would destroy %s\n"),
1066				    name);
1067			} else {
1068				(void) printf(gettext("will destroy %s\n"),
1069				    name);
1070			}
1071		}
1072	} else if (cb->cb_firstsnap != NULL) {
1073		/* end of this range */
1074		uint64_t used = 0;
1075		err = lzc_snaprange_space(cb->cb_firstsnap,
1076		    cb->cb_prevsnap, &used);
1077		cb->cb_snapused += used;
1078		free(cb->cb_firstsnap);
1079		cb->cb_firstsnap = NULL;
1080		free(cb->cb_prevsnap);
1081		cb->cb_prevsnap = NULL;
1082	}
1083	zfs_close(zhp);
1084	return (err);
1085}
1086
1087static int
1088destroy_print_snapshots(zfs_handle_t *fs_zhp, destroy_cbdata_t *cb)
1089{
1090	int err = 0;
1091	assert(cb->cb_firstsnap == NULL);
1092	assert(cb->cb_prevsnap == NULL);
1093	err = zfs_iter_snapshots_sorted(fs_zhp, destroy_print_cb, cb);
1094	if (cb->cb_firstsnap != NULL) {
1095		uint64_t used = 0;
1096		if (err == 0) {
1097			err = lzc_snaprange_space(cb->cb_firstsnap,
1098			    cb->cb_prevsnap, &used);
1099		}
1100		cb->cb_snapused += used;
1101		free(cb->cb_firstsnap);
1102		cb->cb_firstsnap = NULL;
1103		free(cb->cb_prevsnap);
1104		cb->cb_prevsnap = NULL;
1105	}
1106	return (err);
1107}
1108
1109static int
1110snapshot_to_nvl_cb(zfs_handle_t *zhp, void *arg)
1111{
1112	destroy_cbdata_t *cb = arg;
1113	int err = 0;
1114
1115	/* Check for clones. */
1116	if (!cb->cb_doclones && !cb->cb_defer_destroy) {
1117		cb->cb_target = zhp;
1118		cb->cb_first = B_TRUE;
1119		err = zfs_iter_dependents(zhp, B_TRUE,
1120		    destroy_check_dependent, cb);
1121	}
1122
1123	if (err == 0) {
1124		if (nvlist_add_boolean(cb->cb_nvl, zfs_get_name(zhp)))
1125			nomem();
1126	}
1127	zfs_close(zhp);
1128	return (err);
1129}
1130
1131static int
1132gather_snapshots(zfs_handle_t *zhp, void *arg)
1133{
1134	destroy_cbdata_t *cb = arg;
1135	int err = 0;
1136
1137	err = zfs_iter_snapspec(zhp, cb->cb_snapspec, snapshot_to_nvl_cb, cb);
1138	if (err == ENOENT)
1139		err = 0;
1140	if (err != 0)
1141		goto out;
1142
1143	if (cb->cb_verbose) {
1144		err = destroy_print_snapshots(zhp, cb);
1145		if (err != 0)
1146			goto out;
1147	}
1148
1149	if (cb->cb_recurse)
1150		err = zfs_iter_filesystems(zhp, gather_snapshots, cb);
1151
1152out:
1153	zfs_close(zhp);
1154	return (err);
1155}
1156
1157static int
1158destroy_clones(destroy_cbdata_t *cb)
1159{
1160	nvpair_t *pair;
1161	for (pair = nvlist_next_nvpair(cb->cb_nvl, NULL);
1162	    pair != NULL;
1163	    pair = nvlist_next_nvpair(cb->cb_nvl, pair)) {
1164		zfs_handle_t *zhp = zfs_open(g_zfs, nvpair_name(pair),
1165		    ZFS_TYPE_SNAPSHOT);
1166		if (zhp != NULL) {
1167			boolean_t defer = cb->cb_defer_destroy;
1168			int err = 0;
1169
1170			/*
1171			 * We can't defer destroy non-snapshots, so set it to
1172			 * false while destroying the clones.
1173			 */
1174			cb->cb_defer_destroy = B_FALSE;
1175			err = zfs_iter_dependents(zhp, B_FALSE,
1176			    destroy_callback, cb);
1177			cb->cb_defer_destroy = defer;
1178			zfs_close(zhp);
1179			if (err != 0)
1180				return (err);
1181		}
1182	}
1183	return (0);
1184}
1185
1186static int
1187zfs_do_destroy(int argc, char **argv)
1188{
1189	destroy_cbdata_t cb = { 0 };
1190	int rv = 0;
1191	int err = 0;
1192	int c;
1193	zfs_handle_t *zhp = NULL;
1194	char *at, *pound;
1195	zfs_type_t type = ZFS_TYPE_DATASET;
1196
1197	/* check options */
1198	while ((c = getopt(argc, argv, "vpndfrR")) != -1) {
1199		switch (c) {
1200		case 'v':
1201			cb.cb_verbose = B_TRUE;
1202			break;
1203		case 'p':
1204			cb.cb_verbose = B_TRUE;
1205			cb.cb_parsable = B_TRUE;
1206			break;
1207		case 'n':
1208			cb.cb_dryrun = B_TRUE;
1209			break;
1210		case 'd':
1211			cb.cb_defer_destroy = B_TRUE;
1212			type = ZFS_TYPE_SNAPSHOT;
1213			break;
1214		case 'f':
1215			cb.cb_force = B_TRUE;
1216			break;
1217		case 'r':
1218			cb.cb_recurse = B_TRUE;
1219			break;
1220		case 'R':
1221			cb.cb_recurse = B_TRUE;
1222			cb.cb_doclones = B_TRUE;
1223			break;
1224		case '?':
1225		default:
1226			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
1227			    optopt);
1228			usage(B_FALSE);
1229		}
1230	}
1231
1232	argc -= optind;
1233	argv += optind;
1234
1235	/* check number of arguments */
1236	if (argc == 0) {
1237		(void) fprintf(stderr, gettext("missing dataset argument\n"));
1238		usage(B_FALSE);
1239	}
1240	if (argc > 1) {
1241		(void) fprintf(stderr, gettext("too many arguments\n"));
1242		usage(B_FALSE);
1243	}
1244
1245	at = strchr(argv[0], '@');
1246	pound = strchr(argv[0], '#');
1247	if (at != NULL) {
1248
1249		/* Build the list of snaps to destroy in cb_nvl. */
1250		cb.cb_nvl = fnvlist_alloc();
1251
1252		*at = '\0';
1253		zhp = zfs_open(g_zfs, argv[0],
1254		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
1255		if (zhp == NULL)
1256			return (1);
1257
1258		cb.cb_snapspec = at + 1;
1259		if (gather_snapshots(zfs_handle_dup(zhp), &cb) != 0 ||
1260		    cb.cb_error) {
1261			rv = 1;
1262			goto out;
1263		}
1264
1265		if (nvlist_empty(cb.cb_nvl)) {
1266			(void) fprintf(stderr, gettext("could not find any "
1267			    "snapshots to destroy; check snapshot names.\n"));
1268			rv = 1;
1269			goto out;
1270		}
1271
1272		if (cb.cb_verbose) {
1273			char buf[16];
1274			zfs_nicenum(cb.cb_snapused, buf, sizeof (buf));
1275			if (cb.cb_parsable) {
1276				(void) printf("reclaim\t%llu\n",
1277				    cb.cb_snapused);
1278			} else if (cb.cb_dryrun) {
1279				(void) printf(gettext("would reclaim %s\n"),
1280				    buf);
1281			} else {
1282				(void) printf(gettext("will reclaim %s\n"),
1283				    buf);
1284			}
1285		}
1286
1287		if (!cb.cb_dryrun) {
1288			if (cb.cb_doclones) {
1289				cb.cb_batchedsnaps = fnvlist_alloc();
1290				err = destroy_clones(&cb);
1291				if (err == 0) {
1292					err = zfs_destroy_snaps_nvl(g_zfs,
1293					    cb.cb_batchedsnaps, B_FALSE);
1294				}
1295				if (err != 0) {
1296					rv = 1;
1297					goto out;
1298				}
1299			}
1300			if (err == 0) {
1301				err = zfs_destroy_snaps_nvl(g_zfs, cb.cb_nvl,
1302				    cb.cb_defer_destroy);
1303			}
1304		}
1305
1306		if (err != 0)
1307			rv = 1;
1308	} else if (pound != NULL) {
1309		int err;
1310		nvlist_t *nvl;
1311
1312		if (cb.cb_dryrun) {
1313			(void) fprintf(stderr,
1314			    "dryrun is not supported with bookmark\n");
1315			return (-1);
1316		}
1317
1318		if (cb.cb_defer_destroy) {
1319			(void) fprintf(stderr,
1320			    "defer destroy is not supported with bookmark\n");
1321			return (-1);
1322		}
1323
1324		if (cb.cb_recurse) {
1325			(void) fprintf(stderr,
1326			    "recursive is not supported with bookmark\n");
1327			return (-1);
1328		}
1329
1330		if (!zfs_bookmark_exists(argv[0])) {
1331			(void) fprintf(stderr, gettext("bookmark '%s' "
1332			    "does not exist.\n"), argv[0]);
1333			return (1);
1334		}
1335
1336		nvl = fnvlist_alloc();
1337		fnvlist_add_boolean(nvl, argv[0]);
1338
1339		err = lzc_destroy_bookmarks(nvl, NULL);
1340		if (err != 0) {
1341			(void) zfs_standard_error(g_zfs, err,
1342			    "cannot destroy bookmark");
1343		}
1344
1345		nvlist_free(cb.cb_nvl);
1346
1347		return (err);
1348	} else {
1349		/* Open the given dataset */
1350		if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL)
1351			return (1);
1352
1353		cb.cb_target = zhp;
1354
1355		/*
1356		 * Perform an explicit check for pools before going any further.
1357		 */
1358		if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL &&
1359		    zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
1360			(void) fprintf(stderr, gettext("cannot destroy '%s': "
1361			    "operation does not apply to pools\n"),
1362			    zfs_get_name(zhp));
1363			(void) fprintf(stderr, gettext("use 'zfs destroy -r "
1364			    "%s' to destroy all datasets in the pool\n"),
1365			    zfs_get_name(zhp));
1366			(void) fprintf(stderr, gettext("use 'zpool destroy %s' "
1367			    "to destroy the pool itself\n"), zfs_get_name(zhp));
1368			rv = 1;
1369			goto out;
1370		}
1371
1372		/*
1373		 * Check for any dependents and/or clones.
1374		 */
1375		cb.cb_first = B_TRUE;
1376		if (!cb.cb_doclones &&
1377		    zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent,
1378		    &cb) != 0) {
1379			rv = 1;
1380			goto out;
1381		}
1382
1383		if (cb.cb_error) {
1384			rv = 1;
1385			goto out;
1386		}
1387
1388		cb.cb_batchedsnaps = fnvlist_alloc();
1389		if (zfs_iter_dependents(zhp, B_FALSE, destroy_callback,
1390		    &cb) != 0) {
1391			rv = 1;
1392			goto out;
1393		}
1394
1395		/*
1396		 * Do the real thing.  The callback will close the
1397		 * handle regardless of whether it succeeds or not.
1398		 */
1399		err = destroy_callback(zhp, &cb);
1400		zhp = NULL;
1401		if (err == 0) {
1402			err = zfs_destroy_snaps_nvl(g_zfs,
1403			    cb.cb_batchedsnaps, cb.cb_defer_destroy);
1404		}
1405		if (err != 0)
1406			rv = 1;
1407	}
1408
1409out:
1410	fnvlist_free(cb.cb_batchedsnaps);
1411	fnvlist_free(cb.cb_nvl);
1412	if (zhp != NULL)
1413		zfs_close(zhp);
1414	return (rv);
1415}
1416
1417static boolean_t
1418is_recvd_column(zprop_get_cbdata_t *cbp)
1419{
1420	int i;
1421	zfs_get_column_t col;
1422
1423	for (i = 0; i < ZFS_GET_NCOLS &&
1424	    (col = cbp->cb_columns[i]) != GET_COL_NONE; i++)
1425		if (col == GET_COL_RECVD)
1426			return (B_TRUE);
1427	return (B_FALSE);
1428}
1429
1430/*
1431 * zfs get [-rHp] [-o all | field[,field]...] [-s source[,source]...]
1432 *	< all | property[,property]... > < fs | snap | vol > ...
1433 *
1434 *	-r	recurse over any child datasets
1435 *	-H	scripted mode.  Headers are stripped, and fields are separated
1436 *		by tabs instead of spaces.
1437 *	-o	Set of fields to display.  One of "name,property,value,
1438 *		received,source". Default is "name,property,value,source".
1439 *		"all" is an alias for all five.
1440 *	-s	Set of sources to allow.  One of
1441 *		"local,default,inherited,received,temporary,none".  Default is
1442 *		all six.
1443 *	-p	Display values in parsable (literal) format.
1444 *
1445 *  Prints properties for the given datasets.  The user can control which
1446 *  columns to display as well as which property types to allow.
1447 */
1448
1449/*
1450 * Invoked to display the properties for a single dataset.
1451 */
1452static int
1453get_callback(zfs_handle_t *zhp, void *data)
1454{
1455	char buf[ZFS_MAXPROPLEN];
1456	char rbuf[ZFS_MAXPROPLEN];
1457	zprop_source_t sourcetype;
1458	char source[ZFS_MAXNAMELEN];
1459	zprop_get_cbdata_t *cbp = data;
1460	nvlist_t *user_props = zfs_get_user_props(zhp);
1461	zprop_list_t *pl = cbp->cb_proplist;
1462	nvlist_t *propval;
1463	char *strval;
1464	char *sourceval;
1465	boolean_t received = is_recvd_column(cbp);
1466
1467	for (; pl != NULL; pl = pl->pl_next) {
1468		char *recvdval = NULL;
1469		/*
1470		 * Skip the special fake placeholder.  This will also skip over
1471		 * the name property when 'all' is specified.
1472		 */
1473		if (pl->pl_prop == ZFS_PROP_NAME &&
1474		    pl == cbp->cb_proplist)
1475			continue;
1476
1477		if (pl->pl_prop != ZPROP_INVAL) {
1478			if (zfs_prop_get(zhp, pl->pl_prop, buf,
1479			    sizeof (buf), &sourcetype, source,
1480			    sizeof (source),
1481			    cbp->cb_literal) != 0) {
1482				if (pl->pl_all)
1483					continue;
1484				if (!zfs_prop_valid_for_type(pl->pl_prop,
1485				    ZFS_TYPE_DATASET)) {
1486					(void) fprintf(stderr,
1487					    gettext("No such property '%s'\n"),
1488					    zfs_prop_to_name(pl->pl_prop));
1489					continue;
1490				}
1491				sourcetype = ZPROP_SRC_NONE;
1492				(void) strlcpy(buf, "-", sizeof (buf));
1493			}
1494
1495			if (received && (zfs_prop_get_recvd(zhp,
1496			    zfs_prop_to_name(pl->pl_prop), rbuf, sizeof (rbuf),
1497			    cbp->cb_literal) == 0))
1498				recvdval = rbuf;
1499
1500			zprop_print_one_property(zfs_get_name(zhp), cbp,
1501			    zfs_prop_to_name(pl->pl_prop),
1502			    buf, sourcetype, source, recvdval);
1503		} else if (zfs_prop_userquota(pl->pl_user_prop)) {
1504			sourcetype = ZPROP_SRC_LOCAL;
1505
1506			if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,
1507			    buf, sizeof (buf), cbp->cb_literal) != 0) {
1508				sourcetype = ZPROP_SRC_NONE;
1509				(void) strlcpy(buf, "-", sizeof (buf));
1510			}
1511
1512			zprop_print_one_property(zfs_get_name(zhp), cbp,
1513			    pl->pl_user_prop, buf, sourcetype, source, NULL);
1514		} else if (zfs_prop_written(pl->pl_user_prop)) {
1515			sourcetype = ZPROP_SRC_LOCAL;
1516
1517			if (zfs_prop_get_written(zhp, pl->pl_user_prop,
1518			    buf, sizeof (buf), cbp->cb_literal) != 0) {
1519				sourcetype = ZPROP_SRC_NONE;
1520				(void) strlcpy(buf, "-", sizeof (buf));
1521			}
1522
1523			zprop_print_one_property(zfs_get_name(zhp), cbp,
1524			    pl->pl_user_prop, buf, sourcetype, source, NULL);
1525		} else {
1526			if (nvlist_lookup_nvlist(user_props,
1527			    pl->pl_user_prop, &propval) != 0) {
1528				if (pl->pl_all)
1529					continue;
1530				sourcetype = ZPROP_SRC_NONE;
1531				strval = "-";
1532			} else {
1533				verify(nvlist_lookup_string(propval,
1534				    ZPROP_VALUE, &strval) == 0);
1535				verify(nvlist_lookup_string(propval,
1536				    ZPROP_SOURCE, &sourceval) == 0);
1537
1538				if (strcmp(sourceval,
1539				    zfs_get_name(zhp)) == 0) {
1540					sourcetype = ZPROP_SRC_LOCAL;
1541				} else if (strcmp(sourceval,
1542				    ZPROP_SOURCE_VAL_RECVD) == 0) {
1543					sourcetype = ZPROP_SRC_RECEIVED;
1544				} else {
1545					sourcetype = ZPROP_SRC_INHERITED;
1546					(void) strlcpy(source,
1547					    sourceval, sizeof (source));
1548				}
1549			}
1550
1551			if (received && (zfs_prop_get_recvd(zhp,
1552			    pl->pl_user_prop, rbuf, sizeof (rbuf),
1553			    cbp->cb_literal) == 0))
1554				recvdval = rbuf;
1555
1556			zprop_print_one_property(zfs_get_name(zhp), cbp,
1557			    pl->pl_user_prop, strval, sourcetype,
1558			    source, recvdval);
1559		}
1560	}
1561
1562	return (0);
1563}
1564
1565static int
1566zfs_do_get(int argc, char **argv)
1567{
1568	zprop_get_cbdata_t cb = { 0 };
1569	int i, c, flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
1570	int types = ZFS_TYPE_DATASET;
1571	char *value, *fields;
1572	int ret = 0;
1573	int limit = 0;
1574	zprop_list_t fake_name = { 0 };
1575
1576	/*
1577	 * Set up default columns and sources.
1578	 */
1579	cb.cb_sources = ZPROP_SRC_ALL;
1580	cb.cb_columns[0] = GET_COL_NAME;
1581	cb.cb_columns[1] = GET_COL_PROPERTY;
1582	cb.cb_columns[2] = GET_COL_VALUE;
1583	cb.cb_columns[3] = GET_COL_SOURCE;
1584	cb.cb_type = ZFS_TYPE_DATASET;
1585
1586	/* check options */
1587	while ((c = getopt(argc, argv, ":d:o:s:rt:Hp")) != -1) {
1588		switch (c) {
1589		case 'p':
1590			cb.cb_literal = B_TRUE;
1591			break;
1592		case 'd':
1593			limit = parse_depth(optarg, &flags);
1594			break;
1595		case 'r':
1596			flags |= ZFS_ITER_RECURSE;
1597			break;
1598		case 'H':
1599			cb.cb_scripted = B_TRUE;
1600			break;
1601		case ':':
1602			(void) fprintf(stderr, gettext("missing argument for "
1603			    "'%c' option\n"), optopt);
1604			usage(B_FALSE);
1605			break;
1606		case 'o':
1607			/*
1608			 * Process the set of columns to display.  We zero out
1609			 * the structure to give us a blank slate.
1610			 */
1611			bzero(&cb.cb_columns, sizeof (cb.cb_columns));
1612			i = 0;
1613			while (*optarg != '\0') {
1614				static char *col_subopts[] =
1615				    { "name", "property", "value", "received",
1616				    "source", "all", NULL };
1617
1618				if (i == ZFS_GET_NCOLS) {
1619					(void) fprintf(stderr, gettext("too "
1620					    "many fields given to -o "
1621					    "option\n"));
1622					usage(B_FALSE);
1623				}
1624
1625				switch (getsubopt(&optarg, col_subopts,
1626				    &value)) {
1627				case 0:
1628					cb.cb_columns[i++] = GET_COL_NAME;
1629					break;
1630				case 1:
1631					cb.cb_columns[i++] = GET_COL_PROPERTY;
1632					break;
1633				case 2:
1634					cb.cb_columns[i++] = GET_COL_VALUE;
1635					break;
1636				case 3:
1637					cb.cb_columns[i++] = GET_COL_RECVD;
1638					flags |= ZFS_ITER_RECVD_PROPS;
1639					break;
1640				case 4:
1641					cb.cb_columns[i++] = GET_COL_SOURCE;
1642					break;
1643				case 5:
1644					if (i > 0) {
1645						(void) fprintf(stderr,
1646						    gettext("\"all\" conflicts "
1647						    "with specific fields "
1648						    "given to -o option\n"));
1649						usage(B_FALSE);
1650					}
1651					cb.cb_columns[0] = GET_COL_NAME;
1652					cb.cb_columns[1] = GET_COL_PROPERTY;
1653					cb.cb_columns[2] = GET_COL_VALUE;
1654					cb.cb_columns[3] = GET_COL_RECVD;
1655					cb.cb_columns[4] = GET_COL_SOURCE;
1656					flags |= ZFS_ITER_RECVD_PROPS;
1657					i = ZFS_GET_NCOLS;
1658					break;
1659				default:
1660					(void) fprintf(stderr,
1661					    gettext("invalid column name "
1662					    "'%s'\n"), value);
1663					usage(B_FALSE);
1664				}
1665			}
1666			break;
1667
1668		case 's':
1669			cb.cb_sources = 0;
1670			while (*optarg != '\0') {
1671				static char *source_subopts[] = {
1672					"local", "default", "inherited",
1673					"received", "temporary", "none",
1674					NULL };
1675
1676				switch (getsubopt(&optarg, source_subopts,
1677				    &value)) {
1678				case 0:
1679					cb.cb_sources |= ZPROP_SRC_LOCAL;
1680					break;
1681				case 1:
1682					cb.cb_sources |= ZPROP_SRC_DEFAULT;
1683					break;
1684				case 2:
1685					cb.cb_sources |= ZPROP_SRC_INHERITED;
1686					break;
1687				case 3:
1688					cb.cb_sources |= ZPROP_SRC_RECEIVED;
1689					break;
1690				case 4:
1691					cb.cb_sources |= ZPROP_SRC_TEMPORARY;
1692					break;
1693				case 5:
1694					cb.cb_sources |= ZPROP_SRC_NONE;
1695					break;
1696				default:
1697					(void) fprintf(stderr,
1698					    gettext("invalid source "
1699					    "'%s'\n"), value);
1700					usage(B_FALSE);
1701				}
1702			}
1703			break;
1704
1705		case 't':
1706			types = 0;
1707			flags &= ~ZFS_ITER_PROP_LISTSNAPS;
1708			while (*optarg != '\0') {
1709				static char *type_subopts[] = { "filesystem",
1710				    "volume", "snapshot", "bookmark",
1711				    "all", NULL };
1712
1713				switch (getsubopt(&optarg, type_subopts,
1714				    &value)) {
1715				case 0:
1716					types |= ZFS_TYPE_FILESYSTEM;
1717					break;
1718				case 1:
1719					types |= ZFS_TYPE_VOLUME;
1720					break;
1721				case 2:
1722					types |= ZFS_TYPE_SNAPSHOT;
1723					break;
1724				case 3:
1725					types |= ZFS_TYPE_BOOKMARK;
1726					break;
1727				case 4:
1728					types = ZFS_TYPE_DATASET |
1729					    ZFS_TYPE_BOOKMARK;
1730					break;
1731
1732				default:
1733					(void) fprintf(stderr,
1734					    gettext("invalid type '%s'\n"),
1735					    value);
1736					usage(B_FALSE);
1737				}
1738			}
1739			break;
1740
1741		case '?':
1742			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
1743			    optopt);
1744			usage(B_FALSE);
1745		}
1746	}
1747
1748	argc -= optind;
1749	argv += optind;
1750
1751	if (argc < 1) {
1752		(void) fprintf(stderr, gettext("missing property "
1753		    "argument\n"));
1754		usage(B_FALSE);
1755	}
1756
1757	fields = argv[0];
1758
1759	if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)
1760	    != 0)
1761		usage(B_FALSE);
1762
1763	argc--;
1764	argv++;
1765
1766	/*
1767	 * As part of zfs_expand_proplist(), we keep track of the maximum column
1768	 * width for each property.  For the 'NAME' (and 'SOURCE') columns, we
1769	 * need to know the maximum name length.  However, the user likely did
1770	 * not specify 'name' as one of the properties to fetch, so we need to
1771	 * make sure we always include at least this property for
1772	 * print_get_headers() to work properly.
1773	 */
1774	if (cb.cb_proplist != NULL) {
1775		fake_name.pl_prop = ZFS_PROP_NAME;
1776		fake_name.pl_width = strlen(gettext("NAME"));
1777		fake_name.pl_next = cb.cb_proplist;
1778		cb.cb_proplist = &fake_name;
1779	}
1780
1781	cb.cb_first = B_TRUE;
1782
1783	/* run for each object */
1784	ret = zfs_for_each(argc, argv, flags, types, NULL,
1785	    &cb.cb_proplist, limit, get_callback, &cb);
1786
1787	if (cb.cb_proplist == &fake_name)
1788		zprop_free_list(fake_name.pl_next);
1789	else
1790		zprop_free_list(cb.cb_proplist);
1791
1792	return (ret);
1793}
1794
1795/*
1796 * inherit [-rS] <property> <fs|vol> ...
1797 *
1798 *	-r	Recurse over all children
1799 *	-S	Revert to received value, if any
1800 *
1801 * For each dataset specified on the command line, inherit the given property
1802 * from its parent.  Inheriting a property at the pool level will cause it to
1803 * use the default value.  The '-r' flag will recurse over all children, and is
1804 * useful for setting a property on a hierarchy-wide basis, regardless of any
1805 * local modifications for each dataset.
1806 */
1807
1808typedef struct inherit_cbdata {
1809	const char *cb_propname;
1810	boolean_t cb_received;
1811} inherit_cbdata_t;
1812
1813static int
1814inherit_recurse_cb(zfs_handle_t *zhp, void *data)
1815{
1816	inherit_cbdata_t *cb = data;
1817	zfs_prop_t prop = zfs_name_to_prop(cb->cb_propname);
1818
1819	/*
1820	 * If we're doing it recursively, then ignore properties that
1821	 * are not valid for this type of dataset.
1822	 */
1823	if (prop != ZPROP_INVAL &&
1824	    !zfs_prop_valid_for_type(prop, zfs_get_type(zhp)))
1825		return (0);
1826
1827	return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0);
1828}
1829
1830static int
1831inherit_cb(zfs_handle_t *zhp, void *data)
1832{
1833	inherit_cbdata_t *cb = data;
1834
1835	return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0);
1836}
1837
1838static int
1839zfs_do_inherit(int argc, char **argv)
1840{
1841	int c;
1842	zfs_prop_t prop;
1843	inherit_cbdata_t cb = { 0 };
1844	char *propname;
1845	int ret = 0;
1846	int flags = 0;
1847	boolean_t received = B_FALSE;
1848
1849	/* check options */
1850	while ((c = getopt(argc, argv, "rS")) != -1) {
1851		switch (c) {
1852		case 'r':
1853			flags |= ZFS_ITER_RECURSE;
1854			break;
1855		case 'S':
1856			received = B_TRUE;
1857			break;
1858		case '?':
1859		default:
1860			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
1861			    optopt);
1862			usage(B_FALSE);
1863		}
1864	}
1865
1866	argc -= optind;
1867	argv += optind;
1868
1869	/* check number of arguments */
1870	if (argc < 1) {
1871		(void) fprintf(stderr, gettext("missing property argument\n"));
1872		usage(B_FALSE);
1873	}
1874	if (argc < 2) {
1875		(void) fprintf(stderr, gettext("missing dataset argument\n"));
1876		usage(B_FALSE);
1877	}
1878
1879	propname = argv[0];
1880	argc--;
1881	argv++;
1882
1883	if ((prop = zfs_name_to_prop(propname)) != ZPROP_INVAL) {
1884		if (zfs_prop_readonly(prop)) {
1885			(void) fprintf(stderr, gettext(
1886			    "%s property is read-only\n"),
1887			    propname);
1888			return (1);
1889		}
1890		if (!zfs_prop_inheritable(prop) && !received) {
1891			(void) fprintf(stderr, gettext("'%s' property cannot "
1892			    "be inherited\n"), propname);
1893			if (prop == ZFS_PROP_QUOTA ||
1894			    prop == ZFS_PROP_RESERVATION ||
1895			    prop == ZFS_PROP_REFQUOTA ||
1896			    prop == ZFS_PROP_REFRESERVATION)
1897				(void) fprintf(stderr, gettext("use 'zfs set "
1898				    "%s=none' to clear\n"), propname);
1899			return (1);
1900		}
1901		if (received && (prop == ZFS_PROP_VOLSIZE ||
1902		    prop == ZFS_PROP_VERSION)) {
1903			(void) fprintf(stderr, gettext("'%s' property cannot "
1904			    "be reverted to a received value\n"), propname);
1905			return (1);
1906		}
1907	} else if (!zfs_prop_user(propname)) {
1908		(void) fprintf(stderr, gettext("invalid property '%s'\n"),
1909		    propname);
1910		usage(B_FALSE);
1911	}
1912
1913	cb.cb_propname = propname;
1914	cb.cb_received = received;
1915
1916	if (flags & ZFS_ITER_RECURSE) {
1917		ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,
1918		    NULL, NULL, 0, inherit_recurse_cb, &cb);
1919	} else {
1920		ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,
1921		    NULL, NULL, 0, inherit_cb, &cb);
1922	}
1923
1924	return (ret);
1925}
1926
1927typedef struct upgrade_cbdata {
1928	uint64_t cb_numupgraded;
1929	uint64_t cb_numsamegraded;
1930	uint64_t cb_numfailed;
1931	uint64_t cb_version;
1932	boolean_t cb_newer;
1933	boolean_t cb_foundone;
1934	char cb_lastfs[ZFS_MAXNAMELEN];
1935} upgrade_cbdata_t;
1936
1937static int
1938same_pool(zfs_handle_t *zhp, const char *name)
1939{
1940	int len1 = strcspn(name, "/@");
1941	const char *zhname = zfs_get_name(zhp);
1942	int len2 = strcspn(zhname, "/@");
1943
1944	if (len1 != len2)
1945		return (B_FALSE);
1946	return (strncmp(name, zhname, len1) == 0);
1947}
1948
1949static int
1950upgrade_list_callback(zfs_handle_t *zhp, void *data)
1951{
1952	upgrade_cbdata_t *cb = data;
1953	int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
1954
1955	/* list if it's old/new */
1956	if ((!cb->cb_newer && version < ZPL_VERSION) ||
1957	    (cb->cb_newer && version > ZPL_VERSION)) {
1958		char *str;
1959		if (cb->cb_newer) {
1960			str = gettext("The following filesystems are "
1961			    "formatted using a newer software version and\n"
1962			    "cannot be accessed on the current system.\n\n");
1963		} else {
1964			str = gettext("The following filesystems are "
1965			    "out of date, and can be upgraded.  After being\n"
1966			    "upgraded, these filesystems (and any 'zfs send' "
1967			    "streams generated from\n"
1968			    "subsequent snapshots) will no longer be "
1969			    "accessible by older software versions.\n\n");
1970		}
1971
1972		if (!cb->cb_foundone) {
1973			(void) puts(str);
1974			(void) printf(gettext("VER  FILESYSTEM\n"));
1975			(void) printf(gettext("---  ------------\n"));
1976			cb->cb_foundone = B_TRUE;
1977		}
1978
1979		(void) printf("%2u   %s\n", version, zfs_get_name(zhp));
1980	}
1981
1982	return (0);
1983}
1984
1985static int
1986upgrade_set_callback(zfs_handle_t *zhp, void *data)
1987{
1988	upgrade_cbdata_t *cb = data;
1989	int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
1990	int needed_spa_version;
1991	int spa_version;
1992
1993	if (zfs_spa_version(zhp, &spa_version) < 0)
1994		return (-1);
1995
1996	needed_spa_version = zfs_spa_version_map(cb->cb_version);
1997
1998	if (needed_spa_version < 0)
1999		return (-1);
2000
2001	if (spa_version < needed_spa_version) {
2002		/* can't upgrade */
2003		(void) printf(gettext("%s: can not be "
2004		    "upgraded; the pool version needs to first "
2005		    "be upgraded\nto version %d\n\n"),
2006		    zfs_get_name(zhp), needed_spa_version);
2007		cb->cb_numfailed++;
2008		return (0);
2009	}
2010
2011	/* upgrade */
2012	if (version < cb->cb_version) {
2013		char verstr[16];
2014		(void) snprintf(verstr, sizeof (verstr),
2015		    "%llu", cb->cb_version);
2016		if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) {
2017			/*
2018			 * If they did "zfs upgrade -a", then we could
2019			 * be doing ioctls to different pools.  We need
2020			 * to log this history once to each pool, and bypass
2021			 * the normal history logging that happens in main().
2022			 */
2023			(void) zpool_log_history(g_zfs, history_str);
2024			log_history = B_FALSE;
2025		}
2026		if (zfs_prop_set(zhp, "version", verstr) == 0)
2027			cb->cb_numupgraded++;
2028		else
2029			cb->cb_numfailed++;
2030		(void) strcpy(cb->cb_lastfs, zfs_get_name(zhp));
2031	} else if (version > cb->cb_version) {
2032		/* can't downgrade */
2033		(void) printf(gettext("%s: can not be downgraded; "
2034		    "it is already at version %u\n"),
2035		    zfs_get_name(zhp), version);
2036		cb->cb_numfailed++;
2037	} else {
2038		cb->cb_numsamegraded++;
2039	}
2040	return (0);
2041}
2042
2043/*
2044 * zfs upgrade
2045 * zfs upgrade -v
2046 * zfs upgrade [-r] [-V <version>] <-a | filesystem>
2047 */
2048static int
2049zfs_do_upgrade(int argc, char **argv)
2050{
2051	boolean_t all = B_FALSE;
2052	boolean_t showversions = B_FALSE;
2053	int ret = 0;
2054	upgrade_cbdata_t cb = { 0 };
2055	char c;
2056	int flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
2057
2058	/* check options */
2059	while ((c = getopt(argc, argv, "rvV:a")) != -1) {
2060		switch (c) {
2061		case 'r':
2062			flags |= ZFS_ITER_RECURSE;
2063			break;
2064		case 'v':
2065			showversions = B_TRUE;
2066			break;
2067		case 'V':
2068			if (zfs_prop_string_to_index(ZFS_PROP_VERSION,
2069			    optarg, &cb.cb_version) != 0) {
2070				(void) fprintf(stderr,
2071				    gettext("invalid version %s\n"), optarg);
2072				usage(B_FALSE);
2073			}
2074			break;
2075		case 'a':
2076			all = B_TRUE;
2077			break;
2078		case '?':
2079		default:
2080			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
2081			    optopt);
2082			usage(B_FALSE);
2083		}
2084	}
2085
2086	argc -= optind;
2087	argv += optind;
2088
2089	if ((!all && !argc) && ((flags & ZFS_ITER_RECURSE) | cb.cb_version))
2090		usage(B_FALSE);
2091	if (showversions && (flags & ZFS_ITER_RECURSE || all ||
2092	    cb.cb_version || argc))
2093		usage(B_FALSE);
2094	if ((all || argc) && (showversions))
2095		usage(B_FALSE);
2096	if (all && argc)
2097		usage(B_FALSE);
2098
2099	if (showversions) {
2100		/* Show info on available versions. */
2101		(void) printf(gettext("The following filesystem versions are "
2102		    "supported:\n\n"));
2103		(void) printf(gettext("VER  DESCRIPTION\n"));
2104		(void) printf("---  -----------------------------------------"
2105		    "---------------\n");
2106		(void) printf(gettext(" 1   Initial ZFS filesystem version\n"));
2107		(void) printf(gettext(" 2   Enhanced directory entries\n"));
2108		(void) printf(gettext(" 3   Case insensitive and filesystem "
2109		    "user identifier (FUID)\n"));
2110		(void) printf(gettext(" 4   userquota, groupquota "
2111		    "properties\n"));
2112		(void) printf(gettext(" 5   System attributes\n"));
2113		(void) printf(gettext("\nFor more information on a particular "
2114		    "version, including supported releases,\n"));
2115		(void) printf("see the ZFS Administration Guide.\n\n");
2116		ret = 0;
2117	} else if (argc || all) {
2118		/* Upgrade filesystems */
2119		if (cb.cb_version == 0)
2120			cb.cb_version = ZPL_VERSION;
2121		ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_FILESYSTEM,
2122		    NULL, NULL, 0, upgrade_set_callback, &cb);
2123		(void) printf(gettext("%llu filesystems upgraded\n"),
2124		    cb.cb_numupgraded);
2125		if (cb.cb_numsamegraded) {
2126			(void) printf(gettext("%llu filesystems already at "
2127			    "this version\n"),
2128			    cb.cb_numsamegraded);
2129		}
2130		if (cb.cb_numfailed != 0)
2131			ret = 1;
2132	} else {
2133		/* List old-version filesytems */
2134		boolean_t found;
2135		(void) printf(gettext("This system is currently running "
2136		    "ZFS filesystem version %llu.\n\n"), ZPL_VERSION);
2137
2138		flags |= ZFS_ITER_RECURSE;
2139		ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,
2140		    NULL, NULL, 0, upgrade_list_callback, &cb);
2141
2142		found = cb.cb_foundone;
2143		cb.cb_foundone = B_FALSE;
2144		cb.cb_newer = B_TRUE;
2145
2146		ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,
2147		    NULL, NULL, 0, upgrade_list_callback, &cb);
2148
2149		if (!cb.cb_foundone && !found) {
2150			(void) printf(gettext("All filesystems are "
2151			    "formatted with the current version.\n"));
2152		}
2153	}
2154
2155	return (ret);
2156}
2157
2158/*
2159 * zfs userspace [-Hinp] [-o field[,...]] [-s field [-s field]...]
2160 *               [-S field [-S field]...] [-t type[,...]] filesystem | snapshot
2161 * zfs groupspace [-Hinp] [-o field[,...]] [-s field [-s field]...]
2162 *                [-S field [-S field]...] [-t type[,...]] filesystem | snapshot
2163 *
2164 *	-H      Scripted mode; elide headers and separate columns by tabs.
2165 *	-i	Translate SID to POSIX ID.
2166 *	-n	Print numeric ID instead of user/group name.
2167 *	-o      Control which fields to display.
2168 *	-p	Use exact (parsable) numeric output.
2169 *	-s      Specify sort columns, descending order.
2170 *	-S      Specify sort columns, ascending order.
2171 *	-t      Control which object types to display.
2172 *
2173 *	Displays space consumed by, and quotas on, each user in the specified
2174 *	filesystem or snapshot.
2175 */
2176
2177/* us_field_types, us_field_hdr and us_field_names should be kept in sync */
2178enum us_field_types {
2179	USFIELD_TYPE,
2180	USFIELD_NAME,
2181	USFIELD_USED,
2182	USFIELD_QUOTA
2183};
2184static char *us_field_hdr[] = { "TYPE", "NAME", "USED", "QUOTA" };
2185static char *us_field_names[] = { "type", "name", "used", "quota" };
2186#define	USFIELD_LAST	(sizeof (us_field_names) / sizeof (char *))
2187
2188#define	USTYPE_PSX_GRP	(1 << 0)
2189#define	USTYPE_PSX_USR	(1 << 1)
2190#define	USTYPE_SMB_GRP	(1 << 2)
2191#define	USTYPE_SMB_USR	(1 << 3)
2192#define	USTYPE_ALL	\
2193	(USTYPE_PSX_GRP | USTYPE_PSX_USR | USTYPE_SMB_GRP | USTYPE_SMB_USR)
2194
2195static int us_type_bits[] = {
2196	USTYPE_PSX_GRP,
2197	USTYPE_PSX_USR,
2198	USTYPE_SMB_GRP,
2199	USTYPE_SMB_USR,
2200	USTYPE_ALL
2201};
2202static char *us_type_names[] = { "posixgroup", "posxiuser", "smbgroup",
2203	"smbuser", "all" };
2204
2205typedef struct us_node {
2206	nvlist_t	*usn_nvl;
2207	uu_avl_node_t	usn_avlnode;
2208	uu_list_node_t	usn_listnode;
2209} us_node_t;
2210
2211typedef struct us_cbdata {
2212	nvlist_t	**cb_nvlp;
2213	uu_avl_pool_t	*cb_avl_pool;
2214	uu_avl_t	*cb_avl;
2215	boolean_t	cb_numname;
2216	boolean_t	cb_nicenum;
2217	boolean_t	cb_sid2posix;
2218	zfs_userquota_prop_t cb_prop;
2219	zfs_sort_column_t *cb_sortcol;
2220	size_t		cb_width[USFIELD_LAST];
2221} us_cbdata_t;
2222
2223static boolean_t us_populated = B_FALSE;
2224
2225typedef struct {
2226	zfs_sort_column_t *si_sortcol;
2227	boolean_t	si_numname;
2228} us_sort_info_t;
2229
2230static int
2231us_field_index(char *field)
2232{
2233	int i;
2234
2235	for (i = 0; i < USFIELD_LAST; i++) {
2236		if (strcmp(field, us_field_names[i]) == 0)
2237			return (i);
2238	}
2239
2240	return (-1);
2241}
2242
2243static int
2244us_compare(const void *larg, const void *rarg, void *unused)
2245{
2246	const us_node_t *l = larg;
2247	const us_node_t *r = rarg;
2248	us_sort_info_t *si = (us_sort_info_t *)unused;
2249	zfs_sort_column_t *sortcol = si->si_sortcol;
2250	boolean_t numname = si->si_numname;
2251	nvlist_t *lnvl = l->usn_nvl;
2252	nvlist_t *rnvl = r->usn_nvl;
2253	int rc = 0;
2254	boolean_t lvb, rvb;
2255
2256	for (; sortcol != NULL; sortcol = sortcol->sc_next) {
2257		char *lvstr = "";
2258		char *rvstr = "";
2259		uint32_t lv32 = 0;
2260		uint32_t rv32 = 0;
2261		uint64_t lv64 = 0;
2262		uint64_t rv64 = 0;
2263		zfs_prop_t prop = sortcol->sc_prop;
2264		const char *propname = NULL;
2265		boolean_t reverse = sortcol->sc_reverse;
2266
2267		switch (prop) {
2268		case ZFS_PROP_TYPE:
2269			propname = "type";
2270			(void) nvlist_lookup_uint32(lnvl, propname, &lv32);
2271			(void) nvlist_lookup_uint32(rnvl, propname, &rv32);
2272			if (rv32 != lv32)
2273				rc = (rv32 < lv32) ? 1 : -1;
2274			break;
2275		case ZFS_PROP_NAME:
2276			propname = "name";
2277			if (numname) {
2278				(void) nvlist_lookup_uint64(lnvl, propname,
2279				    &lv64);
2280				(void) nvlist_lookup_uint64(rnvl, propname,
2281				    &rv64);
2282				if (rv64 != lv64)
2283					rc = (rv64 < lv64) ? 1 : -1;
2284			} else {
2285				(void) nvlist_lookup_string(lnvl, propname,
2286				    &lvstr);
2287				(void) nvlist_lookup_string(rnvl, propname,
2288				    &rvstr);
2289				rc = strcmp(lvstr, rvstr);
2290			}
2291			break;
2292		case ZFS_PROP_USED:
2293		case ZFS_PROP_QUOTA:
2294			if (!us_populated)
2295				break;
2296			if (prop == ZFS_PROP_USED)
2297				propname = "used";
2298			else
2299				propname = "quota";
2300			(void) nvlist_lookup_uint64(lnvl, propname, &lv64);
2301			(void) nvlist_lookup_uint64(rnvl, propname, &rv64);
2302			if (rv64 != lv64)
2303				rc = (rv64 < lv64) ? 1 : -1;
2304			break;
2305		}
2306
2307		if (rc != 0) {
2308			if (rc < 0)
2309				return (reverse ? 1 : -1);
2310			else
2311				return (reverse ? -1 : 1);
2312		}
2313	}
2314
2315	/*
2316	 * If entries still seem to be the same, check if they are of the same
2317	 * type (smbentity is added only if we are doing SID to POSIX ID
2318	 * translation where we can have duplicate type/name combinations).
2319	 */
2320	if (nvlist_lookup_boolean_value(lnvl, "smbentity", &lvb) == 0 &&
2321	    nvlist_lookup_boolean_value(rnvl, "smbentity", &rvb) == 0 &&
2322	    lvb != rvb)
2323		return (lvb < rvb ? -1 : 1);
2324
2325	return (0);
2326}
2327
2328static inline const char *
2329us_type2str(unsigned field_type)
2330{
2331	switch (field_type) {
2332	case USTYPE_PSX_USR:
2333		return ("POSIX User");
2334	case USTYPE_PSX_GRP:
2335		return ("POSIX Group");
2336	case USTYPE_SMB_USR:
2337		return ("SMB User");
2338	case USTYPE_SMB_GRP:
2339		return ("SMB Group");
2340	default:
2341		return ("Undefined");
2342	}
2343}
2344
2345static int
2346userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space)
2347{
2348	us_cbdata_t *cb = (us_cbdata_t *)arg;
2349	zfs_userquota_prop_t prop = cb->cb_prop;
2350	char *name = NULL;
2351	char *propname;
2352	char sizebuf[32];
2353	us_node_t *node;
2354	uu_avl_pool_t *avl_pool = cb->cb_avl_pool;
2355	uu_avl_t *avl = cb->cb_avl;
2356	uu_avl_index_t idx;
2357	nvlist_t *props;
2358	us_node_t *n;
2359	zfs_sort_column_t *sortcol = cb->cb_sortcol;
2360	unsigned type;
2361	const char *typestr;
2362	size_t namelen;
2363	size_t typelen;
2364	size_t sizelen;
2365	int typeidx, nameidx, sizeidx;
2366	us_sort_info_t sortinfo = { sortcol, cb->cb_numname };
2367	boolean_t smbentity = B_FALSE;
2368
2369	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
2370		nomem();
2371	node = safe_malloc(sizeof (us_node_t));
2372	uu_avl_node_init(node, &node->usn_avlnode, avl_pool);
2373	node->usn_nvl = props;
2374
2375	if (domain != NULL && domain[0] != '\0') {
2376		/* SMB */
2377		char sid[ZFS_MAXNAMELEN + 32];
2378		uid_t id;
2379		uint64_t classes;
2380#ifdef sun
2381		int err;
2382		directory_error_t e;
2383#endif
2384
2385		smbentity = B_TRUE;
2386
2387		(void) snprintf(sid, sizeof (sid), "%s-%u", domain, rid);
2388
2389		if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) {
2390			type = USTYPE_SMB_GRP;
2391#ifdef sun
2392			err = sid_to_id(sid, B_FALSE, &id);
2393#endif
2394		} else {
2395			type = USTYPE_SMB_USR;
2396#ifdef sun
2397			err = sid_to_id(sid, B_TRUE, &id);
2398#endif
2399		}
2400
2401#ifdef sun
2402		if (err == 0) {
2403			rid = id;
2404			if (!cb->cb_sid2posix) {
2405				e = directory_name_from_sid(NULL, sid, &name,
2406				    &classes);
2407				if (e != NULL)
2408					directory_error_free(e);
2409				if (name == NULL)
2410					name = sid;
2411			}
2412		}
2413#endif
2414	}
2415
2416	if (cb->cb_sid2posix || domain == NULL || domain[0] == '\0') {
2417		/* POSIX or -i */
2418		if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) {
2419			type = USTYPE_PSX_GRP;
2420			if (!cb->cb_numname) {
2421				struct group *g;
2422
2423				if ((g = getgrgid(rid)) != NULL)
2424					name = g->gr_name;
2425			}
2426		} else {
2427			type = USTYPE_PSX_USR;
2428			if (!cb->cb_numname) {
2429				struct passwd *p;
2430
2431				if ((p = getpwuid(rid)) != NULL)
2432					name = p->pw_name;
2433			}
2434		}
2435	}
2436
2437	/*
2438	 * Make sure that the type/name combination is unique when doing
2439	 * SID to POSIX ID translation (hence changing the type from SMB to
2440	 * POSIX).
2441	 */
2442	if (cb->cb_sid2posix &&
2443	    nvlist_add_boolean_value(props, "smbentity", smbentity) != 0)
2444		nomem();
2445
2446	/* Calculate/update width of TYPE field */
2447	typestr = us_type2str(type);
2448	typelen = strlen(gettext(typestr));
2449	typeidx = us_field_index("type");
2450	if (typelen > cb->cb_width[typeidx])
2451		cb->cb_width[typeidx] = typelen;
2452	if (nvlist_add_uint32(props, "type", type) != 0)
2453		nomem();
2454
2455	/* Calculate/update width of NAME field */
2456	if ((cb->cb_numname && cb->cb_sid2posix) || name == NULL) {
2457		if (nvlist_add_uint64(props, "name", rid) != 0)
2458			nomem();
2459		namelen = snprintf(NULL, 0, "%u", rid);
2460	} else {
2461		if (nvlist_add_string(props, "name", name) != 0)
2462			nomem();
2463		namelen = strlen(name);
2464	}
2465	nameidx = us_field_index("name");
2466	if (namelen > cb->cb_width[nameidx])
2467		cb->cb_width[nameidx] = namelen;
2468
2469	/*
2470	 * Check if this type/name combination is in the list and update it;
2471	 * otherwise add new node to the list.
2472	 */
2473	if ((n = uu_avl_find(avl, node, &sortinfo, &idx)) == NULL) {
2474		uu_avl_insert(avl, node, idx);
2475	} else {
2476		nvlist_free(props);
2477		free(node);
2478		node = n;
2479		props = node->usn_nvl;
2480	}
2481
2482	/* Calculate/update width of USED/QUOTA fields */
2483	if (cb->cb_nicenum)
2484		zfs_nicenum(space, sizebuf, sizeof (sizebuf));
2485	else
2486		(void) snprintf(sizebuf, sizeof (sizebuf), "%llu", space);
2487	sizelen = strlen(sizebuf);
2488	if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED) {
2489		propname = "used";
2490		if (!nvlist_exists(props, "quota"))
2491			(void) nvlist_add_uint64(props, "quota", 0);
2492	} else {
2493		propname = "quota";
2494		if (!nvlist_exists(props, "used"))
2495			(void) nvlist_add_uint64(props, "used", 0);
2496	}
2497	sizeidx = us_field_index(propname);
2498	if (sizelen > cb->cb_width[sizeidx])
2499		cb->cb_width[sizeidx] = sizelen;
2500
2501	if (nvlist_add_uint64(props, propname, space) != 0)
2502		nomem();
2503
2504	return (0);
2505}
2506
2507static void
2508print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types,
2509    size_t *width, us_node_t *node)
2510{
2511	nvlist_t *nvl = node->usn_nvl;
2512	char valstr[ZFS_MAXNAMELEN];
2513	boolean_t first = B_TRUE;
2514	int cfield = 0;
2515	int field;
2516	uint32_t ustype;
2517
2518	/* Check type */
2519	(void) nvlist_lookup_uint32(nvl, "type", &ustype);
2520	if (!(ustype & types))
2521		return;
2522
2523	while ((field = fields[cfield]) != USFIELD_LAST) {
2524		nvpair_t *nvp = NULL;
2525		data_type_t type;
2526		uint32_t val32;
2527		uint64_t val64;
2528		char *strval = NULL;
2529
2530		while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
2531			if (strcmp(nvpair_name(nvp),
2532			    us_field_names[field]) == 0)
2533				break;
2534		}
2535
2536		type = nvpair_type(nvp);
2537		switch (type) {
2538		case DATA_TYPE_UINT32:
2539			(void) nvpair_value_uint32(nvp, &val32);
2540			break;
2541		case DATA_TYPE_UINT64:
2542			(void) nvpair_value_uint64(nvp, &val64);
2543			break;
2544		case DATA_TYPE_STRING:
2545			(void) nvpair_value_string(nvp, &strval);
2546			break;
2547		default:
2548			(void) fprintf(stderr, "invalid data type\n");
2549		}
2550
2551		switch (field) {
2552		case USFIELD_TYPE:
2553			strval = (char *)us_type2str(val32);
2554			break;
2555		case USFIELD_NAME:
2556			if (type == DATA_TYPE_UINT64) {
2557				(void) sprintf(valstr, "%llu", val64);
2558				strval = valstr;
2559			}
2560			break;
2561		case USFIELD_USED:
2562		case USFIELD_QUOTA:
2563			if (type == DATA_TYPE_UINT64) {
2564				if (parsable) {
2565					(void) sprintf(valstr, "%llu", val64);
2566				} else {
2567					zfs_nicenum(val64, valstr,
2568					    sizeof (valstr));
2569				}
2570				if (field == USFIELD_QUOTA &&
2571				    strcmp(valstr, "0") == 0)
2572					strval = "none";
2573				else
2574					strval = valstr;
2575			}
2576			break;
2577		}
2578
2579		if (!first) {
2580			if (scripted)
2581				(void) printf("\t");
2582			else
2583				(void) printf("  ");
2584		}
2585		if (scripted)
2586			(void) printf("%s", strval);
2587		else if (field == USFIELD_TYPE || field == USFIELD_NAME)
2588			(void) printf("%-*s", width[field], strval);
2589		else
2590			(void) printf("%*s", width[field], strval);
2591
2592		first = B_FALSE;
2593		cfield++;
2594	}
2595
2596	(void) printf("\n");
2597}
2598
2599static void
2600print_us(boolean_t scripted, boolean_t parsable, int *fields, int types,
2601    size_t *width, boolean_t rmnode, uu_avl_t *avl)
2602{
2603	us_node_t *node;
2604	const char *col;
2605	int cfield = 0;
2606	int field;
2607
2608	if (!scripted) {
2609		boolean_t first = B_TRUE;
2610
2611		while ((field = fields[cfield]) != USFIELD_LAST) {
2612			col = gettext(us_field_hdr[field]);
2613			if (field == USFIELD_TYPE || field == USFIELD_NAME) {
2614				(void) printf(first ? "%-*s" : "  %-*s",
2615				    width[field], col);
2616			} else {
2617				(void) printf(first ? "%*s" : "  %*s",
2618				    width[field], col);
2619			}
2620			first = B_FALSE;
2621			cfield++;
2622		}
2623		(void) printf("\n");
2624	}
2625
2626	for (node = uu_avl_first(avl); node; node = uu_avl_next(avl, node)) {
2627		print_us_node(scripted, parsable, fields, types, width, node);
2628		if (rmnode)
2629			nvlist_free(node->usn_nvl);
2630	}
2631}
2632
2633static int
2634zfs_do_userspace(int argc, char **argv)
2635{
2636	zfs_handle_t *zhp;
2637	zfs_userquota_prop_t p;
2638
2639	uu_avl_pool_t *avl_pool;
2640	uu_avl_t *avl_tree;
2641	uu_avl_walk_t *walk;
2642	char *delim;
2643	char deffields[] = "type,name,used,quota";
2644	char *ofield = NULL;
2645	char *tfield = NULL;
2646	int cfield = 0;
2647	int fields[256];
2648	int i;
2649	boolean_t scripted = B_FALSE;
2650	boolean_t prtnum = B_FALSE;
2651	boolean_t parsable = B_FALSE;
2652	boolean_t sid2posix = B_FALSE;
2653	int ret = 0;
2654	int c;
2655	zfs_sort_column_t *sortcol = NULL;
2656	int types = USTYPE_PSX_USR | USTYPE_SMB_USR;
2657	us_cbdata_t cb;
2658	us_node_t *node;
2659	us_node_t *rmnode;
2660	uu_list_pool_t *listpool;
2661	uu_list_t *list;
2662	uu_avl_index_t idx = 0;
2663	uu_list_index_t idx2 = 0;
2664
2665	if (argc < 2)
2666		usage(B_FALSE);
2667
2668	if (strcmp(argv[0], "groupspace") == 0)
2669		/* Toggle default group types */
2670		types = USTYPE_PSX_GRP | USTYPE_SMB_GRP;
2671
2672	while ((c = getopt(argc, argv, "nHpo:s:S:t:i")) != -1) {
2673		switch (c) {
2674		case 'n':
2675			prtnum = B_TRUE;
2676			break;
2677		case 'H':
2678			scripted = B_TRUE;
2679			break;
2680		case 'p':
2681			parsable = B_TRUE;
2682			break;
2683		case 'o':
2684			ofield = optarg;
2685			break;
2686		case 's':
2687		case 'S':
2688			if (zfs_add_sort_column(&sortcol, optarg,
2689			    c == 's' ? B_FALSE : B_TRUE) != 0) {
2690				(void) fprintf(stderr,
2691				    gettext("invalid field '%s'\n"), optarg);
2692				usage(B_FALSE);
2693			}
2694			break;
2695		case 't':
2696			tfield = optarg;
2697			break;
2698		case 'i':
2699			sid2posix = B_TRUE;
2700			break;
2701		case ':':
2702			(void) fprintf(stderr, gettext("missing argument for "
2703			    "'%c' option\n"), optopt);
2704			usage(B_FALSE);
2705			break;
2706		case '?':
2707			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
2708			    optopt);
2709			usage(B_FALSE);
2710		}
2711	}
2712
2713	argc -= optind;
2714	argv += optind;
2715
2716	if (argc < 1) {
2717		(void) fprintf(stderr, gettext("missing dataset name\n"));
2718		usage(B_FALSE);
2719	}
2720	if (argc > 1) {
2721		(void) fprintf(stderr, gettext("too many arguments\n"));
2722		usage(B_FALSE);
2723	}
2724
2725	/* Use default output fields if not specified using -o */
2726	if (ofield == NULL)
2727		ofield = deffields;
2728	do {
2729		if ((delim = strchr(ofield, ',')) != NULL)
2730			*delim = '\0';
2731		if ((fields[cfield++] = us_field_index(ofield)) == -1) {
2732			(void) fprintf(stderr, gettext("invalid type '%s' "
2733			    "for -o option\n"), ofield);
2734			return (-1);
2735		}
2736		if (delim != NULL)
2737			ofield = delim + 1;
2738	} while (delim != NULL);
2739	fields[cfield] = USFIELD_LAST;
2740
2741	/* Override output types (-t option) */
2742	if (tfield != NULL) {
2743		types = 0;
2744
2745		do {
2746			boolean_t found = B_FALSE;
2747
2748			if ((delim = strchr(tfield, ',')) != NULL)
2749				*delim = '\0';
2750			for (i = 0; i < sizeof (us_type_bits) / sizeof (int);
2751			    i++) {
2752				if (strcmp(tfield, us_type_names[i]) == 0) {
2753					found = B_TRUE;
2754					types |= us_type_bits[i];
2755					break;
2756				}
2757			}
2758			if (!found) {
2759				(void) fprintf(stderr, gettext("invalid type "
2760				    "'%s' for -t option\n"), tfield);
2761				return (-1);
2762			}
2763			if (delim != NULL)
2764				tfield = delim + 1;
2765		} while (delim != NULL);
2766	}
2767
2768	if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL)
2769		return (1);
2770
2771	if ((avl_pool = uu_avl_pool_create("us_avl_pool", sizeof (us_node_t),
2772	    offsetof(us_node_t, usn_avlnode), us_compare, UU_DEFAULT)) == NULL)
2773		nomem();
2774	if ((avl_tree = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL)
2775		nomem();
2776
2777	/* Always add default sorting columns */
2778	(void) zfs_add_sort_column(&sortcol, "type", B_FALSE);
2779	(void) zfs_add_sort_column(&sortcol, "name", B_FALSE);
2780
2781	cb.cb_sortcol = sortcol;
2782	cb.cb_numname = prtnum;
2783	cb.cb_nicenum = !parsable;
2784	cb.cb_avl_pool = avl_pool;
2785	cb.cb_avl = avl_tree;
2786	cb.cb_sid2posix = sid2posix;
2787
2788	for (i = 0; i < USFIELD_LAST; i++)
2789		cb.cb_width[i] = strlen(gettext(us_field_hdr[i]));
2790
2791	for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) {
2792		if (((p == ZFS_PROP_USERUSED || p == ZFS_PROP_USERQUOTA) &&
2793		    !(types & (USTYPE_PSX_USR | USTYPE_SMB_USR))) ||
2794		    ((p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA) &&
2795		    !(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP))))
2796			continue;
2797		cb.cb_prop = p;
2798		if ((ret = zfs_userspace(zhp, p, userspace_cb, &cb)) != 0)
2799			return (ret);
2800	}
2801
2802	/* Sort the list */
2803	if ((node = uu_avl_first(avl_tree)) == NULL)
2804		return (0);
2805
2806	us_populated = B_TRUE;
2807
2808	listpool = uu_list_pool_create("tmplist", sizeof (us_node_t),
2809	    offsetof(us_node_t, usn_listnode), NULL, UU_DEFAULT);
2810	list = uu_list_create(listpool, NULL, UU_DEFAULT);
2811	uu_list_node_init(node, &node->usn_listnode, listpool);
2812
2813	while (node != NULL) {
2814		rmnode = node;
2815		node = uu_avl_next(avl_tree, node);
2816		uu_avl_remove(avl_tree, rmnode);
2817		if (uu_list_find(list, rmnode, NULL, &idx2) == NULL)
2818			uu_list_insert(list, rmnode, idx2);
2819	}
2820
2821	for (node = uu_list_first(list); node != NULL;
2822	    node = uu_list_next(list, node)) {
2823		us_sort_info_t sortinfo = { sortcol, cb.cb_numname };
2824
2825		if (uu_avl_find(avl_tree, node, &sortinfo, &idx) == NULL)
2826			uu_avl_insert(avl_tree, node, idx);
2827	}
2828
2829	uu_list_destroy(list);
2830	uu_list_pool_destroy(listpool);
2831
2832	/* Print and free node nvlist memory */
2833	print_us(scripted, parsable, fields, types, cb.cb_width, B_TRUE,
2834	    cb.cb_avl);
2835
2836	zfs_free_sort_columns(sortcol);
2837
2838	/* Clean up the AVL tree */
2839	if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL)
2840		nomem();
2841
2842	while ((node = uu_avl_walk_next(walk)) != NULL) {
2843		uu_avl_remove(cb.cb_avl, node);
2844		free(node);
2845	}
2846
2847	uu_avl_walk_end(walk);
2848	uu_avl_destroy(avl_tree);
2849	uu_avl_pool_destroy(avl_pool);
2850
2851	return (ret);
2852}
2853
2854/*
2855 * list [-Hp][-r|-d max] [-o property[,...]] [-s property] ... [-S property] ...
2856 *      [-t type[,...]] [filesystem|volume|snapshot] ...
2857 *
2858 *	-H	Scripted mode; elide headers and separate columns by tabs.
2859 *	-p	Display values in parsable (literal) format.
2860 *	-r	Recurse over all children.
2861 *	-d	Limit recursion by depth.
2862 *	-o	Control which fields to display.
2863 *	-s	Specify sort columns, descending order.
2864 *	-S	Specify sort columns, ascending order.
2865 *	-t	Control which object types to display.
2866 *
2867 * When given no arguments, list all filesystems in the system.
2868 * Otherwise, list the specified datasets, optionally recursing down them if
2869 * '-r' is specified.
2870 */
2871typedef struct list_cbdata {
2872	boolean_t	cb_first;
2873	boolean_t	cb_literal;
2874	boolean_t	cb_scripted;
2875	zprop_list_t	*cb_proplist;
2876} list_cbdata_t;
2877
2878/*
2879 * Given a list of columns to display, output appropriate headers for each one.
2880 */
2881static void
2882print_header(list_cbdata_t *cb)
2883{
2884	zprop_list_t *pl = cb->cb_proplist;
2885	char headerbuf[ZFS_MAXPROPLEN];
2886	const char *header;
2887	int i;
2888	boolean_t first = B_TRUE;
2889	boolean_t right_justify;
2890
2891	for (; pl != NULL; pl = pl->pl_next) {
2892		if (!first) {
2893			(void) printf("  ");
2894		} else {
2895			first = B_FALSE;
2896		}
2897
2898		right_justify = B_FALSE;
2899		if (pl->pl_prop != ZPROP_INVAL) {
2900			header = zfs_prop_column_name(pl->pl_prop);
2901			right_justify = zfs_prop_align_right(pl->pl_prop);
2902		} else {
2903			for (i = 0; pl->pl_user_prop[i] != '\0'; i++)
2904				headerbuf[i] = toupper(pl->pl_user_prop[i]);
2905			headerbuf[i] = '\0';
2906			header = headerbuf;
2907		}
2908
2909		if (pl->pl_next == NULL && !right_justify)
2910			(void) printf("%s", header);
2911		else if (right_justify)
2912			(void) printf("%*s", pl->pl_width, header);
2913		else
2914			(void) printf("%-*s", pl->pl_width, header);
2915	}
2916
2917	(void) printf("\n");
2918}
2919
2920/*
2921 * Given a dataset and a list of fields, print out all the properties according
2922 * to the described layout.
2923 */
2924static void
2925print_dataset(zfs_handle_t *zhp, list_cbdata_t *cb)
2926{
2927	zprop_list_t *pl = cb->cb_proplist;
2928	boolean_t first = B_TRUE;
2929	char property[ZFS_MAXPROPLEN];
2930	nvlist_t *userprops = zfs_get_user_props(zhp);
2931	nvlist_t *propval;
2932	char *propstr;
2933	boolean_t right_justify;
2934
2935	for (; pl != NULL; pl = pl->pl_next) {
2936		if (!first) {
2937			if (cb->cb_scripted)
2938				(void) printf("\t");
2939			else
2940				(void) printf("  ");
2941		} else {
2942			first = B_FALSE;
2943		}
2944
2945		if (pl->pl_prop == ZFS_PROP_NAME) {
2946			(void) strlcpy(property, zfs_get_name(zhp),
2947			    sizeof(property));
2948			propstr = property;
2949			right_justify = zfs_prop_align_right(pl->pl_prop);
2950		} else if (pl->pl_prop != ZPROP_INVAL) {
2951			if (zfs_prop_get(zhp, pl->pl_prop, property,
2952			    sizeof (property), NULL, NULL, 0,
2953			    cb->cb_literal) != 0)
2954				propstr = "-";
2955			else
2956				propstr = property;
2957			right_justify = zfs_prop_align_right(pl->pl_prop);
2958		} else if (zfs_prop_userquota(pl->pl_user_prop)) {
2959			if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,
2960			    property, sizeof (property), cb->cb_literal) != 0)
2961				propstr = "-";
2962			else
2963				propstr = property;
2964			right_justify = B_TRUE;
2965		} else if (zfs_prop_written(pl->pl_user_prop)) {
2966			if (zfs_prop_get_written(zhp, pl->pl_user_prop,
2967			    property, sizeof (property), cb->cb_literal) != 0)
2968				propstr = "-";
2969			else
2970				propstr = property;
2971			right_justify = B_TRUE;
2972		} else {
2973			if (nvlist_lookup_nvlist(userprops,
2974			    pl->pl_user_prop, &propval) != 0)
2975				propstr = "-";
2976			else
2977				verify(nvlist_lookup_string(propval,
2978				    ZPROP_VALUE, &propstr) == 0);
2979			right_justify = B_FALSE;
2980		}
2981
2982		/*
2983		 * If this is being called in scripted mode, or if this is the
2984		 * last column and it is left-justified, don't include a width
2985		 * format specifier.
2986		 */
2987		if (cb->cb_scripted || (pl->pl_next == NULL && !right_justify))
2988			(void) printf("%s", propstr);
2989		else if (right_justify)
2990			(void) printf("%*s", pl->pl_width, propstr);
2991		else
2992			(void) printf("%-*s", pl->pl_width, propstr);
2993	}
2994
2995	(void) printf("\n");
2996}
2997
2998/*
2999 * Generic callback function to list a dataset or snapshot.
3000 */
3001static int
3002list_callback(zfs_handle_t *zhp, void *data)
3003{
3004	list_cbdata_t *cbp = data;
3005
3006	if (cbp->cb_first) {
3007		if (!cbp->cb_scripted)
3008			print_header(cbp);
3009		cbp->cb_first = B_FALSE;
3010	}
3011
3012	print_dataset(zhp, cbp);
3013
3014	return (0);
3015}
3016
3017static int
3018zfs_do_list(int argc, char **argv)
3019{
3020	int c;
3021	static char default_fields[] =
3022	    "name,used,available,referenced,mountpoint";
3023	int types = ZFS_TYPE_DATASET;
3024	boolean_t types_specified = B_FALSE;
3025	char *fields = NULL;
3026	list_cbdata_t cb = { 0 };
3027	char *value;
3028	int limit = 0;
3029	int ret = 0;
3030	zfs_sort_column_t *sortcol = NULL;
3031	int flags = ZFS_ITER_PROP_LISTSNAPS | ZFS_ITER_ARGS_CAN_BE_PATHS;
3032
3033	/* check options */
3034	while ((c = getopt(argc, argv, "HS:d:o:prs:t:")) != -1) {
3035		switch (c) {
3036		case 'o':
3037			fields = optarg;
3038			break;
3039		case 'p':
3040			cb.cb_literal = B_TRUE;
3041			flags |= ZFS_ITER_LITERAL_PROPS;
3042			break;
3043		case 'd':
3044			limit = parse_depth(optarg, &flags);
3045			break;
3046		case 'r':
3047			flags |= ZFS_ITER_RECURSE;
3048			break;
3049		case 'H':
3050			cb.cb_scripted = B_TRUE;
3051			break;
3052		case 's':
3053			if (zfs_add_sort_column(&sortcol, optarg,
3054			    B_FALSE) != 0) {
3055				(void) fprintf(stderr,
3056				    gettext("invalid property '%s'\n"), optarg);
3057				usage(B_FALSE);
3058			}
3059			break;
3060		case 'S':
3061			if (zfs_add_sort_column(&sortcol, optarg,
3062			    B_TRUE) != 0) {
3063				(void) fprintf(stderr,
3064				    gettext("invalid property '%s'\n"), optarg);
3065				usage(B_FALSE);
3066			}
3067			break;
3068		case 't':
3069			types = 0;
3070			types_specified = B_TRUE;
3071			flags &= ~ZFS_ITER_PROP_LISTSNAPS;
3072			while (*optarg != '\0') {
3073				static char *type_subopts[] = { "filesystem",
3074				    "volume", "snapshot", "snap", "bookmark",
3075				    "all", NULL };
3076
3077				switch (getsubopt(&optarg, type_subopts,
3078				    &value)) {
3079				case 0:
3080					types |= ZFS_TYPE_FILESYSTEM;
3081					break;
3082				case 1:
3083					types |= ZFS_TYPE_VOLUME;
3084					break;
3085				case 2:
3086				case 3:
3087					types |= ZFS_TYPE_SNAPSHOT;
3088					break;
3089				case 4:
3090					types |= ZFS_TYPE_BOOKMARK;
3091					break;
3092				case 5:
3093					types = ZFS_TYPE_DATASET |
3094					    ZFS_TYPE_BOOKMARK;
3095					break;
3096				default:
3097					(void) fprintf(stderr,
3098					    gettext("invalid type '%s'\n"),
3099					    value);
3100					usage(B_FALSE);
3101				}
3102			}
3103			break;
3104		case ':':
3105			(void) fprintf(stderr, gettext("missing argument for "
3106			    "'%c' option\n"), optopt);
3107			usage(B_FALSE);
3108			break;
3109		case '?':
3110			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
3111			    optopt);
3112			usage(B_FALSE);
3113		}
3114	}
3115
3116	argc -= optind;
3117	argv += optind;
3118
3119	if (fields == NULL)
3120		fields = default_fields;
3121
3122	/*
3123	 * If we are only going to list snapshot names and sort by name,
3124	 * then we can use faster version.
3125	 */
3126	if (strcmp(fields, "name") == 0 && zfs_sort_only_by_name(sortcol))
3127		flags |= ZFS_ITER_SIMPLE;
3128
3129	/*
3130	 * If "-o space" and no types were specified, don't display snapshots.
3131	 */
3132	if (strcmp(fields, "space") == 0 && types_specified == B_FALSE)
3133		types &= ~ZFS_TYPE_SNAPSHOT;
3134
3135	/*
3136	 * If the user specifies '-o all', the zprop_get_list() doesn't
3137	 * normally include the name of the dataset.  For 'zfs list', we always
3138	 * want this property to be first.
3139	 */
3140	if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)
3141	    != 0)
3142		usage(B_FALSE);
3143
3144	cb.cb_first = B_TRUE;
3145
3146	ret = zfs_for_each(argc, argv, flags, types, sortcol, &cb.cb_proplist,
3147	    limit, list_callback, &cb);
3148
3149	zprop_free_list(cb.cb_proplist);
3150	zfs_free_sort_columns(sortcol);
3151
3152	if (ret == 0 && cb.cb_first && !cb.cb_scripted)
3153		(void) printf(gettext("no datasets available\n"));
3154
3155	return (ret);
3156}
3157
3158/*
3159 * zfs rename [-f] <fs | snap | vol> <fs | snap | vol>
3160 * zfs rename [-f] -p <fs | vol> <fs | vol>
3161 * zfs rename -r <snap> <snap>
3162 * zfs rename -u [-p] <fs> <fs>
3163 *
3164 * Renames the given dataset to another of the same type.
3165 *
3166 * The '-p' flag creates all the non-existing ancestors of the target first.
3167 */
3168/* ARGSUSED */
3169static int
3170zfs_do_rename(int argc, char **argv)
3171{
3172	zfs_handle_t *zhp;
3173	renameflags_t flags = { 0 };
3174	int c;
3175	int ret = 0;
3176	int types;
3177	boolean_t parents = B_FALSE;
3178	char *snapshot = NULL;
3179
3180	/* check options */
3181	while ((c = getopt(argc, argv, "fpru")) != -1) {
3182		switch (c) {
3183		case 'p':
3184			parents = B_TRUE;
3185			break;
3186		case 'r':
3187			flags.recurse = B_TRUE;
3188			break;
3189		case 'u':
3190			flags.nounmount = B_TRUE;
3191			break;
3192		case 'f':
3193			flags.forceunmount = B_TRUE;
3194			break;
3195		case '?':
3196		default:
3197			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
3198			    optopt);
3199			usage(B_FALSE);
3200		}
3201	}
3202
3203	argc -= optind;
3204	argv += optind;
3205
3206	/* check number of arguments */
3207	if (argc < 1) {
3208		(void) fprintf(stderr, gettext("missing source dataset "
3209		    "argument\n"));
3210		usage(B_FALSE);
3211	}
3212	if (argc < 2) {
3213		(void) fprintf(stderr, gettext("missing target dataset "
3214		    "argument\n"));
3215		usage(B_FALSE);
3216	}
3217	if (argc > 2) {
3218		(void) fprintf(stderr, gettext("too many arguments\n"));
3219		usage(B_FALSE);
3220	}
3221
3222	if (flags.recurse && parents) {
3223		(void) fprintf(stderr, gettext("-p and -r options are mutually "
3224		    "exclusive\n"));
3225		usage(B_FALSE);
3226	}
3227
3228	if (flags.recurse && strchr(argv[0], '@') == 0) {
3229		(void) fprintf(stderr, gettext("source dataset for recursive "
3230		    "rename must be a snapshot\n"));
3231		usage(B_FALSE);
3232	}
3233
3234	if (flags.nounmount && parents) {
3235		(void) fprintf(stderr, gettext("-u and -r options are mutually "
3236		    "exclusive\n"));
3237		usage(B_FALSE);
3238	}
3239
3240	if (flags.nounmount)
3241		types = ZFS_TYPE_FILESYSTEM;
3242	else if (parents)
3243		types = ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;
3244	else
3245		types = ZFS_TYPE_DATASET;
3246
3247	if (flags.recurse) {
3248		/*
3249		 * When we do recursive rename we are fine when the given
3250		 * snapshot for the given dataset doesn't exist - it can
3251		 * still exists below.
3252		 */
3253
3254		snapshot = strchr(argv[0], '@');
3255		assert(snapshot != NULL);
3256		*snapshot = '\0';
3257		snapshot++;
3258	}
3259
3260	if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL)
3261		return (1);
3262
3263	/* If we were asked and the name looks good, try to create ancestors. */
3264	if (parents && zfs_name_valid(argv[1], zfs_get_type(zhp)) &&
3265	    zfs_create_ancestors(g_zfs, argv[1]) != 0) {
3266		zfs_close(zhp);
3267		return (1);
3268	}
3269
3270	ret = (zfs_rename(zhp, snapshot, argv[1], flags) != 0);
3271
3272	zfs_close(zhp);
3273	return (ret);
3274}
3275
3276/*
3277 * zfs promote <fs>
3278 *
3279 * Promotes the given clone fs to be the parent
3280 */
3281/* ARGSUSED */
3282static int
3283zfs_do_promote(int argc, char **argv)
3284{
3285	zfs_handle_t *zhp;
3286	int ret = 0;
3287
3288	/* check options */
3289	if (argc > 1 && argv[1][0] == '-') {
3290		(void) fprintf(stderr, gettext("invalid option '%c'\n"),
3291		    argv[1][1]);
3292		usage(B_FALSE);
3293	}
3294
3295	/* check number of arguments */
3296	if (argc < 2) {
3297		(void) fprintf(stderr, gettext("missing clone filesystem"
3298		    " argument\n"));
3299		usage(B_FALSE);
3300	}
3301	if (argc > 2) {
3302		(void) fprintf(stderr, gettext("too many arguments\n"));
3303		usage(B_FALSE);
3304	}
3305
3306	zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
3307	if (zhp == NULL)
3308		return (1);
3309
3310	ret = (zfs_promote(zhp) != 0);
3311
3312
3313	zfs_close(zhp);
3314	return (ret);
3315}
3316
3317/*
3318 * zfs rollback [-rRf] <snapshot>
3319 *
3320 *	-r	Delete any intervening snapshots before doing rollback
3321 *	-R	Delete any snapshots and their clones
3322 *	-f	ignored for backwards compatability
3323 *
3324 * Given a filesystem, rollback to a specific snapshot, discarding any changes
3325 * since then and making it the active dataset.  If more recent snapshots exist,
3326 * the command will complain unless the '-r' flag is given.
3327 */
3328typedef struct rollback_cbdata {
3329	uint64_t	cb_create;
3330	boolean_t	cb_first;
3331	int		cb_doclones;
3332	char		*cb_target;
3333	int		cb_error;
3334	boolean_t	cb_recurse;
3335} rollback_cbdata_t;
3336
3337static int
3338rollback_check_dependent(zfs_handle_t *zhp, void *data)
3339{
3340	rollback_cbdata_t *cbp = data;
3341
3342	if (cbp->cb_first && cbp->cb_recurse) {
3343		(void) fprintf(stderr, gettext("cannot rollback to "
3344		    "'%s': clones of previous snapshots exist\n"),
3345		    cbp->cb_target);
3346		(void) fprintf(stderr, gettext("use '-R' to "
3347		    "force deletion of the following clones and "
3348		    "dependents:\n"));
3349		cbp->cb_first = 0;
3350		cbp->cb_error = 1;
3351	}
3352
3353	(void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
3354
3355	zfs_close(zhp);
3356	return (0);
3357}
3358/*
3359 * Report any snapshots more recent than the one specified.  Used when '-r' is
3360 * not specified.  We reuse this same callback for the snapshot dependents - if
3361 * 'cb_dependent' is set, then this is a dependent and we should report it
3362 * without checking the transaction group.
3363 */
3364static int
3365rollback_check(zfs_handle_t *zhp, void *data)
3366{
3367	rollback_cbdata_t *cbp = data;
3368
3369	if (cbp->cb_doclones) {
3370		zfs_close(zhp);
3371		return (0);
3372	}
3373
3374	if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) {
3375		if (cbp->cb_first && !cbp->cb_recurse) {
3376			(void) fprintf(stderr, gettext("cannot "
3377			    "rollback to '%s': more recent snapshots "
3378			    "or bookmarks exist\n"),
3379			    cbp->cb_target);
3380			(void) fprintf(stderr, gettext("use '-r' to "
3381			    "force deletion of the following "
3382			    "snapshots and bookmarks:\n"));
3383			cbp->cb_first = 0;
3384			cbp->cb_error = 1;
3385		}
3386
3387		if (cbp->cb_recurse) {
3388			if (zfs_iter_dependents(zhp, B_TRUE,
3389			    rollback_check_dependent, cbp) != 0) {
3390				zfs_close(zhp);
3391				return (-1);
3392			}
3393		} else {
3394			(void) fprintf(stderr, "%s\n",
3395			    zfs_get_name(zhp));
3396		}
3397	}
3398	zfs_close(zhp);
3399	return (0);
3400}
3401
3402static int
3403zfs_do_rollback(int argc, char **argv)
3404{
3405	int ret = 0;
3406	int c;
3407	boolean_t force = B_FALSE;
3408	rollback_cbdata_t cb = { 0 };
3409	zfs_handle_t *zhp, *snap;
3410	char parentname[ZFS_MAXNAMELEN];
3411	char *delim;
3412
3413	/* check options */
3414	while ((c = getopt(argc, argv, "rRf")) != -1) {
3415		switch (c) {
3416		case 'r':
3417			cb.cb_recurse = 1;
3418			break;
3419		case 'R':
3420			cb.cb_recurse = 1;
3421			cb.cb_doclones = 1;
3422			break;
3423		case 'f':
3424			force = B_TRUE;
3425			break;
3426		case '?':
3427			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
3428			    optopt);
3429			usage(B_FALSE);
3430		}
3431	}
3432
3433	argc -= optind;
3434	argv += optind;
3435
3436	/* check number of arguments */
3437	if (argc < 1) {
3438		(void) fprintf(stderr, gettext("missing dataset argument\n"));
3439		usage(B_FALSE);
3440	}
3441	if (argc > 1) {
3442		(void) fprintf(stderr, gettext("too many arguments\n"));
3443		usage(B_FALSE);
3444	}
3445
3446	/* open the snapshot */
3447	if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL)
3448		return (1);
3449
3450	/* open the parent dataset */
3451	(void) strlcpy(parentname, argv[0], sizeof (parentname));
3452	verify((delim = strrchr(parentname, '@')) != NULL);
3453	*delim = '\0';
3454	if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_DATASET)) == NULL) {
3455		zfs_close(snap);
3456		return (1);
3457	}
3458
3459	/*
3460	 * Check for more recent snapshots and/or clones based on the presence
3461	 * of '-r' and '-R'.
3462	 */
3463	cb.cb_target = argv[0];
3464	cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
3465	cb.cb_first = B_TRUE;
3466	cb.cb_error = 0;
3467	if ((ret = zfs_iter_snapshots(zhp, B_FALSE, rollback_check, &cb)) != 0)
3468		goto out;
3469	if ((ret = zfs_iter_bookmarks(zhp, rollback_check, &cb)) != 0)
3470		goto out;
3471
3472	if ((ret = cb.cb_error) != 0)
3473		goto out;
3474
3475	/*
3476	 * Rollback parent to the given snapshot.
3477	 */
3478	ret = zfs_rollback(zhp, snap, force);
3479
3480out:
3481	zfs_close(snap);
3482	zfs_close(zhp);
3483
3484	if (ret == 0)
3485		return (0);
3486	else
3487		return (1);
3488}
3489
3490/*
3491 * zfs set property=value { fs | snap | vol } ...
3492 *
3493 * Sets the given property for all datasets specified on the command line.
3494 */
3495typedef struct set_cbdata {
3496	char		*cb_propname;
3497	char		*cb_value;
3498} set_cbdata_t;
3499
3500static int
3501set_callback(zfs_handle_t *zhp, void *data)
3502{
3503	set_cbdata_t *cbp = data;
3504
3505	if (zfs_prop_set(zhp, cbp->cb_propname, cbp->cb_value) != 0) {
3506		switch (libzfs_errno(g_zfs)) {
3507		case EZFS_MOUNTFAILED:
3508			(void) fprintf(stderr, gettext("property may be set "
3509			    "but unable to remount filesystem\n"));
3510			break;
3511		case EZFS_SHARENFSFAILED:
3512			(void) fprintf(stderr, gettext("property may be set "
3513			    "but unable to reshare filesystem\n"));
3514			break;
3515		}
3516		return (1);
3517	}
3518	return (0);
3519}
3520
3521static int
3522zfs_do_set(int argc, char **argv)
3523{
3524	set_cbdata_t cb;
3525	int ret = 0;
3526
3527	/* check for options */
3528	if (argc > 1 && argv[1][0] == '-') {
3529		(void) fprintf(stderr, gettext("invalid option '%c'\n"),
3530		    argv[1][1]);
3531		usage(B_FALSE);
3532	}
3533
3534	/* check number of arguments */
3535	if (argc < 2) {
3536		(void) fprintf(stderr, gettext("missing property=value "
3537		    "argument\n"));
3538		usage(B_FALSE);
3539	}
3540	if (argc < 3) {
3541		(void) fprintf(stderr, gettext("missing dataset name\n"));
3542		usage(B_FALSE);
3543	}
3544
3545	/* validate property=value argument */
3546	cb.cb_propname = argv[1];
3547	if (((cb.cb_value = strchr(cb.cb_propname, '=')) == NULL) ||
3548	    (cb.cb_value[1] == '\0')) {
3549		(void) fprintf(stderr, gettext("missing value in "
3550		    "property=value argument\n"));
3551		usage(B_FALSE);
3552	}
3553
3554	*cb.cb_value = '\0';
3555	cb.cb_value++;
3556
3557	if (*cb.cb_propname == '\0') {
3558		(void) fprintf(stderr,
3559		    gettext("missing property in property=value argument\n"));
3560		usage(B_FALSE);
3561	}
3562
3563	ret = zfs_for_each(argc - 2, argv + 2, 0,
3564	    ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, &cb);
3565
3566	return (ret);
3567}
3568
3569typedef struct snap_cbdata {
3570	nvlist_t *sd_nvl;
3571	boolean_t sd_recursive;
3572	const char *sd_snapname;
3573} snap_cbdata_t;
3574
3575static int
3576zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)
3577{
3578	snap_cbdata_t *sd = arg;
3579	char *name;
3580	int rv = 0;
3581	int error;
3582
3583	if (sd->sd_recursive &&
3584	    zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) != 0) {
3585		zfs_close(zhp);
3586		return (0);
3587	}
3588
3589	error = asprintf(&name, "%s@%s", zfs_get_name(zhp), sd->sd_snapname);
3590	if (error == -1)
3591		nomem();
3592	fnvlist_add_boolean(sd->sd_nvl, name);
3593	free(name);
3594
3595	if (sd->sd_recursive)
3596		rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd);
3597	zfs_close(zhp);
3598	return (rv);
3599}
3600
3601/*
3602 * zfs snapshot [-r] [-o prop=value] ... <fs@snap>
3603 *
3604 * Creates a snapshot with the given name.  While functionally equivalent to
3605 * 'zfs create', it is a separate command to differentiate intent.
3606 */
3607static int
3608zfs_do_snapshot(int argc, char **argv)
3609{
3610	int ret = 0;
3611	char c;
3612	nvlist_t *props;
3613	snap_cbdata_t sd = { 0 };
3614	boolean_t multiple_snaps = B_FALSE;
3615
3616	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
3617		nomem();
3618	if (nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) != 0)
3619		nomem();
3620
3621	/* check options */
3622	while ((c = getopt(argc, argv, "ro:")) != -1) {
3623		switch (c) {
3624		case 'o':
3625			if (parseprop(props, optarg))
3626				return (1);
3627			break;
3628		case 'r':
3629			sd.sd_recursive = B_TRUE;
3630			multiple_snaps = B_TRUE;
3631			break;
3632		case '?':
3633			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
3634			    optopt);
3635			goto usage;
3636		}
3637	}
3638
3639	argc -= optind;
3640	argv += optind;
3641
3642	/* check number of arguments */
3643	if (argc < 1) {
3644		(void) fprintf(stderr, gettext("missing snapshot argument\n"));
3645		goto usage;
3646	}
3647
3648	if (argc > 1)
3649		multiple_snaps = B_TRUE;
3650	for (; argc > 0; argc--, argv++) {
3651		char *atp;
3652		zfs_handle_t *zhp;
3653
3654		atp = strchr(argv[0], '@');
3655		if (atp == NULL)
3656			goto usage;
3657		*atp = '\0';
3658		sd.sd_snapname = atp + 1;
3659		zhp = zfs_open(g_zfs, argv[0],
3660		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
3661		if (zhp == NULL)
3662			goto usage;
3663		if (zfs_snapshot_cb(zhp, &sd) != 0)
3664			goto usage;
3665	}
3666
3667	ret = zfs_snapshot_nvl(g_zfs, sd.sd_nvl, props);
3668	nvlist_free(sd.sd_nvl);
3669	nvlist_free(props);
3670	if (ret != 0 && multiple_snaps)
3671		(void) fprintf(stderr, gettext("no snapshots were created\n"));
3672	return (ret != 0);
3673
3674usage:
3675	nvlist_free(sd.sd_nvl);
3676	nvlist_free(props);
3677	usage(B_FALSE);
3678	return (-1);
3679}
3680
3681/*
3682 * Send a backup stream to stdout.
3683 */
3684static int
3685zfs_do_send(int argc, char **argv)
3686{
3687	char *fromname = NULL;
3688	char *toname = NULL;
3689	char *cp;
3690	zfs_handle_t *zhp;
3691	sendflags_t flags = { 0 };
3692	int c, err;
3693	nvlist_t *dbgnv = NULL;
3694	boolean_t extraverbose = B_FALSE;
3695
3696	/* check options */
3697	while ((c = getopt(argc, argv, ":i:I:RDpvnP")) != -1) {
3698		switch (c) {
3699		case 'i':
3700			if (fromname)
3701				usage(B_FALSE);
3702			fromname = optarg;
3703			break;
3704		case 'I':
3705			if (fromname)
3706				usage(B_FALSE);
3707			fromname = optarg;
3708			flags.doall = B_TRUE;
3709			break;
3710		case 'R':
3711			flags.replicate = B_TRUE;
3712			break;
3713		case 'p':
3714			flags.props = B_TRUE;
3715			break;
3716		case 'P':
3717			flags.parsable = B_TRUE;
3718			flags.verbose = B_TRUE;
3719			break;
3720		case 'v':
3721			if (flags.verbose)
3722				extraverbose = B_TRUE;
3723			flags.verbose = B_TRUE;
3724			flags.progress = B_TRUE;
3725			break;
3726		case 'D':
3727			flags.dedup = B_TRUE;
3728			break;
3729		case 'n':
3730			flags.dryrun = B_TRUE;
3731			break;
3732		case ':':
3733			(void) fprintf(stderr, gettext("missing argument for "
3734			    "'%c' option\n"), optopt);
3735			usage(B_FALSE);
3736			break;
3737		case '?':
3738			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
3739			    optopt);
3740			usage(B_FALSE);
3741		}
3742	}
3743
3744	argc -= optind;
3745	argv += optind;
3746
3747	/* check number of arguments */
3748	if (argc < 1) {
3749		(void) fprintf(stderr, gettext("missing snapshot argument\n"));
3750		usage(B_FALSE);
3751	}
3752	if (argc > 1) {
3753		(void) fprintf(stderr, gettext("too many arguments\n"));
3754		usage(B_FALSE);
3755	}
3756
3757	if (!flags.dryrun && isatty(STDOUT_FILENO)) {
3758		(void) fprintf(stderr,
3759		    gettext("Error: Stream can not be written to a terminal.\n"
3760		    "You must redirect standard output.\n"));
3761		return (1);
3762	}
3763
3764	/*
3765	 * Special case sending a filesystem, or from a bookmark.
3766	 */
3767	if (strchr(argv[0], '@') == NULL ||
3768	    (fromname && strchr(fromname, '#') != NULL)) {
3769		char frombuf[ZFS_MAXNAMELEN];
3770
3771		if (flags.replicate || flags.doall || flags.props ||
3772		    flags.dedup || flags.dryrun || flags.verbose ||
3773		    flags.progress) {
3774			(void) fprintf(stderr,
3775			    gettext("Error: "
3776			    "Unsupported flag with filesystem or bookmark.\n"));
3777			return (1);
3778		}
3779
3780		zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET);
3781		if (zhp == NULL)
3782			return (1);
3783
3784		if (fromname != NULL &&
3785		    (fromname[0] == '#' || fromname[0] == '@')) {
3786			/*
3787			 * Incremental source name begins with # or @.
3788			 * Default to same fs as target.
3789			 */
3790			(void) strncpy(frombuf, argv[0], sizeof (frombuf));
3791			cp = strchr(frombuf, '@');
3792			if (cp != NULL)
3793				*cp = '\0';
3794			(void) strlcat(frombuf, fromname, sizeof (frombuf));
3795			fromname = frombuf;
3796		}
3797		err = zfs_send_one(zhp, fromname, STDOUT_FILENO);
3798		zfs_close(zhp);
3799		return (err != 0);
3800	}
3801
3802	cp = strchr(argv[0], '@');
3803	*cp = '\0';
3804	toname = cp + 1;
3805	zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
3806	if (zhp == NULL)
3807		return (1);
3808
3809	/*
3810	 * If they specified the full path to the snapshot, chop off
3811	 * everything except the short name of the snapshot, but special
3812	 * case if they specify the origin.
3813	 */
3814	if (fromname && (cp = strchr(fromname, '@')) != NULL) {
3815		char origin[ZFS_MAXNAMELEN];
3816		zprop_source_t src;
3817
3818		(void) zfs_prop_get(zhp, ZFS_PROP_ORIGIN,
3819		    origin, sizeof (origin), &src, NULL, 0, B_FALSE);
3820
3821		if (strcmp(origin, fromname) == 0) {
3822			fromname = NULL;
3823			flags.fromorigin = B_TRUE;
3824		} else {
3825			*cp = '\0';
3826			if (cp != fromname && strcmp(argv[0], fromname)) {
3827				(void) fprintf(stderr,
3828				    gettext("incremental source must be "
3829				    "in same filesystem\n"));
3830				usage(B_FALSE);
3831			}
3832			fromname = cp + 1;
3833			if (strchr(fromname, '@') || strchr(fromname, '/')) {
3834				(void) fprintf(stderr,
3835				    gettext("invalid incremental source\n"));
3836				usage(B_FALSE);
3837			}
3838		}
3839	}
3840
3841	if (flags.replicate && fromname == NULL)
3842		flags.doall = B_TRUE;
3843
3844	err = zfs_send(zhp, fromname, toname, &flags, STDOUT_FILENO, NULL, 0,
3845	    extraverbose ? &dbgnv : NULL);
3846
3847	if (extraverbose && dbgnv != NULL) {
3848		/*
3849		 * dump_nvlist prints to stdout, but that's been
3850		 * redirected to a file.  Make it print to stderr
3851		 * instead.
3852		 */
3853		(void) dup2(STDERR_FILENO, STDOUT_FILENO);
3854		dump_nvlist(dbgnv, 0);
3855		nvlist_free(dbgnv);
3856	}
3857	zfs_close(zhp);
3858
3859	return (err != 0);
3860}
3861
3862/*
3863 * zfs receive [-vnFu] [-d | -e] <fs@snap>
3864 *
3865 * Restore a backup stream from stdin.
3866 */
3867static int
3868zfs_do_receive(int argc, char **argv)
3869{
3870	int c, err;
3871	recvflags_t flags = { 0 };
3872
3873	/* check options */
3874	while ((c = getopt(argc, argv, ":denuvF")) != -1) {
3875		switch (c) {
3876		case 'd':
3877			flags.isprefix = B_TRUE;
3878			break;
3879		case 'e':
3880			flags.isprefix = B_TRUE;
3881			flags.istail = B_TRUE;
3882			break;
3883		case 'n':
3884			flags.dryrun = B_TRUE;
3885			break;
3886		case 'u':
3887			flags.nomount = B_TRUE;
3888			break;
3889		case 'v':
3890			flags.verbose = B_TRUE;
3891			break;
3892		case 'F':
3893			flags.force = B_TRUE;
3894			break;
3895		case ':':
3896			(void) fprintf(stderr, gettext("missing argument for "
3897			    "'%c' option\n"), optopt);
3898			usage(B_FALSE);
3899			break;
3900		case '?':
3901			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
3902			    optopt);
3903			usage(B_FALSE);
3904		}
3905	}
3906
3907	argc -= optind;
3908	argv += optind;
3909
3910	/* check number of arguments */
3911	if (argc < 1) {
3912		(void) fprintf(stderr, gettext("missing snapshot argument\n"));
3913		usage(B_FALSE);
3914	}
3915	if (argc > 1) {
3916		(void) fprintf(stderr, gettext("too many arguments\n"));
3917		usage(B_FALSE);
3918	}
3919
3920	if (isatty(STDIN_FILENO)) {
3921		(void) fprintf(stderr,
3922		    gettext("Error: Backup stream can not be read "
3923		    "from a terminal.\n"
3924		    "You must redirect standard input.\n"));
3925		return (1);
3926	}
3927
3928	err = zfs_receive(g_zfs, argv[0], &flags, STDIN_FILENO, NULL);
3929
3930	return (err != 0);
3931}
3932
3933/*
3934 * allow/unallow stuff
3935 */
3936/* copied from zfs/sys/dsl_deleg.h */
3937#define	ZFS_DELEG_PERM_CREATE		"create"
3938#define	ZFS_DELEG_PERM_DESTROY		"destroy"
3939#define	ZFS_DELEG_PERM_SNAPSHOT		"snapshot"
3940#define	ZFS_DELEG_PERM_ROLLBACK		"rollback"
3941#define	ZFS_DELEG_PERM_CLONE		"clone"
3942#define	ZFS_DELEG_PERM_PROMOTE		"promote"
3943#define	ZFS_DELEG_PERM_RENAME		"rename"
3944#define	ZFS_DELEG_PERM_MOUNT		"mount"
3945#define	ZFS_DELEG_PERM_SHARE		"share"
3946#define	ZFS_DELEG_PERM_SEND		"send"
3947#define	ZFS_DELEG_PERM_RECEIVE		"receive"
3948#define	ZFS_DELEG_PERM_ALLOW		"allow"
3949#define	ZFS_DELEG_PERM_USERPROP		"userprop"
3950#define	ZFS_DELEG_PERM_VSCAN		"vscan" /* ??? */
3951#define	ZFS_DELEG_PERM_USERQUOTA	"userquota"
3952#define	ZFS_DELEG_PERM_GROUPQUOTA	"groupquota"
3953#define	ZFS_DELEG_PERM_USERUSED		"userused"
3954#define	ZFS_DELEG_PERM_GROUPUSED	"groupused"
3955#define	ZFS_DELEG_PERM_HOLD		"hold"
3956#define	ZFS_DELEG_PERM_RELEASE		"release"
3957#define	ZFS_DELEG_PERM_DIFF		"diff"
3958#define	ZFS_DELEG_PERM_BOOKMARK		"bookmark"
3959
3960#define	ZFS_NUM_DELEG_NOTES ZFS_DELEG_NOTE_NONE
3961
3962static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = {
3963	{ ZFS_DELEG_PERM_ALLOW, ZFS_DELEG_NOTE_ALLOW },
3964	{ ZFS_DELEG_PERM_CLONE, ZFS_DELEG_NOTE_CLONE },
3965	{ ZFS_DELEG_PERM_CREATE, ZFS_DELEG_NOTE_CREATE },
3966	{ ZFS_DELEG_PERM_DESTROY, ZFS_DELEG_NOTE_DESTROY },
3967	{ ZFS_DELEG_PERM_DIFF, ZFS_DELEG_NOTE_DIFF},
3968	{ ZFS_DELEG_PERM_HOLD, ZFS_DELEG_NOTE_HOLD },
3969	{ ZFS_DELEG_PERM_MOUNT, ZFS_DELEG_NOTE_MOUNT },
3970	{ ZFS_DELEG_PERM_PROMOTE, ZFS_DELEG_NOTE_PROMOTE },
3971	{ ZFS_DELEG_PERM_RECEIVE, ZFS_DELEG_NOTE_RECEIVE },
3972	{ ZFS_DELEG_PERM_RELEASE, ZFS_DELEG_NOTE_RELEASE },
3973	{ ZFS_DELEG_PERM_RENAME, ZFS_DELEG_NOTE_RENAME },
3974	{ ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK },
3975	{ ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND },
3976	{ ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE },
3977	{ ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT },
3978	{ ZFS_DELEG_PERM_BOOKMARK, ZFS_DELEG_NOTE_BOOKMARK },
3979
3980	{ ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA },
3981	{ ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED },
3982	{ ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP },
3983	{ ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_NOTE_USERQUOTA },
3984	{ ZFS_DELEG_PERM_USERUSED, ZFS_DELEG_NOTE_USERUSED },
3985	{ NULL, ZFS_DELEG_NOTE_NONE }
3986};
3987
3988/* permission structure */
3989typedef struct deleg_perm {
3990	zfs_deleg_who_type_t	dp_who_type;
3991	const char		*dp_name;
3992	boolean_t		dp_local;
3993	boolean_t		dp_descend;
3994} deleg_perm_t;
3995
3996/* */
3997typedef struct deleg_perm_node {
3998	deleg_perm_t		dpn_perm;
3999
4000	uu_avl_node_t		dpn_avl_node;
4001} deleg_perm_node_t;
4002
4003typedef struct fs_perm fs_perm_t;
4004
4005/* permissions set */
4006typedef struct who_perm {
4007	zfs_deleg_who_type_t	who_type;
4008	const char		*who_name;		/* id */
4009	char			who_ug_name[256];	/* user/group name */
4010	fs_perm_t		*who_fsperm;		/* uplink */
4011
4012	uu_avl_t		*who_deleg_perm_avl;	/* permissions */
4013} who_perm_t;
4014
4015/* */
4016typedef struct who_perm_node {
4017	who_perm_t	who_perm;
4018	uu_avl_node_t	who_avl_node;
4019} who_perm_node_t;
4020
4021typedef struct fs_perm_set fs_perm_set_t;
4022/* fs permissions */
4023struct fs_perm {
4024	const char		*fsp_name;
4025
4026	uu_avl_t		*fsp_sc_avl;	/* sets,create */
4027	uu_avl_t		*fsp_uge_avl;	/* user,group,everyone */
4028
4029	fs_perm_set_t		*fsp_set;	/* uplink */
4030};
4031
4032/* */
4033typedef struct fs_perm_node {
4034	fs_perm_t	fspn_fsperm;
4035	uu_avl_t	*fspn_avl;
4036
4037	uu_list_node_t	fspn_list_node;
4038} fs_perm_node_t;
4039
4040/* top level structure */
4041struct fs_perm_set {
4042	uu_list_pool_t	*fsps_list_pool;
4043	uu_list_t	*fsps_list; /* list of fs_perms */
4044
4045	uu_avl_pool_t	*fsps_named_set_avl_pool;
4046	uu_avl_pool_t	*fsps_who_perm_avl_pool;
4047	uu_avl_pool_t	*fsps_deleg_perm_avl_pool;
4048};
4049
4050static inline const char *
4051deleg_perm_type(zfs_deleg_note_t note)
4052{
4053	/* subcommands */
4054	switch (note) {
4055		/* SUBCOMMANDS */
4056		/* OTHER */
4057	case ZFS_DELEG_NOTE_GROUPQUOTA:
4058	case ZFS_DELEG_NOTE_GROUPUSED:
4059	case ZFS_DELEG_NOTE_USERPROP:
4060	case ZFS_DELEG_NOTE_USERQUOTA:
4061	case ZFS_DELEG_NOTE_USERUSED:
4062		/* other */
4063		return (gettext("other"));
4064	default:
4065		return (gettext("subcommand"));
4066	}
4067}
4068
4069static int inline
4070who_type2weight(zfs_deleg_who_type_t who_type)
4071{
4072	int res;
4073	switch (who_type) {
4074		case ZFS_DELEG_NAMED_SET_SETS:
4075		case ZFS_DELEG_NAMED_SET:
4076			res = 0;
4077			break;
4078		case ZFS_DELEG_CREATE_SETS:
4079		case ZFS_DELEG_CREATE:
4080			res = 1;
4081			break;
4082		case ZFS_DELEG_USER_SETS:
4083		case ZFS_DELEG_USER:
4084			res = 2;
4085			break;
4086		case ZFS_DELEG_GROUP_SETS:
4087		case ZFS_DELEG_GROUP:
4088			res = 3;
4089			break;
4090		case ZFS_DELEG_EVERYONE_SETS:
4091		case ZFS_DELEG_EVERYONE:
4092			res = 4;
4093			break;
4094		default:
4095			res = -1;
4096	}
4097
4098	return (res);
4099}
4100
4101/* ARGSUSED */
4102static int
4103who_perm_compare(const void *larg, const void *rarg, void *unused)
4104{
4105	const who_perm_node_t *l = larg;
4106	const who_perm_node_t *r = rarg;
4107	zfs_deleg_who_type_t ltype = l->who_perm.who_type;
4108	zfs_deleg_who_type_t rtype = r->who_perm.who_type;
4109	int lweight = who_type2weight(ltype);
4110	int rweight = who_type2weight(rtype);
4111	int res = lweight - rweight;
4112	if (res == 0)
4113		res = strncmp(l->who_perm.who_name, r->who_perm.who_name,
4114		    ZFS_MAX_DELEG_NAME-1);
4115
4116	if (res == 0)
4117		return (0);
4118	if (res > 0)
4119		return (1);
4120	else
4121		return (-1);
4122}
4123
4124/* ARGSUSED */
4125static int
4126deleg_perm_compare(const void *larg, const void *rarg, void *unused)
4127{
4128	const deleg_perm_node_t *l = larg;
4129	const deleg_perm_node_t *r = rarg;
4130	int res =  strncmp(l->dpn_perm.dp_name, r->dpn_perm.dp_name,
4131	    ZFS_MAX_DELEG_NAME-1);
4132
4133	if (res == 0)
4134		return (0);
4135
4136	if (res > 0)
4137		return (1);
4138	else
4139		return (-1);
4140}
4141
4142static inline void
4143fs_perm_set_init(fs_perm_set_t *fspset)
4144{
4145	bzero(fspset, sizeof (fs_perm_set_t));
4146
4147	if ((fspset->fsps_list_pool = uu_list_pool_create("fsps_list_pool",
4148	    sizeof (fs_perm_node_t), offsetof(fs_perm_node_t, fspn_list_node),
4149	    NULL, UU_DEFAULT)) == NULL)
4150		nomem();
4151	if ((fspset->fsps_list = uu_list_create(fspset->fsps_list_pool, NULL,
4152	    UU_DEFAULT)) == NULL)
4153		nomem();
4154
4155	if ((fspset->fsps_named_set_avl_pool = uu_avl_pool_create(
4156	    "named_set_avl_pool", sizeof (who_perm_node_t), offsetof(
4157	    who_perm_node_t, who_avl_node), who_perm_compare,
4158	    UU_DEFAULT)) == NULL)
4159		nomem();
4160
4161	if ((fspset->fsps_who_perm_avl_pool = uu_avl_pool_create(
4162	    "who_perm_avl_pool", sizeof (who_perm_node_t), offsetof(
4163	    who_perm_node_t, who_avl_node), who_perm_compare,
4164	    UU_DEFAULT)) == NULL)
4165		nomem();
4166
4167	if ((fspset->fsps_deleg_perm_avl_pool = uu_avl_pool_create(
4168	    "deleg_perm_avl_pool", sizeof (deleg_perm_node_t), offsetof(
4169	    deleg_perm_node_t, dpn_avl_node), deleg_perm_compare, UU_DEFAULT))
4170	    == NULL)
4171		nomem();
4172}
4173
4174static inline void fs_perm_fini(fs_perm_t *);
4175static inline void who_perm_fini(who_perm_t *);
4176
4177static inline void
4178fs_perm_set_fini(fs_perm_set_t *fspset)
4179{
4180	fs_perm_node_t *node = uu_list_first(fspset->fsps_list);
4181
4182	while (node != NULL) {
4183		fs_perm_node_t *next_node =
4184		    uu_list_next(fspset->fsps_list, node);
4185		fs_perm_t *fsperm = &node->fspn_fsperm;
4186		fs_perm_fini(fsperm);
4187		uu_list_remove(fspset->fsps_list, node);
4188		free(node);
4189		node = next_node;
4190	}
4191
4192	uu_avl_pool_destroy(fspset->fsps_named_set_avl_pool);
4193	uu_avl_pool_destroy(fspset->fsps_who_perm_avl_pool);
4194	uu_avl_pool_destroy(fspset->fsps_deleg_perm_avl_pool);
4195}
4196
4197static inline void
4198deleg_perm_init(deleg_perm_t *deleg_perm, zfs_deleg_who_type_t type,
4199    const char *name)
4200{
4201	deleg_perm->dp_who_type = type;
4202	deleg_perm->dp_name = name;
4203}
4204
4205static inline void
4206who_perm_init(who_perm_t *who_perm, fs_perm_t *fsperm,
4207    zfs_deleg_who_type_t type, const char *name)
4208{
4209	uu_avl_pool_t	*pool;
4210	pool = fsperm->fsp_set->fsps_deleg_perm_avl_pool;
4211
4212	bzero(who_perm, sizeof (who_perm_t));
4213
4214	if ((who_perm->who_deleg_perm_avl = uu_avl_create(pool, NULL,
4215	    UU_DEFAULT)) == NULL)
4216		nomem();
4217
4218	who_perm->who_type = type;
4219	who_perm->who_name = name;
4220	who_perm->who_fsperm = fsperm;
4221}
4222
4223static inline void
4224who_perm_fini(who_perm_t *who_perm)
4225{
4226	deleg_perm_node_t *node = uu_avl_first(who_perm->who_deleg_perm_avl);
4227
4228	while (node != NULL) {
4229		deleg_perm_node_t *next_node =
4230		    uu_avl_next(who_perm->who_deleg_perm_avl, node);
4231
4232		uu_avl_remove(who_perm->who_deleg_perm_avl, node);
4233		free(node);
4234		node = next_node;
4235	}
4236
4237	uu_avl_destroy(who_perm->who_deleg_perm_avl);
4238}
4239
4240static inline void
4241fs_perm_init(fs_perm_t *fsperm, fs_perm_set_t *fspset, const char *fsname)
4242{
4243	uu_avl_pool_t	*nset_pool = fspset->fsps_named_set_avl_pool;
4244	uu_avl_pool_t	*who_pool = fspset->fsps_who_perm_avl_pool;
4245
4246	bzero(fsperm, sizeof (fs_perm_t));
4247
4248	if ((fsperm->fsp_sc_avl = uu_avl_create(nset_pool, NULL, UU_DEFAULT))
4249	    == NULL)
4250		nomem();
4251
4252	if ((fsperm->fsp_uge_avl = uu_avl_create(who_pool, NULL, UU_DEFAULT))
4253	    == NULL)
4254		nomem();
4255
4256	fsperm->fsp_set = fspset;
4257	fsperm->fsp_name = fsname;
4258}
4259
4260static inline void
4261fs_perm_fini(fs_perm_t *fsperm)
4262{
4263	who_perm_node_t *node = uu_avl_first(fsperm->fsp_sc_avl);
4264	while (node != NULL) {
4265		who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_sc_avl,
4266		    node);
4267		who_perm_t *who_perm = &node->who_perm;
4268		who_perm_fini(who_perm);
4269		uu_avl_remove(fsperm->fsp_sc_avl, node);
4270		free(node);
4271		node = next_node;
4272	}
4273
4274	node = uu_avl_first(fsperm->fsp_uge_avl);
4275	while (node != NULL) {
4276		who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_uge_avl,
4277		    node);
4278		who_perm_t *who_perm = &node->who_perm;
4279		who_perm_fini(who_perm);
4280		uu_avl_remove(fsperm->fsp_uge_avl, node);
4281		free(node);
4282		node = next_node;
4283	}
4284
4285	uu_avl_destroy(fsperm->fsp_sc_avl);
4286	uu_avl_destroy(fsperm->fsp_uge_avl);
4287}
4288
4289static void inline
4290set_deleg_perm_node(uu_avl_t *avl, deleg_perm_node_t *node,
4291    zfs_deleg_who_type_t who_type, const char *name, char locality)
4292{
4293	uu_avl_index_t idx = 0;
4294
4295	deleg_perm_node_t *found_node = NULL;
4296	deleg_perm_t	*deleg_perm = &node->dpn_perm;
4297
4298	deleg_perm_init(deleg_perm, who_type, name);
4299
4300	if ((found_node = uu_avl_find(avl, node, NULL, &idx))
4301	    == NULL)
4302		uu_avl_insert(avl, node, idx);
4303	else {
4304		node = found_node;
4305		deleg_perm = &node->dpn_perm;
4306	}
4307
4308
4309	switch (locality) {
4310	case ZFS_DELEG_LOCAL:
4311		deleg_perm->dp_local = B_TRUE;
4312		break;
4313	case ZFS_DELEG_DESCENDENT:
4314		deleg_perm->dp_descend = B_TRUE;
4315		break;
4316	case ZFS_DELEG_NA:
4317		break;
4318	default:
4319		assert(B_FALSE); /* invalid locality */
4320	}
4321}
4322
4323static inline int
4324parse_who_perm(who_perm_t *who_perm, nvlist_t *nvl, char locality)
4325{
4326	nvpair_t *nvp = NULL;
4327	fs_perm_set_t *fspset = who_perm->who_fsperm->fsp_set;
4328	uu_avl_t *avl = who_perm->who_deleg_perm_avl;
4329	zfs_deleg_who_type_t who_type = who_perm->who_type;
4330
4331	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
4332		const char *name = nvpair_name(nvp);
4333		data_type_t type = nvpair_type(nvp);
4334		uu_avl_pool_t *avl_pool = fspset->fsps_deleg_perm_avl_pool;
4335		deleg_perm_node_t *node =
4336		    safe_malloc(sizeof (deleg_perm_node_t));
4337
4338		assert(type == DATA_TYPE_BOOLEAN);
4339
4340		uu_avl_node_init(node, &node->dpn_avl_node, avl_pool);
4341		set_deleg_perm_node(avl, node, who_type, name, locality);
4342	}
4343
4344	return (0);
4345}
4346
4347static inline int
4348parse_fs_perm(fs_perm_t *fsperm, nvlist_t *nvl)
4349{
4350	nvpair_t *nvp = NULL;
4351	fs_perm_set_t *fspset = fsperm->fsp_set;
4352
4353	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
4354		nvlist_t *nvl2 = NULL;
4355		const char *name = nvpair_name(nvp);
4356		uu_avl_t *avl = NULL;
4357		uu_avl_pool_t *avl_pool;
4358		zfs_deleg_who_type_t perm_type = name[0];
4359		char perm_locality = name[1];
4360		const char *perm_name = name + 3;
4361		boolean_t is_set = B_TRUE;
4362		who_perm_t *who_perm = NULL;
4363
4364		assert('$' == name[2]);
4365
4366		if (nvpair_value_nvlist(nvp, &nvl2) != 0)
4367			return (-1);
4368
4369		switch (perm_type) {
4370		case ZFS_DELEG_CREATE:
4371		case ZFS_DELEG_CREATE_SETS:
4372		case ZFS_DELEG_NAMED_SET:
4373		case ZFS_DELEG_NAMED_SET_SETS:
4374			avl_pool = fspset->fsps_named_set_avl_pool;
4375			avl = fsperm->fsp_sc_avl;
4376			break;
4377		case ZFS_DELEG_USER:
4378		case ZFS_DELEG_USER_SETS:
4379		case ZFS_DELEG_GROUP:
4380		case ZFS_DELEG_GROUP_SETS:
4381		case ZFS_DELEG_EVERYONE:
4382		case ZFS_DELEG_EVERYONE_SETS:
4383			avl_pool = fspset->fsps_who_perm_avl_pool;
4384			avl = fsperm->fsp_uge_avl;
4385			break;
4386		}
4387
4388		if (is_set) {
4389			who_perm_node_t *found_node = NULL;
4390			who_perm_node_t *node = safe_malloc(
4391			    sizeof (who_perm_node_t));
4392			who_perm = &node->who_perm;
4393			uu_avl_index_t idx = 0;
4394
4395			uu_avl_node_init(node, &node->who_avl_node, avl_pool);
4396			who_perm_init(who_perm, fsperm, perm_type, perm_name);
4397
4398			if ((found_node = uu_avl_find(avl, node, NULL, &idx))
4399			    == NULL) {
4400				if (avl == fsperm->fsp_uge_avl) {
4401					uid_t rid = 0;
4402					struct passwd *p = NULL;
4403					struct group *g = NULL;
4404					const char *nice_name = NULL;
4405
4406					switch (perm_type) {
4407					case ZFS_DELEG_USER_SETS:
4408					case ZFS_DELEG_USER:
4409						rid = atoi(perm_name);
4410						p = getpwuid(rid);
4411						if (p)
4412							nice_name = p->pw_name;
4413						break;
4414					case ZFS_DELEG_GROUP_SETS:
4415					case ZFS_DELEG_GROUP:
4416						rid = atoi(perm_name);
4417						g = getgrgid(rid);
4418						if (g)
4419							nice_name = g->gr_name;
4420						break;
4421					}
4422
4423					if (nice_name != NULL)
4424						(void) strlcpy(
4425						    node->who_perm.who_ug_name,
4426						    nice_name, 256);
4427				}
4428
4429				uu_avl_insert(avl, node, idx);
4430			} else {
4431				node = found_node;
4432				who_perm = &node->who_perm;
4433			}
4434		}
4435
4436		(void) parse_who_perm(who_perm, nvl2, perm_locality);
4437	}
4438
4439	return (0);
4440}
4441
4442static inline int
4443parse_fs_perm_set(fs_perm_set_t *fspset, nvlist_t *nvl)
4444{
4445	nvpair_t *nvp = NULL;
4446	uu_avl_index_t idx = 0;
4447
4448	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
4449		nvlist_t *nvl2 = NULL;
4450		const char *fsname = nvpair_name(nvp);
4451		data_type_t type = nvpair_type(nvp);
4452		fs_perm_t *fsperm = NULL;
4453		fs_perm_node_t *node = safe_malloc(sizeof (fs_perm_node_t));
4454		if (node == NULL)
4455			nomem();
4456
4457		fsperm = &node->fspn_fsperm;
4458
4459		assert(DATA_TYPE_NVLIST == type);
4460
4461		uu_list_node_init(node, &node->fspn_list_node,
4462		    fspset->fsps_list_pool);
4463
4464		idx = uu_list_numnodes(fspset->fsps_list);
4465		fs_perm_init(fsperm, fspset, fsname);
4466
4467		if (nvpair_value_nvlist(nvp, &nvl2) != 0)
4468			return (-1);
4469
4470		(void) parse_fs_perm(fsperm, nvl2);
4471
4472		uu_list_insert(fspset->fsps_list, node, idx);
4473	}
4474
4475	return (0);
4476}
4477
4478static inline const char *
4479deleg_perm_comment(zfs_deleg_note_t note)
4480{
4481	const char *str = "";
4482
4483	/* subcommands */
4484	switch (note) {
4485		/* SUBCOMMANDS */
4486	case ZFS_DELEG_NOTE_ALLOW:
4487		str = gettext("Must also have the permission that is being"
4488		    "\n\t\t\t\tallowed");
4489		break;
4490	case ZFS_DELEG_NOTE_CLONE:
4491		str = gettext("Must also have the 'create' ability and 'mount'"
4492		    "\n\t\t\t\tability in the origin file system");
4493		break;
4494	case ZFS_DELEG_NOTE_CREATE:
4495		str = gettext("Must also have the 'mount' ability");
4496		break;
4497	case ZFS_DELEG_NOTE_DESTROY:
4498		str = gettext("Must also have the 'mount' ability");
4499		break;
4500	case ZFS_DELEG_NOTE_DIFF:
4501		str = gettext("Allows lookup of paths within a dataset;"
4502		    "\n\t\t\t\tgiven an object number. Ordinary users need this"
4503		    "\n\t\t\t\tin order to use zfs diff");
4504		break;
4505	case ZFS_DELEG_NOTE_HOLD:
4506		str = gettext("Allows adding a user hold to a snapshot");
4507		break;
4508	case ZFS_DELEG_NOTE_MOUNT:
4509		str = gettext("Allows mount/umount of ZFS datasets");
4510		break;
4511	case ZFS_DELEG_NOTE_PROMOTE:
4512		str = gettext("Must also have the 'mount'\n\t\t\t\tand"
4513		    " 'promote' ability in the origin file system");
4514		break;
4515	case ZFS_DELEG_NOTE_RECEIVE:
4516		str = gettext("Must also have the 'mount' and 'create'"
4517		    " ability");
4518		break;
4519	case ZFS_DELEG_NOTE_RELEASE:
4520		str = gettext("Allows releasing a user hold which\n\t\t\t\t"
4521		    "might destroy the snapshot");
4522		break;
4523	case ZFS_DELEG_NOTE_RENAME:
4524		str = gettext("Must also have the 'mount' and 'create'"
4525		    "\n\t\t\t\tability in the new parent");
4526		break;
4527	case ZFS_DELEG_NOTE_ROLLBACK:
4528		str = gettext("");
4529		break;
4530	case ZFS_DELEG_NOTE_SEND:
4531		str = gettext("");
4532		break;
4533	case ZFS_DELEG_NOTE_SHARE:
4534		str = gettext("Allows sharing file systems over NFS or SMB"
4535		    "\n\t\t\t\tprotocols");
4536		break;
4537	case ZFS_DELEG_NOTE_SNAPSHOT:
4538		str = gettext("");
4539		break;
4540/*
4541 *	case ZFS_DELEG_NOTE_VSCAN:
4542 *		str = gettext("");
4543 *		break;
4544 */
4545		/* OTHER */
4546	case ZFS_DELEG_NOTE_GROUPQUOTA:
4547		str = gettext("Allows accessing any groupquota@... property");
4548		break;
4549	case ZFS_DELEG_NOTE_GROUPUSED:
4550		str = gettext("Allows reading any groupused@... property");
4551		break;
4552	case ZFS_DELEG_NOTE_USERPROP:
4553		str = gettext("Allows changing any user property");
4554		break;
4555	case ZFS_DELEG_NOTE_USERQUOTA:
4556		str = gettext("Allows accessing any userquota@... property");
4557		break;
4558	case ZFS_DELEG_NOTE_USERUSED:
4559		str = gettext("Allows reading any userused@... property");
4560		break;
4561		/* other */
4562	default:
4563		str = "";
4564	}
4565
4566	return (str);
4567}
4568
4569struct allow_opts {
4570	boolean_t local;
4571	boolean_t descend;
4572	boolean_t user;
4573	boolean_t group;
4574	boolean_t everyone;
4575	boolean_t create;
4576	boolean_t set;
4577	boolean_t recursive; /* unallow only */
4578	boolean_t prt_usage;
4579
4580	boolean_t prt_perms;
4581	char *who;
4582	char *perms;
4583	const char *dataset;
4584};
4585
4586static inline int
4587prop_cmp(const void *a, const void *b)
4588{
4589	const char *str1 = *(const char **)a;
4590	const char *str2 = *(const char **)b;
4591	return (strcmp(str1, str2));
4592}
4593
4594static void
4595allow_usage(boolean_t un, boolean_t requested, const char *msg)
4596{
4597	const char *opt_desc[] = {
4598		"-h", gettext("show this help message and exit"),
4599		"-l", gettext("set permission locally"),
4600		"-d", gettext("set permission for descents"),
4601		"-u", gettext("set permission for user"),
4602		"-g", gettext("set permission for group"),
4603		"-e", gettext("set permission for everyone"),
4604		"-c", gettext("set create time permission"),
4605		"-s", gettext("define permission set"),
4606		/* unallow only */
4607		"-r", gettext("remove permissions recursively"),
4608	};
4609	size_t unallow_size = sizeof (opt_desc) / sizeof (char *);
4610	size_t allow_size = unallow_size - 2;
4611	const char *props[ZFS_NUM_PROPS];
4612	int i;
4613	size_t count = 0;
4614	FILE *fp = requested ? stdout : stderr;
4615	zprop_desc_t *pdtbl = zfs_prop_get_table();
4616	const char *fmt = gettext("%-16s %-14s\t%s\n");
4617
4618	(void) fprintf(fp, gettext("Usage: %s\n"), get_usage(un ? HELP_UNALLOW :
4619	    HELP_ALLOW));
4620	(void) fprintf(fp, gettext("Options:\n"));
4621	for (i = 0; i < (un ? unallow_size : allow_size); i++) {
4622		const char *opt = opt_desc[i++];
4623		const char *optdsc = opt_desc[i];
4624		(void) fprintf(fp, gettext("  %-10s  %s\n"), opt, optdsc);
4625	}
4626
4627	(void) fprintf(fp, gettext("\nThe following permissions are "
4628	    "supported:\n\n"));
4629	(void) fprintf(fp, fmt, gettext("NAME"), gettext("TYPE"),
4630	    gettext("NOTES"));
4631	for (i = 0; i < ZFS_NUM_DELEG_NOTES; i++) {
4632		const char *perm_name = zfs_deleg_perm_tbl[i].z_perm;
4633		zfs_deleg_note_t perm_note = zfs_deleg_perm_tbl[i].z_note;
4634		const char *perm_type = deleg_perm_type(perm_note);
4635		const char *perm_comment = deleg_perm_comment(perm_note);
4636		(void) fprintf(fp, fmt, perm_name, perm_type, perm_comment);
4637	}
4638
4639	for (i = 0; i < ZFS_NUM_PROPS; i++) {
4640		zprop_desc_t *pd = &pdtbl[i];
4641		if (pd->pd_visible != B_TRUE)
4642			continue;
4643
4644		if (pd->pd_attr == PROP_READONLY)
4645			continue;
4646
4647		props[count++] = pd->pd_name;
4648	}
4649	props[count] = NULL;
4650
4651	qsort(props, count, sizeof (char *), prop_cmp);
4652
4653	for (i = 0; i < count; i++)
4654		(void) fprintf(fp, fmt, props[i], gettext("property"), "");
4655
4656	if (msg != NULL)
4657		(void) fprintf(fp, gettext("\nzfs: error: %s"), msg);
4658
4659	exit(requested ? 0 : 2);
4660}
4661
4662static inline const char *
4663munge_args(int argc, char **argv, boolean_t un, size_t expected_argc,
4664    char **permsp)
4665{
4666	if (un && argc == expected_argc - 1)
4667		*permsp = NULL;
4668	else if (argc == expected_argc)
4669		*permsp = argv[argc - 2];
4670	else
4671		allow_usage(un, B_FALSE,
4672		    gettext("wrong number of parameters\n"));
4673
4674	return (argv[argc - 1]);
4675}
4676
4677static void
4678parse_allow_args(int argc, char **argv, boolean_t un, struct allow_opts *opts)
4679{
4680	int uge_sum = opts->user + opts->group + opts->everyone;
4681	int csuge_sum = opts->create + opts->set + uge_sum;
4682	int ldcsuge_sum = csuge_sum + opts->local + opts->descend;
4683	int all_sum = un ? ldcsuge_sum + opts->recursive : ldcsuge_sum;
4684
4685	if (uge_sum > 1)
4686		allow_usage(un, B_FALSE,
4687		    gettext("-u, -g, and -e are mutually exclusive\n"));
4688
4689	if (opts->prt_usage)
4690		if (argc == 0 && all_sum == 0)
4691			allow_usage(un, B_TRUE, NULL);
4692		else
4693			usage(B_FALSE);
4694
4695	if (opts->set) {
4696		if (csuge_sum > 1)
4697			allow_usage(un, B_FALSE,
4698			    gettext("invalid options combined with -s\n"));
4699
4700		opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);
4701		if (argv[0][0] != '@')
4702			allow_usage(un, B_FALSE,
4703			    gettext("invalid set name: missing '@' prefix\n"));
4704		opts->who = argv[0];
4705	} else if (opts->create) {
4706		if (ldcsuge_sum > 1)
4707			allow_usage(un, B_FALSE,
4708			    gettext("invalid options combined with -c\n"));
4709		opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
4710	} else if (opts->everyone) {
4711		if (csuge_sum > 1)
4712			allow_usage(un, B_FALSE,
4713			    gettext("invalid options combined with -e\n"));
4714		opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
4715	} else if (uge_sum == 0 && argc > 0 && strcmp(argv[0], "everyone")
4716	    == 0) {
4717		opts->everyone = B_TRUE;
4718		argc--;
4719		argv++;
4720		opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
4721	} else if (argc == 1 && !un) {
4722		opts->prt_perms = B_TRUE;
4723		opts->dataset = argv[argc-1];
4724	} else {
4725		opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);
4726		opts->who = argv[0];
4727	}
4728
4729	if (!opts->local && !opts->descend) {
4730		opts->local = B_TRUE;
4731		opts->descend = B_TRUE;
4732	}
4733}
4734
4735static void
4736store_allow_perm(zfs_deleg_who_type_t type, boolean_t local, boolean_t descend,
4737    const char *who, char *perms, nvlist_t *top_nvl)
4738{
4739	int i;
4740	char ld[2] = { '\0', '\0' };
4741	char who_buf[ZFS_MAXNAMELEN+32];
4742	char base_type;
4743	char set_type;
4744	nvlist_t *base_nvl = NULL;
4745	nvlist_t *set_nvl = NULL;
4746	nvlist_t *nvl;
4747
4748	if (nvlist_alloc(&base_nvl, NV_UNIQUE_NAME, 0) != 0)
4749		nomem();
4750	if (nvlist_alloc(&set_nvl, NV_UNIQUE_NAME, 0) !=  0)
4751		nomem();
4752
4753	switch (type) {
4754	case ZFS_DELEG_NAMED_SET_SETS:
4755	case ZFS_DELEG_NAMED_SET:
4756		set_type = ZFS_DELEG_NAMED_SET_SETS;
4757		base_type = ZFS_DELEG_NAMED_SET;
4758		ld[0] = ZFS_DELEG_NA;
4759		break;
4760	case ZFS_DELEG_CREATE_SETS:
4761	case ZFS_DELEG_CREATE:
4762		set_type = ZFS_DELEG_CREATE_SETS;
4763		base_type = ZFS_DELEG_CREATE;
4764		ld[0] = ZFS_DELEG_NA;
4765		break;
4766	case ZFS_DELEG_USER_SETS:
4767	case ZFS_DELEG_USER:
4768		set_type = ZFS_DELEG_USER_SETS;
4769		base_type = ZFS_DELEG_USER;
4770		if (local)
4771			ld[0] = ZFS_DELEG_LOCAL;
4772		if (descend)
4773			ld[1] = ZFS_DELEG_DESCENDENT;
4774		break;
4775	case ZFS_DELEG_GROUP_SETS:
4776	case ZFS_DELEG_GROUP:
4777		set_type = ZFS_DELEG_GROUP_SETS;
4778		base_type = ZFS_DELEG_GROUP;
4779		if (local)
4780			ld[0] = ZFS_DELEG_LOCAL;
4781		if (descend)
4782			ld[1] = ZFS_DELEG_DESCENDENT;
4783		break;
4784	case ZFS_DELEG_EVERYONE_SETS:
4785	case ZFS_DELEG_EVERYONE:
4786		set_type = ZFS_DELEG_EVERYONE_SETS;
4787		base_type = ZFS_DELEG_EVERYONE;
4788		if (local)
4789			ld[0] = ZFS_DELEG_LOCAL;
4790		if (descend)
4791			ld[1] = ZFS_DELEG_DESCENDENT;
4792	}
4793
4794	if (perms != NULL) {
4795		char *curr = perms;
4796		char *end = curr + strlen(perms);
4797
4798		while (curr < end) {
4799			char *delim = strchr(curr, ',');
4800			if (delim == NULL)
4801				delim = end;
4802			else
4803				*delim = '\0';
4804
4805			if (curr[0] == '@')
4806				nvl = set_nvl;
4807			else
4808				nvl = base_nvl;
4809
4810			(void) nvlist_add_boolean(nvl, curr);
4811			if (delim != end)
4812				*delim = ',';
4813			curr = delim + 1;
4814		}
4815
4816		for (i = 0; i < 2; i++) {
4817			char locality = ld[i];
4818			if (locality == 0)
4819				continue;
4820
4821			if (!nvlist_empty(base_nvl)) {
4822				if (who != NULL)
4823					(void) snprintf(who_buf,
4824					    sizeof (who_buf), "%c%c$%s",
4825					    base_type, locality, who);
4826				else
4827					(void) snprintf(who_buf,
4828					    sizeof (who_buf), "%c%c$",
4829					    base_type, locality);
4830
4831				(void) nvlist_add_nvlist(top_nvl, who_buf,
4832				    base_nvl);
4833			}
4834
4835
4836			if (!nvlist_empty(set_nvl)) {
4837				if (who != NULL)
4838					(void) snprintf(who_buf,
4839					    sizeof (who_buf), "%c%c$%s",
4840					    set_type, locality, who);
4841				else
4842					(void) snprintf(who_buf,
4843					    sizeof (who_buf), "%c%c$",
4844					    set_type, locality);
4845
4846				(void) nvlist_add_nvlist(top_nvl, who_buf,
4847				    set_nvl);
4848			}
4849		}
4850	} else {
4851		for (i = 0; i < 2; i++) {
4852			char locality = ld[i];
4853			if (locality == 0)
4854				continue;
4855
4856			if (who != NULL)
4857				(void) snprintf(who_buf, sizeof (who_buf),
4858				    "%c%c$%s", base_type, locality, who);
4859			else
4860				(void) snprintf(who_buf, sizeof (who_buf),
4861				    "%c%c$", base_type, locality);
4862			(void) nvlist_add_boolean(top_nvl, who_buf);
4863
4864			if (who != NULL)
4865				(void) snprintf(who_buf, sizeof (who_buf),
4866				    "%c%c$%s", set_type, locality, who);
4867			else
4868				(void) snprintf(who_buf, sizeof (who_buf),
4869				    "%c%c$", set_type, locality);
4870			(void) nvlist_add_boolean(top_nvl, who_buf);
4871		}
4872	}
4873}
4874
4875static int
4876construct_fsacl_list(boolean_t un, struct allow_opts *opts, nvlist_t **nvlp)
4877{
4878	if (nvlist_alloc(nvlp, NV_UNIQUE_NAME, 0) != 0)
4879		nomem();
4880
4881	if (opts->set) {
4882		store_allow_perm(ZFS_DELEG_NAMED_SET, opts->local,
4883		    opts->descend, opts->who, opts->perms, *nvlp);
4884	} else if (opts->create) {
4885		store_allow_perm(ZFS_DELEG_CREATE, opts->local,
4886		    opts->descend, NULL, opts->perms, *nvlp);
4887	} else if (opts->everyone) {
4888		store_allow_perm(ZFS_DELEG_EVERYONE, opts->local,
4889		    opts->descend, NULL, opts->perms, *nvlp);
4890	} else {
4891		char *curr = opts->who;
4892		char *end = curr + strlen(curr);
4893
4894		while (curr < end) {
4895			const char *who;
4896			zfs_deleg_who_type_t who_type;
4897			char *endch;
4898			char *delim = strchr(curr, ',');
4899			char errbuf[256];
4900			char id[64];
4901			struct passwd *p = NULL;
4902			struct group *g = NULL;
4903
4904			uid_t rid;
4905			if (delim == NULL)
4906				delim = end;
4907			else
4908				*delim = '\0';
4909
4910			rid = (uid_t)strtol(curr, &endch, 0);
4911			if (opts->user) {
4912				who_type = ZFS_DELEG_USER;
4913				if (*endch != '\0')
4914					p = getpwnam(curr);
4915				else
4916					p = getpwuid(rid);
4917
4918				if (p != NULL)
4919					rid = p->pw_uid;
4920				else {
4921					(void) snprintf(errbuf, 256, gettext(
4922					    "invalid user %s"), curr);
4923					allow_usage(un, B_TRUE, errbuf);
4924				}
4925			} else if (opts->group) {
4926				who_type = ZFS_DELEG_GROUP;
4927				if (*endch != '\0')
4928					g = getgrnam(curr);
4929				else
4930					g = getgrgid(rid);
4931
4932				if (g != NULL)
4933					rid = g->gr_gid;
4934				else {
4935					(void) snprintf(errbuf, 256, gettext(
4936					    "invalid group %s"),  curr);
4937					allow_usage(un, B_TRUE, errbuf);
4938				}
4939			} else {
4940				if (*endch != '\0') {
4941					p = getpwnam(curr);
4942				} else {
4943					p = getpwuid(rid);
4944				}
4945
4946				if (p == NULL)
4947					if (*endch != '\0') {
4948						g = getgrnam(curr);
4949					} else {
4950						g = getgrgid(rid);
4951					}
4952
4953				if (p != NULL) {
4954					who_type = ZFS_DELEG_USER;
4955					rid = p->pw_uid;
4956				} else if (g != NULL) {
4957					who_type = ZFS_DELEG_GROUP;
4958					rid = g->gr_gid;
4959				} else {
4960					(void) snprintf(errbuf, 256, gettext(
4961					    "invalid user/group %s"), curr);
4962					allow_usage(un, B_TRUE, errbuf);
4963				}
4964			}
4965
4966			(void) sprintf(id, "%u", rid);
4967			who = id;
4968
4969			store_allow_perm(who_type, opts->local,
4970			    opts->descend, who, opts->perms, *nvlp);
4971			curr = delim + 1;
4972		}
4973	}
4974
4975	return (0);
4976}
4977
4978static void
4979print_set_creat_perms(uu_avl_t *who_avl)
4980{
4981	const char *sc_title[] = {
4982		gettext("Permission sets:\n"),
4983		gettext("Create time permissions:\n"),
4984		NULL
4985	};
4986	const char **title_ptr = sc_title;
4987	who_perm_node_t *who_node = NULL;
4988	int prev_weight = -1;
4989
4990	for (who_node = uu_avl_first(who_avl); who_node != NULL;
4991	    who_node = uu_avl_next(who_avl, who_node)) {
4992		uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl;
4993		zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;
4994		const char *who_name = who_node->who_perm.who_name;
4995		int weight = who_type2weight(who_type);
4996		boolean_t first = B_TRUE;
4997		deleg_perm_node_t *deleg_node;
4998
4999		if (prev_weight != weight) {
5000			(void) printf(*title_ptr++);
5001			prev_weight = weight;
5002		}
5003
5004		if (who_name == NULL || strnlen(who_name, 1) == 0)
5005			(void) printf("\t");
5006		else
5007			(void) printf("\t%s ", who_name);
5008
5009		for (deleg_node = uu_avl_first(avl); deleg_node != NULL;
5010		    deleg_node = uu_avl_next(avl, deleg_node)) {
5011			if (first) {
5012				(void) printf("%s",
5013				    deleg_node->dpn_perm.dp_name);
5014				first = B_FALSE;
5015			} else
5016				(void) printf(",%s",
5017				    deleg_node->dpn_perm.dp_name);
5018		}
5019
5020		(void) printf("\n");
5021	}
5022}
5023
5024static void inline
5025print_uge_deleg_perms(uu_avl_t *who_avl, boolean_t local, boolean_t descend,
5026    const char *title)
5027{
5028	who_perm_node_t *who_node = NULL;
5029	boolean_t prt_title = B_TRUE;
5030	uu_avl_walk_t *walk;
5031
5032	if ((walk = uu_avl_walk_start(who_avl, UU_WALK_ROBUST)) == NULL)
5033		nomem();
5034
5035	while ((who_node = uu_avl_walk_next(walk)) != NULL) {
5036		const char *who_name = who_node->who_perm.who_name;
5037		const char *nice_who_name = who_node->who_perm.who_ug_name;
5038		uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl;
5039		zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;
5040		char delim = ' ';
5041		deleg_perm_node_t *deleg_node;
5042		boolean_t prt_who = B_TRUE;
5043
5044		for (deleg_node = uu_avl_first(avl);
5045		    deleg_node != NULL;
5046		    deleg_node = uu_avl_next(avl, deleg_node)) {
5047			if (local != deleg_node->dpn_perm.dp_local ||
5048			    descend != deleg_node->dpn_perm.dp_descend)
5049				continue;
5050
5051			if (prt_who) {
5052				const char *who = NULL;
5053				if (prt_title) {
5054					prt_title = B_FALSE;
5055					(void) printf(title);
5056				}
5057
5058				switch (who_type) {
5059				case ZFS_DELEG_USER_SETS:
5060				case ZFS_DELEG_USER:
5061					who = gettext("user");
5062					if (nice_who_name)
5063						who_name  = nice_who_name;
5064					break;
5065				case ZFS_DELEG_GROUP_SETS:
5066				case ZFS_DELEG_GROUP:
5067					who = gettext("group");
5068					if (nice_who_name)
5069						who_name  = nice_who_name;
5070					break;
5071				case ZFS_DELEG_EVERYONE_SETS:
5072				case ZFS_DELEG_EVERYONE:
5073					who = gettext("everyone");
5074					who_name = NULL;
5075				}
5076
5077				prt_who = B_FALSE;
5078				if (who_name == NULL)
5079					(void) printf("\t%s", who);
5080				else
5081					(void) printf("\t%s %s", who, who_name);
5082			}
5083
5084			(void) printf("%c%s", delim,
5085			    deleg_node->dpn_perm.dp_name);
5086			delim = ',';
5087		}
5088
5089		if (!prt_who)
5090			(void) printf("\n");
5091	}
5092
5093	uu_avl_walk_end(walk);
5094}
5095
5096static void
5097print_fs_perms(fs_perm_set_t *fspset)
5098{
5099	fs_perm_node_t *node = NULL;
5100	char buf[ZFS_MAXNAMELEN+32];
5101	const char *dsname = buf;
5102
5103	for (node = uu_list_first(fspset->fsps_list); node != NULL;
5104	    node = uu_list_next(fspset->fsps_list, node)) {
5105		uu_avl_t *sc_avl = node->fspn_fsperm.fsp_sc_avl;
5106		uu_avl_t *uge_avl = node->fspn_fsperm.fsp_uge_avl;
5107		int left = 0;
5108
5109		(void) snprintf(buf, ZFS_MAXNAMELEN+32,
5110		    gettext("---- Permissions on %s "),
5111		    node->fspn_fsperm.fsp_name);
5112		(void) printf(dsname);
5113		left = 70 - strlen(buf);
5114		while (left-- > 0)
5115			(void) printf("-");
5116		(void) printf("\n");
5117
5118		print_set_creat_perms(sc_avl);
5119		print_uge_deleg_perms(uge_avl, B_TRUE, B_FALSE,
5120		    gettext("Local permissions:\n"));
5121		print_uge_deleg_perms(uge_avl, B_FALSE, B_TRUE,
5122		    gettext("Descendent permissions:\n"));
5123		print_uge_deleg_perms(uge_avl, B_TRUE, B_TRUE,
5124		    gettext("Local+Descendent permissions:\n"));
5125	}
5126}
5127
5128static fs_perm_set_t fs_perm_set = { NULL, NULL, NULL, NULL };
5129
5130struct deleg_perms {
5131	boolean_t un;
5132	nvlist_t *nvl;
5133};
5134
5135static int
5136set_deleg_perms(zfs_handle_t *zhp, void *data)
5137{
5138	struct deleg_perms *perms = (struct deleg_perms *)data;
5139	zfs_type_t zfs_type = zfs_get_type(zhp);
5140
5141	if (zfs_type != ZFS_TYPE_FILESYSTEM && zfs_type != ZFS_TYPE_VOLUME)
5142		return (0);
5143
5144	return (zfs_set_fsacl(zhp, perms->un, perms->nvl));
5145}
5146
5147static int
5148zfs_do_allow_unallow_impl(int argc, char **argv, boolean_t un)
5149{
5150	zfs_handle_t *zhp;
5151	nvlist_t *perm_nvl = NULL;
5152	nvlist_t *update_perm_nvl = NULL;
5153	int error = 1;
5154	int c;
5155	struct allow_opts opts = { 0 };
5156
5157	const char *optstr = un ? "ldugecsrh" : "ldugecsh";
5158
5159	/* check opts */
5160	while ((c = getopt(argc, argv, optstr)) != -1) {
5161		switch (c) {
5162		case 'l':
5163			opts.local = B_TRUE;
5164			break;
5165		case 'd':
5166			opts.descend = B_TRUE;
5167			break;
5168		case 'u':
5169			opts.user = B_TRUE;
5170			break;
5171		case 'g':
5172			opts.group = B_TRUE;
5173			break;
5174		case 'e':
5175			opts.everyone = B_TRUE;
5176			break;
5177		case 's':
5178			opts.set = B_TRUE;
5179			break;
5180		case 'c':
5181			opts.create = B_TRUE;
5182			break;
5183		case 'r':
5184			opts.recursive = B_TRUE;
5185			break;
5186		case ':':
5187			(void) fprintf(stderr, gettext("missing argument for "
5188			    "'%c' option\n"), optopt);
5189			usage(B_FALSE);
5190			break;
5191		case 'h':
5192			opts.prt_usage = B_TRUE;
5193			break;
5194		case '?':
5195			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
5196			    optopt);
5197			usage(B_FALSE);
5198		}
5199	}
5200
5201	argc -= optind;
5202	argv += optind;
5203
5204	/* check arguments */
5205	parse_allow_args(argc, argv, un, &opts);
5206
5207	/* try to open the dataset */
5208	if ((zhp = zfs_open(g_zfs, opts.dataset, ZFS_TYPE_FILESYSTEM |
5209	    ZFS_TYPE_VOLUME)) == NULL) {
5210		(void) fprintf(stderr, "Failed to open dataset: %s\n",
5211		    opts.dataset);
5212		return (-1);
5213	}
5214
5215	if (zfs_get_fsacl(zhp, &perm_nvl) != 0)
5216		goto cleanup2;
5217
5218	fs_perm_set_init(&fs_perm_set);
5219	if (parse_fs_perm_set(&fs_perm_set, perm_nvl) != 0) {
5220		(void) fprintf(stderr, "Failed to parse fsacl permissions\n");
5221		goto cleanup1;
5222	}
5223
5224	if (opts.prt_perms)
5225		print_fs_perms(&fs_perm_set);
5226	else {
5227		(void) construct_fsacl_list(un, &opts, &update_perm_nvl);
5228		if (zfs_set_fsacl(zhp, un, update_perm_nvl) != 0)
5229			goto cleanup0;
5230
5231		if (un && opts.recursive) {
5232			struct deleg_perms data = { un, update_perm_nvl };
5233			if (zfs_iter_filesystems(zhp, set_deleg_perms,
5234			    &data) != 0)
5235				goto cleanup0;
5236		}
5237	}
5238
5239	error = 0;
5240
5241cleanup0:
5242	nvlist_free(perm_nvl);
5243	if (update_perm_nvl != NULL)
5244		nvlist_free(update_perm_nvl);
5245cleanup1:
5246	fs_perm_set_fini(&fs_perm_set);
5247cleanup2:
5248	zfs_close(zhp);
5249
5250	return (error);
5251}
5252
5253static int
5254zfs_do_allow(int argc, char **argv)
5255{
5256	return (zfs_do_allow_unallow_impl(argc, argv, B_FALSE));
5257}
5258
5259static int
5260zfs_do_unallow(int argc, char **argv)
5261{
5262	return (zfs_do_allow_unallow_impl(argc, argv, B_TRUE));
5263}
5264
5265static int
5266zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding)
5267{
5268	int errors = 0;
5269	int i;
5270	const char *tag;
5271	boolean_t recursive = B_FALSE;
5272	const char *opts = holding ? "rt" : "r";
5273	int c;
5274
5275	/* check options */
5276	while ((c = getopt(argc, argv, opts)) != -1) {
5277		switch (c) {
5278		case 'r':
5279			recursive = B_TRUE;
5280			break;
5281		case '?':
5282			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
5283			    optopt);
5284			usage(B_FALSE);
5285		}
5286	}
5287
5288	argc -= optind;
5289	argv += optind;
5290
5291	/* check number of arguments */
5292	if (argc < 2)
5293		usage(B_FALSE);
5294
5295	tag = argv[0];
5296	--argc;
5297	++argv;
5298
5299	if (holding && tag[0] == '.') {
5300		/* tags starting with '.' are reserved for libzfs */
5301		(void) fprintf(stderr, gettext("tag may not start with '.'\n"));
5302		usage(B_FALSE);
5303	}
5304
5305	for (i = 0; i < argc; ++i) {
5306		zfs_handle_t *zhp;
5307		char parent[ZFS_MAXNAMELEN];
5308		const char *delim;
5309		char *path = argv[i];
5310
5311		delim = strchr(path, '@');
5312		if (delim == NULL) {
5313			(void) fprintf(stderr,
5314			    gettext("'%s' is not a snapshot\n"), path);
5315			++errors;
5316			continue;
5317		}
5318		(void) strncpy(parent, path, delim - path);
5319		parent[delim - path] = '\0';
5320
5321		zhp = zfs_open(g_zfs, parent,
5322		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
5323		if (zhp == NULL) {
5324			++errors;
5325			continue;
5326		}
5327		if (holding) {
5328			if (zfs_hold(zhp, delim+1, tag, recursive, -1) != 0)
5329				++errors;
5330		} else {
5331			if (zfs_release(zhp, delim+1, tag, recursive) != 0)
5332				++errors;
5333		}
5334		zfs_close(zhp);
5335	}
5336
5337	return (errors != 0);
5338}
5339
5340/*
5341 * zfs hold [-r] [-t] <tag> <snap> ...
5342 *
5343 *	-r	Recursively hold
5344 *
5345 * Apply a user-hold with the given tag to the list of snapshots.
5346 */
5347static int
5348zfs_do_hold(int argc, char **argv)
5349{
5350	return (zfs_do_hold_rele_impl(argc, argv, B_TRUE));
5351}
5352
5353/*
5354 * zfs release [-r] <tag> <snap> ...
5355 *
5356 *	-r	Recursively release
5357 *
5358 * Release a user-hold with the given tag from the list of snapshots.
5359 */
5360static int
5361zfs_do_release(int argc, char **argv)
5362{
5363	return (zfs_do_hold_rele_impl(argc, argv, B_FALSE));
5364}
5365
5366typedef struct holds_cbdata {
5367	boolean_t	cb_recursive;
5368	const char	*cb_snapname;
5369	nvlist_t	**cb_nvlp;
5370	size_t		cb_max_namelen;
5371	size_t		cb_max_taglen;
5372} holds_cbdata_t;
5373
5374#define	STRFTIME_FMT_STR "%a %b %e %k:%M %Y"
5375#define	DATETIME_BUF_LEN (32)
5376/*
5377 *
5378 */
5379static void
5380print_holds(boolean_t scripted, size_t nwidth, size_t tagwidth, nvlist_t *nvl)
5381{
5382	int i;
5383	nvpair_t *nvp = NULL;
5384	char *hdr_cols[] = { "NAME", "TAG", "TIMESTAMP" };
5385	const char *col;
5386
5387	if (!scripted) {
5388		for (i = 0; i < 3; i++) {
5389			col = gettext(hdr_cols[i]);
5390			if (i < 2)
5391				(void) printf("%-*s  ", i ? tagwidth : nwidth,
5392				    col);
5393			else
5394				(void) printf("%s\n", col);
5395		}
5396	}
5397
5398	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
5399		char *zname = nvpair_name(nvp);
5400		nvlist_t *nvl2;
5401		nvpair_t *nvp2 = NULL;
5402		(void) nvpair_value_nvlist(nvp, &nvl2);
5403		while ((nvp2 = nvlist_next_nvpair(nvl2, nvp2)) != NULL) {
5404			char tsbuf[DATETIME_BUF_LEN];
5405			char *tagname = nvpair_name(nvp2);
5406			uint64_t val = 0;
5407			time_t time;
5408			struct tm t;
5409			char sep = scripted ? '\t' : ' ';
5410			size_t sepnum = scripted ? 1 : 2;
5411
5412			(void) nvpair_value_uint64(nvp2, &val);
5413			time = (time_t)val;
5414			(void) localtime_r(&time, &t);
5415			(void) strftime(tsbuf, DATETIME_BUF_LEN,
5416			    gettext(STRFTIME_FMT_STR), &t);
5417
5418			(void) printf("%-*s%*c%-*s%*c%s\n", nwidth, zname,
5419			    sepnum, sep, tagwidth, tagname, sepnum, sep, tsbuf);
5420		}
5421	}
5422}
5423
5424/*
5425 * Generic callback function to list a dataset or snapshot.
5426 */
5427static int
5428holds_callback(zfs_handle_t *zhp, void *data)
5429{
5430	holds_cbdata_t *cbp = data;
5431	nvlist_t *top_nvl = *cbp->cb_nvlp;
5432	nvlist_t *nvl = NULL;
5433	nvpair_t *nvp = NULL;
5434	const char *zname = zfs_get_name(zhp);
5435	size_t znamelen = strnlen(zname, ZFS_MAXNAMELEN);
5436
5437	if (cbp->cb_recursive) {
5438		const char *snapname;
5439		char *delim  = strchr(zname, '@');
5440		if (delim == NULL)
5441			return (0);
5442
5443		snapname = delim + 1;
5444		if (strcmp(cbp->cb_snapname, snapname))
5445			return (0);
5446	}
5447
5448	if (zfs_get_holds(zhp, &nvl) != 0)
5449		return (-1);
5450
5451	if (znamelen > cbp->cb_max_namelen)
5452		cbp->cb_max_namelen  = znamelen;
5453
5454	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
5455		const char *tag = nvpair_name(nvp);
5456		size_t taglen = strnlen(tag, MAXNAMELEN);
5457		if (taglen > cbp->cb_max_taglen)
5458			cbp->cb_max_taglen  = taglen;
5459	}
5460
5461	return (nvlist_add_nvlist(top_nvl, zname, nvl));
5462}
5463
5464/*
5465 * zfs holds [-r] <snap> ...
5466 *
5467 *	-r	Recursively hold
5468 */
5469static int
5470zfs_do_holds(int argc, char **argv)
5471{
5472	int errors = 0;
5473	int c;
5474	int i;
5475	boolean_t scripted = B_FALSE;
5476	boolean_t recursive = B_FALSE;
5477	const char *opts = "rH";
5478	nvlist_t *nvl;
5479
5480	int types = ZFS_TYPE_SNAPSHOT;
5481	holds_cbdata_t cb = { 0 };
5482
5483	int limit = 0;
5484	int ret = 0;
5485	int flags = 0;
5486
5487	/* check options */
5488	while ((c = getopt(argc, argv, opts)) != -1) {
5489		switch (c) {
5490		case 'r':
5491			recursive = B_TRUE;
5492			break;
5493		case 'H':
5494			scripted = B_TRUE;
5495			break;
5496		case '?':
5497			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
5498			    optopt);
5499			usage(B_FALSE);
5500		}
5501	}
5502
5503	if (recursive) {
5504		types |= ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;
5505		flags |= ZFS_ITER_RECURSE;
5506	}
5507
5508	argc -= optind;
5509	argv += optind;
5510
5511	/* check number of arguments */
5512	if (argc < 1)
5513		usage(B_FALSE);
5514
5515	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
5516		nomem();
5517
5518	for (i = 0; i < argc; ++i) {
5519		char *snapshot = argv[i];
5520		const char *delim;
5521		const char *snapname;
5522
5523		delim = strchr(snapshot, '@');
5524		if (delim == NULL) {
5525			(void) fprintf(stderr,
5526			    gettext("'%s' is not a snapshot\n"), snapshot);
5527			++errors;
5528			continue;
5529		}
5530		snapname = delim + 1;
5531		if (recursive)
5532			snapshot[delim - snapshot] = '\0';
5533
5534		cb.cb_recursive = recursive;
5535		cb.cb_snapname = snapname;
5536		cb.cb_nvlp = &nvl;
5537
5538		/*
5539		 *  1. collect holds data, set format options
5540		 */
5541		ret = zfs_for_each(argc, argv, flags, types, NULL, NULL, limit,
5542		    holds_callback, &cb);
5543		if (ret != 0)
5544			++errors;
5545	}
5546
5547	/*
5548	 *  2. print holds data
5549	 */
5550	print_holds(scripted, cb.cb_max_namelen, cb.cb_max_taglen, nvl);
5551
5552	if (nvlist_empty(nvl))
5553		(void) printf(gettext("no datasets available\n"));
5554
5555	nvlist_free(nvl);
5556
5557	return (0 != errors);
5558}
5559
5560#define	CHECK_SPINNER 30
5561#define	SPINNER_TIME 3		/* seconds */
5562#define	MOUNT_TIME 5		/* seconds */
5563
5564static int
5565get_one_dataset(zfs_handle_t *zhp, void *data)
5566{
5567	static char *spin[] = { "-", "\\", "|", "/" };
5568	static int spinval = 0;
5569	static int spincheck = 0;
5570	static time_t last_spin_time = (time_t)0;
5571	get_all_cb_t *cbp = data;
5572	zfs_type_t type = zfs_get_type(zhp);
5573
5574	if (cbp->cb_verbose) {
5575		if (--spincheck < 0) {
5576			time_t now = time(NULL);
5577			if (last_spin_time + SPINNER_TIME < now) {
5578				update_progress(spin[spinval++ % 4]);
5579				last_spin_time = now;
5580			}
5581			spincheck = CHECK_SPINNER;
5582		}
5583	}
5584
5585	/*
5586	 * Interate over any nested datasets.
5587	 */
5588	if (zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) {
5589		zfs_close(zhp);
5590		return (1);
5591	}
5592
5593	/*
5594	 * Skip any datasets whose type does not match.
5595	 */
5596	if ((type & ZFS_TYPE_FILESYSTEM) == 0) {
5597		zfs_close(zhp);
5598		return (0);
5599	}
5600	libzfs_add_handle(cbp, zhp);
5601	assert(cbp->cb_used <= cbp->cb_alloc);
5602
5603	return (0);
5604}
5605
5606static void
5607get_all_datasets(zfs_handle_t ***dslist, size_t *count, boolean_t verbose)
5608{
5609	get_all_cb_t cb = { 0 };
5610	cb.cb_verbose = verbose;
5611	cb.cb_getone = get_one_dataset;
5612
5613	if (verbose)
5614		set_progress_header(gettext("Reading ZFS config"));
5615	(void) zfs_iter_root(g_zfs, get_one_dataset, &cb);
5616
5617	*dslist = cb.cb_handles;
5618	*count = cb.cb_used;
5619
5620	if (verbose)
5621		finish_progress(gettext("done."));
5622}
5623
5624/*
5625 * Generic callback for sharing or mounting filesystems.  Because the code is so
5626 * similar, we have a common function with an extra parameter to determine which
5627 * mode we are using.
5628 */
5629#define	OP_SHARE	0x1
5630#define	OP_MOUNT	0x2
5631
5632/*
5633 * Share or mount a dataset.
5634 */
5635static int
5636share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol,
5637    boolean_t explicit, const char *options)
5638{
5639	char mountpoint[ZFS_MAXPROPLEN];
5640	char shareopts[ZFS_MAXPROPLEN];
5641	char smbshareopts[ZFS_MAXPROPLEN];
5642	const char *cmdname = op == OP_SHARE ? "share" : "mount";
5643	struct mnttab mnt;
5644	uint64_t zoned, canmount;
5645	boolean_t shared_nfs, shared_smb;
5646
5647	assert(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM);
5648
5649	/*
5650	 * Check to make sure we can mount/share this dataset.  If we
5651	 * are in the global zone and the filesystem is exported to a
5652	 * local zone, or if we are in a local zone and the
5653	 * filesystem is not exported, then it is an error.
5654	 */
5655	zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
5656
5657	if (zoned && getzoneid() == GLOBAL_ZONEID) {
5658		if (!explicit)
5659			return (0);
5660
5661		(void) fprintf(stderr, gettext("cannot %s '%s': "
5662		    "dataset is exported to a local zone\n"), cmdname,
5663		    zfs_get_name(zhp));
5664		return (1);
5665
5666	} else if (!zoned && getzoneid() != GLOBAL_ZONEID) {
5667		if (!explicit)
5668			return (0);
5669
5670		(void) fprintf(stderr, gettext("cannot %s '%s': "
5671		    "permission denied\n"), cmdname,
5672		    zfs_get_name(zhp));
5673		return (1);
5674	}
5675
5676	/*
5677	 * Ignore any filesystems which don't apply to us. This
5678	 * includes those with a legacy mountpoint, or those with
5679	 * legacy share options.
5680	 */
5681	verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
5682	    sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0);
5683	verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts,
5684	    sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0);
5685	verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshareopts,
5686	    sizeof (smbshareopts), NULL, NULL, 0, B_FALSE) == 0);
5687
5688	if (op == OP_SHARE && strcmp(shareopts, "off") == 0 &&
5689	    strcmp(smbshareopts, "off") == 0) {
5690		if (!explicit)
5691			return (0);
5692
5693		(void) fprintf(stderr, gettext("cannot share '%s': "
5694		    "legacy share\n"), zfs_get_name(zhp));
5695		(void) fprintf(stderr, gettext("to "
5696		    "share this filesystem set "
5697		    "sharenfs property on\n"));
5698		return (1);
5699	}
5700
5701	/*
5702	 * We cannot share or mount legacy filesystems. If the
5703	 * shareopts is non-legacy but the mountpoint is legacy, we
5704	 * treat it as a legacy share.
5705	 */
5706	if (strcmp(mountpoint, "legacy") == 0) {
5707		if (!explicit)
5708			return (0);
5709
5710		(void) fprintf(stderr, gettext("cannot %s '%s': "
5711		    "legacy mountpoint\n"), cmdname, zfs_get_name(zhp));
5712		(void) fprintf(stderr, gettext("use %s(8) to "
5713		    "%s this filesystem\n"), cmdname, cmdname);
5714		return (1);
5715	}
5716
5717	if (strcmp(mountpoint, "none") == 0) {
5718		if (!explicit)
5719			return (0);
5720
5721		(void) fprintf(stderr, gettext("cannot %s '%s': no "
5722		    "mountpoint set\n"), cmdname, zfs_get_name(zhp));
5723		return (1);
5724	}
5725
5726	/*
5727	 * canmount	explicit	outcome
5728	 * on		no		pass through
5729	 * on		yes		pass through
5730	 * off		no		return 0
5731	 * off		yes		display error, return 1
5732	 * noauto	no		return 0
5733	 * noauto	yes		pass through
5734	 */
5735	canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);
5736	if (canmount == ZFS_CANMOUNT_OFF) {
5737		if (!explicit)
5738			return (0);
5739
5740		(void) fprintf(stderr, gettext("cannot %s '%s': "
5741		    "'canmount' property is set to 'off'\n"), cmdname,
5742		    zfs_get_name(zhp));
5743		return (1);
5744	} else if (canmount == ZFS_CANMOUNT_NOAUTO && !explicit) {
5745		return (0);
5746	}
5747
5748	/*
5749	 * At this point, we have verified that the mountpoint and/or
5750	 * shareopts are appropriate for auto management. If the
5751	 * filesystem is already mounted or shared, return (failing
5752	 * for explicit requests); otherwise mount or share the
5753	 * filesystem.
5754	 */
5755	switch (op) {
5756	case OP_SHARE:
5757
5758		shared_nfs = zfs_is_shared_nfs(zhp, NULL);
5759		shared_smb = zfs_is_shared_smb(zhp, NULL);
5760
5761		if (shared_nfs && shared_smb ||
5762		    (shared_nfs && strcmp(shareopts, "on") == 0 &&
5763		    strcmp(smbshareopts, "off") == 0) ||
5764		    (shared_smb && strcmp(smbshareopts, "on") == 0 &&
5765		    strcmp(shareopts, "off") == 0)) {
5766			if (!explicit)
5767				return (0);
5768
5769			(void) fprintf(stderr, gettext("cannot share "
5770			    "'%s': filesystem already shared\n"),
5771			    zfs_get_name(zhp));
5772			return (1);
5773		}
5774
5775		if (!zfs_is_mounted(zhp, NULL) &&
5776		    zfs_mount(zhp, NULL, 0) != 0)
5777			return (1);
5778
5779		if (protocol == NULL) {
5780			if (zfs_shareall(zhp) != 0)
5781				return (1);
5782		} else if (strcmp(protocol, "nfs") == 0) {
5783			if (zfs_share_nfs(zhp))
5784				return (1);
5785		} else if (strcmp(protocol, "smb") == 0) {
5786			if (zfs_share_smb(zhp))
5787				return (1);
5788		} else {
5789			(void) fprintf(stderr, gettext("cannot share "
5790			    "'%s': invalid share type '%s' "
5791			    "specified\n"),
5792			    zfs_get_name(zhp), protocol);
5793			return (1);
5794		}
5795
5796		break;
5797
5798	case OP_MOUNT:
5799		if (options == NULL)
5800			mnt.mnt_mntopts = "";
5801		else
5802			mnt.mnt_mntopts = (char *)options;
5803
5804		if (!hasmntopt(&mnt, MNTOPT_REMOUNT) &&
5805		    zfs_is_mounted(zhp, NULL)) {
5806			if (!explicit)
5807				return (0);
5808
5809			(void) fprintf(stderr, gettext("cannot mount "
5810			    "'%s': filesystem already mounted\n"),
5811			    zfs_get_name(zhp));
5812			return (1);
5813		}
5814
5815		if (zfs_mount(zhp, options, flags) != 0)
5816			return (1);
5817		break;
5818	}
5819
5820	return (0);
5821}
5822
5823/*
5824 * Reports progress in the form "(current/total)".  Not thread-safe.
5825 */
5826static void
5827report_mount_progress(int current, int total)
5828{
5829	static time_t last_progress_time = 0;
5830	time_t now = time(NULL);
5831	char info[32];
5832
5833	/* report 1..n instead of 0..n-1 */
5834	++current;
5835
5836	/* display header if we're here for the first time */
5837	if (current == 1) {
5838		set_progress_header(gettext("Mounting ZFS filesystems"));
5839	} else if (current != total && last_progress_time + MOUNT_TIME >= now) {
5840		/* too soon to report again */
5841		return;
5842	}
5843
5844	last_progress_time = now;
5845
5846	(void) sprintf(info, "(%d/%d)", current, total);
5847
5848	if (current == total)
5849		finish_progress(info);
5850	else
5851		update_progress(info);
5852}
5853
5854static void
5855append_options(char *mntopts, char *newopts)
5856{
5857	int len = strlen(mntopts);
5858
5859	/* original length plus new string to append plus 1 for the comma */
5860	if (len + 1 + strlen(newopts) >= MNT_LINE_MAX) {
5861		(void) fprintf(stderr, gettext("the opts argument for "
5862		    "'%c' option is too long (more than %d chars)\n"),
5863		    "-o", MNT_LINE_MAX);
5864		usage(B_FALSE);
5865	}
5866
5867	if (*mntopts)
5868		mntopts[len++] = ',';
5869
5870	(void) strcpy(&mntopts[len], newopts);
5871}
5872
5873static int
5874share_mount(int op, int argc, char **argv)
5875{
5876	int do_all = 0;
5877	boolean_t verbose = B_FALSE;
5878	int c, ret = 0;
5879	char *options = NULL;
5880	int flags = 0;
5881
5882	/* check options */
5883	while ((c = getopt(argc, argv, op == OP_MOUNT ? ":avo:O" : "a"))
5884	    != -1) {
5885		switch (c) {
5886		case 'a':
5887			do_all = 1;
5888			break;
5889		case 'v':
5890			verbose = B_TRUE;
5891			break;
5892		case 'o':
5893			if (*optarg == '\0') {
5894				(void) fprintf(stderr, gettext("empty mount "
5895				    "options (-o) specified\n"));
5896				usage(B_FALSE);
5897			}
5898
5899			if (options == NULL)
5900				options = safe_malloc(MNT_LINE_MAX + 1);
5901
5902			/* option validation is done later */
5903			append_options(options, optarg);
5904			break;
5905
5906		case 'O':
5907			warnx("no overlay mounts support on FreeBSD, ignoring");
5908			break;
5909		case ':':
5910			(void) fprintf(stderr, gettext("missing argument for "
5911			    "'%c' option\n"), optopt);
5912			usage(B_FALSE);
5913			break;
5914		case '?':
5915			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
5916			    optopt);
5917			usage(B_FALSE);
5918		}
5919	}
5920
5921	argc -= optind;
5922	argv += optind;
5923
5924	/* check number of arguments */
5925	if (do_all) {
5926		zfs_handle_t **dslist = NULL;
5927		size_t i, count = 0;
5928		char *protocol = NULL;
5929
5930		if (op == OP_SHARE && argc > 0) {
5931			if (strcmp(argv[0], "nfs") != 0 &&
5932			    strcmp(argv[0], "smb") != 0) {
5933				(void) fprintf(stderr, gettext("share type "
5934				    "must be 'nfs' or 'smb'\n"));
5935				usage(B_FALSE);
5936			}
5937			protocol = argv[0];
5938			argc--;
5939			argv++;
5940		}
5941
5942		if (argc != 0) {
5943			(void) fprintf(stderr, gettext("too many arguments\n"));
5944			usage(B_FALSE);
5945		}
5946
5947		start_progress_timer();
5948		get_all_datasets(&dslist, &count, verbose);
5949
5950		if (count == 0)
5951			return (0);
5952
5953		qsort(dslist, count, sizeof (void *), libzfs_dataset_cmp);
5954
5955		for (i = 0; i < count; i++) {
5956			if (verbose)
5957				report_mount_progress(i, count);
5958
5959			if (share_mount_one(dslist[i], op, flags, protocol,
5960			    B_FALSE, options) != 0)
5961				ret = 1;
5962			zfs_close(dslist[i]);
5963		}
5964
5965		free(dslist);
5966	} else if (argc == 0) {
5967		struct mnttab entry;
5968
5969		if ((op == OP_SHARE) || (options != NULL)) {
5970			(void) fprintf(stderr, gettext("missing filesystem "
5971			    "argument (specify -a for all)\n"));
5972			usage(B_FALSE);
5973		}
5974
5975		/*
5976		 * When mount is given no arguments, go through /etc/mnttab and
5977		 * display any active ZFS mounts.  We hide any snapshots, since
5978		 * they are controlled automatically.
5979		 */
5980		rewind(mnttab_file);
5981		while (getmntent(mnttab_file, &entry) == 0) {
5982			if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 ||
5983			    strchr(entry.mnt_special, '@') != NULL)
5984				continue;
5985
5986			(void) printf("%-30s  %s\n", entry.mnt_special,
5987			    entry.mnt_mountp);
5988		}
5989
5990	} else {
5991		zfs_handle_t *zhp;
5992
5993		if (argc > 1) {
5994			(void) fprintf(stderr,
5995			    gettext("too many arguments\n"));
5996			usage(B_FALSE);
5997		}
5998
5999		if ((zhp = zfs_open(g_zfs, argv[0],
6000		    ZFS_TYPE_FILESYSTEM)) == NULL) {
6001			ret = 1;
6002		} else {
6003			ret = share_mount_one(zhp, op, flags, NULL, B_TRUE,
6004			    options);
6005			zfs_close(zhp);
6006		}
6007	}
6008
6009	return (ret);
6010}
6011
6012/*
6013 * zfs mount -a [nfs]
6014 * zfs mount filesystem
6015 *
6016 * Mount all filesystems, or mount the given filesystem.
6017 */
6018static int
6019zfs_do_mount(int argc, char **argv)
6020{
6021	return (share_mount(OP_MOUNT, argc, argv));
6022}
6023
6024/*
6025 * zfs share -a [nfs | smb]
6026 * zfs share filesystem
6027 *
6028 * Share all filesystems, or share the given filesystem.
6029 */
6030static int
6031zfs_do_share(int argc, char **argv)
6032{
6033	return (share_mount(OP_SHARE, argc, argv));
6034}
6035
6036typedef struct unshare_unmount_node {
6037	zfs_handle_t	*un_zhp;
6038	char		*un_mountp;
6039	uu_avl_node_t	un_avlnode;
6040} unshare_unmount_node_t;
6041
6042/* ARGSUSED */
6043static int
6044unshare_unmount_compare(const void *larg, const void *rarg, void *unused)
6045{
6046	const unshare_unmount_node_t *l = larg;
6047	const unshare_unmount_node_t *r = rarg;
6048
6049	return (strcmp(l->un_mountp, r->un_mountp));
6050}
6051
6052/*
6053 * Convenience routine used by zfs_do_umount() and manual_unmount().  Given an
6054 * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem,
6055 * and unmount it appropriately.
6056 */
6057static int
6058unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
6059{
6060	zfs_handle_t *zhp;
6061	int ret = 0;
6062	struct stat64 statbuf;
6063	struct extmnttab entry;
6064	const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount";
6065	ino_t path_inode;
6066
6067	/*
6068	 * Search for the path in /etc/mnttab.  Rather than looking for the
6069	 * specific path, which can be fooled by non-standard paths (i.e. ".."
6070	 * or "//"), we stat() the path and search for the corresponding
6071	 * (major,minor) device pair.
6072	 */
6073	if (stat64(path, &statbuf) != 0) {
6074		(void) fprintf(stderr, gettext("cannot %s '%s': %s\n"),
6075		    cmdname, path, strerror(errno));
6076		return (1);
6077	}
6078	path_inode = statbuf.st_ino;
6079
6080	/*
6081	 * Search for the given (major,minor) pair in the mount table.
6082	 */
6083#ifdef sun
6084	rewind(mnttab_file);
6085	while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) {
6086		if (entry.mnt_major == major(statbuf.st_dev) &&
6087		    entry.mnt_minor == minor(statbuf.st_dev))
6088			break;
6089	}
6090#else
6091	{
6092		struct statfs sfs;
6093
6094		if (statfs(path, &sfs) != 0) {
6095			(void) fprintf(stderr, "%s: %s\n", path,
6096			    strerror(errno));
6097			ret = -1;
6098		}
6099		statfs2mnttab(&sfs, &entry);
6100	}
6101#endif
6102	if (ret != 0) {
6103		if (op == OP_SHARE) {
6104			(void) fprintf(stderr, gettext("cannot %s '%s': not "
6105			    "currently mounted\n"), cmdname, path);
6106			return (1);
6107		}
6108		(void) fprintf(stderr, gettext("warning: %s not in mnttab\n"),
6109		    path);
6110		if ((ret = umount2(path, flags)) != 0)
6111			(void) fprintf(stderr, gettext("%s: %s\n"), path,
6112			    strerror(errno));
6113		return (ret != 0);
6114	}
6115
6116	if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) {
6117		(void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS "
6118		    "filesystem\n"), cmdname, path);
6119		return (1);
6120	}
6121
6122	if ((zhp = zfs_open(g_zfs, entry.mnt_special,
6123	    ZFS_TYPE_FILESYSTEM)) == NULL)
6124		return (1);
6125
6126	ret = 1;
6127	if (stat64(entry.mnt_mountp, &statbuf) != 0) {
6128		(void) fprintf(stderr, gettext("cannot %s '%s': %s\n"),
6129		    cmdname, path, strerror(errno));
6130		goto out;
6131	} else if (statbuf.st_ino != path_inode) {
6132		(void) fprintf(stderr, gettext("cannot "
6133		    "%s '%s': not a mountpoint\n"), cmdname, path);
6134		goto out;
6135	}
6136
6137	if (op == OP_SHARE) {
6138		char nfs_mnt_prop[ZFS_MAXPROPLEN];
6139		char smbshare_prop[ZFS_MAXPROPLEN];
6140
6141		verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, nfs_mnt_prop,
6142		    sizeof (nfs_mnt_prop), NULL, NULL, 0, B_FALSE) == 0);
6143		verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshare_prop,
6144		    sizeof (smbshare_prop), NULL, NULL, 0, B_FALSE) == 0);
6145
6146		if (strcmp(nfs_mnt_prop, "off") == 0 &&
6147		    strcmp(smbshare_prop, "off") == 0) {
6148			(void) fprintf(stderr, gettext("cannot unshare "
6149			    "'%s': legacy share\n"), path);
6150#ifdef illumos
6151			(void) fprintf(stderr, gettext("use "
6152			    "unshare(1M) to unshare this filesystem\n"));
6153#endif
6154		} else if (!zfs_is_shared(zhp)) {
6155			(void) fprintf(stderr, gettext("cannot unshare '%s': "
6156			    "not currently shared\n"), path);
6157		} else {
6158			ret = zfs_unshareall_bypath(zhp, path);
6159		}
6160	} else {
6161		char mtpt_prop[ZFS_MAXPROPLEN];
6162
6163		verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mtpt_prop,
6164		    sizeof (mtpt_prop), NULL, NULL, 0, B_FALSE) == 0);
6165
6166		if (is_manual) {
6167			ret = zfs_unmount(zhp, NULL, flags);
6168		} else if (strcmp(mtpt_prop, "legacy") == 0) {
6169			(void) fprintf(stderr, gettext("cannot unmount "
6170			    "'%s': legacy mountpoint\n"),
6171			    zfs_get_name(zhp));
6172			(void) fprintf(stderr, gettext("use umount(8) "
6173			    "to unmount this filesystem\n"));
6174		} else {
6175			ret = zfs_unmountall(zhp, flags);
6176		}
6177	}
6178
6179out:
6180	zfs_close(zhp);
6181
6182	return (ret != 0);
6183}
6184
6185/*
6186 * Generic callback for unsharing or unmounting a filesystem.
6187 */
6188static int
6189unshare_unmount(int op, int argc, char **argv)
6190{
6191	int do_all = 0;
6192	int flags = 0;
6193	int ret = 0;
6194	int c;
6195	zfs_handle_t *zhp;
6196	char nfs_mnt_prop[ZFS_MAXPROPLEN];
6197	char sharesmb[ZFS_MAXPROPLEN];
6198
6199	/* check options */
6200	while ((c = getopt(argc, argv, op == OP_SHARE ? "a" : "af")) != -1) {
6201		switch (c) {
6202		case 'a':
6203			do_all = 1;
6204			break;
6205		case 'f':
6206			flags = MS_FORCE;
6207			break;
6208		case '?':
6209			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
6210			    optopt);
6211			usage(B_FALSE);
6212		}
6213	}
6214
6215	argc -= optind;
6216	argv += optind;
6217
6218	if (do_all) {
6219		/*
6220		 * We could make use of zfs_for_each() to walk all datasets in
6221		 * the system, but this would be very inefficient, especially
6222		 * since we would have to linearly search /etc/mnttab for each
6223		 * one.  Instead, do one pass through /etc/mnttab looking for
6224		 * zfs entries and call zfs_unmount() for each one.
6225		 *
6226		 * Things get a little tricky if the administrator has created
6227		 * mountpoints beneath other ZFS filesystems.  In this case, we
6228		 * have to unmount the deepest filesystems first.  To accomplish
6229		 * this, we place all the mountpoints in an AVL tree sorted by
6230		 * the special type (dataset name), and walk the result in
6231		 * reverse to make sure to get any snapshots first.
6232		 */
6233		struct mnttab entry;
6234		uu_avl_pool_t *pool;
6235		uu_avl_t *tree;
6236		unshare_unmount_node_t *node;
6237		uu_avl_index_t idx;
6238		uu_avl_walk_t *walk;
6239
6240		if (argc != 0) {
6241			(void) fprintf(stderr, gettext("too many arguments\n"));
6242			usage(B_FALSE);
6243		}
6244
6245		if (((pool = uu_avl_pool_create("unmount_pool",
6246		    sizeof (unshare_unmount_node_t),
6247		    offsetof(unshare_unmount_node_t, un_avlnode),
6248		    unshare_unmount_compare, UU_DEFAULT)) == NULL) ||
6249		    ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL))
6250			nomem();
6251
6252		rewind(mnttab_file);
6253		while (getmntent(mnttab_file, &entry) == 0) {
6254
6255			/* ignore non-ZFS entries */
6256			if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
6257				continue;
6258
6259			/* ignore snapshots */
6260			if (strchr(entry.mnt_special, '@') != NULL)
6261				continue;
6262
6263			if ((zhp = zfs_open(g_zfs, entry.mnt_special,
6264			    ZFS_TYPE_FILESYSTEM)) == NULL) {
6265				ret = 1;
6266				continue;
6267			}
6268
6269			switch (op) {
6270			case OP_SHARE:
6271				verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,
6272				    nfs_mnt_prop,
6273				    sizeof (nfs_mnt_prop),
6274				    NULL, NULL, 0, B_FALSE) == 0);
6275				if (strcmp(nfs_mnt_prop, "off") != 0)
6276					break;
6277				verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,
6278				    nfs_mnt_prop,
6279				    sizeof (nfs_mnt_prop),
6280				    NULL, NULL, 0, B_FALSE) == 0);
6281				if (strcmp(nfs_mnt_prop, "off") == 0)
6282					continue;
6283				break;
6284			case OP_MOUNT:
6285				/* Ignore legacy mounts */
6286				verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
6287				    nfs_mnt_prop,
6288				    sizeof (nfs_mnt_prop),
6289				    NULL, NULL, 0, B_FALSE) == 0);
6290				if (strcmp(nfs_mnt_prop, "legacy") == 0)
6291					continue;
6292				/* Ignore canmount=noauto mounts */
6293				if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) ==
6294				    ZFS_CANMOUNT_NOAUTO)
6295					continue;
6296			default:
6297				break;
6298			}
6299
6300			node = safe_malloc(sizeof (unshare_unmount_node_t));
6301			node->un_zhp = zhp;
6302			node->un_mountp = safe_strdup(entry.mnt_mountp);
6303
6304			uu_avl_node_init(node, &node->un_avlnode, pool);
6305
6306			if (uu_avl_find(tree, node, NULL, &idx) == NULL) {
6307				uu_avl_insert(tree, node, idx);
6308			} else {
6309				zfs_close(node->un_zhp);
6310				free(node->un_mountp);
6311				free(node);
6312			}
6313		}
6314
6315		/*
6316		 * Walk the AVL tree in reverse, unmounting each filesystem and
6317		 * removing it from the AVL tree in the process.
6318		 */
6319		if ((walk = uu_avl_walk_start(tree,
6320		    UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL)
6321			nomem();
6322
6323		while ((node = uu_avl_walk_next(walk)) != NULL) {
6324			uu_avl_remove(tree, node);
6325
6326			switch (op) {
6327			case OP_SHARE:
6328				if (zfs_unshareall_bypath(node->un_zhp,
6329				    node->un_mountp) != 0)
6330					ret = 1;
6331				break;
6332
6333			case OP_MOUNT:
6334				if (zfs_unmount(node->un_zhp,
6335				    node->un_mountp, flags) != 0)
6336					ret = 1;
6337				break;
6338			}
6339
6340			zfs_close(node->un_zhp);
6341			free(node->un_mountp);
6342			free(node);
6343		}
6344
6345		uu_avl_walk_end(walk);
6346		uu_avl_destroy(tree);
6347		uu_avl_pool_destroy(pool);
6348
6349	} else {
6350		if (argc != 1) {
6351			if (argc == 0)
6352				(void) fprintf(stderr,
6353				    gettext("missing filesystem argument\n"));
6354			else
6355				(void) fprintf(stderr,
6356				    gettext("too many arguments\n"));
6357			usage(B_FALSE);
6358		}
6359
6360		/*
6361		 * We have an argument, but it may be a full path or a ZFS
6362		 * filesystem.  Pass full paths off to unmount_path() (shared by
6363		 * manual_unmount), otherwise open the filesystem and pass to
6364		 * zfs_unmount().
6365		 */
6366		if (argv[0][0] == '/')
6367			return (unshare_unmount_path(op, argv[0],
6368			    flags, B_FALSE));
6369
6370		if ((zhp = zfs_open(g_zfs, argv[0],
6371		    ZFS_TYPE_FILESYSTEM)) == NULL)
6372			return (1);
6373
6374		verify(zfs_prop_get(zhp, op == OP_SHARE ?
6375		    ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT,
6376		    nfs_mnt_prop, sizeof (nfs_mnt_prop), NULL,
6377		    NULL, 0, B_FALSE) == 0);
6378
6379		switch (op) {
6380		case OP_SHARE:
6381			verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,
6382			    nfs_mnt_prop,
6383			    sizeof (nfs_mnt_prop),
6384			    NULL, NULL, 0, B_FALSE) == 0);
6385			verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,
6386			    sharesmb, sizeof (sharesmb), NULL, NULL,
6387			    0, B_FALSE) == 0);
6388
6389			if (strcmp(nfs_mnt_prop, "off") == 0 &&
6390			    strcmp(sharesmb, "off") == 0) {
6391				(void) fprintf(stderr, gettext("cannot "
6392				    "unshare '%s': legacy share\n"),
6393				    zfs_get_name(zhp));
6394#ifdef illumos
6395				(void) fprintf(stderr, gettext("use "
6396				    "unshare(1M) to unshare this "
6397				    "filesystem\n"));
6398#endif
6399				ret = 1;
6400			} else if (!zfs_is_shared(zhp)) {
6401				(void) fprintf(stderr, gettext("cannot "
6402				    "unshare '%s': not currently "
6403				    "shared\n"), zfs_get_name(zhp));
6404				ret = 1;
6405			} else if (zfs_unshareall(zhp) != 0) {
6406				ret = 1;
6407			}
6408			break;
6409
6410		case OP_MOUNT:
6411			if (strcmp(nfs_mnt_prop, "legacy") == 0) {
6412				(void) fprintf(stderr, gettext("cannot "
6413				    "unmount '%s': legacy "
6414				    "mountpoint\n"), zfs_get_name(zhp));
6415				(void) fprintf(stderr, gettext("use "
6416				    "umount(8) to unmount this "
6417				    "filesystem\n"));
6418				ret = 1;
6419			} else if (!zfs_is_mounted(zhp, NULL)) {
6420				(void) fprintf(stderr, gettext("cannot "
6421				    "unmount '%s': not currently "
6422				    "mounted\n"),
6423				    zfs_get_name(zhp));
6424				ret = 1;
6425			} else if (zfs_unmountall(zhp, flags) != 0) {
6426				ret = 1;
6427			}
6428			break;
6429		}
6430
6431		zfs_close(zhp);
6432	}
6433
6434	return (ret);
6435}
6436
6437/*
6438 * zfs unmount -a
6439 * zfs unmount filesystem
6440 *
6441 * Unmount all filesystems, or a specific ZFS filesystem.
6442 */
6443static int
6444zfs_do_unmount(int argc, char **argv)
6445{
6446	return (unshare_unmount(OP_MOUNT, argc, argv));
6447}
6448
6449/*
6450 * zfs unshare -a
6451 * zfs unshare filesystem
6452 *
6453 * Unshare all filesystems, or a specific ZFS filesystem.
6454 */
6455static int
6456zfs_do_unshare(int argc, char **argv)
6457{
6458	return (unshare_unmount(OP_SHARE, argc, argv));
6459}
6460
6461/*
6462 * Attach/detach the given dataset to/from the given jail
6463 */
6464/* ARGSUSED */
6465static int
6466do_jail(int argc, char **argv, int attach)
6467{
6468	zfs_handle_t *zhp;
6469	int jailid, ret;
6470
6471	/* check number of arguments */
6472	if (argc < 3) {
6473		(void) fprintf(stderr, gettext("missing argument(s)\n"));
6474		usage(B_FALSE);
6475	}
6476	if (argc > 3) {
6477		(void) fprintf(stderr, gettext("too many arguments\n"));
6478		usage(B_FALSE);
6479	}
6480
6481	jailid = jail_getid(argv[1]);
6482	if (jailid < 0) {
6483		(void) fprintf(stderr, gettext("invalid jail id or name\n"));
6484		usage(B_FALSE);
6485	}
6486
6487	zhp = zfs_open(g_zfs, argv[2], ZFS_TYPE_FILESYSTEM);
6488	if (zhp == NULL)
6489		return (1);
6490
6491	ret = (zfs_jail(zhp, jailid, attach) != 0);
6492
6493	zfs_close(zhp);
6494	return (ret);
6495}
6496
6497/*
6498 * zfs jail jailid filesystem
6499 *
6500 * Attach the given dataset to the given jail
6501 */
6502/* ARGSUSED */
6503static int
6504zfs_do_jail(int argc, char **argv)
6505{
6506
6507	return (do_jail(argc, argv, 1));
6508}
6509
6510/*
6511 * zfs unjail jailid filesystem
6512 *
6513 * Detach the given dataset from the given jail
6514 */
6515/* ARGSUSED */
6516static int
6517zfs_do_unjail(int argc, char **argv)
6518{
6519
6520	return (do_jail(argc, argv, 0));
6521}
6522
6523/*
6524 * Called when invoked as /etc/fs/zfs/mount.  Do the mount if the mountpoint is
6525 * 'legacy'.  Otherwise, complain that use should be using 'zfs mount'.
6526 */
6527static int
6528manual_mount(int argc, char **argv)
6529{
6530	zfs_handle_t *zhp;
6531	char mountpoint[ZFS_MAXPROPLEN];
6532	char mntopts[MNT_LINE_MAX] = { '\0' };
6533	int ret = 0;
6534	int c;
6535	int flags = 0;
6536	char *dataset, *path;
6537
6538	/* check options */
6539	while ((c = getopt(argc, argv, ":mo:O")) != -1) {
6540		switch (c) {
6541		case 'o':
6542			(void) strlcpy(mntopts, optarg, sizeof (mntopts));
6543			break;
6544		case 'O':
6545			flags |= MS_OVERLAY;
6546			break;
6547		case 'm':
6548			flags |= MS_NOMNTTAB;
6549			break;
6550		case ':':
6551			(void) fprintf(stderr, gettext("missing argument for "
6552			    "'%c' option\n"), optopt);
6553			usage(B_FALSE);
6554			break;
6555		case '?':
6556			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
6557			    optopt);
6558			(void) fprintf(stderr, gettext("usage: mount [-o opts] "
6559			    "<path>\n"));
6560			return (2);
6561		}
6562	}
6563
6564	argc -= optind;
6565	argv += optind;
6566
6567	/* check that we only have two arguments */
6568	if (argc != 2) {
6569		if (argc == 0)
6570			(void) fprintf(stderr, gettext("missing dataset "
6571			    "argument\n"));
6572		else if (argc == 1)
6573			(void) fprintf(stderr,
6574			    gettext("missing mountpoint argument\n"));
6575		else
6576			(void) fprintf(stderr, gettext("too many arguments\n"));
6577		(void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n");
6578		return (2);
6579	}
6580
6581	dataset = argv[0];
6582	path = argv[1];
6583
6584	/* try to open the dataset */
6585	if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL)
6586		return (1);
6587
6588	(void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
6589	    sizeof (mountpoint), NULL, NULL, 0, B_FALSE);
6590
6591	/* check for legacy mountpoint and complain appropriately */
6592	ret = 0;
6593	if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
6594		if (zmount(dataset, path, flags, MNTTYPE_ZFS,
6595		    NULL, 0, mntopts, sizeof (mntopts)) != 0) {
6596			(void) fprintf(stderr, gettext("mount failed: %s\n"),
6597			    strerror(errno));
6598			ret = 1;
6599		}
6600	} else {
6601		(void) fprintf(stderr, gettext("filesystem '%s' cannot be "
6602		    "mounted using 'mount -t zfs'\n"), dataset);
6603		(void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' "
6604		    "instead.\n"), path);
6605		(void) fprintf(stderr, gettext("If you must use 'mount -t zfs' "
6606		    "or /etc/fstab, use 'zfs set mountpoint=legacy'.\n"));
6607		(void) fprintf(stderr, gettext("See zfs(8) for more "
6608		    "information.\n"));
6609		ret = 1;
6610	}
6611
6612	return (ret);
6613}
6614
6615/*
6616 * Called when invoked as /etc/fs/zfs/umount.  Unlike a manual mount, we allow
6617 * unmounts of non-legacy filesystems, as this is the dominant administrative
6618 * interface.
6619 */
6620static int
6621manual_unmount(int argc, char **argv)
6622{
6623	int flags = 0;
6624	int c;
6625
6626	/* check options */
6627	while ((c = getopt(argc, argv, "f")) != -1) {
6628		switch (c) {
6629		case 'f':
6630			flags = MS_FORCE;
6631			break;
6632		case '?':
6633			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
6634			    optopt);
6635			(void) fprintf(stderr, gettext("usage: unmount [-f] "
6636			    "<path>\n"));
6637			return (2);
6638		}
6639	}
6640
6641	argc -= optind;
6642	argv += optind;
6643
6644	/* check arguments */
6645	if (argc != 1) {
6646		if (argc == 0)
6647			(void) fprintf(stderr, gettext("missing path "
6648			    "argument\n"));
6649		else
6650			(void) fprintf(stderr, gettext("too many arguments\n"));
6651		(void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n"));
6652		return (2);
6653	}
6654
6655	return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE));
6656}
6657
6658static int
6659find_command_idx(char *command, int *idx)
6660{
6661	int i;
6662
6663	for (i = 0; i < NCOMMAND; i++) {
6664		if (command_table[i].name == NULL)
6665			continue;
6666
6667		if (strcmp(command, command_table[i].name) == 0) {
6668			*idx = i;
6669			return (0);
6670		}
6671	}
6672	return (1);
6673}
6674
6675static int
6676zfs_do_diff(int argc, char **argv)
6677{
6678	zfs_handle_t *zhp;
6679	int flags = 0;
6680	char *tosnap = NULL;
6681	char *fromsnap = NULL;
6682	char *atp, *copy;
6683	int err = 0;
6684	int c;
6685
6686	while ((c = getopt(argc, argv, "FHt")) != -1) {
6687		switch (c) {
6688		case 'F':
6689			flags |= ZFS_DIFF_CLASSIFY;
6690			break;
6691		case 'H':
6692			flags |= ZFS_DIFF_PARSEABLE;
6693			break;
6694		case 't':
6695			flags |= ZFS_DIFF_TIMESTAMP;
6696			break;
6697		default:
6698			(void) fprintf(stderr,
6699			    gettext("invalid option '%c'\n"), optopt);
6700			usage(B_FALSE);
6701		}
6702	}
6703
6704	argc -= optind;
6705	argv += optind;
6706
6707	if (argc < 1) {
6708		(void) fprintf(stderr,
6709		gettext("must provide at least one snapshot name\n"));
6710		usage(B_FALSE);
6711	}
6712
6713	if (argc > 2) {
6714		(void) fprintf(stderr, gettext("too many arguments\n"));
6715		usage(B_FALSE);
6716	}
6717
6718	fromsnap = argv[0];
6719	tosnap = (argc == 2) ? argv[1] : NULL;
6720
6721	copy = NULL;
6722	if (*fromsnap != '@')
6723		copy = strdup(fromsnap);
6724	else if (tosnap)
6725		copy = strdup(tosnap);
6726	if (copy == NULL)
6727		usage(B_FALSE);
6728
6729	if (atp = strchr(copy, '@'))
6730		*atp = '\0';
6731
6732	if ((zhp = zfs_open(g_zfs, copy, ZFS_TYPE_FILESYSTEM)) == NULL)
6733		return (1);
6734
6735	free(copy);
6736
6737	/*
6738	 * Ignore SIGPIPE so that the library can give us
6739	 * information on any failure
6740	 */
6741	(void) sigignore(SIGPIPE);
6742
6743	err = zfs_show_diffs(zhp, STDOUT_FILENO, fromsnap, tosnap, flags);
6744
6745	zfs_close(zhp);
6746
6747	return (err != 0);
6748}
6749
6750/*
6751 * zfs bookmark <fs@snap> <fs#bmark>
6752 *
6753 * Creates a bookmark with the given name from the given snapshot.
6754 */
6755static int
6756zfs_do_bookmark(int argc, char **argv)
6757{
6758	char snapname[ZFS_MAXNAMELEN];
6759	zfs_handle_t *zhp;
6760	nvlist_t *nvl;
6761	int ret = 0;
6762	int c;
6763
6764	/* check options */
6765	while ((c = getopt(argc, argv, "")) != -1) {
6766		switch (c) {
6767		case '?':
6768			(void) fprintf(stderr,
6769			    gettext("invalid option '%c'\n"), optopt);
6770			goto usage;
6771		}
6772	}
6773
6774	argc -= optind;
6775	argv += optind;
6776
6777	/* check number of arguments */
6778	if (argc < 1) {
6779		(void) fprintf(stderr, gettext("missing snapshot argument\n"));
6780		goto usage;
6781	}
6782	if (argc < 2) {
6783		(void) fprintf(stderr, gettext("missing bookmark argument\n"));
6784		goto usage;
6785	}
6786
6787	if (strchr(argv[1], '#') == NULL) {
6788		(void) fprintf(stderr,
6789		    gettext("invalid bookmark name '%s' -- "
6790		    "must contain a '#'\n"), argv[1]);
6791		goto usage;
6792	}
6793
6794	if (argv[0][0] == '@') {
6795		/*
6796		 * Snapshot name begins with @.
6797		 * Default to same fs as bookmark.
6798		 */
6799		(void) strncpy(snapname, argv[1], sizeof (snapname));
6800		*strchr(snapname, '#') = '\0';
6801		(void) strlcat(snapname, argv[0], sizeof (snapname));
6802	} else {
6803		(void) strncpy(snapname, argv[0], sizeof (snapname));
6804	}
6805	zhp = zfs_open(g_zfs, snapname, ZFS_TYPE_SNAPSHOT);
6806	if (zhp == NULL)
6807		goto usage;
6808	zfs_close(zhp);
6809
6810
6811	nvl = fnvlist_alloc();
6812	fnvlist_add_string(nvl, argv[1], snapname);
6813	ret = lzc_bookmark(nvl, NULL);
6814	fnvlist_free(nvl);
6815
6816	if (ret != 0) {
6817		const char *err_msg;
6818		char errbuf[1024];
6819
6820		(void) snprintf(errbuf, sizeof (errbuf),
6821		    dgettext(TEXT_DOMAIN,
6822		    "cannot create bookmark '%s'"), argv[1]);
6823
6824		switch (ret) {
6825		case EXDEV:
6826			err_msg = "bookmark is in a different pool";
6827			break;
6828		case EEXIST:
6829			err_msg = "bookmark exists";
6830			break;
6831		case EINVAL:
6832			err_msg = "invalid argument";
6833			break;
6834		case ENOTSUP:
6835			err_msg = "bookmark feature not enabled";
6836			break;
6837		default:
6838			err_msg = "unknown error";
6839			break;
6840		}
6841		(void) fprintf(stderr, "%s: %s\n", errbuf,
6842		    dgettext(TEXT_DOMAIN, err_msg));
6843	}
6844
6845	return (ret);
6846
6847usage:
6848	usage(B_FALSE);
6849	return (-1);
6850}
6851
6852int
6853main(int argc, char **argv)
6854{
6855	int ret = 0;
6856	int i;
6857	char *progname;
6858	char *cmdname;
6859
6860	(void) setlocale(LC_ALL, "");
6861	(void) textdomain(TEXT_DOMAIN);
6862
6863	opterr = 0;
6864
6865	if ((g_zfs = libzfs_init()) == NULL) {
6866		(void) fprintf(stderr, gettext("internal error: failed to "
6867		    "initialize ZFS library\n"));
6868		return (1);
6869	}
6870
6871	zfs_save_arguments(argc, argv, history_str, sizeof (history_str));
6872
6873	libzfs_print_on_error(g_zfs, B_TRUE);
6874
6875	if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) {
6876		(void) fprintf(stderr, gettext("internal error: unable to "
6877		    "open %s\n"), MNTTAB);
6878		return (1);
6879	}
6880
6881	/*
6882	 * This command also doubles as the /etc/fs mount and unmount program.
6883	 * Determine if we should take this behavior based on argv[0].
6884	 */
6885	progname = basename(argv[0]);
6886	if (strcmp(progname, "mount") == 0) {
6887		ret = manual_mount(argc, argv);
6888	} else if (strcmp(progname, "umount") == 0) {
6889		ret = manual_unmount(argc, argv);
6890	} else {
6891		/*
6892		 * Make sure the user has specified some command.
6893		 */
6894		if (argc < 2) {
6895			(void) fprintf(stderr, gettext("missing command\n"));
6896			usage(B_FALSE);
6897		}
6898
6899		cmdname = argv[1];
6900
6901		/*
6902		 * The 'umount' command is an alias for 'unmount'
6903		 */
6904		if (strcmp(cmdname, "umount") == 0)
6905			cmdname = "unmount";
6906
6907		/*
6908		 * The 'recv' command is an alias for 'receive'
6909		 */
6910		if (strcmp(cmdname, "recv") == 0)
6911			cmdname = "receive";
6912
6913		/*
6914		 * The 'snap' command is an alias for 'snapshot'
6915		 */
6916		if (strcmp(cmdname, "snap") == 0)
6917			cmdname = "snapshot";
6918
6919		/*
6920		 * Special case '-?'
6921		 */
6922		if (strcmp(cmdname, "-?") == 0)
6923			usage(B_TRUE);
6924
6925		/*
6926		 * Run the appropriate command.
6927		 */
6928		libzfs_mnttab_cache(g_zfs, B_TRUE);
6929		if (find_command_idx(cmdname, &i) == 0) {
6930			current_command = &command_table[i];
6931			ret = command_table[i].func(argc - 1, argv + 1);
6932		} else if (strchr(cmdname, '=') != NULL) {
6933			verify(find_command_idx("set", &i) == 0);
6934			current_command = &command_table[i];
6935			ret = command_table[i].func(argc, argv);
6936		} else {
6937			(void) fprintf(stderr, gettext("unrecognized "
6938			    "command '%s'\n"), cmdname);
6939			usage(B_FALSE);
6940		}
6941		libzfs_mnttab_cache(g_zfs, B_FALSE);
6942	}
6943
6944	(void) fclose(mnttab_file);
6945
6946	if (ret == 0 && log_history)
6947		(void) zpool_log_history(g_zfs, history_str);
6948
6949	libzfs_fini(g_zfs);
6950
6951	/*
6952	 * The 'ZFS_ABORT' environment variable causes us to dump core on exit
6953	 * for the purposes of running ::findleaks.
6954	 */
6955	if (getenv("ZFS_ABORT") != NULL) {
6956		(void) printf("dumping core by request\n");
6957		abort();
6958	}
6959
6960	return (ret);
6961}
6962