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