xref: /illumos-gate/usr/src/cmd/zfs/zfs_main.c (revision 606d76de63f7f3335e75e7f3eb3fba75c8998690)
1fa9e4066Sahrens /*
2fa9e4066Sahrens  * CDDL HEADER START
3fa9e4066Sahrens  *
4fa9e4066Sahrens  * The contents of this file are subject to the terms of the
5ea8dc4b6Seschrock  * Common Development and Distribution License (the "License").
6ea8dc4b6Seschrock  * You may not use this file except in compliance with the License.
7fa9e4066Sahrens  *
8fa9e4066Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9fa9e4066Sahrens  * or http://www.opensolaris.org/os/licensing.
10fa9e4066Sahrens  * See the License for the specific language governing permissions
11fa9e4066Sahrens  * and limitations under the License.
12fa9e4066Sahrens  *
13fa9e4066Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14fa9e4066Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15fa9e4066Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16fa9e4066Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17fa9e4066Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18fa9e4066Sahrens  *
19fa9e4066Sahrens  * CDDL HEADER END
20fa9e4066Sahrens  */
21f3861e1aSahl 
22fa9e4066Sahrens /*
233f9d6ad7SLin Ling  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24dfc11533SChris Williamson  * Copyright (c) 2011, 2016 by Delphix. All rights reserved.
2533f5ff17SMilan Jurik  * Copyright 2012 Milan Jurik. All rights reserved.
26b73ccab0SMike Gerdts  * Copyright 2019 Joyent, Inc.
270d8fa8f8SMartin Matuska  * Copyright (c) 2011-2012 Pawel Jakub Dawidek. All rights reserved.
28a7a845e4SSteven Hartland  * Copyright (c) 2013 Steven Hartland.  All rights reserved.
29c3d26abcSMatthew Ahrens  * Copyright (c) 2014 Integros [integros.com]
30c16bcc45SIgor Kozhukhov  * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>.
318808ac5dSYuri Pankov  * Copyright 2016 Nexenta Systems, Inc.
3252675910SAlek Pinchuk  * Copyright (c) 2018 Datto Inc.
331e9e241fSAndrew Stormont  * Copyright 2021 RackTop Systems, Inc.
34fa9e4066Sahrens  */
35fa9e4066Sahrens 
36fa9e4066Sahrens #include <assert.h>
37e9dbad6fSeschrock #include <ctype.h>
38fa9e4066Sahrens #include <errno.h>
395602294fSDan Kimmel #include <getopt.h>
40fa9e4066Sahrens #include <libgen.h>
41fa9e4066Sahrens #include <libintl.h>
42fa9e4066Sahrens #include <libuutil.h>
43ecd6cf80Smarks #include <libnvpair.h>
44fa9e4066Sahrens #include <locale.h>
45fa9e4066Sahrens #include <stddef.h>
46fa9e4066Sahrens #include <stdio.h>
47fa9e4066Sahrens #include <stdlib.h>
48fa9e4066Sahrens #include <strings.h>
49fa9e4066Sahrens #include <unistd.h>
50fa9e4066Sahrens #include <fcntl.h>
51fa9e4066Sahrens #include <zone.h>
5214843421SMatthew Ahrens #include <grp.h>
5314843421SMatthew Ahrens #include <pwd.h>
5499d5e173STim Haley #include <signal.h>
55dfc11533SChris Williamson #include <sys/debug.h>
561af68beaSAlexander Stetsenko #include <sys/list.h>
573cf6ec9cSalex #include <sys/sysmacros.h>
58fa9e4066Sahrens #include <sys/mkdev.h>
59fa9e4066Sahrens #include <sys/mntent.h>
60fa9e4066Sahrens #include <sys/mnttab.h>
61fa9e4066Sahrens #include <sys/mount.h>
62fa9e4066Sahrens #include <sys/stat.h>
6314843421SMatthew Ahrens #include <sys/fs/zfs.h>
64a8b6ddafSMark J Musante #include <sys/types.h>
65a8b6ddafSMark J Musante #include <time.h>
66f67950b2SNasf-Fan #include <sys/zfs_project.h>
67591e0e13SSebastien Roy #include <synch.h>
68fa9e4066Sahrens 
69fa9e4066Sahrens #include <libzfs.h>
704445fffbSMatthew Ahrens #include <libzfs_core.h>
711af68beaSAlexander Stetsenko #include <zfs_prop.h>
721af68beaSAlexander Stetsenko #include <zfs_deleg.h>
73d8ab6e12SDon Brady #include <libzutil.h>
74ecd6cf80Smarks #include <libuutil.h>
751af68beaSAlexander Stetsenko #include <aclutils.h>
761af68beaSAlexander Stetsenko #include <directory.h>
771ed6b69aSGordon Ross #include <idmap.h>
788a981c33SDaniel Hoffman #include <libshare.h>
79fa9e4066Sahrens 
80fa9e4066Sahrens #include "zfs_iter.h"
8199653d4eSeschrock #include "zfs_util.h"
820a586ceaSMark Shellenbaum #include "zfs_comutil.h"
83f67950b2SNasf-Fan #include "zfs_projectutil.h"
8499653d4eSeschrock 
8599653d4eSeschrock libzfs_handle_t *g_zfs;
86fa9e4066Sahrens 
87fa9e4066Sahrens static FILE *mnttab_file;
882a6b87f0Sek110237 static char history_str[HIS_MAX_RECORD_LEN];
894445fffbSMatthew Ahrens static boolean_t log_history = B_TRUE;
90fa9e4066Sahrens 
91fa9e4066Sahrens static int zfs_do_clone(int argc, char **argv);
92fa9e4066Sahrens static int zfs_do_create(int argc, char **argv);
93fa9e4066Sahrens static int zfs_do_destroy(int argc, char **argv);
94fa9e4066Sahrens static int zfs_do_get(int argc, char **argv);
95fa9e4066Sahrens static int zfs_do_inherit(int argc, char **argv);
96fa9e4066Sahrens static int zfs_do_list(int argc, char **argv);
97fa9e4066Sahrens static int zfs_do_mount(int argc, char **argv);
98fa9e4066Sahrens static int zfs_do_rename(int argc, char **argv);
99fa9e4066Sahrens static int zfs_do_rollback(int argc, char **argv);
100fa9e4066Sahrens static int zfs_do_set(int argc, char **argv);
101e7437265Sahrens static int zfs_do_upgrade(int argc, char **argv);
102fa9e4066Sahrens static int zfs_do_snapshot(int argc, char **argv);
103fa9e4066Sahrens static int zfs_do_unmount(int argc, char **argv);
104fa9e4066Sahrens static int zfs_do_share(int argc, char **argv);
105fa9e4066Sahrens static int zfs_do_unshare(int argc, char **argv);
106f2a3c691Sahrens static int zfs_do_send(int argc, char **argv);
107f2a3c691Sahrens static int zfs_do_receive(int argc, char **argv);
10899653d4eSeschrock static int zfs_do_promote(int argc, char **argv);
10914843421SMatthew Ahrens static int zfs_do_userspace(int argc, char **argv);
1101af68beaSAlexander Stetsenko static int zfs_do_allow(int argc, char **argv);
1111af68beaSAlexander Stetsenko static int zfs_do_unallow(int argc, char **argv);
112842727c2SChris Kirby static int zfs_do_hold(int argc, char **argv);
1131af68beaSAlexander Stetsenko static int zfs_do_holds(int argc, char **argv);
114842727c2SChris Kirby static int zfs_do_release(int argc, char **argv);
11599d5e173STim Haley static int zfs_do_diff(int argc, char **argv);
11678f17100SMatthew Ahrens static int zfs_do_bookmark(int argc, char **argv);
1175cabbc6bSPrashanth Sreenivasa static int zfs_do_remap(int argc, char **argv);
118dfc11533SChris Williamson static int zfs_do_channel_program(int argc, char **argv);
119eb633035STom Caputi static int zfs_do_load_key(int argc, char **argv);
120eb633035STom Caputi static int zfs_do_unload_key(int argc, char **argv);
121eb633035STom Caputi static int zfs_do_change_key(int argc, char **argv);
122f67950b2SNasf-Fan static int zfs_do_project(int argc, char **argv);
123fa9e4066Sahrens 
124fa9e4066Sahrens /*
12529ab75c9Srm160521  * Enable a reasonable set of defaults for libumem debugging on DEBUG builds.
126fa9e4066Sahrens  */
12729ab75c9Srm160521 
12829ab75c9Srm160521 #ifdef DEBUG
129fa9e4066Sahrens const char *
_umem_debug_init(void)130f3861e1aSahl _umem_debug_init(void)
131fa9e4066Sahrens {
132fa9e4066Sahrens 	return ("default,verbose"); /* $UMEM_DEBUG setting */
133fa9e4066Sahrens }
134fa9e4066Sahrens 
135fa9e4066Sahrens const char *
_umem_logging_init(void)136fa9e4066Sahrens _umem_logging_init(void)
137fa9e4066Sahrens {
138fa9e4066Sahrens 	return ("fail,contents"); /* $UMEM_LOGGING setting */
139fa9e4066Sahrens }
14029ab75c9Srm160521 #endif
141fa9e4066Sahrens 
14265cd9f28Seschrock typedef enum {
14365cd9f28Seschrock 	HELP_CLONE,
14465cd9f28Seschrock 	HELP_CREATE,
14565cd9f28Seschrock 	HELP_DESTROY,
14665cd9f28Seschrock 	HELP_GET,
14765cd9f28Seschrock 	HELP_INHERIT,
148e7437265Sahrens 	HELP_UPGRADE,
14965cd9f28Seschrock 	HELP_LIST,
15065cd9f28Seschrock 	HELP_MOUNT,
15199653d4eSeschrock 	HELP_PROMOTE,
152f2a3c691Sahrens 	HELP_RECEIVE,
15365cd9f28Seschrock 	HELP_RENAME,
15465cd9f28Seschrock 	HELP_ROLLBACK,
155f2a3c691Sahrens 	HELP_SEND,
15665cd9f28Seschrock 	HELP_SET,
15765cd9f28Seschrock 	HELP_SHARE,
15865cd9f28Seschrock 	HELP_SNAPSHOT,
15965cd9f28Seschrock 	HELP_UNMOUNT,
160ecd6cf80Smarks 	HELP_UNSHARE,
161ecd6cf80Smarks 	HELP_ALLOW,
16214843421SMatthew Ahrens 	HELP_UNALLOW,
16314843421SMatthew Ahrens 	HELP_USERSPACE,
164842727c2SChris Kirby 	HELP_GROUPSPACE,
165f67950b2SNasf-Fan 	HELP_PROJECTSPACE,
166f67950b2SNasf-Fan 	HELP_PROJECT,
167842727c2SChris Kirby 	HELP_HOLD,
168842727c2SChris Kirby 	HELP_HOLDS,
16999d5e173STim Haley 	HELP_RELEASE,
17019b94df9SMatthew Ahrens 	HELP_DIFF,
1715cabbc6bSPrashanth Sreenivasa 	HELP_REMAP,
17278f17100SMatthew Ahrens 	HELP_BOOKMARK,
173dfc11533SChris Williamson 	HELP_CHANNEL_PROGRAM,
174eb633035STom Caputi 	HELP_LOAD_KEY,
175eb633035STom Caputi 	HELP_UNLOAD_KEY,
176eb633035STom Caputi 	HELP_CHANGE_KEY,
17765cd9f28Seschrock } zfs_help_t;
17865cd9f28Seschrock 
179fa9e4066Sahrens typedef struct zfs_command {
180fa9e4066Sahrens 	const char	*name;
181fa9e4066Sahrens 	int		(*func)(int argc, char **argv);
18265cd9f28Seschrock 	zfs_help_t	usage;
183fa9e4066Sahrens } zfs_command_t;
184fa9e4066Sahrens 
185fa9e4066Sahrens /*
186fa9e4066Sahrens  * Master command table.  Each ZFS command has a name, associated function, and
187ea8dc4b6Seschrock  * usage message.  The usage messages need to be internationalized, so we have
188ea8dc4b6Seschrock  * to have a function to return the usage message based on a command index.
18965cd9f28Seschrock  *
19065cd9f28Seschrock  * These commands are organized according to how they are displayed in the usage
19165cd9f28Seschrock  * message.  An empty command (one with a NULL name) indicates an empty line in
19265cd9f28Seschrock  * the generic usage message.
193fa9e4066Sahrens  */
194fa9e4066Sahrens static zfs_command_t command_table[] = {
19565cd9f28Seschrock 	{ "create",	zfs_do_create,		HELP_CREATE		},
19665cd9f28Seschrock 	{ "destroy",	zfs_do_destroy,		HELP_DESTROY		},
197fa9e4066Sahrens 	{ NULL },
19865cd9f28Seschrock 	{ "snapshot",	zfs_do_snapshot,	HELP_SNAPSHOT		},
19965cd9f28Seschrock 	{ "rollback",	zfs_do_rollback,	HELP_ROLLBACK		},
20065cd9f28Seschrock 	{ "clone",	zfs_do_clone,		HELP_CLONE		},
20199653d4eSeschrock 	{ "promote",	zfs_do_promote,		HELP_PROMOTE		},
20265cd9f28Seschrock 	{ "rename",	zfs_do_rename,		HELP_RENAME		},
20378f17100SMatthew Ahrens 	{ "bookmark",	zfs_do_bookmark,	HELP_BOOKMARK		},
204dfc11533SChris Williamson 	{ "program",    zfs_do_channel_program, HELP_CHANNEL_PROGRAM    },
205fa9e4066Sahrens 	{ NULL },
20665cd9f28Seschrock 	{ "list",	zfs_do_list,		HELP_LIST		},
207fa9e4066Sahrens 	{ NULL },
20865cd9f28Seschrock 	{ "set",	zfs_do_set,		HELP_SET		},
20965cd9f28Seschrock 	{ "get",	zfs_do_get,		HELP_GET		},
21065cd9f28Seschrock 	{ "inherit",	zfs_do_inherit,		HELP_INHERIT		},
211e7437265Sahrens 	{ "upgrade",	zfs_do_upgrade,		HELP_UPGRADE		},
212f67950b2SNasf-Fan 	{ NULL },
21314843421SMatthew Ahrens 	{ "userspace",	zfs_do_userspace,	HELP_USERSPACE		},
21414843421SMatthew Ahrens 	{ "groupspace",	zfs_do_userspace,	HELP_GROUPSPACE		},
215f67950b2SNasf-Fan 	{ "projectspace", zfs_do_userspace,	HELP_PROJECTSPACE	},
216f67950b2SNasf-Fan 	{ NULL },
217f67950b2SNasf-Fan 	{ "project",	zfs_do_project,		HELP_PROJECT		},
218fa9e4066Sahrens 	{ NULL },
21965cd9f28Seschrock 	{ "mount",	zfs_do_mount,		HELP_MOUNT		},
22065cd9f28Seschrock 	{ "unmount",	zfs_do_unmount,		HELP_UNMOUNT		},
22165cd9f28Seschrock 	{ "share",	zfs_do_share,		HELP_SHARE		},
22265cd9f28Seschrock 	{ "unshare",	zfs_do_unshare,		HELP_UNSHARE		},
223fa9e4066Sahrens 	{ NULL },
224f2a3c691Sahrens 	{ "send",	zfs_do_send,		HELP_SEND		},
225f2a3c691Sahrens 	{ "receive",	zfs_do_receive,		HELP_RECEIVE		},
226ecd6cf80Smarks 	{ NULL },
2271af68beaSAlexander Stetsenko 	{ "allow",	zfs_do_allow,		HELP_ALLOW		},
228ecd6cf80Smarks 	{ NULL },
2291af68beaSAlexander Stetsenko 	{ "unallow",	zfs_do_unallow,		HELP_UNALLOW		},
230842727c2SChris Kirby 	{ NULL },
231842727c2SChris Kirby 	{ "hold",	zfs_do_hold,		HELP_HOLD		},
2321af68beaSAlexander Stetsenko 	{ "holds",	zfs_do_holds,		HELP_HOLDS		},
233842727c2SChris Kirby 	{ "release",	zfs_do_release,		HELP_RELEASE		},
23499d5e173STim Haley 	{ "diff",	zfs_do_diff,		HELP_DIFF		},
2355cabbc6bSPrashanth Sreenivasa 	{ "remap",	zfs_do_remap,		HELP_REMAP		},
236eb633035STom Caputi 	{ "load-key",	zfs_do_load_key,	HELP_LOAD_KEY		},
237eb633035STom Caputi 	{ "unload-key",	zfs_do_unload_key,	HELP_UNLOAD_KEY		},
238eb633035STom Caputi 	{ "change-key",	zfs_do_change_key,	HELP_CHANGE_KEY		},
239fa9e4066Sahrens };
240fa9e4066Sahrens 
241fa9e4066Sahrens #define	NCOMMAND	(sizeof (command_table) / sizeof (command_table[0]))
242fa9e4066Sahrens 
243fa9e4066Sahrens zfs_command_t *current_command;
244fa9e4066Sahrens 
24565cd9f28Seschrock static const char *
get_usage(zfs_help_t idx)24665cd9f28Seschrock get_usage(zfs_help_t idx)
24765cd9f28Seschrock {
24865cd9f28Seschrock 	switch (idx) {
24965cd9f28Seschrock 	case HELP_CLONE:
250bb0ade09Sahrens 		return (gettext("\tclone [-p] [-o property=value] ... "
251bb0ade09Sahrens 		    "<snapshot> <filesystem|volume>\n"));
25265cd9f28Seschrock 	case HELP_CREATE:
2531320ddf5SMike Gerdts 		return (gettext("\tcreate [-Pnpv] [-o property=value] ... "
254e9dbad6fSeschrock 		    "<filesystem>\n"
2551320ddf5SMike Gerdts 		    "\tcreate [-Pnpsv] [-b blocksize] [-o property=value] ... "
256e45ce728Sahrens 		    "-V <size> <volume>\n"));
25765cd9f28Seschrock 	case HELP_DESTROY:
25819b94df9SMatthew Ahrens 		return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n"
25919b94df9SMatthew Ahrens 		    "\tdestroy [-dnpRrv] "
26078f17100SMatthew Ahrens 		    "<filesystem|volume>@<snap>[%<snap>][,...]\n"
26178f17100SMatthew Ahrens 		    "\tdestroy <filesystem|volume>#<bookmark>\n"));
26265cd9f28Seschrock 	case HELP_GET:
263ae1726b6SChris Gerhard 		return (gettext("\tget [-rHp] [-d max] "
26478f17100SMatthew Ahrens 		    "[-o \"all\" | field[,...]]\n"
26578f17100SMatthew Ahrens 		    "\t    [-t type[,...]] [-s source[,...]]\n"
266e45ce728Sahrens 		    "\t    <\"all\" | property[,...]> "
267edb901aaSMarcel Telka 		    "[filesystem|volume|snapshot|bookmark] ...\n"));
26865cd9f28Seschrock 	case HELP_INHERIT:
26992241e0bSTom Erickson 		return (gettext("\tinherit [-rS] <property> "
270bb0ade09Sahrens 		    "<filesystem|volume|snapshot> ...\n"));
271e7437265Sahrens 	case HELP_UPGRADE:
272e7437265Sahrens 		return (gettext("\tupgrade [-v]\n"
273e7437265Sahrens 		    "\tupgrade [-r] [-V version] <-a | filesystem ...>\n"));
27465cd9f28Seschrock 	case HELP_LIST:
27543d68d68SYuri Pankov 		return (gettext("\tlist [-Hp] [-r|-d max] [-o property[,...]] "
27643d68d68SYuri Pankov 		    "[-s property]...\n\t    [-S property]... [-t type[,...]] "
277e45ce728Sahrens 		    "[filesystem|volume|snapshot] ...\n"));
27865cd9f28Seschrock 	case HELP_MOUNT:
27965cd9f28Seschrock 		return (gettext("\tmount\n"
280eb633035STom Caputi 		    "\tmount [-lvO] [-o opts] <-a | filesystem>\n"));
28199653d4eSeschrock 	case HELP_PROMOTE:
282e45ce728Sahrens 		return (gettext("\tpromote <clone-filesystem>\n"));
283f2a3c691Sahrens 	case HELP_RECEIVE:
2846ccda740Sloli10K 		return (gettext("\treceive [-vnsFhu] "
2856ccda740Sloli10K 		    "[-o <property>=<value>] ... [-x <property>] ...\n"
2866ccda740Sloli10K 		    "\t    <filesystem|volume|snapshot>\n"
2876ccda740Sloli10K 		    "\treceive [-vnsFhu] [-o <property>=<value>] ... "
2886ccda740Sloli10K 		    "[-x <property>] ... \n"
2896ccda740Sloli10K 		    "\t    [-d | -e] <filesystem>\n"
2909c3fd121SMatthew Ahrens 		    "\treceive -A <filesystem|volume>\n"));
29165cd9f28Seschrock 	case HELP_RENAME:
2926a9cb0eaSEric Schrock 		return (gettext("\trename [-f] <filesystem|volume|snapshot> "
293cdf5b4caSmmusante 		    "<filesystem|volume|snapshot>\n"
2946a9cb0eaSEric Schrock 		    "\trename [-f] -p <filesystem|volume> <filesystem|volume>\n"
29578f17100SMatthew Ahrens 		    "\trename -r <snapshot> <snapshot>\n"));
29665cd9f28Seschrock 	case HELP_ROLLBACK:
297f17c7ff8Sahrens 		return (gettext("\trollback [-rRf] <snapshot>\n"));
298f2a3c691Sahrens 	case HELP_SEND:
2996ccda740Sloli10K 		return (gettext("\tsend [-DnPpRvLecwhb] [-[iI] snapshot] "
30078f17100SMatthew Ahrens 		    "<snapshot>\n"
3016ccda740Sloli10K 		    "\tsend [-nvPLecw] [-i snapshot|bookmark] "
3029c3fd121SMatthew Ahrens 		    "<filesystem|volume|snapshot>\n"
3039c3fd121SMatthew Ahrens 		    "\tsend [-nvPe] -t <receive_resume_token>\n"));
30465cd9f28Seschrock 	case HELP_SET:
30530925561SChris Williamson 		return (gettext("\tset <property=value> ... "
306bb0ade09Sahrens 		    "<filesystem|volume|snapshot> ...\n"));
30765cd9f28Seschrock 	case HELP_SHARE:
308eb633035STom Caputi 		return (gettext("\tshare [-l] <-a | filesystem>\n"));
30965cd9f28Seschrock 	case HELP_SNAPSHOT:
310bb0ade09Sahrens 		return (gettext("\tsnapshot [-r] [-o property=value] ... "
31178f17100SMatthew Ahrens 		    "<filesystem|volume>@<snap> ...\n"));
31265cd9f28Seschrock 	case HELP_UNMOUNT:
313e45ce728Sahrens 		return (gettext("\tunmount [-f] "
314e45ce728Sahrens 		    "<-a | filesystem|mountpoint>\n"));
31565cd9f28Seschrock 	case HELP_UNSHARE:
3162269545aSstephanie scheffler 		return (gettext("\tunshare "
317e45ce728Sahrens 		    "<-a | filesystem|mountpoint>\n"));
318ecd6cf80Smarks 	case HELP_ALLOW:
31960b94cc3Sstephanie scheffler 		return (gettext("\tallow <filesystem|volume>\n"
32060b94cc3Sstephanie scheffler 		    "\tallow [-ldug] "
321e45ce728Sahrens 		    "<\"everyone\"|user|group>[,...] <perm|@setname>[,...]\n"
322e45ce728Sahrens 		    "\t    <filesystem|volume>\n"
323e45ce728Sahrens 		    "\tallow [-ld] -e <perm|@setname>[,...] "
324ecd6cf80Smarks 		    "<filesystem|volume>\n"
325e45ce728Sahrens 		    "\tallow -c <perm|@setname>[,...] <filesystem|volume>\n"
326e45ce728Sahrens 		    "\tallow -s @setname <perm|@setname>[,...] "
327ecd6cf80Smarks 		    "<filesystem|volume>\n"));
328ecd6cf80Smarks 	case HELP_UNALLOW:
329e45ce728Sahrens 		return (gettext("\tunallow [-rldug] "
330e45ce728Sahrens 		    "<\"everyone\"|user|group>[,...]\n"
331e45ce728Sahrens 		    "\t    [<perm|@setname>[,...]] <filesystem|volume>\n"
332e45ce728Sahrens 		    "\tunallow [-rld] -e [<perm|@setname>[,...]] "
333ecd6cf80Smarks 		    "<filesystem|volume>\n"
334e45ce728Sahrens 		    "\tunallow [-r] -c [<perm|@setname>[,...]] "
335ecd6cf80Smarks 		    "<filesystem|volume>\n"
336e45ce728Sahrens 		    "\tunallow [-r] -s @setname [<perm|@setname>[,...]] "
337e45ce728Sahrens 		    "<filesystem|volume>\n"));
33814843421SMatthew Ahrens 	case HELP_USERSPACE:
33989f5d17bSYuri Pankov 		return (gettext("\tuserspace [-Hinp] [-o field[,...]] "
34078f17100SMatthew Ahrens 		    "[-s field] ...\n"
34178f17100SMatthew Ahrens 		    "\t    [-S field] ... [-t type[,...]] "
34243d68d68SYuri Pankov 		    "<filesystem|snapshot>\n"));
34314843421SMatthew Ahrens 	case HELP_GROUPSPACE:
34489f5d17bSYuri Pankov 		return (gettext("\tgroupspace [-Hinp] [-o field[,...]] "
34578f17100SMatthew Ahrens 		    "[-s field] ...\n"
34678f17100SMatthew Ahrens 		    "\t    [-S field] ... [-t type[,...]] "
34743d68d68SYuri Pankov 		    "<filesystem|snapshot>\n"));
348f67950b2SNasf-Fan 	case HELP_PROJECTSPACE:
349f67950b2SNasf-Fan 		return (gettext("\tprojectspace [-Hp] [-o field[,...]] "
350f67950b2SNasf-Fan 		    "[-s field] ... \n"
351f67950b2SNasf-Fan 		    "\t    [-S field] ... <filesystem|snapshot>\n"));
352f67950b2SNasf-Fan 	case HELP_PROJECT:
353f67950b2SNasf-Fan 		return (gettext("\tproject [-d|-r] <directory|file ...>\n"
354f67950b2SNasf-Fan 		    "\tproject -c [-0] [-d|-r] [-p id] <directory|file ...>\n"
355f67950b2SNasf-Fan 		    "\tproject -C [-k] [-r] <directory ...>\n"
356f67950b2SNasf-Fan 		    "\tproject [-p id] [-r] [-s] <directory ...>\n"));
357842727c2SChris Kirby 	case HELP_HOLD:
358842727c2SChris Kirby 		return (gettext("\thold [-r] <tag> <snapshot> ...\n"));
359842727c2SChris Kirby 	case HELP_HOLDS:
360842727c2SChris Kirby 		return (gettext("\tholds [-r] <snapshot> ...\n"));
361842727c2SChris Kirby 	case HELP_RELEASE:
362842727c2SChris Kirby 		return (gettext("\trelease [-r] <tag> <snapshot> ...\n"));
36399d5e173STim Haley 	case HELP_DIFF:
36499d5e173STim Haley 		return (gettext("\tdiff [-FHt] <snapshot> "
36599d5e173STim Haley 		    "[snapshot|filesystem]\n"));
3665cabbc6bSPrashanth Sreenivasa 	case HELP_REMAP:
3675cabbc6bSPrashanth Sreenivasa 		return (gettext("\tremap <filesystem | volume>\n"));
36878f17100SMatthew Ahrens 	case HELP_BOOKMARK:
36978f17100SMatthew Ahrens 		return (gettext("\tbookmark <snapshot> <bookmark>\n"));
370dfc11533SChris Williamson 	case HELP_CHANNEL_PROGRAM:
37152675910SAlek Pinchuk 		return (gettext("\tprogram [-jn] [-t <instruction limit>] "
372dfc11533SChris Williamson 		    "[-m <memory limit (b)>] <pool> <program file> "
373dfc11533SChris Williamson 		    "[lua args...]\n"));
374eb633035STom Caputi 	case HELP_LOAD_KEY:
375eb633035STom Caputi 		return (gettext("\tload-key [-rn] [-L <keylocation>] "
376eb633035STom Caputi 		    "<-a | filesystem|volume>\n"));
377eb633035STom Caputi 	case HELP_UNLOAD_KEY:
378eb633035STom Caputi 		return (gettext("\tunload-key [-r] "
379eb633035STom Caputi 		    "<-a | filesystem|volume>\n"));
380eb633035STom Caputi 	case HELP_CHANGE_KEY:
381eb633035STom Caputi 		return (gettext("\tchange-key [-l] [-o keyformat=<value>]\n"
382eb633035STom Caputi 		    "\t    [-o keylocation=<value>] [-o pbkfd2iters=<value>]\n"
383eb633035STom Caputi 		    "\t    <filesystem|volume>\n"
384eb633035STom Caputi 		    "\tchange-key -i [-l] <filesystem|volume>\n"));
38565cd9f28Seschrock 	}
38665cd9f28Seschrock 
38765cd9f28Seschrock 	abort();
38865cd9f28Seschrock 	/* NOTREACHED */
38965cd9f28Seschrock }
39065cd9f28Seschrock 
391a8b6ddafSMark J Musante void
nomem(void)392a8b6ddafSMark J Musante nomem(void)
393a8b6ddafSMark J Musante {
394a8b6ddafSMark J Musante 	(void) fprintf(stderr, gettext("internal error: out of memory\n"));
395a8b6ddafSMark J Musante 	exit(1);
396a8b6ddafSMark J Musante }
397a8b6ddafSMark J Musante 
398fa9e4066Sahrens /*
399fa9e4066Sahrens  * Utility function to guarantee malloc() success.
400fa9e4066Sahrens  */
401a8b6ddafSMark J Musante 
402fa9e4066Sahrens void *
safe_malloc(size_t size)403fa9e4066Sahrens safe_malloc(size_t size)
404fa9e4066Sahrens {
405fa9e4066Sahrens 	void *data;
406fa9e4066Sahrens 
407a8b6ddafSMark J Musante 	if ((data = calloc(1, size)) == NULL)
408a8b6ddafSMark J Musante 		nomem();
409fa9e4066Sahrens 
410fa9e4066Sahrens 	return (data);
411fa9e4066Sahrens }
412fa9e4066Sahrens 
413dfc11533SChris Williamson void *
safe_realloc(void * data,size_t size)414dfc11533SChris Williamson safe_realloc(void *data, size_t size)
415dfc11533SChris Williamson {
416dfc11533SChris Williamson 	void *newp;
417dfc11533SChris Williamson 	if ((newp = realloc(data, size)) == NULL) {
418dfc11533SChris Williamson 		free(data);
419dfc11533SChris Williamson 		nomem();
420dfc11533SChris Williamson 	}
421dfc11533SChris Williamson 
422dfc11533SChris Williamson 	return (newp);
423dfc11533SChris Williamson }
424dfc11533SChris Williamson 
425a8b6ddafSMark J Musante static char *
safe_strdup(char * str)426a8b6ddafSMark J Musante safe_strdup(char *str)
427a8b6ddafSMark J Musante {
428a8b6ddafSMark J Musante 	char *dupstr = strdup(str);
429a8b6ddafSMark J Musante 
430a8b6ddafSMark J Musante 	if (dupstr == NULL)
431a8b6ddafSMark J Musante 		nomem();
432a8b6ddafSMark J Musante 
433a8b6ddafSMark J Musante 	return (dupstr);
434a8b6ddafSMark J Musante }
435a8b6ddafSMark J Musante 
436fa9e4066Sahrens /*
437990b4856Slling  * Callback routine that will print out information for each of
43866e2aaccSgw25295  * the properties.
43966e2aaccSgw25295  */
440990b4856Slling static int
usage_prop_cb(int prop,void * cb)441990b4856Slling usage_prop_cb(int prop, void *cb)
44266e2aaccSgw25295 {
44366e2aaccSgw25295 	FILE *fp = cb;
44466e2aaccSgw25295 
445c5904d13Seschrock 	(void) fprintf(fp, "\t%-15s ", zfs_prop_to_name(prop));
44666e2aaccSgw25295 
447c5904d13Seschrock 	if (zfs_prop_readonly(prop))
44866e2aaccSgw25295 		(void) fprintf(fp, " NO    ");
44966e2aaccSgw25295 	else
45066e2aaccSgw25295 		(void) fprintf(fp, "YES    ");
45166e2aaccSgw25295 
45266e2aaccSgw25295 	if (zfs_prop_inheritable(prop))
45366e2aaccSgw25295 		(void) fprintf(fp, "  YES   ");
45466e2aaccSgw25295 	else
45566e2aaccSgw25295 		(void) fprintf(fp, "   NO   ");
45666e2aaccSgw25295 
45766e2aaccSgw25295 	if (zfs_prop_values(prop) == NULL)
45866e2aaccSgw25295 		(void) fprintf(fp, "-\n");
45966e2aaccSgw25295 	else
46066e2aaccSgw25295 		(void) fprintf(fp, "%s\n", zfs_prop_values(prop));
46166e2aaccSgw25295 
462990b4856Slling 	return (ZPROP_CONT);
46366e2aaccSgw25295 }
46466e2aaccSgw25295 
46566e2aaccSgw25295 /*
466fa9e4066Sahrens  * Display usage message.  If we're inside a command, display only the usage for
467fa9e4066Sahrens  * that command.  Otherwise, iterate over the entire command table and display
468fa9e4066Sahrens  * a complete usage message.
469fa9e4066Sahrens  */
470fa9e4066Sahrens static void
usage(boolean_t requested)47199653d4eSeschrock usage(boolean_t requested)
472fa9e4066Sahrens {
473fa9e4066Sahrens 	int i;
47499653d4eSeschrock 	boolean_t show_properties = B_FALSE;
475fa9e4066Sahrens 	FILE *fp = requested ? stdout : stderr;
476fa9e4066Sahrens 
477fa9e4066Sahrens 	if (current_command == NULL) {
478fa9e4066Sahrens 
479fa9e4066Sahrens 		(void) fprintf(fp, gettext("usage: zfs command args ...\n"));
480fa9e4066Sahrens 		(void) fprintf(fp,
481fa9e4066Sahrens 		    gettext("where 'command' is one of the following:\n\n"));
482fa9e4066Sahrens 
483fa9e4066Sahrens 		for (i = 0; i < NCOMMAND; i++) {
484fa9e4066Sahrens 			if (command_table[i].name == NULL)
485fa9e4066Sahrens 				(void) fprintf(fp, "\n");
486fa9e4066Sahrens 			else
487fa9e4066Sahrens 				(void) fprintf(fp, "%s",
48865cd9f28Seschrock 				    get_usage(command_table[i].usage));
489fa9e4066Sahrens 		}
490fa9e4066Sahrens 
491fa9e4066Sahrens 		(void) fprintf(fp, gettext("\nEach dataset is of the form: "
492fa9e4066Sahrens 		    "pool/[dataset/]*dataset[@name]\n"));
493fa9e4066Sahrens 	} else {
494fa9e4066Sahrens 		(void) fprintf(fp, gettext("usage:\n"));
49565cd9f28Seschrock 		(void) fprintf(fp, "%s", get_usage(current_command->usage));
496fa9e4066Sahrens 	}
497fa9e4066Sahrens 
4985c21526aSdarrenm 	if (current_command != NULL &&
4995c21526aSdarrenm 	    (strcmp(current_command->name, "set") == 0 ||
500fa9e4066Sahrens 	    strcmp(current_command->name, "get") == 0 ||
501fa9e4066Sahrens 	    strcmp(current_command->name, "inherit") == 0 ||
5025c21526aSdarrenm 	    strcmp(current_command->name, "list") == 0))
50399653d4eSeschrock 		show_properties = B_TRUE;
504fa9e4066Sahrens 
505fa9e4066Sahrens 	if (show_properties) {
506fa9e4066Sahrens 		(void) fprintf(fp,
507fa9e4066Sahrens 		    gettext("\nThe following properties are supported:\n"));
508fa9e4066Sahrens 
509a9799022Sck153898 		(void) fprintf(fp, "\n\t%-14s %s  %s   %s\n\n",
510fa9e4066Sahrens 		    "PROPERTY", "EDIT", "INHERIT", "VALUES");
511fa9e4066Sahrens 
51266e2aaccSgw25295 		/* Iterate over all properties */
513990b4856Slling 		(void) zprop_iter(usage_prop_cb, fp, B_FALSE, B_TRUE,
514990b4856Slling 		    ZFS_TYPE_DATASET);
515fa9e4066Sahrens 
51614843421SMatthew Ahrens 		(void) fprintf(fp, "\t%-15s ", "userused@...");
51714843421SMatthew Ahrens 		(void) fprintf(fp, " NO       NO   <size>\n");
51814843421SMatthew Ahrens 		(void) fprintf(fp, "\t%-15s ", "groupused@...");
51914843421SMatthew Ahrens 		(void) fprintf(fp, " NO       NO   <size>\n");
520f67950b2SNasf-Fan 		(void) fprintf(fp, "\t%-15s ", "projectused@...");
521f67950b2SNasf-Fan 		(void) fprintf(fp, " NO       NO   <size>\n");
522f67950b2SNasf-Fan 		(void) fprintf(fp, "\t%-15s ", "userobjused@...");
523f67950b2SNasf-Fan 		(void) fprintf(fp, " NO       NO   <size>\n");
524f67950b2SNasf-Fan 		(void) fprintf(fp, "\t%-15s ", "groupobjused@...");
525f67950b2SNasf-Fan 		(void) fprintf(fp, " NO       NO   <size>\n");
526f67950b2SNasf-Fan 		(void) fprintf(fp, "\t%-15s ", "projectobjused@...");
527f67950b2SNasf-Fan 		(void) fprintf(fp, " NO       NO   <size>\n");
52814843421SMatthew Ahrens 		(void) fprintf(fp, "\t%-15s ", "userquota@...");
52914843421SMatthew Ahrens 		(void) fprintf(fp, "YES       NO   <size> | none\n");
53014843421SMatthew Ahrens 		(void) fprintf(fp, "\t%-15s ", "groupquota@...");
53114843421SMatthew Ahrens 		(void) fprintf(fp, "YES       NO   <size> | none\n");
532f67950b2SNasf-Fan 		(void) fprintf(fp, "\t%-15s ", "projectquota@...");
533f67950b2SNasf-Fan 		(void) fprintf(fp, "YES       NO   <size> | none\n");
534f67950b2SNasf-Fan 		(void) fprintf(fp, "\t%-15s ", "userobjquota@...");
535f67950b2SNasf-Fan 		(void) fprintf(fp, "YES       NO   <size> | none\n");
536f67950b2SNasf-Fan 		(void) fprintf(fp, "\t%-15s ", "groupobjquota@...");
537f67950b2SNasf-Fan 		(void) fprintf(fp, "YES       NO   <size> | none\n");
538f67950b2SNasf-Fan 		(void) fprintf(fp, "\t%-15s ", "projectobjquota@...");
539f67950b2SNasf-Fan 		(void) fprintf(fp, "YES       NO   <size> | none\n");
54019b94df9SMatthew Ahrens 		(void) fprintf(fp, "\t%-15s ", "written@<snap>");
54119b94df9SMatthew Ahrens 		(void) fprintf(fp, " NO       NO   <size>\n");
54214843421SMatthew Ahrens 
543fa9e4066Sahrens 		(void) fprintf(fp, gettext("\nSizes are specified in bytes "
544fa9e4066Sahrens 		    "with standard units such as K, M, G, etc.\n"));
54574e7dc98SMatthew Ahrens 		(void) fprintf(fp, gettext("\nUser-defined properties can "
546e9dbad6fSeschrock 		    "be specified by using a name containing a colon (:).\n"));
547f67950b2SNasf-Fan 		(void) fprintf(fp, gettext("\nThe {user|group|project}"
548f67950b2SNasf-Fan 		    "[obj]{used|quota}@ properties must be appended with\n"
549f67950b2SNasf-Fan 		    "a user|group|project specifier of one of these forms:\n"
55014843421SMatthew Ahrens 		    "    POSIX name      (eg: \"matt\")\n"
55114843421SMatthew Ahrens 		    "    POSIX id        (eg: \"126829\")\n"
55214843421SMatthew Ahrens 		    "    SMB name@domain (eg: \"matt@sun\")\n"
55314843421SMatthew Ahrens 		    "    SMB SID         (eg: \"S-1-234-567-89\")\n"));
5545c21526aSdarrenm 	} else {
5555c21526aSdarrenm 		(void) fprintf(fp,
556deb8317bSMark J Musante 		    gettext("\nFor the property list, run: %s\n"),
557deb8317bSMark J Musante 		    "zfs set|get");
5586949a980Smarks 		(void) fprintf(fp,
559deb8317bSMark J Musante 		    gettext("\nFor the delegated permission list, run: %s\n"),
560deb8317bSMark J Musante 		    "zfs allow|unallow");
561fa9e4066Sahrens 	}
562fa9e4066Sahrens 
563e9dbad6fSeschrock 	/*
564e9dbad6fSeschrock 	 * See comments at end of main().
565e9dbad6fSeschrock 	 */
566e9dbad6fSeschrock 	if (getenv("ZFS_ABORT") != NULL) {
567e9dbad6fSeschrock 		(void) printf("dumping core by request\n");
568e9dbad6fSeschrock 		abort();
569e9dbad6fSeschrock 	}
570e9dbad6fSeschrock 
571fa9e4066Sahrens 	exit(requested ? 0 : 2);
572fa9e4066Sahrens }
573fa9e4066Sahrens 
57430925561SChris Williamson /*
57530925561SChris Williamson  * Take a property=value argument string and add it to the given nvlist.
57630925561SChris Williamson  * Modifies the argument inplace.
57730925561SChris Williamson  */
5786ccda740Sloli10K static boolean_t
parseprop(nvlist_t * props,char * propname)57930925561SChris Williamson parseprop(nvlist_t *props, char *propname)
580bb0ade09Sahrens {
5816ccda740Sloli10K 	char *propval;
582bb0ade09Sahrens 
583bb0ade09Sahrens 	if ((propval = strchr(propname, '=')) == NULL) {
584bb0ade09Sahrens 		(void) fprintf(stderr, gettext("missing "
58530925561SChris Williamson 		    "'=' for property=value argument\n"));
5866ccda740Sloli10K 		return (B_FALSE);
587bb0ade09Sahrens 	}
588bb0ade09Sahrens 	*propval = '\0';
589bb0ade09Sahrens 	propval++;
5906ccda740Sloli10K 	if (nvlist_exists(props, propname)) {
591bb0ade09Sahrens 		(void) fprintf(stderr, gettext("property '%s' "
592bb0ade09Sahrens 		    "specified multiple times\n"), propname);
5936ccda740Sloli10K 		return (B_FALSE);
594bb0ade09Sahrens 	}
595a8b6ddafSMark J Musante 	if (nvlist_add_string(props, propname, propval) != 0)
596a8b6ddafSMark J Musante 		nomem();
5976ccda740Sloli10K 	return (B_TRUE);
5986ccda740Sloli10K }
5996ccda740Sloli10K 
6006ccda740Sloli10K /*
6016ccda740Sloli10K  * Take a property name argument and add it to the given nvlist.
6026ccda740Sloli10K  * Modifies the argument inplace.
6036ccda740Sloli10K  */
6046ccda740Sloli10K static boolean_t
parsepropname(nvlist_t * props,char * propname)6056ccda740Sloli10K parsepropname(nvlist_t *props, char *propname)
6066ccda740Sloli10K {
6076ccda740Sloli10K 	if (strchr(propname, '=') != NULL) {
6086ccda740Sloli10K 		(void) fprintf(stderr, gettext("invalid character "
6096ccda740Sloli10K 		    "'=' in property argument\n"));
6106ccda740Sloli10K 		return (B_FALSE);
6116ccda740Sloli10K 	}
6126ccda740Sloli10K 	if (nvlist_exists(props, propname)) {
6136ccda740Sloli10K 		(void) fprintf(stderr, gettext("property '%s' "
6146ccda740Sloli10K 		    "specified multiple times\n"), propname);
6156ccda740Sloli10K 		return (B_FALSE);
6166ccda740Sloli10K 	}
6176ccda740Sloli10K 	if (nvlist_add_boolean(props, propname) != 0)
6186ccda740Sloli10K 		nomem();
6196ccda740Sloli10K 	return (B_TRUE);
620bb0ade09Sahrens }
621bb0ade09Sahrens 
622ae1726b6SChris Gerhard static int
parse_depth(char * opt,int * flags)623ae1726b6SChris Gerhard parse_depth(char *opt, int *flags)
624ae1726b6SChris Gerhard {
625ae1726b6SChris Gerhard 	char *tmp;
626ae1726b6SChris Gerhard 	int depth;
627ae1726b6SChris Gerhard 
628ae1726b6SChris Gerhard 	depth = (int)strtol(opt, &tmp, 0);
629ae1726b6SChris Gerhard 	if (*tmp) {
630ae1726b6SChris Gerhard 		(void) fprintf(stderr,
631ae1726b6SChris Gerhard 		    gettext("%s is not an integer\n"), optarg);
632ae1726b6SChris Gerhard 		usage(B_FALSE);
633ae1726b6SChris Gerhard 	}
634ae1726b6SChris Gerhard 	if (depth < 0) {
635ae1726b6SChris Gerhard 		(void) fprintf(stderr,
636ae1726b6SChris Gerhard 		    gettext("Depth can not be negative.\n"));
637ae1726b6SChris Gerhard 		usage(B_FALSE);
638ae1726b6SChris Gerhard 	}
639ae1726b6SChris Gerhard 	*flags |= (ZFS_ITER_DEPTH_LIMIT|ZFS_ITER_RECURSE);
640ae1726b6SChris Gerhard 	return (depth);
641ae1726b6SChris Gerhard }
642ae1726b6SChris Gerhard 
643a8b6ddafSMark J Musante #define	PROGRESS_DELAY 2		/* seconds */
644a8b6ddafSMark J Musante 
645a8b6ddafSMark J Musante 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";
646a8b6ddafSMark J Musante static time_t pt_begin;
647a8b6ddafSMark J Musante static char *pt_header = NULL;
648a8b6ddafSMark J Musante static boolean_t pt_shown;
649a8b6ddafSMark J Musante 
650a8b6ddafSMark J Musante static void
start_progress_timer(void)651a8b6ddafSMark J Musante start_progress_timer(void)
652a8b6ddafSMark J Musante {
653a8b6ddafSMark J Musante 	pt_begin = time(NULL) + PROGRESS_DELAY;
654a8b6ddafSMark J Musante 	pt_shown = B_FALSE;
655a8b6ddafSMark J Musante }
656a8b6ddafSMark J Musante 
657a8b6ddafSMark J Musante static void
set_progress_header(char * header)658a8b6ddafSMark J Musante set_progress_header(char *header)
659a8b6ddafSMark J Musante {
660a8b6ddafSMark J Musante 	assert(pt_header == NULL);
661a8b6ddafSMark J Musante 	pt_header = safe_strdup(header);
662a8b6ddafSMark J Musante 	if (pt_shown) {
663a8b6ddafSMark J Musante 		(void) printf("%s: ", header);
664a8b6ddafSMark J Musante 		(void) fflush(stdout);
665a8b6ddafSMark J Musante 	}
666a8b6ddafSMark J Musante }
667a8b6ddafSMark J Musante 
668a8b6ddafSMark J Musante static void
update_progress(char * update)669a8b6ddafSMark J Musante update_progress(char *update)
670a8b6ddafSMark J Musante {
671a8b6ddafSMark J Musante 	if (!pt_shown && time(NULL) > pt_begin) {
672a8b6ddafSMark J Musante 		int len = strlen(update);
673a8b6ddafSMark J Musante 
674a8b6ddafSMark J Musante 		(void) printf("%s: %s%*.*s", pt_header, update, len, len,
675a8b6ddafSMark J Musante 		    pt_reverse);
676a8b6ddafSMark J Musante 		(void) fflush(stdout);
677a8b6ddafSMark J Musante 		pt_shown = B_TRUE;
678a8b6ddafSMark J Musante 	} else if (pt_shown) {
679a8b6ddafSMark J Musante 		int len = strlen(update);
680a8b6ddafSMark J Musante 
681a8b6ddafSMark J Musante 		(void) printf("%s%*.*s", update, len, len, pt_reverse);
682a8b6ddafSMark J Musante 		(void) fflush(stdout);
683a8b6ddafSMark J Musante 	}
684a8b6ddafSMark J Musante }
685a8b6ddafSMark J Musante 
686a8b6ddafSMark J Musante static void
finish_progress(char * done)687a8b6ddafSMark J Musante finish_progress(char *done)
688a8b6ddafSMark J Musante {
689a8b6ddafSMark J Musante 	if (pt_shown) {
690a8b6ddafSMark J Musante 		(void) printf("%s\n", done);
691a8b6ddafSMark J Musante 		(void) fflush(stdout);
692a8b6ddafSMark J Musante 	}
693a8b6ddafSMark J Musante 	free(pt_header);
694a8b6ddafSMark J Musante 	pt_header = NULL;
695a8b6ddafSMark J Musante }
6965d7b4d43SMatthew Ahrens 
697f5a3d331SBrian Behlendorf static int
zfs_mount_and_share(libzfs_handle_t * hdl,const char * dataset,zfs_type_t type)698f5a3d331SBrian Behlendorf zfs_mount_and_share(libzfs_handle_t *hdl, const char *dataset, zfs_type_t type)
699780828c8SAndriy Gapon {
700f5a3d331SBrian Behlendorf 	zfs_handle_t *zhp = NULL;
701f5a3d331SBrian Behlendorf 	int ret = 0;
702f5a3d331SBrian Behlendorf 
703f5a3d331SBrian Behlendorf 	zhp = zfs_open(hdl, dataset, type);
704f5a3d331SBrian Behlendorf 	if (zhp == NULL)
705f5a3d331SBrian Behlendorf 		return (1);
706f5a3d331SBrian Behlendorf 
707f5a3d331SBrian Behlendorf 	/*
708f5a3d331SBrian Behlendorf 	 * Volumes may neither be mounted or shared.  Potentially in the
709f5a3d331SBrian Behlendorf 	 * future filesystems detected on these volumes could be mounted.
710f5a3d331SBrian Behlendorf 	 */
711f5a3d331SBrian Behlendorf 	if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) {
712f5a3d331SBrian Behlendorf 		zfs_close(zhp);
713f5a3d331SBrian Behlendorf 		return (0);
714f5a3d331SBrian Behlendorf 	}
715f5a3d331SBrian Behlendorf 
716f5a3d331SBrian Behlendorf 	/*
717f5a3d331SBrian Behlendorf 	 * Mount and/or share the new filesystem as appropriate.  We provide a
718f5a3d331SBrian Behlendorf 	 * verbose error message to let the user know that their filesystem was
719f5a3d331SBrian Behlendorf 	 * in fact created, even if we failed to mount or share it.
720f5a3d331SBrian Behlendorf 	 *
721f5a3d331SBrian Behlendorf 	 * If the user doesn't want the dataset automatically mounted, then
722f5a3d331SBrian Behlendorf 	 * skip the mount/share step
723f5a3d331SBrian Behlendorf 	 */
724f5a3d331SBrian Behlendorf 	if (zfs_prop_valid_for_type(ZFS_PROP_CANMOUNT, type, B_FALSE) &&
725f5a3d331SBrian Behlendorf 	    zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON) {
726f5a3d331SBrian Behlendorf 		if (zfs_mount(zhp, NULL, 0) != 0) {
727f5a3d331SBrian Behlendorf 			(void) fprintf(stderr, gettext("filesystem "
728f5a3d331SBrian Behlendorf 			    "successfully created, but not mounted\n"));
729f5a3d331SBrian Behlendorf 			ret = 1;
730f5a3d331SBrian Behlendorf 		} else if (zfs_share(zhp) != 0) {
731f5a3d331SBrian Behlendorf 			(void) fprintf(stderr, gettext("filesystem "
732f5a3d331SBrian Behlendorf 			    "successfully created, but not shared\n"));
733f5a3d331SBrian Behlendorf 			ret = 1;
734f5a3d331SBrian Behlendorf 		}
735f5a3d331SBrian Behlendorf 	}
736f5a3d331SBrian Behlendorf 
737f5a3d331SBrian Behlendorf 	zfs_close(zhp);
738f5a3d331SBrian Behlendorf 
739f5a3d331SBrian Behlendorf 	return (ret);
740780828c8SAndriy Gapon }
741780828c8SAndriy Gapon 
742780828c8SAndriy Gapon /*
743bb0ade09Sahrens  * zfs clone [-p] [-o prop=value] ... <snap> <fs | vol>
744fa9e4066Sahrens  *
745fa9e4066Sahrens  * Given an existing dataset, create a writable copy whose initial contents
746fa9e4066Sahrens  * are the same as the source.  The newly created dataset maintains a
747fa9e4066Sahrens  * dependency on the original; the original cannot be destroyed so long as
748fa9e4066Sahrens  * the clone exists.
7497f1f55eaSvb160487  *
7507f1f55eaSvb160487  * The '-p' flag creates all the non-existing ancestors of the target first.
751fa9e4066Sahrens  */
752fa9e4066Sahrens static int
zfs_do_clone(int argc,char ** argv)753fa9e4066Sahrens zfs_do_clone(int argc, char **argv)
754fa9e4066Sahrens {
755bb0ade09Sahrens 	zfs_handle_t *zhp = NULL;
7567f1f55eaSvb160487 	boolean_t parents = B_FALSE;
757bb0ade09Sahrens 	nvlist_t *props;
75805c998d6SRichard Lowe 	int ret = 0;
7597f1f55eaSvb160487 	int c;
760fa9e4066Sahrens 
761a8b6ddafSMark J Musante 	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
762a8b6ddafSMark J Musante 		nomem();
763bb0ade09Sahrens 
764fa9e4066Sahrens 	/* check options */
765bb0ade09Sahrens 	while ((c = getopt(argc, argv, "o:p")) != -1) {
7667f1f55eaSvb160487 		switch (c) {
767bb0ade09Sahrens 		case 'o':
7686ccda740Sloli10K 			if (!parseprop(props, optarg)) {
7696ccda740Sloli10K 				nvlist_free(props);
770bb0ade09Sahrens 				return (1);
7716ccda740Sloli10K 			}
772bb0ade09Sahrens 			break;
7737f1f55eaSvb160487 		case 'p':
7747f1f55eaSvb160487 			parents = B_TRUE;
7757f1f55eaSvb160487 			break;
7767f1f55eaSvb160487 		case '?':
777fa9e4066Sahrens 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
7787f1f55eaSvb160487 			    optopt);
779bb0ade09Sahrens 			goto usage;
780fa9e4066Sahrens 		}
7817f1f55eaSvb160487 	}
7827f1f55eaSvb160487 
7837f1f55eaSvb160487 	argc -= optind;
7847f1f55eaSvb160487 	argv += optind;
785fa9e4066Sahrens 
786fa9e4066Sahrens 	/* check number of arguments */
7877f1f55eaSvb160487 	if (argc < 1) {
788fa9e4066Sahrens 		(void) fprintf(stderr, gettext("missing source dataset "
789fa9e4066Sahrens 		    "argument\n"));
790bb0ade09Sahrens 		goto usage;
791fa9e4066Sahrens 	}
7927f1f55eaSvb160487 	if (argc < 2) {
793fa9e4066Sahrens 		(void) fprintf(stderr, gettext("missing target dataset "
794fa9e4066Sahrens 		    "argument\n"));
795bb0ade09Sahrens 		goto usage;
796fa9e4066Sahrens 	}
7977f1f55eaSvb160487 	if (argc > 2) {
798fa9e4066Sahrens 		(void) fprintf(stderr, gettext("too many arguments\n"));
799bb0ade09Sahrens 		goto usage;
800fa9e4066Sahrens 	}
801fa9e4066Sahrens 
802fa9e4066Sahrens 	/* open the source dataset */
8037f1f55eaSvb160487 	if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL)
804fa9e4066Sahrens 		return (1);
805fa9e4066Sahrens 
8067f1f55eaSvb160487 	if (parents && zfs_name_valid(argv[1], ZFS_TYPE_FILESYSTEM |
8077f1f55eaSvb160487 	    ZFS_TYPE_VOLUME)) {
8087f1f55eaSvb160487 		/*
8097f1f55eaSvb160487 		 * Now create the ancestors of the target dataset.  If the
8107f1f55eaSvb160487 		 * target already exists and '-p' option was used we should not
8117f1f55eaSvb160487 		 * complain.
8127f1f55eaSvb160487 		 */
8137f1f55eaSvb160487 		if (zfs_dataset_exists(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM |
8147f1f55eaSvb160487 		    ZFS_TYPE_VOLUME))
8157f1f55eaSvb160487 			return (0);
8167f1f55eaSvb160487 		if (zfs_create_ancestors(g_zfs, argv[1]) != 0)
8177f1f55eaSvb160487 			return (1);
8187f1f55eaSvb160487 	}
8197f1f55eaSvb160487 
820fa9e4066Sahrens 	/* pass to libzfs */
821bb0ade09Sahrens 	ret = zfs_clone(zhp, argv[1], props);
822fa9e4066Sahrens 
823fa9e4066Sahrens 	/* create the mountpoint if necessary */
824fa9e4066Sahrens 	if (ret == 0) {
825f5a3d331SBrian Behlendorf 		if (log_history) {
826f5a3d331SBrian Behlendorf 			(void) zpool_log_history(g_zfs, history_str);
827f5a3d331SBrian Behlendorf 			log_history = B_FALSE;
828f5a3d331SBrian Behlendorf 		}
829990b4856Slling 
830f5a3d331SBrian Behlendorf 		ret = zfs_mount_and_share(g_zfs, argv[1], ZFS_TYPE_DATASET);
831fa9e4066Sahrens 	}
832fa9e4066Sahrens 
833fa9e4066Sahrens 	zfs_close(zhp);
834bb0ade09Sahrens 	nvlist_free(props);
835fa9e4066Sahrens 
836bb0ade09Sahrens 	return (!!ret);
837bb0ade09Sahrens 
838bb0ade09Sahrens usage:
839bb0ade09Sahrens 	if (zhp)
840bb0ade09Sahrens 		zfs_close(zhp);
841bb0ade09Sahrens 	nvlist_free(props);
842bb0ade09Sahrens 	usage(B_FALSE);
843bb0ade09Sahrens 	return (-1);
844fa9e4066Sahrens }
845fa9e4066Sahrens 
846fa9e4066Sahrens /*
8471320ddf5SMike Gerdts  * zfs create [-Pnpv] [-o prop=value] ... fs
8481320ddf5SMike Gerdts  * zfs create [-Pnpsv] [-b blocksize] [-o prop=value] ... -V vol size
849fa9e4066Sahrens  *
850fa9e4066Sahrens  * Create a new dataset.  This command can be used to create filesystems
851fa9e4066Sahrens  * and volumes.  Snapshot creation is handled by 'zfs snapshot'.
852fa9e4066Sahrens  * For volumes, the user must specify a size to be used.
853fa9e4066Sahrens  *
854fa9e4066Sahrens  * The '-s' flag applies only to volumes, and indicates that we should not try
855fa9e4066Sahrens  * to set the reservation for this volume.  By default we set a reservation
856a9b821a0Sck153898  * equal to the size for any volume.  For pools with SPA_VERSION >=
857a9b821a0Sck153898  * SPA_VERSION_REFRESERVATION, we set a refreservation instead.
8587f1f55eaSvb160487  *
8597f1f55eaSvb160487  * The '-p' flag creates all the non-existing ancestors of the target first.
8601320ddf5SMike Gerdts  *
8611320ddf5SMike Gerdts  * The '-n' flag is no-op (dry run) mode.  This will perform a user-space sanity
8621320ddf5SMike Gerdts  * check of arguments and properties, but does not check for permissions,
8631320ddf5SMike Gerdts  * available space, etc.
8641320ddf5SMike Gerdts  *
8651320ddf5SMike Gerdts  * The '-v' flag is for verbose output.
8661320ddf5SMike Gerdts  *
8671320ddf5SMike Gerdts  * The '-P' flag is used for parseable output.  It implies '-v'.
868fa9e4066Sahrens  */
869fa9e4066Sahrens static int
zfs_do_create(int argc,char ** argv)870fa9e4066Sahrens zfs_do_create(int argc, char **argv)
871fa9e4066Sahrens {
872fa9e4066Sahrens 	zfs_type_t type = ZFS_TYPE_FILESYSTEM;
8731320ddf5SMike Gerdts 	zpool_handle_t *zpool_handle = NULL;
8741320ddf5SMike Gerdts 	nvlist_t *real_props = NULL;
875c16bcc45SIgor Kozhukhov 	uint64_t volsize = 0;
876fa9e4066Sahrens 	int c;
87799653d4eSeschrock 	boolean_t noreserve = B_FALSE;
8783cb34c60Sahrens 	boolean_t bflag = B_FALSE;
8797f1f55eaSvb160487 	boolean_t parents = B_FALSE;
8801320ddf5SMike Gerdts 	boolean_t dryrun = B_FALSE;
8811320ddf5SMike Gerdts 	boolean_t verbose = B_FALSE;
8821320ddf5SMike Gerdts 	boolean_t parseable = B_FALSE;
883e9dbad6fSeschrock 	int ret = 1;
884bb0ade09Sahrens 	nvlist_t *props;
885e9dbad6fSeschrock 	uint64_t intval;
886e9dbad6fSeschrock 
887a8b6ddafSMark J Musante 	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
888a8b6ddafSMark J Musante 		nomem();
889fa9e4066Sahrens 
890fa9e4066Sahrens 	/* check options */
8911320ddf5SMike Gerdts 	while ((c = getopt(argc, argv, ":PV:b:nso:pv")) != -1) {
892fa9e4066Sahrens 		switch (c) {
893fa9e4066Sahrens 		case 'V':
894fa9e4066Sahrens 			type = ZFS_TYPE_VOLUME;
895e9dbad6fSeschrock 			if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {
896e9dbad6fSeschrock 				(void) fprintf(stderr, gettext("bad volume "
897e9dbad6fSeschrock 				    "size '%s': %s\n"), optarg,
898e9dbad6fSeschrock 				    libzfs_error_description(g_zfs));
899e9dbad6fSeschrock 				goto error;
900e9dbad6fSeschrock 			}
901e9dbad6fSeschrock 
902e9dbad6fSeschrock 			if (nvlist_add_uint64(props,
903a8b6ddafSMark J Musante 			    zfs_prop_to_name(ZFS_PROP_VOLSIZE), intval) != 0)
904a8b6ddafSMark J Musante 				nomem();
905e9dbad6fSeschrock 			volsize = intval;
906fa9e4066Sahrens 			break;
9071320ddf5SMike Gerdts 		case 'P':
9081320ddf5SMike Gerdts 			verbose = B_TRUE;
9091320ddf5SMike Gerdts 			parseable = B_TRUE;
9101320ddf5SMike Gerdts 			break;
9117f1f55eaSvb160487 		case 'p':
9127f1f55eaSvb160487 			parents = B_TRUE;
9137f1f55eaSvb160487 			break;
914fa9e4066Sahrens 		case 'b':
9153cb34c60Sahrens 			bflag = B_TRUE;
916e9dbad6fSeschrock 			if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {
917e9dbad6fSeschrock 				(void) fprintf(stderr, gettext("bad volume "
918e9dbad6fSeschrock 				    "block size '%s': %s\n"), optarg,
919e9dbad6fSeschrock 				    libzfs_error_description(g_zfs));
920e9dbad6fSeschrock 				goto error;
921e9dbad6fSeschrock 			}
922e9dbad6fSeschrock 
923e9dbad6fSeschrock 			if (nvlist_add_uint64(props,
924e9dbad6fSeschrock 			    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
925a8b6ddafSMark J Musante 			    intval) != 0)
926a8b6ddafSMark J Musante 				nomem();
927e9dbad6fSeschrock 			break;
9281320ddf5SMike Gerdts 		case 'n':
9291320ddf5SMike Gerdts 			dryrun = B_TRUE;
9301320ddf5SMike Gerdts 			break;
931e9dbad6fSeschrock 		case 'o':
9326ccda740Sloli10K 			if (!parseprop(props, optarg))
933e9dbad6fSeschrock 				goto error;
934fa9e4066Sahrens 			break;
935fa9e4066Sahrens 		case 's':
93699653d4eSeschrock 			noreserve = B_TRUE;
937fa9e4066Sahrens 			break;
9381320ddf5SMike Gerdts 		case 'v':
9391320ddf5SMike Gerdts 			verbose = B_TRUE;
9401320ddf5SMike Gerdts 			break;
941fa9e4066Sahrens 		case ':':
942fa9e4066Sahrens 			(void) fprintf(stderr, gettext("missing size "
943fa9e4066Sahrens 			    "argument\n"));
944e9dbad6fSeschrock 			goto badusage;
945fa9e4066Sahrens 		case '?':
946fa9e4066Sahrens 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
947fa9e4066Sahrens 			    optopt);
948e9dbad6fSeschrock 			goto badusage;
949fa9e4066Sahrens 		}
950fa9e4066Sahrens 	}
951fa9e4066Sahrens 
9523cb34c60Sahrens 	if ((bflag || noreserve) && type != ZFS_TYPE_VOLUME) {
9533cb34c60Sahrens 		(void) fprintf(stderr, gettext("'-s' and '-b' can only be "
9543cb34c60Sahrens 		    "used when creating a volume\n"));
955e9dbad6fSeschrock 		goto badusage;
956fa9e4066Sahrens 	}
957fa9e4066Sahrens 
958fa9e4066Sahrens 	argc -= optind;
959fa9e4066Sahrens 	argv += optind;
960fa9e4066Sahrens 
961fa9e4066Sahrens 	/* check number of arguments */
962fa9e4066Sahrens 	if (argc == 0) {
963fa9e4066Sahrens 		(void) fprintf(stderr, gettext("missing %s argument\n"),
964fa9e4066Sahrens 		    zfs_type_to_name(type));
965e9dbad6fSeschrock 		goto badusage;
966fa9e4066Sahrens 	}
967fa9e4066Sahrens 	if (argc > 1) {
968fa9e4066Sahrens 		(void) fprintf(stderr, gettext("too many arguments\n"));
969e9dbad6fSeschrock 		goto badusage;
970e9dbad6fSeschrock 	}
971e9dbad6fSeschrock 
9721320ddf5SMike Gerdts 	if (dryrun || (type == ZFS_TYPE_VOLUME && !noreserve)) {
9731320ddf5SMike Gerdts 		char msg[ZFS_MAX_DATASET_NAME_LEN * 2];
974a9b821a0Sck153898 		char *p;
975a9b821a0Sck153898 
976c16bcc45SIgor Kozhukhov 		if ((p = strchr(argv[0], '/')) != NULL)
977a9b821a0Sck153898 			*p = '\0';
978a9b821a0Sck153898 		zpool_handle = zpool_open(g_zfs, argv[0]);
979a9b821a0Sck153898 		if (p != NULL)
980a9b821a0Sck153898 			*p = '/';
981a9b821a0Sck153898 		if (zpool_handle == NULL)
982a9b821a0Sck153898 			goto error;
9831320ddf5SMike Gerdts 
9841320ddf5SMike Gerdts 		(void) snprintf(msg, sizeof (msg),
9851320ddf5SMike Gerdts 		    dryrun ? gettext("cannot verify '%s'") :
9861320ddf5SMike Gerdts 		    gettext("cannot create '%s'"), argv[0]);
9871320ddf5SMike Gerdts 		if (props && (real_props = zfs_valid_proplist(g_zfs, type,
9881320ddf5SMike Gerdts 		    props, 0, NULL, zpool_handle, B_TRUE, msg)) == NULL) {
9891320ddf5SMike Gerdts 			zpool_close(zpool_handle);
9901320ddf5SMike Gerdts 			goto error;
9911320ddf5SMike Gerdts 		}
9921320ddf5SMike Gerdts 	}
9931320ddf5SMike Gerdts 
9943cf6ec9cSalex 	/*
9953cf6ec9cSalex 	 * if volsize is not a multiple of volblocksize, round it up to the
9963cf6ec9cSalex 	 * nearest multiple of the volblocksize
9973cf6ec9cSalex 	 */
9983cf6ec9cSalex 	if (type == ZFS_TYPE_VOLUME) {
9993cf6ec9cSalex 		uint64_t volblocksize;
10003cf6ec9cSalex 
10013cf6ec9cSalex 		if (nvlist_lookup_uint64(props,
10023cf6ec9cSalex 		    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
10033cf6ec9cSalex 		    &volblocksize) != 0)
10043cf6ec9cSalex 			volblocksize = ZVOL_DEFAULT_BLOCKSIZE;
10053cf6ec9cSalex 
10063cf6ec9cSalex 		if (volsize % volblocksize) {
10073cf6ec9cSalex 			volsize = P2ROUNDUP_TYPED(volsize, volblocksize,
10083cf6ec9cSalex 			    uint64_t);
10093cf6ec9cSalex 
10103cf6ec9cSalex 			if (nvlist_add_uint64(props,
10113cf6ec9cSalex 			    zfs_prop_to_name(ZFS_PROP_VOLSIZE), volsize) != 0) {
10123cf6ec9cSalex 				nvlist_free(props);
10133cf6ec9cSalex 				nomem();
10143cf6ec9cSalex 			}
10153cf6ec9cSalex 		}
10163cf6ec9cSalex 	}
10173cf6ec9cSalex 
10183cf6ec9cSalex 
10191320ddf5SMike Gerdts 	if (type == ZFS_TYPE_VOLUME && !noreserve) {
10201320ddf5SMike Gerdts 		uint64_t spa_version;
10211320ddf5SMike Gerdts 		zfs_prop_t resv_prop;
10221320ddf5SMike Gerdts 		char *strval;
10231320ddf5SMike Gerdts 
1024a9b821a0Sck153898 		spa_version = zpool_get_prop_int(zpool_handle,
1025a9b821a0Sck153898 		    ZPOOL_PROP_VERSION, NULL);
1026a9b821a0Sck153898 		if (spa_version >= SPA_VERSION_REFRESERVATION)
1027a9b821a0Sck153898 			resv_prop = ZFS_PROP_REFRESERVATION;
1028a9b821a0Sck153898 		else
1029a9b821a0Sck153898 			resv_prop = ZFS_PROP_RESERVATION;
1030c61ea566SGeorge Wilson 
1031b73ccab0SMike Gerdts 		volsize = zvol_volsize_to_reservation(zpool_handle, volsize,
1032b73ccab0SMike Gerdts 		    real_props);
1033a9b821a0Sck153898 
1034a9b821a0Sck153898 		if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop),
1035e9dbad6fSeschrock 		    &strval) != 0) {
1036e9dbad6fSeschrock 			if (nvlist_add_uint64(props,
1037a9b821a0Sck153898 			    zfs_prop_to_name(resv_prop), volsize) != 0) {
1038e9dbad6fSeschrock 				nvlist_free(props);
1039a8b6ddafSMark J Musante 				nomem();
1040e9dbad6fSeschrock 			}
1041fa9e4066Sahrens 		}
1042a9b821a0Sck153898 	}
10431320ddf5SMike Gerdts 	if (zpool_handle != NULL) {
10441320ddf5SMike Gerdts 		zpool_close(zpool_handle);
10451320ddf5SMike Gerdts 		nvlist_free(real_props);
10461320ddf5SMike Gerdts 	}
1047fa9e4066Sahrens 
10487f1f55eaSvb160487 	if (parents && zfs_name_valid(argv[0], type)) {
10497f1f55eaSvb160487 		/*
10507f1f55eaSvb160487 		 * Now create the ancestors of target dataset.  If the target
10517f1f55eaSvb160487 		 * already exists and '-p' option was used we should not
10527f1f55eaSvb160487 		 * complain.
10537f1f55eaSvb160487 		 */
10547f1f55eaSvb160487 		if (zfs_dataset_exists(g_zfs, argv[0], type)) {
10557f1f55eaSvb160487 			ret = 0;
10567f1f55eaSvb160487 			goto error;
10577f1f55eaSvb160487 		}
10581320ddf5SMike Gerdts 		if (verbose) {
10591320ddf5SMike Gerdts 			(void) printf(parseable ? "create_ancestors\t%s\n" :
10601320ddf5SMike Gerdts 			    dryrun ? "would create ancestors of %s\n" :
10611320ddf5SMike Gerdts 			    "create ancestors of %s\n", argv[0]);
10621320ddf5SMike Gerdts 		}
10631320ddf5SMike Gerdts 		if (!dryrun) {
10641320ddf5SMike Gerdts 			if (zfs_create_ancestors(g_zfs, argv[0]) != 0) {
10651320ddf5SMike Gerdts 				goto error;
10661320ddf5SMike Gerdts 			}
10671320ddf5SMike Gerdts 		}
10681320ddf5SMike Gerdts 	}
10691320ddf5SMike Gerdts 
10701320ddf5SMike Gerdts 	if (verbose) {
10711320ddf5SMike Gerdts 		nvpair_t *nvp = NULL;
10721320ddf5SMike Gerdts 		(void) printf(parseable ? "create\t%s\n" :
10731320ddf5SMike Gerdts 		    dryrun ? "would create %s\n" : "create %s\n", argv[0]);
10741320ddf5SMike Gerdts 		while ((nvp = nvlist_next_nvpair(props, nvp)) != NULL) {
10751320ddf5SMike Gerdts 			uint64_t uval;
10761320ddf5SMike Gerdts 			char *sval;
10771320ddf5SMike Gerdts 
10781320ddf5SMike Gerdts 			switch (nvpair_type(nvp)) {
10791320ddf5SMike Gerdts 			case DATA_TYPE_UINT64:
10801320ddf5SMike Gerdts 				VERIFY0(nvpair_value_uint64(nvp, &uval));
10811320ddf5SMike Gerdts 				(void) printf(parseable ?
10821320ddf5SMike Gerdts 				    "property\t%s\t%llu\n" : "\t%s=%llu\n",
10831320ddf5SMike Gerdts 				    nvpair_name(nvp), (u_longlong_t)uval);
10841320ddf5SMike Gerdts 				break;
10851320ddf5SMike Gerdts 			case DATA_TYPE_STRING:
10861320ddf5SMike Gerdts 				VERIFY0(nvpair_value_string(nvp, &sval));
10871320ddf5SMike Gerdts 				(void) printf(parseable ?
10881320ddf5SMike Gerdts 				    "property\t%s\t%s\n" : "\t%s=%s\n",
10891320ddf5SMike Gerdts 				    nvpair_name(nvp), sval);
10901320ddf5SMike Gerdts 				break;
10911320ddf5SMike Gerdts 			default:
10921320ddf5SMike Gerdts 				(void) fprintf(stderr, "property '%s' "
10931320ddf5SMike Gerdts 				    "has illegal type %d\n",
10941320ddf5SMike Gerdts 				    nvpair_name(nvp), nvpair_type(nvp));
10951320ddf5SMike Gerdts 				abort();
10961320ddf5SMike Gerdts 			}
10971320ddf5SMike Gerdts 		}
10981320ddf5SMike Gerdts 	}
10991320ddf5SMike Gerdts 	if (dryrun) {
11001320ddf5SMike Gerdts 		ret = 0;
11017f1f55eaSvb160487 		goto error;
11027f1f55eaSvb160487 	}
11037f1f55eaSvb160487 
1104fa9e4066Sahrens 	/* pass to libzfs */
1105e9dbad6fSeschrock 	if (zfs_create(g_zfs, argv[0], type, props) != 0)
1106e9dbad6fSeschrock 		goto error;
1107fa9e4066Sahrens 
1108f5a3d331SBrian Behlendorf 	if (log_history) {
1109f5a3d331SBrian Behlendorf 		(void) zpool_log_history(g_zfs, history_str);
1110f5a3d331SBrian Behlendorf 		log_history = B_FALSE;
1111fa9e4066Sahrens 	}
1112fa9e4066Sahrens 
1113f5a3d331SBrian Behlendorf 	ret = zfs_mount_and_share(g_zfs, argv[0], ZFS_TYPE_DATASET);
1114e9dbad6fSeschrock error:
1115e9dbad6fSeschrock 	nvlist_free(props);
1116fa9e4066Sahrens 	return (ret);
1117e9dbad6fSeschrock badusage:
1118e9dbad6fSeschrock 	nvlist_free(props);
1119e9dbad6fSeschrock 	usage(B_FALSE);
1120e9dbad6fSeschrock 	return (2);
1121fa9e4066Sahrens }
1122fa9e4066Sahrens 
1123fa9e4066Sahrens /*
1124922d9a97SChris Kirby  * zfs destroy [-rRf] <fs, vol>
1125922d9a97SChris Kirby  * zfs destroy [-rRd] <snap>
1126fa9e4066Sahrens  *
1127fa9e4066Sahrens  *	-r	Recursively destroy all children
1128fa9e4066Sahrens  *	-R	Recursively destroy all dependents, including clones
1129fa9e4066Sahrens  *	-f	Force unmounting of any dependents
1130842727c2SChris Kirby  *	-d	If we can't destroy now, mark for deferred destruction
1131fa9e4066Sahrens  *
1132fa9e4066Sahrens  * Destroys the given dataset.  By default, it will unmount any filesystems,
1133fa9e4066Sahrens  * and refuse to destroy a dataset that has any dependents.  A dependent can
1134fa9e4066Sahrens  * either be a child, or a clone of a child.
1135fa9e4066Sahrens  */
1136fa9e4066Sahrens typedef struct destroy_cbdata {
113799653d4eSeschrock 	boolean_t	cb_first;
113819b94df9SMatthew Ahrens 	boolean_t	cb_force;
113919b94df9SMatthew Ahrens 	boolean_t	cb_recurse;
114019b94df9SMatthew Ahrens 	boolean_t	cb_error;
114119b94df9SMatthew Ahrens 	boolean_t	cb_doclones;
1142fa9e4066Sahrens 	zfs_handle_t	*cb_target;
1143842727c2SChris Kirby 	boolean_t	cb_defer_destroy;
114419b94df9SMatthew Ahrens 	boolean_t	cb_verbose;
114519b94df9SMatthew Ahrens 	boolean_t	cb_parsable;
114619b94df9SMatthew Ahrens 	boolean_t	cb_dryrun;
114719b94df9SMatthew Ahrens 	nvlist_t	*cb_nvl;
11483b2aab18SMatthew Ahrens 	nvlist_t	*cb_batchedsnaps;
114919b94df9SMatthew Ahrens 
115019b94df9SMatthew Ahrens 	/* first snap in contiguous run */
11514445fffbSMatthew Ahrens 	char		*cb_firstsnap;
115219b94df9SMatthew Ahrens 	/* previous snap in contiguous run */
11534445fffbSMatthew Ahrens 	char		*cb_prevsnap;
115419b94df9SMatthew Ahrens 	int64_t		cb_snapused;
115519b94df9SMatthew Ahrens 	char		*cb_snapspec;
115678f17100SMatthew Ahrens 	char		*cb_bookmark;
1157fa9e4066Sahrens } destroy_cbdata_t;
1158fa9e4066Sahrens 
1159fa9e4066Sahrens /*
1160fa9e4066Sahrens  * Check for any dependents based on the '-r' or '-R' flags.
1161fa9e4066Sahrens  */
1162fa9e4066Sahrens static int
destroy_check_dependent(zfs_handle_t * zhp,void * data)1163fa9e4066Sahrens destroy_check_dependent(zfs_handle_t *zhp, void *data)
1164fa9e4066Sahrens {
1165fa9e4066Sahrens 	destroy_cbdata_t *cbp = data;
1166fa9e4066Sahrens 	const char *tname = zfs_get_name(cbp->cb_target);
1167fa9e4066Sahrens 	const char *name = zfs_get_name(zhp);
1168fa9e4066Sahrens 
1169fa9e4066Sahrens 	if (strncmp(tname, name, strlen(tname)) == 0 &&
1170fa9e4066Sahrens 	    (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) {
1171fa9e4066Sahrens 		/*
1172fa9e4066Sahrens 		 * This is a direct descendant, not a clone somewhere else in
1173fa9e4066Sahrens 		 * the hierarchy.
1174fa9e4066Sahrens 		 */
1175fa9e4066Sahrens 		if (cbp->cb_recurse)
1176fa9e4066Sahrens 			goto out;
1177fa9e4066Sahrens 
1178fa9e4066Sahrens 		if (cbp->cb_first) {
1179fa9e4066Sahrens 			(void) fprintf(stderr, gettext("cannot destroy '%s': "
1180fa9e4066Sahrens 			    "%s has children\n"),
1181fa9e4066Sahrens 			    zfs_get_name(cbp->cb_target),
1182fa9e4066Sahrens 			    zfs_type_to_name(zfs_get_type(cbp->cb_target)));
1183fa9e4066Sahrens 			(void) fprintf(stderr, gettext("use '-r' to destroy "
1184fa9e4066Sahrens 			    "the following datasets:\n"));
118599653d4eSeschrock 			cbp->cb_first = B_FALSE;
118619b94df9SMatthew Ahrens 			cbp->cb_error = B_TRUE;
1187fa9e4066Sahrens 		}
1188fa9e4066Sahrens 
1189fa9e4066Sahrens 		(void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
1190fa9e4066Sahrens 	} else {
1191fa9e4066Sahrens 		/*
1192fa9e4066Sahrens 		 * This is a clone.  We only want to report this if the '-r'
1193fa9e4066Sahrens 		 * wasn't specified, or the target is a snapshot.
1194fa9e4066Sahrens 		 */
1195fa9e4066Sahrens 		if (!cbp->cb_recurse &&
1196fa9e4066Sahrens 		    zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT)
1197fa9e4066Sahrens 			goto out;
1198fa9e4066Sahrens 
1199fa9e4066Sahrens 		if (cbp->cb_first) {
1200fa9e4066Sahrens 			(void) fprintf(stderr, gettext("cannot destroy '%s': "
1201fa9e4066Sahrens 			    "%s has dependent clones\n"),
1202fa9e4066Sahrens 			    zfs_get_name(cbp->cb_target),
1203fa9e4066Sahrens 			    zfs_type_to_name(zfs_get_type(cbp->cb_target)));
1204fa9e4066Sahrens 			(void) fprintf(stderr, gettext("use '-R' to destroy "
1205fa9e4066Sahrens 			    "the following datasets:\n"));
120699653d4eSeschrock 			cbp->cb_first = B_FALSE;
120719b94df9SMatthew Ahrens 			cbp->cb_error = B_TRUE;
120819b94df9SMatthew Ahrens 			cbp->cb_dryrun = B_TRUE;
1209fa9e4066Sahrens 		}
1210fa9e4066Sahrens 
1211fa9e4066Sahrens 		(void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
1212fa9e4066Sahrens 	}
1213fa9e4066Sahrens 
1214fa9e4066Sahrens out:
1215fa9e4066Sahrens 	zfs_close(zhp);
1216fa9e4066Sahrens 	return (0);
1217fa9e4066Sahrens }
1218fa9e4066Sahrens 
1219fa9e4066Sahrens static int
destroy_callback(zfs_handle_t * zhp,void * data)1220fa9e4066Sahrens destroy_callback(zfs_handle_t *zhp, void *data)
1221fa9e4066Sahrens {
122219b94df9SMatthew Ahrens 	destroy_cbdata_t *cb = data;
122319b94df9SMatthew Ahrens 	const char *name = zfs_get_name(zhp);
122419b94df9SMatthew Ahrens 
122519b94df9SMatthew Ahrens 	if (cb->cb_verbose) {
122619b94df9SMatthew Ahrens 		if (cb->cb_parsable) {
122719b94df9SMatthew Ahrens 			(void) printf("destroy\t%s\n", name);
122819b94df9SMatthew Ahrens 		} else if (cb->cb_dryrun) {
122919b94df9SMatthew Ahrens 			(void) printf(gettext("would destroy %s\n"),
123019b94df9SMatthew Ahrens 			    name);
123119b94df9SMatthew Ahrens 		} else {
123219b94df9SMatthew Ahrens 			(void) printf(gettext("will destroy %s\n"),
123319b94df9SMatthew Ahrens 			    name);
123419b94df9SMatthew Ahrens 		}
123519b94df9SMatthew Ahrens 	}
1236fa9e4066Sahrens 
1237fa9e4066Sahrens 	/*
1238fa9e4066Sahrens 	 * Ignore pools (which we've already flagged as an error before getting
1239681d9761SEric Taylor 	 * here).
1240fa9e4066Sahrens 	 */
1241fa9e4066Sahrens 	if (strchr(zfs_get_name(zhp), '/') == NULL &&
1242fa9e4066Sahrens 	    zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
1243fa9e4066Sahrens 		zfs_close(zhp);
1244fa9e4066Sahrens 		return (0);
1245fa9e4066Sahrens 	}
12463b2aab18SMatthew Ahrens 	if (cb->cb_dryrun) {
12473b2aab18SMatthew Ahrens 		zfs_close(zhp);
12483b2aab18SMatthew Ahrens 		return (0);
12493b2aab18SMatthew Ahrens 	}
1250fa9e4066Sahrens 
12513b2aab18SMatthew Ahrens 	/*
12523b2aab18SMatthew Ahrens 	 * We batch up all contiguous snapshots (even of different
12533b2aab18SMatthew Ahrens 	 * filesystems) and destroy them with one ioctl.  We can't
12543b2aab18SMatthew Ahrens 	 * simply do all snap deletions and then all fs deletions,
12553b2aab18SMatthew Ahrens 	 * because we must delete a clone before its origin.
12563b2aab18SMatthew Ahrens 	 */
12573b2aab18SMatthew Ahrens 	if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) {
12583b2aab18SMatthew Ahrens 		fnvlist_add_boolean(cb->cb_batchedsnaps, name);
12593b2aab18SMatthew Ahrens 	} else {
12603b2aab18SMatthew Ahrens 		int error = zfs_destroy_snaps_nvl(g_zfs,
12613b2aab18SMatthew Ahrens 		    cb->cb_batchedsnaps, B_FALSE);
12623b2aab18SMatthew Ahrens 		fnvlist_free(cb->cb_batchedsnaps);
12633b2aab18SMatthew Ahrens 		cb->cb_batchedsnaps = fnvlist_alloc();
12643b2aab18SMatthew Ahrens 
12653b2aab18SMatthew Ahrens 		if (error != 0 ||
12663b2aab18SMatthew Ahrens 		    zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 ||
126719b94df9SMatthew Ahrens 		    zfs_destroy(zhp, cb->cb_defer_destroy) != 0) {
1268fa9e4066Sahrens 			zfs_close(zhp);
1269fa9e4066Sahrens 			return (-1);
1270fa9e4066Sahrens 		}
127119b94df9SMatthew Ahrens 	}
1272fa9e4066Sahrens 
1273fa9e4066Sahrens 	zfs_close(zhp);
1274fa9e4066Sahrens 	return (0);
1275fa9e4066Sahrens }
1276fa9e4066Sahrens 
12771d452cf5Sahrens static int
destroy_print_cb(zfs_handle_t * zhp,void * arg)127819b94df9SMatthew Ahrens destroy_print_cb(zfs_handle_t *zhp, void *arg)
12791d452cf5Sahrens {
128019b94df9SMatthew Ahrens 	destroy_cbdata_t *cb = arg;
128119b94df9SMatthew Ahrens 	const char *name = zfs_get_name(zhp);
128219b94df9SMatthew Ahrens 	int err = 0;
12831d452cf5Sahrens 
128419b94df9SMatthew Ahrens 	if (nvlist_exists(cb->cb_nvl, name)) {
128519b94df9SMatthew Ahrens 		if (cb->cb_firstsnap == NULL)
12864445fffbSMatthew Ahrens 			cb->cb_firstsnap = strdup(name);
128719b94df9SMatthew Ahrens 		if (cb->cb_prevsnap != NULL)
12884445fffbSMatthew Ahrens 			free(cb->cb_prevsnap);
128919b94df9SMatthew Ahrens 		/* this snap continues the current range */
12904445fffbSMatthew Ahrens 		cb->cb_prevsnap = strdup(name);
12914445fffbSMatthew Ahrens 		if (cb->cb_firstsnap == NULL || cb->cb_prevsnap == NULL)
12924445fffbSMatthew Ahrens 			nomem();
129319b94df9SMatthew Ahrens 		if (cb->cb_verbose) {
129419b94df9SMatthew Ahrens 			if (cb->cb_parsable) {
129519b94df9SMatthew Ahrens 				(void) printf("destroy\t%s\n", name);
129619b94df9SMatthew Ahrens 			} else if (cb->cb_dryrun) {
129719b94df9SMatthew Ahrens 				(void) printf(gettext("would destroy %s\n"),
129819b94df9SMatthew Ahrens 				    name);
129919b94df9SMatthew Ahrens 			} else {
130019b94df9SMatthew Ahrens 				(void) printf(gettext("will destroy %s\n"),
130119b94df9SMatthew Ahrens 				    name);
130219b94df9SMatthew Ahrens 			}
130319b94df9SMatthew Ahrens 		}
130419b94df9SMatthew Ahrens 	} else if (cb->cb_firstsnap != NULL) {
130519b94df9SMatthew Ahrens 		/* end of this range */
130619b94df9SMatthew Ahrens 		uint64_t used = 0;
13074445fffbSMatthew Ahrens 		err = lzc_snaprange_space(cb->cb_firstsnap,
130819b94df9SMatthew Ahrens 		    cb->cb_prevsnap, &used);
130919b94df9SMatthew Ahrens 		cb->cb_snapused += used;
13104445fffbSMatthew Ahrens 		free(cb->cb_firstsnap);
131119b94df9SMatthew Ahrens 		cb->cb_firstsnap = NULL;
13124445fffbSMatthew Ahrens 		free(cb->cb_prevsnap);
131319b94df9SMatthew Ahrens 		cb->cb_prevsnap = NULL;
131419b94df9SMatthew Ahrens 	}
131519b94df9SMatthew Ahrens 	zfs_close(zhp);
131619b94df9SMatthew Ahrens 	return (err);
131719b94df9SMatthew Ahrens }
13181d452cf5Sahrens 
131919b94df9SMatthew Ahrens static int
destroy_print_snapshots(zfs_handle_t * fs_zhp,destroy_cbdata_t * cb)132019b94df9SMatthew Ahrens destroy_print_snapshots(zfs_handle_t *fs_zhp, destroy_cbdata_t *cb)
132119b94df9SMatthew Ahrens {
132205c998d6SRichard Lowe 	int err = 0;
132319b94df9SMatthew Ahrens 	assert(cb->cb_firstsnap == NULL);
132419b94df9SMatthew Ahrens 	assert(cb->cb_prevsnap == NULL);
132519b94df9SMatthew Ahrens 	err = zfs_iter_snapshots_sorted(fs_zhp, destroy_print_cb, cb);
132619b94df9SMatthew Ahrens 	if (cb->cb_firstsnap != NULL) {
132719b94df9SMatthew Ahrens 		uint64_t used = 0;
132819b94df9SMatthew Ahrens 		if (err == 0) {
13294445fffbSMatthew Ahrens 			err = lzc_snaprange_space(cb->cb_firstsnap,
133019b94df9SMatthew Ahrens 			    cb->cb_prevsnap, &used);
133119b94df9SMatthew Ahrens 		}
133219b94df9SMatthew Ahrens 		cb->cb_snapused += used;
13334445fffbSMatthew Ahrens 		free(cb->cb_firstsnap);
133419b94df9SMatthew Ahrens 		cb->cb_firstsnap = NULL;
13354445fffbSMatthew Ahrens 		free(cb->cb_prevsnap);
133619b94df9SMatthew Ahrens 		cb->cb_prevsnap = NULL;
133719b94df9SMatthew Ahrens 	}
133819b94df9SMatthew Ahrens 	return (err);
133919b94df9SMatthew Ahrens }
134019b94df9SMatthew Ahrens 
134119b94df9SMatthew Ahrens static int
snapshot_to_nvl_cb(zfs_handle_t * zhp,void * arg)134219b94df9SMatthew Ahrens snapshot_to_nvl_cb(zfs_handle_t *zhp, void *arg)
134319b94df9SMatthew Ahrens {
134419b94df9SMatthew Ahrens 	destroy_cbdata_t *cb = arg;
134519b94df9SMatthew Ahrens 	int err = 0;
134619b94df9SMatthew Ahrens 
134719b94df9SMatthew Ahrens 	/* Check for clones. */
134865fec9f6SChristopher Siden 	if (!cb->cb_doclones && !cb->cb_defer_destroy) {
134919b94df9SMatthew Ahrens 		cb->cb_target = zhp;
135019b94df9SMatthew Ahrens 		cb->cb_first = B_TRUE;
135119b94df9SMatthew Ahrens 		err = zfs_iter_dependents(zhp, B_TRUE,
135219b94df9SMatthew Ahrens 		    destroy_check_dependent, cb);
135319b94df9SMatthew Ahrens 	}
135419b94df9SMatthew Ahrens 
135519b94df9SMatthew Ahrens 	if (err == 0) {
135619b94df9SMatthew Ahrens 		if (nvlist_add_boolean(cb->cb_nvl, zfs_get_name(zhp)))
135719b94df9SMatthew Ahrens 			nomem();
135819b94df9SMatthew Ahrens 	}
135919b94df9SMatthew Ahrens 	zfs_close(zhp);
136019b94df9SMatthew Ahrens 	return (err);
136119b94df9SMatthew Ahrens }
136219b94df9SMatthew Ahrens 
136319b94df9SMatthew Ahrens static int
gather_snapshots(zfs_handle_t * zhp,void * arg)136419b94df9SMatthew Ahrens gather_snapshots(zfs_handle_t *zhp, void *arg)
136519b94df9SMatthew Ahrens {
136619b94df9SMatthew Ahrens 	destroy_cbdata_t *cb = arg;
136719b94df9SMatthew Ahrens 	int err = 0;
136819b94df9SMatthew Ahrens 
136919b94df9SMatthew Ahrens 	err = zfs_iter_snapspec(zhp, cb->cb_snapspec, snapshot_to_nvl_cb, cb);
137019b94df9SMatthew Ahrens 	if (err == ENOENT)
137119b94df9SMatthew Ahrens 		err = 0;
137219b94df9SMatthew Ahrens 	if (err != 0)
137319b94df9SMatthew Ahrens 		goto out;
137419b94df9SMatthew Ahrens 
137519b94df9SMatthew Ahrens 	if (cb->cb_verbose) {
137619b94df9SMatthew Ahrens 		err = destroy_print_snapshots(zhp, cb);
137719b94df9SMatthew Ahrens 		if (err != 0)
137819b94df9SMatthew Ahrens 			goto out;
137919b94df9SMatthew Ahrens 	}
138019b94df9SMatthew Ahrens 
138119b94df9SMatthew Ahrens 	if (cb->cb_recurse)
138219b94df9SMatthew Ahrens 		err = zfs_iter_filesystems(zhp, gather_snapshots, cb);
138319b94df9SMatthew Ahrens 
138419b94df9SMatthew Ahrens out:
138519b94df9SMatthew Ahrens 	zfs_close(zhp);
138619b94df9SMatthew Ahrens 	return (err);
138719b94df9SMatthew Ahrens }
138819b94df9SMatthew Ahrens 
138919b94df9SMatthew Ahrens static int
destroy_clones(destroy_cbdata_t * cb)139019b94df9SMatthew Ahrens destroy_clones(destroy_cbdata_t *cb)
139119b94df9SMatthew Ahrens {
139219b94df9SMatthew Ahrens 	nvpair_t *pair;
139319b94df9SMatthew Ahrens 	for (pair = nvlist_next_nvpair(cb->cb_nvl, NULL);
139419b94df9SMatthew Ahrens 	    pair != NULL;
139519b94df9SMatthew Ahrens 	    pair = nvlist_next_nvpair(cb->cb_nvl, pair)) {
139619b94df9SMatthew Ahrens 		zfs_handle_t *zhp = zfs_open(g_zfs, nvpair_name(pair),
139719b94df9SMatthew Ahrens 		    ZFS_TYPE_SNAPSHOT);
139819b94df9SMatthew Ahrens 		if (zhp != NULL) {
139919b94df9SMatthew Ahrens 			boolean_t defer = cb->cb_defer_destroy;
140005c998d6SRichard Lowe 			int err = 0;
140119b94df9SMatthew Ahrens 
14021d452cf5Sahrens 			/*
140319b94df9SMatthew Ahrens 			 * We can't defer destroy non-snapshots, so set it to
140419b94df9SMatthew Ahrens 			 * false while destroying the clones.
14051d452cf5Sahrens 			 */
140619b94df9SMatthew Ahrens 			cb->cb_defer_destroy = B_FALSE;
140719b94df9SMatthew Ahrens 			err = zfs_iter_dependents(zhp, B_FALSE,
140819b94df9SMatthew Ahrens 			    destroy_callback, cb);
140919b94df9SMatthew Ahrens 			cb->cb_defer_destroy = defer;
14103ccfa83cSahrens 			zfs_close(zhp);
141119b94df9SMatthew Ahrens 			if (err != 0)
141219b94df9SMatthew Ahrens 				return (err);
14133bb79becSeschrock 		}
14141d452cf5Sahrens 	}
141519b94df9SMatthew Ahrens 	return (0);
14161d452cf5Sahrens }
1417fa9e4066Sahrens 
1418fa9e4066Sahrens static int
zfs_do_destroy(int argc,char ** argv)1419fa9e4066Sahrens zfs_do_destroy(int argc, char **argv)
1420fa9e4066Sahrens {
1421fa9e4066Sahrens 	destroy_cbdata_t cb = { 0 };
14223b2aab18SMatthew Ahrens 	int rv = 0;
14233b2aab18SMatthew Ahrens 	int err = 0;
1424fa9e4066Sahrens 	int c;
14253b2aab18SMatthew Ahrens 	zfs_handle_t *zhp = NULL;
142678f17100SMatthew Ahrens 	char *at, *pound;
1427922d9a97SChris Kirby 	zfs_type_t type = ZFS_TYPE_DATASET;
1428fa9e4066Sahrens 
1429fa9e4066Sahrens 	/* check options */
143019b94df9SMatthew Ahrens 	while ((c = getopt(argc, argv, "vpndfrR")) != -1) {
1431fa9e4066Sahrens 		switch (c) {
143219b94df9SMatthew Ahrens 		case 'v':
143319b94df9SMatthew Ahrens 			cb.cb_verbose = B_TRUE;
143419b94df9SMatthew Ahrens 			break;
143519b94df9SMatthew Ahrens 		case 'p':
143619b94df9SMatthew Ahrens 			cb.cb_verbose = B_TRUE;
143719b94df9SMatthew Ahrens 			cb.cb_parsable = B_TRUE;
143819b94df9SMatthew Ahrens 			break;
143919b94df9SMatthew Ahrens 		case 'n':
144019b94df9SMatthew Ahrens 			cb.cb_dryrun = B_TRUE;
144119b94df9SMatthew Ahrens 			break;
1442842727c2SChris Kirby 		case 'd':
1443842727c2SChris Kirby 			cb.cb_defer_destroy = B_TRUE;
1444922d9a97SChris Kirby 			type = ZFS_TYPE_SNAPSHOT;
1445842727c2SChris Kirby 			break;
1446fa9e4066Sahrens 		case 'f':
144719b94df9SMatthew Ahrens 			cb.cb_force = B_TRUE;
1448fa9e4066Sahrens 			break;
1449fa9e4066Sahrens 		case 'r':
145019b94df9SMatthew Ahrens 			cb.cb_recurse = B_TRUE;
1451fa9e4066Sahrens 			break;
1452fa9e4066Sahrens 		case 'R':
145319b94df9SMatthew Ahrens 			cb.cb_recurse = B_TRUE;
145419b94df9SMatthew Ahrens 			cb.cb_doclones = B_TRUE;
1455fa9e4066Sahrens 			break;
1456fa9e4066Sahrens 		case '?':
1457fa9e4066Sahrens 		default:
1458fa9e4066Sahrens 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
1459fa9e4066Sahrens 			    optopt);
146099653d4eSeschrock 			usage(B_FALSE);
1461fa9e4066Sahrens 		}
1462fa9e4066Sahrens 	}
1463fa9e4066Sahrens 
1464fa9e4066Sahrens 	argc -= optind;
1465fa9e4066Sahrens 	argv += optind;
1466fa9e4066Sahrens 
1467fa9e4066Sahrens 	/* check number of arguments */
1468fa9e4066Sahrens 	if (argc == 0) {
146919b94df9SMatthew Ahrens 		(void) fprintf(stderr, gettext("missing dataset argument\n"));
147099653d4eSeschrock 		usage(B_FALSE);
1471fa9e4066Sahrens 	}
1472fa9e4066Sahrens 	if (argc > 1) {
1473fa9e4066Sahrens 		(void) fprintf(stderr, gettext("too many arguments\n"));
147499653d4eSeschrock 		usage(B_FALSE);
1475fa9e4066Sahrens 	}
1476fa9e4066Sahrens 
147719b94df9SMatthew Ahrens 	at = strchr(argv[0], '@');
147878f17100SMatthew Ahrens 	pound = strchr(argv[0], '#');
147919b94df9SMatthew Ahrens 	if (at != NULL) {
14801d452cf5Sahrens 
148119b94df9SMatthew Ahrens 		/* Build the list of snaps to destroy in cb_nvl. */
14823b2aab18SMatthew Ahrens 		cb.cb_nvl = fnvlist_alloc();
148319b94df9SMatthew Ahrens 
148419b94df9SMatthew Ahrens 		*at = '\0';
148519b94df9SMatthew Ahrens 		zhp = zfs_open(g_zfs, argv[0],
148619b94df9SMatthew Ahrens 		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
148719b94df9SMatthew Ahrens 		if (zhp == NULL)
14881d452cf5Sahrens 			return (1);
14891d452cf5Sahrens 
149019b94df9SMatthew Ahrens 		cb.cb_snapspec = at + 1;
149119b94df9SMatthew Ahrens 		if (gather_snapshots(zfs_handle_dup(zhp), &cb) != 0 ||
149219b94df9SMatthew Ahrens 		    cb.cb_error) {
14933b2aab18SMatthew Ahrens 			rv = 1;
14943b2aab18SMatthew Ahrens 			goto out;
14953bb79becSeschrock 		}
14961d452cf5Sahrens 
149719b94df9SMatthew Ahrens 		if (nvlist_empty(cb.cb_nvl)) {
149819b94df9SMatthew Ahrens 			(void) fprintf(stderr, gettext("could not find any "
149919b94df9SMatthew Ahrens 			    "snapshots to destroy; check snapshot names.\n"));
15003b2aab18SMatthew Ahrens 			rv = 1;
15013b2aab18SMatthew Ahrens 			goto out;
15021d452cf5Sahrens 		}
15031d452cf5Sahrens 
150419b94df9SMatthew Ahrens 		if (cb.cb_verbose) {
150519b94df9SMatthew Ahrens 			char buf[16];
15066520eed5SToomas Soome 			zfs_nicebytes(cb.cb_snapused, buf, sizeof (buf));
150719b94df9SMatthew Ahrens 			if (cb.cb_parsable) {
150819b94df9SMatthew Ahrens 				(void) printf("reclaim\t%llu\n",
150919b94df9SMatthew Ahrens 				    cb.cb_snapused);
151019b94df9SMatthew Ahrens 			} else if (cb.cb_dryrun) {
151119b94df9SMatthew Ahrens 				(void) printf(gettext("would reclaim %s\n"),
151219b94df9SMatthew Ahrens 				    buf);
151319b94df9SMatthew Ahrens 			} else {
151419b94df9SMatthew Ahrens 				(void) printf(gettext("will reclaim %s\n"),
151519b94df9SMatthew Ahrens 				    buf);
151619b94df9SMatthew Ahrens 			}
151719b94df9SMatthew Ahrens 		}
151819b94df9SMatthew Ahrens 
151919b94df9SMatthew Ahrens 		if (!cb.cb_dryrun) {
15203b2aab18SMatthew Ahrens 			if (cb.cb_doclones) {
15213b2aab18SMatthew Ahrens 				cb.cb_batchedsnaps = fnvlist_alloc();
152219b94df9SMatthew Ahrens 				err = destroy_clones(&cb);
152319b94df9SMatthew Ahrens 				if (err == 0) {
15243b2aab18SMatthew Ahrens 					err = zfs_destroy_snaps_nvl(g_zfs,
15253b2aab18SMatthew Ahrens 					    cb.cb_batchedsnaps, B_FALSE);
15263b2aab18SMatthew Ahrens 				}
15273b2aab18SMatthew Ahrens 				if (err != 0) {
15283b2aab18SMatthew Ahrens 					rv = 1;
15293b2aab18SMatthew Ahrens 					goto out;
15303b2aab18SMatthew Ahrens 				}
15313b2aab18SMatthew Ahrens 			}
15323b2aab18SMatthew Ahrens 			if (err == 0) {
15333b2aab18SMatthew Ahrens 				err = zfs_destroy_snaps_nvl(g_zfs, cb.cb_nvl,
153419b94df9SMatthew Ahrens 				    cb.cb_defer_destroy);
153519b94df9SMatthew Ahrens 			}
153619b94df9SMatthew Ahrens 		}
153719b94df9SMatthew Ahrens 
153819b94df9SMatthew Ahrens 		if (err != 0)
15393b2aab18SMatthew Ahrens 			rv = 1;
154078f17100SMatthew Ahrens 	} else if (pound != NULL) {
154178f17100SMatthew Ahrens 		int err;
154278f17100SMatthew Ahrens 		nvlist_t *nvl;
154378f17100SMatthew Ahrens 
154478f17100SMatthew Ahrens 		if (cb.cb_dryrun) {
154578f17100SMatthew Ahrens 			(void) fprintf(stderr,
154678f17100SMatthew Ahrens 			    "dryrun is not supported with bookmark\n");
154778f17100SMatthew Ahrens 			return (-1);
154878f17100SMatthew Ahrens 		}
154978f17100SMatthew Ahrens 
155078f17100SMatthew Ahrens 		if (cb.cb_defer_destroy) {
155178f17100SMatthew Ahrens 			(void) fprintf(stderr,
155278f17100SMatthew Ahrens 			    "defer destroy is not supported with bookmark\n");
155378f17100SMatthew Ahrens 			return (-1);
155478f17100SMatthew Ahrens 		}
155578f17100SMatthew Ahrens 
155678f17100SMatthew Ahrens 		if (cb.cb_recurse) {
155778f17100SMatthew Ahrens 			(void) fprintf(stderr,
155878f17100SMatthew Ahrens 			    "recursive is not supported with bookmark\n");
155978f17100SMatthew Ahrens 			return (-1);
156078f17100SMatthew Ahrens 		}
156178f17100SMatthew Ahrens 
156278f17100SMatthew Ahrens 		if (!zfs_bookmark_exists(argv[0])) {
156378f17100SMatthew Ahrens 			(void) fprintf(stderr, gettext("bookmark '%s' "
156478f17100SMatthew Ahrens 			    "does not exist.\n"), argv[0]);
156578f17100SMatthew Ahrens 			return (1);
156678f17100SMatthew Ahrens 		}
156778f17100SMatthew Ahrens 
156878f17100SMatthew Ahrens 		nvl = fnvlist_alloc();
156978f17100SMatthew Ahrens 		fnvlist_add_boolean(nvl, argv[0]);
157078f17100SMatthew Ahrens 
157178f17100SMatthew Ahrens 		err = lzc_destroy_bookmarks(nvl, NULL);
157278f17100SMatthew Ahrens 		if (err != 0) {
157378f17100SMatthew Ahrens 			(void) zfs_standard_error(g_zfs, err,
157478f17100SMatthew Ahrens 			    "cannot destroy bookmark");
157578f17100SMatthew Ahrens 		}
157678f17100SMatthew Ahrens 
157778f17100SMatthew Ahrens 		nvlist_free(cb.cb_nvl);
157878f17100SMatthew Ahrens 
157978f17100SMatthew Ahrens 		return (err);
158019b94df9SMatthew Ahrens 	} else {
1581fa9e4066Sahrens 		/* Open the given dataset */
1582922d9a97SChris Kirby 		if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL)
1583fa9e4066Sahrens 			return (1);
1584fa9e4066Sahrens 
1585fa9e4066Sahrens 		cb.cb_target = zhp;
1586fa9e4066Sahrens 
1587fa9e4066Sahrens 		/*
1588fa9e4066Sahrens 		 * Perform an explicit check for pools before going any further.
1589fa9e4066Sahrens 		 */
1590fa9e4066Sahrens 		if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL &&
1591fa9e4066Sahrens 		    zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
1592fa9e4066Sahrens 			(void) fprintf(stderr, gettext("cannot destroy '%s': "
1593fa9e4066Sahrens 			    "operation does not apply to pools\n"),
1594fa9e4066Sahrens 			    zfs_get_name(zhp));
1595fa9e4066Sahrens 			(void) fprintf(stderr, gettext("use 'zfs destroy -r "
1596fa9e4066Sahrens 			    "%s' to destroy all datasets in the pool\n"),
1597fa9e4066Sahrens 			    zfs_get_name(zhp));
1598fa9e4066Sahrens 			(void) fprintf(stderr, gettext("use 'zpool destroy %s' "
1599fa9e4066Sahrens 			    "to destroy the pool itself\n"), zfs_get_name(zhp));
16003b2aab18SMatthew Ahrens 			rv = 1;
16013b2aab18SMatthew Ahrens 			goto out;
1602fa9e4066Sahrens 		}
1603fa9e4066Sahrens 
1604fa9e4066Sahrens 		/*
1605fa9e4066Sahrens 		 * Check for any dependents and/or clones.
1606fa9e4066Sahrens 		 */
160799653d4eSeschrock 		cb.cb_first = B_TRUE;
160819b94df9SMatthew Ahrens 		if (!cb.cb_doclones &&
16093bb79becSeschrock 		    zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent,
16103bb79becSeschrock 		    &cb) != 0) {
16113b2aab18SMatthew Ahrens 			rv = 1;
16123b2aab18SMatthew Ahrens 			goto out;
16133bb79becSeschrock 		}
1614fa9e4066Sahrens 
161519b94df9SMatthew Ahrens 		if (cb.cb_error) {
16163b2aab18SMatthew Ahrens 			rv = 1;
16173b2aab18SMatthew Ahrens 			goto out;
161819b94df9SMatthew Ahrens 		}
161919b94df9SMatthew Ahrens 
16203b2aab18SMatthew Ahrens 		cb.cb_batchedsnaps = fnvlist_alloc();
162119b94df9SMatthew Ahrens 		if (zfs_iter_dependents(zhp, B_FALSE, destroy_callback,
162219b94df9SMatthew Ahrens 		    &cb) != 0) {
16233b2aab18SMatthew Ahrens 			rv = 1;
16243b2aab18SMatthew Ahrens 			goto out;
1625fa9e4066Sahrens 		}
1626fa9e4066Sahrens 
1627fa9e4066Sahrens 		/*
162819b94df9SMatthew Ahrens 		 * Do the real thing.  The callback will close the
162919b94df9SMatthew Ahrens 		 * handle regardless of whether it succeeds or not.
1630fa9e4066Sahrens 		 */
16313b2aab18SMatthew Ahrens 		err = destroy_callback(zhp, &cb);
16323b2aab18SMatthew Ahrens 		zhp = NULL;
16333b2aab18SMatthew Ahrens 		if (err == 0) {
16343b2aab18SMatthew Ahrens 			err = zfs_destroy_snaps_nvl(g_zfs,
16353b2aab18SMatthew Ahrens 			    cb.cb_batchedsnaps, cb.cb_defer_destroy);
16363b2aab18SMatthew Ahrens 		}
16373b2aab18SMatthew Ahrens 		if (err != 0)
16383b2aab18SMatthew Ahrens 			rv = 1;
163919b94df9SMatthew Ahrens 	}
16403bb79becSeschrock 
16413b2aab18SMatthew Ahrens out:
16423b2aab18SMatthew Ahrens 	fnvlist_free(cb.cb_batchedsnaps);
16433b2aab18SMatthew Ahrens 	fnvlist_free(cb.cb_nvl);
16443b2aab18SMatthew Ahrens 	if (zhp != NULL)
16453b2aab18SMatthew Ahrens 		zfs_close(zhp);
16463b2aab18SMatthew Ahrens 	return (rv);
1647fa9e4066Sahrens }
1648fa9e4066Sahrens 
164992241e0bSTom Erickson static boolean_t
is_recvd_column(zprop_get_cbdata_t * cbp)165092241e0bSTom Erickson is_recvd_column(zprop_get_cbdata_t *cbp)
165192241e0bSTom Erickson {
165292241e0bSTom Erickson 	int i;
165392241e0bSTom Erickson 	zfs_get_column_t col;
165492241e0bSTom Erickson 
165592241e0bSTom Erickson 	for (i = 0; i < ZFS_GET_NCOLS &&
165692241e0bSTom Erickson 	    (col = cbp->cb_columns[i]) != GET_COL_NONE; i++)
165792241e0bSTom Erickson 		if (col == GET_COL_RECVD)
165892241e0bSTom Erickson 			return (B_TRUE);
165992241e0bSTom Erickson 	return (B_FALSE);
166092241e0bSTom Erickson }
166192241e0bSTom Erickson 
1662fa9e4066Sahrens /*
166392241e0bSTom Erickson  * zfs get [-rHp] [-o all | field[,field]...] [-s source[,source]...]
166407ba0419Seschrock  *	< all | property[,property]... > < fs | snap | vol > ...
1665fa9e4066Sahrens  *
1666fa9e4066Sahrens  *	-r	recurse over any child datasets
1667fa9e4066Sahrens  *	-H	scripted mode.  Headers are stripped, and fields are separated
1668fa9e4066Sahrens  *		by tabs instead of spaces.
166992241e0bSTom Erickson  *	-o	Set of fields to display.  One of "name,property,value,
167092241e0bSTom Erickson  *		received,source". Default is "name,property,value,source".
167192241e0bSTom Erickson  *		"all" is an alias for all five.
1672fa9e4066Sahrens  *	-s	Set of sources to allow.  One of
167392241e0bSTom Erickson  *		"local,default,inherited,received,temporary,none".  Default is
167492241e0bSTom Erickson  *		all six.
1675fa9e4066Sahrens  *	-p	Display values in parsable (literal) format.
1676fa9e4066Sahrens  *
1677fa9e4066Sahrens  *  Prints properties for the given datasets.  The user can control which
1678fa9e4066Sahrens  *  columns to display as well as which property types to allow.
1679fa9e4066Sahrens  */
1680fa9e4066Sahrens 
1681fa9e4066Sahrens /*
1682fa9e4066Sahrens  * Invoked to display the properties for a single dataset.
1683fa9e4066Sahrens  */
1684fa9e4066Sahrens static int
get_callback(zfs_handle_t * zhp,void * data)1685fa9e4066Sahrens get_callback(zfs_handle_t *zhp, void *data)
1686fa9e4066Sahrens {
1687fa9e4066Sahrens 	char buf[ZFS_MAXPROPLEN];
168892241e0bSTom Erickson 	char rbuf[ZFS_MAXPROPLEN];
1689990b4856Slling 	zprop_source_t sourcetype;
16909adfa60dSMatthew Ahrens 	char source[ZFS_MAX_DATASET_NAME_LEN];
1691990b4856Slling 	zprop_get_cbdata_t *cbp = data;
169292241e0bSTom Erickson 	nvlist_t *user_props = zfs_get_user_props(zhp);
1693990b4856Slling 	zprop_list_t *pl = cbp->cb_proplist;
1694e9dbad6fSeschrock 	nvlist_t *propval;
1695e9dbad6fSeschrock 	char *strval;
1696e9dbad6fSeschrock 	char *sourceval;
169792241e0bSTom Erickson 	boolean_t received = is_recvd_column(cbp);
1698fa9e4066Sahrens 
1699e9dbad6fSeschrock 	for (; pl != NULL; pl = pl->pl_next) {
170092241e0bSTom Erickson 		char *recvdval = NULL;
1701e9dbad6fSeschrock 		/*
1702e9dbad6fSeschrock 		 * Skip the special fake placeholder.  This will also skip over
1703e9dbad6fSeschrock 		 * the name property when 'all' is specified.
1704e9dbad6fSeschrock 		 */
1705e9dbad6fSeschrock 		if (pl->pl_prop == ZFS_PROP_NAME &&
1706e9dbad6fSeschrock 		    pl == cbp->cb_proplist)
170707ba0419Seschrock 			continue;
1708e9dbad6fSeschrock 
1709990b4856Slling 		if (pl->pl_prop != ZPROP_INVAL) {
1710e9dbad6fSeschrock 			if (zfs_prop_get(zhp, pl->pl_prop, buf,
1711e9dbad6fSeschrock 			    sizeof (buf), &sourcetype, source,
1712e9dbad6fSeschrock 			    sizeof (source),
1713e9dbad6fSeschrock 			    cbp->cb_literal) != 0) {
1714e9dbad6fSeschrock 				if (pl->pl_all)
1715e9dbad6fSeschrock 					continue;
1716b1b8ab34Slling 				if (!zfs_prop_valid_for_type(pl->pl_prop,
17174c2bdae2STim Chase 				    ZFS_TYPE_DATASET, B_FALSE)) {
1718b1b8ab34Slling 					(void) fprintf(stderr,
1719b1b8ab34Slling 					    gettext("No such property '%s'\n"),
1720b1b8ab34Slling 					    zfs_prop_to_name(pl->pl_prop));
1721b1b8ab34Slling 					continue;
1722b1b8ab34Slling 				}
1723990b4856Slling 				sourcetype = ZPROP_SRC_NONE;
1724e9dbad6fSeschrock 				(void) strlcpy(buf, "-", sizeof (buf));
1725fa9e4066Sahrens 			}
1726fa9e4066Sahrens 
172792241e0bSTom Erickson 			if (received && (zfs_prop_get_recvd(zhp,
172892241e0bSTom Erickson 			    zfs_prop_to_name(pl->pl_prop), rbuf, sizeof (rbuf),
172992241e0bSTom Erickson 			    cbp->cb_literal) == 0))
173092241e0bSTom Erickson 				recvdval = rbuf;
173192241e0bSTom Erickson 
1732990b4856Slling 			zprop_print_one_property(zfs_get_name(zhp), cbp,
1733e9dbad6fSeschrock 			    zfs_prop_to_name(pl->pl_prop),
173492241e0bSTom Erickson 			    buf, sourcetype, source, recvdval);
173514843421SMatthew Ahrens 		} else if (zfs_prop_userquota(pl->pl_user_prop)) {
173614843421SMatthew Ahrens 			sourcetype = ZPROP_SRC_LOCAL;
173714843421SMatthew Ahrens 
173814843421SMatthew Ahrens 			if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,
173914843421SMatthew Ahrens 			    buf, sizeof (buf), cbp->cb_literal) != 0) {
174014843421SMatthew Ahrens 				sourcetype = ZPROP_SRC_NONE;
174114843421SMatthew Ahrens 				(void) strlcpy(buf, "-", sizeof (buf));
174214843421SMatthew Ahrens 			}
174314843421SMatthew Ahrens 
174414843421SMatthew Ahrens 			zprop_print_one_property(zfs_get_name(zhp), cbp,
174592241e0bSTom Erickson 			    pl->pl_user_prop, buf, sourcetype, source, NULL);
174619b94df9SMatthew Ahrens 		} else if (zfs_prop_written(pl->pl_user_prop)) {
174719b94df9SMatthew Ahrens 			sourcetype = ZPROP_SRC_LOCAL;
174819b94df9SMatthew Ahrens 
174919b94df9SMatthew Ahrens 			if (zfs_prop_get_written(zhp, pl->pl_user_prop,
175019b94df9SMatthew Ahrens 			    buf, sizeof (buf), cbp->cb_literal) != 0) {
175119b94df9SMatthew Ahrens 				sourcetype = ZPROP_SRC_NONE;
175219b94df9SMatthew Ahrens 				(void) strlcpy(buf, "-", sizeof (buf));
175319b94df9SMatthew Ahrens 			}
175419b94df9SMatthew Ahrens 
175519b94df9SMatthew Ahrens 			zprop_print_one_property(zfs_get_name(zhp), cbp,
175619b94df9SMatthew Ahrens 			    pl->pl_user_prop, buf, sourcetype, source, NULL);
1757e9dbad6fSeschrock 		} else {
175892241e0bSTom Erickson 			if (nvlist_lookup_nvlist(user_props,
1759e9dbad6fSeschrock 			    pl->pl_user_prop, &propval) != 0) {
1760e9dbad6fSeschrock 				if (pl->pl_all)
1761e9dbad6fSeschrock 					continue;
1762990b4856Slling 				sourcetype = ZPROP_SRC_NONE;
1763e9dbad6fSeschrock 				strval = "-";
1764e9dbad6fSeschrock 			} else {
1765e9dbad6fSeschrock 				verify(nvlist_lookup_string(propval,
1766990b4856Slling 				    ZPROP_VALUE, &strval) == 0);
1767e9dbad6fSeschrock 				verify(nvlist_lookup_string(propval,
1768990b4856Slling 				    ZPROP_SOURCE, &sourceval) == 0);
1769e9dbad6fSeschrock 
1770e9dbad6fSeschrock 				if (strcmp(sourceval,
1771e9dbad6fSeschrock 				    zfs_get_name(zhp)) == 0) {
1772990b4856Slling 					sourcetype = ZPROP_SRC_LOCAL;
177392241e0bSTom Erickson 				} else if (strcmp(sourceval,
177492241e0bSTom Erickson 				    ZPROP_SOURCE_VAL_RECVD) == 0) {
177592241e0bSTom Erickson 					sourcetype = ZPROP_SRC_RECEIVED;
1776e9dbad6fSeschrock 				} else {
1777990b4856Slling 					sourcetype = ZPROP_SRC_INHERITED;
1778e9dbad6fSeschrock 					(void) strlcpy(source,
1779e9dbad6fSeschrock 					    sourceval, sizeof (source));
1780e9dbad6fSeschrock 				}
1781e9dbad6fSeschrock 			}
1782e9dbad6fSeschrock 
178392241e0bSTom Erickson 			if (received && (zfs_prop_get_recvd(zhp,
178492241e0bSTom Erickson 			    pl->pl_user_prop, rbuf, sizeof (rbuf),
178592241e0bSTom Erickson 			    cbp->cb_literal) == 0))
178692241e0bSTom Erickson 				recvdval = rbuf;
178792241e0bSTom Erickson 
1788990b4856Slling 			zprop_print_one_property(zfs_get_name(zhp), cbp,
1789e9dbad6fSeschrock 			    pl->pl_user_prop, strval, sourcetype,
179092241e0bSTom Erickson 			    source, recvdval);
1791e9dbad6fSeschrock 		}
1792fa9e4066Sahrens 	}
1793fa9e4066Sahrens 
1794fa9e4066Sahrens 	return (0);
1795fa9e4066Sahrens }
1796fa9e4066Sahrens 
1797fa9e4066Sahrens static int
zfs_do_get(int argc,char ** argv)1798fa9e4066Sahrens zfs_do_get(int argc, char **argv)
1799fa9e4066Sahrens {
1800990b4856Slling 	zprop_get_cbdata_t cb = { 0 };
18015ead3ed9SShampavman 	int i, c, flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
1802edb901aaSMarcel Telka 	int types = ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK;
1803e9dbad6fSeschrock 	char *value, *fields;
180405c998d6SRichard Lowe 	int ret = 0;
1805ae1726b6SChris Gerhard 	int limit = 0;
1806990b4856Slling 	zprop_list_t fake_name = { 0 };
1807fa9e4066Sahrens 
1808fa9e4066Sahrens 	/*
1809fa9e4066Sahrens 	 * Set up default columns and sources.
1810fa9e4066Sahrens 	 */
1811990b4856Slling 	cb.cb_sources = ZPROP_SRC_ALL;
1812fa9e4066Sahrens 	cb.cb_columns[0] = GET_COL_NAME;
1813fa9e4066Sahrens 	cb.cb_columns[1] = GET_COL_PROPERTY;
1814fa9e4066Sahrens 	cb.cb_columns[2] = GET_COL_VALUE;
1815fa9e4066Sahrens 	cb.cb_columns[3] = GET_COL_SOURCE;
1816990b4856Slling 	cb.cb_type = ZFS_TYPE_DATASET;
1817fa9e4066Sahrens 
1818fa9e4066Sahrens 	/* check options */
1819441cac7aSAndrew Stormont 	while ((c = getopt(argc, argv, ":d:o:s:rt:Hp")) != -1) {
1820fa9e4066Sahrens 		switch (c) {
1821fa9e4066Sahrens 		case 'p':
182299653d4eSeschrock 			cb.cb_literal = B_TRUE;
1823fa9e4066Sahrens 			break;
1824ae1726b6SChris Gerhard 		case 'd':
1825ae1726b6SChris Gerhard 			limit = parse_depth(optarg, &flags);
1826ae1726b6SChris Gerhard 			break;
1827fa9e4066Sahrens 		case 'r':
1828d5b5bb25SRich Morris 			flags |= ZFS_ITER_RECURSE;
1829fa9e4066Sahrens 			break;
1830fa9e4066Sahrens 		case 'H':
183199653d4eSeschrock 			cb.cb_scripted = B_TRUE;
1832fa9e4066Sahrens 			break;
1833fa9e4066Sahrens 		case ':':
1834fa9e4066Sahrens 			(void) fprintf(stderr, gettext("missing argument for "
1835fa9e4066Sahrens 			    "'%c' option\n"), optopt);
183699653d4eSeschrock 			usage(B_FALSE);
1837fa9e4066Sahrens 			break;
1838fa9e4066Sahrens 		case 'o':
1839fa9e4066Sahrens 			/*
1840fa9e4066Sahrens 			 * Process the set of columns to display.  We zero out
1841fa9e4066Sahrens 			 * the structure to give us a blank slate.
1842fa9e4066Sahrens 			 */
1843fa9e4066Sahrens 			bzero(&cb.cb_columns, sizeof (cb.cb_columns));
1844fa9e4066Sahrens 			i = 0;
1845fa9e4066Sahrens 			while (*optarg != '\0') {
1846fa9e4066Sahrens 				static char *col_subopts[] =
184792241e0bSTom Erickson 				    { "name", "property", "value", "received",
184892241e0bSTom Erickson 				    "source", "all", NULL };
1849fa9e4066Sahrens 
185092241e0bSTom Erickson 				if (i == ZFS_GET_NCOLS) {
1851fa9e4066Sahrens 					(void) fprintf(stderr, gettext("too "
1852fa9e4066Sahrens 					    "many fields given to -o "
1853fa9e4066Sahrens 					    "option\n"));
185499653d4eSeschrock 					usage(B_FALSE);
1855fa9e4066Sahrens 				}
1856fa9e4066Sahrens 
1857fa9e4066Sahrens 				switch (getsubopt(&optarg, col_subopts,
1858fa9e4066Sahrens 				    &value)) {
1859fa9e4066Sahrens 				case 0:
1860fa9e4066Sahrens 					cb.cb_columns[i++] = GET_COL_NAME;
1861fa9e4066Sahrens 					break;
1862fa9e4066Sahrens 				case 1:
1863fa9e4066Sahrens 					cb.cb_columns[i++] = GET_COL_PROPERTY;
1864fa9e4066Sahrens 					break;
1865fa9e4066Sahrens 				case 2:
1866fa9e4066Sahrens 					cb.cb_columns[i++] = GET_COL_VALUE;
1867fa9e4066Sahrens 					break;
1868fa9e4066Sahrens 				case 3:
186992241e0bSTom Erickson 					cb.cb_columns[i++] = GET_COL_RECVD;
187092241e0bSTom Erickson 					flags |= ZFS_ITER_RECVD_PROPS;
187192241e0bSTom Erickson 					break;
187292241e0bSTom Erickson 				case 4:
1873fa9e4066Sahrens 					cb.cb_columns[i++] = GET_COL_SOURCE;
1874fa9e4066Sahrens 					break;
187592241e0bSTom Erickson 				case 5:
187692241e0bSTom Erickson 					if (i > 0) {
187792241e0bSTom Erickson 						(void) fprintf(stderr,
187892241e0bSTom Erickson 						    gettext("\"all\" conflicts "
187992241e0bSTom Erickson 						    "with specific fields "
188092241e0bSTom Erickson 						    "given to -o option\n"));
188192241e0bSTom Erickson 						usage(B_FALSE);
188292241e0bSTom Erickson 					}
188392241e0bSTom Erickson 					cb.cb_columns[0] = GET_COL_NAME;
188492241e0bSTom Erickson 					cb.cb_columns[1] = GET_COL_PROPERTY;
188592241e0bSTom Erickson 					cb.cb_columns[2] = GET_COL_VALUE;
188692241e0bSTom Erickson 					cb.cb_columns[3] = GET_COL_RECVD;
188792241e0bSTom Erickson 					cb.cb_columns[4] = GET_COL_SOURCE;
188892241e0bSTom Erickson 					flags |= ZFS_ITER_RECVD_PROPS;
188992241e0bSTom Erickson 					i = ZFS_GET_NCOLS;
189092241e0bSTom Erickson 					break;
1891fa9e4066Sahrens 				default:
1892fa9e4066Sahrens 					(void) fprintf(stderr,
1893fa9e4066Sahrens 					    gettext("invalid column name "
1894fa9e4066Sahrens 					    "'%s'\n"), value);
189599653d4eSeschrock 					usage(B_FALSE);
1896fa9e4066Sahrens 				}
1897fa9e4066Sahrens 			}
1898fa9e4066Sahrens 			break;
1899fa9e4066Sahrens 
1900fa9e4066Sahrens 		case 's':
1901fa9e4066Sahrens 			cb.cb_sources = 0;
1902fa9e4066Sahrens 			while (*optarg != '\0') {
1903fa9e4066Sahrens 				static char *source_subopts[] = {
1904fa9e4066Sahrens 					"local", "default", "inherited",
190592241e0bSTom Erickson 					"received", "temporary", "none",
190692241e0bSTom Erickson 					NULL };
1907fa9e4066Sahrens 
1908fa9e4066Sahrens 				switch (getsubopt(&optarg, source_subopts,
1909fa9e4066Sahrens 				    &value)) {
1910fa9e4066Sahrens 				case 0:
1911990b4856Slling 					cb.cb_sources |= ZPROP_SRC_LOCAL;
1912fa9e4066Sahrens 					break;
1913fa9e4066Sahrens 				case 1:
1914990b4856Slling 					cb.cb_sources |= ZPROP_SRC_DEFAULT;
1915fa9e4066Sahrens 					break;
1916fa9e4066Sahrens 				case 2:
1917990b4856Slling 					cb.cb_sources |= ZPROP_SRC_INHERITED;
1918fa9e4066Sahrens 					break;
1919fa9e4066Sahrens 				case 3:
192092241e0bSTom Erickson 					cb.cb_sources |= ZPROP_SRC_RECEIVED;
1921fa9e4066Sahrens 					break;
1922fa9e4066Sahrens 				case 4:
192392241e0bSTom Erickson 					cb.cb_sources |= ZPROP_SRC_TEMPORARY;
192492241e0bSTom Erickson 					break;
192592241e0bSTom Erickson 				case 5:
1926990b4856Slling 					cb.cb_sources |= ZPROP_SRC_NONE;
1927fa9e4066Sahrens 					break;
1928fa9e4066Sahrens 				default:
1929fa9e4066Sahrens 					(void) fprintf(stderr,
1930fa9e4066Sahrens 					    gettext("invalid source "
1931fa9e4066Sahrens 					    "'%s'\n"), value);
193299653d4eSeschrock 					usage(B_FALSE);
1933fa9e4066Sahrens 				}
1934fa9e4066Sahrens 			}
1935fa9e4066Sahrens 			break;
1936fa9e4066Sahrens 
1937441cac7aSAndrew Stormont 		case 't':
1938441cac7aSAndrew Stormont 			types = 0;
1939441cac7aSAndrew Stormont 			flags &= ~ZFS_ITER_PROP_LISTSNAPS;
1940441cac7aSAndrew Stormont 			while (*optarg != '\0') {
1941441cac7aSAndrew Stormont 				static char *type_subopts[] = { "filesystem",
194278f17100SMatthew Ahrens 				    "volume", "snapshot", "bookmark",
194378f17100SMatthew Ahrens 				    "all", NULL };
1944441cac7aSAndrew Stormont 
1945441cac7aSAndrew Stormont 				switch (getsubopt(&optarg, type_subopts,
1946441cac7aSAndrew Stormont 				    &value)) {
1947441cac7aSAndrew Stormont 				case 0:
1948441cac7aSAndrew Stormont 					types |= ZFS_TYPE_FILESYSTEM;
1949441cac7aSAndrew Stormont 					break;
1950441cac7aSAndrew Stormont 				case 1:
1951441cac7aSAndrew Stormont 					types |= ZFS_TYPE_VOLUME;
1952441cac7aSAndrew Stormont 					break;
1953441cac7aSAndrew Stormont 				case 2:
1954441cac7aSAndrew Stormont 					types |= ZFS_TYPE_SNAPSHOT;
1955441cac7aSAndrew Stormont 					break;
1956441cac7aSAndrew Stormont 				case 3:
195778f17100SMatthew Ahrens 					types |= ZFS_TYPE_BOOKMARK;
195878f17100SMatthew Ahrens 					break;
195978f17100SMatthew Ahrens 				case 4:
196078f17100SMatthew Ahrens 					types = ZFS_TYPE_DATASET |
196178f17100SMatthew Ahrens 					    ZFS_TYPE_BOOKMARK;
1962441cac7aSAndrew Stormont 					break;
1963441cac7aSAndrew Stormont 
1964441cac7aSAndrew Stormont 				default:
1965441cac7aSAndrew Stormont 					(void) fprintf(stderr,
1966441cac7aSAndrew Stormont 					    gettext("invalid type '%s'\n"),
1967441cac7aSAndrew Stormont 					    value);
1968441cac7aSAndrew Stormont 					usage(B_FALSE);
1969441cac7aSAndrew Stormont 				}
1970441cac7aSAndrew Stormont 			}
1971441cac7aSAndrew Stormont 			break;
1972441cac7aSAndrew Stormont 
1973fa9e4066Sahrens 		case '?':
1974fa9e4066Sahrens 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
1975fa9e4066Sahrens 			    optopt);
197699653d4eSeschrock 			usage(B_FALSE);
1977fa9e4066Sahrens 		}
1978fa9e4066Sahrens 	}
1979fa9e4066Sahrens 
1980fa9e4066Sahrens 	argc -= optind;
1981fa9e4066Sahrens 	argv += optind;
1982fa9e4066Sahrens 
1983fa9e4066Sahrens 	if (argc < 1) {
1984fa9e4066Sahrens 		(void) fprintf(stderr, gettext("missing property "
1985fa9e4066Sahrens 		    "argument\n"));
198699653d4eSeschrock 		usage(B_FALSE);
1987fa9e4066Sahrens 	}
1988fa9e4066Sahrens 
1989fa9e4066Sahrens 	fields = argv[0];
1990fa9e4066Sahrens 
1991990b4856Slling 	if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)
1992990b4856Slling 	    != 0)
199399653d4eSeschrock 		usage(B_FALSE);
1994fa9e4066Sahrens 
1995fa9e4066Sahrens 	argc--;
1996fa9e4066Sahrens 	argv++;
1997fa9e4066Sahrens 
1998e9dbad6fSeschrock 	/*
1999e9dbad6fSeschrock 	 * As part of zfs_expand_proplist(), we keep track of the maximum column
2000e9dbad6fSeschrock 	 * width for each property.  For the 'NAME' (and 'SOURCE') columns, we
2001e9dbad6fSeschrock 	 * need to know the maximum name length.  However, the user likely did
2002e9dbad6fSeschrock 	 * not specify 'name' as one of the properties to fetch, so we need to
2003e9dbad6fSeschrock 	 * make sure we always include at least this property for
2004e9dbad6fSeschrock 	 * print_get_headers() to work properly.
2005e9dbad6fSeschrock 	 */
2006e9dbad6fSeschrock 	if (cb.cb_proplist != NULL) {
2007e9dbad6fSeschrock 		fake_name.pl_prop = ZFS_PROP_NAME;
2008e9dbad6fSeschrock 		fake_name.pl_width = strlen(gettext("NAME"));
2009e9dbad6fSeschrock 		fake_name.pl_next = cb.cb_proplist;
2010e9dbad6fSeschrock 		cb.cb_proplist = &fake_name;
2011fa9e4066Sahrens 	}
2012fa9e4066Sahrens 
2013e9dbad6fSeschrock 	cb.cb_first = B_TRUE;
2014fa9e4066Sahrens 
2015fa9e4066Sahrens 	/* run for each object */
2016441cac7aSAndrew Stormont 	ret = zfs_for_each(argc, argv, flags, types, NULL,
2017ae1726b6SChris Gerhard 	    &cb.cb_proplist, limit, get_callback, &cb);
2018b6825278Ssjelinek 
2019e9dbad6fSeschrock 	if (cb.cb_proplist == &fake_name)
2020990b4856Slling 		zprop_free_list(fake_name.pl_next);
2021e9dbad6fSeschrock 	else
2022990b4856Slling 		zprop_free_list(cb.cb_proplist);
2023e9dbad6fSeschrock 
2024e9dbad6fSeschrock 	return (ret);
2025fa9e4066Sahrens }
2026fa9e4066Sahrens 
2027fa9e4066Sahrens /*
202892241e0bSTom Erickson  * inherit [-rS] <property> <fs|vol> ...
2029fa9e4066Sahrens  *
2030fa9e4066Sahrens  *	-r	Recurse over all children
203192241e0bSTom Erickson  *	-S	Revert to received value, if any
2032fa9e4066Sahrens  *
2033fa9e4066Sahrens  * For each dataset specified on the command line, inherit the given property
2034fa9e4066Sahrens  * from its parent.  Inheriting a property at the pool level will cause it to
2035fa9e4066Sahrens  * use the default value.  The '-r' flag will recurse over all children, and is
2036fa9e4066Sahrens  * useful for setting a property on a hierarchy-wide basis, regardless of any
2037fa9e4066Sahrens  * local modifications for each dataset.
2038fa9e4066Sahrens  */
203906eeb2adSek110237 
204092241e0bSTom Erickson typedef struct inherit_cbdata {
204192241e0bSTom Erickson 	const char *cb_propname;
204292241e0bSTom Erickson 	boolean_t cb_received;
204392241e0bSTom Erickson } inherit_cbdata_t;
204492241e0bSTom Erickson 
2045fa9e4066Sahrens static int
inherit_recurse_cb(zfs_handle_t * zhp,void * data)2046bb0ade09Sahrens inherit_recurse_cb(zfs_handle_t *zhp, void *data)
2047fa9e4066Sahrens {
204892241e0bSTom Erickson 	inherit_cbdata_t *cb = data;
204992241e0bSTom Erickson 	zfs_prop_t prop = zfs_name_to_prop(cb->cb_propname);
205006eeb2adSek110237 
2051bb0ade09Sahrens 	/*
2052bb0ade09Sahrens 	 * If we're doing it recursively, then ignore properties that
2053bb0ade09Sahrens 	 * are not valid for this type of dataset.
2054bb0ade09Sahrens 	 */
2055bb0ade09Sahrens 	if (prop != ZPROP_INVAL &&
20564c2bdae2STim Chase 	    !zfs_prop_valid_for_type(prop, zfs_get_type(zhp), B_FALSE))
2057bb0ade09Sahrens 		return (0);
2058bb0ade09Sahrens 
205992241e0bSTom Erickson 	return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0);
2060bb0ade09Sahrens }
2061bb0ade09Sahrens 
2062bb0ade09Sahrens static int
inherit_cb(zfs_handle_t * zhp,void * data)2063bb0ade09Sahrens inherit_cb(zfs_handle_t *zhp, void *data)
2064bb0ade09Sahrens {
206592241e0bSTom Erickson 	inherit_cbdata_t *cb = data;
2066bb0ade09Sahrens 
206792241e0bSTom Erickson 	return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0);
2068fa9e4066Sahrens }
2069fa9e4066Sahrens 
2070fa9e4066Sahrens static int
zfs_do_inherit(int argc,char ** argv)2071fa9e4066Sahrens zfs_do_inherit(int argc, char **argv)
2072fa9e4066Sahrens {
2073fa9e4066Sahrens 	int c;
2074fa9e4066Sahrens 	zfs_prop_t prop;
207592241e0bSTom Erickson 	inherit_cbdata_t cb = { 0 };
2076ecd6cf80Smarks 	char *propname;
207705c998d6SRichard Lowe 	int ret = 0;
2078d5b5bb25SRich Morris 	int flags = 0;
207992241e0bSTom Erickson 	boolean_t received = B_FALSE;
2080fa9e4066Sahrens 
2081fa9e4066Sahrens 	/* check options */
208292241e0bSTom Erickson 	while ((c = getopt(argc, argv, "rS")) != -1) {
2083fa9e4066Sahrens 		switch (c) {
2084fa9e4066Sahrens 		case 'r':
2085d5b5bb25SRich Morris 			flags |= ZFS_ITER_RECURSE;
2086fa9e4066Sahrens 			break;
208792241e0bSTom Erickson 		case 'S':
208892241e0bSTom Erickson 			received = B_TRUE;
208992241e0bSTom Erickson 			break;
2090fa9e4066Sahrens 		case '?':
2091fa9e4066Sahrens 		default:
2092fa9e4066Sahrens 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
2093fa9e4066Sahrens 			    optopt);
209499653d4eSeschrock 			usage(B_FALSE);
2095fa9e4066Sahrens 		}
2096fa9e4066Sahrens 	}
2097fa9e4066Sahrens 
2098fa9e4066Sahrens 	argc -= optind;
2099fa9e4066Sahrens 	argv += optind;
2100fa9e4066Sahrens 
2101fa9e4066Sahrens 	/* check number of arguments */
2102fa9e4066Sahrens 	if (argc < 1) {
2103fa9e4066Sahrens 		(void) fprintf(stderr, gettext("missing property argument\n"));
210499653d4eSeschrock 		usage(B_FALSE);
2105fa9e4066Sahrens 	}
2106fa9e4066Sahrens 	if (argc < 2) {
2107fa9e4066Sahrens 		(void) fprintf(stderr, gettext("missing dataset argument\n"));
210899653d4eSeschrock 		usage(B_FALSE);
2109fa9e4066Sahrens 	}
2110fa9e4066Sahrens 
2111ecd6cf80Smarks 	propname = argv[0];
2112e9dbad6fSeschrock 	argc--;
2113e9dbad6fSeschrock 	argv++;
2114fa9e4066Sahrens 
2115990b4856Slling 	if ((prop = zfs_name_to_prop(propname)) != ZPROP_INVAL) {
2116fa9e4066Sahrens 		if (zfs_prop_readonly(prop)) {
2117e9dbad6fSeschrock 			(void) fprintf(stderr, gettext(
2118e9dbad6fSeschrock 			    "%s property is read-only\n"),
2119ecd6cf80Smarks 			    propname);
2120fa9e4066Sahrens 			return (1);
2121fa9e4066Sahrens 		}
212292241e0bSTom Erickson 		if (!zfs_prop_inheritable(prop) && !received) {
2123e9dbad6fSeschrock 			(void) fprintf(stderr, gettext("'%s' property cannot "
2124ecd6cf80Smarks 			    "be inherited\n"), propname);
2125e9dbad6fSeschrock 			if (prop == ZFS_PROP_QUOTA ||
2126a9799022Sck153898 			    prop == ZFS_PROP_RESERVATION ||
2127a9799022Sck153898 			    prop == ZFS_PROP_REFQUOTA ||
21285ff8cfa9SPaul B. Henson 			    prop == ZFS_PROP_REFRESERVATION) {
2129e9dbad6fSeschrock 				(void) fprintf(stderr, gettext("use 'zfs set "
2130ecd6cf80Smarks 				    "%s=none' to clear\n"), propname);
21315ff8cfa9SPaul B. Henson 				(void) fprintf(stderr, gettext("use 'zfs "
21325ff8cfa9SPaul B. Henson 				    "inherit -S %s' to revert to received "
21335ff8cfa9SPaul B. Henson 				    "value\n"), propname);
21345ff8cfa9SPaul B. Henson 			}
2135fa9e4066Sahrens 			return (1);
2136fa9e4066Sahrens 		}
2137a79992aaSTom Erickson 		if (received && (prop == ZFS_PROP_VOLSIZE ||
2138a79992aaSTom Erickson 		    prop == ZFS_PROP_VERSION)) {
2139a79992aaSTom Erickson 			(void) fprintf(stderr, gettext("'%s' property cannot "
2140a79992aaSTom Erickson 			    "be reverted to a received value\n"), propname);
2141a79992aaSTom Erickson 			return (1);
2142a79992aaSTom Erickson 		}
2143ecd6cf80Smarks 	} else if (!zfs_prop_user(propname)) {
2144ecd6cf80Smarks 		(void) fprintf(stderr, gettext("invalid property '%s'\n"),
2145ecd6cf80Smarks 		    propname);
2146e9dbad6fSeschrock 		usage(B_FALSE);
2147e9dbad6fSeschrock 	}
2148fa9e4066Sahrens 
214992241e0bSTom Erickson 	cb.cb_propname = propname;
215092241e0bSTom Erickson 	cb.cb_received = received;
215192241e0bSTom Erickson 
2152d5b5bb25SRich Morris 	if (flags & ZFS_ITER_RECURSE) {
2153d5b5bb25SRich Morris 		ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,
215492241e0bSTom Erickson 		    NULL, NULL, 0, inherit_recurse_cb, &cb);
2155bb0ade09Sahrens 	} else {
2156d5b5bb25SRich Morris 		ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,
215792241e0bSTom Erickson 		    NULL, NULL, 0, inherit_cb, &cb);
2158bb0ade09Sahrens 	}
215906eeb2adSek110237 
216006eeb2adSek110237 	return (ret);
2161fa9e4066Sahrens }
2162fa9e4066Sahrens 
2163e7437265Sahrens typedef struct upgrade_cbdata {
2164e7437265Sahrens 	uint64_t cb_numupgraded;
2165e7437265Sahrens 	uint64_t cb_numsamegraded;
2166e45ce728Sahrens 	uint64_t cb_numfailed;
2167e7437265Sahrens 	uint64_t cb_version;
2168e7437265Sahrens 	boolean_t cb_newer;
2169e7437265Sahrens 	boolean_t cb_foundone;
21709adfa60dSMatthew Ahrens 	char cb_lastfs[ZFS_MAX_DATASET_NAME_LEN];
2171e7437265Sahrens } upgrade_cbdata_t;
2172e7437265Sahrens 
2173e7437265Sahrens static int
same_pool(zfs_handle_t * zhp,const char * name)2174e7437265Sahrens same_pool(zfs_handle_t *zhp, const char *name)
2175e7437265Sahrens {
2176e7437265Sahrens 	int len1 = strcspn(name, "/@");
2177e7437265Sahrens 	const char *zhname = zfs_get_name(zhp);
2178e7437265Sahrens 	int len2 = strcspn(zhname, "/@");
2179e7437265Sahrens 
2180e7437265Sahrens 	if (len1 != len2)
2181e7437265Sahrens 		return (B_FALSE);
21822a6b87f0Sek110237 	return (strncmp(name, zhname, len1) == 0);
2183e7437265Sahrens }
2184e7437265Sahrens 
2185e7437265Sahrens static int
upgrade_list_callback(zfs_handle_t * zhp,void * data)2186e7437265Sahrens upgrade_list_callback(zfs_handle_t *zhp, void *data)
2187e7437265Sahrens {
2188e7437265Sahrens 	upgrade_cbdata_t *cb = data;
2189e7437265Sahrens 	int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
2190e7437265Sahrens 
2191e7437265Sahrens 	/* list if it's old/new */
2192e7437265Sahrens 	if ((!cb->cb_newer && version < ZPL_VERSION) ||
21933cb34c60Sahrens 	    (cb->cb_newer && version > ZPL_VERSION)) {
2194e7437265Sahrens 		char *str;
2195e7437265Sahrens 		if (cb->cb_newer) {
2196e7437265Sahrens 			str = gettext("The following filesystems are "
2197e7437265Sahrens 			    "formatted using a newer software version and\n"
2198e7437265Sahrens 			    "cannot be accessed on the current system.\n\n");
2199e7437265Sahrens 		} else {
2200e7437265Sahrens 			str = gettext("The following filesystems are "
2201e7437265Sahrens 			    "out of date, and can be upgraded.  After being\n"
2202e7437265Sahrens 			    "upgraded, these filesystems (and any 'zfs send' "
2203e7437265Sahrens 			    "streams generated from\n"
2204e7437265Sahrens 			    "subsequent snapshots) will no longer be "
2205e7437265Sahrens 			    "accessible by older software versions.\n\n");
2206e7437265Sahrens 		}
2207e7437265Sahrens 
2208e7437265Sahrens 		if (!cb->cb_foundone) {
2209e7437265Sahrens 			(void) puts(str);
2210e7437265Sahrens 			(void) printf(gettext("VER  FILESYSTEM\n"));
2211e7437265Sahrens 			(void) printf(gettext("---  ------------\n"));
2212e7437265Sahrens 			cb->cb_foundone = B_TRUE;
2213e7437265Sahrens 		}
2214e7437265Sahrens 
2215e7437265Sahrens 		(void) printf("%2u   %s\n", version, zfs_get_name(zhp));
2216e7437265Sahrens 	}
2217e7437265Sahrens 
2218e7437265Sahrens 	return (0);
2219e7437265Sahrens }
2220e7437265Sahrens 
2221e7437265Sahrens static int
upgrade_set_callback(zfs_handle_t * zhp,void * data)2222e7437265Sahrens upgrade_set_callback(zfs_handle_t *zhp, void *data)
2223e7437265Sahrens {
2224e7437265Sahrens 	upgrade_cbdata_t *cb = data;
2225e7437265Sahrens 	int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
22260a586ceaSMark Shellenbaum 	int needed_spa_version;
2227da6c28aaSamw 	int spa_version;
2228da6c28aaSamw 
22297b97dc1aSrm160521 	if (zfs_spa_version(zhp, &spa_version) < 0)
2230da6c28aaSamw 		return (-1);
2231da6c28aaSamw 
22320a586ceaSMark Shellenbaum 	needed_spa_version = zfs_spa_version_map(cb->cb_version);
22330a586ceaSMark Shellenbaum 
22340a586ceaSMark Shellenbaum 	if (needed_spa_version < 0)
22350a586ceaSMark Shellenbaum 		return (-1);
22360a586ceaSMark Shellenbaum 
22370a586ceaSMark Shellenbaum 	if (spa_version < needed_spa_version) {
2238da6c28aaSamw 		/* can't upgrade */
223914843421SMatthew Ahrens 		(void) printf(gettext("%s: can not be "
224014843421SMatthew Ahrens 		    "upgraded; the pool version needs to first "
224114843421SMatthew Ahrens 		    "be upgraded\nto version %d\n\n"),
22420a586ceaSMark Shellenbaum 		    zfs_get_name(zhp), needed_spa_version);
2243da6c28aaSamw 		cb->cb_numfailed++;
2244da6c28aaSamw 		return (0);
2245da6c28aaSamw 	}
2246da6c28aaSamw 
2247e7437265Sahrens 	/* upgrade */
2248e7437265Sahrens 	if (version < cb->cb_version) {
2249e7437265Sahrens 		char verstr[16];
2250e45ce728Sahrens 		(void) snprintf(verstr, sizeof (verstr),
2251e45ce728Sahrens 		    "%llu", cb->cb_version);
2252e7437265Sahrens 		if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) {
2253e7437265Sahrens 			/*
2254e7437265Sahrens 			 * If they did "zfs upgrade -a", then we could
2255e7437265Sahrens 			 * be doing ioctls to different pools.  We need
22564445fffbSMatthew Ahrens 			 * to log this history once to each pool, and bypass
22574445fffbSMatthew Ahrens 			 * the normal history logging that happens in main().
2258e7437265Sahrens 			 */
22594445fffbSMatthew Ahrens 			(void) zpool_log_history(g_zfs, history_str);
22604445fffbSMatthew Ahrens 			log_history = B_FALSE;
2261e7437265Sahrens 		}
2262e7437265Sahrens 		if (zfs_prop_set(zhp, "version", verstr) == 0)
2263e7437265Sahrens 			cb->cb_numupgraded++;
2264e45ce728Sahrens 		else
2265e45ce728Sahrens 			cb->cb_numfailed++;
2266e7437265Sahrens 		(void) strcpy(cb->cb_lastfs, zfs_get_name(zhp));
2267e7437265Sahrens 	} else if (version > cb->cb_version) {
2268e7437265Sahrens 		/* can't downgrade */
2269e7437265Sahrens 		(void) printf(gettext("%s: can not be downgraded; "
2270e7437265Sahrens 		    "it is already at version %u\n"),
2271e7437265Sahrens 		    zfs_get_name(zhp), version);
2272e45ce728Sahrens 		cb->cb_numfailed++;
2273e7437265Sahrens 	} else {
2274e7437265Sahrens 		cb->cb_numsamegraded++;
2275e7437265Sahrens 	}
2276e7437265Sahrens 	return (0);
2277e7437265Sahrens }
2278e7437265Sahrens 
2279e7437265Sahrens /*
2280e7437265Sahrens  * zfs upgrade
2281e7437265Sahrens  * zfs upgrade -v
2282e7437265Sahrens  * zfs upgrade [-r] [-V <version>] <-a | filesystem>
2283e7437265Sahrens  */
2284e7437265Sahrens static int
zfs_do_upgrade(int argc,char ** argv)2285e7437265Sahrens zfs_do_upgrade(int argc, char **argv)
2286e7437265Sahrens {
2287e7437265Sahrens 	boolean_t all = B_FALSE;
2288e7437265Sahrens 	boolean_t showversions = B_FALSE;
228905c998d6SRichard Lowe 	int ret = 0;
2290e7437265Sahrens 	upgrade_cbdata_t cb = { 0 };
2291ef150c2bSRichard Lowe 	int c;
2292d5b5bb25SRich Morris 	int flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
2293e7437265Sahrens 
2294e7437265Sahrens 	/* check options */
2295e7437265Sahrens 	while ((c = getopt(argc, argv, "rvV:a")) != -1) {
2296e7437265Sahrens 		switch (c) {
2297e7437265Sahrens 		case 'r':
2298d5b5bb25SRich Morris 			flags |= ZFS_ITER_RECURSE;
2299e7437265Sahrens 			break;
2300e7437265Sahrens 		case 'v':
2301e7437265Sahrens 			showversions = B_TRUE;
2302e7437265Sahrens 			break;
2303e7437265Sahrens 		case 'V':
2304e7437265Sahrens 			if (zfs_prop_string_to_index(ZFS_PROP_VERSION,
2305e7437265Sahrens 			    optarg, &cb.cb_version) != 0) {
2306e7437265Sahrens 				(void) fprintf(stderr,
2307e7437265Sahrens 				    gettext("invalid version %s\n"), optarg);
2308e7437265Sahrens 				usage(B_FALSE);
2309e7437265Sahrens 			}
2310e7437265Sahrens 			break;
2311e7437265Sahrens 		case 'a':
2312e7437265Sahrens 			all = B_TRUE;
2313e7437265Sahrens 			break;
2314e7437265Sahrens 		case '?':
2315e7437265Sahrens 		default:
2316e7437265Sahrens 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
2317e7437265Sahrens 			    optopt);
2318e7437265Sahrens 			usage(B_FALSE);
2319e7437265Sahrens 		}
2320e7437265Sahrens 	}
2321e7437265Sahrens 
2322e7437265Sahrens 	argc -= optind;
2323e7437265Sahrens 	argv += optind;
2324e7437265Sahrens 
2325d5b5bb25SRich Morris 	if ((!all && !argc) && ((flags & ZFS_ITER_RECURSE) | cb.cb_version))
2326e7437265Sahrens 		usage(B_FALSE);
2327d5b5bb25SRich Morris 	if (showversions && (flags & ZFS_ITER_RECURSE || all ||
2328d5b5bb25SRich Morris 	    cb.cb_version || argc))
2329e7437265Sahrens 		usage(B_FALSE);
2330e7437265Sahrens 	if ((all || argc) && (showversions))
2331e7437265Sahrens 		usage(B_FALSE);
2332e7437265Sahrens 	if (all && argc)
2333e7437265Sahrens 		usage(B_FALSE);
2334e7437265Sahrens 
2335e7437265Sahrens 	if (showversions) {
2336e7437265Sahrens 		/* Show info on available versions. */
2337e7437265Sahrens 		(void) printf(gettext("The following filesystem versions are "
2338e7437265Sahrens 		    "supported:\n\n"));
2339e7437265Sahrens 		(void) printf(gettext("VER  DESCRIPTION\n"));
2340e7437265Sahrens 		(void) printf("---  -----------------------------------------"
2341e7437265Sahrens 		    "---------------\n");
2342e7437265Sahrens 		(void) printf(gettext(" 1   Initial ZFS filesystem version\n"));
2343e7437265Sahrens 		(void) printf(gettext(" 2   Enhanced directory entries\n"));
234419b94df9SMatthew Ahrens 		(void) printf(gettext(" 3   Case insensitive and filesystem "
234519b94df9SMatthew Ahrens 		    "user identifier (FUID)\n"));
234614843421SMatthew Ahrens 		(void) printf(gettext(" 4   userquota, groupquota "
234714843421SMatthew Ahrens 		    "properties\n"));
23480a586ceaSMark Shellenbaum 		(void) printf(gettext(" 5   System attributes\n"));
2349e7437265Sahrens 		(void) printf(gettext("\nFor more information on a particular "
23509a8685acSstephanie scheffler 		    "version, including supported releases,\n"));
23519a8685acSstephanie scheffler 		(void) printf("see the ZFS Administration Guide.\n\n");
2352e7437265Sahrens 		ret = 0;
2353e7437265Sahrens 	} else if (argc || all) {
2354e7437265Sahrens 		/* Upgrade filesystems */
2355e7437265Sahrens 		if (cb.cb_version == 0)
2356e7437265Sahrens 			cb.cb_version = ZPL_VERSION;
2357d5b5bb25SRich Morris 		ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_FILESYSTEM,
2358ae1726b6SChris Gerhard 		    NULL, NULL, 0, upgrade_set_callback, &cb);
2359e7437265Sahrens 		(void) printf(gettext("%llu filesystems upgraded\n"),
2360e7437265Sahrens 		    cb.cb_numupgraded);
2361e7437265Sahrens 		if (cb.cb_numsamegraded) {
2362e7437265Sahrens 			(void) printf(gettext("%llu filesystems already at "
2363e7437265Sahrens 			    "this version\n"),
2364e7437265Sahrens 			    cb.cb_numsamegraded);
2365e7437265Sahrens 		}
2366e45ce728Sahrens 		if (cb.cb_numfailed != 0)
2367e7437265Sahrens 			ret = 1;
2368e7437265Sahrens 	} else {
2369edc8ef7dSToomas Soome 		/* List old-version filesystems */
2370e7437265Sahrens 		boolean_t found;
2371e7437265Sahrens 		(void) printf(gettext("This system is currently running "
2372e7437265Sahrens 		    "ZFS filesystem version %llu.\n\n"), ZPL_VERSION);
2373e7437265Sahrens 
2374d5b5bb25SRich Morris 		flags |= ZFS_ITER_RECURSE;
2375d5b5bb25SRich Morris 		ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,
2376ae1726b6SChris Gerhard 		    NULL, NULL, 0, upgrade_list_callback, &cb);
2377e7437265Sahrens 
2378e7437265Sahrens 		found = cb.cb_foundone;
2379e7437265Sahrens 		cb.cb_foundone = B_FALSE;
2380e7437265Sahrens 		cb.cb_newer = B_TRUE;
2381e7437265Sahrens 
2382d5b5bb25SRich Morris 		ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,
2383ae1726b6SChris Gerhard 		    NULL, NULL, 0, upgrade_list_callback, &cb);
2384e7437265Sahrens 
2385e7437265Sahrens 		if (!cb.cb_foundone && !found) {
2386e7437265Sahrens 			(void) printf(gettext("All filesystems are "
2387e7437265Sahrens 			    "formatted with the current version.\n"));
2388e7437265Sahrens 		}
2389e7437265Sahrens 	}
2390e7437265Sahrens 
2391e7437265Sahrens 	return (ret);
2392e7437265Sahrens }
2393e7437265Sahrens 
239489f5d17bSYuri Pankov /*
239589f5d17bSYuri Pankov  * zfs userspace [-Hinp] [-o field[,...]] [-s field [-s field]...]
239689f5d17bSYuri Pankov  *               [-S field [-S field]...] [-t type[,...]] filesystem | snapshot
239789f5d17bSYuri Pankov  * zfs groupspace [-Hinp] [-o field[,...]] [-s field [-s field]...]
239889f5d17bSYuri Pankov  *                [-S field [-S field]...] [-t type[,...]] filesystem | snapshot
2399f67950b2SNasf-Fan  * zfs projectspace [-Hp] [-o field[,...]] [-s field [-s field]...]
2400f67950b2SNasf-Fan  *                [-S field [-S field]...] filesystem | snapshot
240189f5d17bSYuri Pankov  *
240289f5d17bSYuri Pankov  *	-H      Scripted mode; elide headers and separate columns by tabs.
240389f5d17bSYuri Pankov  *	-i	Translate SID to POSIX ID.
240489f5d17bSYuri Pankov  *	-n	Print numeric ID instead of user/group name.
240589f5d17bSYuri Pankov  *	-o      Control which fields to display.
240643d68d68SYuri Pankov  *	-p	Use exact (parsable) numeric output.
240789f5d17bSYuri Pankov  *	-s      Specify sort columns, descending order.
240889f5d17bSYuri Pankov  *	-S      Specify sort columns, ascending order.
240989f5d17bSYuri Pankov  *	-t      Control which object types to display.
241089f5d17bSYuri Pankov  *
241189f5d17bSYuri Pankov  *	Displays space consumed by, and quotas on, each user in the specified
241289f5d17bSYuri Pankov  *	filesystem or snapshot.
241389f5d17bSYuri Pankov  */
24141af68beaSAlexander Stetsenko 
241589f5d17bSYuri Pankov /* us_field_types, us_field_hdr and us_field_names should be kept in sync */
241689f5d17bSYuri Pankov enum us_field_types {
241789f5d17bSYuri Pankov 	USFIELD_TYPE,
241889f5d17bSYuri Pankov 	USFIELD_NAME,
241989f5d17bSYuri Pankov 	USFIELD_USED,
2420f67950b2SNasf-Fan 	USFIELD_QUOTA,
2421f67950b2SNasf-Fan 	USFIELD_OBJUSED,
2422f67950b2SNasf-Fan 	USFIELD_OBJQUOTA
242389f5d17bSYuri Pankov };
2424f67950b2SNasf-Fan static char *us_field_hdr[] = { "TYPE", "NAME", "USED", "QUOTA",
2425f67950b2SNasf-Fan 				    "OBJUSED", "OBJQUOTA" };
2426f67950b2SNasf-Fan static char *us_field_names[] = { "type", "name", "used", "quota",
2427f67950b2SNasf-Fan 				    "objused", "objquota" };
242889f5d17bSYuri Pankov #define	USFIELD_LAST	(sizeof (us_field_names) / sizeof (char *))
24291af68beaSAlexander Stetsenko 
243089f5d17bSYuri Pankov #define	USTYPE_PSX_GRP	(1 << 0)
243189f5d17bSYuri Pankov #define	USTYPE_PSX_USR	(1 << 1)
243289f5d17bSYuri Pankov #define	USTYPE_SMB_GRP	(1 << 2)
243389f5d17bSYuri Pankov #define	USTYPE_SMB_USR	(1 << 3)
2434f67950b2SNasf-Fan #define	USTYPE_PROJ	(1 << 4)
243589f5d17bSYuri Pankov #define	USTYPE_ALL	\
2436f67950b2SNasf-Fan 	(USTYPE_PSX_GRP | USTYPE_PSX_USR | USTYPE_SMB_GRP | USTYPE_SMB_USR | \
2437f67950b2SNasf-Fan 	    USTYPE_PROJ)
24381af68beaSAlexander Stetsenko 
243989f5d17bSYuri Pankov static int us_type_bits[] = {
244089f5d17bSYuri Pankov 	USTYPE_PSX_GRP,
244189f5d17bSYuri Pankov 	USTYPE_PSX_USR,
244289f5d17bSYuri Pankov 	USTYPE_SMB_GRP,
244389f5d17bSYuri Pankov 	USTYPE_SMB_USR,
244489f5d17bSYuri Pankov 	USTYPE_ALL
244589f5d17bSYuri Pankov };
2446f38cb554SJohn Wren Kennedy static char *us_type_names[] = { "posixgroup", "posixuser", "smbgroup",
244789f5d17bSYuri Pankov 	"smbuser", "all" };
24481af68beaSAlexander Stetsenko 
24491af68beaSAlexander Stetsenko typedef struct us_node {
24501af68beaSAlexander Stetsenko 	nvlist_t	*usn_nvl;
24511af68beaSAlexander Stetsenko 	uu_avl_node_t	usn_avlnode;
24521af68beaSAlexander Stetsenko 	uu_list_node_t	usn_listnode;
24531af68beaSAlexander Stetsenko } us_node_t;
24541af68beaSAlexander Stetsenko 
24551af68beaSAlexander Stetsenko typedef struct us_cbdata {
24561af68beaSAlexander Stetsenko 	nvlist_t	**cb_nvlp;
24571af68beaSAlexander Stetsenko 	uu_avl_pool_t	*cb_avl_pool;
24581af68beaSAlexander Stetsenko 	uu_avl_t	*cb_avl;
24591af68beaSAlexander Stetsenko 	boolean_t	cb_numname;
24601af68beaSAlexander Stetsenko 	boolean_t	cb_nicenum;
24611af68beaSAlexander Stetsenko 	boolean_t	cb_sid2posix;
24621af68beaSAlexander Stetsenko 	zfs_userquota_prop_t cb_prop;
24631af68beaSAlexander Stetsenko 	zfs_sort_column_t *cb_sortcol;
246489f5d17bSYuri Pankov 	size_t		cb_width[USFIELD_LAST];
24651af68beaSAlexander Stetsenko } us_cbdata_t;
24661af68beaSAlexander Stetsenko 
246789f5d17bSYuri Pankov static boolean_t us_populated = B_FALSE;
246889f5d17bSYuri Pankov 
24691af68beaSAlexander Stetsenko typedef struct {
24701af68beaSAlexander Stetsenko 	zfs_sort_column_t *si_sortcol;
247189f5d17bSYuri Pankov 	boolean_t	si_numname;
24721af68beaSAlexander Stetsenko } us_sort_info_t;
24731af68beaSAlexander Stetsenko 
24741af68beaSAlexander Stetsenko static int
us_field_index(char * field)247589f5d17bSYuri Pankov us_field_index(char *field)
247689f5d17bSYuri Pankov {
247789f5d17bSYuri Pankov 	int i;
247889f5d17bSYuri Pankov 
247989f5d17bSYuri Pankov 	for (i = 0; i < USFIELD_LAST; i++) {
248089f5d17bSYuri Pankov 		if (strcmp(field, us_field_names[i]) == 0)
248189f5d17bSYuri Pankov 			return (i);
248289f5d17bSYuri Pankov 	}
248389f5d17bSYuri Pankov 
248489f5d17bSYuri Pankov 	return (-1);
248589f5d17bSYuri Pankov }
248689f5d17bSYuri Pankov 
248789f5d17bSYuri Pankov static int
us_compare(const void * larg,const void * rarg,void * unused)24881af68beaSAlexander Stetsenko us_compare(const void *larg, const void *rarg, void *unused)
24891af68beaSAlexander Stetsenko {
24901af68beaSAlexander Stetsenko 	const us_node_t *l = larg;
24911af68beaSAlexander Stetsenko 	const us_node_t *r = rarg;
24921af68beaSAlexander Stetsenko 	us_sort_info_t *si = (us_sort_info_t *)unused;
24931af68beaSAlexander Stetsenko 	zfs_sort_column_t *sortcol = si->si_sortcol;
249489f5d17bSYuri Pankov 	boolean_t numname = si->si_numname;
24951af68beaSAlexander Stetsenko 	nvlist_t *lnvl = l->usn_nvl;
24961af68beaSAlexander Stetsenko 	nvlist_t *rnvl = r->usn_nvl;
249789f5d17bSYuri Pankov 	int rc = 0;
249889f5d17bSYuri Pankov 	boolean_t lvb, rvb;
24991af68beaSAlexander Stetsenko 
25001af68beaSAlexander Stetsenko 	for (; sortcol != NULL; sortcol = sortcol->sc_next) {
25011af68beaSAlexander Stetsenko 		char *lvstr = "";
25021af68beaSAlexander Stetsenko 		char *rvstr = "";
25031af68beaSAlexander Stetsenko 		uint32_t lv32 = 0;
25041af68beaSAlexander Stetsenko 		uint32_t rv32 = 0;
25051af68beaSAlexander Stetsenko 		uint64_t lv64 = 0;
25061af68beaSAlexander Stetsenko 		uint64_t rv64 = 0;
25071af68beaSAlexander Stetsenko 		zfs_prop_t prop = sortcol->sc_prop;
25081af68beaSAlexander Stetsenko 		const char *propname = NULL;
25091af68beaSAlexander Stetsenko 		boolean_t reverse = sortcol->sc_reverse;
25101af68beaSAlexander Stetsenko 
25111af68beaSAlexander Stetsenko 		switch (prop) {
25121af68beaSAlexander Stetsenko 		case ZFS_PROP_TYPE:
25131af68beaSAlexander Stetsenko 			propname = "type";
25141af68beaSAlexander Stetsenko 			(void) nvlist_lookup_uint32(lnvl, propname, &lv32);
25151af68beaSAlexander Stetsenko 			(void) nvlist_lookup_uint32(rnvl, propname, &rv32);
25161af68beaSAlexander Stetsenko 			if (rv32 != lv32)
251789f5d17bSYuri Pankov 				rc = (rv32 < lv32) ? 1 : -1;
25181af68beaSAlexander Stetsenko 			break;
25191af68beaSAlexander Stetsenko 		case ZFS_PROP_NAME:
25201af68beaSAlexander Stetsenko 			propname = "name";
252189f5d17bSYuri Pankov 			if (numname) {
252289f5d17bSYuri Pankov 				(void) nvlist_lookup_uint64(lnvl, propname,
252389f5d17bSYuri Pankov 				    &lv64);
252489f5d17bSYuri Pankov 				(void) nvlist_lookup_uint64(rnvl, propname,
252589f5d17bSYuri Pankov 				    &rv64);
252689f5d17bSYuri Pankov 				if (rv64 != lv64)
252789f5d17bSYuri Pankov 					rc = (rv64 < lv64) ? 1 : -1;
25281af68beaSAlexander Stetsenko 			} else {
25291af68beaSAlexander Stetsenko 				(void) nvlist_lookup_string(lnvl, propname,
25301af68beaSAlexander Stetsenko 				    &lvstr);
25311af68beaSAlexander Stetsenko 				(void) nvlist_lookup_string(rnvl, propname,
25321af68beaSAlexander Stetsenko 				    &rvstr);
25331af68beaSAlexander Stetsenko 				rc = strcmp(lvstr, rvstr);
25341af68beaSAlexander Stetsenko 			}
25351af68beaSAlexander Stetsenko 			break;
25361af68beaSAlexander Stetsenko 		case ZFS_PROP_USED:
25371af68beaSAlexander Stetsenko 		case ZFS_PROP_QUOTA:
253889f5d17bSYuri Pankov 			if (!us_populated)
253989f5d17bSYuri Pankov 				break;
254089f5d17bSYuri Pankov 			if (prop == ZFS_PROP_USED)
25411af68beaSAlexander Stetsenko 				propname = "used";
25421af68beaSAlexander Stetsenko 			else
25431af68beaSAlexander Stetsenko 				propname = "quota";
25441af68beaSAlexander Stetsenko 			(void) nvlist_lookup_uint64(lnvl, propname, &lv64);
25451af68beaSAlexander Stetsenko 			(void) nvlist_lookup_uint64(rnvl, propname, &rv64);
25461af68beaSAlexander Stetsenko 			if (rv64 != lv64)
254789f5d17bSYuri Pankov 				rc = (rv64 < lv64) ? 1 : -1;
254889f5d17bSYuri Pankov 			break;
2549c16bcc45SIgor Kozhukhov 
2550c16bcc45SIgor Kozhukhov 		default:
2551c16bcc45SIgor Kozhukhov 			break;
25521af68beaSAlexander Stetsenko 		}
25531af68beaSAlexander Stetsenko 
255489f5d17bSYuri Pankov 		if (rc != 0) {
25551af68beaSAlexander Stetsenko 			if (rc < 0)
25561af68beaSAlexander Stetsenko 				return (reverse ? 1 : -1);
25571af68beaSAlexander Stetsenko 			else
25581af68beaSAlexander Stetsenko 				return (reverse ? -1 : 1);
25591af68beaSAlexander Stetsenko 		}
256089f5d17bSYuri Pankov 	}
25611af68beaSAlexander Stetsenko 
256289f5d17bSYuri Pankov 	/*
256389f5d17bSYuri Pankov 	 * If entries still seem to be the same, check if they are of the same
256489f5d17bSYuri Pankov 	 * type (smbentity is added only if we are doing SID to POSIX ID
256589f5d17bSYuri Pankov 	 * translation where we can have duplicate type/name combinations).
256689f5d17bSYuri Pankov 	 */
256789f5d17bSYuri Pankov 	if (nvlist_lookup_boolean_value(lnvl, "smbentity", &lvb) == 0 &&
256889f5d17bSYuri Pankov 	    nvlist_lookup_boolean_value(rnvl, "smbentity", &rvb) == 0 &&
256989f5d17bSYuri Pankov 	    lvb != rvb)
257089f5d17bSYuri Pankov 		return (lvb < rvb ? -1 : 1);
257189f5d17bSYuri Pankov 
257289f5d17bSYuri Pankov 	return (0);
25731af68beaSAlexander Stetsenko }
25741af68beaSAlexander Stetsenko 
2575f67950b2SNasf-Fan static boolean_t
zfs_prop_is_user(unsigned p)2576f67950b2SNasf-Fan zfs_prop_is_user(unsigned p)
2577f67950b2SNasf-Fan {
2578f67950b2SNasf-Fan 	return (p == ZFS_PROP_USERUSED || p == ZFS_PROP_USERQUOTA ||
2579f67950b2SNasf-Fan 	    p == ZFS_PROP_USEROBJUSED || p == ZFS_PROP_USEROBJQUOTA);
2580f67950b2SNasf-Fan }
2581f67950b2SNasf-Fan 
2582f67950b2SNasf-Fan static boolean_t
zfs_prop_is_group(unsigned p)2583f67950b2SNasf-Fan zfs_prop_is_group(unsigned p)
2584f67950b2SNasf-Fan {
2585f67950b2SNasf-Fan 	return (p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA ||
2586f67950b2SNasf-Fan 	    p == ZFS_PROP_GROUPOBJUSED || p == ZFS_PROP_GROUPOBJQUOTA);
2587f67950b2SNasf-Fan }
2588f67950b2SNasf-Fan 
2589f67950b2SNasf-Fan static boolean_t
zfs_prop_is_project(unsigned p)2590f67950b2SNasf-Fan zfs_prop_is_project(unsigned p)
2591f67950b2SNasf-Fan {
2592f67950b2SNasf-Fan 	return (p == ZFS_PROP_PROJECTUSED || p == ZFS_PROP_PROJECTQUOTA ||
2593f67950b2SNasf-Fan 	    p == ZFS_PROP_PROJECTOBJUSED || p == ZFS_PROP_PROJECTOBJQUOTA);
2594f67950b2SNasf-Fan }
2595f67950b2SNasf-Fan 
25961af68beaSAlexander Stetsenko static inline const char *
us_type2str(unsigned field_type)25971af68beaSAlexander Stetsenko us_type2str(unsigned field_type)
25981af68beaSAlexander Stetsenko {
25991af68beaSAlexander Stetsenko 	switch (field_type) {
26001af68beaSAlexander Stetsenko 	case USTYPE_PSX_USR:
26011af68beaSAlexander Stetsenko 		return ("POSIX User");
26021af68beaSAlexander Stetsenko 	case USTYPE_PSX_GRP:
26031af68beaSAlexander Stetsenko 		return ("POSIX Group");
26041af68beaSAlexander Stetsenko 	case USTYPE_SMB_USR:
26051af68beaSAlexander Stetsenko 		return ("SMB User");
26061af68beaSAlexander Stetsenko 	case USTYPE_SMB_GRP:
26071af68beaSAlexander Stetsenko 		return ("SMB Group");
2608f67950b2SNasf-Fan 	case USTYPE_PROJ:
2609f67950b2SNasf-Fan 		return ("Project");
26101af68beaSAlexander Stetsenko 	default:
26111af68beaSAlexander Stetsenko 		return ("Undefined");
26121af68beaSAlexander Stetsenko 	}
26131af68beaSAlexander Stetsenko }
26141af68beaSAlexander Stetsenko 
26150aea4b19SMatthew Ahrens static int
userspace_cb(void * arg,const char * domain,uid_t rid,uint64_t space)261614843421SMatthew Ahrens userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space)
261714843421SMatthew Ahrens {
26181af68beaSAlexander Stetsenko 	us_cbdata_t *cb = (us_cbdata_t *)arg;
26191af68beaSAlexander Stetsenko 	zfs_userquota_prop_t prop = cb->cb_prop;
26200aea4b19SMatthew Ahrens 	char *name = NULL;
26211af68beaSAlexander Stetsenko 	char *propname;
262214843421SMatthew Ahrens 	char sizebuf[32];
26231af68beaSAlexander Stetsenko 	us_node_t *node;
26241af68beaSAlexander Stetsenko 	uu_avl_pool_t *avl_pool = cb->cb_avl_pool;
26251af68beaSAlexander Stetsenko 	uu_avl_t *avl = cb->cb_avl;
26261af68beaSAlexander Stetsenko 	uu_avl_index_t idx;
26271af68beaSAlexander Stetsenko 	nvlist_t *props;
26281af68beaSAlexander Stetsenko 	us_node_t *n;
26291af68beaSAlexander Stetsenko 	zfs_sort_column_t *sortcol = cb->cb_sortcol;
2630c16bcc45SIgor Kozhukhov 	unsigned type = 0;
26311af68beaSAlexander Stetsenko 	const char *typestr;
26321af68beaSAlexander Stetsenko 	size_t namelen;
26331af68beaSAlexander Stetsenko 	size_t typelen;
26341af68beaSAlexander Stetsenko 	size_t sizelen;
263589f5d17bSYuri Pankov 	int typeidx, nameidx, sizeidx;
26361af68beaSAlexander Stetsenko 	us_sort_info_t sortinfo = { sortcol, cb->cb_numname };
263789f5d17bSYuri Pankov 	boolean_t smbentity = B_FALSE;
263814843421SMatthew Ahrens 
263989f5d17bSYuri Pankov 	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
264089f5d17bSYuri Pankov 		nomem();
264189f5d17bSYuri Pankov 	node = safe_malloc(sizeof (us_node_t));
264289f5d17bSYuri Pankov 	uu_avl_node_init(node, &node->usn_avlnode, avl_pool);
264389f5d17bSYuri Pankov 	node->usn_nvl = props;
264489f5d17bSYuri Pankov 
264589f5d17bSYuri Pankov 	if (domain != NULL && domain[0] != '\0') {
264689f5d17bSYuri Pankov 		/* SMB */
26479adfa60dSMatthew Ahrens 		char sid[MAXNAMELEN + 32];
26481af68beaSAlexander Stetsenko 		uid_t id;
264989f5d17bSYuri Pankov 		int err;
26501ed6b69aSGordon Ross 		int flag = IDMAP_REQ_FLG_USE_CACHE;
26511af68beaSAlexander Stetsenko 
265289f5d17bSYuri Pankov 		smbentity = B_TRUE;
265389f5d17bSYuri Pankov 
26541af68beaSAlexander Stetsenko 		(void) snprintf(sid, sizeof (sid), "%s-%u", domain, rid);
265589f5d17bSYuri Pankov 
26561af68beaSAlexander Stetsenko 		if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) {
26571af68beaSAlexander Stetsenko 			type = USTYPE_SMB_GRP;
26581af68beaSAlexander Stetsenko 			err = sid_to_id(sid, B_FALSE, &id);
26591af68beaSAlexander Stetsenko 		} else {
26601af68beaSAlexander Stetsenko 			type = USTYPE_SMB_USR;
26611af68beaSAlexander Stetsenko 			err = sid_to_id(sid, B_TRUE, &id);
266214843421SMatthew Ahrens 		}
266314843421SMatthew Ahrens 
26641af68beaSAlexander Stetsenko 		if (err == 0) {
26651af68beaSAlexander Stetsenko 			rid = id;
266689f5d17bSYuri Pankov 			if (!cb->cb_sid2posix) {
26671ed6b69aSGordon Ross 				if (type == USTYPE_SMB_USR) {
26681ed6b69aSGordon Ross 					(void) idmap_getwinnamebyuid(rid, flag,
26691ed6b69aSGordon Ross 					    &name, NULL);
26701ed6b69aSGordon Ross 				} else {
26711ed6b69aSGordon Ross 					(void) idmap_getwinnamebygid(rid, flag,
26721ed6b69aSGordon Ross 					    &name, NULL);
26731ed6b69aSGordon Ross 				}
26741af68beaSAlexander Stetsenko 				if (name == NULL)
26751af68beaSAlexander Stetsenko 					name = sid;
26761af68beaSAlexander Stetsenko 			}
26771af68beaSAlexander Stetsenko 		}
26781af68beaSAlexander Stetsenko 	}
26791af68beaSAlexander Stetsenko 
268089f5d17bSYuri Pankov 	if (cb->cb_sid2posix || domain == NULL || domain[0] == '\0') {
268189f5d17bSYuri Pankov 		/* POSIX or -i */
2682f67950b2SNasf-Fan 		if (zfs_prop_is_group(prop)) {
268389f5d17bSYuri Pankov 			type = USTYPE_PSX_GRP;
268489f5d17bSYuri Pankov 			if (!cb->cb_numname) {
268589f5d17bSYuri Pankov 				struct group *g;
268689f5d17bSYuri Pankov 
268789f5d17bSYuri Pankov 				if ((g = getgrgid(rid)) != NULL)
268889f5d17bSYuri Pankov 					name = g->gr_name;
268989f5d17bSYuri Pankov 			}
2690f67950b2SNasf-Fan 		} else if (zfs_prop_is_user(prop)) {
269189f5d17bSYuri Pankov 			type = USTYPE_PSX_USR;
269289f5d17bSYuri Pankov 			if (!cb->cb_numname) {
269389f5d17bSYuri Pankov 				struct passwd *p;
269489f5d17bSYuri Pankov 
269589f5d17bSYuri Pankov 				if ((p = getpwuid(rid)) != NULL)
269689f5d17bSYuri Pankov 					name = p->pw_name;
269789f5d17bSYuri Pankov 			}
2698f67950b2SNasf-Fan 		} else {
2699f67950b2SNasf-Fan 			type = USTYPE_PROJ;
270089f5d17bSYuri Pankov 		}
270189f5d17bSYuri Pankov 	}
270289f5d17bSYuri Pankov 
270389f5d17bSYuri Pankov 	/*
270489f5d17bSYuri Pankov 	 * Make sure that the type/name combination is unique when doing
270589f5d17bSYuri Pankov 	 * SID to POSIX ID translation (hence changing the type from SMB to
270689f5d17bSYuri Pankov 	 * POSIX).
270789f5d17bSYuri Pankov 	 */
270889f5d17bSYuri Pankov 	if (cb->cb_sid2posix &&
270989f5d17bSYuri Pankov 	    nvlist_add_boolean_value(props, "smbentity", smbentity) != 0)
271089f5d17bSYuri Pankov 		nomem();
271189f5d17bSYuri Pankov 
271289f5d17bSYuri Pankov 	/* Calculate/update width of TYPE field */
271389f5d17bSYuri Pankov 	typestr = us_type2str(type);
271489f5d17bSYuri Pankov 	typelen = strlen(gettext(typestr));
271589f5d17bSYuri Pankov 	typeidx = us_field_index("type");
271689f5d17bSYuri Pankov 	if (typelen > cb->cb_width[typeidx])
271789f5d17bSYuri Pankov 		cb->cb_width[typeidx] = typelen;
27181af68beaSAlexander Stetsenko 	if (nvlist_add_uint32(props, "type", type) != 0)
27191af68beaSAlexander Stetsenko 		nomem();
27201af68beaSAlexander Stetsenko 
272189f5d17bSYuri Pankov 	/* Calculate/update width of NAME field */
272289f5d17bSYuri Pankov 	if ((cb->cb_numname && cb->cb_sid2posix) || name == NULL) {
272389f5d17bSYuri Pankov 		if (nvlist_add_uint64(props, "name", rid) != 0)
27241af68beaSAlexander Stetsenko 			nomem();
272589f5d17bSYuri Pankov 		namelen = snprintf(NULL, 0, "%u", rid);
27261af68beaSAlexander Stetsenko 	} else {
27271af68beaSAlexander Stetsenko 		if (nvlist_add_string(props, "name", name) != 0)
27281af68beaSAlexander Stetsenko 			nomem();
27291af68beaSAlexander Stetsenko 		namelen = strlen(name);
27301af68beaSAlexander Stetsenko 	}
273189f5d17bSYuri Pankov 	nameidx = us_field_index("name");
273289f5d17bSYuri Pankov 	if (namelen > cb->cb_width[nameidx])
273389f5d17bSYuri Pankov 		cb->cb_width[nameidx] = namelen;
27341af68beaSAlexander Stetsenko 
273589f5d17bSYuri Pankov 	/*
273689f5d17bSYuri Pankov 	 * Check if this type/name combination is in the list and update it;
273789f5d17bSYuri Pankov 	 * otherwise add new node to the list.
273889f5d17bSYuri Pankov 	 */
273989f5d17bSYuri Pankov 	if ((n = uu_avl_find(avl, node, &sortinfo, &idx)) == NULL) {
27401af68beaSAlexander Stetsenko 		uu_avl_insert(avl, node, idx);
274189f5d17bSYuri Pankov 	} else {
27421af68beaSAlexander Stetsenko 		nvlist_free(props);
27431af68beaSAlexander Stetsenko 		free(node);
27441af68beaSAlexander Stetsenko 		node = n;
27451af68beaSAlexander Stetsenko 		props = node->usn_nvl;
27461af68beaSAlexander Stetsenko 	}
27471af68beaSAlexander Stetsenko 
274889f5d17bSYuri Pankov 	/* Calculate/update width of USED/QUOTA fields */
2749f67950b2SNasf-Fan 	if (cb->cb_nicenum) {
2750f67950b2SNasf-Fan 		if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED ||
2751f67950b2SNasf-Fan 		    prop == ZFS_PROP_USERQUOTA || prop == ZFS_PROP_GROUPQUOTA ||
2752f67950b2SNasf-Fan 		    prop == ZFS_PROP_PROJECTUSED ||
2753f67950b2SNasf-Fan 		    prop == ZFS_PROP_PROJECTQUOTA) {
27546520eed5SToomas Soome 			zfs_nicebytes(space, sizebuf, sizeof (sizebuf));
2755f67950b2SNasf-Fan 		} else {
2756f67950b2SNasf-Fan 			zfs_nicenum(space, sizebuf, sizeof (sizebuf));
2757f67950b2SNasf-Fan 		}
2758f67950b2SNasf-Fan 	} else {
275989f5d17bSYuri Pankov 		(void) snprintf(sizebuf, sizeof (sizebuf), "%llu", space);
2760f67950b2SNasf-Fan 	}
276189f5d17bSYuri Pankov 	sizelen = strlen(sizebuf);
2762f67950b2SNasf-Fan 	if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED ||
2763f67950b2SNasf-Fan 	    prop == ZFS_PROP_PROJECTUSED) {
276489f5d17bSYuri Pankov 		propname = "used";
276589f5d17bSYuri Pankov 		if (!nvlist_exists(props, "quota"))
276689f5d17bSYuri Pankov 			(void) nvlist_add_uint64(props, "quota", 0);
2767f67950b2SNasf-Fan 	} else if (prop == ZFS_PROP_USERQUOTA || prop == ZFS_PROP_GROUPQUOTA ||
2768f67950b2SNasf-Fan 	    prop == ZFS_PROP_PROJECTQUOTA) {
276989f5d17bSYuri Pankov 		propname = "quota";
277089f5d17bSYuri Pankov 		if (!nvlist_exists(props, "used"))
277189f5d17bSYuri Pankov 			(void) nvlist_add_uint64(props, "used", 0);
2772f67950b2SNasf-Fan 	} else if (prop == ZFS_PROP_USEROBJUSED ||
2773f67950b2SNasf-Fan 	    prop == ZFS_PROP_GROUPOBJUSED || prop == ZFS_PROP_PROJECTOBJUSED) {
2774f67950b2SNasf-Fan 		propname = "objused";
2775f67950b2SNasf-Fan 		if (!nvlist_exists(props, "objquota"))
2776f67950b2SNasf-Fan 			(void) nvlist_add_uint64(props, "objquota", 0);
2777f67950b2SNasf-Fan 	} else if (prop == ZFS_PROP_USEROBJQUOTA ||
2778f67950b2SNasf-Fan 	    prop == ZFS_PROP_GROUPOBJQUOTA ||
2779f67950b2SNasf-Fan 	    prop == ZFS_PROP_PROJECTOBJQUOTA) {
2780f67950b2SNasf-Fan 		propname = "objquota";
2781f67950b2SNasf-Fan 		if (!nvlist_exists(props, "objused"))
2782f67950b2SNasf-Fan 			(void) nvlist_add_uint64(props, "objused", 0);
2783f67950b2SNasf-Fan 	} else {
2784f67950b2SNasf-Fan 		return (-1);
278589f5d17bSYuri Pankov 	}
278689f5d17bSYuri Pankov 	sizeidx = us_field_index(propname);
278789f5d17bSYuri Pankov 	if (sizelen > cb->cb_width[sizeidx])
278889f5d17bSYuri Pankov 		cb->cb_width[sizeidx] = sizelen;
278989f5d17bSYuri Pankov 
27901af68beaSAlexander Stetsenko 	if (nvlist_add_uint64(props, propname, space) != 0)
27911af68beaSAlexander Stetsenko 		nomem();
27920aea4b19SMatthew Ahrens 
27930aea4b19SMatthew Ahrens 	return (0);
279414843421SMatthew Ahrens }
279514843421SMatthew Ahrens 
27961af68beaSAlexander Stetsenko static void
print_us_node(boolean_t scripted,boolean_t parsable,int * fields,int types,size_t * width,us_node_t * node)279789f5d17bSYuri Pankov print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types,
279889f5d17bSYuri Pankov     size_t *width, us_node_t *node)
27991af68beaSAlexander Stetsenko {
28001af68beaSAlexander Stetsenko 	nvlist_t *nvl = node->usn_nvl;
28019adfa60dSMatthew Ahrens 	char valstr[MAXNAMELEN];
28021af68beaSAlexander Stetsenko 	boolean_t first = B_TRUE;
280389f5d17bSYuri Pankov 	int cfield = 0;
280489f5d17bSYuri Pankov 	int field;
280589f5d17bSYuri Pankov 	uint32_t ustype;
28061af68beaSAlexander Stetsenko 
280789f5d17bSYuri Pankov 	/* Check type */
280889f5d17bSYuri Pankov 	(void) nvlist_lookup_uint32(nvl, "type", &ustype);
280989f5d17bSYuri Pankov 	if (!(ustype & types))
281089f5d17bSYuri Pankov 		return;
281189f5d17bSYuri Pankov 
281289f5d17bSYuri Pankov 	while ((field = fields[cfield]) != USFIELD_LAST) {
281389f5d17bSYuri Pankov 		nvpair_t *nvp = NULL;
281489f5d17bSYuri Pankov 		data_type_t type;
281589f5d17bSYuri Pankov 		uint32_t val32;
281689f5d17bSYuri Pankov 		uint64_t val64;
2817f67950b2SNasf-Fan 		char *strval = "-";
28181af68beaSAlexander Stetsenko 
28191af68beaSAlexander Stetsenko 		while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
282089f5d17bSYuri Pankov 			if (strcmp(nvpair_name(nvp),
282189f5d17bSYuri Pankov 			    us_field_names[field]) == 0)
28221af68beaSAlexander Stetsenko 				break;
28231af68beaSAlexander Stetsenko 		}
28241af68beaSAlexander Stetsenko 
2825f67950b2SNasf-Fan 		type = nvp == NULL ? DATA_TYPE_UNKNOWN : nvpair_type(nvp);
28261af68beaSAlexander Stetsenko 		switch (type) {
28271af68beaSAlexander Stetsenko 		case DATA_TYPE_UINT32:
28281af68beaSAlexander Stetsenko 			(void) nvpair_value_uint32(nvp, &val32);
28291af68beaSAlexander Stetsenko 			break;
28301af68beaSAlexander Stetsenko 		case DATA_TYPE_UINT64:
28311af68beaSAlexander Stetsenko 			(void) nvpair_value_uint64(nvp, &val64);
28321af68beaSAlexander Stetsenko 			break;
28331af68beaSAlexander Stetsenko 		case DATA_TYPE_STRING:
28341af68beaSAlexander Stetsenko 			(void) nvpair_value_string(nvp, &strval);
28351af68beaSAlexander Stetsenko 			break;
2836f67950b2SNasf-Fan 		case DATA_TYPE_UNKNOWN:
2837f67950b2SNasf-Fan 			break;
28381af68beaSAlexander Stetsenko 		default:
283989f5d17bSYuri Pankov 			(void) fprintf(stderr, "invalid data type\n");
28401af68beaSAlexander Stetsenko 		}
28411af68beaSAlexander Stetsenko 
28421af68beaSAlexander Stetsenko 		switch (field) {
28431af68beaSAlexander Stetsenko 		case USFIELD_TYPE:
2844f67950b2SNasf-Fan 			if (type == DATA_TYPE_UINT32)
28451af68beaSAlexander Stetsenko 				strval = (char *)us_type2str(val32);
28461af68beaSAlexander Stetsenko 			break;
28471af68beaSAlexander Stetsenko 		case USFIELD_NAME:
28481af68beaSAlexander Stetsenko 			if (type == DATA_TYPE_UINT64) {
28491af68beaSAlexander Stetsenko 				(void) sprintf(valstr, "%llu", val64);
28501af68beaSAlexander Stetsenko 				strval = valstr;
28511af68beaSAlexander Stetsenko 			}
28521af68beaSAlexander Stetsenko 			break;
28531af68beaSAlexander Stetsenko 		case USFIELD_USED:
28541af68beaSAlexander Stetsenko 		case USFIELD_QUOTA:
28556520eed5SToomas Soome 			if (type == DATA_TYPE_UINT64) {
28566520eed5SToomas Soome 				if (parsable) {
28576520eed5SToomas Soome 					(void) sprintf(valstr, "%llu", val64);
28586520eed5SToomas Soome 					strval = valstr;
28596520eed5SToomas Soome 				} else if (field == USFIELD_QUOTA &&
28606520eed5SToomas Soome 				    val64 == 0) {
28616520eed5SToomas Soome 					strval = "none";
28626520eed5SToomas Soome 				} else {
28636520eed5SToomas Soome 					zfs_nicebytes(val64, valstr,
28646520eed5SToomas Soome 					    sizeof (valstr));
28656520eed5SToomas Soome 					strval = valstr;
28666520eed5SToomas Soome 				}
28676520eed5SToomas Soome 			}
28686520eed5SToomas Soome 			break;
2869f67950b2SNasf-Fan 		case USFIELD_OBJUSED:
2870f67950b2SNasf-Fan 		case USFIELD_OBJQUOTA:
28711af68beaSAlexander Stetsenko 			if (type == DATA_TYPE_UINT64) {
287289f5d17bSYuri Pankov 				if (parsable) {
28731af68beaSAlexander Stetsenko 					(void) sprintf(valstr, "%llu", val64);
28746520eed5SToomas Soome 					strval = valstr;
28756520eed5SToomas Soome 				} else if (field == USFIELD_OBJQUOTA &&
28766520eed5SToomas Soome 				    val64 == 0) {
28776520eed5SToomas Soome 					strval = "none";
287889f5d17bSYuri Pankov 				} else {
28791af68beaSAlexander Stetsenko 					zfs_nicenum(val64, valstr,
28801af68beaSAlexander Stetsenko 					    sizeof (valstr));
28811af68beaSAlexander Stetsenko 					strval = valstr;
28821af68beaSAlexander Stetsenko 				}
28836520eed5SToomas Soome 			}
28841af68beaSAlexander Stetsenko 			break;
28851af68beaSAlexander Stetsenko 		}
28861af68beaSAlexander Stetsenko 
288789f5d17bSYuri Pankov 		if (!first) {
288889f5d17bSYuri Pankov 			if (scripted)
288989f5d17bSYuri Pankov 				(void) printf("\t");
28901af68beaSAlexander Stetsenko 			else
289189f5d17bSYuri Pankov 				(void) printf("  ");
28921af68beaSAlexander Stetsenko 		}
289389f5d17bSYuri Pankov 		if (scripted)
289489f5d17bSYuri Pankov 			(void) printf("%s", strval);
289589f5d17bSYuri Pankov 		else if (field == USFIELD_TYPE || field == USFIELD_NAME)
289689f5d17bSYuri Pankov 			(void) printf("%-*s", width[field], strval);
289789f5d17bSYuri Pankov 		else
289889f5d17bSYuri Pankov 			(void) printf("%*s", width[field], strval);
28991af68beaSAlexander Stetsenko 
29001af68beaSAlexander Stetsenko 		first = B_FALSE;
290189f5d17bSYuri Pankov 		cfield++;
29021af68beaSAlexander Stetsenko 	}
29031af68beaSAlexander Stetsenko 
29041af68beaSAlexander Stetsenko 	(void) printf("\n");
29051af68beaSAlexander Stetsenko }
29061af68beaSAlexander Stetsenko 
29071af68beaSAlexander Stetsenko static void
print_us(boolean_t scripted,boolean_t parsable,int * fields,int types,size_t * width,boolean_t rmnode,uu_avl_t * avl)290889f5d17bSYuri Pankov print_us(boolean_t scripted, boolean_t parsable, int *fields, int types,
290989f5d17bSYuri Pankov     size_t *width, boolean_t rmnode, uu_avl_t *avl)
29101af68beaSAlexander Stetsenko {
29111af68beaSAlexander Stetsenko 	us_node_t *node;
29121af68beaSAlexander Stetsenko 	const char *col;
291389f5d17bSYuri Pankov 	int cfield = 0;
291489f5d17bSYuri Pankov 	int field;
29151af68beaSAlexander Stetsenko 
29161af68beaSAlexander Stetsenko 	if (!scripted) {
29171af68beaSAlexander Stetsenko 		boolean_t first = B_TRUE;
29181af68beaSAlexander Stetsenko 
291989f5d17bSYuri Pankov 		while ((field = fields[cfield]) != USFIELD_LAST) {
292089f5d17bSYuri Pankov 			col = gettext(us_field_hdr[field]);
292189f5d17bSYuri Pankov 			if (field == USFIELD_TYPE || field == USFIELD_NAME) {
292289f5d17bSYuri Pankov 				(void) printf(first ? "%-*s" : "  %-*s",
292389f5d17bSYuri Pankov 				    width[field], col);
292489f5d17bSYuri Pankov 			} else {
292589f5d17bSYuri Pankov 				(void) printf(first ? "%*s" : "  %*s",
292689f5d17bSYuri Pankov 				    width[field], col);
292789f5d17bSYuri Pankov 			}
29281af68beaSAlexander Stetsenko 			first = B_FALSE;
292989f5d17bSYuri Pankov 			cfield++;
29301af68beaSAlexander Stetsenko 		}
29311af68beaSAlexander Stetsenko 		(void) printf("\n");
29321af68beaSAlexander Stetsenko 	}
29331af68beaSAlexander Stetsenko 
293489f5d17bSYuri Pankov 	for (node = uu_avl_first(avl); node; node = uu_avl_next(avl, node)) {
293589f5d17bSYuri Pankov 		print_us_node(scripted, parsable, fields, types, width, node);
29361af68beaSAlexander Stetsenko 		if (rmnode)
29371af68beaSAlexander Stetsenko 			nvlist_free(node->usn_nvl);
29381af68beaSAlexander Stetsenko 	}
29391af68beaSAlexander Stetsenko }
29401af68beaSAlexander Stetsenko 
294114843421SMatthew Ahrens static int
zfs_do_userspace(int argc,char ** argv)294214843421SMatthew Ahrens zfs_do_userspace(int argc, char **argv)
294314843421SMatthew Ahrens {
294414843421SMatthew Ahrens 	zfs_handle_t *zhp;
294514843421SMatthew Ahrens 	zfs_userquota_prop_t p;
29461af68beaSAlexander Stetsenko 	uu_avl_pool_t *avl_pool;
29471af68beaSAlexander Stetsenko 	uu_avl_t *avl_tree;
29481af68beaSAlexander Stetsenko 	uu_avl_walk_t *walk;
294989f5d17bSYuri Pankov 	char *delim;
2950f67950b2SNasf-Fan 	char deffields[] = "type,name,used,quota,objused,objquota";
295189f5d17bSYuri Pankov 	char *ofield = NULL;
295289f5d17bSYuri Pankov 	char *tfield = NULL;
295389f5d17bSYuri Pankov 	int cfield = 0;
295489f5d17bSYuri Pankov 	int fields[256];
295589f5d17bSYuri Pankov 	int i;
29561af68beaSAlexander Stetsenko 	boolean_t scripted = B_FALSE;
29571af68beaSAlexander Stetsenko 	boolean_t prtnum = B_FALSE;
295889f5d17bSYuri Pankov 	boolean_t parsable = B_FALSE;
29591af68beaSAlexander Stetsenko 	boolean_t sid2posix = B_FALSE;
296070f56fa6SYuri Pankov 	int ret = 0;
29611af68beaSAlexander Stetsenko 	int c;
29621af68beaSAlexander Stetsenko 	zfs_sort_column_t *sortcol = NULL;
296389f5d17bSYuri Pankov 	int types = USTYPE_PSX_USR | USTYPE_SMB_USR;
29641af68beaSAlexander Stetsenko 	us_cbdata_t cb;
29651af68beaSAlexander Stetsenko 	us_node_t *node;
296689f5d17bSYuri Pankov 	us_node_t *rmnode;
296789f5d17bSYuri Pankov 	uu_list_pool_t *listpool;
296889f5d17bSYuri Pankov 	uu_list_t *list;
296989f5d17bSYuri Pankov 	uu_avl_index_t idx = 0;
297089f5d17bSYuri Pankov 	uu_list_index_t idx2 = 0;
297114843421SMatthew Ahrens 
29721af68beaSAlexander Stetsenko 	if (argc < 2)
29731af68beaSAlexander Stetsenko 		usage(B_FALSE);
297414843421SMatthew Ahrens 
2975f67950b2SNasf-Fan 	if (strcmp(argv[0], "groupspace") == 0) {
297689f5d17bSYuri Pankov 		/* Toggle default group types */
29771af68beaSAlexander Stetsenko 		types = USTYPE_PSX_GRP | USTYPE_SMB_GRP;
2978f67950b2SNasf-Fan 	} else if (strcmp(argv[0], "projectspace") == 0) {
2979f67950b2SNasf-Fan 		types = USTYPE_PROJ;
2980f67950b2SNasf-Fan 		prtnum = B_TRUE;
2981f67950b2SNasf-Fan 	}
29821af68beaSAlexander Stetsenko 
29831af68beaSAlexander Stetsenko 	while ((c = getopt(argc, argv, "nHpo:s:S:t:i")) != -1) {
29841af68beaSAlexander Stetsenko 		switch (c) {
29851af68beaSAlexander Stetsenko 		case 'n':
2986f67950b2SNasf-Fan 			if (types == USTYPE_PROJ) {
2987f67950b2SNasf-Fan 				(void) fprintf(stderr,
2988f67950b2SNasf-Fan 				    gettext("invalid option 'n'\n"));
2989f67950b2SNasf-Fan 				usage(B_FALSE);
2990f67950b2SNasf-Fan 			}
29911af68beaSAlexander Stetsenko 			prtnum = B_TRUE;
29921af68beaSAlexander Stetsenko 			break;
29931af68beaSAlexander Stetsenko 		case 'H':
29941af68beaSAlexander Stetsenko 			scripted = B_TRUE;
29951af68beaSAlexander Stetsenko 			break;
29961af68beaSAlexander Stetsenko 		case 'p':
299789f5d17bSYuri Pankov 			parsable = B_TRUE;
29981af68beaSAlexander Stetsenko 			break;
29991af68beaSAlexander Stetsenko 		case 'o':
300089f5d17bSYuri Pankov 			ofield = optarg;
30011af68beaSAlexander Stetsenko 			break;
30021af68beaSAlexander Stetsenko 		case 's':
30031af68beaSAlexander Stetsenko 		case 'S':
30041af68beaSAlexander Stetsenko 			if (zfs_add_sort_column(&sortcol, optarg,
300589f5d17bSYuri Pankov 			    c == 's' ? B_FALSE : B_TRUE) != 0) {
30061af68beaSAlexander Stetsenko 				(void) fprintf(stderr,
300789f5d17bSYuri Pankov 				    gettext("invalid field '%s'\n"), optarg);
30081af68beaSAlexander Stetsenko 				usage(B_FALSE);
30091af68beaSAlexander Stetsenko 			}
30101af68beaSAlexander Stetsenko 			break;
30111af68beaSAlexander Stetsenko 		case 't':
3012f67950b2SNasf-Fan 			if (types == USTYPE_PROJ) {
3013f67950b2SNasf-Fan 				(void) fprintf(stderr,
3014f67950b2SNasf-Fan 				    gettext("invalid option 't'\n"));
3015f67950b2SNasf-Fan 				usage(B_FALSE);
3016f67950b2SNasf-Fan 			}
301789f5d17bSYuri Pankov 			tfield = optarg;
30181af68beaSAlexander Stetsenko 			break;
30191af68beaSAlexander Stetsenko 		case 'i':
3020f67950b2SNasf-Fan 			if (types == USTYPE_PROJ) {
3021f67950b2SNasf-Fan 				(void) fprintf(stderr,
3022f67950b2SNasf-Fan 				    gettext("invalid option 'i'\n"));
3023f67950b2SNasf-Fan 				usage(B_FALSE);
3024f67950b2SNasf-Fan 			}
30251af68beaSAlexander Stetsenko 			sid2posix = B_TRUE;
30261af68beaSAlexander Stetsenko 			break;
30271af68beaSAlexander Stetsenko 		case ':':
30281af68beaSAlexander Stetsenko 			(void) fprintf(stderr, gettext("missing argument for "
30291af68beaSAlexander Stetsenko 			    "'%c' option\n"), optopt);
30301af68beaSAlexander Stetsenko 			usage(B_FALSE);
30311af68beaSAlexander Stetsenko 			break;
30321af68beaSAlexander Stetsenko 		case '?':
30331af68beaSAlexander Stetsenko 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
30341af68beaSAlexander Stetsenko 			    optopt);
30351af68beaSAlexander Stetsenko 			usage(B_FALSE);
30361af68beaSAlexander Stetsenko 		}
30371af68beaSAlexander Stetsenko 	}
30381af68beaSAlexander Stetsenko 
30391af68beaSAlexander Stetsenko 	argc -= optind;
30401af68beaSAlexander Stetsenko 	argv += optind;
30411af68beaSAlexander Stetsenko 
304289f5d17bSYuri Pankov 	if (argc < 1) {
304389f5d17bSYuri Pankov 		(void) fprintf(stderr, gettext("missing dataset name\n"));
304489f5d17bSYuri Pankov 		usage(B_FALSE);
304589f5d17bSYuri Pankov 	}
304689f5d17bSYuri Pankov 	if (argc > 1) {
304789f5d17bSYuri Pankov 		(void) fprintf(stderr, gettext("too many arguments\n"));
304889f5d17bSYuri Pankov 		usage(B_FALSE);
304989f5d17bSYuri Pankov 	}
305089f5d17bSYuri Pankov 
305189f5d17bSYuri Pankov 	/* Use default output fields if not specified using -o */
305289f5d17bSYuri Pankov 	if (ofield == NULL)
305389f5d17bSYuri Pankov 		ofield = deffields;
305489f5d17bSYuri Pankov 	do {
305589f5d17bSYuri Pankov 		if ((delim = strchr(ofield, ',')) != NULL)
305689f5d17bSYuri Pankov 			*delim = '\0';
305789f5d17bSYuri Pankov 		if ((fields[cfield++] = us_field_index(ofield)) == -1) {
305889f5d17bSYuri Pankov 			(void) fprintf(stderr, gettext("invalid type '%s' "
305989f5d17bSYuri Pankov 			    "for -o option\n"), ofield);
306089f5d17bSYuri Pankov 			return (-1);
306189f5d17bSYuri Pankov 		}
306289f5d17bSYuri Pankov 		if (delim != NULL)
306389f5d17bSYuri Pankov 			ofield = delim + 1;
306489f5d17bSYuri Pankov 	} while (delim != NULL);
306589f5d17bSYuri Pankov 	fields[cfield] = USFIELD_LAST;
306689f5d17bSYuri Pankov 
306789f5d17bSYuri Pankov 	/* Override output types (-t option) */
306889f5d17bSYuri Pankov 	if (tfield != NULL) {
306989f5d17bSYuri Pankov 		types = 0;
307089f5d17bSYuri Pankov 
307189f5d17bSYuri Pankov 		do {
307289f5d17bSYuri Pankov 			boolean_t found = B_FALSE;
307389f5d17bSYuri Pankov 
307489f5d17bSYuri Pankov 			if ((delim = strchr(tfield, ',')) != NULL)
307589f5d17bSYuri Pankov 				*delim = '\0';
307689f5d17bSYuri Pankov 			for (i = 0; i < sizeof (us_type_bits) / sizeof (int);
307789f5d17bSYuri Pankov 			    i++) {
307889f5d17bSYuri Pankov 				if (strcmp(tfield, us_type_names[i]) == 0) {
307989f5d17bSYuri Pankov 					found = B_TRUE;
308089f5d17bSYuri Pankov 					types |= us_type_bits[i];
30811af68beaSAlexander Stetsenko 					break;
30821af68beaSAlexander Stetsenko 				}
30831af68beaSAlexander Stetsenko 			}
308489f5d17bSYuri Pankov 			if (!found) {
308589f5d17bSYuri Pankov 				(void) fprintf(stderr, gettext("invalid type "
308689f5d17bSYuri Pankov 				    "'%s' for -t option\n"), tfield);
308789f5d17bSYuri Pankov 				return (-1);
308889f5d17bSYuri Pankov 			}
308989f5d17bSYuri Pankov 			if (delim != NULL)
309089f5d17bSYuri Pankov 				tfield = delim + 1;
309189f5d17bSYuri Pankov 		} while (delim != NULL);
30921af68beaSAlexander Stetsenko 	}
30931af68beaSAlexander Stetsenko 
309489f5d17bSYuri Pankov 	if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL)
309514843421SMatthew Ahrens 		return (1);
309614843421SMatthew Ahrens 
30971af68beaSAlexander Stetsenko 	if ((avl_pool = uu_avl_pool_create("us_avl_pool", sizeof (us_node_t),
309889f5d17bSYuri Pankov 	    offsetof(us_node_t, usn_avlnode), us_compare, UU_DEFAULT)) == NULL)
30991af68beaSAlexander Stetsenko 		nomem();
31001af68beaSAlexander Stetsenko 	if ((avl_tree = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL)
31011af68beaSAlexander Stetsenko 		nomem();
31021af68beaSAlexander Stetsenko 
310389f5d17bSYuri Pankov 	/* Always add default sorting columns */
310489f5d17bSYuri Pankov 	(void) zfs_add_sort_column(&sortcol, "type", B_FALSE);
310589f5d17bSYuri Pankov 	(void) zfs_add_sort_column(&sortcol, "name", B_FALSE);
310689f5d17bSYuri Pankov 
31071af68beaSAlexander Stetsenko 	cb.cb_sortcol = sortcol;
31081af68beaSAlexander Stetsenko 	cb.cb_numname = prtnum;
310989f5d17bSYuri Pankov 	cb.cb_nicenum = !parsable;
31101af68beaSAlexander Stetsenko 	cb.cb_avl_pool = avl_pool;
31111af68beaSAlexander Stetsenko 	cb.cb_avl = avl_tree;
31121af68beaSAlexander Stetsenko 	cb.cb_sid2posix = sid2posix;
311389f5d17bSYuri Pankov 
311489f5d17bSYuri Pankov 	for (i = 0; i < USFIELD_LAST; i++)
311589f5d17bSYuri Pankov 		cb.cb_width[i] = strlen(gettext(us_field_hdr[i]));
311614843421SMatthew Ahrens 
311714843421SMatthew Ahrens 	for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) {
3118f67950b2SNasf-Fan 		if ((zfs_prop_is_user(p) &&
311989f5d17bSYuri Pankov 		    !(types & (USTYPE_PSX_USR | USTYPE_SMB_USR))) ||
3120f67950b2SNasf-Fan 		    (zfs_prop_is_group(p) &&
3121f67950b2SNasf-Fan 		    !(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP))) ||
3122f67950b2SNasf-Fan 		    (zfs_prop_is_project(p) && types != USTYPE_PROJ))
31231af68beaSAlexander Stetsenko 			continue;
3124f67950b2SNasf-Fan 
31251af68beaSAlexander Stetsenko 		cb.cb_prop = p;
312670f56fa6SYuri Pankov 		if ((ret = zfs_userspace(zhp, p, userspace_cb, &cb)) != 0)
312770f56fa6SYuri Pankov 			return (ret);
312814843421SMatthew Ahrens 	}
31291af68beaSAlexander Stetsenko 
313089f5d17bSYuri Pankov 	/* Sort the list */
313170f56fa6SYuri Pankov 	if ((node = uu_avl_first(avl_tree)) == NULL)
313270f56fa6SYuri Pankov 		return (0);
313370f56fa6SYuri Pankov 
313489f5d17bSYuri Pankov 	us_populated = B_TRUE;
313570f56fa6SYuri Pankov 
31361af68beaSAlexander Stetsenko 	listpool = uu_list_pool_create("tmplist", sizeof (us_node_t),
313789f5d17bSYuri Pankov 	    offsetof(us_node_t, usn_listnode), NULL, UU_DEFAULT);
31381af68beaSAlexander Stetsenko 	list = uu_list_create(listpool, NULL, UU_DEFAULT);
31391af68beaSAlexander Stetsenko 	uu_list_node_init(node, &node->usn_listnode, listpool);
314089f5d17bSYuri Pankov 
31411af68beaSAlexander Stetsenko 	while (node != NULL) {
31421af68beaSAlexander Stetsenko 		rmnode = node;
31431af68beaSAlexander Stetsenko 		node = uu_avl_next(avl_tree, node);
31441af68beaSAlexander Stetsenko 		uu_avl_remove(avl_tree, rmnode);
314589f5d17bSYuri Pankov 		if (uu_list_find(list, rmnode, NULL, &idx2) == NULL)
31461af68beaSAlexander Stetsenko 			uu_list_insert(list, rmnode, idx2);
31471af68beaSAlexander Stetsenko 	}
31481af68beaSAlexander Stetsenko 
31491af68beaSAlexander Stetsenko 	for (node = uu_list_first(list); node != NULL;
31501af68beaSAlexander Stetsenko 	    node = uu_list_next(list, node)) {
31511af68beaSAlexander Stetsenko 		us_sort_info_t sortinfo = { sortcol, cb.cb_numname };
315289f5d17bSYuri Pankov 
315389f5d17bSYuri Pankov 		if (uu_avl_find(avl_tree, node, &sortinfo, &idx) == NULL)
31541af68beaSAlexander Stetsenko 			uu_avl_insert(avl_tree, node, idx);
31551af68beaSAlexander Stetsenko 	}
31561af68beaSAlexander Stetsenko 
31571af68beaSAlexander Stetsenko 	uu_list_destroy(list);
315889f5d17bSYuri Pankov 	uu_list_pool_destroy(listpool);
31591af68beaSAlexander Stetsenko 
316089f5d17bSYuri Pankov 	/* Print and free node nvlist memory */
316189f5d17bSYuri Pankov 	print_us(scripted, parsable, fields, types, cb.cb_width, B_TRUE,
316289f5d17bSYuri Pankov 	    cb.cb_avl);
31631af68beaSAlexander Stetsenko 
31641af68beaSAlexander Stetsenko 	zfs_free_sort_columns(sortcol);
31651af68beaSAlexander Stetsenko 
316689f5d17bSYuri Pankov 	/* Clean up the AVL tree */
31671af68beaSAlexander Stetsenko 	if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL)
31681af68beaSAlexander Stetsenko 		nomem();
31691af68beaSAlexander Stetsenko 
31701af68beaSAlexander Stetsenko 	while ((node = uu_avl_walk_next(walk)) != NULL) {
31711af68beaSAlexander Stetsenko 		uu_avl_remove(cb.cb_avl, node);
31721af68beaSAlexander Stetsenko 		free(node);
31731af68beaSAlexander Stetsenko 	}
31741af68beaSAlexander Stetsenko 
31751af68beaSAlexander Stetsenko 	uu_avl_walk_end(walk);
31761af68beaSAlexander Stetsenko 	uu_avl_destroy(avl_tree);
31771af68beaSAlexander Stetsenko 	uu_avl_pool_destroy(avl_pool);
31781af68beaSAlexander Stetsenko 
317970f56fa6SYuri Pankov 	return (ret);
318014843421SMatthew Ahrens }
318114843421SMatthew Ahrens 
318214843421SMatthew Ahrens /*
318343d68d68SYuri Pankov  * list [-Hp][-r|-d max] [-o property[,...]] [-s property] ... [-S property] ...
318443d68d68SYuri Pankov  *      [-t type[,...]] [filesystem|volume|snapshot] ...
3185fa9e4066Sahrens  *
318643d68d68SYuri Pankov  *	-H	Scripted mode; elide headers and separate columns by tabs.
318743d68d68SYuri Pankov  *	-p	Display values in parsable (literal) format.
318843d68d68SYuri Pankov  *	-r	Recurse over all children.
3189ae1726b6SChris Gerhard  *	-d	Limit recursion by depth.
3190fa9e4066Sahrens  *	-o	Control which fields to display.
3191b6825278Ssjelinek  *	-s	Specify sort columns, descending order.
3192b6825278Ssjelinek  *	-S	Specify sort columns, ascending order.
319343d68d68SYuri Pankov  *	-t	Control which object types to display.
3194fa9e4066Sahrens  *
319543d68d68SYuri Pankov  * When given no arguments, list all filesystems in the system.
3196fa9e4066Sahrens  * Otherwise, list the specified datasets, optionally recursing down them if
3197fa9e4066Sahrens  * '-r' is specified.
3198fa9e4066Sahrens  */
3199fa9e4066Sahrens typedef struct list_cbdata {
320099653d4eSeschrock 	boolean_t	cb_first;
320143d68d68SYuri Pankov 	boolean_t	cb_literal;
320299653d4eSeschrock 	boolean_t	cb_scripted;
3203990b4856Slling 	zprop_list_t	*cb_proplist;
3204fa9e4066Sahrens } list_cbdata_t;
3205fa9e4066Sahrens 
3206fa9e4066Sahrens /*
3207fa9e4066Sahrens  * Given a list of columns to display, output appropriate headers for each one.
3208fa9e4066Sahrens  */
3209fa9e4066Sahrens static void
print_header(list_cbdata_t * cb)321043d68d68SYuri Pankov print_header(list_cbdata_t *cb)
3211fa9e4066Sahrens {
321243d68d68SYuri Pankov 	zprop_list_t *pl = cb->cb_proplist;
3213e9dbad6fSeschrock 	char headerbuf[ZFS_MAXPROPLEN];
3214e9dbad6fSeschrock 	const char *header;
3215fa9e4066Sahrens 	int i;
3216e9dbad6fSeschrock 	boolean_t first = B_TRUE;
3217e9dbad6fSeschrock 	boolean_t right_justify;
3218fa9e4066Sahrens 
3219e9dbad6fSeschrock 	for (; pl != NULL; pl = pl->pl_next) {
3220e9dbad6fSeschrock 		if (!first) {
3221fa9e4066Sahrens 			(void) printf("  ");
3222e9dbad6fSeschrock 		} else {
3223e9dbad6fSeschrock 			first = B_FALSE;
3224e9dbad6fSeschrock 		}
3225e9dbad6fSeschrock 
3226e9dbad6fSeschrock 		right_justify = B_FALSE;
3227990b4856Slling 		if (pl->pl_prop != ZPROP_INVAL) {
3228e9dbad6fSeschrock 			header = zfs_prop_column_name(pl->pl_prop);
3229e9dbad6fSeschrock 			right_justify = zfs_prop_align_right(pl->pl_prop);
3230e9dbad6fSeschrock 		} else {
3231e9dbad6fSeschrock 			for (i = 0; pl->pl_user_prop[i] != '\0'; i++)
3232e9dbad6fSeschrock 				headerbuf[i] = toupper(pl->pl_user_prop[i]);
3233e9dbad6fSeschrock 			headerbuf[i] = '\0';
3234e9dbad6fSeschrock 			header = headerbuf;
3235e9dbad6fSeschrock 		}
3236e9dbad6fSeschrock 
3237e9dbad6fSeschrock 		if (pl->pl_next == NULL && !right_justify)
3238e9dbad6fSeschrock 			(void) printf("%s", header);
3239e9dbad6fSeschrock 		else if (right_justify)
3240e9dbad6fSeschrock 			(void) printf("%*s", pl->pl_width, header);
3241e9dbad6fSeschrock 		else
3242e9dbad6fSeschrock 			(void) printf("%-*s", pl->pl_width, header);
3243fa9e4066Sahrens 	}
3244fa9e4066Sahrens 
3245fa9e4066Sahrens 	(void) printf("\n");
3246fa9e4066Sahrens }
3247fa9e4066Sahrens 
3248fa9e4066Sahrens /*
3249fa9e4066Sahrens  * Given a dataset and a list of fields, print out all the properties according
3250fa9e4066Sahrens  * to the described layout.
3251fa9e4066Sahrens  */
3252fa9e4066Sahrens static void
print_dataset(zfs_handle_t * zhp,list_cbdata_t * cb)325343d68d68SYuri Pankov print_dataset(zfs_handle_t *zhp, list_cbdata_t *cb)
3254fa9e4066Sahrens {
325543d68d68SYuri Pankov 	zprop_list_t *pl = cb->cb_proplist;
3256e9dbad6fSeschrock 	boolean_t first = B_TRUE;
3257fa9e4066Sahrens 	char property[ZFS_MAXPROPLEN];
3258e9dbad6fSeschrock 	nvlist_t *userprops = zfs_get_user_props(zhp);
3259e9dbad6fSeschrock 	nvlist_t *propval;
3260e9dbad6fSeschrock 	char *propstr;
3261e9dbad6fSeschrock 	boolean_t right_justify;
3262fa9e4066Sahrens 
3263e9dbad6fSeschrock 	for (; pl != NULL; pl = pl->pl_next) {
3264e9dbad6fSeschrock 		if (!first) {
326543d68d68SYuri Pankov 			if (cb->cb_scripted)
3266fa9e4066Sahrens 				(void) printf("\t");
3267fa9e4066Sahrens 			else
3268fa9e4066Sahrens 				(void) printf("  ");
3269e9dbad6fSeschrock 		} else {
3270e9dbad6fSeschrock 			first = B_FALSE;
3271fa9e4066Sahrens 		}
3272fa9e4066Sahrens 
32730d8fa8f8SMartin Matuska 		if (pl->pl_prop == ZFS_PROP_NAME) {
32740d8fa8f8SMartin Matuska 			(void) strlcpy(property, zfs_get_name(zhp),
32750d8fa8f8SMartin Matuska 			    sizeof (property));
32760d8fa8f8SMartin Matuska 			propstr = property;
32770d8fa8f8SMartin Matuska 			right_justify = zfs_prop_align_right(pl->pl_prop);
32780d8fa8f8SMartin Matuska 		} else if (pl->pl_prop != ZPROP_INVAL) {
3279e9dbad6fSeschrock 			if (zfs_prop_get(zhp, pl->pl_prop, property,
328043d68d68SYuri Pankov 			    sizeof (property), NULL, NULL, 0,
328143d68d68SYuri Pankov 			    cb->cb_literal) != 0)
3282e9dbad6fSeschrock 				propstr = "-";
3283e9dbad6fSeschrock 			else
3284e9dbad6fSeschrock 				propstr = property;
3285e9dbad6fSeschrock 			right_justify = zfs_prop_align_right(pl->pl_prop);
328614843421SMatthew Ahrens 		} else if (zfs_prop_userquota(pl->pl_user_prop)) {
328714843421SMatthew Ahrens 			if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,
328843d68d68SYuri Pankov 			    property, sizeof (property), cb->cb_literal) != 0)
328914843421SMatthew Ahrens 				propstr = "-";
329014843421SMatthew Ahrens 			else
329114843421SMatthew Ahrens 				propstr = property;
329214843421SMatthew Ahrens 			right_justify = B_TRUE;
329319b94df9SMatthew Ahrens 		} else if (zfs_prop_written(pl->pl_user_prop)) {
329419b94df9SMatthew Ahrens 			if (zfs_prop_get_written(zhp, pl->pl_user_prop,
329543d68d68SYuri Pankov 			    property, sizeof (property), cb->cb_literal) != 0)
329619b94df9SMatthew Ahrens 				propstr = "-";
329719b94df9SMatthew Ahrens 			else
329819b94df9SMatthew Ahrens 				propstr = property;
329919b94df9SMatthew Ahrens 			right_justify = B_TRUE;
3300e9dbad6fSeschrock 		} else {
3301e9dbad6fSeschrock 			if (nvlist_lookup_nvlist(userprops,
3302e9dbad6fSeschrock 			    pl->pl_user_prop, &propval) != 0)
3303e9dbad6fSeschrock 				propstr = "-";
3304e9dbad6fSeschrock 			else
3305e9dbad6fSeschrock 				verify(nvlist_lookup_string(propval,
3306990b4856Slling 				    ZPROP_VALUE, &propstr) == 0);
330714843421SMatthew Ahrens 			right_justify = B_FALSE;
3308e9dbad6fSeschrock 		}
3309e9dbad6fSeschrock 
331007ba0419Seschrock 		/*
331107ba0419Seschrock 		 * If this is being called in scripted mode, or if this is the
331207ba0419Seschrock 		 * last column and it is left-justified, don't include a width
331307ba0419Seschrock 		 * format specifier.
331407ba0419Seschrock 		 */
331543d68d68SYuri Pankov 		if (cb->cb_scripted || (pl->pl_next == NULL && !right_justify))
3316e9dbad6fSeschrock 			(void) printf("%s", propstr);
3317e9dbad6fSeschrock 		else if (right_justify)
331843d68d68SYuri Pankov 			(void) printf("%*s", pl->pl_width, propstr);
3319e9dbad6fSeschrock 		else
332043d68d68SYuri Pankov 			(void) printf("%-*s", pl->pl_width, propstr);
3321fa9e4066Sahrens 	}
3322fa9e4066Sahrens 
3323fa9e4066Sahrens 	(void) printf("\n");
3324fa9e4066Sahrens }
3325fa9e4066Sahrens 
3326fa9e4066Sahrens /*
3327fa9e4066Sahrens  * Generic callback function to list a dataset or snapshot.
3328fa9e4066Sahrens  */
3329fa9e4066Sahrens static int
list_callback(zfs_handle_t * zhp,void * data)3330fa9e4066Sahrens list_callback(zfs_handle_t *zhp, void *data)
3331fa9e4066Sahrens {
3332fa9e4066Sahrens 	list_cbdata_t *cbp = data;
3333fa9e4066Sahrens 
3334fa9e4066Sahrens 	if (cbp->cb_first) {
3335fa9e4066Sahrens 		if (!cbp->cb_scripted)
333643d68d68SYuri Pankov 			print_header(cbp);
333799653d4eSeschrock 		cbp->cb_first = B_FALSE;
3338fa9e4066Sahrens 	}
3339fa9e4066Sahrens 
334043d68d68SYuri Pankov 	print_dataset(zhp, cbp);
3341fa9e4066Sahrens 
3342fa9e4066Sahrens 	return (0);
3343fa9e4066Sahrens }
3344fa9e4066Sahrens 
3345fa9e4066Sahrens static int
zfs_do_list(int argc,char ** argv)3346fa9e4066Sahrens zfs_do_list(int argc, char **argv)
3347fa9e4066Sahrens {
3348fa9e4066Sahrens 	int c;
3349fa9e4066Sahrens 	static char default_fields[] =
3350fa9e4066Sahrens 	    "name,used,available,referenced,mountpoint";
33517f73c863SRich Morris 	int types = ZFS_TYPE_DATASET;
3352d5b5bb25SRich Morris 	boolean_t types_specified = B_FALSE;
3353fa9e4066Sahrens 	char *fields = NULL;
3354fa9e4066Sahrens 	list_cbdata_t cb = { 0 };
3355fa9e4066Sahrens 	char *value;
3356ae1726b6SChris Gerhard 	int limit = 0;
335705c998d6SRichard Lowe 	int ret = 0;
3358b6825278Ssjelinek 	zfs_sort_column_t *sortcol = NULL;
3359d5b5bb25SRich Morris 	int flags = ZFS_ITER_PROP_LISTSNAPS | ZFS_ITER_ARGS_CAN_BE_PATHS;
3360fa9e4066Sahrens 
3361fa9e4066Sahrens 	/* check options */
336243d68d68SYuri Pankov 	while ((c = getopt(argc, argv, "HS:d:o:prs:t:")) != -1) {
3363fa9e4066Sahrens 		switch (c) {
3364fa9e4066Sahrens 		case 'o':
3365fa9e4066Sahrens 			fields = optarg;
3366fa9e4066Sahrens 			break;
336743d68d68SYuri Pankov 		case 'p':
336843d68d68SYuri Pankov 			cb.cb_literal = B_TRUE;
336943d68d68SYuri Pankov 			flags |= ZFS_ITER_LITERAL_PROPS;
337043d68d68SYuri Pankov 			break;
3371ae1726b6SChris Gerhard 		case 'd':
3372ae1726b6SChris Gerhard 			limit = parse_depth(optarg, &flags);
3373ae1726b6SChris Gerhard 			break;
3374fa9e4066Sahrens 		case 'r':
3375d5b5bb25SRich Morris 			flags |= ZFS_ITER_RECURSE;
3376fa9e4066Sahrens 			break;
3377fa9e4066Sahrens 		case 'H':
337843d68d68SYuri Pankov 			cb.cb_scripted = B_TRUE;
3379fa9e4066Sahrens 			break;
3380b6825278Ssjelinek 		case 's':
3381e9dbad6fSeschrock 			if (zfs_add_sort_column(&sortcol, optarg,
3382e9dbad6fSeschrock 			    B_FALSE) != 0) {
3383b6825278Ssjelinek 				(void) fprintf(stderr,
3384b6825278Ssjelinek 				    gettext("invalid property '%s'\n"), optarg);
3385b6825278Ssjelinek 				usage(B_FALSE);
3386b6825278Ssjelinek 			}
3387b6825278Ssjelinek 			break;
3388b6825278Ssjelinek 		case 'S':
3389e9dbad6fSeschrock 			if (zfs_add_sort_column(&sortcol, optarg,
3390e9dbad6fSeschrock 			    B_TRUE) != 0) {
3391b6825278Ssjelinek 				(void) fprintf(stderr,
3392b6825278Ssjelinek 				    gettext("invalid property '%s'\n"), optarg);
3393b6825278Ssjelinek 				usage(B_FALSE);
3394b6825278Ssjelinek 			}
3395b6825278Ssjelinek 			break;
3396fa9e4066Sahrens 		case 't':
3397fa9e4066Sahrens 			types = 0;
3398d5b5bb25SRich Morris 			types_specified = B_TRUE;
3399d5b5bb25SRich Morris 			flags &= ~ZFS_ITER_PROP_LISTSNAPS;
3400fa9e4066Sahrens 			while (*optarg != '\0') {
3401d5b5bb25SRich Morris 				static char *type_subopts[] = { "filesystem",
34027dbbcd83SAdam Stevko 				    "volume", "snapshot", "snap", "bookmark",
340378f17100SMatthew Ahrens 				    "all", NULL };
3404d5b5bb25SRich Morris 
3405fa9e4066Sahrens 				switch (getsubopt(&optarg, type_subopts,
3406fa9e4066Sahrens 				    &value)) {
3407fa9e4066Sahrens 				case 0:
3408fa9e4066Sahrens 					types |= ZFS_TYPE_FILESYSTEM;
3409fa9e4066Sahrens 					break;
3410fa9e4066Sahrens 				case 1:
3411fa9e4066Sahrens 					types |= ZFS_TYPE_VOLUME;
3412fa9e4066Sahrens 					break;
3413fa9e4066Sahrens 				case 2:
34147dbbcd83SAdam Stevko 				case 3:
3415fa9e4066Sahrens 					types |= ZFS_TYPE_SNAPSHOT;
3416fa9e4066Sahrens 					break;
34177dbbcd83SAdam Stevko 				case 4:
341878f17100SMatthew Ahrens 					types |= ZFS_TYPE_BOOKMARK;
3419d5b5bb25SRich Morris 					break;
34207dbbcd83SAdam Stevko 				case 5:
342178f17100SMatthew Ahrens 					types = ZFS_TYPE_DATASET |
342278f17100SMatthew Ahrens 					    ZFS_TYPE_BOOKMARK;
342378f17100SMatthew Ahrens 					break;
3424fa9e4066Sahrens 				default:
3425fa9e4066Sahrens 					(void) fprintf(stderr,
3426fa9e4066Sahrens 					    gettext("invalid type '%s'\n"),
3427fa9e4066Sahrens 					    value);
342899653d4eSeschrock 					usage(B_FALSE);
3429fa9e4066Sahrens 				}
3430fa9e4066Sahrens 			}
3431fa9e4066Sahrens 			break;
3432fa9e4066Sahrens 		case ':':
3433fa9e4066Sahrens 			(void) fprintf(stderr, gettext("missing argument for "
3434fa9e4066Sahrens 			    "'%c' option\n"), optopt);
343599653d4eSeschrock 			usage(B_FALSE);
3436fa9e4066Sahrens 			break;
3437fa9e4066Sahrens 		case '?':
3438fa9e4066Sahrens 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
3439fa9e4066Sahrens 			    optopt);
344099653d4eSeschrock 			usage(B_FALSE);
3441fa9e4066Sahrens 		}
3442fa9e4066Sahrens 	}
3443fa9e4066Sahrens 
3444fa9e4066Sahrens 	argc -= optind;
3445fa9e4066Sahrens 	argv += optind;
3446fa9e4066Sahrens 
3447fa9e4066Sahrens 	if (fields == NULL)
344874e7dc98SMatthew Ahrens 		fields = default_fields;
344974e7dc98SMatthew Ahrens 
345074e7dc98SMatthew Ahrens 	/*
34510d8fa8f8SMartin Matuska 	 * If we are only going to list snapshot names and sort by name,
34520d8fa8f8SMartin Matuska 	 * then we can use faster version.
34530d8fa8f8SMartin Matuska 	 */
34540d8fa8f8SMartin Matuska 	if (strcmp(fields, "name") == 0 && zfs_sort_only_by_name(sortcol))
34550d8fa8f8SMartin Matuska 		flags |= ZFS_ITER_SIMPLE;
34560d8fa8f8SMartin Matuska 
34570d8fa8f8SMartin Matuska 	/*
3458d5b5bb25SRich Morris 	 * If "-o space" and no types were specified, don't display snapshots.
345974e7dc98SMatthew Ahrens 	 */
3460d5b5bb25SRich Morris 	if (strcmp(fields, "space") == 0 && types_specified == B_FALSE)
346174e7dc98SMatthew Ahrens 		types &= ~ZFS_TYPE_SNAPSHOT;
3462fa9e4066Sahrens 
346307ba0419Seschrock 	/*
3464990b4856Slling 	 * If the user specifies '-o all', the zprop_get_list() doesn't
346507ba0419Seschrock 	 * normally include the name of the dataset.  For 'zfs list', we always
346607ba0419Seschrock 	 * want this property to be first.
346707ba0419Seschrock 	 */
3468990b4856Slling 	if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)
3469990b4856Slling 	    != 0)
347099653d4eSeschrock 		usage(B_FALSE);
3471fa9e4066Sahrens 
347299653d4eSeschrock 	cb.cb_first = B_TRUE;
3473fa9e4066Sahrens 
3474d5b5bb25SRich Morris 	ret = zfs_for_each(argc, argv, flags, types, sortcol, &cb.cb_proplist,
3475ae1726b6SChris Gerhard 	    limit, list_callback, &cb);
3476b6825278Ssjelinek 
3477990b4856Slling 	zprop_free_list(cb.cb_proplist);
3478b6825278Ssjelinek 	zfs_free_sort_columns(sortcol);
3479fa9e4066Sahrens 
3480fce7d82bSmmusante 	if (ret == 0 && cb.cb_first && !cb.cb_scripted)
3481fa9e4066Sahrens 		(void) printf(gettext("no datasets available\n"));
3482fa9e4066Sahrens 
3483fa9e4066Sahrens 	return (ret);
3484fa9e4066Sahrens }
3485fa9e4066Sahrens 
3486fa9e4066Sahrens /*
34876a9cb0eaSEric Schrock  * zfs rename [-f] <fs | snap | vol> <fs | snap | vol>
34886a9cb0eaSEric Schrock  * zfs rename [-f] -p <fs | vol> <fs | vol>
34897f1f55eaSvb160487  * zfs rename -r <snap> <snap>
3490fa9e4066Sahrens  *
3491fa9e4066Sahrens  * Renames the given dataset to another of the same type.
34927f1f55eaSvb160487  *
34937f1f55eaSvb160487  * The '-p' flag creates all the non-existing ancestors of the target first.
3494fa9e4066Sahrens  */
3495fa9e4066Sahrens /* ARGSUSED */
3496fa9e4066Sahrens static int
zfs_do_rename(int argc,char ** argv)3497fa9e4066Sahrens zfs_do_rename(int argc, char **argv)
3498fa9e4066Sahrens {
3499fa9e4066Sahrens 	zfs_handle_t *zhp;
3500cdf5b4caSmmusante 	int c;
350105c998d6SRichard Lowe 	int ret = 0;
35027f1f55eaSvb160487 	boolean_t recurse = B_FALSE;
35037f1f55eaSvb160487 	boolean_t parents = B_FALSE;
35046a9cb0eaSEric Schrock 	boolean_t force_unmount = B_FALSE;
3505fa9e4066Sahrens 
3506fa9e4066Sahrens 	/* check options */
35076a9cb0eaSEric Schrock 	while ((c = getopt(argc, argv, "prf")) != -1) {
3508cdf5b4caSmmusante 		switch (c) {
35097f1f55eaSvb160487 		case 'p':
35107f1f55eaSvb160487 			parents = B_TRUE;
35117f1f55eaSvb160487 			break;
3512cdf5b4caSmmusante 		case 'r':
35137f1f55eaSvb160487 			recurse = B_TRUE;
3514cdf5b4caSmmusante 			break;
35156a9cb0eaSEric Schrock 		case 'f':
35166a9cb0eaSEric Schrock 			force_unmount = B_TRUE;
35176a9cb0eaSEric Schrock 			break;
3518cdf5b4caSmmusante 		case '?':
3519cdf5b4caSmmusante 		default:
3520fa9e4066Sahrens 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
3521cdf5b4caSmmusante 			    optopt);
352299653d4eSeschrock 			usage(B_FALSE);
3523fa9e4066Sahrens 		}
3524cdf5b4caSmmusante 	}
3525cdf5b4caSmmusante 
3526cdf5b4caSmmusante 	argc -= optind;
3527cdf5b4caSmmusante 	argv += optind;
3528fa9e4066Sahrens 
3529fa9e4066Sahrens 	/* check number of arguments */
3530cdf5b4caSmmusante 	if (argc < 1) {
3531fa9e4066Sahrens 		(void) fprintf(stderr, gettext("missing source dataset "
3532fa9e4066Sahrens 		    "argument\n"));
353399653d4eSeschrock 		usage(B_FALSE);
3534fa9e4066Sahrens 	}
3535cdf5b4caSmmusante 	if (argc < 2) {
3536fa9e4066Sahrens 		(void) fprintf(stderr, gettext("missing target dataset "
3537fa9e4066Sahrens 		    "argument\n"));
353899653d4eSeschrock 		usage(B_FALSE);
3539fa9e4066Sahrens 	}
3540cdf5b4caSmmusante 	if (argc > 2) {
3541fa9e4066Sahrens 		(void) fprintf(stderr, gettext("too many arguments\n"));
354299653d4eSeschrock 		usage(B_FALSE);
3543fa9e4066Sahrens 	}
3544fa9e4066Sahrens 
35457f1f55eaSvb160487 	if (recurse && parents) {
35467f1f55eaSvb160487 		(void) fprintf(stderr, gettext("-p and -r options are mutually "
35477f1f55eaSvb160487 		    "exclusive\n"));
35487f1f55eaSvb160487 		usage(B_FALSE);
35497f1f55eaSvb160487 	}
35507f1f55eaSvb160487 
3551cdf5b4caSmmusante 	if (recurse && strchr(argv[0], '@') == 0) {
3552cdf5b4caSmmusante 		(void) fprintf(stderr, gettext("source dataset for recursive "
3553cdf5b4caSmmusante 		    "rename must be a snapshot\n"));
3554cdf5b4caSmmusante 		usage(B_FALSE);
3555cdf5b4caSmmusante 	}
3556cdf5b4caSmmusante 
35577f1f55eaSvb160487 	if ((zhp = zfs_open(g_zfs, argv[0], parents ? ZFS_TYPE_FILESYSTEM |
3558990b4856Slling 	    ZFS_TYPE_VOLUME : ZFS_TYPE_DATASET)) == NULL)
3559fa9e4066Sahrens 		return (1);
3560fa9e4066Sahrens 
35617f1f55eaSvb160487 	/* If we were asked and the name looks good, try to create ancestors. */
35627f1f55eaSvb160487 	if (parents && zfs_name_valid(argv[1], zfs_get_type(zhp)) &&
35637f1f55eaSvb160487 	    zfs_create_ancestors(g_zfs, argv[1]) != 0) {
35647f1f55eaSvb160487 		zfs_close(zhp);
35657f1f55eaSvb160487 		return (1);
35667f1f55eaSvb160487 	}
35677f1f55eaSvb160487 
35686a9cb0eaSEric Schrock 	ret = (zfs_rename(zhp, argv[1], recurse, force_unmount) != 0);
3569fa9e4066Sahrens 
357099653d4eSeschrock 	zfs_close(zhp);
357199653d4eSeschrock 	return (ret);
357299653d4eSeschrock }
357399653d4eSeschrock 
357499653d4eSeschrock /*
357599653d4eSeschrock  * zfs promote <fs>
357699653d4eSeschrock  *
357799653d4eSeschrock  * Promotes the given clone fs to be the parent
357899653d4eSeschrock  */
357999653d4eSeschrock /* ARGSUSED */
358099653d4eSeschrock static int
zfs_do_promote(int argc,char ** argv)358199653d4eSeschrock zfs_do_promote(int argc, char **argv)
358299653d4eSeschrock {
358399653d4eSeschrock 	zfs_handle_t *zhp;
358405c998d6SRichard Lowe 	int ret = 0;
358599653d4eSeschrock 
358699653d4eSeschrock 	/* check options */
358799653d4eSeschrock 	if (argc > 1 && argv[1][0] == '-') {
358899653d4eSeschrock 		(void) fprintf(stderr, gettext("invalid option '%c'\n"),
358999653d4eSeschrock 		    argv[1][1]);
359099653d4eSeschrock 		usage(B_FALSE);
359199653d4eSeschrock 	}
359299653d4eSeschrock 
359399653d4eSeschrock 	/* check number of arguments */
359499653d4eSeschrock 	if (argc < 2) {
359599653d4eSeschrock 		(void) fprintf(stderr, gettext("missing clone filesystem"
359699653d4eSeschrock 		    " argument\n"));
359799653d4eSeschrock 		usage(B_FALSE);
359899653d4eSeschrock 	}
359999653d4eSeschrock 	if (argc > 2) {
360099653d4eSeschrock 		(void) fprintf(stderr, gettext("too many arguments\n"));
360199653d4eSeschrock 		usage(B_FALSE);
360299653d4eSeschrock 	}
360399653d4eSeschrock 
360499653d4eSeschrock 	zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
360599653d4eSeschrock 	if (zhp == NULL)
360699653d4eSeschrock 		return (1);
360799653d4eSeschrock 
360899653d4eSeschrock 	ret = (zfs_promote(zhp) != 0);
360999653d4eSeschrock 
361006eeb2adSek110237 
3611fa9e4066Sahrens 	zfs_close(zhp);
3612fa9e4066Sahrens 	return (ret);
3613fa9e4066Sahrens }
3614fa9e4066Sahrens 
3615fa9e4066Sahrens /*
3616f17c7ff8Sahrens  * zfs rollback [-rRf] <snapshot>
3617fa9e4066Sahrens  *
3618fa9e4066Sahrens  *	-r	Delete any intervening snapshots before doing rollback
3619fa9e4066Sahrens  *	-R	Delete any snapshots and their clones
3620f17c7ff8Sahrens  *	-f	ignored for backwards compatability
3621fa9e4066Sahrens  *
3622fa9e4066Sahrens  * Given a filesystem, rollback to a specific snapshot, discarding any changes
3623fa9e4066Sahrens  * since then and making it the active dataset.  If more recent snapshots exist,
3624fa9e4066Sahrens  * the command will complain unless the '-r' flag is given.
3625fa9e4066Sahrens  */
3626fa9e4066Sahrens typedef struct rollback_cbdata {
3627fa9e4066Sahrens 	uint64_t	cb_create;
362899653d4eSeschrock 	boolean_t	cb_first;
3629fa9e4066Sahrens 	int		cb_doclones;
3630fa9e4066Sahrens 	char		*cb_target;
3631fa9e4066Sahrens 	int		cb_error;
363299653d4eSeschrock 	boolean_t	cb_recurse;
3633fa9e4066Sahrens } rollback_cbdata_t;
3634fa9e4066Sahrens 
363578f17100SMatthew Ahrens static int
rollback_check_dependent(zfs_handle_t * zhp,void * data)363678f17100SMatthew Ahrens rollback_check_dependent(zfs_handle_t *zhp, void *data)
363778f17100SMatthew Ahrens {
363878f17100SMatthew Ahrens 	rollback_cbdata_t *cbp = data;
363978f17100SMatthew Ahrens 
364078f17100SMatthew Ahrens 	if (cbp->cb_first && cbp->cb_recurse) {
364178f17100SMatthew Ahrens 		(void) fprintf(stderr, gettext("cannot rollback to "
364278f17100SMatthew Ahrens 		    "'%s': clones of previous snapshots exist\n"),
364378f17100SMatthew Ahrens 		    cbp->cb_target);
364478f17100SMatthew Ahrens 		(void) fprintf(stderr, gettext("use '-R' to "
364578f17100SMatthew Ahrens 		    "force deletion of the following clones and "
364678f17100SMatthew Ahrens 		    "dependents:\n"));
364778f17100SMatthew Ahrens 		cbp->cb_first = 0;
364878f17100SMatthew Ahrens 		cbp->cb_error = 1;
364978f17100SMatthew Ahrens 	}
365078f17100SMatthew Ahrens 
365178f17100SMatthew Ahrens 	(void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
365278f17100SMatthew Ahrens 
365378f17100SMatthew Ahrens 	zfs_close(zhp);
365478f17100SMatthew Ahrens 	return (0);
365578f17100SMatthew Ahrens }
36565d7b4d43SMatthew Ahrens 
3657fa9e4066Sahrens /*
3658fa9e4066Sahrens  * Report any snapshots more recent than the one specified.  Used when '-r' is
3659fa9e4066Sahrens  * not specified.  We reuse this same callback for the snapshot dependents - if
3660fa9e4066Sahrens  * 'cb_dependent' is set, then this is a dependent and we should report it
3661fa9e4066Sahrens  * without checking the transaction group.
3662fa9e4066Sahrens  */
3663fa9e4066Sahrens static int
rollback_check(zfs_handle_t * zhp,void * data)3664fa9e4066Sahrens rollback_check(zfs_handle_t *zhp, void *data)
3665fa9e4066Sahrens {
3666fa9e4066Sahrens 	rollback_cbdata_t *cbp = data;
3667fa9e4066Sahrens 
366899653d4eSeschrock 	if (cbp->cb_doclones) {
366999653d4eSeschrock 		zfs_close(zhp);
3670fa9e4066Sahrens 		return (0);
367199653d4eSeschrock 	}
3672fa9e4066Sahrens 
367378f17100SMatthew Ahrens 	if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) {
3674fa9e4066Sahrens 		if (cbp->cb_first && !cbp->cb_recurse) {
3675fa9e4066Sahrens 			(void) fprintf(stderr, gettext("cannot "
3676fa9e4066Sahrens 			    "rollback to '%s': more recent snapshots "
367778f17100SMatthew Ahrens 			    "or bookmarks exist\n"),
3678fa9e4066Sahrens 			    cbp->cb_target);
3679fa9e4066Sahrens 			(void) fprintf(stderr, gettext("use '-r' to "
3680fa9e4066Sahrens 			    "force deletion of the following "
368178f17100SMatthew Ahrens 			    "snapshots and bookmarks:\n"));
3682fa9e4066Sahrens 			cbp->cb_first = 0;
3683fa9e4066Sahrens 			cbp->cb_error = 1;
3684fa9e4066Sahrens 		}
3685fa9e4066Sahrens 
3686fa9e4066Sahrens 		if (cbp->cb_recurse) {
36873bb79becSeschrock 			if (zfs_iter_dependents(zhp, B_TRUE,
368878f17100SMatthew Ahrens 			    rollback_check_dependent, cbp) != 0) {
36893bb79becSeschrock 				zfs_close(zhp);
36903bb79becSeschrock 				return (-1);
36913bb79becSeschrock 			}
3692fa9e4066Sahrens 		} else {
3693fa9e4066Sahrens 			(void) fprintf(stderr, "%s\n",
3694fa9e4066Sahrens 			    zfs_get_name(zhp));
3695fa9e4066Sahrens 		}
3696fa9e4066Sahrens 	}
3697fa9e4066Sahrens 	zfs_close(zhp);
3698fa9e4066Sahrens 	return (0);
3699fa9e4066Sahrens }
3700fa9e4066Sahrens 
3701fa9e4066Sahrens static int
zfs_do_rollback(int argc,char ** argv)3702fa9e4066Sahrens zfs_do_rollback(int argc, char **argv)
3703fa9e4066Sahrens {
370405c998d6SRichard Lowe 	int ret = 0;
3705fa9e4066Sahrens 	int c;
3706c391e322Sahrens 	boolean_t force = B_FALSE;
3707fa9e4066Sahrens 	rollback_cbdata_t cb = { 0 };
3708fa9e4066Sahrens 	zfs_handle_t *zhp, *snap;
37099adfa60dSMatthew Ahrens 	char parentname[ZFS_MAX_DATASET_NAME_LEN];
3710fa9e4066Sahrens 	char *delim;
3711fa9e4066Sahrens 
3712fa9e4066Sahrens 	/* check options */
3713f17c7ff8Sahrens 	while ((c = getopt(argc, argv, "rRf")) != -1) {
3714fa9e4066Sahrens 		switch (c) {
3715fa9e4066Sahrens 		case 'r':
3716fa9e4066Sahrens 			cb.cb_recurse = 1;
3717fa9e4066Sahrens 			break;
3718fa9e4066Sahrens 		case 'R':
3719fa9e4066Sahrens 			cb.cb_recurse = 1;
3720fa9e4066Sahrens 			cb.cb_doclones = 1;
3721fa9e4066Sahrens 			break;
3722f17c7ff8Sahrens 		case 'f':
3723c391e322Sahrens 			force = B_TRUE;
3724f17c7ff8Sahrens 			break;
3725fa9e4066Sahrens 		case '?':
3726fa9e4066Sahrens 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
3727fa9e4066Sahrens 			    optopt);
372899653d4eSeschrock 			usage(B_FALSE);
3729fa9e4066Sahrens 		}
3730fa9e4066Sahrens 	}
3731fa9e4066Sahrens 
3732fa9e4066Sahrens 	argc -= optind;
3733fa9e4066Sahrens 	argv += optind;
3734fa9e4066Sahrens 
3735fa9e4066Sahrens 	/* check number of arguments */
3736fa9e4066Sahrens 	if (argc < 1) {
3737fa9e4066Sahrens 		(void) fprintf(stderr, gettext("missing dataset argument\n"));
373899653d4eSeschrock 		usage(B_FALSE);
3739fa9e4066Sahrens 	}
3740fa9e4066Sahrens 	if (argc > 1) {
3741fa9e4066Sahrens 		(void) fprintf(stderr, gettext("too many arguments\n"));
374299653d4eSeschrock 		usage(B_FALSE);
3743fa9e4066Sahrens 	}
3744fa9e4066Sahrens 
3745fa9e4066Sahrens 	/* open the snapshot */
374699653d4eSeschrock 	if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL)
3747fa9e4066Sahrens 		return (1);
3748fa9e4066Sahrens 
3749b12a1c38Slling 	/* open the parent dataset */
3750b12a1c38Slling 	(void) strlcpy(parentname, argv[0], sizeof (parentname));
3751fa9e4066Sahrens 	verify((delim = strrchr(parentname, '@')) != NULL);
3752fa9e4066Sahrens 	*delim = '\0';
3753990b4856Slling 	if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_DATASET)) == NULL) {
3754fa9e4066Sahrens 		zfs_close(snap);
3755fa9e4066Sahrens 		return (1);
3756fa9e4066Sahrens 	}
3757fa9e4066Sahrens 
3758fa9e4066Sahrens 	/*
3759fa9e4066Sahrens 	 * Check for more recent snapshots and/or clones based on the presence
3760fa9e4066Sahrens 	 * of '-r' and '-R'.
3761fa9e4066Sahrens 	 */
3762b12a1c38Slling 	cb.cb_target = argv[0];
3763b12a1c38Slling 	cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
376499653d4eSeschrock 	cb.cb_first = B_TRUE;
3765fa9e4066Sahrens 	cb.cb_error = 0;
37660d8fa8f8SMartin Matuska 	if ((ret = zfs_iter_snapshots(zhp, B_FALSE, rollback_check, &cb)) != 0)
376778f17100SMatthew Ahrens 		goto out;
376878f17100SMatthew Ahrens 	if ((ret = zfs_iter_bookmarks(zhp, rollback_check, &cb)) != 0)
37693bb79becSeschrock 		goto out;
3770fa9e4066Sahrens 
3771fa9e4066Sahrens 	if ((ret = cb.cb_error) != 0)
3772fa9e4066Sahrens 		goto out;
3773fa9e4066Sahrens 
3774fa9e4066Sahrens 	/*
3775b12a1c38Slling 	 * Rollback parent to the given snapshot.
3776fa9e4066Sahrens 	 */
3777c391e322Sahrens 	ret = zfs_rollback(zhp, snap, force);
3778fa9e4066Sahrens 
3779fa9e4066Sahrens out:
3780fa9e4066Sahrens 	zfs_close(snap);
3781fa9e4066Sahrens 	zfs_close(zhp);
3782fa9e4066Sahrens 
3783fa9e4066Sahrens 	if (ret == 0)
3784fa9e4066Sahrens 		return (0);
3785fa9e4066Sahrens 	else
3786fa9e4066Sahrens 		return (1);
3787fa9e4066Sahrens }
3788fa9e4066Sahrens 
3789fa9e4066Sahrens /*
379030925561SChris Williamson  * zfs set property=value ... { fs | snap | vol } ...
3791fa9e4066Sahrens  *
379230925561SChris Williamson  * Sets the given properties for all datasets specified on the command line.
3793fa9e4066Sahrens  */
3794fa9e4066Sahrens 
3795fa9e4066Sahrens static int
set_callback(zfs_handle_t * zhp,void * data)3796fa9e4066Sahrens set_callback(zfs_handle_t *zhp, void *data)
3797fa9e4066Sahrens {
379830925561SChris Williamson 	nvlist_t *props = data;
3799fa9e4066Sahrens 
380030925561SChris Williamson 	if (zfs_prop_set_list(zhp, props) != 0) {
3801efc555ebSnd150628 		switch (libzfs_errno(g_zfs)) {
3802efc555ebSnd150628 		case EZFS_MOUNTFAILED:
3803efc555ebSnd150628 			(void) fprintf(stderr, gettext("property may be set "
3804efc555ebSnd150628 			    "but unable to remount filesystem\n"));
3805efc555ebSnd150628 			break;
3806f3861e1aSahl 		case EZFS_SHARENFSFAILED:
3807efc555ebSnd150628 			(void) fprintf(stderr, gettext("property may be set "
3808efc555ebSnd150628 			    "but unable to reshare filesystem\n"));
3809efc555ebSnd150628 			break;
3810efc555ebSnd150628 		}
3811fa9e4066Sahrens 		return (1);
3812efc555ebSnd150628 	}
38135ad82045Snd150628 	return (0);
3814fa9e4066Sahrens }
3815fa9e4066Sahrens 
3816fa9e4066Sahrens static int
zfs_do_set(int argc,char ** argv)3817fa9e4066Sahrens zfs_do_set(int argc, char **argv)
3818fa9e4066Sahrens {
381930925561SChris Williamson 	nvlist_t *props = NULL;
382030925561SChris Williamson 	int ds_start = -1; /* argv idx of first dataset arg */
382105c998d6SRichard Lowe 	int ret = 0;
3822fa9e4066Sahrens 
3823fa9e4066Sahrens 	/* check for options */
3824fa9e4066Sahrens 	if (argc > 1 && argv[1][0] == '-') {
3825fa9e4066Sahrens 		(void) fprintf(stderr, gettext("invalid option '%c'\n"),
3826fa9e4066Sahrens 		    argv[1][1]);
382799653d4eSeschrock 		usage(B_FALSE);
3828fa9e4066Sahrens 	}
3829fa9e4066Sahrens 
3830fa9e4066Sahrens 	/* check number of arguments */
3831fa9e4066Sahrens 	if (argc < 2) {
383230925561SChris Williamson 		(void) fprintf(stderr, gettext("missing arguments\n"));
383399653d4eSeschrock 		usage(B_FALSE);
3834fa9e4066Sahrens 	}
3835fa9e4066Sahrens 	if (argc < 3) {
383630925561SChris Williamson 		if (strchr(argv[1], '=') == NULL) {
383730925561SChris Williamson 			(void) fprintf(stderr, gettext("missing property=value "
383830925561SChris Williamson 			    "argument(s)\n"));
383930925561SChris Williamson 		} else {
384030925561SChris Williamson 			(void) fprintf(stderr, gettext("missing dataset "
384130925561SChris Williamson 			    "name(s)\n"));
384230925561SChris Williamson 		}
384399653d4eSeschrock 		usage(B_FALSE);
3844fa9e4066Sahrens 	}
3845fa9e4066Sahrens 
384630925561SChris Williamson 	/* validate argument order:  prop=val args followed by dataset args */
384730925561SChris Williamson 	for (int i = 1; i < argc; i++) {
384830925561SChris Williamson 		if (strchr(argv[i], '=') != NULL) {
384930925561SChris Williamson 			if (ds_start > 0) {
385030925561SChris Williamson 				/* out-of-order prop=val argument */
385130925561SChris Williamson 				(void) fprintf(stderr, gettext("invalid "
385230925561SChris Williamson 				    "argument order\n"), i);
385330925561SChris Williamson 				usage(B_FALSE);
385430925561SChris Williamson 			}
385530925561SChris Williamson 		} else if (ds_start < 0) {
385630925561SChris Williamson 			ds_start = i;
385730925561SChris Williamson 		}
385830925561SChris Williamson 	}
385930925561SChris Williamson 	if (ds_start < 0) {
386030925561SChris Williamson 		(void) fprintf(stderr, gettext("missing dataset name(s)\n"));
386199653d4eSeschrock 		usage(B_FALSE);
3862fa9e4066Sahrens 	}
3863fa9e4066Sahrens 
386430925561SChris Williamson 	/* Populate a list of property settings */
386530925561SChris Williamson 	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
386630925561SChris Williamson 		nomem();
386730925561SChris Williamson 	for (int i = 1; i < ds_start; i++) {
38686ccda740Sloli10K 		if (!parseprop(props, argv[i])) {
38696ccda740Sloli10K 			ret = -1;
387030925561SChris Williamson 			goto error;
3871fa9e4066Sahrens 		}
38726ccda740Sloli10K 	}
3873fa9e4066Sahrens 
387430925561SChris Williamson 	ret = zfs_for_each(argc - ds_start, argv + ds_start, 0,
387530925561SChris Williamson 	    ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, props);
387606eeb2adSek110237 
387730925561SChris Williamson error:
387830925561SChris Williamson 	nvlist_free(props);
387906eeb2adSek110237 	return (ret);
3880fa9e4066Sahrens }
3881fa9e4066Sahrens 
38824445fffbSMatthew Ahrens typedef struct snap_cbdata {
38834445fffbSMatthew Ahrens 	nvlist_t *sd_nvl;
38844445fffbSMatthew Ahrens 	boolean_t sd_recursive;
38854445fffbSMatthew Ahrens 	const char *sd_snapname;
38864445fffbSMatthew Ahrens } snap_cbdata_t;
38874445fffbSMatthew Ahrens 
38884445fffbSMatthew Ahrens static int
zfs_snapshot_cb(zfs_handle_t * zhp,void * arg)38894445fffbSMatthew Ahrens zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)
38904445fffbSMatthew Ahrens {
38914445fffbSMatthew Ahrens 	snap_cbdata_t *sd = arg;
38924445fffbSMatthew Ahrens 	char *name;
38934445fffbSMatthew Ahrens 	int rv = 0;
38944445fffbSMatthew Ahrens 	int error;
38954445fffbSMatthew Ahrens 
3896ca48f36fSKeith M Wesolowski 	if (sd->sd_recursive &&
3897ca48f36fSKeith M Wesolowski 	    zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) != 0) {
3898ca48f36fSKeith M Wesolowski 		zfs_close(zhp);
3899ca48f36fSKeith M Wesolowski 		return (0);
3900ca48f36fSKeith M Wesolowski 	}
3901ca48f36fSKeith M Wesolowski 
39024445fffbSMatthew Ahrens 	error = asprintf(&name, "%s@%s", zfs_get_name(zhp), sd->sd_snapname);
39034445fffbSMatthew Ahrens 	if (error == -1)
39044445fffbSMatthew Ahrens 		nomem();
39054445fffbSMatthew Ahrens 	fnvlist_add_boolean(sd->sd_nvl, name);
39064445fffbSMatthew Ahrens 	free(name);
39074445fffbSMatthew Ahrens 
39084445fffbSMatthew Ahrens 	if (sd->sd_recursive)
39094445fffbSMatthew Ahrens 		rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd);
39104445fffbSMatthew Ahrens 	zfs_close(zhp);
39114445fffbSMatthew Ahrens 	return (rv);
39124445fffbSMatthew Ahrens }
39134445fffbSMatthew Ahrens 
3914fa9e4066Sahrens /*
3915bb0ade09Sahrens  * zfs snapshot [-r] [-o prop=value] ... <fs@snap>
3916fa9e4066Sahrens  *
3917fa9e4066Sahrens  * Creates a snapshot with the given name.  While functionally equivalent to
3918da6c28aaSamw  * 'zfs create', it is a separate command to differentiate intent.
3919fa9e4066Sahrens  */
3920fa9e4066Sahrens static int
zfs_do_snapshot(int argc,char ** argv)3921fa9e4066Sahrens zfs_do_snapshot(int argc, char **argv)
3922fa9e4066Sahrens {
392305c998d6SRichard Lowe 	int ret = 0;
3924ef150c2bSRichard Lowe 	int c;
3925bb0ade09Sahrens 	nvlist_t *props;
39264445fffbSMatthew Ahrens 	snap_cbdata_t sd = { 0 };
39274445fffbSMatthew Ahrens 	boolean_t multiple_snaps = B_FALSE;
3928bb0ade09Sahrens 
3929a8b6ddafSMark J Musante 	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
3930a8b6ddafSMark J Musante 		nomem();
39314445fffbSMatthew Ahrens 	if (nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) != 0)
39324445fffbSMatthew Ahrens 		nomem();
39331d452cf5Sahrens 
3934fa9e4066Sahrens 	/* check options */
3935bb0ade09Sahrens 	while ((c = getopt(argc, argv, "ro:")) != -1) {
39361d452cf5Sahrens 		switch (c) {
3937bb0ade09Sahrens 		case 'o':
39386ccda740Sloli10K 			if (!parseprop(props, optarg)) {
39396ccda740Sloli10K 				nvlist_free(sd.sd_nvl);
39406ccda740Sloli10K 				nvlist_free(props);
3941bb0ade09Sahrens 				return (1);
39426ccda740Sloli10K 			}
3943bb0ade09Sahrens 			break;
39441d452cf5Sahrens 		case 'r':
39454445fffbSMatthew Ahrens 			sd.sd_recursive = B_TRUE;
39464445fffbSMatthew Ahrens 			multiple_snaps = B_TRUE;
39471d452cf5Sahrens 			break;
39481d452cf5Sahrens 		case '?':
3949fa9e4066Sahrens 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
39501d452cf5Sahrens 			    optopt);
3951bb0ade09Sahrens 			goto usage;
3952fa9e4066Sahrens 		}
39531d452cf5Sahrens 	}
39541d452cf5Sahrens 
39551d452cf5Sahrens 	argc -= optind;
39561d452cf5Sahrens 	argv += optind;
3957fa9e4066Sahrens 
3958fa9e4066Sahrens 	/* check number of arguments */
39591d452cf5Sahrens 	if (argc < 1) {
3960fa9e4066Sahrens 		(void) fprintf(stderr, gettext("missing snapshot argument\n"));
3961bb0ade09Sahrens 		goto usage;
3962fa9e4066Sahrens 	}
39634445fffbSMatthew Ahrens 
39644445fffbSMatthew Ahrens 	if (argc > 1)
39654445fffbSMatthew Ahrens 		multiple_snaps = B_TRUE;
39664445fffbSMatthew Ahrens 	for (; argc > 0; argc--, argv++) {
39674445fffbSMatthew Ahrens 		char *atp;
39684445fffbSMatthew Ahrens 		zfs_handle_t *zhp;
39694445fffbSMatthew Ahrens 
39704445fffbSMatthew Ahrens 		atp = strchr(argv[0], '@');
39714445fffbSMatthew Ahrens 		if (atp == NULL)
39724445fffbSMatthew Ahrens 			goto usage;
39734445fffbSMatthew Ahrens 		*atp = '\0';
39744445fffbSMatthew Ahrens 		sd.sd_snapname = atp + 1;
39754445fffbSMatthew Ahrens 		zhp = zfs_open(g_zfs, argv[0],
39764445fffbSMatthew Ahrens 		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
39774445fffbSMatthew Ahrens 		if (zhp == NULL)
39784445fffbSMatthew Ahrens 			goto usage;
39794445fffbSMatthew Ahrens 		if (zfs_snapshot_cb(zhp, &sd) != 0)
3980bb0ade09Sahrens 			goto usage;
3981fa9e4066Sahrens 	}
3982fa9e4066Sahrens 
39834445fffbSMatthew Ahrens 	ret = zfs_snapshot_nvl(g_zfs, sd.sd_nvl, props);
39844445fffbSMatthew Ahrens 	nvlist_free(sd.sd_nvl);
3985bb0ade09Sahrens 	nvlist_free(props);
39864445fffbSMatthew Ahrens 	if (ret != 0 && multiple_snaps)
39871d452cf5Sahrens 		(void) fprintf(stderr, gettext("no snapshots were created\n"));
39881d452cf5Sahrens 	return (ret != 0);
3989bb0ade09Sahrens 
3990bb0ade09Sahrens usage:
39914445fffbSMatthew Ahrens 	nvlist_free(sd.sd_nvl);
3992bb0ade09Sahrens 	nvlist_free(props);
3993bb0ade09Sahrens 	usage(B_FALSE);
3994bb0ade09Sahrens 	return (-1);
3995fa9e4066Sahrens }
3996fa9e4066Sahrens 
3997fa9e4066Sahrens /*
3998fa9e4066Sahrens  * Send a backup stream to stdout.
3999fa9e4066Sahrens  */
4000fa9e4066Sahrens static int
zfs_do_send(int argc,char ** argv)4001f2a3c691Sahrens zfs_do_send(int argc, char **argv)
4002fa9e4066Sahrens {
4003fa9e4066Sahrens 	char *fromname = NULL;
40043cb34c60Sahrens 	char *toname = NULL;
40059c3fd121SMatthew Ahrens 	char *resume_token = NULL;
4006a2eea2e1Sahrens 	char *cp;
4007a2eea2e1Sahrens 	zfs_handle_t *zhp;
40089e69d7d0SLori Alt 	sendflags_t flags = { 0 };
4009fa9e4066Sahrens 	int c, err;
401019b94df9SMatthew Ahrens 	nvlist_t *dbgnv = NULL;
40113f9d6ad7SLin Ling 	boolean_t extraverbose = B_FALSE;
4012fa9e4066Sahrens 
40135602294fSDan Kimmel 	struct option long_options[] = {
40145602294fSDan Kimmel 		{"replicate",	no_argument,		NULL, 'R'},
40155602294fSDan Kimmel 		{"props",	no_argument,		NULL, 'p'},
40165602294fSDan Kimmel 		{"parsable",	no_argument,		NULL, 'P'},
40175602294fSDan Kimmel 		{"dedup",	no_argument,		NULL, 'D'},
40185602294fSDan Kimmel 		{"verbose",	no_argument,		NULL, 'v'},
40195602294fSDan Kimmel 		{"dryrun",	no_argument,		NULL, 'n'},
40205602294fSDan Kimmel 		{"large-block",	no_argument,		NULL, 'L'},
40215602294fSDan Kimmel 		{"embed",	no_argument,		NULL, 'e'},
40225602294fSDan Kimmel 		{"resume",	required_argument,	NULL, 't'},
40235602294fSDan Kimmel 		{"compressed",	no_argument,		NULL, 'c'},
4024eb633035STom Caputi 		{"raw",		no_argument,		NULL, 'w'},
40256ccda740Sloli10K 		{"backup",	no_argument,		NULL, 'b'},
40266ccda740Sloli10K 		{"holds",	no_argument,		NULL, 'h'},
40275602294fSDan Kimmel 		{0, 0, 0, 0}
40285602294fSDan Kimmel 	};
40295602294fSDan Kimmel 
4030fa9e4066Sahrens 	/* check options */
40316ccda740Sloli10K 	while ((c = getopt_long(argc, argv, ":i:I:RDpvnPLeht:cwb", long_options,
40325602294fSDan Kimmel 	    NULL)) != -1) {
4033fa9e4066Sahrens 		switch (c) {
4034fa9e4066Sahrens 		case 'i':
4035a2eea2e1Sahrens 			if (fromname)
4036a2eea2e1Sahrens 				usage(B_FALSE);
4037fa9e4066Sahrens 			fromname = optarg;
4038fa9e4066Sahrens 			break;
40393cb34c60Sahrens 		case 'I':
40403cb34c60Sahrens 			if (fromname)
40413cb34c60Sahrens 				usage(B_FALSE);
40423cb34c60Sahrens 			fromname = optarg;
40439e69d7d0SLori Alt 			flags.doall = B_TRUE;
40443cb34c60Sahrens 			break;
40453cb34c60Sahrens 		case 'R':
40469e69d7d0SLori Alt 			flags.replicate = B_TRUE;
40473cb34c60Sahrens 			break;
404892241e0bSTom Erickson 		case 'p':
404992241e0bSTom Erickson 			flags.props = B_TRUE;
405092241e0bSTom Erickson 			break;
40516ccda740Sloli10K 		case 'b':
40526ccda740Sloli10K 			flags.backup = B_TRUE;
40536ccda740Sloli10K 			break;
40546ccda740Sloli10K 		case 'h':
40556ccda740Sloli10K 			flags.holds = B_TRUE;
40566ccda740Sloli10K 			break;
405719b94df9SMatthew Ahrens 		case 'P':
405819b94df9SMatthew Ahrens 			flags.parsable = B_TRUE;
405919b94df9SMatthew Ahrens 			flags.verbose = B_TRUE;
406019b94df9SMatthew Ahrens 			break;
40613cb34c60Sahrens 		case 'v':
40623f9d6ad7SLin Ling 			if (flags.verbose)
40633f9d6ad7SLin Ling 				extraverbose = B_TRUE;
40649e69d7d0SLori Alt 			flags.verbose = B_TRUE;
40654e3c9f44SBill Pijewski 			flags.progress = B_TRUE;
40669e69d7d0SLori Alt 			break;
40679e69d7d0SLori Alt 		case 'D':
40689e69d7d0SLori Alt 			flags.dedup = B_TRUE;
40693cb34c60Sahrens 			break;
407019b94df9SMatthew Ahrens 		case 'n':
407119b94df9SMatthew Ahrens 			flags.dryrun = B_TRUE;
407219b94df9SMatthew Ahrens 			break;
4073b5152584SMatthew Ahrens 		case 'L':
4074b5152584SMatthew Ahrens 			flags.largeblock = B_TRUE;
4075b5152584SMatthew Ahrens 			break;
40765d7b4d43SMatthew Ahrens 		case 'e':
40775d7b4d43SMatthew Ahrens 			flags.embed_data = B_TRUE;
40785d7b4d43SMatthew Ahrens 			break;
40799c3fd121SMatthew Ahrens 		case 't':
40809c3fd121SMatthew Ahrens 			resume_token = optarg;
40819c3fd121SMatthew Ahrens 			break;
40825602294fSDan Kimmel 		case 'c':
40835602294fSDan Kimmel 			flags.compress = B_TRUE;
40845602294fSDan Kimmel 			break;
4085eb633035STom Caputi 		case 'w':
4086eb633035STom Caputi 			flags.raw = B_TRUE;
4087eb633035STom Caputi 			flags.compress = B_TRUE;
4088eb633035STom Caputi 			flags.embed_data = B_TRUE;
4089eb633035STom Caputi 			flags.largeblock = B_TRUE;
4090eb633035STom Caputi 			break;
4091fa9e4066Sahrens 		case ':':
40925c653870SPaul Dagnelie 			/*
40935c653870SPaul Dagnelie 			 * If a parameter was not passed, optopt contains the
40945c653870SPaul Dagnelie 			 * value that would normally lead us into the
40955c653870SPaul Dagnelie 			 * appropriate case statement.  If it's > 256, then this
40965c653870SPaul Dagnelie 			 * must be a longopt and we should look at argv to get
40975c653870SPaul Dagnelie 			 * the string.  Otherwise it's just the character, so we
40985c653870SPaul Dagnelie 			 * should use it directly.
40995c653870SPaul Dagnelie 			 */
41005c653870SPaul Dagnelie 			if (optopt <= UINT8_MAX) {
41015c653870SPaul Dagnelie 				(void) fprintf(stderr,
41025c653870SPaul Dagnelie 				    gettext("missing argument for '%c' "
41035c653870SPaul Dagnelie 				    "option\n"), optopt);
41045c653870SPaul Dagnelie 			} else {
41055c653870SPaul Dagnelie 				(void) fprintf(stderr,
41065c653870SPaul Dagnelie 				    gettext("missing argument for '%s' "
41075c653870SPaul Dagnelie 				    "option\n"), argv[optind - 1]);
41085c653870SPaul Dagnelie 			}
410999653d4eSeschrock 			usage(B_FALSE);
4110fa9e4066Sahrens 			break;
4111fa9e4066Sahrens 		case '?':
41125602294fSDan Kimmel 			/*FALLTHROUGH*/
41135602294fSDan Kimmel 		default:
41145c653870SPaul Dagnelie 			/*
41155c653870SPaul Dagnelie 			 * If an invalid flag was passed, optopt contains the
41165c653870SPaul Dagnelie 			 * character if it was a short flag, or 0 if it was a
41175c653870SPaul Dagnelie 			 * longopt.
41185c653870SPaul Dagnelie 			 */
41195c653870SPaul Dagnelie 			if (optopt != 0) {
41205c653870SPaul Dagnelie 				(void) fprintf(stderr,
41215c653870SPaul Dagnelie 				    gettext("invalid option '%c'\n"), optopt);
41225c653870SPaul Dagnelie 			} else {
41235c653870SPaul Dagnelie 				(void) fprintf(stderr,
41245c653870SPaul Dagnelie 				    gettext("invalid option '%s'\n"),
41255c653870SPaul Dagnelie 				    argv[optind - 1]);
41265c653870SPaul Dagnelie 
41275c653870SPaul Dagnelie 			}
412899653d4eSeschrock 			usage(B_FALSE);
4129fa9e4066Sahrens 		}
4130fa9e4066Sahrens 	}
4131fa9e4066Sahrens 
4132fa9e4066Sahrens 	argc -= optind;
4133fa9e4066Sahrens 	argv += optind;
4134fa9e4066Sahrens 
41359c3fd121SMatthew Ahrens 	if (resume_token != NULL) {
41369c3fd121SMatthew Ahrens 		if (fromname != NULL || flags.replicate || flags.props ||
41376ccda740Sloli10K 		    flags.backup || flags.dedup) {
41389c3fd121SMatthew Ahrens 			(void) fprintf(stderr,
41399c3fd121SMatthew Ahrens 			    gettext("invalid flags combined with -t\n"));
41409c3fd121SMatthew Ahrens 			usage(B_FALSE);
41419c3fd121SMatthew Ahrens 		}
41429c3fd121SMatthew Ahrens 		if (argc != 0) {
41439c3fd121SMatthew Ahrens 			(void) fprintf(stderr, gettext("no additional "
41449c3fd121SMatthew Ahrens 			    "arguments are permitted with -t\n"));
41459c3fd121SMatthew Ahrens 			usage(B_FALSE);
41469c3fd121SMatthew Ahrens 		}
41479c3fd121SMatthew Ahrens 	} else {
4148fa9e4066Sahrens 		if (argc < 1) {
41499c3fd121SMatthew Ahrens 			(void) fprintf(stderr,
41509c3fd121SMatthew Ahrens 			    gettext("missing snapshot argument\n"));
415199653d4eSeschrock 			usage(B_FALSE);
4152fa9e4066Sahrens 		}
4153fa9e4066Sahrens 		if (argc > 1) {
4154fa9e4066Sahrens 			(void) fprintf(stderr, gettext("too many arguments\n"));
415599653d4eSeschrock 			usage(B_FALSE);
4156fa9e4066Sahrens 		}
41579c3fd121SMatthew Ahrens 	}
4158fa9e4066Sahrens 
415919b94df9SMatthew Ahrens 	if (!flags.dryrun && isatty(STDOUT_FILENO)) {
4160fa9e4066Sahrens 		(void) fprintf(stderr,
4161a2eea2e1Sahrens 		    gettext("Error: Stream can not be written to a terminal.\n"
4162fa9e4066Sahrens 		    "You must redirect standard output.\n"));
4163fa9e4066Sahrens 		return (1);
4164fa9e4066Sahrens 	}
4165fa9e4066Sahrens 
41669c3fd121SMatthew Ahrens 	if (resume_token != NULL) {
41679c3fd121SMatthew Ahrens 		return (zfs_send_resume(g_zfs, &flags, STDOUT_FILENO,
41689c3fd121SMatthew Ahrens 		    resume_token));
41699c3fd121SMatthew Ahrens 	}
41709c3fd121SMatthew Ahrens 
417178f17100SMatthew Ahrens 	/*
417278f17100SMatthew Ahrens 	 * Special case sending a filesystem, or from a bookmark.
417378f17100SMatthew Ahrens 	 */
417478f17100SMatthew Ahrens 	if (strchr(argv[0], '@') == NULL ||
417578f17100SMatthew Ahrens 	    (fromname && strchr(fromname, '#') != NULL)) {
41769adfa60dSMatthew Ahrens 		char frombuf[ZFS_MAX_DATASET_NAME_LEN];
41775d7b4d43SMatthew Ahrens 		enum lzc_send_flags lzc_flags = 0;
417878f17100SMatthew Ahrens 
417978f17100SMatthew Ahrens 		if (flags.replicate || flags.doall || flags.props ||
41806ccda740Sloli10K 		    flags.backup || flags.dedup || flags.holds ||
41816ccda740Sloli10K 		    flags.dryrun || flags.verbose || flags.progress) {
41823cb34c60Sahrens 			(void) fprintf(stderr,
418378f17100SMatthew Ahrens 			    gettext("Error: "
418478f17100SMatthew Ahrens 			    "Unsupported flag with filesystem or bookmark.\n"));
418578f17100SMatthew Ahrens 			return (1);
41863cb34c60Sahrens 		}
418778f17100SMatthew Ahrens 
418878f17100SMatthew Ahrens 		zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET);
418978f17100SMatthew Ahrens 		if (zhp == NULL)
419078f17100SMatthew Ahrens 			return (1);
419178f17100SMatthew Ahrens 
4192b5152584SMatthew Ahrens 		if (flags.largeblock)
4193b5152584SMatthew Ahrens 			lzc_flags |= LZC_SEND_FLAG_LARGE_BLOCK;
41945d7b4d43SMatthew Ahrens 		if (flags.embed_data)
41955d7b4d43SMatthew Ahrens 			lzc_flags |= LZC_SEND_FLAG_EMBED_DATA;
41965602294fSDan Kimmel 		if (flags.compress)
41975602294fSDan Kimmel 			lzc_flags |= LZC_SEND_FLAG_COMPRESS;
4198eb633035STom Caputi 		if (flags.raw)
4199eb633035STom Caputi 			lzc_flags |= LZC_SEND_FLAG_RAW;
42005d7b4d43SMatthew Ahrens 
420178f17100SMatthew Ahrens 		if (fromname != NULL &&
420278f17100SMatthew Ahrens 		    (fromname[0] == '#' || fromname[0] == '@')) {
420378f17100SMatthew Ahrens 			/*
420478f17100SMatthew Ahrens 			 * Incremental source name begins with # or @.
420578f17100SMatthew Ahrens 			 * Default to same fs as target.
420678f17100SMatthew Ahrens 			 */
42073382f241Sluozhengzheng 			(void) strlcpy(frombuf, argv[0], sizeof (frombuf));
420878f17100SMatthew Ahrens 			cp = strchr(frombuf, '@');
420978f17100SMatthew Ahrens 			if (cp != NULL)
421078f17100SMatthew Ahrens 				*cp = '\0';
421178f17100SMatthew Ahrens 			(void) strlcat(frombuf, fromname, sizeof (frombuf));
421278f17100SMatthew Ahrens 			fromname = frombuf;
421378f17100SMatthew Ahrens 		}
42145d7b4d43SMatthew Ahrens 		err = zfs_send_one(zhp, fromname, STDOUT_FILENO, lzc_flags);
421578f17100SMatthew Ahrens 		zfs_close(zhp);
421678f17100SMatthew Ahrens 		return (err != 0);
421778f17100SMatthew Ahrens 	}
421878f17100SMatthew Ahrens 
421978f17100SMatthew Ahrens 	cp = strchr(argv[0], '@');
42203cb34c60Sahrens 	*cp = '\0';
42213cb34c60Sahrens 	toname = cp + 1;
42223cb34c60Sahrens 	zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
42233cb34c60Sahrens 	if (zhp == NULL)
422498579b20Snd150628 		return (1);
422598579b20Snd150628 
422698579b20Snd150628 	/*
4227a2eea2e1Sahrens 	 * If they specified the full path to the snapshot, chop off
42283cb34c60Sahrens 	 * everything except the short name of the snapshot, but special
42293cb34c60Sahrens 	 * case if they specify the origin.
423098579b20Snd150628 	 */
4231a2eea2e1Sahrens 	if (fromname && (cp = strchr(fromname, '@')) != NULL) {
42329adfa60dSMatthew Ahrens 		char origin[ZFS_MAX_DATASET_NAME_LEN];
42333cb34c60Sahrens 		zprop_source_t src;
42343cb34c60Sahrens 
42353cb34c60Sahrens 		(void) zfs_prop_get(zhp, ZFS_PROP_ORIGIN,
42363cb34c60Sahrens 		    origin, sizeof (origin), &src, NULL, 0, B_FALSE);
42373cb34c60Sahrens 
42383cb34c60Sahrens 		if (strcmp(origin, fromname) == 0) {
42393cb34c60Sahrens 			fromname = NULL;
42409e69d7d0SLori Alt 			flags.fromorigin = B_TRUE;
42413cb34c60Sahrens 		} else {
42423cb34c60Sahrens 			*cp = '\0';
42433cb34c60Sahrens 			if (cp != fromname && strcmp(argv[0], fromname)) {
4244a2eea2e1Sahrens 				(void) fprintf(stderr,
4245a2eea2e1Sahrens 				    gettext("incremental source must be "
4246a2eea2e1Sahrens 				    "in same filesystem\n"));
4247a2eea2e1Sahrens 				usage(B_FALSE);
4248a2eea2e1Sahrens 			}
4249a2eea2e1Sahrens 			fromname = cp + 1;
4250a2eea2e1Sahrens 			if (strchr(fromname, '@') || strchr(fromname, '/')) {
4251a2eea2e1Sahrens 				(void) fprintf(stderr,
4252a2eea2e1Sahrens 				    gettext("invalid incremental source\n"));
4253a2eea2e1Sahrens 				usage(B_FALSE);
4254a2eea2e1Sahrens 			}
425598579b20Snd150628 		}
42563cb34c60Sahrens 	}
425798579b20Snd150628 
42589e69d7d0SLori Alt 	if (flags.replicate && fromname == NULL)
42599e69d7d0SLori Alt 		flags.doall = B_TRUE;
42603cb34c60Sahrens 
426119b94df9SMatthew Ahrens 	err = zfs_send(zhp, fromname, toname, &flags, STDOUT_FILENO, NULL, 0,
42623f9d6ad7SLin Ling 	    extraverbose ? &dbgnv : NULL);
42633f9d6ad7SLin Ling 
426419b94df9SMatthew Ahrens 	if (extraverbose && dbgnv != NULL) {
42653f9d6ad7SLin Ling 		/*
42663f9d6ad7SLin Ling 		 * dump_nvlist prints to stdout, but that's been
42673f9d6ad7SLin Ling 		 * redirected to a file.  Make it print to stderr
42683f9d6ad7SLin Ling 		 * instead.
42693f9d6ad7SLin Ling 		 */
42703f9d6ad7SLin Ling 		(void) dup2(STDERR_FILENO, STDOUT_FILENO);
42713f9d6ad7SLin Ling 		dump_nvlist(dbgnv, 0);
42723f9d6ad7SLin Ling 		nvlist_free(dbgnv);
42733f9d6ad7SLin Ling 	}
4274a2eea2e1Sahrens 	zfs_close(zhp);
4275fa9e4066Sahrens 
4276fa9e4066Sahrens 	return (err != 0);
4277fa9e4066Sahrens }
4278fa9e4066Sahrens 
4279fa9e4066Sahrens /*
4280fa9e4066Sahrens  * Restore a backup stream from stdin.
4281fa9e4066Sahrens  */
4282fa9e4066Sahrens static int
zfs_do_receive(int argc,char ** argv)4283f2a3c691Sahrens zfs_do_receive(int argc, char **argv)
4284fa9e4066Sahrens {
4285c16bcc45SIgor Kozhukhov 	int c, err = 0;
428692241e0bSTom Erickson 	recvflags_t flags = { 0 };
42879c3fd121SMatthew Ahrens 	boolean_t abort_resumable = B_FALSE;
4288a2cdcdd2SPaul Dagnelie 	nvlist_t *props;
4289a2cdcdd2SPaul Dagnelie 
4290a2cdcdd2SPaul Dagnelie 	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
4291a2cdcdd2SPaul Dagnelie 		nomem();
4292fa9e4066Sahrens 
4293fa9e4066Sahrens 	/* check options */
42946ccda740Sloli10K 	while ((c = getopt(argc, argv, ":o:x:dehnuvFsA")) != -1) {
4295fa9e4066Sahrens 		switch (c) {
4296a2cdcdd2SPaul Dagnelie 		case 'o':
42976ccda740Sloli10K 			if (!parseprop(props, optarg)) {
42986ccda740Sloli10K 				nvlist_free(props);
42996ccda740Sloli10K 				usage(B_FALSE);
43006ccda740Sloli10K 			}
43016ccda740Sloli10K 			break;
43026ccda740Sloli10K 		case 'x':
43036ccda740Sloli10K 			if (!parsepropname(props, optarg)) {
43046ccda740Sloli10K 				nvlist_free(props);
43056ccda740Sloli10K 				usage(B_FALSE);
43066ccda740Sloli10K 			}
4307a2cdcdd2SPaul Dagnelie 			break;
4308fa9e4066Sahrens 		case 'd':
43093cb34c60Sahrens 			flags.isprefix = B_TRUE;
4310fa9e4066Sahrens 			break;
4311f64930f5STom Erickson 		case 'e':
4312f64930f5STom Erickson 			flags.isprefix = B_TRUE;
4313f64930f5STom Erickson 			flags.istail = B_TRUE;
4314f64930f5STom Erickson 			break;
43156ccda740Sloli10K 		case 'h':
43166ccda740Sloli10K 			flags.skipholds = B_TRUE;
43176ccda740Sloli10K 			break;
4318fa9e4066Sahrens 		case 'n':
43193cb34c60Sahrens 			flags.dryrun = B_TRUE;
4320fa9e4066Sahrens 			break;
432133408eefSLori Alt 		case 'u':
432233408eefSLori Alt 			flags.nomount = B_TRUE;
432333408eefSLori Alt 			break;
4324fa9e4066Sahrens 		case 'v':
43253cb34c60Sahrens 			flags.verbose = B_TRUE;
4326fa9e4066Sahrens 			break;
43279c3fd121SMatthew Ahrens 		case 's':
43289c3fd121SMatthew Ahrens 			flags.resumable = B_TRUE;
43299c3fd121SMatthew Ahrens 			break;
433098579b20Snd150628 		case 'F':
43313cb34c60Sahrens 			flags.force = B_TRUE;
433298579b20Snd150628 			break;
43339c3fd121SMatthew Ahrens 		case 'A':
43349c3fd121SMatthew Ahrens 			abort_resumable = B_TRUE;
43359c3fd121SMatthew Ahrens 			break;
4336fa9e4066Sahrens 		case ':':
4337fa9e4066Sahrens 			(void) fprintf(stderr, gettext("missing argument for "
4338fa9e4066Sahrens 			    "'%c' option\n"), optopt);
433999653d4eSeschrock 			usage(B_FALSE);
4340fa9e4066Sahrens 			break;
4341fa9e4066Sahrens 		case '?':
4342fa9e4066Sahrens 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
4343fa9e4066Sahrens 			    optopt);
434499653d4eSeschrock 			usage(B_FALSE);
4345fa9e4066Sahrens 		}
4346fa9e4066Sahrens 	}
4347fa9e4066Sahrens 
4348fa9e4066Sahrens 	argc -= optind;
4349fa9e4066Sahrens 	argv += optind;
4350fa9e4066Sahrens 
4351fa9e4066Sahrens 	/* check number of arguments */
4352fa9e4066Sahrens 	if (argc < 1) {
4353fa9e4066Sahrens 		(void) fprintf(stderr, gettext("missing snapshot argument\n"));
435499653d4eSeschrock 		usage(B_FALSE);
4355fa9e4066Sahrens 	}
4356fa9e4066Sahrens 	if (argc > 1) {
4357fa9e4066Sahrens 		(void) fprintf(stderr, gettext("too many arguments\n"));
435899653d4eSeschrock 		usage(B_FALSE);
4359fa9e4066Sahrens 	}
4360fa9e4066Sahrens 
43619c3fd121SMatthew Ahrens 	if (abort_resumable) {
43629c3fd121SMatthew Ahrens 		if (flags.isprefix || flags.istail || flags.dryrun ||
43639c3fd121SMatthew Ahrens 		    flags.resumable || flags.nomount) {
43649c3fd121SMatthew Ahrens 			(void) fprintf(stderr, gettext("invalid option"));
43659c3fd121SMatthew Ahrens 			usage(B_FALSE);
43669c3fd121SMatthew Ahrens 		}
43679c3fd121SMatthew Ahrens 
43689adfa60dSMatthew Ahrens 		char namebuf[ZFS_MAX_DATASET_NAME_LEN];
43699c3fd121SMatthew Ahrens 		(void) snprintf(namebuf, sizeof (namebuf),
43709c3fd121SMatthew Ahrens 		    "%s/%%recv", argv[0]);
43719c3fd121SMatthew Ahrens 
43729c3fd121SMatthew Ahrens 		if (zfs_dataset_exists(g_zfs, namebuf,
43739c3fd121SMatthew Ahrens 		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) {
43749c3fd121SMatthew Ahrens 			zfs_handle_t *zhp = zfs_open(g_zfs,
43759c3fd121SMatthew Ahrens 			    namebuf, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
43769c3fd121SMatthew Ahrens 			if (zhp == NULL)
43779c3fd121SMatthew Ahrens 				return (1);
43789c3fd121SMatthew Ahrens 			err = zfs_destroy(zhp, B_FALSE);
43799c3fd121SMatthew Ahrens 		} else {
43809c3fd121SMatthew Ahrens 			zfs_handle_t *zhp = zfs_open(g_zfs,
43819c3fd121SMatthew Ahrens 			    argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
43829c3fd121SMatthew Ahrens 			if (zhp == NULL)
43839c3fd121SMatthew Ahrens 				usage(B_FALSE);
43849c3fd121SMatthew Ahrens 			if (!zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) ||
43859c3fd121SMatthew Ahrens 			    zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,
43869c3fd121SMatthew Ahrens 			    NULL, 0, NULL, NULL, 0, B_TRUE) == -1) {
43879c3fd121SMatthew Ahrens 				(void) fprintf(stderr,
43889c3fd121SMatthew Ahrens 				    gettext("'%s' does not have any "
43899c3fd121SMatthew Ahrens 				    "resumable receive state to abort\n"),
43909c3fd121SMatthew Ahrens 				    argv[0]);
43919c3fd121SMatthew Ahrens 				return (1);
43929c3fd121SMatthew Ahrens 			}
43939c3fd121SMatthew Ahrens 			err = zfs_destroy(zhp, B_FALSE);
43949c3fd121SMatthew Ahrens 		}
43959c3fd121SMatthew Ahrens 
43969c3fd121SMatthew Ahrens 		return (err != 0);
43979c3fd121SMatthew Ahrens 	}
43989c3fd121SMatthew Ahrens 
4399fa9e4066Sahrens 	if (isatty(STDIN_FILENO)) {
4400fa9e4066Sahrens 		(void) fprintf(stderr,
4401fa9e4066Sahrens 		    gettext("Error: Backup stream can not be read "
4402fa9e4066Sahrens 		    "from a terminal.\n"
4403fa9e4066Sahrens 		    "You must redirect standard input.\n"));
4404fa9e4066Sahrens 		return (1);
4405fa9e4066Sahrens 	}
4406a2cdcdd2SPaul Dagnelie 	err = zfs_receive(g_zfs, argv[0], props, &flags, STDIN_FILENO, NULL);
440706eeb2adSek110237 
4408ecd6cf80Smarks 	return (err != 0);
440906eeb2adSek110237 }
441006eeb2adSek110237 
44111af68beaSAlexander Stetsenko /*
44121af68beaSAlexander Stetsenko  * allow/unallow stuff
44131af68beaSAlexander Stetsenko  */
44141af68beaSAlexander Stetsenko /* copied from zfs/sys/dsl_deleg.h */
44151af68beaSAlexander Stetsenko #define	ZFS_DELEG_PERM_CREATE		"create"
44161af68beaSAlexander Stetsenko #define	ZFS_DELEG_PERM_DESTROY		"destroy"
44171af68beaSAlexander Stetsenko #define	ZFS_DELEG_PERM_SNAPSHOT		"snapshot"
44181af68beaSAlexander Stetsenko #define	ZFS_DELEG_PERM_ROLLBACK		"rollback"
44191af68beaSAlexander Stetsenko #define	ZFS_DELEG_PERM_CLONE		"clone"
44201af68beaSAlexander Stetsenko #define	ZFS_DELEG_PERM_PROMOTE		"promote"
44211af68beaSAlexander Stetsenko #define	ZFS_DELEG_PERM_RENAME		"rename"
44221af68beaSAlexander Stetsenko #define	ZFS_DELEG_PERM_MOUNT		"mount"
44231af68beaSAlexander Stetsenko #define	ZFS_DELEG_PERM_SHARE		"share"
44241af68beaSAlexander Stetsenko #define	ZFS_DELEG_PERM_SEND		"send"
44251af68beaSAlexander Stetsenko #define	ZFS_DELEG_PERM_RECEIVE		"receive"
44261af68beaSAlexander Stetsenko #define	ZFS_DELEG_PERM_ALLOW		"allow"
44271af68beaSAlexander Stetsenko #define	ZFS_DELEG_PERM_USERPROP		"userprop"
44281af68beaSAlexander Stetsenko #define	ZFS_DELEG_PERM_VSCAN		"vscan" /* ??? */
44291af68beaSAlexander Stetsenko #define	ZFS_DELEG_PERM_USERQUOTA	"userquota"
44301af68beaSAlexander Stetsenko #define	ZFS_DELEG_PERM_GROUPQUOTA	"groupquota"
44311af68beaSAlexander Stetsenko #define	ZFS_DELEG_PERM_USERUSED		"userused"
44321af68beaSAlexander Stetsenko #define	ZFS_DELEG_PERM_GROUPUSED	"groupused"
4433f67950b2SNasf-Fan #define	ZFS_DELEG_PERM_USEROBJQUOTA	"userobjquota"
4434f67950b2SNasf-Fan #define	ZFS_DELEG_PERM_GROUPOBJQUOTA	"groupobjquota"
4435f67950b2SNasf-Fan #define	ZFS_DELEG_PERM_USEROBJUSED	"userobjused"
4436f67950b2SNasf-Fan #define	ZFS_DELEG_PERM_GROUPOBJUSED	"groupobjused"
4437f67950b2SNasf-Fan 
44381af68beaSAlexander Stetsenko #define	ZFS_DELEG_PERM_HOLD		"hold"
44391af68beaSAlexander Stetsenko #define	ZFS_DELEG_PERM_RELEASE		"release"
44401af68beaSAlexander Stetsenko #define	ZFS_DELEG_PERM_DIFF		"diff"
444178f17100SMatthew Ahrens #define	ZFS_DELEG_PERM_BOOKMARK		"bookmark"
44425cabbc6bSPrashanth Sreenivasa #define	ZFS_DELEG_PERM_REMAP		"remap"
4443eb633035STom Caputi #define	ZFS_DELEG_PERM_LOAD_KEY		"load-key"
4444eb633035STom Caputi #define	ZFS_DELEG_PERM_CHANGE_KEY	"change-key"
44451af68beaSAlexander Stetsenko 
4446f67950b2SNasf-Fan #define	ZFS_DELEG_PERM_PROJECTUSED	"projectused"
4447f67950b2SNasf-Fan #define	ZFS_DELEG_PERM_PROJECTQUOTA	"projectquota"
4448f67950b2SNasf-Fan #define	ZFS_DELEG_PERM_PROJECTOBJUSED	"projectobjused"
4449f67950b2SNasf-Fan #define	ZFS_DELEG_PERM_PROJECTOBJQUOTA	"projectobjquota"
4450f67950b2SNasf-Fan 
44511af68beaSAlexander Stetsenko #define	ZFS_NUM_DELEG_NOTES ZFS_DELEG_NOTE_NONE
44521af68beaSAlexander Stetsenko 
44531af68beaSAlexander Stetsenko static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = {
44541af68beaSAlexander Stetsenko 	{ ZFS_DELEG_PERM_ALLOW, ZFS_DELEG_NOTE_ALLOW },
44551af68beaSAlexander Stetsenko 	{ ZFS_DELEG_PERM_CLONE, ZFS_DELEG_NOTE_CLONE },
44561af68beaSAlexander Stetsenko 	{ ZFS_DELEG_PERM_CREATE, ZFS_DELEG_NOTE_CREATE },
44571af68beaSAlexander Stetsenko 	{ ZFS_DELEG_PERM_DESTROY, ZFS_DELEG_NOTE_DESTROY },
44581af68beaSAlexander Stetsenko 	{ ZFS_DELEG_PERM_DIFF, ZFS_DELEG_NOTE_DIFF},
44591af68beaSAlexander Stetsenko 	{ ZFS_DELEG_PERM_HOLD, ZFS_DELEG_NOTE_HOLD },
44601af68beaSAlexander Stetsenko 	{ ZFS_DELEG_PERM_MOUNT, ZFS_DELEG_NOTE_MOUNT },
44611af68beaSAlexander Stetsenko 	{ ZFS_DELEG_PERM_PROMOTE, ZFS_DELEG_NOTE_PROMOTE },
44621af68beaSAlexander Stetsenko 	{ ZFS_DELEG_PERM_RECEIVE, ZFS_DELEG_NOTE_RECEIVE },
44631af68beaSAlexander Stetsenko 	{ ZFS_DELEG_PERM_RELEASE, ZFS_DELEG_NOTE_RELEASE },
44641af68beaSAlexander Stetsenko 	{ ZFS_DELEG_PERM_RENAME, ZFS_DELEG_NOTE_RENAME },
44651af68beaSAlexander Stetsenko 	{ ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK },
44661af68beaSAlexander Stetsenko 	{ ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND },
44671af68beaSAlexander Stetsenko 	{ ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE },
44681af68beaSAlexander Stetsenko 	{ ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT },
446978f17100SMatthew Ahrens 	{ ZFS_DELEG_PERM_BOOKMARK, ZFS_DELEG_NOTE_BOOKMARK },
44705cabbc6bSPrashanth Sreenivasa 	{ ZFS_DELEG_PERM_REMAP, ZFS_DELEG_NOTE_REMAP },
4471eb633035STom Caputi 	{ ZFS_DELEG_PERM_LOAD_KEY, ZFS_DELEG_NOTE_LOAD_KEY },
4472eb633035STom Caputi 	{ ZFS_DELEG_PERM_CHANGE_KEY, ZFS_DELEG_NOTE_CHANGE_KEY },
44731af68beaSAlexander Stetsenko 
44741af68beaSAlexander Stetsenko 	{ ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA },
44751af68beaSAlexander Stetsenko 	{ ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED },
44761af68beaSAlexander Stetsenko 	{ ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP },
44771af68beaSAlexander Stetsenko 	{ ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_NOTE_USERQUOTA },
44781af68beaSAlexander Stetsenko 	{ ZFS_DELEG_PERM_USERUSED, ZFS_DELEG_NOTE_USERUSED },
4479f67950b2SNasf-Fan 	{ ZFS_DELEG_PERM_USEROBJQUOTA, ZFS_DELEG_NOTE_USEROBJQUOTA },
4480f67950b2SNasf-Fan 	{ ZFS_DELEG_PERM_USEROBJUSED, ZFS_DELEG_NOTE_USEROBJUSED },
4481f67950b2SNasf-Fan 	{ ZFS_DELEG_PERM_GROUPOBJQUOTA, ZFS_DELEG_NOTE_GROUPOBJQUOTA },
4482f67950b2SNasf-Fan 	{ ZFS_DELEG_PERM_GROUPOBJUSED, ZFS_DELEG_NOTE_GROUPOBJUSED },
4483f67950b2SNasf-Fan 	{ ZFS_DELEG_PERM_PROJECTUSED, ZFS_DELEG_NOTE_PROJECTUSED },
4484f67950b2SNasf-Fan 	{ ZFS_DELEG_PERM_PROJECTQUOTA, ZFS_DELEG_NOTE_PROJECTQUOTA },
4485f67950b2SNasf-Fan 	{ ZFS_DELEG_PERM_PROJECTOBJUSED, ZFS_DELEG_NOTE_PROJECTOBJUSED },
4486f67950b2SNasf-Fan 	{ ZFS_DELEG_PERM_PROJECTOBJQUOTA, ZFS_DELEG_NOTE_PROJECTOBJQUOTA },
44871af68beaSAlexander Stetsenko 	{ NULL, ZFS_DELEG_NOTE_NONE }
44881af68beaSAlexander Stetsenko };
44891af68beaSAlexander Stetsenko 
44901af68beaSAlexander Stetsenko /* permission structure */
44911af68beaSAlexander Stetsenko typedef struct deleg_perm {
44921af68beaSAlexander Stetsenko 	zfs_deleg_who_type_t	dp_who_type;
44931af68beaSAlexander Stetsenko 	const char		*dp_name;
44941af68beaSAlexander Stetsenko 	boolean_t		dp_local;
44951af68beaSAlexander Stetsenko 	boolean_t		dp_descend;
44961af68beaSAlexander Stetsenko } deleg_perm_t;
44971af68beaSAlexander Stetsenko 
44981af68beaSAlexander Stetsenko /* */
44991af68beaSAlexander Stetsenko typedef struct deleg_perm_node {
45001af68beaSAlexander Stetsenko 	deleg_perm_t		dpn_perm;
45011af68beaSAlexander Stetsenko 
45021af68beaSAlexander Stetsenko 	uu_avl_node_t		dpn_avl_node;
45031af68beaSAlexander Stetsenko } deleg_perm_node_t;
45041af68beaSAlexander Stetsenko 
45051af68beaSAlexander Stetsenko typedef struct fs_perm fs_perm_t;
45061af68beaSAlexander Stetsenko 
45071af68beaSAlexander Stetsenko /* permissions set */
45081af68beaSAlexander Stetsenko typedef struct who_perm {
45091af68beaSAlexander Stetsenko 	zfs_deleg_who_type_t	who_type;
45101af68beaSAlexander Stetsenko 	const char		*who_name;		/* id */
45111af68beaSAlexander Stetsenko 	char			who_ug_name[256];	/* user/group name */
45121af68beaSAlexander Stetsenko 	fs_perm_t		*who_fsperm;		/* uplink */
45131af68beaSAlexander Stetsenko 
45141af68beaSAlexander Stetsenko 	uu_avl_t		*who_deleg_perm_avl;	/* permissions */
45151af68beaSAlexander Stetsenko } who_perm_t;
45161af68beaSAlexander Stetsenko 
45171af68beaSAlexander Stetsenko /* */
45181af68beaSAlexander Stetsenko typedef struct who_perm_node {
45191af68beaSAlexander Stetsenko 	who_perm_t	who_perm;
45201af68beaSAlexander Stetsenko 	uu_avl_node_t	who_avl_node;
45211af68beaSAlexander Stetsenko } who_perm_node_t;
45221af68beaSAlexander Stetsenko 
45231af68beaSAlexander Stetsenko typedef struct fs_perm_set fs_perm_set_t;
45241af68beaSAlexander Stetsenko /* fs permissions */
45251af68beaSAlexander Stetsenko struct fs_perm {
45261af68beaSAlexander Stetsenko 	const char		*fsp_name;
45271af68beaSAlexander Stetsenko 
45281af68beaSAlexander Stetsenko 	uu_avl_t		*fsp_sc_avl;	/* sets,create */
45291af68beaSAlexander Stetsenko 	uu_avl_t		*fsp_uge_avl;	/* user,group,everyone */
45301af68beaSAlexander Stetsenko 
45311af68beaSAlexander Stetsenko 	fs_perm_set_t		*fsp_set;	/* uplink */
45321af68beaSAlexander Stetsenko };
45331af68beaSAlexander Stetsenko 
45341af68beaSAlexander Stetsenko /* */
45351af68beaSAlexander Stetsenko typedef struct fs_perm_node {
45361af68beaSAlexander Stetsenko 	fs_perm_t	fspn_fsperm;
45371af68beaSAlexander Stetsenko 	uu_avl_t	*fspn_avl;
45381af68beaSAlexander Stetsenko 
45391af68beaSAlexander Stetsenko 	uu_list_node_t	fspn_list_node;
45401af68beaSAlexander Stetsenko } fs_perm_node_t;
45411af68beaSAlexander Stetsenko 
45421af68beaSAlexander Stetsenko /* top level structure */
45431af68beaSAlexander Stetsenko struct fs_perm_set {
45441af68beaSAlexander Stetsenko 	uu_list_pool_t	*fsps_list_pool;
45451af68beaSAlexander Stetsenko 	uu_list_t	*fsps_list; /* list of fs_perms */
45461af68beaSAlexander Stetsenko 
45471af68beaSAlexander Stetsenko 	uu_avl_pool_t	*fsps_named_set_avl_pool;
45481af68beaSAlexander Stetsenko 	uu_avl_pool_t	*fsps_who_perm_avl_pool;
45491af68beaSAlexander Stetsenko 	uu_avl_pool_t	*fsps_deleg_perm_avl_pool;
45501af68beaSAlexander Stetsenko };
45511af68beaSAlexander Stetsenko 
45521af68beaSAlexander Stetsenko static inline const char *
deleg_perm_type(zfs_deleg_note_t note)45531af68beaSAlexander Stetsenko deleg_perm_type(zfs_deleg_note_t note)
45541af68beaSAlexander Stetsenko {
45551af68beaSAlexander Stetsenko 	/* subcommands */
45561af68beaSAlexander Stetsenko 	switch (note) {
45571af68beaSAlexander Stetsenko 		/* SUBCOMMANDS */
45581af68beaSAlexander Stetsenko 		/* OTHER */
45591af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_GROUPQUOTA:
45601af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_GROUPUSED:
45611af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_USERPROP:
45621af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_USERQUOTA:
45631af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_USERUSED:
4564f67950b2SNasf-Fan 	case ZFS_DELEG_NOTE_USEROBJQUOTA:
4565f67950b2SNasf-Fan 	case ZFS_DELEG_NOTE_USEROBJUSED:
4566f67950b2SNasf-Fan 	case ZFS_DELEG_NOTE_GROUPOBJQUOTA:
4567f67950b2SNasf-Fan 	case ZFS_DELEG_NOTE_GROUPOBJUSED:
4568f67950b2SNasf-Fan 	case ZFS_DELEG_NOTE_PROJECTUSED:
4569f67950b2SNasf-Fan 	case ZFS_DELEG_NOTE_PROJECTQUOTA:
4570f67950b2SNasf-Fan 	case ZFS_DELEG_NOTE_PROJECTOBJUSED:
4571f67950b2SNasf-Fan 	case ZFS_DELEG_NOTE_PROJECTOBJQUOTA:
45721af68beaSAlexander Stetsenko 		/* other */
45731af68beaSAlexander Stetsenko 		return (gettext("other"));
45741af68beaSAlexander Stetsenko 	default:
45751af68beaSAlexander Stetsenko 		return (gettext("subcommand"));
45761af68beaSAlexander Stetsenko 	}
45771af68beaSAlexander Stetsenko }
45781af68beaSAlexander Stetsenko 
4579c16bcc45SIgor Kozhukhov static int
who_type2weight(zfs_deleg_who_type_t who_type)45801af68beaSAlexander Stetsenko who_type2weight(zfs_deleg_who_type_t who_type)
45811af68beaSAlexander Stetsenko {
45821af68beaSAlexander Stetsenko 	int res;
45831af68beaSAlexander Stetsenko 	switch (who_type) {
45841af68beaSAlexander Stetsenko 		case ZFS_DELEG_NAMED_SET_SETS:
45851af68beaSAlexander Stetsenko 		case ZFS_DELEG_NAMED_SET:
45861af68beaSAlexander Stetsenko 			res = 0;
45871af68beaSAlexander Stetsenko 			break;
45881af68beaSAlexander Stetsenko 		case ZFS_DELEG_CREATE_SETS:
45891af68beaSAlexander Stetsenko 		case ZFS_DELEG_CREATE:
45901af68beaSAlexander Stetsenko 			res = 1;
45911af68beaSAlexander Stetsenko 			break;
45921af68beaSAlexander Stetsenko 		case ZFS_DELEG_USER_SETS:
45931af68beaSAlexander Stetsenko 		case ZFS_DELEG_USER:
45941af68beaSAlexander Stetsenko 			res = 2;
45951af68beaSAlexander Stetsenko 			break;
45961af68beaSAlexander Stetsenko 		case ZFS_DELEG_GROUP_SETS:
45971af68beaSAlexander Stetsenko 		case ZFS_DELEG_GROUP:
45981af68beaSAlexander Stetsenko 			res = 3;
45991af68beaSAlexander Stetsenko 			break;
46001af68beaSAlexander Stetsenko 		case ZFS_DELEG_EVERYONE_SETS:
46011af68beaSAlexander Stetsenko 		case ZFS_DELEG_EVERYONE:
46021af68beaSAlexander Stetsenko 			res = 4;
46031af68beaSAlexander Stetsenko 			break;
46041af68beaSAlexander Stetsenko 		default:
46051af68beaSAlexander Stetsenko 			res = -1;
46061af68beaSAlexander Stetsenko 	}
46071af68beaSAlexander Stetsenko 
46081af68beaSAlexander Stetsenko 	return (res);
46091af68beaSAlexander Stetsenko }
46101af68beaSAlexander Stetsenko 
46111af68beaSAlexander Stetsenko /* ARGSUSED */
46121af68beaSAlexander Stetsenko static int
who_perm_compare(const void * larg,const void * rarg,void * unused)46131af68beaSAlexander Stetsenko who_perm_compare(const void *larg, const void *rarg, void *unused)
46141af68beaSAlexander Stetsenko {
46151af68beaSAlexander Stetsenko 	const who_perm_node_t *l = larg;
46161af68beaSAlexander Stetsenko 	const who_perm_node_t *r = rarg;
46171af68beaSAlexander Stetsenko 	zfs_deleg_who_type_t ltype = l->who_perm.who_type;
46181af68beaSAlexander Stetsenko 	zfs_deleg_who_type_t rtype = r->who_perm.who_type;
46191af68beaSAlexander Stetsenko 	int lweight = who_type2weight(ltype);
46201af68beaSAlexander Stetsenko 	int rweight = who_type2weight(rtype);
46211af68beaSAlexander Stetsenko 	int res = lweight - rweight;
46221af68beaSAlexander Stetsenko 	if (res == 0)
46231af68beaSAlexander Stetsenko 		res = strncmp(l->who_perm.who_name, r->who_perm.who_name,
46241af68beaSAlexander Stetsenko 		    ZFS_MAX_DELEG_NAME-1);
46251af68beaSAlexander Stetsenko 
46261af68beaSAlexander Stetsenko 	if (res == 0)
46271af68beaSAlexander Stetsenko 		return (0);
46281af68beaSAlexander Stetsenko 	if (res > 0)
46291af68beaSAlexander Stetsenko 		return (1);
46301af68beaSAlexander Stetsenko 	else
46311af68beaSAlexander Stetsenko 		return (-1);
46321af68beaSAlexander Stetsenko }
46331af68beaSAlexander Stetsenko 
46341af68beaSAlexander Stetsenko /* ARGSUSED */
46351af68beaSAlexander Stetsenko static int
deleg_perm_compare(const void * larg,const void * rarg,void * unused)46361af68beaSAlexander Stetsenko deleg_perm_compare(const void *larg, const void *rarg, void *unused)
46371af68beaSAlexander Stetsenko {
46381af68beaSAlexander Stetsenko 	const deleg_perm_node_t *l = larg;
46391af68beaSAlexander Stetsenko 	const deleg_perm_node_t *r = rarg;
46401af68beaSAlexander Stetsenko 	int res =  strncmp(l->dpn_perm.dp_name, r->dpn_perm.dp_name,
46411af68beaSAlexander Stetsenko 	    ZFS_MAX_DELEG_NAME-1);
46421af68beaSAlexander Stetsenko 
46431af68beaSAlexander Stetsenko 	if (res == 0)
46441af68beaSAlexander Stetsenko 		return (0);
46451af68beaSAlexander Stetsenko 
46461af68beaSAlexander Stetsenko 	if (res > 0)
46471af68beaSAlexander Stetsenko 		return (1);
46481af68beaSAlexander Stetsenko 	else
46491af68beaSAlexander Stetsenko 		return (-1);
46501af68beaSAlexander Stetsenko }
46511af68beaSAlexander Stetsenko 
46521af68beaSAlexander Stetsenko static inline void
fs_perm_set_init(fs_perm_set_t * fspset)46531af68beaSAlexander Stetsenko fs_perm_set_init(fs_perm_set_t *fspset)
46541af68beaSAlexander Stetsenko {
46551af68beaSAlexander Stetsenko 	bzero(fspset, sizeof (fs_perm_set_t));
46561af68beaSAlexander Stetsenko 
46571af68beaSAlexander Stetsenko 	if ((fspset->fsps_list_pool = uu_list_pool_create("fsps_list_pool",
46581af68beaSAlexander Stetsenko 	    sizeof (fs_perm_node_t), offsetof(fs_perm_node_t, fspn_list_node),
46591af68beaSAlexander Stetsenko 	    NULL, UU_DEFAULT)) == NULL)
46601af68beaSAlexander Stetsenko 		nomem();
46611af68beaSAlexander Stetsenko 	if ((fspset->fsps_list = uu_list_create(fspset->fsps_list_pool, NULL,
46621af68beaSAlexander Stetsenko 	    UU_DEFAULT)) == NULL)
46631af68beaSAlexander Stetsenko 		nomem();
46641af68beaSAlexander Stetsenko 
46651af68beaSAlexander Stetsenko 	if ((fspset->fsps_named_set_avl_pool = uu_avl_pool_create(
46661af68beaSAlexander Stetsenko 	    "named_set_avl_pool", sizeof (who_perm_node_t), offsetof(
46671af68beaSAlexander Stetsenko 	    who_perm_node_t, who_avl_node), who_perm_compare,
46681af68beaSAlexander Stetsenko 	    UU_DEFAULT)) == NULL)
46691af68beaSAlexander Stetsenko 		nomem();
46701af68beaSAlexander Stetsenko 
46711af68beaSAlexander Stetsenko 	if ((fspset->fsps_who_perm_avl_pool = uu_avl_pool_create(
46721af68beaSAlexander Stetsenko 	    "who_perm_avl_pool", sizeof (who_perm_node_t), offsetof(
46731af68beaSAlexander Stetsenko 	    who_perm_node_t, who_avl_node), who_perm_compare,
46741af68beaSAlexander Stetsenko 	    UU_DEFAULT)) == NULL)
46751af68beaSAlexander Stetsenko 		nomem();
46761af68beaSAlexander Stetsenko 
46771af68beaSAlexander Stetsenko 	if ((fspset->fsps_deleg_perm_avl_pool = uu_avl_pool_create(
46781af68beaSAlexander Stetsenko 	    "deleg_perm_avl_pool", sizeof (deleg_perm_node_t), offsetof(
46791af68beaSAlexander Stetsenko 	    deleg_perm_node_t, dpn_avl_node), deleg_perm_compare, UU_DEFAULT))
46801af68beaSAlexander Stetsenko 	    == NULL)
46811af68beaSAlexander Stetsenko 		nomem();
46821af68beaSAlexander Stetsenko }
46831af68beaSAlexander Stetsenko 
46841af68beaSAlexander Stetsenko static inline void fs_perm_fini(fs_perm_t *);
46851af68beaSAlexander Stetsenko static inline void who_perm_fini(who_perm_t *);
46861af68beaSAlexander Stetsenko 
46871af68beaSAlexander Stetsenko static inline void
fs_perm_set_fini(fs_perm_set_t * fspset)46881af68beaSAlexander Stetsenko fs_perm_set_fini(fs_perm_set_t *fspset)
46891af68beaSAlexander Stetsenko {
46901af68beaSAlexander Stetsenko 	fs_perm_node_t *node = uu_list_first(fspset->fsps_list);
46911af68beaSAlexander Stetsenko 
46921af68beaSAlexander Stetsenko 	while (node != NULL) {
46931af68beaSAlexander Stetsenko 		fs_perm_node_t *next_node =
46941af68beaSAlexander Stetsenko 		    uu_list_next(fspset->fsps_list, node);
46951af68beaSAlexander Stetsenko 		fs_perm_t *fsperm = &node->fspn_fsperm;
46961af68beaSAlexander Stetsenko 		fs_perm_fini(fsperm);
46971af68beaSAlexander Stetsenko 		uu_list_remove(fspset->fsps_list, node);
46981af68beaSAlexander Stetsenko 		free(node);
46991af68beaSAlexander Stetsenko 		node = next_node;
47001af68beaSAlexander Stetsenko 	}
47011af68beaSAlexander Stetsenko 
47021af68beaSAlexander Stetsenko 	uu_avl_pool_destroy(fspset->fsps_named_set_avl_pool);
47031af68beaSAlexander Stetsenko 	uu_avl_pool_destroy(fspset->fsps_who_perm_avl_pool);
47041af68beaSAlexander Stetsenko 	uu_avl_pool_destroy(fspset->fsps_deleg_perm_avl_pool);
47051af68beaSAlexander Stetsenko }
47061af68beaSAlexander Stetsenko 
47071af68beaSAlexander Stetsenko static inline void
deleg_perm_init(deleg_perm_t * deleg_perm,zfs_deleg_who_type_t type,const char * name)47081af68beaSAlexander Stetsenko deleg_perm_init(deleg_perm_t *deleg_perm, zfs_deleg_who_type_t type,
47091af68beaSAlexander Stetsenko     const char *name)
47101af68beaSAlexander Stetsenko {
47111af68beaSAlexander Stetsenko 	deleg_perm->dp_who_type = type;
47121af68beaSAlexander Stetsenko 	deleg_perm->dp_name = name;
47131af68beaSAlexander Stetsenko }
47141af68beaSAlexander Stetsenko 
47151af68beaSAlexander Stetsenko static inline void
who_perm_init(who_perm_t * who_perm,fs_perm_t * fsperm,zfs_deleg_who_type_t type,const char * name)47161af68beaSAlexander Stetsenko who_perm_init(who_perm_t *who_perm, fs_perm_t *fsperm,
47171af68beaSAlexander Stetsenko     zfs_deleg_who_type_t type, const char *name)
47181af68beaSAlexander Stetsenko {
47191af68beaSAlexander Stetsenko 	uu_avl_pool_t	*pool;
47201af68beaSAlexander Stetsenko 	pool = fsperm->fsp_set->fsps_deleg_perm_avl_pool;
47211af68beaSAlexander Stetsenko 
47221af68beaSAlexander Stetsenko 	bzero(who_perm, sizeof (who_perm_t));
47231af68beaSAlexander Stetsenko 
47241af68beaSAlexander Stetsenko 	if ((who_perm->who_deleg_perm_avl = uu_avl_create(pool, NULL,
47251af68beaSAlexander Stetsenko 	    UU_DEFAULT)) == NULL)
47261af68beaSAlexander Stetsenko 		nomem();
47271af68beaSAlexander Stetsenko 
47281af68beaSAlexander Stetsenko 	who_perm->who_type = type;
47291af68beaSAlexander Stetsenko 	who_perm->who_name = name;
47301af68beaSAlexander Stetsenko 	who_perm->who_fsperm = fsperm;
47311af68beaSAlexander Stetsenko }
47321af68beaSAlexander Stetsenko 
47331af68beaSAlexander Stetsenko static inline void
who_perm_fini(who_perm_t * who_perm)47341af68beaSAlexander Stetsenko who_perm_fini(who_perm_t *who_perm)
47351af68beaSAlexander Stetsenko {
47361af68beaSAlexander Stetsenko 	deleg_perm_node_t *node = uu_avl_first(who_perm->who_deleg_perm_avl);
47371af68beaSAlexander Stetsenko 
47381af68beaSAlexander Stetsenko 	while (node != NULL) {
47391af68beaSAlexander Stetsenko 		deleg_perm_node_t *next_node =
47401af68beaSAlexander Stetsenko 		    uu_avl_next(who_perm->who_deleg_perm_avl, node);
47411af68beaSAlexander Stetsenko 
47421af68beaSAlexander Stetsenko 		uu_avl_remove(who_perm->who_deleg_perm_avl, node);
47431af68beaSAlexander Stetsenko 		free(node);
47441af68beaSAlexander Stetsenko 		node = next_node;
47451af68beaSAlexander Stetsenko 	}
47461af68beaSAlexander Stetsenko 
47471af68beaSAlexander Stetsenko 	uu_avl_destroy(who_perm->who_deleg_perm_avl);
47481af68beaSAlexander Stetsenko }
47491af68beaSAlexander Stetsenko 
47501af68beaSAlexander Stetsenko static inline void
fs_perm_init(fs_perm_t * fsperm,fs_perm_set_t * fspset,const char * fsname)47511af68beaSAlexander Stetsenko fs_perm_init(fs_perm_t *fsperm, fs_perm_set_t *fspset, const char *fsname)
47521af68beaSAlexander Stetsenko {
47531af68beaSAlexander Stetsenko 	uu_avl_pool_t	*nset_pool = fspset->fsps_named_set_avl_pool;
47541af68beaSAlexander Stetsenko 	uu_avl_pool_t	*who_pool = fspset->fsps_who_perm_avl_pool;
47551af68beaSAlexander Stetsenko 
47561af68beaSAlexander Stetsenko 	bzero(fsperm, sizeof (fs_perm_t));
47571af68beaSAlexander Stetsenko 
47581af68beaSAlexander Stetsenko 	if ((fsperm->fsp_sc_avl = uu_avl_create(nset_pool, NULL, UU_DEFAULT))
47591af68beaSAlexander Stetsenko 	    == NULL)
47601af68beaSAlexander Stetsenko 		nomem();
47611af68beaSAlexander Stetsenko 
47621af68beaSAlexander Stetsenko 	if ((fsperm->fsp_uge_avl = uu_avl_create(who_pool, NULL, UU_DEFAULT))
47631af68beaSAlexander Stetsenko 	    == NULL)
47641af68beaSAlexander Stetsenko 		nomem();
47651af68beaSAlexander Stetsenko 
47661af68beaSAlexander Stetsenko 	fsperm->fsp_set = fspset;
47671af68beaSAlexander Stetsenko 	fsperm->fsp_name = fsname;
47681af68beaSAlexander Stetsenko }
47691af68beaSAlexander Stetsenko 
47701af68beaSAlexander Stetsenko static inline void
fs_perm_fini(fs_perm_t * fsperm)47711af68beaSAlexander Stetsenko fs_perm_fini(fs_perm_t *fsperm)
47721af68beaSAlexander Stetsenko {
47731af68beaSAlexander Stetsenko 	who_perm_node_t *node = uu_avl_first(fsperm->fsp_sc_avl);
47741af68beaSAlexander Stetsenko 	while (node != NULL) {
47751af68beaSAlexander Stetsenko 		who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_sc_avl,
47761af68beaSAlexander Stetsenko 		    node);
47771af68beaSAlexander Stetsenko 		who_perm_t *who_perm = &node->who_perm;
47781af68beaSAlexander Stetsenko 		who_perm_fini(who_perm);
47791af68beaSAlexander Stetsenko 		uu_avl_remove(fsperm->fsp_sc_avl, node);
47801af68beaSAlexander Stetsenko 		free(node);
47811af68beaSAlexander Stetsenko 		node = next_node;
47821af68beaSAlexander Stetsenko 	}
47831af68beaSAlexander Stetsenko 
47841af68beaSAlexander Stetsenko 	node = uu_avl_first(fsperm->fsp_uge_avl);
47851af68beaSAlexander Stetsenko 	while (node != NULL) {
47861af68beaSAlexander Stetsenko 		who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_uge_avl,
47871af68beaSAlexander Stetsenko 		    node);
47881af68beaSAlexander Stetsenko 		who_perm_t *who_perm = &node->who_perm;
47891af68beaSAlexander Stetsenko 		who_perm_fini(who_perm);
47901af68beaSAlexander Stetsenko 		uu_avl_remove(fsperm->fsp_uge_avl, node);
47911af68beaSAlexander Stetsenko 		free(node);
47921af68beaSAlexander Stetsenko 		node = next_node;
47931af68beaSAlexander Stetsenko 	}
47941af68beaSAlexander Stetsenko 
47951af68beaSAlexander Stetsenko 	uu_avl_destroy(fsperm->fsp_sc_avl);
47961af68beaSAlexander Stetsenko 	uu_avl_destroy(fsperm->fsp_uge_avl);
47971af68beaSAlexander Stetsenko }
47981af68beaSAlexander Stetsenko 
4799c16bcc45SIgor Kozhukhov static void
set_deleg_perm_node(uu_avl_t * avl,deleg_perm_node_t * node,zfs_deleg_who_type_t who_type,const char * name,char locality)48001af68beaSAlexander Stetsenko set_deleg_perm_node(uu_avl_t *avl, deleg_perm_node_t *node,
48011af68beaSAlexander Stetsenko     zfs_deleg_who_type_t who_type, const char *name, char locality)
48021af68beaSAlexander Stetsenko {
48031af68beaSAlexander Stetsenko 	uu_avl_index_t idx = 0;
48041af68beaSAlexander Stetsenko 
48051af68beaSAlexander Stetsenko 	deleg_perm_node_t *found_node = NULL;
48061af68beaSAlexander Stetsenko 	deleg_perm_t	*deleg_perm = &node->dpn_perm;
48071af68beaSAlexander Stetsenko 
48081af68beaSAlexander Stetsenko 	deleg_perm_init(deleg_perm, who_type, name);
48091af68beaSAlexander Stetsenko 
48101af68beaSAlexander Stetsenko 	if ((found_node = uu_avl_find(avl, node, NULL, &idx))
48111af68beaSAlexander Stetsenko 	    == NULL)
48121af68beaSAlexander Stetsenko 		uu_avl_insert(avl, node, idx);
48131af68beaSAlexander Stetsenko 	else {
48141af68beaSAlexander Stetsenko 		node = found_node;
48151af68beaSAlexander Stetsenko 		deleg_perm = &node->dpn_perm;
48161af68beaSAlexander Stetsenko 	}
48171af68beaSAlexander Stetsenko 
48181af68beaSAlexander Stetsenko 
48191af68beaSAlexander Stetsenko 	switch (locality) {
48201af68beaSAlexander Stetsenko 	case ZFS_DELEG_LOCAL:
48211af68beaSAlexander Stetsenko 		deleg_perm->dp_local = B_TRUE;
48221af68beaSAlexander Stetsenko 		break;
48231af68beaSAlexander Stetsenko 	case ZFS_DELEG_DESCENDENT:
48241af68beaSAlexander Stetsenko 		deleg_perm->dp_descend = B_TRUE;
48251af68beaSAlexander Stetsenko 		break;
48261af68beaSAlexander Stetsenko 	case ZFS_DELEG_NA:
48271af68beaSAlexander Stetsenko 		break;
48281af68beaSAlexander Stetsenko 	default:
48291af68beaSAlexander Stetsenko 		assert(B_FALSE); /* invalid locality */
48301af68beaSAlexander Stetsenko 	}
48311af68beaSAlexander Stetsenko }
48321af68beaSAlexander Stetsenko 
48331af68beaSAlexander Stetsenko static inline int
parse_who_perm(who_perm_t * who_perm,nvlist_t * nvl,char locality)48341af68beaSAlexander Stetsenko parse_who_perm(who_perm_t *who_perm, nvlist_t *nvl, char locality)
48351af68beaSAlexander Stetsenko {
48361af68beaSAlexander Stetsenko 	nvpair_t *nvp = NULL;
48371af68beaSAlexander Stetsenko 	fs_perm_set_t *fspset = who_perm->who_fsperm->fsp_set;
48381af68beaSAlexander Stetsenko 	uu_avl_t *avl = who_perm->who_deleg_perm_avl;
48391af68beaSAlexander Stetsenko 	zfs_deleg_who_type_t who_type = who_perm->who_type;
48401af68beaSAlexander Stetsenko 
48411af68beaSAlexander Stetsenko 	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
48421af68beaSAlexander Stetsenko 		const char *name = nvpair_name(nvp);
48431af68beaSAlexander Stetsenko 		data_type_t type = nvpair_type(nvp);
48441af68beaSAlexander Stetsenko 		uu_avl_pool_t *avl_pool = fspset->fsps_deleg_perm_avl_pool;
48451af68beaSAlexander Stetsenko 		deleg_perm_node_t *node =
48461af68beaSAlexander Stetsenko 		    safe_malloc(sizeof (deleg_perm_node_t));
48471af68beaSAlexander Stetsenko 
48481af68beaSAlexander Stetsenko 		assert(type == DATA_TYPE_BOOLEAN);
48491af68beaSAlexander Stetsenko 
48501af68beaSAlexander Stetsenko 		uu_avl_node_init(node, &node->dpn_avl_node, avl_pool);
48511af68beaSAlexander Stetsenko 		set_deleg_perm_node(avl, node, who_type, name, locality);
48521af68beaSAlexander Stetsenko 	}
48531af68beaSAlexander Stetsenko 
48541af68beaSAlexander Stetsenko 	return (0);
48551af68beaSAlexander Stetsenko }
48561af68beaSAlexander Stetsenko 
48571af68beaSAlexander Stetsenko static inline int
parse_fs_perm(fs_perm_t * fsperm,nvlist_t * nvl)48581af68beaSAlexander Stetsenko parse_fs_perm(fs_perm_t *fsperm, nvlist_t *nvl)
48591af68beaSAlexander Stetsenko {
48601af68beaSAlexander Stetsenko 	nvpair_t *nvp = NULL;
48611af68beaSAlexander Stetsenko 	fs_perm_set_t *fspset = fsperm->fsp_set;
48621af68beaSAlexander Stetsenko 
48631af68beaSAlexander Stetsenko 	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
48641af68beaSAlexander Stetsenko 		nvlist_t *nvl2 = NULL;
48651af68beaSAlexander Stetsenko 		const char *name = nvpair_name(nvp);
48661af68beaSAlexander Stetsenko 		uu_avl_t *avl = NULL;
4867c16bcc45SIgor Kozhukhov 		uu_avl_pool_t *avl_pool = NULL;
48681af68beaSAlexander Stetsenko 		zfs_deleg_who_type_t perm_type = name[0];
48691af68beaSAlexander Stetsenko 		char perm_locality = name[1];
48701af68beaSAlexander Stetsenko 		const char *perm_name = name + 3;
48711af68beaSAlexander Stetsenko 		boolean_t is_set = B_TRUE;
48721af68beaSAlexander Stetsenko 		who_perm_t *who_perm = NULL;
48731af68beaSAlexander Stetsenko 
48741af68beaSAlexander Stetsenko 		assert('$' == name[2]);
48751af68beaSAlexander Stetsenko 
48761af68beaSAlexander Stetsenko 		if (nvpair_value_nvlist(nvp, &nvl2) != 0)
48771af68beaSAlexander Stetsenko 			return (-1);
48781af68beaSAlexander Stetsenko 
48791af68beaSAlexander Stetsenko 		switch (perm_type) {
48801af68beaSAlexander Stetsenko 		case ZFS_DELEG_CREATE:
48811af68beaSAlexander Stetsenko 		case ZFS_DELEG_CREATE_SETS:
48821af68beaSAlexander Stetsenko 		case ZFS_DELEG_NAMED_SET:
48831af68beaSAlexander Stetsenko 		case ZFS_DELEG_NAMED_SET_SETS:
48841af68beaSAlexander Stetsenko 			avl_pool = fspset->fsps_named_set_avl_pool;
48851af68beaSAlexander Stetsenko 			avl = fsperm->fsp_sc_avl;
48861af68beaSAlexander Stetsenko 			break;
48871af68beaSAlexander Stetsenko 		case ZFS_DELEG_USER:
48881af68beaSAlexander Stetsenko 		case ZFS_DELEG_USER_SETS:
48891af68beaSAlexander Stetsenko 		case ZFS_DELEG_GROUP:
48901af68beaSAlexander Stetsenko 		case ZFS_DELEG_GROUP_SETS:
48911af68beaSAlexander Stetsenko 		case ZFS_DELEG_EVERYONE:
48921af68beaSAlexander Stetsenko 		case ZFS_DELEG_EVERYONE_SETS:
48931af68beaSAlexander Stetsenko 			avl_pool = fspset->fsps_who_perm_avl_pool;
48941af68beaSAlexander Stetsenko 			avl = fsperm->fsp_uge_avl;
48951af68beaSAlexander Stetsenko 			break;
4896c16bcc45SIgor Kozhukhov 
4897c16bcc45SIgor Kozhukhov 		default:
4898c16bcc45SIgor Kozhukhov 			assert(!"unhandled zfs_deleg_who_type_t");
48991af68beaSAlexander Stetsenko 		}
49001af68beaSAlexander Stetsenko 
49011af68beaSAlexander Stetsenko 		if (is_set) {
49021af68beaSAlexander Stetsenko 			who_perm_node_t *found_node = NULL;
49031af68beaSAlexander Stetsenko 			who_perm_node_t *node = safe_malloc(
49041af68beaSAlexander Stetsenko 			    sizeof (who_perm_node_t));
49051af68beaSAlexander Stetsenko 			who_perm = &node->who_perm;
49061af68beaSAlexander Stetsenko 			uu_avl_index_t idx = 0;
49071af68beaSAlexander Stetsenko 
49081af68beaSAlexander Stetsenko 			uu_avl_node_init(node, &node->who_avl_node, avl_pool);
49091af68beaSAlexander Stetsenko 			who_perm_init(who_perm, fsperm, perm_type, perm_name);
49101af68beaSAlexander Stetsenko 
49111af68beaSAlexander Stetsenko 			if ((found_node = uu_avl_find(avl, node, NULL, &idx))
49121af68beaSAlexander Stetsenko 			    == NULL) {
49131af68beaSAlexander Stetsenko 				if (avl == fsperm->fsp_uge_avl) {
49141af68beaSAlexander Stetsenko 					uid_t rid = 0;
49151af68beaSAlexander Stetsenko 					struct passwd *p = NULL;
49161af68beaSAlexander Stetsenko 					struct group *g = NULL;
49171af68beaSAlexander Stetsenko 					const char *nice_name = NULL;
49181af68beaSAlexander Stetsenko 
49191af68beaSAlexander Stetsenko 					switch (perm_type) {
49201af68beaSAlexander Stetsenko 					case ZFS_DELEG_USER_SETS:
49211af68beaSAlexander Stetsenko 					case ZFS_DELEG_USER:
49221af68beaSAlexander Stetsenko 						rid = atoi(perm_name);
49231af68beaSAlexander Stetsenko 						p = getpwuid(rid);
49241af68beaSAlexander Stetsenko 						if (p)
49251af68beaSAlexander Stetsenko 							nice_name = p->pw_name;
49261af68beaSAlexander Stetsenko 						break;
49271af68beaSAlexander Stetsenko 					case ZFS_DELEG_GROUP_SETS:
49281af68beaSAlexander Stetsenko 					case ZFS_DELEG_GROUP:
49291af68beaSAlexander Stetsenko 						rid = atoi(perm_name);
49301af68beaSAlexander Stetsenko 						g = getgrgid(rid);
49311af68beaSAlexander Stetsenko 						if (g)
49321af68beaSAlexander Stetsenko 							nice_name = g->gr_name;
49331af68beaSAlexander Stetsenko 						break;
4934c16bcc45SIgor Kozhukhov 
4935c16bcc45SIgor Kozhukhov 					default:
4936c16bcc45SIgor Kozhukhov 						break;
49371af68beaSAlexander Stetsenko 					}
49381af68beaSAlexander Stetsenko 
49391af68beaSAlexander Stetsenko 					if (nice_name != NULL)
49401af68beaSAlexander Stetsenko 						(void) strlcpy(
49411af68beaSAlexander Stetsenko 						    node->who_perm.who_ug_name,
49421af68beaSAlexander Stetsenko 						    nice_name, 256);
49431af68beaSAlexander Stetsenko 				}
49441af68beaSAlexander Stetsenko 
49451af68beaSAlexander Stetsenko 				uu_avl_insert(avl, node, idx);
49461af68beaSAlexander Stetsenko 			} else {
49471af68beaSAlexander Stetsenko 				node = found_node;
49481af68beaSAlexander Stetsenko 				who_perm = &node->who_perm;
49491af68beaSAlexander Stetsenko 			}
49501af68beaSAlexander Stetsenko 		}
49511af68beaSAlexander Stetsenko 
49521af68beaSAlexander Stetsenko 		(void) parse_who_perm(who_perm, nvl2, perm_locality);
49531af68beaSAlexander Stetsenko 	}
49541af68beaSAlexander Stetsenko 
49551af68beaSAlexander Stetsenko 	return (0);
49561af68beaSAlexander Stetsenko }
49571af68beaSAlexander Stetsenko 
49581af68beaSAlexander Stetsenko static inline int
parse_fs_perm_set(fs_perm_set_t * fspset,nvlist_t * nvl)49591af68beaSAlexander Stetsenko parse_fs_perm_set(fs_perm_set_t *fspset, nvlist_t *nvl)
49601af68beaSAlexander Stetsenko {
49611af68beaSAlexander Stetsenko 	nvpair_t *nvp = NULL;
49621af68beaSAlexander Stetsenko 	uu_avl_index_t idx = 0;
49631af68beaSAlexander Stetsenko 
49641af68beaSAlexander Stetsenko 	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
49651af68beaSAlexander Stetsenko 		nvlist_t *nvl2 = NULL;
49661af68beaSAlexander Stetsenko 		const char *fsname = nvpair_name(nvp);
49671af68beaSAlexander Stetsenko 		data_type_t type = nvpair_type(nvp);
49681af68beaSAlexander Stetsenko 		fs_perm_t *fsperm = NULL;
49691af68beaSAlexander Stetsenko 		fs_perm_node_t *node = safe_malloc(sizeof (fs_perm_node_t));
49701af68beaSAlexander Stetsenko 		if (node == NULL)
49711af68beaSAlexander Stetsenko 			nomem();
49721af68beaSAlexander Stetsenko 
49731af68beaSAlexander Stetsenko 		fsperm = &node->fspn_fsperm;
49741af68beaSAlexander Stetsenko 
49751af68beaSAlexander Stetsenko 		assert(DATA_TYPE_NVLIST == type);
49761af68beaSAlexander Stetsenko 
49771af68beaSAlexander Stetsenko 		uu_list_node_init(node, &node->fspn_list_node,
49781af68beaSAlexander Stetsenko 		    fspset->fsps_list_pool);
49791af68beaSAlexander Stetsenko 
49801af68beaSAlexander Stetsenko 		idx = uu_list_numnodes(fspset->fsps_list);
49811af68beaSAlexander Stetsenko 		fs_perm_init(fsperm, fspset, fsname);
49821af68beaSAlexander Stetsenko 
49831af68beaSAlexander Stetsenko 		if (nvpair_value_nvlist(nvp, &nvl2) != 0)
49841af68beaSAlexander Stetsenko 			return (-1);
49851af68beaSAlexander Stetsenko 
49861af68beaSAlexander Stetsenko 		(void) parse_fs_perm(fsperm, nvl2);
49871af68beaSAlexander Stetsenko 
49881af68beaSAlexander Stetsenko 		uu_list_insert(fspset->fsps_list, node, idx);
49891af68beaSAlexander Stetsenko 	}
49901af68beaSAlexander Stetsenko 
49911af68beaSAlexander Stetsenko 	return (0);
49921af68beaSAlexander Stetsenko }
49931af68beaSAlexander Stetsenko 
49941af68beaSAlexander Stetsenko static inline const char *
deleg_perm_comment(zfs_deleg_note_t note)49951af68beaSAlexander Stetsenko deleg_perm_comment(zfs_deleg_note_t note)
49961af68beaSAlexander Stetsenko {
49971af68beaSAlexander Stetsenko 	const char *str = "";
49981af68beaSAlexander Stetsenko 
49991af68beaSAlexander Stetsenko 	/* subcommands */
50001af68beaSAlexander Stetsenko 	switch (note) {
50011af68beaSAlexander Stetsenko 		/* SUBCOMMANDS */
50021af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_ALLOW:
50031af68beaSAlexander Stetsenko 		str = gettext("Must also have the permission that is being"
50041af68beaSAlexander Stetsenko 		    "\n\t\t\t\tallowed");
50051af68beaSAlexander Stetsenko 		break;
50061af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_CLONE:
50071af68beaSAlexander Stetsenko 		str = gettext("Must also have the 'create' ability and 'mount'"
50081af68beaSAlexander Stetsenko 		    "\n\t\t\t\tability in the origin file system");
50091af68beaSAlexander Stetsenko 		break;
50101af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_CREATE:
50111af68beaSAlexander Stetsenko 		str = gettext("Must also have the 'mount' ability");
50121af68beaSAlexander Stetsenko 		break;
50131af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_DESTROY:
50141af68beaSAlexander Stetsenko 		str = gettext("Must also have the 'mount' ability");
50151af68beaSAlexander Stetsenko 		break;
50161af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_DIFF:
50171af68beaSAlexander Stetsenko 		str = gettext("Allows lookup of paths within a dataset;"
50181af68beaSAlexander Stetsenko 		    "\n\t\t\t\tgiven an object number. Ordinary users need this"
50191af68beaSAlexander Stetsenko 		    "\n\t\t\t\tin order to use zfs diff");
50201af68beaSAlexander Stetsenko 		break;
50211af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_HOLD:
50221af68beaSAlexander Stetsenko 		str = gettext("Allows adding a user hold to a snapshot");
50231af68beaSAlexander Stetsenko 		break;
50241af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_MOUNT:
50251af68beaSAlexander Stetsenko 		str = gettext("Allows mount/umount of ZFS datasets");
50261af68beaSAlexander Stetsenko 		break;
50271af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_PROMOTE:
50281af68beaSAlexander Stetsenko 		str = gettext("Must also have the 'mount'\n\t\t\t\tand"
50291af68beaSAlexander Stetsenko 		    " 'promote' ability in the origin file system");
50301af68beaSAlexander Stetsenko 		break;
50311af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_RECEIVE:
50321af68beaSAlexander Stetsenko 		str = gettext("Must also have the 'mount' and 'create'"
50331af68beaSAlexander Stetsenko 		    " ability");
50341af68beaSAlexander Stetsenko 		break;
50351af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_RELEASE:
50361af68beaSAlexander Stetsenko 		str = gettext("Allows releasing a user hold which\n\t\t\t\t"
50371af68beaSAlexander Stetsenko 		    "might destroy the snapshot");
50381af68beaSAlexander Stetsenko 		break;
50391af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_RENAME:
50401af68beaSAlexander Stetsenko 		str = gettext("Must also have the 'mount' and 'create'"
50411af68beaSAlexander Stetsenko 		    "\n\t\t\t\tability in the new parent");
50421af68beaSAlexander Stetsenko 		break;
50431af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_ROLLBACK:
50441af68beaSAlexander Stetsenko 		str = gettext("");
50451af68beaSAlexander Stetsenko 		break;
50461af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_SEND:
50471af68beaSAlexander Stetsenko 		str = gettext("");
50481af68beaSAlexander Stetsenko 		break;
50491af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_SHARE:
50501af68beaSAlexander Stetsenko 		str = gettext("Allows sharing file systems over NFS or SMB"
50511af68beaSAlexander Stetsenko 		    "\n\t\t\t\tprotocols");
50521af68beaSAlexander Stetsenko 		break;
50531af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_SNAPSHOT:
50541af68beaSAlexander Stetsenko 		str = gettext("");
50551af68beaSAlexander Stetsenko 		break;
5056eb633035STom Caputi 	case ZFS_DELEG_NOTE_LOAD_KEY:
5057eb633035STom Caputi 		str = gettext("Allows loading or unloading an encryption key");
5058eb633035STom Caputi 		break;
5059eb633035STom Caputi 	case ZFS_DELEG_NOTE_CHANGE_KEY:
5060eb633035STom Caputi 		str = gettext("Allows changing or adding an encryption key");
5061eb633035STom Caputi 		break;
50621af68beaSAlexander Stetsenko /*
50631af68beaSAlexander Stetsenko  *	case ZFS_DELEG_NOTE_VSCAN:
50641af68beaSAlexander Stetsenko  *		str = gettext("");
50651af68beaSAlexander Stetsenko  *		break;
50661af68beaSAlexander Stetsenko  */
50671af68beaSAlexander Stetsenko 		/* OTHER */
50681af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_GROUPQUOTA:
50691af68beaSAlexander Stetsenko 		str = gettext("Allows accessing any groupquota@... property");
50701af68beaSAlexander Stetsenko 		break;
50711af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_GROUPUSED:
50721af68beaSAlexander Stetsenko 		str = gettext("Allows reading any groupused@... property");
50731af68beaSAlexander Stetsenko 		break;
50741af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_USERPROP:
50751af68beaSAlexander Stetsenko 		str = gettext("Allows changing any user property");
50761af68beaSAlexander Stetsenko 		break;
50771af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_USERQUOTA:
50781af68beaSAlexander Stetsenko 		str = gettext("Allows accessing any userquota@... property");
50791af68beaSAlexander Stetsenko 		break;
50801af68beaSAlexander Stetsenko 	case ZFS_DELEG_NOTE_USERUSED:
50811af68beaSAlexander Stetsenko 		str = gettext("Allows reading any userused@... property");
50821af68beaSAlexander Stetsenko 		break;
5083f67950b2SNasf-Fan 	case ZFS_DELEG_NOTE_USEROBJQUOTA:
5084f67950b2SNasf-Fan 		str = gettext("Allows accessing any userobjquota@... property");
5085f67950b2SNasf-Fan 		break;
5086f67950b2SNasf-Fan 	case ZFS_DELEG_NOTE_GROUPOBJQUOTA:
5087f67950b2SNasf-Fan 		str = gettext("Allows accessing any \n\t\t\t\t"
5088f67950b2SNasf-Fan 		    "groupobjquota@... property");
5089f67950b2SNasf-Fan 		break;
5090f67950b2SNasf-Fan 	case ZFS_DELEG_NOTE_GROUPOBJUSED:
5091f67950b2SNasf-Fan 		str = gettext("Allows reading any groupobjused@... property");
5092f67950b2SNasf-Fan 		break;
5093f67950b2SNasf-Fan 	case ZFS_DELEG_NOTE_USEROBJUSED:
5094f67950b2SNasf-Fan 		str = gettext("Allows reading any userobjused@... property");
5095f67950b2SNasf-Fan 		break;
5096f67950b2SNasf-Fan 	case ZFS_DELEG_NOTE_PROJECTQUOTA:
5097f67950b2SNasf-Fan 		str = gettext("Allows accessing any projectquota@... property");
5098f67950b2SNasf-Fan 		break;
5099f67950b2SNasf-Fan 	case ZFS_DELEG_NOTE_PROJECTOBJQUOTA:
5100f67950b2SNasf-Fan 		str = gettext("Allows accessing any \n\t\t\t\t"
5101f67950b2SNasf-Fan 		    "projectobjquota@... property");
5102f67950b2SNasf-Fan 		break;
5103f67950b2SNasf-Fan 	case ZFS_DELEG_NOTE_PROJECTUSED:
5104f67950b2SNasf-Fan 		str = gettext("Allows reading any projectused@... property");
5105f67950b2SNasf-Fan 		break;
5106f67950b2SNasf-Fan 	case ZFS_DELEG_NOTE_PROJECTOBJUSED:
5107f67950b2SNasf-Fan 		str = gettext("Allows accessing any \n\t\t\t\t"
5108f67950b2SNasf-Fan 		    "projectobjused@... property");
5109f67950b2SNasf-Fan 		break;
51101af68beaSAlexander Stetsenko 		/* other */
51111af68beaSAlexander Stetsenko 	default:
51121af68beaSAlexander Stetsenko 		str = "";
51131af68beaSAlexander Stetsenko 	}
51141af68beaSAlexander Stetsenko 
51151af68beaSAlexander Stetsenko 	return (str);
51161af68beaSAlexander Stetsenko }
51171af68beaSAlexander Stetsenko 
51181af68beaSAlexander Stetsenko struct allow_opts {
51191af68beaSAlexander Stetsenko 	boolean_t local;
51201af68beaSAlexander Stetsenko 	boolean_t descend;
51211af68beaSAlexander Stetsenko 	boolean_t user;
51221af68beaSAlexander Stetsenko 	boolean_t group;
51231af68beaSAlexander Stetsenko 	boolean_t everyone;
51241af68beaSAlexander Stetsenko 	boolean_t create;
51251af68beaSAlexander Stetsenko 	boolean_t set;
51261af68beaSAlexander Stetsenko 	boolean_t recursive; /* unallow only */
51271af68beaSAlexander Stetsenko 	boolean_t prt_usage;
51281af68beaSAlexander Stetsenko 
51291af68beaSAlexander Stetsenko 	boolean_t prt_perms;
51301af68beaSAlexander Stetsenko 	char *who;
51311af68beaSAlexander Stetsenko 	char *perms;
51321af68beaSAlexander Stetsenko 	const char *dataset;
51331af68beaSAlexander Stetsenko };
51341af68beaSAlexander Stetsenko 
51351af68beaSAlexander Stetsenko static inline int
prop_cmp(const void * a,const void * b)51361af68beaSAlexander Stetsenko prop_cmp(const void *a, const void *b)
51371af68beaSAlexander Stetsenko {
51381af68beaSAlexander Stetsenko 	const char *str1 = *(const char **)a;
51391af68beaSAlexander Stetsenko 	const char *str2 = *(const char **)b;
51401af68beaSAlexander Stetsenko 	return (strcmp(str1, str2));
51411af68beaSAlexander Stetsenko }
51421af68beaSAlexander Stetsenko 
51431af68beaSAlexander Stetsenko static void
allow_usage(boolean_t un,boolean_t requested,const char * msg)51441af68beaSAlexander Stetsenko allow_usage(boolean_t un, boolean_t requested, const char *msg)
51451af68beaSAlexander Stetsenko {
51461af68beaSAlexander Stetsenko 	const char *opt_desc[] = {
51471af68beaSAlexander Stetsenko 		"-h", gettext("show this help message and exit"),
51481af68beaSAlexander Stetsenko 		"-l", gettext("set permission locally"),
51491af68beaSAlexander Stetsenko 		"-d", gettext("set permission for descents"),
51501af68beaSAlexander Stetsenko 		"-u", gettext("set permission for user"),
51511af68beaSAlexander Stetsenko 		"-g", gettext("set permission for group"),
51521af68beaSAlexander Stetsenko 		"-e", gettext("set permission for everyone"),
51531af68beaSAlexander Stetsenko 		"-c", gettext("set create time permission"),
51541af68beaSAlexander Stetsenko 		"-s", gettext("define permission set"),
51551af68beaSAlexander Stetsenko 		/* unallow only */
51561af68beaSAlexander Stetsenko 		"-r", gettext("remove permissions recursively"),
51571af68beaSAlexander Stetsenko 	};
51581af68beaSAlexander Stetsenko 	size_t unallow_size = sizeof (opt_desc) / sizeof (char *);
51591af68beaSAlexander Stetsenko 	size_t allow_size = unallow_size - 2;
51601af68beaSAlexander Stetsenko 	const char *props[ZFS_NUM_PROPS];
51611af68beaSAlexander Stetsenko 	int i;
51621af68beaSAlexander Stetsenko 	size_t count = 0;
51631af68beaSAlexander Stetsenko 	FILE *fp = requested ? stdout : stderr;
51641af68beaSAlexander Stetsenko 	zprop_desc_t *pdtbl = zfs_prop_get_table();
51651af68beaSAlexander Stetsenko 	const char *fmt = gettext("%-16s %-14s\t%s\n");
51661af68beaSAlexander Stetsenko 
51671af68beaSAlexander Stetsenko 	(void) fprintf(fp, gettext("Usage: %s\n"), get_usage(un ? HELP_UNALLOW :
51681af68beaSAlexander Stetsenko 	    HELP_ALLOW));
51691af68beaSAlexander Stetsenko 	(void) fprintf(fp, gettext("Options:\n"));
51701af68beaSAlexander Stetsenko 	for (int i = 0; i < (un ? unallow_size : allow_size); i++) {
51711af68beaSAlexander Stetsenko 		const char *opt = opt_desc[i++];
51721af68beaSAlexander Stetsenko 		const char *optdsc = opt_desc[i];
51731af68beaSAlexander Stetsenko 		(void) fprintf(fp, gettext("  %-10s  %s\n"), opt, optdsc);
51741af68beaSAlexander Stetsenko 	}
51751af68beaSAlexander Stetsenko 
51761af68beaSAlexander Stetsenko 	(void) fprintf(fp, gettext("\nThe following permissions are "
51771af68beaSAlexander Stetsenko 	    "supported:\n\n"));
51781af68beaSAlexander Stetsenko 	(void) fprintf(fp, fmt, gettext("NAME"), gettext("TYPE"),
51791af68beaSAlexander Stetsenko 	    gettext("NOTES"));
51801af68beaSAlexander Stetsenko 	for (i = 0; i < ZFS_NUM_DELEG_NOTES; i++) {
51811af68beaSAlexander Stetsenko 		const char *perm_name = zfs_deleg_perm_tbl[i].z_perm;
51821af68beaSAlexander Stetsenko 		zfs_deleg_note_t perm_note = zfs_deleg_perm_tbl[i].z_note;
51831af68beaSAlexander Stetsenko 		const char *perm_type = deleg_perm_type(perm_note);
51841af68beaSAlexander Stetsenko 		const char *perm_comment = deleg_perm_comment(perm_note);
51851af68beaSAlexander Stetsenko 		(void) fprintf(fp, fmt, perm_name, perm_type, perm_comment);
51861af68beaSAlexander Stetsenko 	}
51871af68beaSAlexander Stetsenko 
51881af68beaSAlexander Stetsenko 	for (i = 0; i < ZFS_NUM_PROPS; i++) {
51891af68beaSAlexander Stetsenko 		zprop_desc_t *pd = &pdtbl[i];
51901af68beaSAlexander Stetsenko 		if (pd->pd_visible != B_TRUE)
51911af68beaSAlexander Stetsenko 			continue;
51921af68beaSAlexander Stetsenko 
51931af68beaSAlexander Stetsenko 		if (pd->pd_attr == PROP_READONLY)
51941af68beaSAlexander Stetsenko 			continue;
51951af68beaSAlexander Stetsenko 
51961af68beaSAlexander Stetsenko 		props[count++] = pd->pd_name;
51971af68beaSAlexander Stetsenko 	}
51981af68beaSAlexander Stetsenko 	props[count] = NULL;
51991af68beaSAlexander Stetsenko 
52001af68beaSAlexander Stetsenko 	qsort(props, count, sizeof (char *), prop_cmp);
52011af68beaSAlexander Stetsenko 
52021af68beaSAlexander Stetsenko 	for (i = 0; i < count; i++)
52031af68beaSAlexander Stetsenko 		(void) fprintf(fp, fmt, props[i], gettext("property"), "");
52041af68beaSAlexander Stetsenko 
52051af68beaSAlexander Stetsenko 	if (msg != NULL)
52061af68beaSAlexander Stetsenko 		(void) fprintf(fp, gettext("\nzfs: error: %s"), msg);
52071af68beaSAlexander Stetsenko 
52081af68beaSAlexander Stetsenko 	exit(requested ? 0 : 2);
52091af68beaSAlexander Stetsenko }
52101af68beaSAlexander Stetsenko 
52111af68beaSAlexander Stetsenko static inline const char *
munge_args(int argc,char ** argv,boolean_t un,size_t expected_argc,char ** permsp)52121af68beaSAlexander Stetsenko munge_args(int argc, char **argv, boolean_t un, size_t expected_argc,
52131af68beaSAlexander Stetsenko     char **permsp)
52141af68beaSAlexander Stetsenko {
52151af68beaSAlexander Stetsenko 	if (un && argc == expected_argc - 1)
52161af68beaSAlexander Stetsenko 		*permsp = NULL;
52171af68beaSAlexander Stetsenko 	else if (argc == expected_argc)
52181af68beaSAlexander Stetsenko 		*permsp = argv[argc - 2];
52191af68beaSAlexander Stetsenko 	else
52201af68beaSAlexander Stetsenko 		allow_usage(un, B_FALSE,
52211af68beaSAlexander Stetsenko 		    gettext("wrong number of parameters\n"));
52221af68beaSAlexander Stetsenko 
52231af68beaSAlexander Stetsenko 	return (argv[argc - 1]);
52241af68beaSAlexander Stetsenko }
52251af68beaSAlexander Stetsenko 
52261af68beaSAlexander Stetsenko static void
parse_allow_args(int argc,char ** argv,boolean_t un,struct allow_opts * opts)52271af68beaSAlexander Stetsenko parse_allow_args(int argc, char **argv, boolean_t un, struct allow_opts *opts)
52281af68beaSAlexander Stetsenko {
52291af68beaSAlexander Stetsenko 	int uge_sum = opts->user + opts->group + opts->everyone;
52301af68beaSAlexander Stetsenko 	int csuge_sum = opts->create + opts->set + uge_sum;
52311af68beaSAlexander Stetsenko 	int ldcsuge_sum = csuge_sum + opts->local + opts->descend;
52321af68beaSAlexander Stetsenko 	int all_sum = un ? ldcsuge_sum + opts->recursive : ldcsuge_sum;
52331af68beaSAlexander Stetsenko 
52341af68beaSAlexander Stetsenko 	if (uge_sum > 1)
52351af68beaSAlexander Stetsenko 		allow_usage(un, B_FALSE,
52361af68beaSAlexander Stetsenko 		    gettext("-u, -g, and -e are mutually exclusive\n"));
52371af68beaSAlexander Stetsenko 
5238c16bcc45SIgor Kozhukhov 	if (opts->prt_usage) {
52391af68beaSAlexander Stetsenko 		if (argc == 0 && all_sum == 0)
52401af68beaSAlexander Stetsenko 			allow_usage(un, B_TRUE, NULL);
52411af68beaSAlexander Stetsenko 		else
52421af68beaSAlexander Stetsenko 			usage(B_FALSE);
5243c16bcc45SIgor Kozhukhov 	}
52441af68beaSAlexander Stetsenko 
52451af68beaSAlexander Stetsenko 	if (opts->set) {
52461af68beaSAlexander Stetsenko 		if (csuge_sum > 1)
52471af68beaSAlexander Stetsenko 			allow_usage(un, B_FALSE,
52481af68beaSAlexander Stetsenko 			    gettext("invalid options combined with -s\n"));
52491af68beaSAlexander Stetsenko 
52501af68beaSAlexander Stetsenko 		opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);
52511af68beaSAlexander Stetsenko 		if (argv[0][0] != '@')
52521af68beaSAlexander Stetsenko 			allow_usage(un, B_FALSE,
52531af68beaSAlexander Stetsenko 			    gettext("invalid set name: missing '@' prefix\n"));
52541af68beaSAlexander Stetsenko 		opts->who = argv[0];
52551af68beaSAlexander Stetsenko 	} else if (opts->create) {
52561af68beaSAlexander Stetsenko 		if (ldcsuge_sum > 1)
52571af68beaSAlexander Stetsenko 			allow_usage(un, B_FALSE,
52581af68beaSAlexander Stetsenko 			    gettext("invalid options combined with -c\n"));
52591af68beaSAlexander Stetsenko 		opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
52601af68beaSAlexander Stetsenko 	} else if (opts->everyone) {
52611af68beaSAlexander Stetsenko 		if (csuge_sum > 1)
52621af68beaSAlexander Stetsenko 			allow_usage(un, B_FALSE,
52631af68beaSAlexander Stetsenko 			    gettext("invalid options combined with -e\n"));
52641af68beaSAlexander Stetsenko 		opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
52651af68beaSAlexander Stetsenko 	} else if (uge_sum == 0 && argc > 0 && strcmp(argv[0], "everyone")
52661af68beaSAlexander Stetsenko 	    == 0) {
52671af68beaSAlexander Stetsenko 		opts->everyone = B_TRUE;
52681af68beaSAlexander Stetsenko 		argc--;
52691af68beaSAlexander Stetsenko 		argv++;
52701af68beaSAlexander Stetsenko 		opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
5271c560ed2bSAlexander Eremin 	} else if (argc == 1 && !un) {
52721af68beaSAlexander Stetsenko 		opts->prt_perms = B_TRUE;
52731af68beaSAlexander Stetsenko 		opts->dataset = argv[argc-1];
52741af68beaSAlexander Stetsenko 	} else {
52751af68beaSAlexander Stetsenko 		opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);
52761af68beaSAlexander Stetsenko 		opts->who = argv[0];
52771af68beaSAlexander Stetsenko 	}
52781af68beaSAlexander Stetsenko 
52791af68beaSAlexander Stetsenko 	if (!opts->local && !opts->descend) {
52801af68beaSAlexander Stetsenko 		opts->local = B_TRUE;
52811af68beaSAlexander Stetsenko 		opts->descend = B_TRUE;
52821af68beaSAlexander Stetsenko 	}
52831af68beaSAlexander Stetsenko }
52841af68beaSAlexander Stetsenko 
52851af68beaSAlexander Stetsenko static void
store_allow_perm(zfs_deleg_who_type_t type,boolean_t local,boolean_t descend,const char * who,char * perms,nvlist_t * top_nvl)52861af68beaSAlexander Stetsenko store_allow_perm(zfs_deleg_who_type_t type, boolean_t local, boolean_t descend,
52871af68beaSAlexander Stetsenko     const char *who, char *perms, nvlist_t *top_nvl)
52881af68beaSAlexander Stetsenko {
52891af68beaSAlexander Stetsenko 	int i;
52901af68beaSAlexander Stetsenko 	char ld[2] = { '\0', '\0' };
52919adfa60dSMatthew Ahrens 	char who_buf[MAXNAMELEN + 32];
5292c16bcc45SIgor Kozhukhov 	char base_type = '\0';
5293c16bcc45SIgor Kozhukhov 	char set_type = '\0';
52941af68beaSAlexander Stetsenko 	nvlist_t *base_nvl = NULL;
52951af68beaSAlexander Stetsenko 	nvlist_t *set_nvl = NULL;
52961af68beaSAlexander Stetsenko 	nvlist_t *nvl;
52971af68beaSAlexander Stetsenko 
52981af68beaSAlexander Stetsenko 	if (nvlist_alloc(&base_nvl, NV_UNIQUE_NAME, 0) != 0)
52991af68beaSAlexander Stetsenko 		nomem();
53001af68beaSAlexander Stetsenko 	if (nvlist_alloc(&set_nvl, NV_UNIQUE_NAME, 0) !=  0)
53011af68beaSAlexander Stetsenko 		nomem();
53021af68beaSAlexander Stetsenko 
53031af68beaSAlexander Stetsenko 	switch (type) {
53041af68beaSAlexander Stetsenko 	case ZFS_DELEG_NAMED_SET_SETS:
53051af68beaSAlexander Stetsenko 	case ZFS_DELEG_NAMED_SET:
53061af68beaSAlexander Stetsenko 		set_type = ZFS_DELEG_NAMED_SET_SETS;
53071af68beaSAlexander Stetsenko 		base_type = ZFS_DELEG_NAMED_SET;
53081af68beaSAlexander Stetsenko 		ld[0] = ZFS_DELEG_NA;
53091af68beaSAlexander Stetsenko 		break;
53101af68beaSAlexander Stetsenko 	case ZFS_DELEG_CREATE_SETS:
53111af68beaSAlexander Stetsenko 	case ZFS_DELEG_CREATE:
53121af68beaSAlexander Stetsenko 		set_type = ZFS_DELEG_CREATE_SETS;
53131af68beaSAlexander Stetsenko 		base_type = ZFS_DELEG_CREATE;
53141af68beaSAlexander Stetsenko 		ld[0] = ZFS_DELEG_NA;
53151af68beaSAlexander Stetsenko 		break;
53161af68beaSAlexander Stetsenko 	case ZFS_DELEG_USER_SETS:
53171af68beaSAlexander Stetsenko 	case ZFS_DELEG_USER:
53181af68beaSAlexander Stetsenko 		set_type = ZFS_DELEG_USER_SETS;
53191af68beaSAlexander Stetsenko 		base_type = ZFS_DELEG_USER;
53201af68beaSAlexander Stetsenko 		if (local)
53211af68beaSAlexander Stetsenko 			ld[0] = ZFS_DELEG_LOCAL;
53221af68beaSAlexander Stetsenko 		if (descend)
53231af68beaSAlexander Stetsenko 			ld[1] = ZFS_DELEG_DESCENDENT;
53241af68beaSAlexander Stetsenko 		break;
53251af68beaSAlexander Stetsenko 	case ZFS_DELEG_GROUP_SETS:
53261af68beaSAlexander Stetsenko 	case ZFS_DELEG_GROUP:
53271af68beaSAlexander Stetsenko 		set_type = ZFS_DELEG_GROUP_SETS;
53281af68beaSAlexander Stetsenko 		base_type = ZFS_DELEG_GROUP;
53291af68beaSAlexander Stetsenko 		if (local)
53301af68beaSAlexander Stetsenko 			ld[0] = ZFS_DELEG_LOCAL;
53311af68beaSAlexander Stetsenko 		if (descend)
53321af68beaSAlexander Stetsenko 			ld[1] = ZFS_DELEG_DESCENDENT;
53331af68beaSAlexander Stetsenko 		break;
53341af68beaSAlexander Stetsenko 	case ZFS_DELEG_EVERYONE_SETS:
53351af68beaSAlexander Stetsenko 	case ZFS_DELEG_EVERYONE:
53361af68beaSAlexander Stetsenko 		set_type = ZFS_DELEG_EVERYONE_SETS;
53371af68beaSAlexander Stetsenko 		base_type = ZFS_DELEG_EVERYONE;
53381af68beaSAlexander Stetsenko 		if (local)
53391af68beaSAlexander Stetsenko 			ld[0] = ZFS_DELEG_LOCAL;
53401af68beaSAlexander Stetsenko 		if (descend)
53411af68beaSAlexander Stetsenko 			ld[1] = ZFS_DELEG_DESCENDENT;
5342c16bcc45SIgor Kozhukhov 		break;
5343c16bcc45SIgor Kozhukhov 
5344c16bcc45SIgor Kozhukhov 	default:
5345c16bcc45SIgor Kozhukhov 		assert(set_type != '\0' && base_type != '\0');
53461af68beaSAlexander Stetsenko 	}
53471af68beaSAlexander Stetsenko 
53481af68beaSAlexander Stetsenko 	if (perms != NULL) {
53491af68beaSAlexander Stetsenko 		char *curr = perms;
53501af68beaSAlexander Stetsenko 		char *end = curr + strlen(perms);
53511af68beaSAlexander Stetsenko 
53521af68beaSAlexander Stetsenko 		while (curr < end) {
53531af68beaSAlexander Stetsenko 			char *delim = strchr(curr, ',');
53541af68beaSAlexander Stetsenko 			if (delim == NULL)
53551af68beaSAlexander Stetsenko 				delim = end;
53561af68beaSAlexander Stetsenko 			else
53571af68beaSAlexander Stetsenko 				*delim = '\0';
53581af68beaSAlexander Stetsenko 
53591af68beaSAlexander Stetsenko 			if (curr[0] == '@')
53601af68beaSAlexander Stetsenko 				nvl = set_nvl;
53611af68beaSAlexander Stetsenko 			else
53621af68beaSAlexander Stetsenko 				nvl = base_nvl;
53631af68beaSAlexander Stetsenko 
53641af68beaSAlexander Stetsenko 			(void) nvlist_add_boolean(nvl, curr);
53651af68beaSAlexander Stetsenko 			if (delim != end)
53661af68beaSAlexander Stetsenko 				*delim = ',';
53671af68beaSAlexander Stetsenko 			curr = delim + 1;
53681af68beaSAlexander Stetsenko 		}
53691af68beaSAlexander Stetsenko 
53701af68beaSAlexander Stetsenko 		for (i = 0; i < 2; i++) {
53711af68beaSAlexander Stetsenko 			char locality = ld[i];
53721af68beaSAlexander Stetsenko 			if (locality == 0)
53731af68beaSAlexander Stetsenko 				continue;
53741af68beaSAlexander Stetsenko 
53751af68beaSAlexander Stetsenko 			if (!nvlist_empty(base_nvl)) {
53761af68beaSAlexander Stetsenko 				if (who != NULL)
53771af68beaSAlexander Stetsenko 					(void) snprintf(who_buf,
53781af68beaSAlexander Stetsenko 					    sizeof (who_buf), "%c%c$%s",
53791af68beaSAlexander Stetsenko 					    base_type, locality, who);
53801af68beaSAlexander Stetsenko 				else
53811af68beaSAlexander Stetsenko 					(void) snprintf(who_buf,
53821af68beaSAlexander Stetsenko 					    sizeof (who_buf), "%c%c$",
53831af68beaSAlexander Stetsenko 					    base_type, locality);
53841af68beaSAlexander Stetsenko 
53851af68beaSAlexander Stetsenko 				(void) nvlist_add_nvlist(top_nvl, who_buf,
53861af68beaSAlexander Stetsenko 				    base_nvl);
53871af68beaSAlexander Stetsenko 			}
53881af68beaSAlexander Stetsenko 
53891af68beaSAlexander Stetsenko 
53901af68beaSAlexander Stetsenko 			if (!nvlist_empty(set_nvl)) {
53911af68beaSAlexander Stetsenko 				if (who != NULL)
53921af68beaSAlexander Stetsenko 					(void) snprintf(who_buf,
53931af68beaSAlexander Stetsenko 					    sizeof (who_buf), "%c%c$%s",
53941af68beaSAlexander Stetsenko 					    set_type, locality, who);
53951af68beaSAlexander Stetsenko 				else
53961af68beaSAlexander Stetsenko 					(void) snprintf(who_buf,
53971af68beaSAlexander Stetsenko 					    sizeof (who_buf), "%c%c$",
53981af68beaSAlexander Stetsenko 					    set_type, locality);
53991af68beaSAlexander Stetsenko 
54001af68beaSAlexander Stetsenko 				(void) nvlist_add_nvlist(top_nvl, who_buf,
54011af68beaSAlexander Stetsenko 				    set_nvl);
54021af68beaSAlexander Stetsenko 			}
54031af68beaSAlexander Stetsenko 		}
54041af68beaSAlexander Stetsenko 	} else {
54051af68beaSAlexander Stetsenko 		for (i = 0; i < 2; i++) {
54061af68beaSAlexander Stetsenko 			char locality = ld[i];
54071af68beaSAlexander Stetsenko 			if (locality == 0)
54081af68beaSAlexander Stetsenko 				continue;
54091af68beaSAlexander Stetsenko 
54101af68beaSAlexander Stetsenko 			if (who != NULL)
54111af68beaSAlexander Stetsenko 				(void) snprintf(who_buf, sizeof (who_buf),
54121af68beaSAlexander Stetsenko 				    "%c%c$%s", base_type, locality, who);
54131af68beaSAlexander Stetsenko 			else
54141af68beaSAlexander Stetsenko 				(void) snprintf(who_buf, sizeof (who_buf),
54151af68beaSAlexander Stetsenko 				    "%c%c$", base_type, locality);
54161af68beaSAlexander Stetsenko 			(void) nvlist_add_boolean(top_nvl, who_buf);
54171af68beaSAlexander Stetsenko 
54181af68beaSAlexander Stetsenko 			if (who != NULL)
54191af68beaSAlexander Stetsenko 				(void) snprintf(who_buf, sizeof (who_buf),
54201af68beaSAlexander Stetsenko 				    "%c%c$%s", set_type, locality, who);
54211af68beaSAlexander Stetsenko 			else
54221af68beaSAlexander Stetsenko 				(void) snprintf(who_buf, sizeof (who_buf),
54231af68beaSAlexander Stetsenko 				    "%c%c$", set_type, locality);
54241af68beaSAlexander Stetsenko 			(void) nvlist_add_boolean(top_nvl, who_buf);
54251af68beaSAlexander Stetsenko 		}
54261af68beaSAlexander Stetsenko 	}
54271af68beaSAlexander Stetsenko }
54281af68beaSAlexander Stetsenko 
54291af68beaSAlexander Stetsenko static int
construct_fsacl_list(boolean_t un,struct allow_opts * opts,nvlist_t ** nvlp)54301af68beaSAlexander Stetsenko construct_fsacl_list(boolean_t un, struct allow_opts *opts, nvlist_t **nvlp)
54311af68beaSAlexander Stetsenko {
54321af68beaSAlexander Stetsenko 	if (nvlist_alloc(nvlp, NV_UNIQUE_NAME, 0) != 0)
54331af68beaSAlexander Stetsenko 		nomem();
54341af68beaSAlexander Stetsenko 
54351af68beaSAlexander Stetsenko 	if (opts->set) {
54361af68beaSAlexander Stetsenko 		store_allow_perm(ZFS_DELEG_NAMED_SET, opts->local,
54371af68beaSAlexander Stetsenko 		    opts->descend, opts->who, opts->perms, *nvlp);
54381af68beaSAlexander Stetsenko 	} else if (opts->create) {
54391af68beaSAlexander Stetsenko 		store_allow_perm(ZFS_DELEG_CREATE, opts->local,
54401af68beaSAlexander Stetsenko 		    opts->descend, NULL, opts->perms, *nvlp);
54411af68beaSAlexander Stetsenko 	} else if (opts->everyone) {
54421af68beaSAlexander Stetsenko 		store_allow_perm(ZFS_DELEG_EVERYONE, opts->local,
54431af68beaSAlexander Stetsenko 		    opts->descend, NULL, opts->perms, *nvlp);
54441af68beaSAlexander Stetsenko 	} else {
54451af68beaSAlexander Stetsenko 		char *curr = opts->who;
54461af68beaSAlexander Stetsenko 		char *end = curr + strlen(curr);
54471af68beaSAlexander Stetsenko 
54481af68beaSAlexander Stetsenko 		while (curr < end) {
54491af68beaSAlexander Stetsenko 			const char *who;
5450c16bcc45SIgor Kozhukhov 			zfs_deleg_who_type_t who_type = ZFS_DELEG_WHO_UNKNOWN;
54511af68beaSAlexander Stetsenko 			char *endch;
54521af68beaSAlexander Stetsenko 			char *delim = strchr(curr, ',');
54531af68beaSAlexander Stetsenko 			char errbuf[256];
54541af68beaSAlexander Stetsenko 			char id[64];
54551af68beaSAlexander Stetsenko 			struct passwd *p = NULL;
54561af68beaSAlexander Stetsenko 			struct group *g = NULL;
54571af68beaSAlexander Stetsenko 
54581af68beaSAlexander Stetsenko 			uid_t rid;
54591af68beaSAlexander Stetsenko 			if (delim == NULL)
54601af68beaSAlexander Stetsenko 				delim = end;
54611af68beaSAlexander Stetsenko 			else
54621af68beaSAlexander Stetsenko 				*delim = '\0';
54631af68beaSAlexander Stetsenko 
54641af68beaSAlexander Stetsenko 			rid = (uid_t)strtol(curr, &endch, 0);
54651af68beaSAlexander Stetsenko 			if (opts->user) {
54661af68beaSAlexander Stetsenko 				who_type = ZFS_DELEG_USER;
54671af68beaSAlexander Stetsenko 				if (*endch != '\0')
54681af68beaSAlexander Stetsenko 					p = getpwnam(curr);
54691af68beaSAlexander Stetsenko 				else
54701af68beaSAlexander Stetsenko 					p = getpwuid(rid);
54711af68beaSAlexander Stetsenko 
54721af68beaSAlexander Stetsenko 				if (p != NULL)
54731af68beaSAlexander Stetsenko 					rid = p->pw_uid;
54741af68beaSAlexander Stetsenko 				else {
54751af68beaSAlexander Stetsenko 					(void) snprintf(errbuf, 256, gettext(
54761af68beaSAlexander Stetsenko 					    "invalid user %s"), curr);
54771af68beaSAlexander Stetsenko 					allow_usage(un, B_TRUE, errbuf);
54781af68beaSAlexander Stetsenko 				}
54791af68beaSAlexander Stetsenko 			} else if (opts->group) {
54801af68beaSAlexander Stetsenko 				who_type = ZFS_DELEG_GROUP;
54811af68beaSAlexander Stetsenko 				if (*endch != '\0')
54821af68beaSAlexander Stetsenko 					g = getgrnam(curr);
54831af68beaSAlexander Stetsenko 				else
54841af68beaSAlexander Stetsenko 					g = getgrgid(rid);
54851af68beaSAlexander Stetsenko 
54861af68beaSAlexander Stetsenko 				if (g != NULL)
54871af68beaSAlexander Stetsenko 					rid = g->gr_gid;
54881af68beaSAlexander Stetsenko 				else {
54891af68beaSAlexander Stetsenko 					(void) snprintf(errbuf, 256, gettext(
54901af68beaSAlexander Stetsenko 					    "invalid group %s"),  curr);
54911af68beaSAlexander Stetsenko 					allow_usage(un, B_TRUE, errbuf);
54921af68beaSAlexander Stetsenko 				}
54931af68beaSAlexander Stetsenko 			} else {
54941af68beaSAlexander Stetsenko 				if (*endch != '\0') {
54951af68beaSAlexander Stetsenko 					p = getpwnam(curr);
54961af68beaSAlexander Stetsenko 				} else {
54971af68beaSAlexander Stetsenko 					p = getpwuid(rid);
54981af68beaSAlexander Stetsenko 				}
54991af68beaSAlexander Stetsenko 
5500c16bcc45SIgor Kozhukhov 				if (p == NULL) {
55011af68beaSAlexander Stetsenko 					if (*endch != '\0') {
55021af68beaSAlexander Stetsenko 						g = getgrnam(curr);
55031af68beaSAlexander Stetsenko 					} else {
55041af68beaSAlexander Stetsenko 						g = getgrgid(rid);
55051af68beaSAlexander Stetsenko 					}
5506c16bcc45SIgor Kozhukhov 				}
55071af68beaSAlexander Stetsenko 
55081af68beaSAlexander Stetsenko 				if (p != NULL) {
55091af68beaSAlexander Stetsenko 					who_type = ZFS_DELEG_USER;
55101af68beaSAlexander Stetsenko 					rid = p->pw_uid;
55111af68beaSAlexander Stetsenko 				} else if (g != NULL) {
55121af68beaSAlexander Stetsenko 					who_type = ZFS_DELEG_GROUP;
55131af68beaSAlexander Stetsenko 					rid = g->gr_gid;
55141af68beaSAlexander Stetsenko 				} else {
55151af68beaSAlexander Stetsenko 					(void) snprintf(errbuf, 256, gettext(
55161af68beaSAlexander Stetsenko 					    "invalid user/group %s"), curr);
55171af68beaSAlexander Stetsenko 					allow_usage(un, B_TRUE, errbuf);
55181af68beaSAlexander Stetsenko 				}
55191af68beaSAlexander Stetsenko 			}
55201af68beaSAlexander Stetsenko 
55211af68beaSAlexander Stetsenko 			(void) sprintf(id, "%u", rid);
55221af68beaSAlexander Stetsenko 			who = id;
55231af68beaSAlexander Stetsenko 
55241af68beaSAlexander Stetsenko 			store_allow_perm(who_type, opts->local,
55251af68beaSAlexander Stetsenko 			    opts->descend, who, opts->perms, *nvlp);
55261af68beaSAlexander Stetsenko 			curr = delim + 1;
55271af68beaSAlexander Stetsenko 		}
55281af68beaSAlexander Stetsenko 	}
55291af68beaSAlexander Stetsenko 
55301af68beaSAlexander Stetsenko 	return (0);
55311af68beaSAlexander Stetsenko }
55321af68beaSAlexander Stetsenko 
55331af68beaSAlexander Stetsenko static void
print_set_creat_perms(uu_avl_t * who_avl)55341af68beaSAlexander Stetsenko print_set_creat_perms(uu_avl_t *who_avl)
55351af68beaSAlexander Stetsenko {
55361af68beaSAlexander Stetsenko 	const char *sc_title[] = {
55371af68beaSAlexander Stetsenko 		gettext("Permission sets:\n"),
55381af68beaSAlexander Stetsenko 		gettext("Create time permissions:\n"),
55391af68beaSAlexander Stetsenko 		NULL
55401af68beaSAlexander Stetsenko 	};
55411af68beaSAlexander Stetsenko 	who_perm_node_t *who_node = NULL;
55421af68beaSAlexander Stetsenko 	int prev_weight = -1;
55431af68beaSAlexander Stetsenko 
55441af68beaSAlexander Stetsenko 	for (who_node = uu_avl_first(who_avl); who_node != NULL;
55451af68beaSAlexander Stetsenko 	    who_node = uu_avl_next(who_avl, who_node)) {
55461af68beaSAlexander Stetsenko 		uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl;
55471af68beaSAlexander Stetsenko 		zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;
55481af68beaSAlexander Stetsenko 		const char *who_name = who_node->who_perm.who_name;
55491af68beaSAlexander Stetsenko 		int weight = who_type2weight(who_type);
55501af68beaSAlexander Stetsenko 		boolean_t first = B_TRUE;
55511af68beaSAlexander Stetsenko 		deleg_perm_node_t *deleg_node;
55521af68beaSAlexander Stetsenko 
55531af68beaSAlexander Stetsenko 		if (prev_weight != weight) {
555451463258SBill Sommerfeld 			VERIFY3S(weight, >=, 0);
555551463258SBill Sommerfeld 			VERIFY3S(weight, <=, 1);
555651463258SBill Sommerfeld 			(void) printf(sc_title[weight]);
55571af68beaSAlexander Stetsenko 			prev_weight = weight;
55581af68beaSAlexander Stetsenko 		}
55591af68beaSAlexander Stetsenko 
55601af68beaSAlexander Stetsenko 		if (who_name == NULL || strnlen(who_name, 1) == 0)
55611af68beaSAlexander Stetsenko 			(void) printf("\t");
55621af68beaSAlexander Stetsenko 		else
55631af68beaSAlexander Stetsenko 			(void) printf("\t%s ", who_name);
55641af68beaSAlexander Stetsenko 
55651af68beaSAlexander Stetsenko 		for (deleg_node = uu_avl_first(avl); deleg_node != NULL;
55661af68beaSAlexander Stetsenko 		    deleg_node = uu_avl_next(avl, deleg_node)) {
55671af68beaSAlexander Stetsenko 			if (first) {
55681af68beaSAlexander Stetsenko 				(void) printf("%s",
55691af68beaSAlexander Stetsenko 				    deleg_node->dpn_perm.dp_name);
55701af68beaSAlexander Stetsenko 				first = B_FALSE;
55711af68beaSAlexander Stetsenko 			} else
55721af68beaSAlexander Stetsenko 				(void) printf(",%s",
55731af68beaSAlexander Stetsenko 				    deleg_node->dpn_perm.dp_name);
55741af68beaSAlexander Stetsenko 		}
55751af68beaSAlexander Stetsenko 
55761af68beaSAlexander Stetsenko 		(void) printf("\n");
55771af68beaSAlexander Stetsenko 	}
55781af68beaSAlexander Stetsenko }
55791af68beaSAlexander Stetsenko 
5580c16bcc45SIgor Kozhukhov static void
print_uge_deleg_perms(uu_avl_t * who_avl,boolean_t local,boolean_t descend,const char * title)55811af68beaSAlexander Stetsenko print_uge_deleg_perms(uu_avl_t *who_avl, boolean_t local, boolean_t descend,
55821af68beaSAlexander Stetsenko     const char *title)
55831af68beaSAlexander Stetsenko {
55841af68beaSAlexander Stetsenko 	who_perm_node_t *who_node = NULL;
55851af68beaSAlexander Stetsenko 	boolean_t prt_title = B_TRUE;
55861af68beaSAlexander Stetsenko 	uu_avl_walk_t *walk;
55871af68beaSAlexander Stetsenko 
55881af68beaSAlexander Stetsenko 	if ((walk = uu_avl_walk_start(who_avl, UU_WALK_ROBUST)) == NULL)
55891af68beaSAlexander Stetsenko 		nomem();
55901af68beaSAlexander Stetsenko 
55911af68beaSAlexander Stetsenko 	while ((who_node = uu_avl_walk_next(walk)) != NULL) {
55921af68beaSAlexander Stetsenko 		const char *who_name = who_node->who_perm.who_name;
55931af68beaSAlexander Stetsenko 		const char *nice_who_name = who_node->who_perm.who_ug_name;
55941af68beaSAlexander Stetsenko 		uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl;
55951af68beaSAlexander Stetsenko 		zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;
55961af68beaSAlexander Stetsenko 		char delim = ' ';
55971af68beaSAlexander Stetsenko 		deleg_perm_node_t *deleg_node;
55981af68beaSAlexander Stetsenko 		boolean_t prt_who = B_TRUE;
55991af68beaSAlexander Stetsenko 
56001af68beaSAlexander Stetsenko 		for (deleg_node = uu_avl_first(avl);
56011af68beaSAlexander Stetsenko 		    deleg_node != NULL;
56021af68beaSAlexander Stetsenko 		    deleg_node = uu_avl_next(avl, deleg_node)) {
56031af68beaSAlexander Stetsenko 			if (local != deleg_node->dpn_perm.dp_local ||
56041af68beaSAlexander Stetsenko 			    descend != deleg_node->dpn_perm.dp_descend)
56051af68beaSAlexander Stetsenko 				continue;
56061af68beaSAlexander Stetsenko 
56071af68beaSAlexander Stetsenko 			if (prt_who) {
56081af68beaSAlexander Stetsenko 				const char *who = NULL;
56091af68beaSAlexander Stetsenko 				if (prt_title) {
56101af68beaSAlexander Stetsenko 					prt_title = B_FALSE;
56111af68beaSAlexander Stetsenko 					(void) printf(title);
56121af68beaSAlexander Stetsenko 				}
56131af68beaSAlexander Stetsenko 
56141af68beaSAlexander Stetsenko 				switch (who_type) {
56151af68beaSAlexander Stetsenko 				case ZFS_DELEG_USER_SETS:
56161af68beaSAlexander Stetsenko 				case ZFS_DELEG_USER:
56171af68beaSAlexander Stetsenko 					who = gettext("user");
56181af68beaSAlexander Stetsenko 					if (nice_who_name)
56191af68beaSAlexander Stetsenko 						who_name  = nice_who_name;
56201af68beaSAlexander Stetsenko 					break;
56211af68beaSAlexander Stetsenko 				case ZFS_DELEG_GROUP_SETS:
56221af68beaSAlexander Stetsenko 				case ZFS_DELEG_GROUP:
56231af68beaSAlexander Stetsenko 					who = gettext("group");
56241af68beaSAlexander Stetsenko 					if (nice_who_name)
56251af68beaSAlexander Stetsenko 						who_name  = nice_who_name;
56261af68beaSAlexander Stetsenko 					break;
56271af68beaSAlexander Stetsenko 				case ZFS_DELEG_EVERYONE_SETS:
56281af68beaSAlexander Stetsenko 				case ZFS_DELEG_EVERYONE:
56291af68beaSAlexander Stetsenko 					who = gettext("everyone");
56301af68beaSAlexander Stetsenko 					who_name = NULL;
5631c16bcc45SIgor Kozhukhov 					break;
5632c16bcc45SIgor Kozhukhov 
5633c16bcc45SIgor Kozhukhov 				default:
5634c16bcc45SIgor Kozhukhov 					assert(who != NULL);
56351af68beaSAlexander Stetsenko 				}
56361af68beaSAlexander Stetsenko 
56371af68beaSAlexander Stetsenko 				prt_who = B_FALSE;
56381af68beaSAlexander Stetsenko 				if (who_name == NULL)
56391af68beaSAlexander Stetsenko 					(void) printf("\t%s", who);
56401af68beaSAlexander Stetsenko 				else
56411af68beaSAlexander Stetsenko 					(void) printf("\t%s %s", who, who_name);
56421af68beaSAlexander Stetsenko 			}
56431af68beaSAlexander Stetsenko 
56441af68beaSAlexander Stetsenko 			(void) printf("%c%s", delim,
56451af68beaSAlexander Stetsenko 			    deleg_node->dpn_perm.dp_name);
56461af68beaSAlexander Stetsenko 			delim = ',';
56471af68beaSAlexander Stetsenko 		}
56481af68beaSAlexander Stetsenko 
56491af68beaSAlexander Stetsenko 		if (!prt_who)
56501af68beaSAlexander Stetsenko 			(void) printf("\n");
56511af68beaSAlexander Stetsenko 	}
56521af68beaSAlexander Stetsenko 
56531af68beaSAlexander Stetsenko 	uu_avl_walk_end(walk);
56541af68beaSAlexander Stetsenko }
56551af68beaSAlexander Stetsenko 
56561af68beaSAlexander Stetsenko static void
print_fs_perms(fs_perm_set_t * fspset)56571af68beaSAlexander Stetsenko print_fs_perms(fs_perm_set_t *fspset)
56581af68beaSAlexander Stetsenko {
56591af68beaSAlexander Stetsenko 	fs_perm_node_t *node = NULL;
56609adfa60dSMatthew Ahrens 	char buf[MAXNAMELEN + 32];
56611af68beaSAlexander Stetsenko 	const char *dsname = buf;
56621af68beaSAlexander Stetsenko 
56631af68beaSAlexander Stetsenko 	for (node = uu_list_first(fspset->fsps_list); node != NULL;
56641af68beaSAlexander Stetsenko 	    node = uu_list_next(fspset->fsps_list, node)) {
56651af68beaSAlexander Stetsenko 		uu_avl_t *sc_avl = node->fspn_fsperm.fsp_sc_avl;
56661af68beaSAlexander Stetsenko 		uu_avl_t *uge_avl = node->fspn_fsperm.fsp_uge_avl;
56671af68beaSAlexander Stetsenko 		int left = 0;
56681af68beaSAlexander Stetsenko 
56699adfa60dSMatthew Ahrens 		(void) snprintf(buf, sizeof (buf),
56701af68beaSAlexander Stetsenko 		    gettext("---- Permissions on %s "),
56711af68beaSAlexander Stetsenko 		    node->fspn_fsperm.fsp_name);
56721af68beaSAlexander Stetsenko 		(void) printf(dsname);
56731af68beaSAlexander Stetsenko 		left = 70 - strlen(buf);
56741af68beaSAlexander Stetsenko 		while (left-- > 0)
56751af68beaSAlexander Stetsenko 			(void) printf("-");
56761af68beaSAlexander Stetsenko 		(void) printf("\n");
56771af68beaSAlexander Stetsenko 
56781af68beaSAlexander Stetsenko 		print_set_creat_perms(sc_avl);
56791af68beaSAlexander Stetsenko 		print_uge_deleg_perms(uge_avl, B_TRUE, B_FALSE,
56801af68beaSAlexander Stetsenko 		    gettext("Local permissions:\n"));
56811af68beaSAlexander Stetsenko 		print_uge_deleg_perms(uge_avl, B_FALSE, B_TRUE,
56821af68beaSAlexander Stetsenko 		    gettext("Descendent permissions:\n"));
56831af68beaSAlexander Stetsenko 		print_uge_deleg_perms(uge_avl, B_TRUE, B_TRUE,
56841af68beaSAlexander Stetsenko 		    gettext("Local+Descendent permissions:\n"));
56851af68beaSAlexander Stetsenko 	}
56861af68beaSAlexander Stetsenko }
56871af68beaSAlexander Stetsenko 
56881af68beaSAlexander Stetsenko static fs_perm_set_t fs_perm_set = { NULL, NULL, NULL, NULL };
56891af68beaSAlexander Stetsenko 
56901af68beaSAlexander Stetsenko struct deleg_perms {
56911af68beaSAlexander Stetsenko 	boolean_t un;
56921af68beaSAlexander Stetsenko 	nvlist_t *nvl;
56931af68beaSAlexander Stetsenko };
56941af68beaSAlexander Stetsenko 
56951af68beaSAlexander Stetsenko static int
set_deleg_perms(zfs_handle_t * zhp,void * data)56961af68beaSAlexander Stetsenko set_deleg_perms(zfs_handle_t *zhp, void *data)
56971af68beaSAlexander Stetsenko {
56981af68beaSAlexander Stetsenko 	struct deleg_perms *perms = (struct deleg_perms *)data;
56991af68beaSAlexander Stetsenko 	zfs_type_t zfs_type = zfs_get_type(zhp);
57001af68beaSAlexander Stetsenko 
57011af68beaSAlexander Stetsenko 	if (zfs_type != ZFS_TYPE_FILESYSTEM && zfs_type != ZFS_TYPE_VOLUME)
57021af68beaSAlexander Stetsenko 		return (0);
57031af68beaSAlexander Stetsenko 
57041af68beaSAlexander Stetsenko 	return (zfs_set_fsacl(zhp, perms->un, perms->nvl));
57051af68beaSAlexander Stetsenko }
57061af68beaSAlexander Stetsenko 
57071af68beaSAlexander Stetsenko static int
zfs_do_allow_unallow_impl(int argc,char ** argv,boolean_t un)57081af68beaSAlexander Stetsenko zfs_do_allow_unallow_impl(int argc, char **argv, boolean_t un)
57091af68beaSAlexander Stetsenko {
57101af68beaSAlexander Stetsenko 	zfs_handle_t *zhp;
57111af68beaSAlexander Stetsenko 	nvlist_t *perm_nvl = NULL;
57121af68beaSAlexander Stetsenko 	nvlist_t *update_perm_nvl = NULL;
57131af68beaSAlexander Stetsenko 	int error = 1;
57141af68beaSAlexander Stetsenko 	int c;
57151af68beaSAlexander Stetsenko 	struct allow_opts opts = { 0 };
57161af68beaSAlexander Stetsenko 
57171af68beaSAlexander Stetsenko 	const char *optstr = un ? "ldugecsrh" : "ldugecsh";
57181af68beaSAlexander Stetsenko 
57191af68beaSAlexander Stetsenko 	/* check opts */
57201af68beaSAlexander Stetsenko 	while ((c = getopt(argc, argv, optstr)) != -1) {
57211af68beaSAlexander Stetsenko 		switch (c) {
57221af68beaSAlexander Stetsenko 		case 'l':
57231af68beaSAlexander Stetsenko 			opts.local = B_TRUE;
57241af68beaSAlexander Stetsenko 			break;
57251af68beaSAlexander Stetsenko 		case 'd':
57261af68beaSAlexander Stetsenko 			opts.descend = B_TRUE;
57271af68beaSAlexander Stetsenko 			break;
57281af68beaSAlexander Stetsenko 		case 'u':
57291af68beaSAlexander Stetsenko 			opts.user = B_TRUE;
57301af68beaSAlexander Stetsenko 			break;
57311af68beaSAlexander Stetsenko 		case 'g':
57321af68beaSAlexander Stetsenko 			opts.group = B_TRUE;
57331af68beaSAlexander Stetsenko 			break;
57341af68beaSAlexander Stetsenko 		case 'e':
57351af68beaSAlexander Stetsenko 			opts.everyone = B_TRUE;
57361af68beaSAlexander Stetsenko 			break;
57371af68beaSAlexander Stetsenko 		case 's':
57381af68beaSAlexander Stetsenko 			opts.set = B_TRUE;
57391af68beaSAlexander Stetsenko 			break;
57401af68beaSAlexander Stetsenko 		case 'c':
57411af68beaSAlexander Stetsenko 			opts.create = B_TRUE;
57421af68beaSAlexander Stetsenko 			break;
57431af68beaSAlexander Stetsenko 		case 'r':
57441af68beaSAlexander Stetsenko 			opts.recursive = B_TRUE;
57451af68beaSAlexander Stetsenko 			break;
57461af68beaSAlexander Stetsenko 		case ':':
57471af68beaSAlexander Stetsenko 			(void) fprintf(stderr, gettext("missing argument for "
57481af68beaSAlexander Stetsenko 			    "'%c' option\n"), optopt);
57491af68beaSAlexander Stetsenko 			usage(B_FALSE);
57501af68beaSAlexander Stetsenko 			break;
57511af68beaSAlexander Stetsenko 		case 'h':
57521af68beaSAlexander Stetsenko 			opts.prt_usage = B_TRUE;
57531af68beaSAlexander Stetsenko 			break;
57541af68beaSAlexander Stetsenko 		case '?':
57551af68beaSAlexander Stetsenko 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
57561af68beaSAlexander Stetsenko 			    optopt);
57571af68beaSAlexander Stetsenko 			usage(B_FALSE);
57581af68beaSAlexander Stetsenko 		}
57591af68beaSAlexander Stetsenko 	}
57601af68beaSAlexander Stetsenko 
57611af68beaSAlexander Stetsenko 	argc -= optind;
57621af68beaSAlexander Stetsenko 	argv += optind;
57631af68beaSAlexander Stetsenko 
57641af68beaSAlexander Stetsenko 	/* check arguments */
57651af68beaSAlexander Stetsenko 	parse_allow_args(argc, argv, un, &opts);
57661af68beaSAlexander Stetsenko 
57671af68beaSAlexander Stetsenko 	/* try to open the dataset */
5768a640714eSAlexander Eremin 	if ((zhp = zfs_open(g_zfs, opts.dataset, ZFS_TYPE_FILESYSTEM |
5769a640714eSAlexander Eremin 	    ZFS_TYPE_VOLUME)) == NULL) {
5770a640714eSAlexander Eremin 		(void) fprintf(stderr, "Failed to open dataset: %s\n",
57711af68beaSAlexander Stetsenko 		    opts.dataset);
57721af68beaSAlexander Stetsenko 		return (-1);
57731af68beaSAlexander Stetsenko 	}
57741af68beaSAlexander Stetsenko 
57751af68beaSAlexander Stetsenko 	if (zfs_get_fsacl(zhp, &perm_nvl) != 0)
57761af68beaSAlexander Stetsenko 		goto cleanup2;
57771af68beaSAlexander Stetsenko 
57781af68beaSAlexander Stetsenko 	fs_perm_set_init(&fs_perm_set);
57791af68beaSAlexander Stetsenko 	if (parse_fs_perm_set(&fs_perm_set, perm_nvl) != 0) {
5780a640714eSAlexander Eremin 		(void) fprintf(stderr, "Failed to parse fsacl permissions\n");
57811af68beaSAlexander Stetsenko 		goto cleanup1;
57821af68beaSAlexander Stetsenko 	}
57831af68beaSAlexander Stetsenko 
57841af68beaSAlexander Stetsenko 	if (opts.prt_perms)
57851af68beaSAlexander Stetsenko 		print_fs_perms(&fs_perm_set);
57861af68beaSAlexander Stetsenko 	else {
57871af68beaSAlexander Stetsenko 		(void) construct_fsacl_list(un, &opts, &update_perm_nvl);
57881af68beaSAlexander Stetsenko 		if (zfs_set_fsacl(zhp, un, update_perm_nvl) != 0)
57891af68beaSAlexander Stetsenko 			goto cleanup0;
57901af68beaSAlexander Stetsenko 
57911af68beaSAlexander Stetsenko 		if (un && opts.recursive) {
57921af68beaSAlexander Stetsenko 			struct deleg_perms data = { un, update_perm_nvl };
57931af68beaSAlexander Stetsenko 			if (zfs_iter_filesystems(zhp, set_deleg_perms,
57941af68beaSAlexander Stetsenko 			    &data) != 0)
57951af68beaSAlexander Stetsenko 				goto cleanup0;
57961af68beaSAlexander Stetsenko 		}
57971af68beaSAlexander Stetsenko 	}
57981af68beaSAlexander Stetsenko 
57991af68beaSAlexander Stetsenko 	error = 0;
58001af68beaSAlexander Stetsenko 
58011af68beaSAlexander Stetsenko cleanup0:
58021af68beaSAlexander Stetsenko 	nvlist_free(perm_nvl);
58031af68beaSAlexander Stetsenko 	nvlist_free(update_perm_nvl);
58041af68beaSAlexander Stetsenko cleanup1:
58051af68beaSAlexander Stetsenko 	fs_perm_set_fini(&fs_perm_set);
58061af68beaSAlexander Stetsenko cleanup2:
58071af68beaSAlexander Stetsenko 	zfs_close(zhp);
58081af68beaSAlexander Stetsenko 
58091af68beaSAlexander Stetsenko 	return (error);
58101af68beaSAlexander Stetsenko }
58111af68beaSAlexander Stetsenko 
58121af68beaSAlexander Stetsenko static int
zfs_do_allow(int argc,char ** argv)58131af68beaSAlexander Stetsenko zfs_do_allow(int argc, char **argv)
58141af68beaSAlexander Stetsenko {
58151af68beaSAlexander Stetsenko 	return (zfs_do_allow_unallow_impl(argc, argv, B_FALSE));
58161af68beaSAlexander Stetsenko }
58171af68beaSAlexander Stetsenko 
58181af68beaSAlexander Stetsenko static int
zfs_do_unallow(int argc,char ** argv)58191af68beaSAlexander Stetsenko zfs_do_unallow(int argc, char **argv)
58201af68beaSAlexander Stetsenko {
58211af68beaSAlexander Stetsenko 	return (zfs_do_allow_unallow_impl(argc, argv, B_TRUE));
58221af68beaSAlexander Stetsenko }
58231af68beaSAlexander Stetsenko 
5824842727c2SChris Kirby static int
zfs_do_hold_rele_impl(int argc,char ** argv,boolean_t holding)5825842727c2SChris Kirby zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding)
5826842727c2SChris Kirby {
5827842727c2SChris Kirby 	int errors = 0;
5828842727c2SChris Kirby 	int i;
5829842727c2SChris Kirby 	const char *tag;
5830842727c2SChris Kirby 	boolean_t recursive = B_FALSE;
5831ca45db41SChris Kirby 	const char *opts = holding ? "rt" : "r";
5832842727c2SChris Kirby 	int c;
5833842727c2SChris Kirby 
5834842727c2SChris Kirby 	/* check options */
5835ca45db41SChris Kirby 	while ((c = getopt(argc, argv, opts)) != -1) {
5836842727c2SChris Kirby 		switch (c) {
5837842727c2SChris Kirby 		case 'r':
5838842727c2SChris Kirby 			recursive = B_TRUE;
5839842727c2SChris Kirby 			break;
5840842727c2SChris Kirby 		case '?':
5841842727c2SChris Kirby 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
5842842727c2SChris Kirby 			    optopt);
5843842727c2SChris Kirby 			usage(B_FALSE);
5844842727c2SChris Kirby 		}
5845842727c2SChris Kirby 	}
5846842727c2SChris Kirby 
5847842727c2SChris Kirby 	argc -= optind;
5848842727c2SChris Kirby 	argv += optind;
5849842727c2SChris Kirby 
5850842727c2SChris Kirby 	/* check number of arguments */
5851842727c2SChris Kirby 	if (argc < 2)
5852842727c2SChris Kirby 		usage(B_FALSE);
5853842727c2SChris Kirby 
5854842727c2SChris Kirby 	tag = argv[0];
5855842727c2SChris Kirby 	--argc;
5856842727c2SChris Kirby 	++argv;
5857842727c2SChris Kirby 
5858ca45db41SChris Kirby 	if (holding && tag[0] == '.') {
5859842727c2SChris Kirby 		/* tags starting with '.' are reserved for libzfs */
5860ca45db41SChris Kirby 		(void) fprintf(stderr, gettext("tag may not start with '.'\n"));
5861842727c2SChris Kirby 		usage(B_FALSE);
5862842727c2SChris Kirby 	}
5863842727c2SChris Kirby 
5864842727c2SChris Kirby 	for (i = 0; i < argc; ++i) {
5865842727c2SChris Kirby 		zfs_handle_t *zhp;
58669adfa60dSMatthew Ahrens 		char parent[ZFS_MAX_DATASET_NAME_LEN];
5867842727c2SChris Kirby 		const char *delim;
5868842727c2SChris Kirby 		char *path = argv[i];
5869842727c2SChris Kirby 
5870842727c2SChris Kirby 		delim = strchr(path, '@');
5871842727c2SChris Kirby 		if (delim == NULL) {
5872842727c2SChris Kirby 			(void) fprintf(stderr,
5873842727c2SChris Kirby 			    gettext("'%s' is not a snapshot\n"), path);
5874842727c2SChris Kirby 			++errors;
5875842727c2SChris Kirby 			continue;
5876842727c2SChris Kirby 		}
5877842727c2SChris Kirby 		(void) strncpy(parent, path, delim - path);
5878842727c2SChris Kirby 		parent[delim - path] = '\0';
5879842727c2SChris Kirby 
5880842727c2SChris Kirby 		zhp = zfs_open(g_zfs, parent,
5881842727c2SChris Kirby 		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
5882842727c2SChris Kirby 		if (zhp == NULL) {
5883842727c2SChris Kirby 			++errors;
5884842727c2SChris Kirby 			continue;
5885842727c2SChris Kirby 		}
5886ca45db41SChris Kirby 		if (holding) {
5887a7a845e4SSteven Hartland 			if (zfs_hold(zhp, delim+1, tag, recursive, -1) != 0)
5888842727c2SChris Kirby 				++errors;
5889ca45db41SChris Kirby 		} else {
5890ca45db41SChris Kirby 			if (zfs_release(zhp, delim+1, tag, recursive) != 0)
5891ca45db41SChris Kirby 				++errors;
5892ca45db41SChris Kirby 		}
5893842727c2SChris Kirby 		zfs_close(zhp);
5894842727c2SChris Kirby 	}
5895842727c2SChris Kirby 
5896842727c2SChris Kirby 	return (errors != 0);
5897842727c2SChris Kirby }
5898842727c2SChris Kirby 
5899842727c2SChris Kirby /*
5900ca45db41SChris Kirby  * zfs hold [-r] [-t] <tag> <snap> ...
5901842727c2SChris Kirby  *
5902842727c2SChris Kirby  *	-r	Recursively hold
5903842727c2SChris Kirby  *
5904842727c2SChris Kirby  * Apply a user-hold with the given tag to the list of snapshots.
5905842727c2SChris Kirby  */
5906842727c2SChris Kirby static int
zfs_do_hold(int argc,char ** argv)5907842727c2SChris Kirby zfs_do_hold(int argc, char **argv)
5908842727c2SChris Kirby {
5909842727c2SChris Kirby 	return (zfs_do_hold_rele_impl(argc, argv, B_TRUE));
5910842727c2SChris Kirby }
5911842727c2SChris Kirby 
5912842727c2SChris Kirby /*
5913842727c2SChris Kirby  * zfs release [-r] <tag> <snap> ...
5914842727c2SChris Kirby  *
5915842727c2SChris Kirby  *	-r	Recursively release
5916842727c2SChris Kirby  *
5917842727c2SChris Kirby  * Release a user-hold with the given tag from the list of snapshots.
5918842727c2SChris Kirby  */
5919842727c2SChris Kirby static int
zfs_do_release(int argc,char ** argv)5920842727c2SChris Kirby zfs_do_release(int argc, char **argv)
5921842727c2SChris Kirby {
5922842727c2SChris Kirby 	return (zfs_do_hold_rele_impl(argc, argv, B_FALSE));
5923842727c2SChris Kirby }
5924842727c2SChris Kirby 
59251af68beaSAlexander Stetsenko typedef struct holds_cbdata {
59261af68beaSAlexander Stetsenko 	boolean_t	cb_recursive;
59271af68beaSAlexander Stetsenko 	const char	*cb_snapname;
59281af68beaSAlexander Stetsenko 	nvlist_t	**cb_nvlp;
59291af68beaSAlexander Stetsenko 	size_t		cb_max_namelen;
59301af68beaSAlexander Stetsenko 	size_t		cb_max_taglen;
59311af68beaSAlexander Stetsenko } holds_cbdata_t;
59321af68beaSAlexander Stetsenko 
59331af68beaSAlexander Stetsenko #define	STRFTIME_FMT_STR "%a %b %e %k:%M %Y"
59341af68beaSAlexander Stetsenko #define	DATETIME_BUF_LEN (32)
59351af68beaSAlexander Stetsenko /*
59361af68beaSAlexander Stetsenko  *
59371af68beaSAlexander Stetsenko  */
59381af68beaSAlexander Stetsenko static void
print_holds(boolean_t scripted,size_t nwidth,size_t tagwidth,nvlist_t * nvl)59391af68beaSAlexander Stetsenko print_holds(boolean_t scripted, size_t nwidth, size_t tagwidth, nvlist_t *nvl)
59401af68beaSAlexander Stetsenko {
59411af68beaSAlexander Stetsenko 	int i;
59421af68beaSAlexander Stetsenko 	nvpair_t *nvp = NULL;
59431af68beaSAlexander Stetsenko 	char *hdr_cols[] = { "NAME", "TAG", "TIMESTAMP" };
59441af68beaSAlexander Stetsenko 	const char *col;
59451af68beaSAlexander Stetsenko 
59461af68beaSAlexander Stetsenko 	if (!scripted) {
59471af68beaSAlexander Stetsenko 		for (i = 0; i < 3; i++) {
59481af68beaSAlexander Stetsenko 			col = gettext(hdr_cols[i]);
59491af68beaSAlexander Stetsenko 			if (i < 2)
59501af68beaSAlexander Stetsenko 				(void) printf("%-*s  ", i ? tagwidth : nwidth,
59511af68beaSAlexander Stetsenko 				    col);
59521af68beaSAlexander Stetsenko 			else
59531af68beaSAlexander Stetsenko 				(void) printf("%s\n", col);
59541af68beaSAlexander Stetsenko 		}
59551af68beaSAlexander Stetsenko 	}
59561af68beaSAlexander Stetsenko 
59571af68beaSAlexander Stetsenko 	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
59581af68beaSAlexander Stetsenko 		char *zname = nvpair_name(nvp);
59591af68beaSAlexander Stetsenko 		nvlist_t *nvl2;
59601af68beaSAlexander Stetsenko 		nvpair_t *nvp2 = NULL;
59611af68beaSAlexander Stetsenko 		(void) nvpair_value_nvlist(nvp, &nvl2);
59621af68beaSAlexander Stetsenko 		while ((nvp2 = nvlist_next_nvpair(nvl2, nvp2)) != NULL) {
59631af68beaSAlexander Stetsenko 			char tsbuf[DATETIME_BUF_LEN];
59641af68beaSAlexander Stetsenko 			char *tagname = nvpair_name(nvp2);
59651af68beaSAlexander Stetsenko 			uint64_t val = 0;
59661af68beaSAlexander Stetsenko 			time_t time;
59671af68beaSAlexander Stetsenko 			struct tm t;
59681af68beaSAlexander Stetsenko 
59691af68beaSAlexander Stetsenko 			(void) nvpair_value_uint64(nvp2, &val);
59701af68beaSAlexander Stetsenko 			time = (time_t)val;
59711af68beaSAlexander Stetsenko 			(void) localtime_r(&time, &t);
59721af68beaSAlexander Stetsenko 			(void) strftime(tsbuf, DATETIME_BUF_LEN,
59731af68beaSAlexander Stetsenko 			    gettext(STRFTIME_FMT_STR), &t);
59741af68beaSAlexander Stetsenko 
5975e9b7d6e7SAllan Jude 			if (scripted) {
5976e9b7d6e7SAllan Jude 				(void) printf("%s\t%s\t%s\n", zname,
5977e9b7d6e7SAllan Jude 				    tagname, tsbuf);
5978e9b7d6e7SAllan Jude 			} else {
5979e9b7d6e7SAllan Jude 				(void) printf("%-*s  %-*s  %s\n", nwidth,
5980e9b7d6e7SAllan Jude 				    zname, tagwidth, tagname, tsbuf);
5981e9b7d6e7SAllan Jude 			}
59821af68beaSAlexander Stetsenko 		}
59831af68beaSAlexander Stetsenko 	}
59841af68beaSAlexander Stetsenko }
59851af68beaSAlexander Stetsenko 
59861af68beaSAlexander Stetsenko /*
59871af68beaSAlexander Stetsenko  * Generic callback function to list a dataset or snapshot.
59881af68beaSAlexander Stetsenko  */
59891af68beaSAlexander Stetsenko static int
holds_callback(zfs_handle_t * zhp,void * data)59901af68beaSAlexander Stetsenko holds_callback(zfs_handle_t *zhp, void *data)
59911af68beaSAlexander Stetsenko {
59921af68beaSAlexander Stetsenko 	holds_cbdata_t *cbp = data;
59931af68beaSAlexander Stetsenko 	nvlist_t *top_nvl = *cbp->cb_nvlp;
59941af68beaSAlexander Stetsenko 	nvlist_t *nvl = NULL;
59951af68beaSAlexander Stetsenko 	nvpair_t *nvp = NULL;
59961af68beaSAlexander Stetsenko 	const char *zname = zfs_get_name(zhp);
59979adfa60dSMatthew Ahrens 	size_t znamelen = strlen(zname);
59981af68beaSAlexander Stetsenko 
59991af68beaSAlexander Stetsenko 	if (cbp->cb_recursive) {
60001af68beaSAlexander Stetsenko 		const char *snapname;
60011af68beaSAlexander Stetsenko 		char *delim  = strchr(zname, '@');
60021af68beaSAlexander Stetsenko 		if (delim == NULL)
60031af68beaSAlexander Stetsenko 			return (0);
60041af68beaSAlexander Stetsenko 
60051af68beaSAlexander Stetsenko 		snapname = delim + 1;
60061af68beaSAlexander Stetsenko 		if (strcmp(cbp->cb_snapname, snapname))
60071af68beaSAlexander Stetsenko 			return (0);
60081af68beaSAlexander Stetsenko 	}
60091af68beaSAlexander Stetsenko 
60101af68beaSAlexander Stetsenko 	if (zfs_get_holds(zhp, &nvl) != 0)
60111af68beaSAlexander Stetsenko 		return (-1);
60121af68beaSAlexander Stetsenko 
60131af68beaSAlexander Stetsenko 	if (znamelen > cbp->cb_max_namelen)
60141af68beaSAlexander Stetsenko 		cbp->cb_max_namelen  = znamelen;
60151af68beaSAlexander Stetsenko 
60161af68beaSAlexander Stetsenko 	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
60171af68beaSAlexander Stetsenko 		const char *tag = nvpair_name(nvp);
60189adfa60dSMatthew Ahrens 		size_t taglen = strlen(tag);
60191af68beaSAlexander Stetsenko 		if (taglen > cbp->cb_max_taglen)
60201af68beaSAlexander Stetsenko 			cbp->cb_max_taglen  = taglen;
60211af68beaSAlexander Stetsenko 	}
60221af68beaSAlexander Stetsenko 
60231af68beaSAlexander Stetsenko 	return (nvlist_add_nvlist(top_nvl, zname, nvl));
60241af68beaSAlexander Stetsenko }
60251af68beaSAlexander Stetsenko 
60261af68beaSAlexander Stetsenko /*
60271af68beaSAlexander Stetsenko  * zfs holds [-r] <snap> ...
60281af68beaSAlexander Stetsenko  *
60291af68beaSAlexander Stetsenko  *	-r	Recursively hold
60301af68beaSAlexander Stetsenko  */
60311af68beaSAlexander Stetsenko static int
zfs_do_holds(int argc,char ** argv)60321af68beaSAlexander Stetsenko zfs_do_holds(int argc, char **argv)
60331af68beaSAlexander Stetsenko {
60341af68beaSAlexander Stetsenko 	int errors = 0;
60351af68beaSAlexander Stetsenko 	int c;
60361af68beaSAlexander Stetsenko 	int i;
60371af68beaSAlexander Stetsenko 	boolean_t scripted = B_FALSE;
60381af68beaSAlexander Stetsenko 	boolean_t recursive = B_FALSE;
60391af68beaSAlexander Stetsenko 	const char *opts = "rH";
60401af68beaSAlexander Stetsenko 	nvlist_t *nvl;
60411af68beaSAlexander Stetsenko 
60421af68beaSAlexander Stetsenko 	int types = ZFS_TYPE_SNAPSHOT;
60431af68beaSAlexander Stetsenko 	holds_cbdata_t cb = { 0 };
60441af68beaSAlexander Stetsenko 
60451af68beaSAlexander Stetsenko 	int limit = 0;
604605c998d6SRichard Lowe 	int ret = 0;
60471af68beaSAlexander Stetsenko 	int flags = 0;
60481af68beaSAlexander Stetsenko 
60491af68beaSAlexander Stetsenko 	/* check options */
60501af68beaSAlexander Stetsenko 	while ((c = getopt(argc, argv, opts)) != -1) {
60511af68beaSAlexander Stetsenko 		switch (c) {
60521af68beaSAlexander Stetsenko 		case 'r':
60531af68beaSAlexander Stetsenko 			recursive = B_TRUE;
60541af68beaSAlexander Stetsenko 			break;
60551af68beaSAlexander Stetsenko 		case 'H':
60561af68beaSAlexander Stetsenko 			scripted = B_TRUE;
60571af68beaSAlexander Stetsenko 			break;
60581af68beaSAlexander Stetsenko 		case '?':
60591af68beaSAlexander Stetsenko 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
60601af68beaSAlexander Stetsenko 			    optopt);
60611af68beaSAlexander Stetsenko 			usage(B_FALSE);
60621af68beaSAlexander Stetsenko 		}
60631af68beaSAlexander Stetsenko 	}
60641af68beaSAlexander Stetsenko 
60651af68beaSAlexander Stetsenko 	if (recursive) {
60661af68beaSAlexander Stetsenko 		types |= ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;
60671af68beaSAlexander Stetsenko 		flags |= ZFS_ITER_RECURSE;
60681af68beaSAlexander Stetsenko 	}
60691af68beaSAlexander Stetsenko 
60701af68beaSAlexander Stetsenko 	argc -= optind;
60711af68beaSAlexander Stetsenko 	argv += optind;
60721af68beaSAlexander Stetsenko 
60731af68beaSAlexander Stetsenko 	/* check number of arguments */
60741af68beaSAlexander Stetsenko 	if (argc < 1)
60751af68beaSAlexander Stetsenko 		usage(B_FALSE);
60761af68beaSAlexander Stetsenko 
60771af68beaSAlexander Stetsenko 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
60781af68beaSAlexander Stetsenko 		nomem();
60791af68beaSAlexander Stetsenko 
60801af68beaSAlexander Stetsenko 	for (i = 0; i < argc; ++i) {
60811af68beaSAlexander Stetsenko 		char *snapshot = argv[i];
60821af68beaSAlexander Stetsenko 		const char *delim;
60831af68beaSAlexander Stetsenko 		const char *snapname;
60841af68beaSAlexander Stetsenko 
60851af68beaSAlexander Stetsenko 		delim = strchr(snapshot, '@');
60861af68beaSAlexander Stetsenko 		if (delim == NULL) {
60871af68beaSAlexander Stetsenko 			(void) fprintf(stderr,
60881af68beaSAlexander Stetsenko 			    gettext("'%s' is not a snapshot\n"), snapshot);
60891af68beaSAlexander Stetsenko 			++errors;
60901af68beaSAlexander Stetsenko 			continue;
60911af68beaSAlexander Stetsenko 		}
60921af68beaSAlexander Stetsenko 		snapname = delim + 1;
60931af68beaSAlexander Stetsenko 		if (recursive)
60941af68beaSAlexander Stetsenko 			snapshot[delim - snapshot] = '\0';
60951af68beaSAlexander Stetsenko 
60961af68beaSAlexander Stetsenko 		cb.cb_recursive = recursive;
60971af68beaSAlexander Stetsenko 		cb.cb_snapname = snapname;
60981af68beaSAlexander Stetsenko 		cb.cb_nvlp = &nvl;
60991af68beaSAlexander Stetsenko 
61001af68beaSAlexander Stetsenko 		/*
61011af68beaSAlexander Stetsenko 		 *  1. collect holds data, set format options
61021af68beaSAlexander Stetsenko 		 */
61031e9e241fSAndrew Stormont 		ret = zfs_for_each(1, &argv[i], flags, types, NULL, NULL, limit,
61041af68beaSAlexander Stetsenko 		    holds_callback, &cb);
61051af68beaSAlexander Stetsenko 		if (ret != 0)
61061af68beaSAlexander Stetsenko 			++errors;
61071af68beaSAlexander Stetsenko 	}
61081af68beaSAlexander Stetsenko 
61091af68beaSAlexander Stetsenko 	/*
61101af68beaSAlexander Stetsenko 	 *  2. print holds data
61111af68beaSAlexander Stetsenko 	 */
61121af68beaSAlexander Stetsenko 	print_holds(scripted, cb.cb_max_namelen, cb.cb_max_taglen, nvl);
61131af68beaSAlexander Stetsenko 
61141af68beaSAlexander Stetsenko 	nvlist_free(nvl);
61151af68beaSAlexander Stetsenko 
61161af68beaSAlexander Stetsenko 	return (0 != errors);
61171af68beaSAlexander Stetsenko }
61181af68beaSAlexander Stetsenko 
6119dae68f5eSmmusante #define	CHECK_SPINNER 30
6120dae68f5eSmmusante #define	SPINNER_TIME 3		/* seconds */
6121591e0e13SSebastien Roy #define	MOUNT_TIME 1		/* seconds */
6122591e0e13SSebastien Roy 
6123591e0e13SSebastien Roy typedef struct get_all_state {
6124591e0e13SSebastien Roy 	boolean_t	ga_verbose;
6125591e0e13SSebastien Roy 	get_all_cb_t	*ga_cbp;
6126591e0e13SSebastien Roy } get_all_state_t;
6127dae68f5eSmmusante 
61287f7322feSeschrock static int
get_one_dataset(zfs_handle_t * zhp,void * data)6129f3861e1aSahl get_one_dataset(zfs_handle_t *zhp, void *data)
61307f7322feSeschrock {
6131a8b6ddafSMark J Musante 	static char *spin[] = { "-", "\\", "|", "/" };
6132dae68f5eSmmusante 	static int spinval = 0;
6133dae68f5eSmmusante 	static int spincheck = 0;
6134dae68f5eSmmusante 	static time_t last_spin_time = (time_t)0;
6135591e0e13SSebastien Roy 	get_all_state_t *state = data;
6136f3861e1aSahl 	zfs_type_t type = zfs_get_type(zhp);
61377f7322feSeschrock 
6138591e0e13SSebastien Roy 	if (state->ga_verbose) {
6139dae68f5eSmmusante 		if (--spincheck < 0) {
6140dae68f5eSmmusante 			time_t now = time(NULL);
6141dae68f5eSmmusante 			if (last_spin_time + SPINNER_TIME < now) {
6142a8b6ddafSMark J Musante 				update_progress(spin[spinval++ % 4]);
6143dae68f5eSmmusante 				last_spin_time = now;
6144dae68f5eSmmusante 			}
6145dae68f5eSmmusante 			spincheck = CHECK_SPINNER;
6146dae68f5eSmmusante 		}
6147dae68f5eSmmusante 	}
6148dae68f5eSmmusante 
61497f7322feSeschrock 	/*
6150f3861e1aSahl 	 * Interate over any nested datasets.
61517f7322feSeschrock 	 */
61529d9a58e3SEric Taylor 	if (zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) {
61533ccfa83cSahrens 		zfs_close(zhp);
6154f3861e1aSahl 		return (1);
6155f3861e1aSahl 	}
6156f3861e1aSahl 
6157f3861e1aSahl 	/*
6158f3861e1aSahl 	 * Skip any datasets whose type does not match.
6159f3861e1aSahl 	 */
61609d9a58e3SEric Taylor 	if ((type & ZFS_TYPE_FILESYSTEM) == 0) {
61617f7322feSeschrock 		zfs_close(zhp);
61627f7322feSeschrock 		return (0);
61637f7322feSeschrock 	}
6164591e0e13SSebastien Roy 	libzfs_add_handle(state->ga_cbp, zhp);
6165591e0e13SSebastien Roy 	assert(state->ga_cbp->cb_used <= state->ga_cbp->cb_alloc);
61667f7322feSeschrock 
6167f3861e1aSahl 	return (0);
61687f7322feSeschrock }
61697f7322feSeschrock 
61707f7322feSeschrock static void
get_all_datasets(get_all_cb_t * cbp,boolean_t verbose)6171591e0e13SSebastien Roy get_all_datasets(get_all_cb_t *cbp, boolean_t verbose)
61727f7322feSeschrock {
6173591e0e13SSebastien Roy 	get_all_state_t state = {
6174591e0e13SSebastien Roy 	    .ga_verbose = verbose,
6175591e0e13SSebastien Roy 	    .ga_cbp = cbp
6176591e0e13SSebastien Roy 	};
6177dae68f5eSmmusante 
6178a8b6ddafSMark J Musante 	if (verbose)
6179a8b6ddafSMark J Musante 		set_progress_header(gettext("Reading ZFS config"));
6180591e0e13SSebastien Roy 	(void) zfs_iter_root(g_zfs, get_one_dataset, &state);
6181dae68f5eSmmusante 
6182a8b6ddafSMark J Musante 	if (verbose)
6183a8b6ddafSMark J Musante 		finish_progress(gettext("done."));
61847f7322feSeschrock }
61857f7322feSeschrock 
6186fa9e4066Sahrens /*
6187fa9e4066Sahrens  * Generic callback for sharing or mounting filesystems.  Because the code is so
6188fa9e4066Sahrens  * similar, we have a common function with an extra parameter to determine which
6189fa9e4066Sahrens  * mode we are using.
6190fa9e4066Sahrens  */
6191591e0e13SSebastien Roy typedef enum { OP_SHARE, OP_MOUNT } share_mount_op_t;
6192591e0e13SSebastien Roy 
6193591e0e13SSebastien Roy typedef struct share_mount_state {
6194591e0e13SSebastien Roy 	share_mount_op_t	sm_op;
6195591e0e13SSebastien Roy 	boolean_t	sm_verbose;
6196591e0e13SSebastien Roy 	int	sm_flags;
6197591e0e13SSebastien Roy 	char	*sm_options;
6198591e0e13SSebastien Roy 	char	*sm_proto; /* only valid for OP_SHARE */
6199591e0e13SSebastien Roy 	mutex_t	sm_lock; /* protects the remaining fields */
6200591e0e13SSebastien Roy 	uint_t	sm_total; /* number of filesystems to process */
6201591e0e13SSebastien Roy 	uint_t	sm_done; /* number of filesystems processed */
6202591e0e13SSebastien Roy 	int	sm_status; /* -1 if any of the share/mount operations failed */
6203591e0e13SSebastien Roy } share_mount_state_t;
6204fa9e4066Sahrens 
6205fa9e4066Sahrens /*
6206f3861e1aSahl  * Share or mount a dataset.
6207fa9e4066Sahrens  */
6208fa9e4066Sahrens static int
share_mount_one(zfs_handle_t * zhp,int op,int flags,char * protocol,boolean_t explicit,const char * options)6209da6c28aaSamw share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol,
6210da6c28aaSamw     boolean_t explicit, const char *options)
6211fa9e4066Sahrens {
6212fa9e4066Sahrens 	char mountpoint[ZFS_MAXPROPLEN];
6213fa9e4066Sahrens 	char shareopts[ZFS_MAXPROPLEN];
6214da6c28aaSamw 	char smbshareopts[ZFS_MAXPROPLEN];
6215f3861e1aSahl 	const char *cmdname = op == OP_SHARE ? "share" : "mount";
6216fa9e4066Sahrens 	struct mnttab mnt;
6217e9dbad6fSeschrock 	uint64_t zoned, canmount;
6218da6c28aaSamw 	boolean_t shared_nfs, shared_smb;
6219fa9e4066Sahrens 
62209d9a58e3SEric Taylor 	assert(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM);
6221fa9e4066Sahrens 
6222fa9e4066Sahrens 	/*
6223f3861e1aSahl 	 * Check to make sure we can mount/share this dataset.  If we
6224f3861e1aSahl 	 * are in the global zone and the filesystem is exported to a
6225f3861e1aSahl 	 * local zone, or if we are in a local zone and the
6226f3861e1aSahl 	 * filesystem is not exported, then it is an error.
6227fa9e4066Sahrens 	 */
6228fa9e4066Sahrens 	zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
6229fa9e4066Sahrens 
6230fa9e4066Sahrens 	if (zoned && getzoneid() == GLOBAL_ZONEID) {
6231f3861e1aSahl 		if (!explicit)
6232fa9e4066Sahrens 			return (0);
6233fa9e4066Sahrens 
6234f3861e1aSahl 		(void) fprintf(stderr, gettext("cannot %s '%s': "
6235f3861e1aSahl 		    "dataset is exported to a local zone\n"), cmdname,
6236f3861e1aSahl 		    zfs_get_name(zhp));
6237fa9e4066Sahrens 		return (1);
6238fa9e4066Sahrens 
6239fa9e4066Sahrens 	} else if (!zoned && getzoneid() != GLOBAL_ZONEID) {
6240f3861e1aSahl 		if (!explicit)
6241fa9e4066Sahrens 			return (0);
6242fa9e4066Sahrens 
6243f3861e1aSahl 		(void) fprintf(stderr, gettext("cannot %s '%s': "
6244f3861e1aSahl 		    "permission denied\n"), cmdname,
6245f3861e1aSahl 		    zfs_get_name(zhp));
6246fa9e4066Sahrens 		return (1);
6247fa9e4066Sahrens 	}
6248fa9e4066Sahrens 
6249fa9e4066Sahrens 	/*
6250f3861e1aSahl 	 * Ignore any filesystems which don't apply to us. This
6251f3861e1aSahl 	 * includes those with a legacy mountpoint, or those with
6252f3861e1aSahl 	 * legacy share options.
6253fa9e4066Sahrens 	 */
6254fa9e4066Sahrens 	verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
625599653d4eSeschrock 	    sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0);
6256fa9e4066Sahrens 	verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts,
625799653d4eSeschrock 	    sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0);
6258da6c28aaSamw 	verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshareopts,
6259da6c28aaSamw 	    sizeof (smbshareopts), NULL, NULL, 0, B_FALSE) == 0);
6260fa9e4066Sahrens 
6261da6c28aaSamw 	if (op == OP_SHARE && strcmp(shareopts, "off") == 0 &&
6262da6c28aaSamw 	    strcmp(smbshareopts, "off") == 0) {
6263f3861e1aSahl 		if (!explicit)
6264fa9e4066Sahrens 			return (0);
6265fa9e4066Sahrens 
6266fa9e4066Sahrens 		(void) fprintf(stderr, gettext("cannot share '%s': "
6267fa9e4066Sahrens 		    "legacy share\n"), zfs_get_name(zhp));
6268bbf21555SRichard Lowe 		(void) fprintf(stderr, gettext("use share(8) to "
6269deb8317bSMark J Musante 		    "share this filesystem, or set "
6270deb8317bSMark J Musante 		    "sharenfs property on\n"));
6271fa9e4066Sahrens 		return (1);
6272fa9e4066Sahrens 	}
6273fa9e4066Sahrens 
6274fa9e4066Sahrens 	/*
6275f3861e1aSahl 	 * We cannot share or mount legacy filesystems. If the
6276f3861e1aSahl 	 * shareopts is non-legacy but the mountpoint is legacy, we
6277f3861e1aSahl 	 * treat it as a legacy share.
6278fa9e4066Sahrens 	 */
6279fa9e4066Sahrens 	if (strcmp(mountpoint, "legacy") == 0) {
6280f3861e1aSahl 		if (!explicit)
6281fa9e4066Sahrens 			return (0);
6282fa9e4066Sahrens 
6283fa9e4066Sahrens 		(void) fprintf(stderr, gettext("cannot %s '%s': "
6284fa9e4066Sahrens 		    "legacy mountpoint\n"), cmdname, zfs_get_name(zhp));
6285bbf21555SRichard Lowe 		(void) fprintf(stderr, gettext("use %s(8) to "
62863cb34c60Sahrens 		    "%s this filesystem\n"), cmdname, cmdname);
6287fa9e4066Sahrens 		return (1);
6288fa9e4066Sahrens 	}
6289fa9e4066Sahrens 
6290fa9e4066Sahrens 	if (strcmp(mountpoint, "none") == 0) {
6291f3861e1aSahl 		if (!explicit)
6292fa9e4066Sahrens 			return (0);
6293fa9e4066Sahrens 
6294fa9e4066Sahrens 		(void) fprintf(stderr, gettext("cannot %s '%s': no "
6295fa9e4066Sahrens 		    "mountpoint set\n"), cmdname, zfs_get_name(zhp));
6296fa9e4066Sahrens 		return (1);
6297fa9e4066Sahrens 	}
6298fa9e4066Sahrens 
6299a227b7f4Shs24103 	/*
6300a227b7f4Shs24103 	 * canmount	explicit	outcome
6301a227b7f4Shs24103 	 * on		no		pass through
6302a227b7f4Shs24103 	 * on		yes		pass through
6303a227b7f4Shs24103 	 * off		no		return 0
6304a227b7f4Shs24103 	 * off		yes		display error, return 1
6305a227b7f4Shs24103 	 * noauto	no		return 0
6306a227b7f4Shs24103 	 * noauto	yes		pass through
6307a227b7f4Shs24103 	 */
6308deb8317bSMark J Musante 	canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);
6309a227b7f4Shs24103 	if (canmount == ZFS_CANMOUNT_OFF) {
6310f3861e1aSahl 		if (!explicit)
6311e9dbad6fSeschrock 			return (0);
6312e9dbad6fSeschrock 
6313f3861e1aSahl 		(void) fprintf(stderr, gettext("cannot %s '%s': "
6314f3861e1aSahl 		    "'canmount' property is set to 'off'\n"), cmdname,
6315f3861e1aSahl 		    zfs_get_name(zhp));
6316e9dbad6fSeschrock 		return (1);
6317a227b7f4Shs24103 	} else if (canmount == ZFS_CANMOUNT_NOAUTO && !explicit) {
6318a227b7f4Shs24103 		return (0);
6319e9dbad6fSeschrock 	}
6320e9dbad6fSeschrock 
6321fa9e4066Sahrens 	/*
6322eb633035STom Caputi 	 * If this filesystem is encrypted and does not have
6323eb633035STom Caputi 	 * a loaded key, we can not mount it.
6324eb633035STom Caputi 	 */
6325eb633035STom Caputi 	if ((flags & MS_CRYPT) == 0 &&
6326eb633035STom Caputi 	    zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) != ZIO_CRYPT_OFF &&
6327eb633035STom Caputi 	    zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS) ==
6328eb633035STom Caputi 	    ZFS_KEYSTATUS_UNAVAILABLE) {
6329eb633035STom Caputi 		if (!explicit)
6330eb633035STom Caputi 			return (0);
6331eb633035STom Caputi 
6332eb633035STom Caputi 		(void) fprintf(stderr, gettext("cannot %s '%s': "
6333eb633035STom Caputi 		    "encryption key not loaded\n"), cmdname, zfs_get_name(zhp));
6334eb633035STom Caputi 		return (1);
6335eb633035STom Caputi 	}
6336eb633035STom Caputi 
6337eb633035STom Caputi 	/*
63389c3fd121SMatthew Ahrens 	 * If this filesystem is inconsistent and has a receive resume
63399c3fd121SMatthew Ahrens 	 * token, we can not mount it.
63409c3fd121SMatthew Ahrens 	 */
63419c3fd121SMatthew Ahrens 	if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) &&
63429c3fd121SMatthew Ahrens 	    zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,
63439c3fd121SMatthew Ahrens 	    NULL, 0, NULL, NULL, 0, B_TRUE) == 0) {
63449c3fd121SMatthew Ahrens 		if (!explicit)
63459c3fd121SMatthew Ahrens 			return (0);
63469c3fd121SMatthew Ahrens 
63479c3fd121SMatthew Ahrens 		(void) fprintf(stderr, gettext("cannot %s '%s': "
63489c3fd121SMatthew Ahrens 		    "Contains partially-completed state from "
63496ccda740Sloli10K 		    "\"zfs receive -s\", which can be resumed with "
63509c3fd121SMatthew Ahrens 		    "\"zfs send -t\"\n"),
63519c3fd121SMatthew Ahrens 		    cmdname, zfs_get_name(zhp));
63529c3fd121SMatthew Ahrens 		return (1);
63539c3fd121SMatthew Ahrens 	}
63549c3fd121SMatthew Ahrens 
63559c3fd121SMatthew Ahrens 	/*
6356f3861e1aSahl 	 * At this point, we have verified that the mountpoint and/or
6357f3861e1aSahl 	 * shareopts are appropriate for auto management. If the
6358f3861e1aSahl 	 * filesystem is already mounted or shared, return (failing
6359f3861e1aSahl 	 * for explicit requests); otherwise mount or share the
6360f3861e1aSahl 	 * filesystem.
6361fa9e4066Sahrens 	 */
6362f3861e1aSahl 	switch (op) {
6363fa9e4066Sahrens 	case OP_SHARE:
6364da6c28aaSamw 
6365da6c28aaSamw 		shared_nfs = zfs_is_shared_nfs(zhp, NULL);
6366da6c28aaSamw 		shared_smb = zfs_is_shared_smb(zhp, NULL);
6367da6c28aaSamw 
6368c16bcc45SIgor Kozhukhov 		if ((shared_nfs && shared_smb) ||
6369da6c28aaSamw 		    (shared_nfs && strcmp(shareopts, "on") == 0 &&
6370da6c28aaSamw 		    strcmp(smbshareopts, "off") == 0) ||
6371da6c28aaSamw 		    (shared_smb && strcmp(smbshareopts, "on") == 0 &&
6372da6c28aaSamw 		    strcmp(shareopts, "off") == 0)) {
6373f3861e1aSahl 			if (!explicit)
6374f3861e1aSahl 				return (0);
6375f3861e1aSahl 
6376fa9e4066Sahrens 			(void) fprintf(stderr, gettext("cannot share "
6377fa9e4066Sahrens 			    "'%s': filesystem already shared\n"),
6378fa9e4066Sahrens 			    zfs_get_name(zhp));
6379fa9e4066Sahrens 			return (1);
6380fa9e4066Sahrens 		}
6381fa9e4066Sahrens 
6382fa9e4066Sahrens 		if (!zfs_is_mounted(zhp, NULL) &&
6383eb633035STom Caputi 		    zfs_mount(zhp, NULL, flags) != 0)
6384fa9e4066Sahrens 			return (1);
6385fa9e4066Sahrens 
6386da6c28aaSamw 		if (protocol == NULL) {
6387da6c28aaSamw 			if (zfs_shareall(zhp) != 0)
6388fa9e4066Sahrens 				return (1);
6389da6c28aaSamw 		} else if (strcmp(protocol, "nfs") == 0) {
6390da6c28aaSamw 			if (zfs_share_nfs(zhp))
6391da6c28aaSamw 				return (1);
6392da6c28aaSamw 		} else if (strcmp(protocol, "smb") == 0) {
6393da6c28aaSamw 			if (zfs_share_smb(zhp))
6394da6c28aaSamw 				return (1);
6395da6c28aaSamw 		} else {
6396da6c28aaSamw 			(void) fprintf(stderr, gettext("cannot share "
6397da6c28aaSamw 			    "'%s': invalid share type '%s' "
6398da6c28aaSamw 			    "specified\n"),
6399da6c28aaSamw 			    zfs_get_name(zhp), protocol);
6400da6c28aaSamw 			return (1);
6401da6c28aaSamw 		}
6402da6c28aaSamw 
6403fa9e4066Sahrens 		break;
6404fa9e4066Sahrens 
6405fa9e4066Sahrens 	case OP_MOUNT:
6406f3861e1aSahl 		if (options == NULL)
6407f3861e1aSahl 			mnt.mnt_mntopts = "";
6408f3861e1aSahl 		else
6409f3861e1aSahl 			mnt.mnt_mntopts = (char *)options;
6410f3861e1aSahl 
6411f3861e1aSahl 		if (!hasmntopt(&mnt, MNTOPT_REMOUNT) &&
6412f3861e1aSahl 		    zfs_is_mounted(zhp, NULL)) {
6413f3861e1aSahl 			if (!explicit)
6414f3861e1aSahl 				return (0);
6415f3861e1aSahl 
6416f3861e1aSahl 			(void) fprintf(stderr, gettext("cannot mount "
6417f3861e1aSahl 			    "'%s': filesystem already mounted\n"),
6418f3861e1aSahl 			    zfs_get_name(zhp));
6419f3861e1aSahl 			return (1);
6420f3861e1aSahl 		}
6421f3861e1aSahl 
6422f3861e1aSahl 		if (zfs_mount(zhp, options, flags) != 0)
6423fa9e4066Sahrens 			return (1);
6424fa9e4066Sahrens 		break;
6425fa9e4066Sahrens 	}
6426f3861e1aSahl 
6427fa9e4066Sahrens 	return (0);
6428fa9e4066Sahrens }
6429fa9e4066Sahrens 
6430dae68f5eSmmusante /*
6431dae68f5eSmmusante  * Reports progress in the form "(current/total)".  Not thread-safe.
6432dae68f5eSmmusante  */
6433dae68f5eSmmusante static void
report_mount_progress(int current,int total)6434dae68f5eSmmusante report_mount_progress(int current, int total)
6435dae68f5eSmmusante {
6436a8b6ddafSMark J Musante 	static time_t last_progress_time = 0;
6437dae68f5eSmmusante 	time_t now = time(NULL);
6438a8b6ddafSMark J Musante 	char info[32];
6439dae68f5eSmmusante 
6440dae68f5eSmmusante 	/* display header if we're here for the first time */
6441dae68f5eSmmusante 	if (current == 1) {
6442a8b6ddafSMark J Musante 		set_progress_header(gettext("Mounting ZFS filesystems"));
64433cb34c60Sahrens 	} else if (current != total && last_progress_time + MOUNT_TIME >= now) {
64443cb34c60Sahrens 		/* too soon to report again */
64453cb34c60Sahrens 		return;
64463cb34c60Sahrens 	}
6447dae68f5eSmmusante 
6448dae68f5eSmmusante 	last_progress_time = now;
6449dae68f5eSmmusante 
6450a8b6ddafSMark J Musante 	(void) sprintf(info, "(%d/%d)", current, total);
6451dae68f5eSmmusante 
6452a8b6ddafSMark J Musante 	if (current == total)
6453a8b6ddafSMark J Musante 		finish_progress(info);
6454a8b6ddafSMark J Musante 	else
6455a8b6ddafSMark J Musante 		update_progress(info);
6456dae68f5eSmmusante }
6457dae68f5eSmmusante 
6458591e0e13SSebastien Roy /*
6459591e0e13SSebastien Roy  * zfs_foreach_mountpoint() callback that mounts or shares one filesystem and
6460591e0e13SSebastien Roy  * updates the progress meter.
6461591e0e13SSebastien Roy  */
6462591e0e13SSebastien Roy static int
share_mount_one_cb(zfs_handle_t * zhp,void * arg)6463591e0e13SSebastien Roy share_mount_one_cb(zfs_handle_t *zhp, void *arg)
6464591e0e13SSebastien Roy {
6465591e0e13SSebastien Roy 	share_mount_state_t *sms = arg;
6466591e0e13SSebastien Roy 	int ret;
6467591e0e13SSebastien Roy 
6468591e0e13SSebastien Roy 	ret = share_mount_one(zhp, sms->sm_op, sms->sm_flags, sms->sm_proto,
6469591e0e13SSebastien Roy 	    B_FALSE, sms->sm_options);
6470591e0e13SSebastien Roy 
6471591e0e13SSebastien Roy 	mutex_enter(&sms->sm_lock);
6472591e0e13SSebastien Roy 	if (ret != 0)
6473591e0e13SSebastien Roy 		sms->sm_status = ret;
6474591e0e13SSebastien Roy 	sms->sm_done++;
6475591e0e13SSebastien Roy 	if (sms->sm_verbose)
6476591e0e13SSebastien Roy 		report_mount_progress(sms->sm_done, sms->sm_total);
6477591e0e13SSebastien Roy 	mutex_exit(&sms->sm_lock);
6478591e0e13SSebastien Roy 	return (ret);
6479591e0e13SSebastien Roy }
6480591e0e13SSebastien Roy 
6481c6ef114fSmmusante static void
append_options(char * mntopts,char * newopts)6482c6ef114fSmmusante append_options(char *mntopts, char *newopts)
6483c6ef114fSmmusante {
6484c6ef114fSmmusante 	int len = strlen(mntopts);
6485c6ef114fSmmusante 
6486c6ef114fSmmusante 	/* original length plus new string to append plus 1 for the comma */
6487c6ef114fSmmusante 	if (len + 1 + strlen(newopts) >= MNT_LINE_MAX) {
6488c6ef114fSmmusante 		(void) fprintf(stderr, gettext("the opts argument for "
6489c6ef114fSmmusante 		    "'%c' option is too long (more than %d chars)\n"),
6490c6ef114fSmmusante 		    "-o", MNT_LINE_MAX);
6491c6ef114fSmmusante 		usage(B_FALSE);
6492c6ef114fSmmusante 	}
6493c6ef114fSmmusante 
6494c6ef114fSmmusante 	if (*mntopts)
6495c6ef114fSmmusante 		mntopts[len++] = ',';
6496c6ef114fSmmusante 
6497c6ef114fSmmusante 	(void) strcpy(&mntopts[len], newopts);
6498c6ef114fSmmusante }
6499c6ef114fSmmusante 
6500fa9e4066Sahrens static int
share_mount(int op,int argc,char ** argv)6501f3861e1aSahl share_mount(int op, int argc, char **argv)
6502fa9e4066Sahrens {
6503fa9e4066Sahrens 	int do_all = 0;
6504dae68f5eSmmusante 	boolean_t verbose = B_FALSE;
650516299908Slling 	int c, ret = 0;
6506c6ef114fSmmusante 	char *options = NULL;
65079d9a58e3SEric Taylor 	int flags = 0;
6508fa9e4066Sahrens 
6509fa9e4066Sahrens 	/* check options */
6510eb633035STom Caputi 	while ((c = getopt(argc, argv, op == OP_MOUNT ? ":alvo:O" : "al"))
6511fa9e4066Sahrens 	    != -1) {
6512fa9e4066Sahrens 		switch (c) {
6513fa9e4066Sahrens 		case 'a':
6514fa9e4066Sahrens 			do_all = 1;
6515fa9e4066Sahrens 			break;
6516dae68f5eSmmusante 		case 'v':
6517dae68f5eSmmusante 			verbose = B_TRUE;
6518dae68f5eSmmusante 			break;
6519eb633035STom Caputi 		case 'l':
6520eb633035STom Caputi 			flags |= MS_CRYPT;
6521eb633035STom Caputi 			break;
6522fa9e4066Sahrens 		case 'o':
6523c6ef114fSmmusante 			if (*optarg == '\0') {
6524c6ef114fSmmusante 				(void) fprintf(stderr, gettext("empty mount "
6525c6ef114fSmmusante 				    "options (-o) specified\n"));
6526ce5e3b86Srm160521 				usage(B_FALSE);
6527c6ef114fSmmusante 			}
6528c6ef114fSmmusante 
6529c6ef114fSmmusante 			if (options == NULL)
6530c6ef114fSmmusante 				options = safe_malloc(MNT_LINE_MAX + 1);
6531c6ef114fSmmusante 
6532c6ef114fSmmusante 			/* option validation is done later */
6533c6ef114fSmmusante 			append_options(options, optarg);
6534ce5e3b86Srm160521 			break;
6535ce5e3b86Srm160521 
6536fa9e4066Sahrens 		case 'O':
6537f3861e1aSahl 			flags |= MS_OVERLAY;
6538fa9e4066Sahrens 			break;
6539fa9e4066Sahrens 		case ':':
6540fa9e4066Sahrens 			(void) fprintf(stderr, gettext("missing argument for "
6541fa9e4066Sahrens 			    "'%c' option\n"), optopt);
654299653d4eSeschrock 			usage(B_FALSE);
6543fa9e4066Sahrens 			break;
6544fa9e4066Sahrens 		case '?':
6545fa9e4066Sahrens 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
6546fa9e4066Sahrens 			    optopt);
654799653d4eSeschrock 			usage(B_FALSE);
6548fa9e4066Sahrens 		}
6549fa9e4066Sahrens 	}
6550fa9e4066Sahrens 
6551fa9e4066Sahrens 	argc -= optind;
6552fa9e4066Sahrens 	argv += optind;
6553fa9e4066Sahrens 
6554fa9e4066Sahrens 	/* check number of arguments */
6555fa9e4066Sahrens 	if (do_all) {
6556da6c28aaSamw 		char *protocol = NULL;
65577f7322feSeschrock 
65589d9a58e3SEric Taylor 		if (op == OP_SHARE && argc > 0) {
65599d9a58e3SEric Taylor 			if (strcmp(argv[0], "nfs") != 0 &&
65609d9a58e3SEric Taylor 			    strcmp(argv[0], "smb") != 0) {
6561f3861e1aSahl 				(void) fprintf(stderr, gettext("share type "
6562ab003da8SJim Dunham 				    "must be 'nfs' or 'smb'\n"));
6563f3861e1aSahl 				usage(B_FALSE);
6564f3861e1aSahl 			}
6565da6c28aaSamw 			protocol = argv[0];
6566f3861e1aSahl 			argc--;
6567f3861e1aSahl 			argv++;
6568f3861e1aSahl 		}
6569f3861e1aSahl 
6570fa9e4066Sahrens 		if (argc != 0) {
6571fa9e4066Sahrens 			(void) fprintf(stderr, gettext("too many arguments\n"));
657299653d4eSeschrock 			usage(B_FALSE);
6573fa9e4066Sahrens 		}
6574fa9e4066Sahrens 
6575a8b6ddafSMark J Musante 		start_progress_timer();
6576591e0e13SSebastien Roy 		get_all_cb_t cb = { 0 };
6577591e0e13SSebastien Roy 		get_all_datasets(&cb, verbose);
65787f7322feSeschrock 
6579591e0e13SSebastien Roy 		if (cb.cb_used == 0)
65807f7322feSeschrock 			return (0);
65817f7322feSeschrock 
6582591e0e13SSebastien Roy 		if (op == OP_SHARE) {
65838a981c33SDaniel Hoffman 			sa_init_selective_arg_t sharearg;
6584591e0e13SSebastien Roy 			sharearg.zhandle_arr = cb.cb_handles;
6585591e0e13SSebastien Roy 			sharearg.zhandle_len = cb.cb_used;
6586591e0e13SSebastien Roy 			if ((ret = zfs_init_libshare_arg(g_zfs,
65878a981c33SDaniel Hoffman 			    SA_INIT_SHARE_API_SELECTIVE, &sharearg)) != SA_OK) {
6588591e0e13SSebastien Roy 				(void) fprintf(stderr, gettext(
6589591e0e13SSebastien Roy 				    "Could not initialize libshare, %d"), ret);
65908a981c33SDaniel Hoffman 				return (ret);
65918a981c33SDaniel Hoffman 			}
65927f7322feSeschrock 		}
65937f7322feSeschrock 
6594591e0e13SSebastien Roy 		share_mount_state_t share_mount_state = { 0 };
6595591e0e13SSebastien Roy 		share_mount_state.sm_op = op;
6596591e0e13SSebastien Roy 		share_mount_state.sm_verbose = verbose;
6597591e0e13SSebastien Roy 		share_mount_state.sm_flags = flags;
6598591e0e13SSebastien Roy 		share_mount_state.sm_options = options;
6599591e0e13SSebastien Roy 		share_mount_state.sm_proto = protocol;
6600591e0e13SSebastien Roy 		share_mount_state.sm_total = cb.cb_used;
6601591e0e13SSebastien Roy 		(void) mutex_init(&share_mount_state.sm_lock,
6602591e0e13SSebastien Roy 		    LOCK_NORMAL | LOCK_ERRORCHECK, NULL);
6603591e0e13SSebastien Roy 		/*
6604591e0e13SSebastien Roy 		 * libshare isn't mt-safe, so only do the operation in parallel
6605591e0e13SSebastien Roy 		 * if we're mounting.
6606591e0e13SSebastien Roy 		 */
6607591e0e13SSebastien Roy 		zfs_foreach_mountpoint(g_zfs, cb.cb_handles, cb.cb_used,
6608591e0e13SSebastien Roy 		    share_mount_one_cb, &share_mount_state, op == OP_MOUNT);
6609591e0e13SSebastien Roy 		ret = share_mount_state.sm_status;
6610591e0e13SSebastien Roy 
6611591e0e13SSebastien Roy 		for (int i = 0; i < cb.cb_used; i++)
6612591e0e13SSebastien Roy 			zfs_close(cb.cb_handles[i]);
6613591e0e13SSebastien Roy 		free(cb.cb_handles);
6614fa9e4066Sahrens 	} else if (argc == 0) {
6615fa9e4066Sahrens 		struct mnttab entry;
6616fa9e4066Sahrens 
6617ce5e3b86Srm160521 		if ((op == OP_SHARE) || (options != NULL)) {
6618fa9e4066Sahrens 			(void) fprintf(stderr, gettext("missing filesystem "
6619ce5e3b86Srm160521 			    "argument (specify -a for all)\n"));
662099653d4eSeschrock 			usage(B_FALSE);
6621fa9e4066Sahrens 		}
6622fa9e4066Sahrens 
6623fa9e4066Sahrens 		/*
6624fa9e4066Sahrens 		 * When mount is given no arguments, go through /etc/mnttab and
6625fa9e4066Sahrens 		 * display any active ZFS mounts.  We hide any snapshots, since
6626fa9e4066Sahrens 		 * they are controlled automatically.
6627fa9e4066Sahrens 		 */
6628fa9e4066Sahrens 		rewind(mnttab_file);
6629fa9e4066Sahrens 		while (getmntent(mnttab_file, &entry) == 0) {
6630fa9e4066Sahrens 			if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 ||
6631fa9e4066Sahrens 			    strchr(entry.mnt_special, '@') != NULL)
6632fa9e4066Sahrens 				continue;
6633fa9e4066Sahrens 
6634fa9e4066Sahrens 			(void) printf("%-30s  %s\n", entry.mnt_special,
6635fa9e4066Sahrens 			    entry.mnt_mountp);
6636fa9e4066Sahrens 		}
6637fa9e4066Sahrens 
6638fa9e4066Sahrens 	} else {
6639fa9e4066Sahrens 		zfs_handle_t *zhp;
6640fa9e4066Sahrens 
6641fa9e4066Sahrens 		if (argc > 1) {
6642fa9e4066Sahrens 			(void) fprintf(stderr,
6643fa9e4066Sahrens 			    gettext("too many arguments\n"));
664499653d4eSeschrock 			usage(B_FALSE);
6645fa9e4066Sahrens 		}
6646fa9e4066Sahrens 
66479d9a58e3SEric Taylor 		if ((zhp = zfs_open(g_zfs, argv[0],
66489d9a58e3SEric Taylor 		    ZFS_TYPE_FILESYSTEM)) == NULL) {
6649fa9e4066Sahrens 			ret = 1;
6650f3861e1aSahl 		} else {
6651da6c28aaSamw 			ret = share_mount_one(zhp, op, flags, NULL, B_TRUE,
6652f3861e1aSahl 			    options);
6653fa9e4066Sahrens 			zfs_close(zhp);
6654fa9e4066Sahrens 		}
6655fa9e4066Sahrens 	}
6656fa9e4066Sahrens 
6657fa9e4066Sahrens 	return (ret);
6658fa9e4066Sahrens }
6659fa9e4066Sahrens 
6660fa9e4066Sahrens /*
6661ab003da8SJim Dunham  * zfs mount -a [nfs]
6662fa9e4066Sahrens  * zfs mount filesystem
6663fa9e4066Sahrens  *
6664fa9e4066Sahrens  * Mount all filesystems, or mount the given filesystem.
6665fa9e4066Sahrens  */
6666fa9e4066Sahrens static int
zfs_do_mount(int argc,char ** argv)6667fa9e4066Sahrens zfs_do_mount(int argc, char **argv)
6668fa9e4066Sahrens {
6669f3861e1aSahl 	return (share_mount(OP_MOUNT, argc, argv));
6670fa9e4066Sahrens }
6671fa9e4066Sahrens 
6672fa9e4066Sahrens /*
6673ab003da8SJim Dunham  * zfs share -a [nfs | smb]
6674fa9e4066Sahrens  * zfs share filesystem
6675fa9e4066Sahrens  *
6676fa9e4066Sahrens  * Share all filesystems, or share the given filesystem.
6677fa9e4066Sahrens  */
6678fa9e4066Sahrens static int
zfs_do_share(int argc,char ** argv)6679fa9e4066Sahrens zfs_do_share(int argc, char **argv)
6680fa9e4066Sahrens {
6681f3861e1aSahl 	return (share_mount(OP_SHARE, argc, argv));
6682fa9e4066Sahrens }
6683fa9e4066Sahrens 
6684fa9e4066Sahrens typedef struct unshare_unmount_node {
6685fa9e4066Sahrens 	zfs_handle_t	*un_zhp;
6686fa9e4066Sahrens 	char		*un_mountp;
6687fa9e4066Sahrens 	uu_avl_node_t	un_avlnode;
6688fa9e4066Sahrens } unshare_unmount_node_t;
6689fa9e4066Sahrens 
6690fa9e4066Sahrens /* ARGSUSED */
6691fa9e4066Sahrens static int
unshare_unmount_compare(const void * larg,const void * rarg,void * unused)6692fa9e4066Sahrens unshare_unmount_compare(const void *larg, const void *rarg, void *unused)
6693fa9e4066Sahrens {
6694fa9e4066Sahrens 	const unshare_unmount_node_t *l = larg;
6695fa9e4066Sahrens 	const unshare_unmount_node_t *r = rarg;
6696fa9e4066Sahrens 
6697fa9e4066Sahrens 	return (strcmp(l->un_mountp, r->un_mountp));
6698fa9e4066Sahrens }
6699fa9e4066Sahrens 
6700fa9e4066Sahrens /*
6701fa9e4066Sahrens  * Convenience routine used by zfs_do_umount() and manual_unmount().  Given an
6702fa9e4066Sahrens  * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem,
6703fa9e4066Sahrens  * and unmount it appropriately.
6704fa9e4066Sahrens  */
6705fa9e4066Sahrens static int
unshare_unmount_path(int op,char * path,int flags,boolean_t is_manual)6706f3861e1aSahl unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
6707fa9e4066Sahrens {
6708fa9e4066Sahrens 	zfs_handle_t *zhp;
670905c998d6SRichard Lowe 	int ret = 0;
6710fa9e4066Sahrens 	struct stat64 statbuf;
6711fa9e4066Sahrens 	struct extmnttab entry;
6712f3861e1aSahl 	const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount";
6713c6ef114fSmmusante 	ino_t path_inode;
6714fa9e4066Sahrens 
6715fa9e4066Sahrens 	/*
6716fa9e4066Sahrens 	 * Search for the path in /etc/mnttab.  Rather than looking for the
6717fa9e4066Sahrens 	 * specific path, which can be fooled by non-standard paths (i.e. ".."
6718fa9e4066Sahrens 	 * or "//"), we stat() the path and search for the corresponding
6719fa9e4066Sahrens 	 * (major,minor) device pair.
6720fa9e4066Sahrens 	 */
6721fa9e4066Sahrens 	if (stat64(path, &statbuf) != 0) {
6722fa9e4066Sahrens 		(void) fprintf(stderr, gettext("cannot %s '%s': %s\n"),
6723fa9e4066Sahrens 		    cmdname, path, strerror(errno));
6724fa9e4066Sahrens 		return (1);
6725fa9e4066Sahrens 	}
6726c6ef114fSmmusante 	path_inode = statbuf.st_ino;
6727fa9e4066Sahrens 
6728fa9e4066Sahrens 	/*
6729fa9e4066Sahrens 	 * Search for the given (major,minor) pair in the mount table.
6730fa9e4066Sahrens 	 */
6731fa9e4066Sahrens 	rewind(mnttab_file);
6732fa9e4066Sahrens 	while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) {
6733fa9e4066Sahrens 		if (entry.mnt_major == major(statbuf.st_dev) &&
6734fa9e4066Sahrens 		    entry.mnt_minor == minor(statbuf.st_dev))
6735fa9e4066Sahrens 			break;
6736fa9e4066Sahrens 	}
6737fa9e4066Sahrens 	if (ret != 0) {
673811022c7cStimh 		if (op == OP_SHARE) {
6739fa9e4066Sahrens 			(void) fprintf(stderr, gettext("cannot %s '%s': not "
6740fa9e4066Sahrens 			    "currently mounted\n"), cmdname, path);
6741fa9e4066Sahrens 			return (1);
6742fa9e4066Sahrens 		}
674311022c7cStimh 		(void) fprintf(stderr, gettext("warning: %s not in mnttab\n"),
674411022c7cStimh 		    path);
674511022c7cStimh 		if ((ret = umount2(path, flags)) != 0)
674611022c7cStimh 			(void) fprintf(stderr, gettext("%s: %s\n"), path,
674711022c7cStimh 			    strerror(errno));
674811022c7cStimh 		return (ret != 0);
674911022c7cStimh 	}
6750fa9e4066Sahrens 
6751fa9e4066Sahrens 	if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) {
6752fa9e4066Sahrens 		(void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS "
6753fa9e4066Sahrens 		    "filesystem\n"), cmdname, path);
6754fa9e4066Sahrens 		return (1);
6755fa9e4066Sahrens 	}
6756fa9e4066Sahrens 
675799653d4eSeschrock 	if ((zhp = zfs_open(g_zfs, entry.mnt_special,
675899653d4eSeschrock 	    ZFS_TYPE_FILESYSTEM)) == NULL)
6759fa9e4066Sahrens 		return (1);
6760fa9e4066Sahrens 
6761c6ef114fSmmusante 	ret = 1;
6762d72c03d9SSachin Gaikwad 	if (stat64(entry.mnt_mountp, &statbuf) != 0) {
6763d72c03d9SSachin Gaikwad 		(void) fprintf(stderr, gettext("cannot %s '%s': %s\n"),
6764d72c03d9SSachin Gaikwad 		    cmdname, path, strerror(errno));
6765d72c03d9SSachin Gaikwad 		goto out;
6766d72c03d9SSachin Gaikwad 	} else if (statbuf.st_ino != path_inode) {
6767d72c03d9SSachin Gaikwad 		(void) fprintf(stderr, gettext("cannot "
6768d72c03d9SSachin Gaikwad 		    "%s '%s': not a mountpoint\n"), cmdname, path);
6769d72c03d9SSachin Gaikwad 		goto out;
6770d72c03d9SSachin Gaikwad 	}
6771d72c03d9SSachin Gaikwad 
6772c6ef114fSmmusante 	if (op == OP_SHARE) {
6773c6ef114fSmmusante 		char nfs_mnt_prop[ZFS_MAXPROPLEN];
6774c6ef114fSmmusante 		char smbshare_prop[ZFS_MAXPROPLEN];
6775c6ef114fSmmusante 
6776c6ef114fSmmusante 		verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, nfs_mnt_prop,
6777da6c28aaSamw 		    sizeof (nfs_mnt_prop), NULL, NULL, 0, B_FALSE) == 0);
6778c6ef114fSmmusante 		verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshare_prop,
6779da6c28aaSamw 		    sizeof (smbshare_prop), NULL, NULL, 0, B_FALSE) == 0);
6780fa9e4066Sahrens 
6781da6c28aaSamw 		if (strcmp(nfs_mnt_prop, "off") == 0 &&
6782da6c28aaSamw 		    strcmp(smbshare_prop, "off") == 0) {
6783fa9e4066Sahrens 			(void) fprintf(stderr, gettext("cannot unshare "
6784fa9e4066Sahrens 			    "'%s': legacy share\n"), path);
6785fa9e4066Sahrens 			(void) fprintf(stderr, gettext("use "
6786bbf21555SRichard Lowe 			    "unshare(8) to unshare this filesystem\n"));
6787da6c28aaSamw 		} else if (!zfs_is_shared(zhp)) {
6788fa9e4066Sahrens 			(void) fprintf(stderr, gettext("cannot unshare '%s': "
6789fa9e4066Sahrens 			    "not currently shared\n"), path);
6790fa9e4066Sahrens 		} else {
6791da6c28aaSamw 			ret = zfs_unshareall_bypath(zhp, path);
6792fa9e4066Sahrens 		}
6793fa9e4066Sahrens 	} else {
6794c6ef114fSmmusante 		char mtpt_prop[ZFS_MAXPROPLEN];
6795c6ef114fSmmusante 
6796c6ef114fSmmusante 		verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mtpt_prop,
6797c6ef114fSmmusante 		    sizeof (mtpt_prop), NULL, NULL, 0, B_FALSE) == 0);
6798c6ef114fSmmusante 
6799d72c03d9SSachin Gaikwad 		if (is_manual) {
68006b90ca48Slling 			ret = zfs_unmount(zhp, NULL, flags);
6801c6ef114fSmmusante 		} else if (strcmp(mtpt_prop, "legacy") == 0) {
6802fa9e4066Sahrens 			(void) fprintf(stderr, gettext("cannot unmount "
6803fa9e4066Sahrens 			    "'%s': legacy mountpoint\n"),
6804fa9e4066Sahrens 			    zfs_get_name(zhp));
6805bbf21555SRichard Lowe 			(void) fprintf(stderr, gettext("use umount(8) "
6806fa9e4066Sahrens 			    "to unmount this filesystem\n"));
6807fa9e4066Sahrens 		} else {
6808fa9e4066Sahrens 			ret = zfs_unmountall(zhp, flags);
6809fa9e4066Sahrens 		}
6810fa9e4066Sahrens 	}
6811fa9e4066Sahrens 
6812d72c03d9SSachin Gaikwad out:
6813fa9e4066Sahrens 	zfs_close(zhp);
6814fa9e4066Sahrens 
6815fa9e4066Sahrens 	return (ret != 0);
6816fa9e4066Sahrens }
6817fa9e4066Sahrens 
6818fa9e4066Sahrens /*
6819fa9e4066Sahrens  * Generic callback for unsharing or unmounting a filesystem.
6820fa9e4066Sahrens  */
6821fa9e4066Sahrens static int
unshare_unmount(int op,int argc,char ** argv)6822f3861e1aSahl unshare_unmount(int op, int argc, char **argv)
6823fa9e4066Sahrens {
6824fa9e4066Sahrens 	int do_all = 0;
6825fa9e4066Sahrens 	int flags = 0;
6826fa9e4066Sahrens 	int ret = 0;
68279d9a58e3SEric Taylor 	int c;
6828fa9e4066Sahrens 	zfs_handle_t *zhp;
6829ab003da8SJim Dunham 	char nfs_mnt_prop[ZFS_MAXPROPLEN];
6830da6c28aaSamw 	char sharesmb[ZFS_MAXPROPLEN];
6831fa9e4066Sahrens 
6832fa9e4066Sahrens 	/* check options */
6833f3861e1aSahl 	while ((c = getopt(argc, argv, op == OP_SHARE ? "a" : "af")) != -1) {
6834fa9e4066Sahrens 		switch (c) {
6835fa9e4066Sahrens 		case 'a':
6836fa9e4066Sahrens 			do_all = 1;
6837fa9e4066Sahrens 			break;
6838fa9e4066Sahrens 		case 'f':
6839fa9e4066Sahrens 			flags = MS_FORCE;
6840fa9e4066Sahrens 			break;
6841fa9e4066Sahrens 		case '?':
6842fa9e4066Sahrens 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
6843fa9e4066Sahrens 			    optopt);
684499653d4eSeschrock 			usage(B_FALSE);
6845fa9e4066Sahrens 		}
6846fa9e4066Sahrens 	}
6847fa9e4066Sahrens 
6848fa9e4066Sahrens 	argc -= optind;
6849fa9e4066Sahrens 	argv += optind;
6850fa9e4066Sahrens 
6851fa9e4066Sahrens 	if (do_all) {
6852fa9e4066Sahrens 		/*
6853fa9e4066Sahrens 		 * We could make use of zfs_for_each() to walk all datasets in
6854fa9e4066Sahrens 		 * the system, but this would be very inefficient, especially
6855fa9e4066Sahrens 		 * since we would have to linearly search /etc/mnttab for each
6856fa9e4066Sahrens 		 * one.  Instead, do one pass through /etc/mnttab looking for
6857fa9e4066Sahrens 		 * zfs entries and call zfs_unmount() for each one.
6858fa9e4066Sahrens 		 *
6859fa9e4066Sahrens 		 * Things get a little tricky if the administrator has created
6860fa9e4066Sahrens 		 * mountpoints beneath other ZFS filesystems.  In this case, we
6861fa9e4066Sahrens 		 * have to unmount the deepest filesystems first.  To accomplish
6862fa9e4066Sahrens 		 * this, we place all the mountpoints in an AVL tree sorted by
6863fa9e4066Sahrens 		 * the special type (dataset name), and walk the result in
6864fa9e4066Sahrens 		 * reverse to make sure to get any snapshots first.
6865fa9e4066Sahrens 		 */
6866fa9e4066Sahrens 		struct mnttab entry;
6867fa9e4066Sahrens 		uu_avl_pool_t *pool;
6868c16bcc45SIgor Kozhukhov 		uu_avl_t *tree = NULL;
6869fa9e4066Sahrens 		unshare_unmount_node_t *node;
6870fa9e4066Sahrens 		uu_avl_index_t idx;
6871fa9e4066Sahrens 		uu_avl_walk_t *walk;
6872fa9e4066Sahrens 
6873f3861e1aSahl 		if (argc != 0) {
6874f3861e1aSahl 			(void) fprintf(stderr, gettext("too many arguments\n"));
6875f3861e1aSahl 			usage(B_FALSE);
6876f3861e1aSahl 		}
6877f3861e1aSahl 
6878a8b6ddafSMark J Musante 		if (((pool = uu_avl_pool_create("unmount_pool",
6879fa9e4066Sahrens 		    sizeof (unshare_unmount_node_t),
6880fa9e4066Sahrens 		    offsetof(unshare_unmount_node_t, un_avlnode),
6881a8b6ddafSMark J Musante 		    unshare_unmount_compare, UU_DEFAULT)) == NULL) ||
6882a8b6ddafSMark J Musante 		    ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL))
6883a8b6ddafSMark J Musante 			nomem();
6884fa9e4066Sahrens 
6885fa9e4066Sahrens 		rewind(mnttab_file);
6886fa9e4066Sahrens 		while (getmntent(mnttab_file, &entry) == 0) {
6887fa9e4066Sahrens 
6888fa9e4066Sahrens 			/* ignore non-ZFS entries */
6889fa9e4066Sahrens 			if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
6890fa9e4066Sahrens 				continue;
6891fa9e4066Sahrens 
6892fa9e4066Sahrens 			/* ignore snapshots */
6893fa9e4066Sahrens 			if (strchr(entry.mnt_special, '@') != NULL)
6894fa9e4066Sahrens 				continue;
6895fa9e4066Sahrens 
689699653d4eSeschrock 			if ((zhp = zfs_open(g_zfs, entry.mnt_special,
6897fa9e4066Sahrens 			    ZFS_TYPE_FILESYSTEM)) == NULL) {
6898fa9e4066Sahrens 				ret = 1;
6899fa9e4066Sahrens 				continue;
6900fa9e4066Sahrens 			}
6901fa9e4066Sahrens 
69028808ac5dSYuri Pankov 			/*
69038808ac5dSYuri Pankov 			 * Ignore datasets that are excluded/restricted by
69048808ac5dSYuri Pankov 			 * parent pool name.
69058808ac5dSYuri Pankov 			 */
69068808ac5dSYuri Pankov 			if (zpool_skip_pool(zfs_get_pool_name(zhp))) {
69078808ac5dSYuri Pankov 				zfs_close(zhp);
69088808ac5dSYuri Pankov 				continue;
69098808ac5dSYuri Pankov 			}
69108808ac5dSYuri Pankov 
6911da6c28aaSamw 			switch (op) {
6912da6c28aaSamw 			case OP_SHARE:
6913da6c28aaSamw 				verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,
6914ab003da8SJim Dunham 				    nfs_mnt_prop,
6915ab003da8SJim Dunham 				    sizeof (nfs_mnt_prop),
6916da6c28aaSamw 				    NULL, NULL, 0, B_FALSE) == 0);
6917ab003da8SJim Dunham 				if (strcmp(nfs_mnt_prop, "off") != 0)
6918da6c28aaSamw 					break;
6919da6c28aaSamw 				verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,
6920ab003da8SJim Dunham 				    nfs_mnt_prop,
6921ab003da8SJim Dunham 				    sizeof (nfs_mnt_prop),
6922da6c28aaSamw 				    NULL, NULL, 0, B_FALSE) == 0);
6923ab003da8SJim Dunham 				if (strcmp(nfs_mnt_prop, "off") == 0)
6924fa9e4066Sahrens 					continue;
6925da6c28aaSamw 				break;
6926da6c28aaSamw 			case OP_MOUNT:
6927da6c28aaSamw 				/* Ignore legacy mounts */
6928da6c28aaSamw 				verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
6929ab003da8SJim Dunham 				    nfs_mnt_prop,
6930ab003da8SJim Dunham 				    sizeof (nfs_mnt_prop),
6931da6c28aaSamw 				    NULL, NULL, 0, B_FALSE) == 0);
6932ab003da8SJim Dunham 				if (strcmp(nfs_mnt_prop, "legacy") == 0)
6933da6c28aaSamw 					continue;
6934a227b7f4Shs24103 				/* Ignore canmount=noauto mounts */
6935a227b7f4Shs24103 				if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) ==
6936a227b7f4Shs24103 				    ZFS_CANMOUNT_NOAUTO)
6937a227b7f4Shs24103 					continue;
6938da6c28aaSamw 			default:
6939da6c28aaSamw 				break;
6940fa9e4066Sahrens 			}
6941fa9e4066Sahrens 
6942fa9e4066Sahrens 			node = safe_malloc(sizeof (unshare_unmount_node_t));
6943fa9e4066Sahrens 			node->un_zhp = zhp;
6944a8b6ddafSMark J Musante 			node->un_mountp = safe_strdup(entry.mnt_mountp);
6945fa9e4066Sahrens 
6946fa9e4066Sahrens 			uu_avl_node_init(node, &node->un_avlnode, pool);
6947fa9e4066Sahrens 
6948fa9e4066Sahrens 			if (uu_avl_find(tree, node, NULL, &idx) == NULL) {
6949fa9e4066Sahrens 				uu_avl_insert(tree, node, idx);
6950fa9e4066Sahrens 			} else {
6951fa9e4066Sahrens 				zfs_close(node->un_zhp);
6952fa9e4066Sahrens 				free(node->un_mountp);
6953fa9e4066Sahrens 				free(node);
6954fa9e4066Sahrens 			}
6955fa9e4066Sahrens 		}
6956fa9e4066Sahrens 
6957fa9e4066Sahrens 		/*
6958b8dc1b43SJoyce McIntosh 		 * Initialize libshare SA_INIT_SHARE_API_SELECTIVE here
6959b8dc1b43SJoyce McIntosh 		 * to avoid unnecessary load/unload of the libshare API
6960b8dc1b43SJoyce McIntosh 		 * per shared dataset downstream.
6961b8dc1b43SJoyce McIntosh 		 */
6962b8dc1b43SJoyce McIntosh 		if (op == OP_SHARE) {
6963b8dc1b43SJoyce McIntosh 			get_all_cb_t dslist = { 0 };
6964b8dc1b43SJoyce McIntosh 			get_all_datasets(&dslist, B_FALSE);
6965b8dc1b43SJoyce McIntosh 
6966b8dc1b43SJoyce McIntosh 			if (dslist.cb_used != 0) {
6967b8dc1b43SJoyce McIntosh 				sa_init_selective_arg_t sharearg;
6968b8dc1b43SJoyce McIntosh 				sharearg.zhandle_arr = dslist.cb_handles;
6969b8dc1b43SJoyce McIntosh 				sharearg.zhandle_len = dslist.cb_used;
6970b8dc1b43SJoyce McIntosh 				if ((ret = zfs_init_libshare_arg(g_zfs,
6971b8dc1b43SJoyce McIntosh 				    SA_INIT_SHARE_API_SELECTIVE, &sharearg)) !=
6972b8dc1b43SJoyce McIntosh 				    SA_OK) {
6973b8dc1b43SJoyce McIntosh 					(void) fprintf(stderr, gettext(
6974b8dc1b43SJoyce McIntosh 					    "Could not initialize libshare, "
6975b8dc1b43SJoyce McIntosh 					    "%d"), ret);
6976b8dc1b43SJoyce McIntosh 					return (1);
6977b8dc1b43SJoyce McIntosh 				}
6978b8dc1b43SJoyce McIntosh 			}
6979b8dc1b43SJoyce McIntosh 		}
6980b8dc1b43SJoyce McIntosh 
6981b8dc1b43SJoyce McIntosh 		/*
6982fa9e4066Sahrens 		 * Walk the AVL tree in reverse, unmounting each filesystem and
6983fa9e4066Sahrens 		 * removing it from the AVL tree in the process.
6984fa9e4066Sahrens 		 */
6985fa9e4066Sahrens 		if ((walk = uu_avl_walk_start(tree,
6986a8b6ddafSMark J Musante 		    UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL)
6987a8b6ddafSMark J Musante 			nomem();
6988fa9e4066Sahrens 
6989fa9e4066Sahrens 		while ((node = uu_avl_walk_next(walk)) != NULL) {
6990fa9e4066Sahrens 			uu_avl_remove(tree, node);
6991fa9e4066Sahrens 
6992f3861e1aSahl 			switch (op) {
6993fa9e4066Sahrens 			case OP_SHARE:
6994da6c28aaSamw 				if (zfs_unshareall_bypath(node->un_zhp,
6995fa9e4066Sahrens 				    node->un_mountp) != 0)
6996fa9e4066Sahrens 					ret = 1;
6997fa9e4066Sahrens 				break;
6998fa9e4066Sahrens 
6999fa9e4066Sahrens 			case OP_MOUNT:
7000fa9e4066Sahrens 				if (zfs_unmount(node->un_zhp,
7001fa9e4066Sahrens 				    node->un_mountp, flags) != 0)
7002fa9e4066Sahrens 					ret = 1;
7003fa9e4066Sahrens 				break;
7004fa9e4066Sahrens 			}
7005fa9e4066Sahrens 
7006fa9e4066Sahrens 			zfs_close(node->un_zhp);
7007fa9e4066Sahrens 			free(node->un_mountp);
7008fa9e4066Sahrens 			free(node);
7009fa9e4066Sahrens 		}
7010fa9e4066Sahrens 
7011fa9e4066Sahrens 		uu_avl_walk_end(walk);
7012fa9e4066Sahrens 		uu_avl_destroy(tree);
7013fa9e4066Sahrens 		uu_avl_pool_destroy(pool);
7014f3861e1aSahl 
7015fa9e4066Sahrens 	} else {
7016f3861e1aSahl 		if (argc != 1) {
7017f3861e1aSahl 			if (argc == 0)
7018f3861e1aSahl 				(void) fprintf(stderr,
7019f3861e1aSahl 				    gettext("missing filesystem argument\n"));
7020f3861e1aSahl 			else
7021f3861e1aSahl 				(void) fprintf(stderr,
7022f3861e1aSahl 				    gettext("too many arguments\n"));
7023f3861e1aSahl 			usage(B_FALSE);
7024f3861e1aSahl 		}
7025f3861e1aSahl 
7026fa9e4066Sahrens 		/*
7027fa9e4066Sahrens 		 * We have an argument, but it may be a full path or a ZFS
7028fa9e4066Sahrens 		 * filesystem.  Pass full paths off to unmount_path() (shared by
7029fa9e4066Sahrens 		 * manual_unmount), otherwise open the filesystem and pass to
7030fa9e4066Sahrens 		 * zfs_unmount().
7031fa9e4066Sahrens 		 */
7032fa9e4066Sahrens 		if (argv[0][0] == '/')
7033f3861e1aSahl 			return (unshare_unmount_path(op, argv[0],
703499653d4eSeschrock 			    flags, B_FALSE));
7035fa9e4066Sahrens 
70369d9a58e3SEric Taylor 		if ((zhp = zfs_open(g_zfs, argv[0],
70379d9a58e3SEric Taylor 		    ZFS_TYPE_FILESYSTEM)) == NULL)
7038fa9e4066Sahrens 			return (1);
7039fa9e4066Sahrens 
7040f3861e1aSahl 		verify(zfs_prop_get(zhp, op == OP_SHARE ?
7041da6c28aaSamw 		    ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT,
7042ab003da8SJim Dunham 		    nfs_mnt_prop, sizeof (nfs_mnt_prop), NULL,
7043da6c28aaSamw 		    NULL, 0, B_FALSE) == 0);
7044fa9e4066Sahrens 
7045f3861e1aSahl 		switch (op) {
7046fa9e4066Sahrens 		case OP_SHARE:
7047da6c28aaSamw 			verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,
7048ab003da8SJim Dunham 			    nfs_mnt_prop,
7049ab003da8SJim Dunham 			    sizeof (nfs_mnt_prop),
7050da6c28aaSamw 			    NULL, NULL, 0, B_FALSE) == 0);
7051da6c28aaSamw 			verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,
7052da6c28aaSamw 			    sharesmb, sizeof (sharesmb), NULL, NULL,
7053da6c28aaSamw 			    0, B_FALSE) == 0);
7054da6c28aaSamw 
7055ab003da8SJim Dunham 			if (strcmp(nfs_mnt_prop, "off") == 0 &&
7056da6c28aaSamw 			    strcmp(sharesmb, "off") == 0) {
7057f3861e1aSahl 				(void) fprintf(stderr, gettext("cannot "
7058f3861e1aSahl 				    "unshare '%s': legacy share\n"),
7059fa9e4066Sahrens 				    zfs_get_name(zhp));
7060f3861e1aSahl 				(void) fprintf(stderr, gettext("use "
7061bbf21555SRichard Lowe 				    "unshare(8) to unshare this "
7062f3861e1aSahl 				    "filesystem\n"));
7063fa9e4066Sahrens 				ret = 1;
7064da6c28aaSamw 			} else if (!zfs_is_shared(zhp)) {
7065f3861e1aSahl 				(void) fprintf(stderr, gettext("cannot "
7066f3861e1aSahl 				    "unshare '%s': not currently "
7067f3861e1aSahl 				    "shared\n"), zfs_get_name(zhp));
7068f3861e1aSahl 				ret = 1;
7069da6c28aaSamw 			} else if (zfs_unshareall(zhp) != 0) {
7070fa9e4066Sahrens 				ret = 1;
7071fa9e4066Sahrens 			}
7072fa9e4066Sahrens 			break;
7073fa9e4066Sahrens 
7074fa9e4066Sahrens 		case OP_MOUNT:
7075ab003da8SJim Dunham 			if (strcmp(nfs_mnt_prop, "legacy") == 0) {
7076f3861e1aSahl 				(void) fprintf(stderr, gettext("cannot "
7077f3861e1aSahl 				    "unmount '%s': legacy "
7078f3861e1aSahl 				    "mountpoint\n"), zfs_get_name(zhp));
7079f3861e1aSahl 				(void) fprintf(stderr, gettext("use "
7080bbf21555SRichard Lowe 				    "umount(8) to unmount this "
7081f3861e1aSahl 				    "filesystem\n"));
7082fa9e4066Sahrens 				ret = 1;
7083fa9e4066Sahrens 			} else if (!zfs_is_mounted(zhp, NULL)) {
7084f3861e1aSahl 				(void) fprintf(stderr, gettext("cannot "
7085f3861e1aSahl 				    "unmount '%s': not currently "
7086f3861e1aSahl 				    "mounted\n"),
7087fa9e4066Sahrens 				    zfs_get_name(zhp));
7088fa9e4066Sahrens 				ret = 1;
7089fa9e4066Sahrens 			} else if (zfs_unmountall(zhp, flags) != 0) {
7090fa9e4066Sahrens 				ret = 1;
7091fa9e4066Sahrens 			}
7092f3861e1aSahl 			break;
7093f3861e1aSahl 		}
7094fa9e4066Sahrens 
7095fa9e4066Sahrens 		zfs_close(zhp);
7096fa9e4066Sahrens 	}
7097fa9e4066Sahrens 
7098fa9e4066Sahrens 	return (ret);
7099fa9e4066Sahrens }
7100fa9e4066Sahrens 
7101fa9e4066Sahrens /*
7102fa9e4066Sahrens  * zfs unmount -a
7103fa9e4066Sahrens  * zfs unmount filesystem
7104fa9e4066Sahrens  *
7105fa9e4066Sahrens  * Unmount all filesystems, or a specific ZFS filesystem.
7106fa9e4066Sahrens  */
7107fa9e4066Sahrens static int
zfs_do_unmount(int argc,char ** argv)7108fa9e4066Sahrens zfs_do_unmount(int argc, char **argv)
7109fa9e4066Sahrens {
7110fa9e4066Sahrens 	return (unshare_unmount(OP_MOUNT, argc, argv));
7111fa9e4066Sahrens }
7112fa9e4066Sahrens 
7113fa9e4066Sahrens /*
7114fa9e4066Sahrens  * zfs unshare -a
7115fa9e4066Sahrens  * zfs unshare filesystem
7116fa9e4066Sahrens  *
7117fa9e4066Sahrens  * Unshare all filesystems, or a specific ZFS filesystem.
7118fa9e4066Sahrens  */
7119fa9e4066Sahrens static int
zfs_do_unshare(int argc,char ** argv)7120fa9e4066Sahrens zfs_do_unshare(int argc, char **argv)
7121fa9e4066Sahrens {
7122fa9e4066Sahrens 	return (unshare_unmount(OP_SHARE, argc, argv));
7123fa9e4066Sahrens }
7124fa9e4066Sahrens 
7125fa9e4066Sahrens /*
7126fa9e4066Sahrens  * Called when invoked as /etc/fs/zfs/mount.  Do the mount if the mountpoint is
7127fa9e4066Sahrens  * 'legacy'.  Otherwise, complain that use should be using 'zfs mount'.
7128fa9e4066Sahrens  */
7129fa9e4066Sahrens static int
manual_mount(int argc,char ** argv)7130fa9e4066Sahrens manual_mount(int argc, char **argv)
7131fa9e4066Sahrens {
7132fa9e4066Sahrens 	zfs_handle_t *zhp;
7133fa9e4066Sahrens 	char mountpoint[ZFS_MAXPROPLEN];
7134fa9e4066Sahrens 	char mntopts[MNT_LINE_MAX] = { '\0' };
713505c998d6SRichard Lowe 	int ret = 0;
7136fa9e4066Sahrens 	int c;
7137fa9e4066Sahrens 	int flags = 0;
7138fa9e4066Sahrens 	char *dataset, *path;
7139fa9e4066Sahrens 
7140fa9e4066Sahrens 	/* check options */
7141ea8dc4b6Seschrock 	while ((c = getopt(argc, argv, ":mo:O")) != -1) {
7142fa9e4066Sahrens 		switch (c) {
7143fa9e4066Sahrens 		case 'o':
7144fa9e4066Sahrens 			(void) strlcpy(mntopts, optarg, sizeof (mntopts));
7145fa9e4066Sahrens 			break;
7146fa9e4066Sahrens 		case 'O':
7147fa9e4066Sahrens 			flags |= MS_OVERLAY;
7148fa9e4066Sahrens 			break;
7149ea8dc4b6Seschrock 		case 'm':
7150ea8dc4b6Seschrock 			flags |= MS_NOMNTTAB;
7151ea8dc4b6Seschrock 			break;
7152fa9e4066Sahrens 		case ':':
7153fa9e4066Sahrens 			(void) fprintf(stderr, gettext("missing argument for "
7154fa9e4066Sahrens 			    "'%c' option\n"), optopt);
715599653d4eSeschrock 			usage(B_FALSE);
7156fa9e4066Sahrens 			break;
7157fa9e4066Sahrens 		case '?':
7158fa9e4066Sahrens 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
7159fa9e4066Sahrens 			    optopt);
7160fa9e4066Sahrens 			(void) fprintf(stderr, gettext("usage: mount [-o opts] "
7161fa9e4066Sahrens 			    "<path>\n"));
7162fa9e4066Sahrens 			return (2);
7163fa9e4066Sahrens 		}
7164fa9e4066Sahrens 	}
7165fa9e4066Sahrens 
7166fa9e4066Sahrens 	argc -= optind;
7167fa9e4066Sahrens 	argv += optind;
7168fa9e4066Sahrens 
7169fa9e4066Sahrens 	/* check that we only have two arguments */
7170fa9e4066Sahrens 	if (argc != 2) {
7171fa9e4066Sahrens 		if (argc == 0)
7172fa9e4066Sahrens 			(void) fprintf(stderr, gettext("missing dataset "
7173fa9e4066Sahrens 			    "argument\n"));
7174fa9e4066Sahrens 		else if (argc == 1)
7175fa9e4066Sahrens 			(void) fprintf(stderr,
7176fa9e4066Sahrens 			    gettext("missing mountpoint argument\n"));
7177fa9e4066Sahrens 		else
7178fa9e4066Sahrens 			(void) fprintf(stderr, gettext("too many arguments\n"));
7179fa9e4066Sahrens 		(void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n");
7180fa9e4066Sahrens 		return (2);
7181fa9e4066Sahrens 	}
7182fa9e4066Sahrens 
7183fa9e4066Sahrens 	dataset = argv[0];
7184fa9e4066Sahrens 	path = argv[1];
7185fa9e4066Sahrens 
7186fa9e4066Sahrens 	/* try to open the dataset */
718799653d4eSeschrock 	if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL)
7188fa9e4066Sahrens 		return (1);
7189fa9e4066Sahrens 
7190fa9e4066Sahrens 	(void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
719199653d4eSeschrock 	    sizeof (mountpoint), NULL, NULL, 0, B_FALSE);
7192fa9e4066Sahrens 
7193fa9e4066Sahrens 	/* check for legacy mountpoint and complain appropriately */
7194fa9e4066Sahrens 	ret = 0;
7195fa9e4066Sahrens 	if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
7196fa9e4066Sahrens 		if (mount(dataset, path, MS_OPTIONSTR | flags, MNTTYPE_ZFS,
7197fa9e4066Sahrens 		    NULL, 0, mntopts, sizeof (mntopts)) != 0) {
7198fa9e4066Sahrens 			(void) fprintf(stderr, gettext("mount failed: %s\n"),
7199fa9e4066Sahrens 			    strerror(errno));
7200fa9e4066Sahrens 			ret = 1;
7201fa9e4066Sahrens 		}
7202fa9e4066Sahrens 	} else {
7203fa9e4066Sahrens 		(void) fprintf(stderr, gettext("filesystem '%s' cannot be "
7204fa9e4066Sahrens 		    "mounted using 'mount -F zfs'\n"), dataset);
7205fa9e4066Sahrens 		(void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' "
7206fa9e4066Sahrens 		    "instead.\n"), path);
7207fa9e4066Sahrens 		(void) fprintf(stderr, gettext("If you must use 'mount -F zfs' "
7208fa9e4066Sahrens 		    "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n"));
7209bbf21555SRichard Lowe 		(void) fprintf(stderr, gettext("See zfs(8) for more "
7210fa9e4066Sahrens 		    "information.\n"));
7211fa9e4066Sahrens 		ret = 1;
7212fa9e4066Sahrens 	}
7213fa9e4066Sahrens 
7214fa9e4066Sahrens 	return (ret);
7215fa9e4066Sahrens }
7216fa9e4066Sahrens 
7217fa9e4066Sahrens /*
7218fa9e4066Sahrens  * Called when invoked as /etc/fs/zfs/umount.  Unlike a manual mount, we allow
7219fa9e4066Sahrens  * unmounts of non-legacy filesystems, as this is the dominant administrative
7220fa9e4066Sahrens  * interface.
7221fa9e4066Sahrens  */
7222fa9e4066Sahrens static int
manual_unmount(int argc,char ** argv)7223fa9e4066Sahrens manual_unmount(int argc, char **argv)
7224fa9e4066Sahrens {
7225fa9e4066Sahrens 	int flags = 0;
7226fa9e4066Sahrens 	int c;
7227fa9e4066Sahrens 
7228fa9e4066Sahrens 	/* check options */
7229fa9e4066Sahrens 	while ((c = getopt(argc, argv, "f")) != -1) {
7230fa9e4066Sahrens 		switch (c) {
7231fa9e4066Sahrens 		case 'f':
7232fa9e4066Sahrens 			flags = MS_FORCE;
7233fa9e4066Sahrens 			break;
7234fa9e4066Sahrens 		case '?':
7235fa9e4066Sahrens 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
7236fa9e4066Sahrens 			    optopt);
7237fa9e4066Sahrens 			(void) fprintf(stderr, gettext("usage: unmount [-f] "
7238fa9e4066Sahrens 			    "<path>\n"));
7239fa9e4066Sahrens 			return (2);
7240fa9e4066Sahrens 		}
7241fa9e4066Sahrens 	}
7242fa9e4066Sahrens 
7243fa9e4066Sahrens 	argc -= optind;
7244fa9e4066Sahrens 	argv += optind;
7245fa9e4066Sahrens 
7246fa9e4066Sahrens 	/* check arguments */
7247fa9e4066Sahrens 	if (argc != 1) {
7248fa9e4066Sahrens 		if (argc == 0)
7249fa9e4066Sahrens 			(void) fprintf(stderr, gettext("missing path "
7250fa9e4066Sahrens 			    "argument\n"));
7251fa9e4066Sahrens 		else
7252fa9e4066Sahrens 			(void) fprintf(stderr, gettext("too many arguments\n"));
7253fa9e4066Sahrens 		(void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n"));
7254fa9e4066Sahrens 		return (2);
7255fa9e4066Sahrens 	}
7256fa9e4066Sahrens 
725799653d4eSeschrock 	return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE));
7258fa9e4066Sahrens }
7259fa9e4066Sahrens 
7260fa9e4066Sahrens static int
find_command_idx(char * command,int * idx)7261ecd6cf80Smarks find_command_idx(char *command, int *idx)
7262ecd6cf80Smarks {
7263ecd6cf80Smarks 	int i;
7264ecd6cf80Smarks 
7265ecd6cf80Smarks 	for (i = 0; i < NCOMMAND; i++) {
7266ecd6cf80Smarks 		if (command_table[i].name == NULL)
7267ecd6cf80Smarks 			continue;
7268ecd6cf80Smarks 
7269ecd6cf80Smarks 		if (strcmp(command, command_table[i].name) == 0) {
7270ecd6cf80Smarks 			*idx = i;
7271ecd6cf80Smarks 			return (0);
7272ecd6cf80Smarks 		}
7273ecd6cf80Smarks 	}
7274ecd6cf80Smarks 	return (1);
7275ecd6cf80Smarks }
7276ecd6cf80Smarks 
727799d5e173STim Haley static int
zfs_do_diff(int argc,char ** argv)727899d5e173STim Haley zfs_do_diff(int argc, char **argv)
727999d5e173STim Haley {
728099d5e173STim Haley 	zfs_handle_t *zhp;
728199d5e173STim Haley 	int flags = 0;
728299d5e173STim Haley 	char *tosnap = NULL;
728399d5e173STim Haley 	char *fromsnap = NULL;
728499d5e173STim Haley 	char *atp, *copy;
728505c998d6SRichard Lowe 	int err = 0;
728699d5e173STim Haley 	int c;
728799d5e173STim Haley 
728899d5e173STim Haley 	while ((c = getopt(argc, argv, "FHt")) != -1) {
728999d5e173STim Haley 		switch (c) {
729099d5e173STim Haley 		case 'F':
729199d5e173STim Haley 			flags |= ZFS_DIFF_CLASSIFY;
729299d5e173STim Haley 			break;
729399d5e173STim Haley 		case 'H':
729499d5e173STim Haley 			flags |= ZFS_DIFF_PARSEABLE;
729599d5e173STim Haley 			break;
729699d5e173STim Haley 		case 't':
729799d5e173STim Haley 			flags |= ZFS_DIFF_TIMESTAMP;
729899d5e173STim Haley 			break;
729999d5e173STim Haley 		default:
730099d5e173STim Haley 			(void) fprintf(stderr,
730199d5e173STim Haley 			    gettext("invalid option '%c'\n"), optopt);
730299d5e173STim Haley 			usage(B_FALSE);
730399d5e173STim Haley 		}
730499d5e173STim Haley 	}
730599d5e173STim Haley 
730699d5e173STim Haley 	argc -= optind;
730799d5e173STim Haley 	argv += optind;
730899d5e173STim Haley 
730999d5e173STim Haley 	if (argc < 1) {
731099d5e173STim Haley 		(void) fprintf(stderr,
731199d5e173STim Haley 		    gettext("must provide at least one snapshot name\n"));
731299d5e173STim Haley 		usage(B_FALSE);
731399d5e173STim Haley 	}
731499d5e173STim Haley 
731599d5e173STim Haley 	if (argc > 2) {
731699d5e173STim Haley 		(void) fprintf(stderr, gettext("too many arguments\n"));
731799d5e173STim Haley 		usage(B_FALSE);
731899d5e173STim Haley 	}
731999d5e173STim Haley 
732099d5e173STim Haley 	fromsnap = argv[0];
732199d5e173STim Haley 	tosnap = (argc == 2) ? argv[1] : NULL;
732299d5e173STim Haley 
732399d5e173STim Haley 	copy = NULL;
732499d5e173STim Haley 	if (*fromsnap != '@')
732599d5e173STim Haley 		copy = strdup(fromsnap);
732699d5e173STim Haley 	else if (tosnap)
732799d5e173STim Haley 		copy = strdup(tosnap);
732899d5e173STim Haley 	if (copy == NULL)
732999d5e173STim Haley 		usage(B_FALSE);
733099d5e173STim Haley 
7331c16bcc45SIgor Kozhukhov 	if ((atp = strchr(copy, '@')) != NULL)
733299d5e173STim Haley 		*atp = '\0';
733399d5e173STim Haley 
7334*606d76deScao 	if ((zhp = zfs_open(g_zfs, copy, ZFS_TYPE_FILESYSTEM)) == NULL) {
7335*606d76deScao 		free(copy);
733699d5e173STim Haley 		return (1);
7337*606d76deScao 	}
733899d5e173STim Haley 	free(copy);
733999d5e173STim Haley 
734099d5e173STim Haley 	/*
734199d5e173STim Haley 	 * Ignore SIGPIPE so that the library can give us
734299d5e173STim Haley 	 * information on any failure
734399d5e173STim Haley 	 */
734499d5e173STim Haley 	(void) sigignore(SIGPIPE);
734599d5e173STim Haley 
734699d5e173STim Haley 	err = zfs_show_diffs(zhp, STDOUT_FILENO, fromsnap, tosnap, flags);
734799d5e173STim Haley 
734899d5e173STim Haley 	zfs_close(zhp);
734999d5e173STim Haley 
735099d5e173STim Haley 	return (err != 0);
735199d5e173STim Haley }
735299d5e173STim Haley 
73530b2e8253Sloli10K /*
73540b2e8253Sloli10K  * zfs remap <filesystem | volume>
73550b2e8253Sloli10K  *
73560b2e8253Sloli10K  * Remap the indirect blocks in the given fileystem or volume.
73570b2e8253Sloli10K  */
73585cabbc6bSPrashanth Sreenivasa static int
zfs_do_remap(int argc,char ** argv)73595cabbc6bSPrashanth Sreenivasa zfs_do_remap(int argc, char **argv)
73605cabbc6bSPrashanth Sreenivasa {
73615cabbc6bSPrashanth Sreenivasa 	const char *fsname;
73625cabbc6bSPrashanth Sreenivasa 	int err = 0;
73630b2e8253Sloli10K 	int c;
73640b2e8253Sloli10K 
73650b2e8253Sloli10K 	/* check options */
73660b2e8253Sloli10K 	while ((c = getopt(argc, argv, "")) != -1) {
73670b2e8253Sloli10K 		switch (c) {
73680b2e8253Sloli10K 		case '?':
73690b2e8253Sloli10K 			(void) fprintf(stderr,
73700b2e8253Sloli10K 			    gettext("invalid option '%c'\n"), optopt);
73710b2e8253Sloli10K 			usage(B_FALSE);
73720b2e8253Sloli10K 		}
73730b2e8253Sloli10K 	}
73740b2e8253Sloli10K 
73755cabbc6bSPrashanth Sreenivasa 	if (argc != 2) {
73765cabbc6bSPrashanth Sreenivasa 		(void) fprintf(stderr, gettext("wrong number of arguments\n"));
73775cabbc6bSPrashanth Sreenivasa 		usage(B_FALSE);
73785cabbc6bSPrashanth Sreenivasa 	}
73795cabbc6bSPrashanth Sreenivasa 
73805cabbc6bSPrashanth Sreenivasa 	fsname = argv[1];
73815cabbc6bSPrashanth Sreenivasa 	err = zfs_remap_indirects(g_zfs, fsname);
73825cabbc6bSPrashanth Sreenivasa 
73835cabbc6bSPrashanth Sreenivasa 	return (err);
73845cabbc6bSPrashanth Sreenivasa }
73855cabbc6bSPrashanth Sreenivasa 
738678f17100SMatthew Ahrens /*
738778f17100SMatthew Ahrens  * zfs bookmark <fs@snap> <fs#bmark>
738878f17100SMatthew Ahrens  *
738978f17100SMatthew Ahrens  * Creates a bookmark with the given name from the given snapshot.
739078f17100SMatthew Ahrens  */
739178f17100SMatthew Ahrens static int
zfs_do_bookmark(int argc,char ** argv)739278f17100SMatthew Ahrens zfs_do_bookmark(int argc, char **argv)
739378f17100SMatthew Ahrens {
73949adfa60dSMatthew Ahrens 	char snapname[ZFS_MAX_DATASET_NAME_LEN];
739578f17100SMatthew Ahrens 	zfs_handle_t *zhp;
739678f17100SMatthew Ahrens 	nvlist_t *nvl;
739778f17100SMatthew Ahrens 	int ret = 0;
739878f17100SMatthew Ahrens 	int c;
739978f17100SMatthew Ahrens 
740078f17100SMatthew Ahrens 	/* check options */
740178f17100SMatthew Ahrens 	while ((c = getopt(argc, argv, "")) != -1) {
740278f17100SMatthew Ahrens 		switch (c) {
740378f17100SMatthew Ahrens 		case '?':
740478f17100SMatthew Ahrens 			(void) fprintf(stderr,
740578f17100SMatthew Ahrens 			    gettext("invalid option '%c'\n"), optopt);
740678f17100SMatthew Ahrens 			goto usage;
740778f17100SMatthew Ahrens 		}
740878f17100SMatthew Ahrens 	}
740978f17100SMatthew Ahrens 
741078f17100SMatthew Ahrens 	argc -= optind;
741178f17100SMatthew Ahrens 	argv += optind;
741278f17100SMatthew Ahrens 
741378f17100SMatthew Ahrens 	/* check number of arguments */
741478f17100SMatthew Ahrens 	if (argc < 1) {
741578f17100SMatthew Ahrens 		(void) fprintf(stderr, gettext("missing snapshot argument\n"));
741678f17100SMatthew Ahrens 		goto usage;
741778f17100SMatthew Ahrens 	}
741878f17100SMatthew Ahrens 	if (argc < 2) {
741978f17100SMatthew Ahrens 		(void) fprintf(stderr, gettext("missing bookmark argument\n"));
742078f17100SMatthew Ahrens 		goto usage;
742178f17100SMatthew Ahrens 	}
742278f17100SMatthew Ahrens 
742378f17100SMatthew Ahrens 	if (strchr(argv[1], '#') == NULL) {
742478f17100SMatthew Ahrens 		(void) fprintf(stderr,
742578f17100SMatthew Ahrens 		    gettext("invalid bookmark name '%s' -- "
742678f17100SMatthew Ahrens 		    "must contain a '#'\n"), argv[1]);
742778f17100SMatthew Ahrens 		goto usage;
742878f17100SMatthew Ahrens 	}
742978f17100SMatthew Ahrens 
743078f17100SMatthew Ahrens 	if (argv[0][0] == '@') {
743178f17100SMatthew Ahrens 		/*
743278f17100SMatthew Ahrens 		 * Snapshot name begins with @.
743378f17100SMatthew Ahrens 		 * Default to same fs as bookmark.
743478f17100SMatthew Ahrens 		 */
743578f17100SMatthew Ahrens 		(void) strncpy(snapname, argv[1], sizeof (snapname));
743678f17100SMatthew Ahrens 		*strchr(snapname, '#') = '\0';
743778f17100SMatthew Ahrens 		(void) strlcat(snapname, argv[0], sizeof (snapname));
743878f17100SMatthew Ahrens 	} else {
74393382f241Sluozhengzheng 		(void) strlcpy(snapname, argv[0], sizeof (snapname));
744078f17100SMatthew Ahrens 	}
744178f17100SMatthew Ahrens 	zhp = zfs_open(g_zfs, snapname, ZFS_TYPE_SNAPSHOT);
744278f17100SMatthew Ahrens 	if (zhp == NULL)
744378f17100SMatthew Ahrens 		goto usage;
744478f17100SMatthew Ahrens 	zfs_close(zhp);
744578f17100SMatthew Ahrens 
744678f17100SMatthew Ahrens 
744778f17100SMatthew Ahrens 	nvl = fnvlist_alloc();
744878f17100SMatthew Ahrens 	fnvlist_add_string(nvl, argv[1], snapname);
744978f17100SMatthew Ahrens 	ret = lzc_bookmark(nvl, NULL);
745078f17100SMatthew Ahrens 	fnvlist_free(nvl);
745178f17100SMatthew Ahrens 
745278f17100SMatthew Ahrens 	if (ret != 0) {
74537ac89354SDon Brady 		const char *err_msg = NULL;
745478f17100SMatthew Ahrens 		char errbuf[1024];
745578f17100SMatthew Ahrens 
745678f17100SMatthew Ahrens 		(void) snprintf(errbuf, sizeof (errbuf),
745778f17100SMatthew Ahrens 		    dgettext(TEXT_DOMAIN,
745878f17100SMatthew Ahrens 		    "cannot create bookmark '%s'"), argv[1]);
745978f17100SMatthew Ahrens 
746078f17100SMatthew Ahrens 		switch (ret) {
746178f17100SMatthew Ahrens 		case EXDEV:
746278f17100SMatthew Ahrens 			err_msg = "bookmark is in a different pool";
746378f17100SMatthew Ahrens 			break;
746478f17100SMatthew Ahrens 		case EEXIST:
746578f17100SMatthew Ahrens 			err_msg = "bookmark exists";
746678f17100SMatthew Ahrens 			break;
746778f17100SMatthew Ahrens 		case EINVAL:
746878f17100SMatthew Ahrens 			err_msg = "invalid argument";
746978f17100SMatthew Ahrens 			break;
747078f17100SMatthew Ahrens 		case ENOTSUP:
747178f17100SMatthew Ahrens 			err_msg = "bookmark feature not enabled";
747278f17100SMatthew Ahrens 			break;
74737d46dc6cSMatthew Ahrens 		case ENOSPC:
74747d46dc6cSMatthew Ahrens 			err_msg = "out of space";
74757d46dc6cSMatthew Ahrens 			break;
747678f17100SMatthew Ahrens 		default:
74777ac89354SDon Brady 			(void) zfs_standard_error(g_zfs, ret, errbuf);
747878f17100SMatthew Ahrens 			break;
747978f17100SMatthew Ahrens 		}
74807ac89354SDon Brady 		if (err_msg != NULL) {
748178f17100SMatthew Ahrens 			(void) fprintf(stderr, "%s: %s\n", errbuf,
748278f17100SMatthew Ahrens 			    dgettext(TEXT_DOMAIN, err_msg));
748378f17100SMatthew Ahrens 		}
74847ac89354SDon Brady 	}
748578f17100SMatthew Ahrens 
74867d46dc6cSMatthew Ahrens 	return (ret != 0);
748778f17100SMatthew Ahrens 
748878f17100SMatthew Ahrens usage:
748978f17100SMatthew Ahrens 	usage(B_FALSE);
749078f17100SMatthew Ahrens 	return (-1);
749178f17100SMatthew Ahrens }
749278f17100SMatthew Ahrens 
7493dfc11533SChris Williamson static int
zfs_do_channel_program(int argc,char ** argv)7494dfc11533SChris Williamson zfs_do_channel_program(int argc, char **argv)
7495dfc11533SChris Williamson {
7496dfc11533SChris Williamson 	int ret, fd;
7497ef150c2bSRichard Lowe 	int c;
7498dfc11533SChris Williamson 	char *progbuf, *filename, *poolname;
7499dfc11533SChris Williamson 	size_t progsize, progread;
75007ac89354SDon Brady 	nvlist_t *outnvl = NULL;
7501dfc11533SChris Williamson 	uint64_t instrlimit = ZCP_DEFAULT_INSTRLIMIT;
7502dfc11533SChris Williamson 	uint64_t memlimit = ZCP_DEFAULT_MEMLIMIT;
750352675910SAlek Pinchuk 	boolean_t sync_flag = B_TRUE, json_output = B_FALSE;
7504dfc11533SChris Williamson 	zpool_handle_t *zhp;
7505dfc11533SChris Williamson 
7506dfc11533SChris Williamson 	/* check options */
7507dfc11533SChris Williamson 	while (-1 !=
750852675910SAlek Pinchuk 	    (c = getopt(argc, argv, "jnt:(instr-limit)m:(memory-limit)"))) {
7509dfc11533SChris Williamson 		switch (c) {
7510dfc11533SChris Williamson 		case 't':
7511dfc11533SChris Williamson 		case 'm': {
7512dfc11533SChris Williamson 			uint64_t arg;
7513dfc11533SChris Williamson 			char *endp;
7514dfc11533SChris Williamson 
7515dfc11533SChris Williamson 			errno = 0;
7516dfc11533SChris Williamson 			arg = strtoull(optarg, &endp, 0);
7517dfc11533SChris Williamson 			if (errno != 0 || *endp != '\0') {
7518dfc11533SChris Williamson 				(void) fprintf(stderr, gettext(
7519dfc11533SChris Williamson 				    "invalid argument "
7520dfc11533SChris Williamson 				    "'%s': expected integer\n"), optarg);
7521dfc11533SChris Williamson 				goto usage;
7522dfc11533SChris Williamson 			}
7523dfc11533SChris Williamson 
7524dfc11533SChris Williamson 			if (c == 't') {
7525dfc11533SChris Williamson 				if (arg > ZCP_MAX_INSTRLIMIT || arg == 0) {
7526dfc11533SChris Williamson 					(void) fprintf(stderr, gettext(
7527dfc11533SChris Williamson 					    "Invalid instruction limit: "
7528dfc11533SChris Williamson 					    "%s\n"), optarg);
7529dfc11533SChris Williamson 					return (1);
7530dfc11533SChris Williamson 				} else {
7531dfc11533SChris Williamson 					instrlimit = arg;
7532dfc11533SChris Williamson 				}
7533dfc11533SChris Williamson 			} else {
7534dfc11533SChris Williamson 				ASSERT3U(c, ==, 'm');
7535dfc11533SChris Williamson 				if (arg > ZCP_MAX_MEMLIMIT || arg == 0) {
7536dfc11533SChris Williamson 					(void) fprintf(stderr, gettext(
7537dfc11533SChris Williamson 					    "Invalid memory limit: "
7538dfc11533SChris Williamson 					    "%s\n"), optarg);
7539dfc11533SChris Williamson 					return (1);
7540dfc11533SChris Williamson 				} else {
7541dfc11533SChris Williamson 					memlimit = arg;
7542dfc11533SChris Williamson 				}
7543dfc11533SChris Williamson 			}
7544dfc11533SChris Williamson 			break;
7545dfc11533SChris Williamson 		}
7546a3b28680SSerapheim Dimitropoulos 		case 'n': {
7547a3b28680SSerapheim Dimitropoulos 			sync_flag = B_FALSE;
7548a3b28680SSerapheim Dimitropoulos 			break;
7549a3b28680SSerapheim Dimitropoulos 		}
755052675910SAlek Pinchuk 		case 'j': {
755152675910SAlek Pinchuk 			json_output = B_TRUE;
755252675910SAlek Pinchuk 			break;
755352675910SAlek Pinchuk 		}
7554dfc11533SChris Williamson 		case '?':
7555dfc11533SChris Williamson 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
7556dfc11533SChris Williamson 			    optopt);
7557dfc11533SChris Williamson 			goto usage;
7558dfc11533SChris Williamson 		}
7559dfc11533SChris Williamson 	}
7560dfc11533SChris Williamson 
7561dfc11533SChris Williamson 	argc -= optind;
7562dfc11533SChris Williamson 	argv += optind;
7563dfc11533SChris Williamson 
7564dfc11533SChris Williamson 	if (argc < 2) {
7565dfc11533SChris Williamson 		(void) fprintf(stderr,
7566dfc11533SChris Williamson 		    gettext("invalid number of arguments\n"));
7567dfc11533SChris Williamson 		goto usage;
7568dfc11533SChris Williamson 	}
7569dfc11533SChris Williamson 
7570dfc11533SChris Williamson 	poolname = argv[0];
7571dfc11533SChris Williamson 	filename = argv[1];
7572dfc11533SChris Williamson 	if (strcmp(filename, "-") == 0) {
7573dfc11533SChris Williamson 		fd = 0;
7574dfc11533SChris Williamson 		filename = "standard input";
7575dfc11533SChris Williamson 	} else if ((fd = open(filename, O_RDONLY)) < 0) {
7576dfc11533SChris Williamson 		(void) fprintf(stderr, gettext("cannot open '%s': %s\n"),
7577dfc11533SChris Williamson 		    filename, strerror(errno));
7578dfc11533SChris Williamson 		return (1);
7579dfc11533SChris Williamson 	}
7580dfc11533SChris Williamson 
7581dfc11533SChris Williamson 	if ((zhp = zpool_open(g_zfs, poolname)) == NULL) {
7582dfc11533SChris Williamson 		(void) fprintf(stderr, gettext("cannot open pool '%s'"),
7583dfc11533SChris Williamson 		    poolname);
7584dfc11533SChris Williamson 		return (1);
7585dfc11533SChris Williamson 	}
7586dfc11533SChris Williamson 	zpool_close(zhp);
7587dfc11533SChris Williamson 
7588dfc11533SChris Williamson 	/*
7589dfc11533SChris Williamson 	 * Read in the channel program, expanding the program buffer as
7590dfc11533SChris Williamson 	 * necessary.
7591dfc11533SChris Williamson 	 */
7592dfc11533SChris Williamson 	progread = 0;
7593dfc11533SChris Williamson 	progsize = 1024;
7594dfc11533SChris Williamson 	progbuf = safe_malloc(progsize);
7595dfc11533SChris Williamson 	do {
7596dfc11533SChris Williamson 		ret = read(fd, progbuf + progread, progsize - progread);
7597dfc11533SChris Williamson 		progread += ret;
7598dfc11533SChris Williamson 		if (progread == progsize && ret > 0) {
7599dfc11533SChris Williamson 			progsize *= 2;
7600dfc11533SChris Williamson 			progbuf = safe_realloc(progbuf, progsize);
7601dfc11533SChris Williamson 		}
7602dfc11533SChris Williamson 	} while (ret > 0);
7603dfc11533SChris Williamson 
7604dfc11533SChris Williamson 	if (fd != 0)
7605dfc11533SChris Williamson 		(void) close(fd);
7606dfc11533SChris Williamson 	if (ret < 0) {
7607dfc11533SChris Williamson 		free(progbuf);
7608dfc11533SChris Williamson 		(void) fprintf(stderr,
7609dfc11533SChris Williamson 		    gettext("cannot read '%s': %s\n"),
7610dfc11533SChris Williamson 		    filename, strerror(errno));
7611dfc11533SChris Williamson 		return (1);
7612dfc11533SChris Williamson 	}
7613dfc11533SChris Williamson 	progbuf[progread] = '\0';
7614dfc11533SChris Williamson 
7615dfc11533SChris Williamson 	/*
7616dfc11533SChris Williamson 	 * Any remaining arguments are passed as arguments to the lua script as
7617dfc11533SChris Williamson 	 * a string array:
7618dfc11533SChris Williamson 	 * {
7619dfc11533SChris Williamson 	 *	"argv" -> [ "arg 1", ... "arg n" ],
7620dfc11533SChris Williamson 	 * }
7621dfc11533SChris Williamson 	 */
7622dfc11533SChris Williamson 	nvlist_t *argnvl = fnvlist_alloc();
7623dfc11533SChris Williamson 	fnvlist_add_string_array(argnvl, ZCP_ARG_CLIARGV, argv + 2, argc - 2);
7624dfc11533SChris Williamson 
7625a3b28680SSerapheim Dimitropoulos 	if (sync_flag) {
7626a3b28680SSerapheim Dimitropoulos 		ret = lzc_channel_program(poolname, progbuf,
7627a3b28680SSerapheim Dimitropoulos 		    instrlimit, memlimit, argnvl, &outnvl);
7628a3b28680SSerapheim Dimitropoulos 	} else {
7629a3b28680SSerapheim Dimitropoulos 		ret = lzc_channel_program_nosync(poolname, progbuf,
7630a3b28680SSerapheim Dimitropoulos 		    instrlimit, memlimit, argnvl, &outnvl);
7631a3b28680SSerapheim Dimitropoulos 	}
7632dfc11533SChris Williamson 
7633dfc11533SChris Williamson 	if (ret != 0) {
7634dfc11533SChris Williamson 		/*
7635dfc11533SChris Williamson 		 * On error, report the error message handed back by lua if one
7636dfc11533SChris Williamson 		 * exists.  Otherwise, generate an appropriate error message,
7637dfc11533SChris Williamson 		 * falling back on strerror() for an unexpected return code.
7638dfc11533SChris Williamson 		 */
7639dfc11533SChris Williamson 		char *errstring = NULL;
76407ac89354SDon Brady 		const char *msg = gettext("Channel program execution failed");
76417ac89354SDon Brady 		if (outnvl != NULL && nvlist_exists(outnvl, ZCP_RET_ERROR)) {
7642dfc11533SChris Williamson 			(void) nvlist_lookup_string(outnvl,
7643dfc11533SChris Williamson 			    ZCP_RET_ERROR, &errstring);
7644dfc11533SChris Williamson 			if (errstring == NULL)
7645dfc11533SChris Williamson 				errstring = strerror(ret);
7646dfc11533SChris Williamson 		} else {
7647dfc11533SChris Williamson 			switch (ret) {
7648dfc11533SChris Williamson 			case EINVAL:
7649dfc11533SChris Williamson 				errstring =
7650dfc11533SChris Williamson 				    "Invalid instruction or memory limit.";
7651dfc11533SChris Williamson 				break;
7652dfc11533SChris Williamson 			case ENOMEM:
7653dfc11533SChris Williamson 				errstring = "Return value too large.";
7654dfc11533SChris Williamson 				break;
7655dfc11533SChris Williamson 			case ENOSPC:
7656dfc11533SChris Williamson 				errstring = "Memory limit exhausted.";
7657dfc11533SChris Williamson 				break;
7658dfc11533SChris Williamson 			case ETIME:
7659dfc11533SChris Williamson 				errstring = "Timed out.";
7660dfc11533SChris Williamson 				break;
7661dfc11533SChris Williamson 			case EPERM:
7662dfc11533SChris Williamson 				errstring = "Permission denied. Channel "
7663dfc11533SChris Williamson 				    "programs must be run as root.";
7664dfc11533SChris Williamson 				break;
7665dfc11533SChris Williamson 			default:
76667ac89354SDon Brady 				(void) zfs_standard_error(g_zfs, ret, msg);
7667dfc11533SChris Williamson 			}
7668dfc11533SChris Williamson 		}
76697ac89354SDon Brady 		if (errstring != NULL)
76707ac89354SDon Brady 			(void) fprintf(stderr, "%s:\n%s\n", msg, errstring);
7671dfc11533SChris Williamson 	} else {
767252675910SAlek Pinchuk 		if (json_output) {
767352675910SAlek Pinchuk 			(void) nvlist_print_json(stdout, outnvl);
767452675910SAlek Pinchuk 		} else if (nvlist_empty(outnvl)) {
767552675910SAlek Pinchuk 			(void) fprintf(stdout, gettext("Channel program fully "
767652675910SAlek Pinchuk 			    "executed and did not produce output.\n"));
7677dfc11533SChris Williamson 		} else {
767852675910SAlek Pinchuk 			(void) fprintf(stdout, gettext("Channel program fully "
767952675910SAlek Pinchuk 			    "executed and produced output:\n"));
7680dfc11533SChris Williamson 			dump_nvlist(outnvl, 4);
7681dfc11533SChris Williamson 		}
7682dfc11533SChris Williamson 	}
7683dfc11533SChris Williamson 
7684dfc11533SChris Williamson 	free(progbuf);
7685dfc11533SChris Williamson 	fnvlist_free(outnvl);
7686dfc11533SChris Williamson 	fnvlist_free(argnvl);
7687dfc11533SChris Williamson 	return (ret != 0);
7688dfc11533SChris Williamson 
7689dfc11533SChris Williamson usage:
7690dfc11533SChris Williamson 	usage(B_FALSE);
7691dfc11533SChris Williamson 	return (-1);
7692dfc11533SChris Williamson }
7693dfc11533SChris Williamson 
7694eb633035STom Caputi typedef struct loadkey_cbdata {
7695eb633035STom Caputi 	boolean_t cb_loadkey;
7696eb633035STom Caputi 	boolean_t cb_recursive;
7697eb633035STom Caputi 	boolean_t cb_noop;
7698eb633035STom Caputi 	char *cb_keylocation;
7699eb633035STom Caputi 	uint64_t cb_numfailed;
7700eb633035STom Caputi 	uint64_t cb_numattempted;
7701eb633035STom Caputi } loadkey_cbdata_t;
7702eb633035STom Caputi 
7703eb633035STom Caputi static int
load_key_callback(zfs_handle_t * zhp,void * data)7704eb633035STom Caputi load_key_callback(zfs_handle_t *zhp, void *data)
7705eb633035STom Caputi {
7706eb633035STom Caputi 	int ret;
7707eb633035STom Caputi 	boolean_t is_encroot;
7708eb633035STom Caputi 	loadkey_cbdata_t *cb = data;
7709eb633035STom Caputi 	uint64_t keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS);
7710eb633035STom Caputi 
7711eb633035STom Caputi 	/*
7712eb633035STom Caputi 	 * If we are working recursively, we want to skip loading / unloading
7713eb633035STom Caputi 	 * keys for non-encryption roots and datasets whose keys are already
7714eb633035STom Caputi 	 * in the desired end-state.
7715eb633035STom Caputi 	 */
7716eb633035STom Caputi 	if (cb->cb_recursive) {
7717eb633035STom Caputi 		ret = zfs_crypto_get_encryption_root(zhp, &is_encroot, NULL);
7718eb633035STom Caputi 		if (ret != 0)
7719eb633035STom Caputi 			return (ret);
7720eb633035STom Caputi 		if (!is_encroot)
7721eb633035STom Caputi 			return (0);
7722eb633035STom Caputi 
7723eb633035STom Caputi 		if ((cb->cb_loadkey && keystatus == ZFS_KEYSTATUS_AVAILABLE) ||
7724eb633035STom Caputi 		    (!cb->cb_loadkey && keystatus == ZFS_KEYSTATUS_UNAVAILABLE))
7725eb633035STom Caputi 			return (0);
7726eb633035STom Caputi 	}
7727eb633035STom Caputi 
7728eb633035STom Caputi 	cb->cb_numattempted++;
7729eb633035STom Caputi 
7730eb633035STom Caputi 	if (cb->cb_loadkey)
7731eb633035STom Caputi 		ret = zfs_crypto_load_key(zhp, cb->cb_noop, cb->cb_keylocation);
7732eb633035STom Caputi 	else
7733eb633035STom Caputi 		ret = zfs_crypto_unload_key(zhp);
7734eb633035STom Caputi 
7735eb633035STom Caputi 	if (ret != 0) {
7736eb633035STom Caputi 		cb->cb_numfailed++;
7737eb633035STom Caputi 		return (ret);
7738eb633035STom Caputi 	}
7739eb633035STom Caputi 
7740eb633035STom Caputi 	return (0);
7741eb633035STom Caputi }
7742eb633035STom Caputi 
7743eb633035STom Caputi static int
load_unload_keys(int argc,char ** argv,boolean_t loadkey)7744eb633035STom Caputi load_unload_keys(int argc, char **argv, boolean_t loadkey)
7745eb633035STom Caputi {
7746eb633035STom Caputi 	int c, ret = 0, flags = 0;
7747eb633035STom Caputi 	boolean_t do_all = B_FALSE;
7748eb633035STom Caputi 	loadkey_cbdata_t cb = { 0 };
7749eb633035STom Caputi 
7750eb633035STom Caputi 	cb.cb_loadkey = loadkey;
7751eb633035STom Caputi 
7752eb633035STom Caputi 	while ((c = getopt(argc, argv, "anrL:")) != -1) {
7753eb633035STom Caputi 		/* noop and alternate keylocations only apply to zfs load-key */
7754eb633035STom Caputi 		if (loadkey) {
7755eb633035STom Caputi 			switch (c) {
7756eb633035STom Caputi 			case 'n':
7757eb633035STom Caputi 				cb.cb_noop = B_TRUE;
7758eb633035STom Caputi 				continue;
7759eb633035STom Caputi 			case 'L':
7760eb633035STom Caputi 				cb.cb_keylocation = optarg;
7761eb633035STom Caputi 				continue;
7762eb633035STom Caputi 			default:
7763eb633035STom Caputi 				break;
7764eb633035STom Caputi 			}
7765eb633035STom Caputi 		}
7766eb633035STom Caputi 
7767eb633035STom Caputi 		switch (c) {
7768eb633035STom Caputi 		case 'a':
7769eb633035STom Caputi 			do_all = B_TRUE;
7770eb633035STom Caputi 			cb.cb_recursive = B_TRUE;
7771eb633035STom Caputi 			break;
7772eb633035STom Caputi 		case 'r':
7773eb633035STom Caputi 			flags |= ZFS_ITER_RECURSE;
7774eb633035STom Caputi 			cb.cb_recursive = B_TRUE;
7775eb633035STom Caputi 			break;
7776eb633035STom Caputi 		default:
7777eb633035STom Caputi 			(void) fprintf(stderr,
7778eb633035STom Caputi 			    gettext("invalid option '%c'\n"), optopt);
7779eb633035STom Caputi 			usage(B_FALSE);
7780eb633035STom Caputi 		}
7781eb633035STom Caputi 	}
7782eb633035STom Caputi 
7783eb633035STom Caputi 	argc -= optind;
7784eb633035STom Caputi 	argv += optind;
7785eb633035STom Caputi 
7786eb633035STom Caputi 	if (!do_all && argc == 0) {
7787eb633035STom Caputi 		(void) fprintf(stderr,
7788eb633035STom Caputi 		    gettext("Missing dataset argument or -a option\n"));
7789eb633035STom Caputi 		usage(B_FALSE);
7790eb633035STom Caputi 	}
7791eb633035STom Caputi 
7792eb633035STom Caputi 	if (do_all && argc != 0) {
7793eb633035STom Caputi 		(void) fprintf(stderr,
7794eb633035STom Caputi 		    gettext("Cannot specify dataset with -a option\n"));
7795eb633035STom Caputi 		usage(B_FALSE);
7796eb633035STom Caputi 	}
7797eb633035STom Caputi 
7798eb633035STom Caputi 	if (cb.cb_recursive && cb.cb_keylocation != NULL &&
7799eb633035STom Caputi 	    strcmp(cb.cb_keylocation, "prompt") != 0) {
7800eb633035STom Caputi 		(void) fprintf(stderr, gettext("alternate keylocation may only "
7801eb633035STom Caputi 		    "be 'prompt' with -r or -a\n"));
7802eb633035STom Caputi 		usage(B_FALSE);
7803eb633035STom Caputi 	}
7804eb633035STom Caputi 
7805eb633035STom Caputi 	ret = zfs_for_each(argc, argv, flags,
7806eb633035STom Caputi 	    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, NULL, 0,
7807eb633035STom Caputi 	    load_key_callback, &cb);
7808eb633035STom Caputi 
7809eb633035STom Caputi 	if (cb.cb_noop || (cb.cb_recursive && cb.cb_numattempted != 0)) {
7810eb633035STom Caputi 		(void) printf(gettext("%llu / %llu key(s) successfully %s\n"),
7811eb633035STom Caputi 		    (u_longlong_t)(cb.cb_numattempted - cb.cb_numfailed),
7812eb633035STom Caputi 		    (u_longlong_t)cb.cb_numattempted,
7813eb633035STom Caputi 		    loadkey ? (cb.cb_noop ? "verified" : "loaded") :
7814eb633035STom Caputi 		    "unloaded");
7815eb633035STom Caputi 	}
7816eb633035STom Caputi 
7817eb633035STom Caputi 	if (cb.cb_numfailed != 0)
7818eb633035STom Caputi 		ret = -1;
7819eb633035STom Caputi 
7820eb633035STom Caputi 	return (ret);
7821eb633035STom Caputi }
7822eb633035STom Caputi 
7823eb633035STom Caputi static int
zfs_do_load_key(int argc,char ** argv)7824eb633035STom Caputi zfs_do_load_key(int argc, char **argv)
7825eb633035STom Caputi {
7826eb633035STom Caputi 	return (load_unload_keys(argc, argv, B_TRUE));
7827eb633035STom Caputi }
7828eb633035STom Caputi 
7829eb633035STom Caputi 
7830eb633035STom Caputi static int
zfs_do_unload_key(int argc,char ** argv)7831eb633035STom Caputi zfs_do_unload_key(int argc, char **argv)
7832eb633035STom Caputi {
7833eb633035STom Caputi 	return (load_unload_keys(argc, argv, B_FALSE));
7834eb633035STom Caputi }
7835eb633035STom Caputi 
7836eb633035STom Caputi static int
zfs_do_change_key(int argc,char ** argv)7837eb633035STom Caputi zfs_do_change_key(int argc, char **argv)
7838eb633035STom Caputi {
7839eb633035STom Caputi 	int c, ret;
7840eb633035STom Caputi 	uint64_t keystatus;
7841eb633035STom Caputi 	boolean_t loadkey = B_FALSE, inheritkey = B_FALSE;
7842eb633035STom Caputi 	zfs_handle_t *zhp = NULL;
7843eb633035STom Caputi 	nvlist_t *props = fnvlist_alloc();
7844eb633035STom Caputi 
7845eb633035STom Caputi 	while ((c = getopt(argc, argv, "lio:")) != -1) {
7846eb633035STom Caputi 		switch (c) {
7847eb633035STom Caputi 		case 'l':
7848eb633035STom Caputi 			loadkey = B_TRUE;
7849eb633035STom Caputi 			break;
7850eb633035STom Caputi 		case 'i':
7851eb633035STom Caputi 			inheritkey = B_TRUE;
7852eb633035STom Caputi 			break;
7853eb633035STom Caputi 		case 'o':
78546ccda740Sloli10K 			if (!parseprop(props, optarg)) {
7855eb633035STom Caputi 				nvlist_free(props);
7856eb633035STom Caputi 				return (1);
7857eb633035STom Caputi 			}
7858eb633035STom Caputi 			break;
7859eb633035STom Caputi 		default:
7860eb633035STom Caputi 			(void) fprintf(stderr,
7861eb633035STom Caputi 			    gettext("invalid option '%c'\n"), optopt);
7862eb633035STom Caputi 			usage(B_FALSE);
7863eb633035STom Caputi 		}
7864eb633035STom Caputi 	}
7865eb633035STom Caputi 
7866eb633035STom Caputi 	if (inheritkey && !nvlist_empty(props)) {
7867eb633035STom Caputi 		(void) fprintf(stderr,
7868eb633035STom Caputi 		    gettext("Properties not allowed for inheriting\n"));
7869eb633035STom Caputi 		usage(B_FALSE);
7870eb633035STom Caputi 	}
7871eb633035STom Caputi 
7872eb633035STom Caputi 	argc -= optind;
7873eb633035STom Caputi 	argv += optind;
7874eb633035STom Caputi 
7875eb633035STom Caputi 	if (argc < 1) {
7876eb633035STom Caputi 		(void) fprintf(stderr, gettext("Missing dataset argument\n"));
7877eb633035STom Caputi 		usage(B_FALSE);
7878eb633035STom Caputi 	}
7879eb633035STom Caputi 
7880eb633035STom Caputi 	if (argc > 1) {
7881eb633035STom Caputi 		(void) fprintf(stderr, gettext("Too many arguments\n"));
7882eb633035STom Caputi 		usage(B_FALSE);
7883eb633035STom Caputi 	}
7884eb633035STom Caputi 
7885eb633035STom Caputi 	zhp = zfs_open(g_zfs, argv[argc - 1],
7886eb633035STom Caputi 	    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
7887eb633035STom Caputi 	if (zhp == NULL)
7888eb633035STom Caputi 		usage(B_FALSE);
7889eb633035STom Caputi 
7890eb633035STom Caputi 	if (loadkey) {
7891eb633035STom Caputi 		keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS);
7892eb633035STom Caputi 		if (keystatus != ZFS_KEYSTATUS_AVAILABLE) {
7893eb633035STom Caputi 			ret = zfs_crypto_load_key(zhp, B_FALSE, NULL);
7894eb633035STom Caputi 			if (ret != 0) {
7895eb633035STom Caputi 				nvlist_free(props);
7896eb633035STom Caputi 				zfs_close(zhp);
7897eb633035STom Caputi 				return (-1);
7898eb633035STom Caputi 			}
7899eb633035STom Caputi 		}
7900eb633035STom Caputi 
7901eb633035STom Caputi 		/* refresh the properties so the new keystatus is visible */
7902eb633035STom Caputi 		zfs_refresh_properties(zhp);
7903eb633035STom Caputi 	}
7904eb633035STom Caputi 
7905eb633035STom Caputi 	ret = zfs_crypto_rewrap(zhp, props, inheritkey);
7906eb633035STom Caputi 	if (ret != 0) {
7907eb633035STom Caputi 		nvlist_free(props);
7908eb633035STom Caputi 		zfs_close(zhp);
7909eb633035STom Caputi 		return (-1);
7910eb633035STom Caputi 	}
7911eb633035STom Caputi 
7912eb633035STom Caputi 	nvlist_free(props);
7913eb633035STom Caputi 	zfs_close(zhp);
7914eb633035STom Caputi 	return (0);
7915eb633035STom Caputi }
7916eb633035STom Caputi 
7917f67950b2SNasf-Fan /*
7918f67950b2SNasf-Fan  * 1) zfs project [-d|-r] <file|directory ...>
7919f67950b2SNasf-Fan  *    List project ID and inherit flag of file(s) or directories.
7920f67950b2SNasf-Fan  *    -d: List the directory itself, not its children.
7921f67950b2SNasf-Fan  *    -r: List subdirectories recursively.
7922f67950b2SNasf-Fan  *
7923f67950b2SNasf-Fan  * 2) zfs project -C [-k] [-r] <file|directory ...>
7924f67950b2SNasf-Fan  *    Clear project inherit flag and/or ID on the file(s) or directories.
7925f67950b2SNasf-Fan  *    -k: Keep the project ID unchanged. If not specified, the project ID
7926f67950b2SNasf-Fan  *	  will be reset as zero.
7927f67950b2SNasf-Fan  *    -r: Clear on subdirectories recursively.
7928f67950b2SNasf-Fan  *
7929f67950b2SNasf-Fan  * 3) zfs project -c [-0] [-d|-r] [-p id] <file|directory ...>
7930f67950b2SNasf-Fan  *    Check project ID and inherit flag on the file(s) or directories,
7931f67950b2SNasf-Fan  *    report the outliers.
7932f67950b2SNasf-Fan  *    -0: Print file name followed by a NUL instead of newline.
7933f67950b2SNasf-Fan  *    -d: Check the directory itself, not its children.
7934f67950b2SNasf-Fan  *    -p: Specify the referenced ID for comparing with the target file(s)
7935f67950b2SNasf-Fan  *	  or directories' project IDs. If not specified, the target (top)
7936f67950b2SNasf-Fan  *	  directory's project ID will be used as the referenced one.
7937f67950b2SNasf-Fan  *    -r: Check subdirectories recursively.
7938f67950b2SNasf-Fan  *
7939f67950b2SNasf-Fan  * 4) zfs project [-p id] [-r] [-s] <file|directory ...>
7940f67950b2SNasf-Fan  *    Set project ID and/or inherit flag on the file(s) or directories.
7941f67950b2SNasf-Fan  *    -p: Set the project ID as the given id.
7942f67950b2SNasf-Fan  *    -r: Set on subdirectory recursively. If not specify "-p" option,
7943f67950b2SNasf-Fan  *	  it will use top-level directory's project ID as the given id,
7944f67950b2SNasf-Fan  *	  then set both project ID and inherit flag on all descendants
7945f67950b2SNasf-Fan  *	  of the top-level directory.
7946f67950b2SNasf-Fan  *    -s: Set project inherit flag.
7947f67950b2SNasf-Fan  */
7948f67950b2SNasf-Fan static int
zfs_do_project(int argc,char ** argv)7949f67950b2SNasf-Fan zfs_do_project(int argc, char **argv)
7950f67950b2SNasf-Fan {
7951f67950b2SNasf-Fan 	zfs_project_control_t zpc = {
7952f67950b2SNasf-Fan 		.zpc_expected_projid = ZFS_INVALID_PROJID,
7953f67950b2SNasf-Fan 		.zpc_op = ZFS_PROJECT_OP_DEFAULT,
7954f67950b2SNasf-Fan 		.zpc_dironly = B_FALSE,
7955f67950b2SNasf-Fan 		.zpc_keep_projid = B_FALSE,
7956f67950b2SNasf-Fan 		.zpc_newline = B_TRUE,
7957f67950b2SNasf-Fan 		.zpc_recursive = B_FALSE,
7958f67950b2SNasf-Fan 		.zpc_set_flag = B_FALSE,
7959f67950b2SNasf-Fan 	};
7960f67950b2SNasf-Fan 	int ret = 0, c;
7961f67950b2SNasf-Fan 
7962f67950b2SNasf-Fan 	if (argc < 2)
7963f67950b2SNasf-Fan 		usage(B_FALSE);
7964f67950b2SNasf-Fan 
7965f67950b2SNasf-Fan 	while ((c = getopt(argc, argv, "0Ccdkp:rs")) != -1) {
7966f67950b2SNasf-Fan 		switch (c) {
7967f67950b2SNasf-Fan 		case '0':
7968f67950b2SNasf-Fan 			zpc.zpc_newline = B_FALSE;
7969f67950b2SNasf-Fan 			break;
7970f67950b2SNasf-Fan 		case 'C':
7971f67950b2SNasf-Fan 			if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) {
7972f67950b2SNasf-Fan 				(void) fprintf(stderr, gettext("cannot "
7973f67950b2SNasf-Fan 				    "specify '-C' '-c' '-s' together\n"));
7974f67950b2SNasf-Fan 				usage(B_FALSE);
7975f67950b2SNasf-Fan 			}
7976f67950b2SNasf-Fan 
7977f67950b2SNasf-Fan 			zpc.zpc_op = ZFS_PROJECT_OP_CLEAR;
7978f67950b2SNasf-Fan 			break;
7979f67950b2SNasf-Fan 		case 'c':
7980f67950b2SNasf-Fan 			if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) {
7981f67950b2SNasf-Fan 				(void) fprintf(stderr, gettext("cannot "
7982f67950b2SNasf-Fan 				    "specify '-C' '-c' '-s' together\n"));
7983f67950b2SNasf-Fan 				usage(B_FALSE);
7984f67950b2SNasf-Fan 			}
7985f67950b2SNasf-Fan 
7986f67950b2SNasf-Fan 			zpc.zpc_op = ZFS_PROJECT_OP_CHECK;
7987f67950b2SNasf-Fan 			break;
7988f67950b2SNasf-Fan 		case 'd':
7989f67950b2SNasf-Fan 			zpc.zpc_dironly = B_TRUE;
7990f67950b2SNasf-Fan 			/* overwrite "-r" option */
7991f67950b2SNasf-Fan 			zpc.zpc_recursive = B_FALSE;
7992f67950b2SNasf-Fan 			break;
7993f67950b2SNasf-Fan 		case 'k':
7994f67950b2SNasf-Fan 			zpc.zpc_keep_projid = B_TRUE;
7995f67950b2SNasf-Fan 			break;
7996f67950b2SNasf-Fan 		case 'p': {
7997f67950b2SNasf-Fan 			char *endptr;
7998f67950b2SNasf-Fan 
7999f67950b2SNasf-Fan 			errno = 0;
8000f67950b2SNasf-Fan 			zpc.zpc_expected_projid = strtoull(optarg, &endptr, 0);
8001f67950b2SNasf-Fan 			if (errno != 0 || *endptr != '\0') {
8002f67950b2SNasf-Fan 				(void) fprintf(stderr,
8003f67950b2SNasf-Fan 				    gettext("project ID must be less than "
8004f67950b2SNasf-Fan 				    "%u\n"), UINT32_MAX);
8005f67950b2SNasf-Fan 				usage(B_FALSE);
8006f67950b2SNasf-Fan 			}
8007f67950b2SNasf-Fan 			if (zpc.zpc_expected_projid >= UINT32_MAX) {
8008f67950b2SNasf-Fan 				(void) fprintf(stderr,
8009f67950b2SNasf-Fan 				    gettext("invalid project ID\n"));
8010f67950b2SNasf-Fan 				usage(B_FALSE);
8011f67950b2SNasf-Fan 			}
8012f67950b2SNasf-Fan 			break;
8013f67950b2SNasf-Fan 		}
8014f67950b2SNasf-Fan 		case 'r':
8015f67950b2SNasf-Fan 			zpc.zpc_recursive = B_TRUE;
8016f67950b2SNasf-Fan 			/* overwrite "-d" option */
8017f67950b2SNasf-Fan 			zpc.zpc_dironly = B_FALSE;
8018f67950b2SNasf-Fan 			break;
8019f67950b2SNasf-Fan 		case 's':
8020f67950b2SNasf-Fan 			if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) {
8021f67950b2SNasf-Fan 				(void) fprintf(stderr, gettext("cannot "
8022f67950b2SNasf-Fan 				    "specify '-C' '-c' '-s' together\n"));
8023f67950b2SNasf-Fan 				usage(B_FALSE);
8024f67950b2SNasf-Fan 			}
8025f67950b2SNasf-Fan 
8026f67950b2SNasf-Fan 			zpc.zpc_set_flag = B_TRUE;
8027f67950b2SNasf-Fan 			zpc.zpc_op = ZFS_PROJECT_OP_SET;
8028f67950b2SNasf-Fan 			break;
8029f67950b2SNasf-Fan 		default:
8030f67950b2SNasf-Fan 			(void) fprintf(stderr, gettext("invalid option '%c'\n"),
8031f67950b2SNasf-Fan 			    optopt);
8032f67950b2SNasf-Fan 			usage(B_FALSE);
8033f67950b2SNasf-Fan 		}
8034f67950b2SNasf-Fan 	}
8035f67950b2SNasf-Fan 
8036f67950b2SNasf-Fan 	if (zpc.zpc_op == ZFS_PROJECT_OP_DEFAULT) {
8037f67950b2SNasf-Fan 		if (zpc.zpc_expected_projid != ZFS_INVALID_PROJID)
8038f67950b2SNasf-Fan 			zpc.zpc_op = ZFS_PROJECT_OP_SET;
8039f67950b2SNasf-Fan 		else
8040f67950b2SNasf-Fan 			zpc.zpc_op = ZFS_PROJECT_OP_LIST;
8041f67950b2SNasf-Fan 	}
8042f67950b2SNasf-Fan 
8043f67950b2SNasf-Fan 	switch (zpc.zpc_op) {
8044f67950b2SNasf-Fan 	case ZFS_PROJECT_OP_LIST:
8045f67950b2SNasf-Fan 		if (zpc.zpc_keep_projid) {
8046f67950b2SNasf-Fan 			(void) fprintf(stderr,
8047f67950b2SNasf-Fan 			    gettext("'-k' is only valid together with '-C'\n"));
8048f67950b2SNasf-Fan 			usage(B_FALSE);
8049f67950b2SNasf-Fan 		}
8050f67950b2SNasf-Fan 		if (!zpc.zpc_newline) {
8051f67950b2SNasf-Fan 			(void) fprintf(stderr,
8052f67950b2SNasf-Fan 			    gettext("'-0' is only valid together with '-c'\n"));
8053f67950b2SNasf-Fan 			usage(B_FALSE);
8054f67950b2SNasf-Fan 		}
8055f67950b2SNasf-Fan 		break;
8056f67950b2SNasf-Fan 	case ZFS_PROJECT_OP_CHECK:
8057f67950b2SNasf-Fan 		if (zpc.zpc_keep_projid) {
8058f67950b2SNasf-Fan 			(void) fprintf(stderr,
8059f67950b2SNasf-Fan 			    gettext("'-k' is only valid together with '-C'\n"));
8060f67950b2SNasf-Fan 			usage(B_FALSE);
8061f67950b2SNasf-Fan 		}
8062f67950b2SNasf-Fan 		break;
8063f67950b2SNasf-Fan 	case ZFS_PROJECT_OP_CLEAR:
8064f67950b2SNasf-Fan 		if (zpc.zpc_dironly) {
8065f67950b2SNasf-Fan 			(void) fprintf(stderr,
8066f67950b2SNasf-Fan 			    gettext("'-d' is useless together with '-C'\n"));
8067f67950b2SNasf-Fan 			usage(B_FALSE);
8068f67950b2SNasf-Fan 		}
8069f67950b2SNasf-Fan 		if (!zpc.zpc_newline) {
8070f67950b2SNasf-Fan 			(void) fprintf(stderr,
8071f67950b2SNasf-Fan 			    gettext("'-0' is only valid together with '-c'\n"));
8072f67950b2SNasf-Fan 			usage(B_FALSE);
8073f67950b2SNasf-Fan 		}
8074f67950b2SNasf-Fan 		if (zpc.zpc_expected_projid != ZFS_INVALID_PROJID) {
8075f67950b2SNasf-Fan 			(void) fprintf(stderr,
8076f67950b2SNasf-Fan 			    gettext("'-p' is useless together with '-C'\n"));
8077f67950b2SNasf-Fan 			usage(B_FALSE);
8078f67950b2SNasf-Fan 		}
8079f67950b2SNasf-Fan 		break;
8080f67950b2SNasf-Fan 	case ZFS_PROJECT_OP_SET:
8081f67950b2SNasf-Fan 		if (zpc.zpc_dironly) {
8082f67950b2SNasf-Fan 			(void) fprintf(stderr,
8083f67950b2SNasf-Fan 			    gettext("'-d' is useless for set project ID and/or "
8084f67950b2SNasf-Fan 			    "inherit flag\n"));
8085f67950b2SNasf-Fan 			usage(B_FALSE);
8086f67950b2SNasf-Fan 		}
8087f67950b2SNasf-Fan 		if (zpc.zpc_keep_projid) {
8088f67950b2SNasf-Fan 			(void) fprintf(stderr,
8089f67950b2SNasf-Fan 			    gettext("'-k' is only valid together with '-C'\n"));
8090f67950b2SNasf-Fan 			usage(B_FALSE);
8091f67950b2SNasf-Fan 		}
8092f67950b2SNasf-Fan 		if (!zpc.zpc_newline) {
8093f67950b2SNasf-Fan 			(void) fprintf(stderr,
8094f67950b2SNasf-Fan 			    gettext("'-0' is only valid together with '-c'\n"));
8095f67950b2SNasf-Fan 			usage(B_FALSE);
8096f67950b2SNasf-Fan 		}
8097f67950b2SNasf-Fan 		break;
8098f67950b2SNasf-Fan 	default:
8099f67950b2SNasf-Fan 		ASSERT(0);
8100f67950b2SNasf-Fan 		break;
8101f67950b2SNasf-Fan 	}
8102f67950b2SNasf-Fan 
8103f67950b2SNasf-Fan 	argv += optind;
8104f67950b2SNasf-Fan 	argc -= optind;
8105f67950b2SNasf-Fan 	if (argc == 0) {
8106f67950b2SNasf-Fan 		(void) fprintf(stderr,
8107f67950b2SNasf-Fan 		    gettext("missing file or directory target(s)\n"));
8108f67950b2SNasf-Fan 		usage(B_FALSE);
8109f67950b2SNasf-Fan 	}
8110f67950b2SNasf-Fan 
8111f67950b2SNasf-Fan 	for (int i = 0; i < argc; i++) {
8112f67950b2SNasf-Fan 		int err;
8113f67950b2SNasf-Fan 
8114f67950b2SNasf-Fan 		err = zfs_project_handle(argv[i], &zpc);
8115f67950b2SNasf-Fan 		if (err && !ret)
8116f67950b2SNasf-Fan 			ret = err;
8117f67950b2SNasf-Fan 	}
8118f67950b2SNasf-Fan 
8119f67950b2SNasf-Fan 	return (ret);
8120f67950b2SNasf-Fan }
8121f67950b2SNasf-Fan 
8122fa9e4066Sahrens int
main(int argc,char ** argv)8123fa9e4066Sahrens main(int argc, char **argv)
8124fa9e4066Sahrens {
812505c998d6SRichard Lowe 	int ret = 0;
8126fa9e4066Sahrens 	int i;
8127fa9e4066Sahrens 	char *progname;
8128fa9e4066Sahrens 	char *cmdname;
8129fa9e4066Sahrens 
8130fa9e4066Sahrens 	(void) setlocale(LC_ALL, "");
8131fa9e4066Sahrens 	(void) textdomain(TEXT_DOMAIN);
8132fa9e4066Sahrens 
8133fa9e4066Sahrens 	opterr = 0;
8134fa9e4066Sahrens 
813599653d4eSeschrock 	if ((g_zfs = libzfs_init()) == NULL) {
813699653d4eSeschrock 		(void) fprintf(stderr, gettext("internal error: failed to "
813799653d4eSeschrock 		    "initialize ZFS library\n"));
813899653d4eSeschrock 		return (1);
813999653d4eSeschrock 	}
814099653d4eSeschrock 
81414445fffbSMatthew Ahrens 	zfs_save_arguments(argc, argv, history_str, sizeof (history_str));
8142ecd6cf80Smarks 
814399653d4eSeschrock 	libzfs_print_on_error(g_zfs, B_TRUE);
814499653d4eSeschrock 
8145fa9e4066Sahrens 	if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) {
8146fa9e4066Sahrens 		(void) fprintf(stderr, gettext("internal error: unable to "
8147fa9e4066Sahrens 		    "open %s\n"), MNTTAB);
8148fa9e4066Sahrens 		return (1);
8149fa9e4066Sahrens 	}
8150fa9e4066Sahrens 
8151fa9e4066Sahrens 	/*
8152fa9e4066Sahrens 	 * This command also doubles as the /etc/fs mount and unmount program.
8153fa9e4066Sahrens 	 * Determine if we should take this behavior based on argv[0].
8154fa9e4066Sahrens 	 */
8155fa9e4066Sahrens 	progname = basename(argv[0]);
8156fa9e4066Sahrens 	if (strcmp(progname, "mount") == 0) {
8157fa9e4066Sahrens 		ret = manual_mount(argc, argv);
8158fa9e4066Sahrens 	} else if (strcmp(progname, "umount") == 0) {
8159fa9e4066Sahrens 		ret = manual_unmount(argc, argv);
8160fa9e4066Sahrens 	} else {
8161fa9e4066Sahrens 		/*
8162fa9e4066Sahrens 		 * Make sure the user has specified some command.
8163fa9e4066Sahrens 		 */
8164fa9e4066Sahrens 		if (argc < 2) {
8165fa9e4066Sahrens 			(void) fprintf(stderr, gettext("missing command\n"));
816699653d4eSeschrock 			usage(B_FALSE);
8167fa9e4066Sahrens 		}
8168fa9e4066Sahrens 
8169fa9e4066Sahrens 		cmdname = argv[1];
8170fa9e4066Sahrens 
8171fa9e4066Sahrens 		/*
8172fa9e4066Sahrens 		 * The 'umount' command is an alias for 'unmount'
8173fa9e4066Sahrens 		 */
8174fa9e4066Sahrens 		if (strcmp(cmdname, "umount") == 0)
8175fa9e4066Sahrens 			cmdname = "unmount";
8176fa9e4066Sahrens 
8177fa9e4066Sahrens 		/*
8178f2a3c691Sahrens 		 * The 'recv' command is an alias for 'receive'
8179f2a3c691Sahrens 		 */
8180f2a3c691Sahrens 		if (strcmp(cmdname, "recv") == 0)
8181f2a3c691Sahrens 			cmdname = "receive";
8182f2a3c691Sahrens 
8183f2a3c691Sahrens 		/*
81847dbbcd83SAdam Stevko 		 * The 'snap' command is an alias for 'snapshot'
81857dbbcd83SAdam Stevko 		 */
81867dbbcd83SAdam Stevko 		if (strcmp(cmdname, "snap") == 0)
81877dbbcd83SAdam Stevko 			cmdname = "snapshot";
81887dbbcd83SAdam Stevko 
81897dbbcd83SAdam Stevko 		/*
8190fa9e4066Sahrens 		 * Special case '-?'
8191fa9e4066Sahrens 		 */
8192fa9e4066Sahrens 		if (strcmp(cmdname, "-?") == 0)
819399653d4eSeschrock 			usage(B_TRUE);
8194fa9e4066Sahrens 
8195fa9e4066Sahrens 		/*
8196fa9e4066Sahrens 		 * Run the appropriate command.
8197fa9e4066Sahrens 		 */
8198b2634b9cSEric Taylor 		libzfs_mnttab_cache(g_zfs, B_TRUE);
8199ecd6cf80Smarks 		if (find_command_idx(cmdname, &i) == 0) {
8200fa9e4066Sahrens 			current_command = &command_table[i];
8201fa9e4066Sahrens 			ret = command_table[i].func(argc - 1, argv + 1);
820291ebeef5Sahrens 		} else if (strchr(cmdname, '=') != NULL) {
820391ebeef5Sahrens 			verify(find_command_idx("set", &i) == 0);
8204ecd6cf80Smarks 			current_command = &command_table[i];
8205ecd6cf80Smarks 			ret = command_table[i].func(argc, argv);
8206ecd6cf80Smarks 		} else {
8207fa9e4066Sahrens 			(void) fprintf(stderr, gettext("unrecognized "
8208fa9e4066Sahrens 			    "command '%s'\n"), cmdname);
820999653d4eSeschrock 			usage(B_FALSE);
8210fa9e4066Sahrens 		}
8211b2634b9cSEric Taylor 		libzfs_mnttab_cache(g_zfs, B_FALSE);
8212fa9e4066Sahrens 	}
8213fa9e4066Sahrens 
8214fa9e4066Sahrens 	(void) fclose(mnttab_file);
8215fa9e4066Sahrens 
82164445fffbSMatthew Ahrens 	if (ret == 0 && log_history)
82174445fffbSMatthew Ahrens 		(void) zpool_log_history(g_zfs, history_str);
82184445fffbSMatthew Ahrens 
821999653d4eSeschrock 	libzfs_fini(g_zfs);
822099653d4eSeschrock 
8221fa9e4066Sahrens 	/*
8222fa9e4066Sahrens 	 * The 'ZFS_ABORT' environment variable causes us to dump core on exit
8223fa9e4066Sahrens 	 * for the purposes of running ::findleaks.
8224fa9e4066Sahrens 	 */
8225fa9e4066Sahrens 	if (getenv("ZFS_ABORT") != NULL) {
8226fa9e4066Sahrens 		(void) printf("dumping core by request\n");
8227fa9e4066Sahrens 		abort();
8228fa9e4066Sahrens 	}
8229fa9e4066Sahrens 
8230fa9e4066Sahrens 	return (ret);
8231fa9e4066Sahrens }
8232