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