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