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