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